Skip to content

branch-4.2: [fix](be) Score MATCH on keyword inverted indexes like SEARCH #69790 - #70008

Merged
yiguolei merged 3 commits into
apache:branch-4.2from
airborne12:backport/69790-branch-4.2
Nov 5, 2026
Merged

yiguolei merged 3 commits into
apache:branch-4.2from
airborne12:backport/69790-branch-4.2

Conversation

@airborne12

@airborne12 airborne12 commented Nov 3, 2026 •

Copy link
Copy Markdown
Member

Backport of #69790 to branch-4.2.

What problem does this PR solve?

Issue Number: None

Related PR: #69790, #69051

Problem Summary: score() of a MATCH on an untokenized (keyword) inverted index is always 0, while SEARCH on the same field scores BM25:

INDEX idx_redirect (redirect) USING INVERTED
-- one row ('BP')
SELECT score() FROM t WHERE redirect MATCH 'BP';  -- 0
SELECT score() FROM t WHERE SEARCH('redirect:BP'); -- 0.2876821

Elasticsearch scores match, term and match_phrase on a keyword field with BM25 in query context, so the score is the idf of the whole value. #69051 (already on 4.2) made the engine score keyword postings but kept MATCH out. This makes a keyword index eligible for MATCH scoring and collects its whole value as one term, the one the reader looks up. It also carries the follow-up fixes from #69790's review:

  • A keyword MATCH_PHRASE drops its ~N slop before collecting.
  • A MATCH on a non-string leaf such as CAST(n AS STRING) collects nothing.
  • A VARIANT path that a segment indexed as BKD, or does not hold, adds no statistics. This covers the BKD searcher coming from the searcher cache, and SNII.
  • A SNII index kind is looked up without copying the directory.

Branch adaptations

Validation

On this branch merged with branch-4.2 c2aaed9, ASAN build:

  • BE UT: 1611 tests in IndexReaderHelperTest, CollectionStatistics* and *Snii* pass.
  • Regression: test_match_keyword_score passes against master's .out on V2/V3/SNII. The following also pass: test_match_phrase_prefix_score, test_bm25_score, test_score_filter_matrix, test_score_filter_usage, test_search_score_topn_predicates, test_multi_analyzer_query, test_match_using_analyzer, test_single_column_multi_index (with debug points on), test_search_score_keyword_filters, test_search_score_raw_index, test_search_score_pattern_constant.
  • Local note: the local thirdparty predates the lance-c upgrade, so lance_reader.cpp's lance_scanner_set_scalar_index_auto_selection call was stubbed for the local build only. It is not part of this PR.

Release note

score() of MATCH / MATCH_ALL / MATCH_PHRASE on a keyword (untokenized) inverted index now returns the BM25 score of the value, the same as SEARCH, instead of 0.

Check List (For Author)

  • Test: Regression test / Unit Test (see Validation)
  • Behavior changed: Yes. Keyword MATCH contributes its BM25 score to score() instead of 0.
  • Does this need documentation: No

🤖 Generated with Claude Code

…#69790)

### What problem does this PR solve?

Issue Number: None

Related PR: apache#69051

Problem Summary: score() of a MATCH on an untokenized (keyword) inverted
index is always 0, while SEARCH on the same field scores BM25:

    INDEX idx_redirect (redirect) USING INVERTED
    -- one row ('BP')
    SELECT score() FROM t WHERE redirect MATCH 'BP';  -- 0
    SELECT score() FROM t WHERE SEARCH('redirect:BP'); -- 0.2876821

Elasticsearch scores a match, term or match_phrase query on a keyword
field with BM25 in query context, so the score is the idf of the whole
value. apache#69051 made the engine score keyword postings but kept MATCH out:
can_score_match required an analyzed index and MatchPredicateCollector
returned early for indexes without an analyzer.

Fix: a keyword index is eligible for MATCH scoring and the collector
collects its whole value as one term, the one the reader looks up (a
keyword MATCH_PHRASE drops its " ~N" slop first). Statistics skip what
has no terms: a MATCH on a non-string leaf such as CAST(n AS STRING),
and a VARIANT path a segment indexed as BKD or does not hold, also when
the BKD searcher comes from the searcher cache. A SNII index kind is
looked up without copying the directory.

Branch adaptations for branch-4.2:
- can_score_match keeps 4.2's analyzed rule (support_phrase only); 4.2
  has no lowercase-normalizer clause.
- test_omit_norms does not exist on 4.2, so its .out change is dropped,
  and IndexReaderHelperTest has no regexp test here (apache#69815 is not on
  4.2).
- test_match_keyword_score inserts the VARIANT rows as JSON strings:
  parse_to_variant in VALUES fails on 4.2 with a max-subcolumns
  conversion error. The expected output is master's, unchanged.
- apache#69901 (statistics per index) is not on 4.2. A MATCH on a keyword and a
  MATCH on an analyzed index of the same column in one score() query
  fails with "Scoring predicates for one field select different inverted
  indexes", as it did on master before apache#69901; before this change the
  keyword clause scored 0 there.

### Release note

score() of MATCH / MATCH_ALL / MATCH_PHRASE on a keyword (untokenized)
inverted index now returns the BM25 score of the value, the same as
SEARCH, instead of 0.

### Check List (For Author)

- Test: Regression test / Unit Test
    - Unit Test: 1593 tests in IndexReaderHelperTest, CollectionStatistics*
      and *Snii* pass on branch-4.2
    - Regression test: test_match_keyword_score (master's .out, V2/V3/SNII),
      test_match_phrase_prefix_score, test_bm25_score,
      test_score_filter_matrix, test_score_filter_usage,
      test_search_score_topn_predicates, test_multi_analyzer_query,
      test_match_using_analyzer, test_single_column_multi_index,
      test_search_score_keyword_filters, test_search_score_raw_index pass
- Behavior changed: Yes. Keyword MATCH contributes its BM25 score to
  score() instead of 0.
- Does this need documentation: No

Source-PR: apache#69790
Source-Head: a66e73d

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@airborne12
airborne12 requested a review from yiguolei as a code owner November 3, 2026 02:34
@hello-stephen

Copy link
Copy Markdown
Contributor

Thank you for your contribution to Apache Doris.
Don't know what should be done next? See How to process your PR.

Please clearly describe your PR:

  1. What problem was fixed (it's best to include specific error reporting information). How it was fixed.
  2. Which behaviors were modified. What was the previous behavior, what is it now, why was it modified, and what possible impacts might there be.
  3. What features were added. Why was this function added?
  4. Which code was refactored and why was this part of the code refactored?
  5. Which functions were optimized and what is the difference before and after the optimization?

@airborne12

Copy link
Copy Markdown
Member Author

run buildall

…branch-4.2

# Conflicts:
#	be/test/storage/segment/index_reader_helper_test.cpp
@airborne12

Copy link
Copy Markdown
Member Author

run buildall

Resolving the backport conflict left the analyzed-index return of
can_score_match with the old continuation indent. clang-format 16 only.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@airborne12

Copy link
Copy Markdown
Member Author

run buildall

@hello-stephen

Copy link
Copy Markdown
Contributor

BE UT Coverage Report

Increment line coverage 88.68% (47/53) 🎉

Increment coverage report
Complete coverage report

Category Coverage
Function Coverage 64.81% (30378/46876)
Line Coverage 49.71% (321751/647293)
Region Coverage 45.46% (259156/570043)
Branch Coverage 47.17% (122617/259966)

@hello-stephen

Copy link
Copy Markdown
Contributor

BE Regression && UT Coverage Report

Increment line coverage 89.66% (52/58) 🎉

Increment coverage report
Complete coverage report

Category Coverage
Function Coverage 76.34% (34756/45528)
Line Coverage 61.23% (393051/641897)
Region Coverage 58.02% (331213/570902)
Branch Coverage 58.99% (153181/259656)

@yiguolei
yiguolei merged commit e4263cb into apache:branch-4.2 Nov 5, 2026
28 of 29 checks passed
@airborne12
airborne12 deleted the backport/69790-branch-4.2 branch November 5, 2026 03:33
@github-actions

github-actions Bot commented Nov 5, 2026

Copy link
Copy Markdown
Contributor

PR approved by anyone and no changes requested.

@github-actions github-actions Bot added the approved Indicates a PR has been approved by one committer. label Nov 5, 2026
@github-actions

github-actions Bot commented Nov 5, 2026

Copy link
Copy Markdown
Contributor

PR approved by at least one committer and no changes requested.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

approved Indicates a PR has been approved by one committer. reviewed

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants