feat(store): implement contact search over contacts_fts #354

Merged
KofTwentyTwo merged 1 commits from story/ws-e10-search-contacts into dev 2026-07-31 22:05:22 +00:00
KofTwentyTwo commented 2026-07-31 22:03:44 +00:00 (Migrated from github.com)

Summary

  • Adds SearchEngine::search_contacts and search_contacts_page (keyset-paginated) in crates/nuncio-store/src/search.rs, querying the existing contacts_fts FTS5 table, mirroring the search_messages/search_messages_page and search_events patterns exactly (same sanitizer, same snippet()/rank usage, same SearchHit { id, title, snippet } shape).
  • Verified contacts_fts was already populated correctly by the contacts_ai/contacts_au/contacts_ad triggers in crates/nuncio-store/src/db.rs (created in an earlier story but unused until now) -- no trigger changes were needed; save_contact's INSERT OR REPLACE correctly fires the delete-then-insert triggers so the FTS row is replaced, not duplicated or left stale (already covered by the pre-existing save_contact_upserts_existing_row_and_fts_entry test).
  • Engine-only change, per WS-E10 scope -- no proto/gRPC change (that's WS-E11).

Tests

New tests in crates/nuncio-store/src/search.rs:

  • fts5_contact_search_ranks_matches_and_excludes_others -- inserts several contacts, confirms matches are ranked/returned and non-matches excluded.
  • fts5_contact_search_reflects_update_and_delete -- proves contacts_au/contacts_ad keep the FTS index in sync after an update and a delete.
  • fts5_contact_search_matches_email_and_survives_edge_query -- matches against the JSON-encoded email column and confirms an FTS5-operator-laden query does not error.
  • search_contacts_page_returns_every_hit_once_no_gaps -- keyset pagination returns every matching contact exactly once across pages, mirroring the existing message-search pagination test.

Gate output

cargo fmt --all -- --check   # clean
cargo check-all               # clean, 0 warnings
cargo test-all                 # all green, 0 ignored
cargo test -p nuncio-store    # 80 passed; 0 failed; 0 ignored

Refs #318

## Summary - Adds `SearchEngine::search_contacts` and `search_contacts_page` (keyset-paginated) in `crates/nuncio-store/src/search.rs`, querying the existing `contacts_fts` FTS5 table, mirroring the `search_messages`/`search_messages_page` and `search_events` patterns exactly (same sanitizer, same `snippet()`/`rank` usage, same `SearchHit { id, title, snippet }` shape). - Verified `contacts_fts` was already populated correctly by the `contacts_ai`/`contacts_au`/`contacts_ad` triggers in `crates/nuncio-store/src/db.rs` (created in an earlier story but unused until now) -- no trigger changes were needed; `save_contact`'s `INSERT OR REPLACE` correctly fires the delete-then-insert triggers so the FTS row is replaced, not duplicated or left stale (already covered by the pre-existing `save_contact_upserts_existing_row_and_fts_entry` test). - Engine-only change, per WS-E10 scope -- no proto/gRPC change (that's WS-E11). ## Tests New tests in `crates/nuncio-store/src/search.rs`: - `fts5_contact_search_ranks_matches_and_excludes_others` -- inserts several contacts, confirms matches are ranked/returned and non-matches excluded. - `fts5_contact_search_reflects_update_and_delete` -- proves `contacts_au`/`contacts_ad` keep the FTS index in sync after an update and a delete. - `fts5_contact_search_matches_email_and_survives_edge_query` -- matches against the JSON-encoded email column and confirms an FTS5-operator-laden query does not error. - `search_contacts_page_returns_every_hit_once_no_gaps` -- keyset pagination returns every matching contact exactly once across pages, mirroring the existing message-search pagination test. ## Gate output ``` cargo fmt --all -- --check # clean cargo check-all # clean, 0 warnings cargo test-all # all green, 0 ignored cargo test -p nuncio-store # 80 passed; 0 failed; 0 ignored ``` Refs #318
Sign in to join this conversation.