This is an automated email from the ASF dual-hosted git repository.
yiguolei pushed a commit to branch branch-4.2
in repository https://gitbox.apache.org/repos/asf/doris.git
The following commit(s) were added to refs/heads/branch-4.2 by this push:
new 624c42cd74e branch-4.2: [fix](be) Score regexp and wildcard search
terms with BM25 #69052 (#69236)
624c42cd74e is described below
commit 624c42cd74ef285a193f9fddf07db6a7750dfc1d
Author: github-actions[bot]
<41898282+github-actions[bot]@users.noreply.github.com>
AuthorDate: Mon Oct 19 11:35:59 2026 +0800
branch-4.2: [fix](be) Score regexp and wildcard search terms with BM25
#69052 (#69236)
Cherry-picked from #69052
Co-authored-by: Jack <[email protected]>
---
.../query/engine/expand_query/expand_query.h | 5 +-
.../query/engine/expand_query/expand_weight.cpp | 69 +++++++++++++++++++++-
.../query/engine/expand_query/expand_weight.h | 9 ++-
.../exprs/function/search/function_search_test.cpp | 23 +++++---
.../inverted/query/engine/prefix_query_test.cpp | 23 ++++++++
.../inverted/query/engine/regexp_query_test.cpp | 48 ++++++++++++---
.../inverted/query/engine/wildcard_query_test.cpp | 54 ++++++++++++++---
.../index/inverted/query/spi/fake_index_source.h | 13 ++++
8 files changed, 210 insertions(+), 34 deletions(-)
diff --git
a/be/src/storage/index/inverted/query/engine/expand_query/expand_query.h
b/be/src/storage/index/inverted/query/engine/expand_query/expand_query.h
index 45816618fc9..155acf16c73 100644
--- a/be/src/storage/index/inverted/query/engine/expand_query/expand_query.h
+++ b/be/src/storage/index/inverted/query/engine/expand_query/expand_query.h
@@ -38,9 +38,8 @@ public:
_pattern(std::move(pattern)) {}
~ExpandQuery() override = default;
- // Scores are constant, so scoring changes nothing.
- WeightPtr weight(bool /*enable_scoring*/) override {
- return std::make_shared<ExpandWeight>(_context, _field, _kind,
_pattern);
+ WeightPtr weight(bool enable_scoring) override {
+ return std::make_shared<ExpandWeight>(_context, _field, _kind,
_pattern, enable_scoring);
}
private:
diff --git
a/be/src/storage/index/inverted/query/engine/expand_query/expand_weight.cpp
b/be/src/storage/index/inverted/query/engine/expand_query/expand_weight.cpp
index 3c55f171ec4..85fd10a51d0 100644
--- a/be/src/storage/index/inverted/query/engine/expand_query/expand_weight.cpp
+++ b/be/src/storage/index/inverted/query/engine/expand_query/expand_weight.cpp
@@ -17,6 +17,7 @@
#include "storage/index/inverted/query/engine/expand_query/expand_weight.h"
+#include <cmath>
#include <memory>
#include <roaring/roaring.hh>
#include <vector>
@@ -25,10 +26,14 @@
#include "storage/index/inverted/query/engine/bit_set_query/bit_set_scorer.h"
#include
"storage/index/inverted/query/engine/const_score_query/const_score_scorer.h"
#include "storage/index/inverted/query/engine/nullable_scorer.h"
+#include "storage/index/inverted/query/engine/score_combiner.h"
+#include "storage/index/inverted/query/engine/term_query/term_scorer.h"
+#include "storage/index/inverted/query/engine/union/buffered_union.h"
#include "storage/index/inverted/query/exec/block_doc_set.h"
#include "storage/index/inverted/query/exec/collect_postings.h"
#include "storage/index/inverted/query/exec/roaring_docid_sink.h"
#include "storage/index/inverted/query/exec/term_waves.h"
+#include "storage/index/inverted/query/scoring/bm25_similarity.h"
#include "storage/index/inverted/query/spi/postings_cursor.h"
namespace doris::segment_v2::inverted_index::query_v2 {
@@ -58,11 +63,15 @@ Status collect_expanded_rows(index_query::IndexSource&
source, index_query::Term
}
ExpandWeight::ExpandWeight(IndexQueryContextPtr context, std::wstring field,
- index_query::TermPatternKind kind, std::string
pattern)
+ index_query::TermPatternKind kind, std::string
pattern,
+ bool enable_scoring)
: _context(std::move(context)),
_field(std::move(field)),
_kind(kind),
- _pattern(std::move(pattern)) {}
+ _pattern(std::move(pattern)),
+ // Prefix and phrase-edge expansions retain their constant-score
behavior.
+ _enable_scoring(enable_scoring && (kind ==
index_query::TermPatternKind::kRegexp ||
+ kind ==
index_query::TermPatternKind::kWildcard)) {}
std::shared_ptr<roaring::Roaring> ExpandWeight::_rows(const
QueryExecutionContext& context,
const std::string&
binding_key,
@@ -85,12 +94,68 @@ std::shared_ptr<roaring::Roaring> ExpandWeight::_rows(const
QueryExecutionContex
ScorerPtr ExpandWeight::scorer(const QueryExecutionContext& context,
const std::string& binding_key) {
+ // The listing fast path discards term frequencies, so scored patterns
need term scorers.
+ if (_enable_scoring) {
+ return make_nullable_scorer(_scored_terms(context, binding_key),
+ logical_field_or_fallback(context,
binding_key, _field),
+ context.null_resolver);
+ }
auto scorer = std::make_shared<ConstScoreScorer<BitSetScorerPtr>>(
std::make_shared<BitSetScorer>(_rows(context, binding_key,
nullptr)));
return make_nullable_scorer(scorer, logical_field_or_fallback(context,
binding_key, _field),
context.null_resolver);
}
+ScorerPtr ExpandWeight::_scored_terms(const QueryExecutionContext& context,
+ const std::string& binding_key) {
+ index_query::TermPattern pattern;
+ THROW_IF_ERROR(index_query::TermPattern::create(_kind, _pattern,
&pattern));
+ auto source = lookup_source(_field, context, binding_key);
+ // An unbound field has no postings and contributes no matches.
+ if (source == nullptr) {
+ return std::make_shared<EmptyScorer>();
+ }
+ std::vector<std::string> terms;
+ THROW_IF_ERROR(source->expand_terms(
+ pattern, index_query::expansion_limit(_kind,
index_query::max_expansions(*_context)),
+ &terms));
+ // A valid pattern may match no dictionary terms.
+ if (terms.empty()) {
+ return std::make_shared<EmptyScorer>();
+ }
+ // Expansion terms are absent from collection statistics; use the bound
source's statistics.
+ index_query::IndexScoringStats stats;
+ THROW_IF_ERROR(source->scoring_stats(&stats));
+ DORIS_CHECK_GT(stats.doc_count, 0);
+ // Docs-only SNII still reports its physical token count. Only legacy
sources without
+ // that metadata report zero, so use unit average length only for those
sources.
+ const uint64_t token_count = stats.token_count != 0 ? stats.token_count :
stats.doc_count;
+ const float avgdl = static_cast<float>(token_count) / stats.doc_count;
+ DORIS_CHECK_GT(avgdl, 0.0F);
+ std::vector<ScorerPtr> scorers;
+ scorers.reserve(terms.size());
+ auto logical_field = logical_field_or_fallback(context, binding_key,
_field);
+ for (const auto& term : terms) {
+ uint64_t doc_freq = 0;
+ THROW_IF_ERROR(source->doc_freq(term, &doc_freq));
+ // The immutable source just enumerated this term and must have its
postings.
+ DCHECK_GT(doc_freq, 0);
+ const auto idf = static_cast<float>(std::log1p(
+ (static_cast<double>(stats.doc_count) - doc_freq + 0.5) /
(doc_freq + 0.5)));
+ auto similarity = std::make_shared<BM25Similarity>(idf, avgdl);
+ auto postings = open_postings(*source, term, /*positions=*/false,
/*enable_scoring=*/true,
+ similarity);
+ DCHECK(postings != nullptr);
+ scorers.emplace_back(std::make_shared<TermScorer>(std::move(postings),
+
std::move(similarity), logical_field));
+ }
+ // One expanded term needs no union; multiple terms add their per-document
BM25 scores.
+ if (scorers.size() == 1) {
+ return std::move(scorers.front());
+ }
+ return make_buffered_union(scorers, std::make_shared<SumCombiner>());
+}
+
index_query::TruthSet ExpandWeight::listed_rows(const QueryExecutionContext&
context,
const std::string& binding_key,
const roaring::Roaring*
candidates) {
diff --git
a/be/src/storage/index/inverted/query/engine/expand_query/expand_weight.h
b/be/src/storage/index/inverted/query/engine/expand_query/expand_weight.h
index b5e979aca5d..3bbe43531c3 100644
--- a/be/src/storage/index/inverted/query/engine/expand_query/expand_weight.h
+++ b/be/src/storage/index/inverted/query/engine/expand_query/expand_weight.h
@@ -29,23 +29,25 @@ Status collect_expanded_rows(index_query::IndexSource&
source, index_query::Term
int32_t max_expansions, const roaring::Roaring*
candidates,
roaring::Roaring* rows);
-// Every document that holds a term the pattern expands to, with a constant
score.
+// Every document that holds an expanded term; scored regexp and wildcard
clauses sum BM25.
class ExpandWeight : public Weight {
public:
ExpandWeight(IndexQueryContextPtr context, std::wstring field,
- index_query::TermPatternKind kind, std::string pattern);
+ index_query::TermPatternKind kind, std::string pattern,
+ bool enable_scoring = false);
~ExpandWeight() override = default;
ScorerPtr scorer(const QueryExecutionContext& context, const std::string&
binding_key) override;
bool lists_rows(const QueryExecutionContext&, const std::string&) const
override {
- return true;
+ return !_enable_scoring;
}
index_query::TruthSet listed_rows(const QueryExecutionContext& context,
const std::string& binding_key,
const roaring::Roaring* candidates)
override;
private:
+ ScorerPtr _scored_terms(const QueryExecutionContext& context, const
std::string& binding_key);
std::shared_ptr<roaring::Roaring> _rows(const QueryExecutionContext&
context,
const std::string& binding_key,
const roaring::Roaring*
candidates);
@@ -53,6 +55,7 @@ private:
std::wstring _field;
index_query::TermPatternKind _kind;
std::string _pattern;
+ bool _enable_scoring;
};
} // namespace doris::segment_v2::inverted_index::query_v2
diff --git a/be/test/exprs/function/search/function_search_test.cpp
b/be/test/exprs/function/search/function_search_test.cpp
index 74423142019..c9c3ea985e9 100644
--- a/be/test/exprs/function/search/function_search_test.cpp
+++ b/be/test/exprs/function/search/function_search_test.cpp
@@ -21,6 +21,7 @@
#include <gtest/gtest.h>
#include <chrono>
+#include <cmath>
#include <initializer_list>
#include <map>
#include <memory>
@@ -1861,6 +1862,8 @@ TEST_F(FunctionSearchTest,
TestBuildLeafQueryExecutesSelectedSniiWildcardReader)
std::make_shared<RecordingNativeInvertedIndexReader>(&decoy_meta,
decoy_file_reader);
auto selected_reader =
std::make_shared<RecordingNativeInvertedIndexReader>(
&selected_meta, selected_file_reader);
+ selected_reader->fake_source->set_doc_count(4);
+ selected_reader->fake_source->set_scoring_stats({.doc_count = 4,
.token_count = 3});
selected_reader->fake_source->add("alpha", {0, 2});
selected_reader->fake_source->add("gamma", {1});
decoy_reader->fake_source->add("alpha", {1});
@@ -1909,7 +1912,8 @@ TEST_F(FunctionSearchTest,
TestBuildLeafQueryExecutesSelectedSniiWildcardReader)
binding_key);
ASSERT_NE(nullptr, scorer);
EXPECT_EQ(0U, scorer->doc());
- EXPECT_FLOAT_EQ(1.0F, scorer->score());
+ // The source has three tokens over four rows, even without stored norms.
+ EXPECT_NEAR(0.88F * std::log(2.0F), scorer->score(), 1e-6F);
expect_bitmap_eq(collect_docs(scorer), {0, 2});
// The pattern was lowercased by the selected analyzer and enumerated on
the selected
// index only; a leading wildcard has no prefix to enumerate from.
@@ -2509,7 +2513,8 @@ static void search_snii_pattern(const
std::map<std::string, std::string>& proper
ASSERT_TRUE(status.ok()) << status.to_string();
EXPECT_EQ(0, reader->query_calls) << value;
// The scorer enumerates the stem on the source; the reader itself is
never asked.
- auto weight = query->weight(true);
+ // This helper checks normalization and routing, without requesting
scoring statistics.
+ auto weight = query->weight(false);
ASSERT_NE(nullptr, weight);
auto scorer =
weight->scorer(build_variant_search_query_execution_context(4, resolver,
nullptr),
binding_key);
@@ -2740,13 +2745,12 @@ TEST_F(FunctionSearchTest,
TestSniiScoredLeafRanksByTheEngineBm25) {
}
}
-// A leaf for which the reader publishes no per-document score must keep the
constant that the
-// CLucene path also gives its own constant-score leaves, so that routing
scored clauses through a
-// new scorer does not quietly re-rank the unscored ones. The fake reader is
what withholds the
-// scores here, so this pins the "nothing published -> BitSetQuery -> 1.0"
behaviour; it does not
-// pin which query types the production is_need_similarity_score gate rejects.
-TEST_F(FunctionSearchTest,
TestSniiNativeLeafWithoutPublishedScoresKeepsConstantScore) {
+// Scored wildcard leaves read frequencies and statistics through the unified
source,
+// even when the old reader query API publishes no per-document scores.
+TEST_F(FunctionSearchTest, TestSniiNativeWildcardUsesSourceScoringStatistics) {
SniiScoringFixture fixture(43, 4);
+ fixture.reader->fake_source->set_doc_count(4);
+ fixture.reader->fake_source->set_scoring_stats({.doc_count = 4,
.token_count = 2});
fixture.reader->fake_source->add("alpha", {0, 2});
inverted_index::query_v2::QueryPtr query;
@@ -2763,7 +2767,8 @@ TEST_F(FunctionSearchTest,
TestSniiNativeLeafWithoutPublishedScoresKeepsConstant
auto scorer = weight->scorer(exec_ctx, binding_key);
ASSERT_NE(nullptr, scorer);
EXPECT_EQ(0U, scorer->doc());
- EXPECT_FLOAT_EQ(1.0F, scorer->score());
+ // avgdl = 2/4 and implicit length = 1: BM25 is idf * 2.2 / 3.1.
+ EXPECT_NEAR((2.2F / 3.1F) * std::log(2.0F), scorer->score(), 1e-6F);
expect_bitmap_eq(collect_docs(scorer), {0, 2});
}
diff --git a/be/test/storage/index/inverted/query/engine/prefix_query_test.cpp
b/be/test/storage/index/inverted/query/engine/prefix_query_test.cpp
index 4d68efdf47a..e711deb3ed9 100644
--- a/be/test/storage/index/inverted/query/engine/prefix_query_test.cpp
+++ b/be/test/storage/index/inverted/query/engine/prefix_query_test.cpp
@@ -18,6 +18,7 @@
#include <CLucene.h>
#include <gtest/gtest.h>
+#include <cmath>
#include <memory>
#include <roaring/roaring.hh>
#include <string>
@@ -32,6 +33,8 @@
#include "storage/index/inverted/inverted_index_reader.h"
#include "storage/index/inverted/query/engine/expand_query/expand_query.h"
#include "storage/index/inverted/query/logical/node.h"
+#include "storage/index/inverted/query/scoring/bm25_similarity.h"
+#include "storage/index/inverted/query/scoring/collection_similarity.h"
#include "storage/index/inverted/query/spi/fake_index_source.h"
#include "storage/olap_common.h"
@@ -492,6 +495,8 @@ TEST(ExpansionLeafTest,
KeepsPatternLimitsAndLeavesRootCandidatesToTheCaller) {
auto source =
std::make_shared<index_query::testing::FakeIndexSource>();
source->batches = batches;
source->set_doc_count(10);
+ // Scored regexp and wildcard leaves need statistics from their bound
source.
+ source->set_scoring_stats({.doc_count = 10, .token_count = 10});
source->add("apple", {0, 4});
source->add("application", {1});
source->add("apply", {2, 4});
@@ -509,12 +514,30 @@ TEST(ExpansionLeafTest,
KeepsPatternLimitsAndLeavesRootCandidatesToTheCaller) {
expected.add(2);
}
for (const bool scoring : {false, true}) {
+ context->collection_similarity =
+ scoring ? std::make_shared<CollectionSimilarity>() :
nullptr;
auto result = std::make_shared<roaring::Roaring>();
result->add(9);
const auto status = run_leaf(context, L"content", leaf,
&candidates, scoring,
source, source->doc_count(),
result);
ASSERT_TRUE(status.ok()) << status;
EXPECT_EQ(*result, expected) << pattern << " scoring=" <<
scoring;
+ if (scoring) {
+ const auto& scores =
context->collection_similarity->_bm25_scores;
+ EXPECT_EQ(scores.size(), expected.cardinality() - 1);
+ const bool bm25 = kind == logical::ExpandKind::kWildcard ||
+ kind == logical::ExpandKind::kRegexp;
+ for (const auto& [doc, score] : scores) {
+ // Row 9 predates this leaf and must not acquire a
score from it.
+ EXPECT_NE(doc, 9U);
+ EXPECT_TRUE(expected.contains(doc));
+ const float df = doc == 1 ? 1.0F : 2.0F;
+ const float idf = std::log1p((10.0F - df + 0.5F) / (df
+ 0.5F));
+ const float expected_score =
+ bm25 ? BM25Similarity(idf, 1.0F).score(1.0F,
1) : 1.0F;
+ EXPECT_FLOAT_EQ(score, expected_score) << pattern << "
doc=" << doc;
+ }
+ }
}
}
}
diff --git a/be/test/storage/index/inverted/query/engine/regexp_query_test.cpp
b/be/test/storage/index/inverted/query/engine/regexp_query_test.cpp
index 6de9c997661..9695f3da776 100644
--- a/be/test/storage/index/inverted/query/engine/regexp_query_test.cpp
+++ b/be/test/storage/index/inverted/query/engine/regexp_query_test.cpp
@@ -49,13 +49,15 @@ public:
st = io::global_local_filesystem()->create_directory(kTestDir);
ASSERT_TRUE(st.ok()) << st;
std::string field_name = "content";
- create_test_index(
- field_name, kTestDir,
- {"apple123", "apple456", "banana789", "test123abc",
"pattern456",
- "regex999", "match123", "search456", "prefix123",
"suffix789",
- "apple_test", "banana_data", "test_pattern", "data_regex",
"apple_banana",
- "test_match", "pattern_test", "prefix_suffix", "abc123xyz",
"def456ghi"},
- "standard");
+ create_test_index(field_name, kTestDir,
+ {"apple123", "apple456", "banana789",
+ "test123abc", "pattern456", "regex999",
+ "match123", "search456", "prefix123",
+ "suffix789", "apple_test", "banana_data",
+ "test_pattern", "data_regex", "apple_banana",
+ "test_match", "pattern_test", "prefix_suffix",
+ "abc123xyz", "def456ghi", "apple789 apple789
apple456"},
+ "standard");
}
void TearDown() override {
@@ -179,6 +181,36 @@ TEST_F(RegexpQueryV2Test, test_regexp_query_with_scoring) {
ASSERT_NE(regexp_weight, nullptr);
}
+TEST_F(RegexpQueryV2Test, test_regexp_query_scores_matching_terms) {
+ auto context = std::make_shared<IndexQueryContext>();
+ context->collection_statistics = std::make_shared<CollectionStatistics>();
+ context->collection_similarity = std::make_shared<CollectionSimilarity>();
+
+ auto* dir = FSDirectory::getDirectory(kTestDir.c_str());
+ auto reader_holder =
make_shared_reader(lucene::index::IndexReader::open(dir, true));
+ ASSERT_NE(reader_holder, nullptr);
+
+ std::wstring field = StringHelper::to_wstring("content");
+ auto query = std::make_shared<query_v2::ExpandQuery>(
+ context, field, index_query::TermPatternKind::kRegexp,
anchored("apple.*"));
+ auto weight = query->weight(true);
+
+ query_v2::QueryExecutionContext exec_ctx;
+ exec_ctx.segment_num_rows = reader_holder->maxDoc();
+ exec_ctx.sources = {clucene_index_source(reader_holder, field, nullptr)};
+ exec_ctx.field_sources.emplace(field, exec_ctx.sources.front());
+
+ auto scorer = weight->scorer(exec_ctx);
+ ASSERT_EQ(scorer->seek(0), 0);
+ float single_term_score = scorer->score();
+ EXPECT_NE(single_term_score, 1.0F);
+ uint32_t last_doc = reader_holder->maxDoc() - 1;
+ ASSERT_EQ(scorer->seek(last_doc), last_doc);
+ EXPECT_GT(scorer->score(), single_term_score);
+
+ _CLDECDELETE(dir);
+}
+
// Test regexp query execution
TEST_F(RegexpQueryV2Test, test_regexp_query_execution) {
auto context = std::make_shared<IndexQueryContext>();
@@ -627,7 +659,7 @@ TEST_F(RegexpQueryV2Test, test_whole_term_match_any_term) {
doc = scorer->advance();
}
- EXPECT_EQ(result.cardinality(), 20);
+ EXPECT_EQ(result.cardinality(), 21);
_CLDECDELETE(dir);
}
diff --git
a/be/test/storage/index/inverted/query/engine/wildcard_query_test.cpp
b/be/test/storage/index/inverted/query/engine/wildcard_query_test.cpp
index 15d1b143ad4..1822c79e9e5 100644
--- a/be/test/storage/index/inverted/query/engine/wildcard_query_test.cpp
+++ b/be/test/storage/index/inverted/query/engine/wildcard_query_test.cpp
@@ -58,14 +58,20 @@ public:
private:
void create_test_index(const std::string& field_name, const std::string&
dir) {
- std::vector<std::string> test_data = {
- "apple", "application", "apply", "apricot",
"banana", "band",
- "bandana", "can", "candy", "cat",
"catalog", "dog",
- "document", "door", "hello", "help",
"helpful", "test",
- "testing", "testcase", "wildcard", "wild",
"wilderness", "card",
- "cardboard", "abc", "abcd", "abcde",
"prefix123", "123suffix",
- "pre123", "123suf", "both123both", "star", "start",
"started",
- "starter", "question", "quest", "query",
"queries"};
+ std::vector<std::string> test_data = {"apple", "application",
"apply",
+ "apricot", "banana",
"band",
+ "bandana", "can",
"candy",
+ "cat", "catalog",
"dog",
+ "document", "door",
"hello",
+ "help", "helpful",
"test",
+ "testing", "testcase",
"wildcard",
+ "wild", "wilderness",
"card",
+ "cardboard", "abc",
"abcd",
+ "abcde", "prefix123",
"123suffix",
+ "pre123", "123suf",
"both123both",
+ "star", "start",
"started",
+ "starter", "question",
"quest",
+ "query", "queries",
"test test testing"};
CustomAnalyzerConfig::Builder builder;
builder.with_tokenizer_config("standard", {});
@@ -158,6 +164,36 @@ TEST_F(WildcardQueryV2Test,
test_wildcard_query_with_scoring) {
ASSERT_NE(wildcard_weight, nullptr);
}
+TEST_F(WildcardQueryV2Test, test_wildcard_query_scores_matching_terms) {
+ auto context = std::make_shared<IndexQueryContext>();
+ context->collection_statistics = std::make_shared<CollectionStatistics>();
+ context->collection_similarity = std::make_shared<CollectionSimilarity>();
+
+ auto* dir = FSDirectory::getDirectory(kTestDir.c_str());
+ auto reader_holder =
make_shared_reader(lucene::index::IndexReader::open(dir, true));
+ ASSERT_NE(reader_holder, nullptr);
+
+ std::wstring field = StringHelper::to_wstring("field");
+ auto query = std::make_shared<query_v2::ExpandQuery>(
+ context, field, index_query::TermPatternKind::kWildcard, "test*");
+ auto weight = query->weight(true);
+
+ query_v2::QueryExecutionContext exec_ctx;
+ exec_ctx.segment_num_rows = reader_holder->maxDoc();
+ exec_ctx.sources = {clucene_index_source(reader_holder, field, nullptr)};
+ exec_ctx.field_sources.emplace(field, exec_ctx.sources.front());
+
+ auto scorer = weight->scorer(exec_ctx);
+ ASSERT_EQ(scorer->seek(17), 17);
+ float single_term_score = scorer->score();
+ EXPECT_NE(single_term_score, 1.0F);
+ uint32_t last_doc = reader_holder->maxDoc() - 1;
+ ASSERT_EQ(scorer->seek(last_doc), last_doc);
+ EXPECT_GT(scorer->score(), single_term_score);
+
+ _CLDECDELETE(dir);
+}
+
// Test wildcard query with asterisk prefix pattern
TEST_F(WildcardQueryV2Test, test_wildcard_query_prefix_pattern) {
auto context = std::make_shared<IndexQueryContext>();
@@ -539,4 +575,4 @@ TEST_F(WildcardQueryV2Test,
test_wildcard_query_move_semantics) {
ASSERT_NE(weight2, nullptr);
}
-} // namespace doris::segment_v2
\ No newline at end of file
+} // namespace doris::segment_v2
diff --git a/be/test/storage/index/inverted/query/spi/fake_index_source.h
b/be/test/storage/index/inverted/query/spi/fake_index_source.h
index 1c1db1545f3..db6fcbdac4b 100644
--- a/be/test/storage/index/inverted/query/spi/fake_index_source.h
+++ b/be/test/storage/index/inverted/query/spi/fake_index_source.h
@@ -21,6 +21,7 @@
#include <cstdint>
#include <map>
#include <memory>
+#include <optional>
#include <set>
#include <span>
#include <string>
@@ -214,6 +215,17 @@ public:
void remove(uint32_t doc) { _deleted.insert(doc); }
void set_doc_count(uint32_t doc_count) { _doc_count = doc_count; }
+ void set_scoring_stats(IndexScoringStats stats) { _scoring_stats = stats; }
+
+ Status scoring_stats(IndexScoringStats* out) const override {
+ // Most fixtures do not score expansions and deliberately provide no
source statistics.
+ if (!_scoring_stats.has_value()) {
+ return IndexSource::scoring_stats(out);
+ }
+ *out = *_scoring_stats;
+ return Status::OK();
+ }
+
uint32_t doc_count() const override { return _doc_count; }
bool batches_reads() const override { return batches; }
@@ -331,6 +343,7 @@ private:
std::map<std::string, std::vector<Posting>> _terms;
std::set<uint32_t> _deleted;
uint32_t _doc_count = 0;
+ std::optional<IndexScoringStats> _scoring_stats;
};
} // namespace doris::index_query::testing
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]