SearchMessages previously ranked SQLite results with the default bm25
(every column equal) while PostgreSQL applies setweight 'A' to subject
and 'B' to sender. The two backends returned the same rows in different
orders.
SQLiteDialect.FTSSearchClause now orders by
bm25(messages_fts, 1.0, 10.0, 1.0, 4.0, 1.0, 1.0)
positional over every declared FTS5 column (the leading slot is the
UNINDEXED message_id). The 10:4:1 ratio across subject/sender/body
matches PG's 'A'=1.0 / 'B'=0.4 / 'D'=0.1, so subject-only matches
outrank sender-only matches, which outrank body-only matches on both
backends. bm25 and ts_rank remain different scoring functions, so
intra-class tie-breaking can still diverge — documented as a known
difference in PG_STATUS.md.
Adds TestFTSRankWeightsAcrossBackends, a cross-backend test that seeds
the search token in exactly one FTS column per row and asserts the
subject > sender > body ordering on whichever backend NewTestStore
resolves to.
Additional commits squashed:
* store: add multi-query FTS rank parity fixture test
* store: adapt FTS rank parity test import path for upstream module path
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This is PR4c of what was going to be 4 PRs. The main larger PR4 for PostgreSQL capability is pending PR3 merge.
This one is smaller and independent.
Summary
SQLiteDialect.FTSSearchClausenow orders results bybm25(messages_fts, 1.0, 10.0, 1.0, 4.0, 1.0, 1.0)so subject matches outranksender matches outrank body/recipient matches — matching what PostgreSQL already does with
setweight 'A'=1.0 / 'B'=0.4 / 'D'=0.1.TestFTSRankWeightsAcrossBackends(single-token rank attribution) andTestFTSRankParityFixture(multi-query fixture suite that logs ordering per backend).
docs/PG_STATUS.mdto mark FTS rank ordering as partially resolved (intra-class tie-breaking still differs because bm25 andts_rankare different scorer functions).