Skip to content

fix: search date matching and embedding-score limits - #201

Open
SmilyOrg wants to merge 12 commits into
mainfrom
mcp-issues
Open

SmilyOrg wants to merge 12 commits into
mainfrom
mcp-issues

Conversation

@SmilyOrg

@SmilyOrg SmilyOrg commented Oct 11, 2026 •

Copy link
Copy Markdown
Owner

Summary

Two fixes for the search_photos MCP tool, each found while testing end-to-end and verified with unit tests plus an e2e run:

  1. created: matched the UTC instant, not the stored local date (e67d817).
    Photos with a negative UTC offset were missed by date filters even though every display surface showed the local date. The Go post-filter in listWithPrefixIds compared against bare time.Unix(unix, 0) while the SQL pre-filter used unix + tz_offset*60. Now both evaluate the same local wall-clock instant.

  2. Embedding queries: SQL LIMIT applied before Go scoring (a7068aa).
    The similarity threshold and image/face scoring run in Go, but the SQL LIMIT clipped the row stream first, so only the first limit rows were ever scored and the best matches beyond that were silently discarded. Now goScores queries disable the SQL limit and enforce it in Go after threshold/dedup filtering; embedding queries without an explicit sort default to -similarity (matching the tool description), and the search_photos description documents the per-query-kind default sort.

Also

  • Corrected the search_photos filename: example, which claimed the wrong default order (eee5b01) — it now demonstrates sort:+date.
  • Split out the third fix found during this work — dedup: comparing against all kept rows instead of only the last one — into #202 for independent review (reverted here in 35a0547; the embeddingBytes test helper moves into similarity_search_test.go, which uses it). Merge this PR before fix: compare dedup: against all kept rows, not just the last one #202 to keep that diff clean.

Remaining open issues (filed, not fixed here)

Test plan

  • New unit tests: created_search_test.go, similarity_search_test.go, internal/collection/search_test.go (TestPrimaryOrderFor).
  • e2e: ran img:6 t:0.3 with hand-seeded float16 embeddings — limit 2 returns the top two matches (sim 1.0, 0.9) instead of the two newest rows; sort:+date limit 4 returns all four threshold-passing rows across the date range.
  • go test . ./internal/... passes on the branch.

created: searches filtered created_at_unix against UTC-midnight bounds,
so a photo stored with a real timezone offset (e.g. XMP CreateDate
+09:00) landed on the UTC calendar day and was missed or misfiled by
up to the offset amount. The metadata displays the local date, so the
filter now compares created_at_unix + created_at_tz_offset*60 — the
same value the created_at display column uses — both in the SQL filter
and in the Go wildcard post-filter.

Reproduced end-to-end with a photo stamped 2025-06-01 03:30+09:00
(2025-05-31 18:30 UTC): created:2025-06-01 missed it and
created:2025-05-31 wrongly returned it. Covered by
created_search_test.go.
dedup: only suppressed a row when it was similar to the immediately
previously kept row, so two near-duplicates separated by an unrelated
photo both survived — contradicting the documented guarantee to keep
only photos less than the threshold similar to each other. Track the
embeddings of every kept row and suppress any row similar to any of
them. Covered by dedup_search_test.go (non-adjacent near-duplicates,
synthetic CLIP embeddings).
… -similarity

text, img: and face: queries are scored in Go, but the SQL LIMIT was
applied before scoring, so threshold (t:) and dedup: filters only ever
saw the first N rows in the SQL order — matches beyond the window were
silently dropped, and 'img:5 t:0.8 limit 5' could return 0 rows.

- image.listWithPrefixIds: disable the SQL LIMIT whenever an embedding
  is involved and enforce it in Go after threshold/dedup filtering
- collection.Search: an explicit sort still wins, but embedding queries
  now default to -similarity (best matches first) instead of -date,
  which is what the search_photos description always promised
- search_photos description: document the per-query-kind default sort
- tests: pin the threshold/limit interaction at the DB level and the
  default-sort resolution at the collection level
Moved out of this PR for independent review; the fix lands via
dedup-all-kept-rows (cherry-pick of 2a6aa37). Merge #201 first to
keep that PR's diff clean.

embeddingBytes moves into similarity_search_test.go — a7068aa's
similarity tests use the helper that lived in dedup_search_test.go.
@SmilyOrg SmilyOrg changed the title fix: search date matching, dedup, and embedding-score limits fix: search date matching and embedding-score limits Oct 11, 2026
… the pool while

the writePendingInfosSqlite goroutine could still be flushing a batch, which reads
dirUpdateFuncs (data race caught in TestListThresholdLimitSpansFullSet). Close now
signals the writer by closing pending, waits for it to drain and exit, then
touches the shared state.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

The MCP description gives contradictory defaults for image and face queries.

1 open finding
What changed in this PR

Fixes timezone-aware date matching and embedding search ranking/limits in search_photos.

Changes:

  • Matches created: filters against local wall-clock dates.
  • Applies embedding limits after scoring and defaults embedding searches to similarity order.
  • Adds regression tests and safe database-writer shutdown synchronization.
File Description
created_search_test.go Tests timezone-aware date filtering.
similarity_search_test.go Tests scoring before result limits.
internal/​image/​database.go Fixes filtering, limiting, and writer shutdown.
internal/​collection/​search.go Selects query-aware default ordering.
internal/​collection/​search_test.go Tests default-order selection.
internal/​mcp/​mcp.go Updates search tool guidance.

🧠 Review effort: Balanced


💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread internal/mcp/mcp.go Outdated
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants