Four moving parts: the app on your phone, the copy of the index the app keeps on the phone, the Opensolr platform, and the phone's own Opensolr Index. Since 2.5 most of what the app does touches none of the network: browsing, suggestions and a sync with nothing to do are answered from that copy, and only typed searches, filtered views, the filter lists, the duplicate groups, the photos the map draws, new or changed photos and edited words leave the phone, over HTTPS. Besides those it reaches only two hosts, carrying nothing about you or your photos: api.github.com, to ask whether a newer version exists, and tile.openstreetmap.org, for the map tiles while the map screen is open.
Figure 1 — how a photo becomes searchable.
- Finds photos through Android's MediaStore, in the folders you chose.
- Reads each photo's camera details itself: date, camera, lens, exposure, place.
- Makes a small upright JPEG copy, 1024 px on its long edge, for the reader. The copy is re-encoded from pixels and carries only a chosen set of EXIF tags: the time, the camera and lens, the exposure and the GPS position. Nothing else from the original travels with it.
- Reads the photo’s own facts from the file: its EXIF, and the names of the people in it when another app already wrote them into the file. It never writes to the file.
- Finds the faces in each photo and tells them apart on the phone; you name a face once and the app finds that person in your other photos. No face goes to Opensolr’s AI servers.
- Writes the documents into the index with
/update, and asks the index with/selectonly for a typed search, a filtered view, the filter lists, the duplicate groups and the photos the map draws. Stats is counted on the phone. Everything else is answered on the phone. - Runs sync in the background, one at a time, comparing your folders with the copy of the index it keeps on the phone. Nothing changed means no requests at all: for a library of 10,000 photos version 2.4 made about 21 requests and downloaded about 1.8 MB on every sync, only to learn that nothing had changed.
- Saves your edits — tags, the people in a photo, your own wording — on the phone first, and finishes there. The sync that starts straight after carries them up through
photos_words, 50 photos per call, with no picture attached, because only the words changed. Nothing is written into the photo files. - Answers tag and name suggestions, and the list of what the selected photos already carry, from its copy of the index. The groups of alike photos are still asked of the index, because the keys behind them live only there. Stats is counted on the phone, from the same copy. It shares or deletes selected photos when you ask.
- Watches the photo library and syncs on its own when photos are added, changed or deleted. Pulling the photo grid down reloads it and starts a sync too; a sync that is running is stopped first and started again.
opensolr.com
The sign-in and approval pages, and the account API: creating the index, uploading its configuration, reading its address and password, reading plan limits and usage, and nearby_places, which turns a GPS position into the nearest place: city, region, country.
api.opensolr.com
photos_ingest takes up to ten photos at a time and indexes them completely on the server: EXIF from the copy, the words the image model reads in it, the same model as Opensolr's own image search, the search vector, the place, the text printed in the photo when it carries any, the names of the people on it, and the write into your index, storing no picture. The phone is the authority for your tags and wording and sends them with the photo; what was read out of the picture before — its printed text, its words — is kept when this pass cannot read it, but only while the file's md5 says it is the same picture. photos_words carries your edited tags, names and wording up during the next sync, 50 photos per call, no picture attached; on a plan with vector search it makes the new vector out of those words. The vector of a photo whose tags or words you edited is no longer asked for from the phone at all: photos_words makes it on the server. photos_select turns a typed search into one and runs the search with it, on your index.
Reading the text
photos_ingest reads the printed words in every photo it receives — a receipt, an invoice, a label, a screenshot — on the same server with tesseract, every photo of the batch at the same time, while the image model works on them. A reading is kept only when it holds real text; the words land in ocr_t and the search reads them with everything else. Reading a photo costs no more than indexing it.
A regular Opensolr Index named photos_<ANDROID_ID>__dense, created in your account on a server that runs vector search, with the app's own schema. The app reads and writes it directly with its credentials: /select for a typed search, a filtered view, the filter lists, the groups of alike photos and the photos the map draws, /update to add and delete. Listing, the counts, the grouping by year, month and day, and the suggestions are answered on the phone and never reach it. See the phone's index.
Figure 2 — where each piece of data lives. The arrows cross the edge of the phone only for the few things that still go over the network.
The phone keeps a copy of every document its index holds: the id, the path, the file name, the size, the dates, the camera, the EXIF, the place, the words the photo was read into, the printed text read out of it, the people, your own tags and the md5 of the file. Two things it deliberately does not hold: the search vector, which the phone cannot use, and the keys that find duplicates. They are the heavy fields.
The copy is read out of the index once, at install or reinstall. From then on every write keeps it in step, so it is never read again.
The comparison a sync makes, so a sync with nothing to do makes no requests. Plain browsing — the years, the months, the days, their counts and the photos in them. Tag and name suggestions, and the list of what the selected photos already carry. The filter lists are asked of the index once and kept until a sync actually writes something; the groups of alike photos and the photos the map draws are still read from the index, because the copy does not hold the keys behind them.
- You take a photo; the camera saves it in
DCIM/Camera/. - At the next sync the app finds it, gives it its id and sees it is not in its copy of the index. Nothing is asked of the index at this step.
- The app reads its date, camera and GPS position, and makes the 1024 px copy.
photos_ingesttakes the copy with its EXIF and does the rest on the server: twelve words, for example dog, beach, sand, sea, …, their search vector, the place, the words printed in the photo when it carries any, and the write into the index, in one call for up to ten photos.nearby_placesturns the position into Austin, Travis, Texas, United States of America.- You put your own tags, names or wording on the photo. The save is done on the phone and is finished there; the sync that starts straight after sends the words alone through
photos_words, up to 50 photos per call, no picture attached. The server rewrites the document with the new words and, on a plan with vector search, a new vector made out of them. - The document — the words, the printed text, the people, your tags and wording, the search vector, date, camera, place, path and the keys that find duplicates — is written to the index, the copy on the phone is brought in step with it, and the photo appears in search.
- You edit the photo in your gallery; a minute later a sync sees that the file no longer matches the copy on the phone, and hands it over to be read again.
- You delete the photo from your gallery; a minute later its id is missing from the phone, and its document is deleted from the index and from the copy. That is the invariant that lets the next sync stay at no requests.
Nothing is sent to be read at all: no words, no printed text, no vector. The photo is indexed by its date, camera, place, file name and your own words, and search is lexical. Steps 4 and 6 above describe the plans that have vector search.
A photo is known by the md5 of its file. The printed text and the words already read out of it survive a later pass that cannot read it — a plan without vector search, or the month's AI requests used up — as long as it is the same file. A photo with no date of its own keeps the date the index already holds when your words are written into the file, instead of jumping to today.