airborne12 commented on code in PR #69790:
URL: https://github.com/apache/doris/pull/69790#discussion_r4412691841


##########
be/src/storage/index/inverted/reader/predicate_collector.cpp:
##########
@@ -285,6 +285,24 @@ void preserve_selected_index_metadata(const 
ScoringIndexCandidates& candidates,
 
 using namespace scoring_collection_detail;
 
+namespace {
+
+// The terms a MATCH scores, the ones its reader looks up. A keyword index 
stores the whole value
+// as one term, case and spaces included: tag MATCH 'Hello World' scores the 
single term
+// "Hello World", as SEARCH('tag:"Hello World"') does. Any other index cuts 
the value with the
+// query's analyzer.
+Result<std::vector<TermInfo>> match_scoring_terms(const std::string& value,
+                                                  const TabletIndex& 
index_meta,
+                                                  const 
InvertedIndexAnalyzerCtx& analyzer_ctx) {
+    if 
(!inverted_index::InvertedIndexAnalyzer::should_analyzer(index_meta.properties()))
 {
+        return std::vector<TermInfo> {TermInfo(value)};

Review Comment:
   Fixed in db7d9a86dc. Confirmed, and the effect was worse than a wrong IDF: 
the scorer looked up `BP`, found no statistics for it, and the query failed 
with `Not such term`. For a keyword MATCH_PHRASE the collector now strips the 
slop with the same `take_slop` that `lower_match` (and the no-index path in 
`match.cpp`) uses, so it collects the term the reader looks up. Covered by 
`CollectionStatisticsTest.KeywordPhraseCollectsTermWithoutSlop` (`~N` and 
`~N+`; MATCH_ANY keeps the whole value) and the `match_phrase_slop` / 
`match_phrase_ordered_slop` cases in `test_match_keyword_score` (V2/V3/SNII), 
which score the same as `tag MATCH_PHRASE 'BP'`.
   



##########
be/src/storage/index/index_reader_helper.cpp:
##########
@@ -24,12 +24,16 @@ namespace {
 
 bool can_score_match(const TabletIndex* index_meta) {
     const auto& properties = index_meta->properties();
-    // parser=none can inherit support_phrase=true from FE defaults. MATCH 
statistics skip
-    // these keyword indexes, so their filters must not request a scoring 
context either.
-    return inverted_index::InvertedIndexAnalyzer::should_analyzer(properties) 
&&
-           (get_parser_phrase_support_string_from_properties(properties) ==
-                    INVERTED_INDEX_PARSER_PHRASE_SUPPORT_YES ||
-            IndexReaderHelper::is_normalizer_only(properties));
+    // A keyword index keeps each value as one term, without positions or 
norms, and its postings
+    // score with unit frequency and length whatever support_phrase says, as 
an Elasticsearch
+    // keyword field does: over tags (x, x, y, z), tag MATCH 'x' scores idf(x) 
= ln(2) for both
+    // matching rows, the same as SEARCH('tag:x'). MATCH statistics collect 
these terms too.
+    if (!inverted_index::InvertedIndexAnalyzer::should_analyzer(properties)) {
+        return true;
+    }

Review Comment:
   Fixed in db7d9a86dc. Confirmed: `find_slot_ref` looks through the cast to 
`n`, the selector picks its BKD index for an INT field, and a BKD index has no 
analyzer, so the keyword rule admitted it. Only a text index is ever read with 
a score (`TextIndexReader` is the only reader-side caller of 
`is_need_similarity_score`), so `MatchPredicateCollector` now collects nothing 
for a MATCH on a non-string leaf. The SEARCH collector returns an error for a 
numeric leaf instead (`SearchScoringRejectsNumericBkdLeaf`), but here the 
predicate is only a scalar filter, so returning an error would fail a query 
that used to run. Covered by 
`CollectionStatisticsTest.MatchOnNumericColumnCollectsNothing` and the 
`analyzed_and_numeric_cast` case in `test_match_keyword_score` (`body MATCH 
'apple' AND CAST(n AS STRING) MATCH '1'`, V2/V3/SNII), where only the body leaf 
scores.
   



##########
be/src/storage/index/inverted/reader/predicate_collector.cpp:
##########
@@ -365,15 +383,14 @@ Status MatchPredicateCollector::collect(RuntimeState* 
state, const TabletSchemaS
     const auto* index_meta = DORIS_TRY(
             select_index_meta(candidates.index_metas, candidates.field_type, 
query_type,
                               analyzer_ctx->analyzer_key, 
analyzer_ctx->legacy_analyzer_key));
-    if 
(!inverted_index::InvertedIndexAnalyzer::should_analyzer(index_meta->properties())
 ||
-        !IndexReaderHelper::is_need_similarity_score(expr->op(), index_meta)) {
+    if (!IndexReaderHelper::is_need_similarity_score(expr->op(), index_meta)) {

Review Comment:
   Confirmed, but this is out of scope for this PR. The restriction is 
`validate_same_physical_index`, which exists on master already: statistics are 
keyed per field (column unique id + suffix path), and scoring two different 
inverted indexes of one column in one query has always been rejected. Two 
analyzed indexes on one column, a path this PR does not change, fail with the 
same error. I checked it locally: with `parser=english` and `parser=standard` 
indexes on `name`, `WHERE name MATCH 'apple' USING ANALYZER english AND name 
MATCH 'apple' USING ANALYZER standard ORDER BY score() DESC LIMIT 10` returns 
"Scoring predicates for one field select different inverted indexes". This PR 
makes keyword indexes score, so a keyword + analyzed pair on one column now 
falls under that same existing rule. Before, the keyword leaf did not 
participate in scoring at all, which is why that pair used to run.
   
   Lifting the restriction means keying the collected statistics and the scorer 
lookup by physical index across the collector, `CollectionStatistics` and the 
V2/V3/SNII readers. That is a separate change, and it applies equally to the 
analyzed + analyzed case, so I'd rather do it in its own PR than grow this fix. 
The query needs two indexes on the same column and both of them used in one 
`score()` query with different `USING ANALYZER` clauses, so it is rare in 
practice.
   



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to