This is an automated email from the ASF dual-hosted git repository.

yiguolei pushed a commit to branch branch-4.1
in repository https://gitbox.apache.org/repos/asf/doris.git


The following commit(s) were added to refs/heads/branch-4.1 by this push:
     new 8cf932abf67 branch-4.1: [fix](be) Score regexp and wildcard search 
terms with BM25 #69052 (#69269)
8cf932abf67 is described below

commit 8cf932abf67ad76816ca41f3903cfbba578792d2
Author: Jack <[email protected]>
AuthorDate: Tue Oct 20 09:25:06 2026 +0800

    branch-4.1: [fix](be) Score regexp and wildcard search terms with BM25 
#69052 (#69269)
    
    ### What problem does this PR solve?
    
    Related PR: #69052
    
    Backport regexp and wildcard SEARCH scoring to `branch-4.1`. Scored
    expansions currently return constant scores. The fix uses existing
    `TermScorer` instances to compute BM25 for each expanded term and sums
    the matching contributions. Unscored queries retain the bitmap path.
    
    This uses the compatible original change from
    `34a123b85da6ffc4990157820211dd08462744ec` with the branch's `query_v2`
    interfaces. Legacy indexes without total-term-frequency metadata retain
    the unit-average-length fallback. The backport also adds independent
    term-score assertions and SQL regression coverage.
    
    ### Release note
    
    Scored SEARCH regexp and wildcard expansions use BM25 relevance scores.
    
    ### Validation
    
    Tested head: `07de3d3acc1adeddae8ba85c6da295aa047f0b6f`, including
    current `branch-4.1` base `c7f95adaaaec4bc0caeab9ee831a9009745d56d0`.
    
    - ASAN UT: 636 tests from 36 related suites passed; zero failures,
    errors or skips. The two new expansion-scoring tests fail against the
    original production implementation and pass with this fix, including
    equality with independently collected `TermQuery` contributions.
    - Full ASAN and Release FE/BE builds completed. After synchronizing the
    newer Boolean-scoring base, both BE types were rebuilt and the unchanged
    FE artifacts were reused. Both binaries retain GLIBC compatibility
    (maximum required GLIBC 2.10); ASAN instrumentation was verified in the
    ASAN binary.
    - ASAN and Release each passed 16 related SEARCH/inverted-index suites,
    with zero failed suites, fatal scripts or skips. Coverage includes
    scoring/cache/TopN/range predicates, Boolean branches, regexp and array
    regexp, phrase/prefix/edge, custom analyzers and raw indexes.
    - Each binary passed 24 direct score-comparison SQL queries across
    V2/V3, standard/keyword indexes and WAND on/off. Regexp and wildcard
    scores match explicit expanded-term queries. The new regression has 48
    framework-generated golden cases, replayed on both binaries, including
    NULL input, empty matches, ordering and unscored queries.
    - Runtime logs contain no sanitizer errors. All task-owned test
    instances were stopped. Full merged-tree clang-format 16 and
    English-description checks passed.
    
    Additional static-check limit: clang-tidy 16 cannot parse the
    compiler-20 JNI assertions; clang-tidy 20 is blocked by an unmatched
    NOLINTEND in the unchanged baseline. An overall clang-tidy pass is not
    claimed.
    
    ### Check List (For Author)
    
    - Test
        - [x] Regression test
        - [x] Unit Test
        - [x] Manual test (score-comparison matrix described above)
    - Behavior changed:
    - [x] Yes. Scored regexp/wildcard expansions return BM25 contributions.
    - Does this need documentation?
        - [x] No. No syntax or setting is added.
    
    ### Check List (For Reviewer who merge this PR)
    
    - [ ] Confirm the release note
    - [ ] Confirm test cases
    - [ ] Confirm document
    - [ ] Add branch pick label
---
 .../query_v2/regexp_query/regexp_weight.cpp        |  50 +++
 .../inverted/query_v2/regexp_query/regexp_weight.h |   6 +-
 .../index/inverted/query_v2/regexp_query_test.cpp  |  61 +++-
 .../inverted/query_v2/wildcard_query_test.cpp      |  72 ++++-
 .../search/test_search_score_regexp_wildcard.out   | 337 +++++++++++++++++++++
 .../test_search_score_regexp_wildcard.groovy       |  80 +++++
 6 files changed, 591 insertions(+), 15 deletions(-)

diff --git 
a/be/src/storage/index/inverted/query_v2/regexp_query/regexp_weight.cpp 
b/be/src/storage/index/inverted/query_v2/regexp_query/regexp_weight.cpp
index 5ecfad0d3ae..e2576724ab4 100644
--- a/be/src/storage/index/inverted/query_v2/regexp_query/regexp_weight.cpp
+++ b/be/src/storage/index/inverted/query_v2/regexp_query/regexp_weight.cpp
@@ -34,12 +34,18 @@
 #include <gen_cpp/PaloBrokerService_types.h>
 
 #include <algorithm>
+#include <cmath>
 #include <string>
+#include <vector>
 
 #include "storage/index/inverted/query_v2/bit_set_query/bit_set_scorer.h"
 #include 
"storage/index/inverted/query_v2/const_score_query/const_score_scorer.h"
 #include "storage/index/inverted/query_v2/nullable_scorer.h"
+#include "storage/index/inverted/query_v2/score_combiner.h"
 #include "storage/index/inverted/query_v2/segment_postings.h"
+#include "storage/index/inverted/query_v2/term_query/term_scorer.h"
+#include "storage/index/inverted/query_v2/union/buffered_union.h"
+#include "storage/index/inverted/similarity/bm25_similarity.h"
 #include "storage/index/inverted/util/string_helper.h"
 #include "util/hyperscan_util.h"
 
@@ -110,6 +116,10 @@ ScorerPtr RegexpWeight::regexp_scorer(const 
QueryExecutionContext& context,
     }
 
     auto reader = lookup_reader(_field, context, binding_key);
+    if (_enable_scoring) {
+        return scored_terms_scorer(reader.get(), context, binding_key, 
matching_terms);
+    }
+
     auto doc_bitset = std::make_shared<roaring::Roaring>();
     for (const auto& term : matching_terms) {
         auto segment_postings =
@@ -126,6 +136,46 @@ ScorerPtr RegexpWeight::regexp_scorer(const 
QueryExecutionContext& context,
     return const_score;
 }
 
+ScorerPtr RegexpWeight::scored_terms_scorer(lucene::index::IndexReader* reader,
+                                            const QueryExecutionContext& 
context,
+                                            const std::string& binding_key,
+                                            const std::vector<std::wstring>& 
matching_terms) {
+    // Expanded terms are not collected in collection statistics, so use 
reader statistics.
+    const auto num_docs = reader->maxDoc();
+    DORIS_CHECK_GT(num_docs, 0);
+    const auto total_term_freq = reader->sumTotalTermFreq(_field.c_str());
+    // Without norms, postings use a document length of one.
+    const float avgdl = static_cast<float>(total_term_freq.value_or(num_docs)) 
/ num_docs;
+    DORIS_CHECK_GT(avgdl, 0.0F);
+
+    std::vector<ScorerPtr> term_scorers;
+    term_scorers.reserve(matching_terms.size());
+    auto logical_field = logical_field_or_fallback(context, binding_key, 
_field);
+    for (const auto& term : matching_terms) {
+        Term lucene_term(_field.c_str(), term.c_str());
+        const int32_t doc_freq = reader->docFreq(&lucene_term);
+        if (doc_freq == 0) {
+            continue;
+        }
+        const auto idf = static_cast<float>(
+                std::log1p((static_cast<double>(num_docs) - doc_freq + 0.5) / 
(doc_freq + 0.5)));
+        auto similarity = std::make_shared<BM25Similarity>(idf, avgdl);
+        auto posting =
+                create_term_posting(reader, _field, term, true, similarity, 
_context->io_ctx);
+        if (posting) {
+            term_scorers.emplace_back(std::make_shared<TermScorer>(
+                    std::move(posting), std::move(similarity), logical_field));
+        }
+    }
+    if (term_scorers.empty()) {
+        return std::make_shared<EmptyScorer>();
+    }
+    if (term_scorers.size() == 1) {
+        return std::move(term_scorers.front());
+    }
+    return make_buffered_union(term_scorers, std::make_shared<SumCombiner>());
+}
+
 std::optional<std::string> RegexpWeight::get_regex_prefix(const std::string& 
pattern) {
     DBUG_EXECUTE_IF("RegexpQuery.get_regex_prefix", { return std::nullopt; });
 
diff --git 
a/be/src/storage/index/inverted/query_v2/regexp_query/regexp_weight.h 
b/be/src/storage/index/inverted/query_v2/regexp_query/regexp_weight.h
index f8b1a0ba9fa..6ef13c71fa5 100644
--- a/be/src/storage/index/inverted/query_v2/regexp_query/regexp_weight.h
+++ b/be/src/storage/index/inverted/query_v2/regexp_query/regexp_weight.h
@@ -35,6 +35,10 @@ public:
 
 private:
     ScorerPtr regexp_scorer(const QueryExecutionContext& context, const 
std::string& binding_key);
+    ScorerPtr scored_terms_scorer(lucene::index::IndexReader* reader,
+                                  const QueryExecutionContext& context,
+                                  const std::string& binding_key,
+                                  const std::vector<std::wstring>& 
matching_terms);
 
     std::optional<std::string> get_regex_prefix(const std::string& pattern);
     void collect_matching_terms(const QueryExecutionContext& context,
@@ -46,7 +50,7 @@ private:
 
     std::wstring _field;
     std::string _pattern;
-    [[maybe_unused]] bool _enable_scoring = false;
+    bool _enable_scoring = false;
     bool _nullable = true;
     // Set to 0 to disable limit (ES has no default limit for prefix queries)
     // The limit prevents collecting too many terms, but can cause incorrect 
results
diff --git a/be/test/storage/index/inverted/query_v2/regexp_query_test.cpp 
b/be/test/storage/index/inverted/query_v2/regexp_query_test.cpp
index aea4ed627ed..ed02ac578d3 100644
--- a/be/test/storage/index/inverted/query_v2/regexp_query_test.cpp
+++ b/be/test/storage/index/inverted/query_v2/regexp_query_test.cpp
@@ -28,6 +28,7 @@
 #include "storage/index/index_query_context.h"
 #include "storage/index/inverted/analyzer/custom_analyzer.h"
 #include "storage/index/inverted/query_v2/regexp_query/regexp_weight.h"
+#include "storage/index/inverted/query_v2/term_query/term_query.h"
 #include "storage/index/inverted/util/string_helper.h"
 
 CL_NS_USE(search)
@@ -58,10 +59,13 @@ public:
 private:
     void create_test_index(const std::string& field_name, const std::string& 
dir) {
         std::vector<std::string> test_data = {
-                "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"};
+                "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"};
 
         CustomAnalyzerConfig::Builder builder;
         builder.with_tokenizer_config("standard", {});
@@ -167,6 +171,53 @@ 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::RegexpQuery>(context, field, 
"apple.*");
+    auto weight = query->weight(true);
+
+    query_v2::QueryExecutionContext exec_ctx;
+    exec_ctx.segment_num_rows = reader_holder->maxDoc();
+    exec_ctx.readers = {reader_holder};
+    exec_ctx.field_reader_bindings.emplace(field, reader_holder);
+
+    auto statistics = context->collection_statistics;
+    statistics->_total_num_docs = reader_holder->maxDoc();
+    statistics->_total_num_tokens[field] =
+            
reader_holder->sumTotalTermFreq(field.c_str()).value_or(reader_holder->maxDoc());
+    for (const auto* term : {L"apple123", L"apple789", L"apple456"}) {
+        Term lucene_term(field.c_str(), term);
+        statistics->_term_doc_freqs[field][term] = 
reader_holder->docFreq(&lucene_term);
+    }
+    auto term_score = [&](const wchar_t* term, uint32_t doc_id) {
+        auto term_query = std::make_shared<query_v2::TermQuery>(context, 
field, term);
+        auto term_scorer = term_query->weight(true)->scorer(exec_ctx);
+        EXPECT_EQ(term_scorer->seek(doc_id), doc_id);
+        return term_scorer->score();
+    };
+
+    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);
+    EXPECT_FLOAT_EQ(single_term_score, term_score(L"apple123", 0));
+    uint32_t last_doc = reader_holder->maxDoc() - 1;
+    ASSERT_EQ(scorer->seek(last_doc), last_doc);
+    EXPECT_GT(scorer->score(), single_term_score);
+    EXPECT_NEAR(scorer->score(),
+                term_score(L"apple789", last_doc) + term_score(L"apple456", 
last_doc), 1e-5F);
+
+    _CLDECDELETE(dir);
+}
+
 // Test regexp query execution
 TEST_F(RegexpQueryV2Test, test_regexp_query_execution) {
     auto context = std::make_shared<IndexQueryContext>();
@@ -574,7 +625,7 @@ TEST_F(RegexpQueryV2Test, 
test_make_exact_match_wildcard_pattern) {
         doc = scorer->advance();
     }
 
-    EXPECT_EQ(result.cardinality(), 20);
+    EXPECT_EQ(result.cardinality(), 21);
 
     _CLDECDELETE(dir);
 }
diff --git a/be/test/storage/index/inverted/query_v2/wildcard_query_test.cpp 
b/be/test/storage/index/inverted/query_v2/wildcard_query_test.cpp
index af119cdd260..3807a2787db 100644
--- a/be/test/storage/index/inverted/query_v2/wildcard_query_test.cpp
+++ b/be/test/storage/index/inverted/query_v2/wildcard_query_test.cpp
@@ -27,6 +27,7 @@
 #include "common/status.h"
 #include "storage/index/index_query_context.h"
 #include "storage/index/inverted/analyzer/custom_analyzer.h"
+#include "storage/index/inverted/query_v2/term_query/term_query.h"
 #include "storage/index/inverted/query_v2/wildcard_query/wildcard_weight.h"
 #include "storage/index/inverted/util/string_helper.h"
 
@@ -57,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", {});
@@ -155,6 +162,53 @@ 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::WildcardQuery>(context, field, 
"test*");
+    auto weight = query->weight(true);
+
+    query_v2::QueryExecutionContext exec_ctx;
+    exec_ctx.segment_num_rows = reader_holder->maxDoc();
+    exec_ctx.readers = {reader_holder};
+    exec_ctx.field_reader_bindings.emplace(field, reader_holder);
+
+    auto statistics = context->collection_statistics;
+    statistics->_total_num_docs = reader_holder->maxDoc();
+    statistics->_total_num_tokens[field] =
+            
reader_holder->sumTotalTermFreq(field.c_str()).value_or(reader_holder->maxDoc());
+    for (const auto* term : {L"test", L"testing"}) {
+        Term lucene_term(field.c_str(), term);
+        statistics->_term_doc_freqs[field][term] = 
reader_holder->docFreq(&lucene_term);
+    }
+    auto term_score = [&](const wchar_t* term, uint32_t doc_id) {
+        auto term_query = std::make_shared<query_v2::TermQuery>(context, 
field, term);
+        auto term_scorer = term_query->weight(true)->scorer(exec_ctx);
+        EXPECT_EQ(term_scorer->seek(doc_id), doc_id);
+        return term_scorer->score();
+    };
+
+    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);
+    EXPECT_FLOAT_EQ(single_term_score, term_score(L"test", 17));
+    uint32_t last_doc = reader_holder->maxDoc() - 1;
+    ASSERT_EQ(scorer->seek(last_doc), last_doc);
+    EXPECT_GT(scorer->score(), single_term_score);
+    EXPECT_NEAR(scorer->score(), term_score(L"test", last_doc) + 
term_score(L"testing", last_doc),
+                1e-5F);
+
+    _CLDECDELETE(dir);
+}
+
 // Test wildcard query with asterisk prefix pattern
 TEST_F(WildcardQueryV2Test, test_wildcard_query_prefix_pattern) {
     auto context = std::make_shared<IndexQueryContext>();
@@ -524,4 +578,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/regression-test/data/search/test_search_score_regexp_wildcard.out 
b/regression-test/data/search/test_search_score_regexp_wildcard.out
new file mode 100644
index 00000000000..393a5781dab
--- /dev/null
+++ b/regression-test/data/search/test_search_score_regexp_wildcard.out
@@ -0,0 +1,337 @@
+-- This file is automatically generated. You should know what you did if you 
want to edit this
+-- !V2_standard_unscored_regexp --
+1
+2
+3
+4
+7
+8
+
+-- !V2_standard_unscored_wildcard --
+1
+2
+3
+4
+7
+8
+
+-- !V2_standard_false_regexp --
+8      2.087193
+4      1.312985
+7      1.248584
+3      1.120813
+2      0.655108
+1      0.584433
+
+-- !V2_standard_false_wildcard --
+8      2.087193
+4      1.312985
+7      1.248584
+3      1.120813
+2      0.655108
+1      0.584433
+
+-- !V2_standard_false_terms --
+8      2.087193
+4      1.312985
+7      1.248584
+3      1.120813
+2      0.655108
+1      0.584433
+
+-- !V2_standard_false_ascending --
+1      0.584433
+2      0.655108
+3      1.120813
+7      1.248584
+4      1.312985
+8      2.087193
+
+-- !V2_standard_false_missing --
+
+-- !V2_standard_true_regexp --
+8      2.087193
+4      1.312985
+7      1.248584
+3      1.120813
+2      0.655108
+1      0.584433
+
+-- !V2_standard_true_wildcard --
+8      2.087193
+4      1.312985
+7      1.248584
+3      1.120813
+2      0.655108
+1      0.584433
+
+-- !V2_standard_true_terms --
+8      2.087193
+4      1.312985
+7      1.248584
+3      1.120813
+2      0.655108
+1      0.584433
+
+-- !V2_standard_true_ascending --
+1      0.584433
+2      0.655108
+3      1.120813
+7      1.248584
+4      1.312985
+8      2.087193
+
+-- !V2_standard_true_missing --
+
+-- !V2_none_unscored_regexp --
+1
+2
+3
+4
+7
+8
+
+-- !V2_none_unscored_wildcard --
+1
+2
+3
+4
+7
+8
+
+-- !V2_none_false_regexp --
+1      1.791759
+2      1.791759
+3      1.791759
+4      1.791759
+7      1.791759
+8      1.791759
+
+-- !V2_none_false_wildcard --
+1      1.791759
+2      1.791759
+3      1.791759
+4      1.791759
+7      1.791759
+8      1.791759
+
+-- !V2_none_false_terms --
+1      1.791759
+2      1.791759
+3      1.791759
+4      1.791759
+7      1.791759
+8      1.791759
+
+-- !V2_none_false_ascending --
+1      1.791759
+2      1.791759
+3      1.791759
+4      1.791759
+7      1.791759
+8      1.791759
+
+-- !V2_none_false_missing --
+
+-- !V2_none_true_regexp --
+1      1.791759
+2      1.791759
+3      1.791759
+4      1.791759
+7      1.791759
+8      1.791759
+
+-- !V2_none_true_wildcard --
+1      1.791759
+2      1.791759
+3      1.791759
+4      1.791759
+7      1.791759
+8      1.791759
+
+-- !V2_none_true_terms --
+1      1.791759
+2      1.791759
+3      1.791759
+4      1.791759
+7      1.791759
+8      1.791759
+
+-- !V2_none_true_ascending --
+1      1.791759
+2      1.791759
+3      1.791759
+4      1.791759
+7      1.791759
+8      1.791759
+
+-- !V2_none_true_missing --
+
+-- !V3_standard_unscored_regexp --
+1
+2
+3
+4
+7
+8
+
+-- !V3_standard_unscored_wildcard --
+1
+2
+3
+4
+7
+8
+
+-- !V3_standard_false_regexp --
+8      2.087193
+4      1.312985
+7      1.248584
+3      1.120813
+2      0.655108
+1      0.584433
+
+-- !V3_standard_false_wildcard --
+8      2.087193
+4      1.312985
+7      1.248584
+3      1.120813
+2      0.655108
+1      0.584433
+
+-- !V3_standard_false_terms --
+8      2.087193
+4      1.312985
+7      1.248584
+3      1.120813
+2      0.655108
+1      0.584433
+
+-- !V3_standard_false_ascending --
+1      0.584433
+2      0.655108
+3      1.120813
+7      1.248584
+4      1.312985
+8      2.087193
+
+-- !V3_standard_false_missing --
+
+-- !V3_standard_true_regexp --
+8      2.087193
+4      1.312985
+7      1.248584
+3      1.120813
+2      0.655108
+1      0.584433
+
+-- !V3_standard_true_wildcard --
+8      2.087193
+4      1.312985
+7      1.248584
+3      1.120813
+2      0.655108
+1      0.584433
+
+-- !V3_standard_true_terms --
+8      2.087193
+4      1.312985
+7      1.248584
+3      1.120813
+2      0.655108
+1      0.584433
+
+-- !V3_standard_true_ascending --
+1      0.584433
+2      0.655108
+3      1.120813
+7      1.248584
+4      1.312985
+8      2.087193
+
+-- !V3_standard_true_missing --
+
+-- !V3_none_unscored_regexp --
+1
+2
+3
+4
+7
+8
+
+-- !V3_none_unscored_wildcard --
+1
+2
+3
+4
+7
+8
+
+-- !V3_none_false_regexp --
+1      1.791759
+2      1.791759
+3      1.791759
+4      1.791759
+7      1.791759
+8      1.791759
+
+-- !V3_none_false_wildcard --
+1      1.791759
+2      1.791759
+3      1.791759
+4      1.791759
+7      1.791759
+8      1.791759
+
+-- !V3_none_false_terms --
+1      1.791759
+2      1.791759
+3      1.791759
+4      1.791759
+7      1.791759
+8      1.791759
+
+-- !V3_none_false_ascending --
+1      1.791759
+2      1.791759
+3      1.791759
+4      1.791759
+7      1.791759
+8      1.791759
+
+-- !V3_none_false_missing --
+
+-- !V3_none_true_regexp --
+1      1.791759
+2      1.791759
+3      1.791759
+4      1.791759
+7      1.791759
+8      1.791759
+
+-- !V3_none_true_wildcard --
+1      1.791759
+2      1.791759
+3      1.791759
+4      1.791759
+7      1.791759
+8      1.791759
+
+-- !V3_none_true_terms --
+1      1.791759
+2      1.791759
+3      1.791759
+4      1.791759
+7      1.791759
+8      1.791759
+
+-- !V3_none_true_ascending --
+1      1.791759
+2      1.791759
+3      1.791759
+4      1.791759
+7      1.791759
+8      1.791759
+
+-- !V3_none_true_missing --
+
diff --git 
a/regression-test/suites/search/test_search_score_regexp_wildcard.groovy 
b/regression-test/suites/search/test_search_score_regexp_wildcard.groovy
new file mode 100644
index 00000000000..01cd72c70a7
--- /dev/null
+++ b/regression-test/suites/search/test_search_score_regexp_wildcard.groovy
@@ -0,0 +1,80 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+suite("test_search_score_regexp_wildcard", "p0") {
+    sql "set enable_common_expr_pushdown = true"
+    sql "set enable_inverted_index_query = true"
+
+    def scoredQuery = { predicate, direction, limit ->
+        """
+            SELECT id, ROUND(bm25_score, 6)
+            FROM (
+                SELECT id, score() AS bm25_score
+                FROM test_search_score_regexp_wildcard
+                WHERE ${predicate}
+                ORDER BY bm25_score ${direction} LIMIT ${limit}
+            ) hits
+            ORDER BY bm25_score ${direction}, id
+        """
+    }
+    for (String storageFormat : ["V2", "V3"]) {
+        for (String parser : ["standard", "none"]) {
+            sql "DROP TABLE IF EXISTS test_search_score_regexp_wildcard"
+            sql """
+                CREATE TABLE test_search_score_regexp_wildcard (
+                    id INT NOT NULL,
+                    content STRING NULL,
+                    INDEX idx_content(content) USING INVERTED 
PROPERTIES("parser" = "${parser}")
+                ) ENGINE=OLAP
+                DUPLICATE KEY(id)
+                DISTRIBUTED BY HASH(id) BUCKETS 1
+                PROPERTIES (
+                    "replication_allocation" = "tag.location.default:1",
+                    "inverted_index_storage_format" = "${storageFormat}"
+                )
+            """
+            sql """
+                INSERT INTO test_search_score_regexp_wildcard VALUES
+                    (1, 'apple'), (2, 'apple apple apple'), (3, 'application'),
+                    (4, 'apple application'), (5, 'banana'), (6, NULL),
+                    (7, 'apple application apple'), (8, 'apply apple')
+            """
+            sql "sync"
+            String regexp = "search('content:/app.*/', 
'{\"mode\":\"lucene\"}')"
+            String wildcard = "search('content:*pp*', '{\"mode\":\"lucene\"}')"
+            String terms = parser == "standard"
+                    ? "search('content:apple OR content:application OR 
content:apply', '{\"mode\":\"lucene\"}')"
+                    : "search('content:EXACT(apple) OR content:EXACT(apple 
apple apple) OR content:EXACT(application) OR content:EXACT(apple application) 
OR content:EXACT(apple application apple) OR content:EXACT(apply apple)', 
'{\"mode\":\"standard\"}')"
+            String label = "${storageFormat}_${parser}"
+            quickTest("${label}_unscored_regexp", """
+                SELECT id FROM test_search_score_regexp_wildcard WHERE 
${regexp} ORDER BY id
+            """)
+            quickTest("${label}_unscored_wildcard", """
+                SELECT id FROM test_search_score_regexp_wildcard WHERE 
${wildcard} ORDER BY id
+            """)
+            for (boolean wand : [false, true]) {
+                sql "set enable_inverted_index_wand_query = ${wand}"
+                quickTest("${label}_${wand}_regexp", scoredQuery(regexp, 
"DESC", 100))
+                quickTest("${label}_${wand}_wildcard", scoredQuery(wildcard, 
"DESC", 100))
+                quickTest("${label}_${wand}_terms", scoredQuery(terms, "DESC", 
100))
+                quickTest("${label}_${wand}_ascending", scoredQuery(regexp, 
"ASC", 100))
+                quickTest("${label}_${wand}_missing", scoredQuery(
+                        "search('content:/missing.*/', 
'{\"mode\":\"lucene\"}')", "DESC", 100))
+            }
+        }
+    }
+}


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

Reply via email to