Skip to content

fix(fts): propagate nested multimatch limits - #8073

Merged
BubbleCal merged 1 commit into
mainfrom
yang/oss-1599-fix-nested-multimatch-limit-propagation-in-compound-fts
Jul 29, 2026
Merged

fix(fts): propagate nested multimatch limits#8073
BubbleCal merged 1 commit into
mainfrom
yang/oss-1599-fix-nested-multimatch-limit-propagation-in-compound-fts

Conversation

@BubbleCal

Copy link
Copy Markdown
Contributor

What is the bug?

BooleanQuery and BoostQuery recursively plan their children without a limit so outer score composition remains exact. Nested MultiMatchQuery, however, applied its final fetch from the ambient scanner limit instead of the recursively supplied FTS search parameters.

Linear: OSS-1599

What issues or incorrect behavior does the bug cause?

A nested MultiMatch could discard candidates before an outer MUST clause, SHOULD score accumulation, or BoostQuery demotion finished combining scores. This could omit the true top-k or return incomplete scores. Equal-score rows could also appear in a different order between bounded and exhaustive execution.

How does this PR fix the problem?

  • Use FtsSearchParams::limit as the recursive planning contract for MultiMatch fetches.
  • Treat None as complete execution for compound parents and document that future competitive-score pruning needs a separate contract.
  • Order compound FTS ties by _score DESC, _rowid ASC.
  • Add a regression test covering MUST, SHOULD, BoostQuery, standalone MultiMatch, no limit, small k, multiple fields, three fragments/segments, and score ties.

This is a correctness and planning-semantics fix. It does not replace the current MultiMatch Union/Aggregate/Sort execution.

Validation

  • cargo test -p lance --lib test_nested_multimatch_limit_propagation -- --nocapture
  • cargo test -p lance --lib io::exec::fts::tests -- --nocapture (15 passed)
  • cargo test -p lance --lib dataset::tests::dataset_index::test_fts_ -- --nocapture (22 passed)
  • cargo clippy -p lance -p lance-index --lib --tests -- -D warnings
  • cargo fmt --all -- --check
  • git diff --check

@github-actions github-actions Bot added A-index Vector index, linalg, tokenizer bug Something isn't working labels Jul 29, 2026
@BubbleCal
BubbleCal marked this pull request as ready for review July 29, 2026 12:17
@codecov

codecov Bot commented Jul 29, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 90.90909% with 2 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
rust/lance/src/dataset/scanner.rs 83.33% 0 Missing and 2 partials ⚠️

📢 Thoughts on this report? Let us know!

@BubbleCal
BubbleCal merged commit d8c36df into main Jul 29, 2026
36 checks passed
@BubbleCal
BubbleCal deleted the yang/oss-1599-fix-nested-multimatch-limit-propagation-in-compound-fts branch July 29, 2026 13:59
sbrunk added a commit to sbrunk/lance that referenced this pull request Jul 30, 2026
lance-format#8073 made `FtsSearchParams::limit` the recursive planning contract for FTS
nodes: `Some(k)` permits bounded top-k execution, `None` requires every
candidate so a compound parent can finish composing scores before its own
fetch. `combined_fields` already honors it, but no test nested it under a
compound parent, so a regression that let the ambient scanner limit reach the
child would have gone unnoticed.

Add `test_nested_combined_fields_limit_propagation` over MUST, SHOULD,
`BoostQuery`, and the standalone query, mirroring
`test_nested_multimatch_limit_propagation`, and lift the three-fragment
two-column fixture both tests need into `compound_fts_dataset`.
sbrunk added a commit to sbrunk/lance that referenced this pull request Jul 30, 2026
lance-format#8073 gave the `MultiMatch` arm the same `(score DESC, row_id ASC)` sort
expressions `combined_fields_sort_exprs` builds, so the block existed twice
verbatim. Rename the helper `fts_score_sort_exprs`, drop the
combined_fields-only framing from its doc comment, and call it from both arms.

The fts.rs test helper likewise hand-rolled the comparator lance-format#8073 added as
`compare_scored_rows`; use that instead. Neither path changes behavior.
sbrunk added a commit to sbrunk/lance that referenced this pull request Aug 2, 2026
lance-format#8073 gave the `MultiMatch` arm the same `(score DESC, row_id ASC)` sort
expressions `combined_fields_sort_exprs` builds, so the block existed twice
verbatim. Rename the helper `fts_score_sort_exprs`, drop the
combined_fields-only framing from its doc comment, and call it from both arms.

The fts.rs test helper likewise hand-rolled the comparator lance-format#8073 added as
`compare_scored_rows`; use that instead. Neither path changes behavior.
sbrunk added a commit to sbrunk/lance that referenced this pull request Aug 5, 2026
Upstream lance-format#8073 gave the `MultiMatch` arm the same `(score DESC, row_id ASC)`
sort expressions `combined_fields_sort_exprs` builds, so the block existed twice
verbatim. Rename the helper `fts_score_sort_exprs`, drop the
combined_fields-only framing from its doc comment, and call it from both arms.
No behavior change.
sbrunk added a commit to sbrunk/lance that referenced this pull request Aug 6, 2026
Upstream lance-format#8073 gave the `MultiMatch` arm the same `(score DESC, row_id ASC)`
sort expressions `combined_fields_sort_exprs` builds, so the block existed twice
verbatim. Rename the helper `fts_score_sort_exprs`, drop the
combined_fields-only framing from its doc comment, and call it from both arms.
No behavior change.
sbrunk added a commit to sbrunk/lance that referenced this pull request Aug 10, 2026
Upstream lance-format#8073 gave the `MultiMatch` arm the same `(score DESC, row_id ASC)`
sort expressions `combined_fields_sort_exprs` builds, so the block existed twice
verbatim. Rename the helper `fts_score_sort_exprs`, drop the
combined_fields-only framing from its doc comment, and call it from both arms.
No behavior change.
sbrunk added a commit to sbrunk/lance that referenced this pull request Aug 10, 2026
Upstream lance-format#8073 gave the `MultiMatch` arm the same `(score DESC, row_id ASC)`
sort expressions `combined_fields_sort_exprs` builds, so the block existed twice
verbatim. Rename the helper `fts_score_sort_exprs`, drop the
combined_fields-only framing from its doc comment, and call it from both arms.
No behavior change.
sbrunk added a commit to sbrunk/lance that referenced this pull request Aug 12, 2026
Upstream lance-format#8073 gave the `MultiMatch` arm the same `(score DESC, row_id ASC)`
sort expressions `combined_fields_sort_exprs` builds, so the block existed twice
verbatim. Rename the helper `fts_score_sort_exprs`, drop the
combined_fields-only framing from its doc comment, and call it from both arms.
No behavior change.
sbrunk added a commit to sbrunk/lance that referenced this pull request Aug 14, 2026
Upstream lance-format#8073 gave the `MultiMatch` arm the same `(score DESC, row_id ASC)`
sort expressions `combined_fields_sort_exprs` builds, so the block existed twice
verbatim. Rename the helper `fts_score_sort_exprs`, drop the
combined_fields-only framing from its doc comment, and call it from both arms.
No behavior change.
sbrunk added a commit to sbrunk/lance that referenced this pull request Aug 14, 2026
Upstream lance-format#8073 gave the `MultiMatch` arm the same `(score DESC, row_id ASC)`
sort expressions `combined_fields_sort_exprs` builds, so the block existed twice
verbatim. Rename the helper `fts_score_sort_exprs`, drop the
combined_fields-only framing from its doc comment, and call it from both arms.
No behavior change.
sbrunk added a commit to sbrunk/lance that referenced this pull request Aug 16, 2026
Upstream lance-format#8073 gave the `MultiMatch` arm the same `(score DESC, row_id ASC)`
sort expressions `combined_fields_sort_exprs` builds, so the block existed twice
verbatim. Rename the helper `fts_score_sort_exprs`, drop the
combined_fields-only framing from its doc comment, and call it from both arms.
No behavior change.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

A-index Vector index, linalg, tokenizer bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants