There is one algorithm. A first Sync and a Re-Sync differ only in what the index already holds: a first sync compares your photos with an empty index, so everything is added.
Figure 1 — the five steps of every sync, forced or scheduled.
- Make sure the index is there. Reuse it, create it, or create it again if it vanished; check it carries the app's current configuration and bring it up to date if not. See the phone's index.
- Scan the chosen folders through MediaStore and give every photo its id, the md5 of its absolute path.
- Download every id the index holds, 1000 at a time:
q=*:*,fl=id,sort=id asc,rows=1000, withstartmoving on by 1000 until a page comes back shorter. Sorting by id keeps the pages lined up. - Delete every id that is in the index but no longer on the phone, 500 per request.
- Add every photo that is on the phone but not in the index, 20 per round (below). The same round also rewrites photos that need it: photos edited in place (same path, so the same id, but a different size or modification time than the cache remembers), photos you asked to re-sync from the grid, and photos with a position whose place words are still missing.
After the last round the app makes a hard commit, forgets cached photos that are no longer on the phone, and reads the plan usage again.
Figure 2 — the comparison: keep what is in both, add what is only on the phone, delete what is only in the index.
- For each photo, take its document from the phone's cache if the file is unchanged. Otherwise read its camera details on the phone, make an upright 640 px JPEG copy, and send the copy to
image_clip, which answers twelve words describing it. - For the photos of the round that have a GPS position and no place yet, look the positions up in one call to
nearby_placeson opensolr.com and write the nearest named place into each document: city, region, province, community, country. Positions are grouped to about 10 m and remembered on the phone, so a place is asked for once. - On plans with vector search, send the round's words to
batch_embedin one call and get one vector per photo back. - Write the round's documents to the index in one
POST /updatewithcommitWithin=10000, so they appear in search within ten seconds while the next round starts. - Store each photo's document and vector in the cache, and move the progress on.
Reading a photo and embedding its words both count against the plan. The phone keeps, per photo, the document that was written and its vector, in a private SQLite database. An entry is used only while the file's size and modification time are unchanged, so an edited photo is read again, and the change is noticed at the next sync even though the photo's id did not change. Opensolr keeps its own permanent answers too: the same picture is never read by CLIP twice, and a position is never looked up twice.
Empty the index from the control panel, lose it, or reinstall the app: the next sync refills the index from the cache without a single AI request. Only photos the phone has never read cost anything.
Skips it, counts it as skipped, carries on. It is tried again at every Re-Sync.
Waits as long as the server asks and retries, up to six times.
Commits what was written, stops, and posts a notification with an Upgrade button.
Stops and posts a notification with an Upgrade button.
Carries on without vectors.
Reads the new one from the account and retries.
Stops and asks you to sign in again.
Commits what was written and stops; the next sync continues.
A run never has to start over. The next Re-Sync compares ids again and only adds what is still missing.
A new version of the app can ship a newer schema.xml or solrconfig.xml (new fields, autocomplete, spellcheck). The first sync after the update notices that the index is on the older configuration, uploads the new one, and then writes every photo again from the phone's cache, without AI requests, so the new fields are filled in. Search keeps working throughout; while this runs the photo grid shows Index update in progress, and the new filters fill in as photos are written.