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 21db7165cea branch-4.1: [fix](be) Disable DSL cache for score queries 
#65436 (#65590)
21db7165cea is described below

commit 21db7165cea8623804dd233ceca1900c2e1fccf3
Author: github-actions[bot] 
<41898282+github-actions[bot]@users.noreply.github.com>
AuthorDate: Wed Jul 15 11:47:58 2026 +0800

    branch-4.1: [fix](be) Disable DSL cache for score queries #65436 (#65590)
    
    Cherry-picked from #65436
    
    Co-authored-by: liangj777 
<[email protected]>
---
 be/src/exprs/function/function_search.cpp          |   9 +-
 .../data/search/test_search_score_cache.out        |  42 ++++
 .../suites/search/test_search_cache.groovy         |  30 +++
 .../suites/search/test_search_score_cache.groovy   | 254 +++++++++++++++++++++
 4 files changed, 333 insertions(+), 2 deletions(-)

diff --git a/be/src/exprs/function/function_search.cpp 
b/be/src/exprs/function/function_search.cpp
index 9c33752598c..e87aff95ee0 100644
--- a/be/src/exprs/function/function_search.cpp
+++ b/be/src/exprs/function/function_search.cpp
@@ -274,8 +274,13 @@ Status 
FunctionSearch::evaluate_inverted_index_with_search_param(
     OlapReaderStatistics* outer_stats = index_query_context ? 
index_query_context->stats : nullptr;
     SCOPED_RAW_TIMER(outer_stats ? &outer_stats->inverted_index_query_timer : 
&query_timer_dummy);
 
-    // DSL result cache: reuse InvertedIndexQueryCache with SEARCH_DSL_QUERY 
type
-    auto* dsl_cache = enable_cache ? InvertedIndexQueryCache::instance() : 
nullptr;
+    const bool need_similarity_score =
+            index_query_context && index_query_context->collection_similarity;
+
+    // DSL result cache only stores bitmap/null bitmap. It does not store BM25 
scores,
+    // so score() queries must execute scorers to populate 
CollectionSimilarity.
+    auto* dsl_cache = (enable_cache && !need_similarity_score) ? 
InvertedIndexQueryCache::instance()
+                                                               : nullptr;
     std::string seg_prefix;
     std::string dsl_sig;
     InvertedIndexQueryCache::CacheKey dsl_cache_key;
diff --git a/regression-test/data/search/test_search_score_cache.out 
b/regression-test/data/search/test_search_score_cache.out
new file mode 100644
index 00000000000..58f33cb879d
--- /dev/null
+++ b/regression-test/data/search/test_search_score_cache.out
@@ -0,0 +1,42 @@
+-- This file is automatically generated. You should know what you did if you 
want to edit this
+-- !search_dsl_score --
+8
+1
+2
+4
+6
+
+-- !match_any_score --
+8
+1
+2
+4
+6
+
+-- !match_all_score --
+2
+1
+
+-- !match_phrase_score --
+2
+1
+
+-- !match_any_with_filter_score --
+8
+1
+2
+4
+6
+
+-- !variant_match_phrase_score --
+2
+4
+
+-- !union_all_score --
+3
+7
+8
+1
+2
+2
+
diff --git a/regression-test/suites/search/test_search_cache.groovy 
b/regression-test/suites/search/test_search_cache.groovy
index 39af2184bb7..4ebb5c173a2 100644
--- a/regression-test/suites/search/test_search_cache.groovy
+++ b/regression-test/suites/search/test_search_cache.groovy
@@ -134,5 +134,35 @@ suite("test_search_cache", "p0") {
     """
     assertEquals(complex_cached, complex_uncached)
 
+    // Test 7: SEARCH DSL + score() must not reuse bitmap-only DSL cache.
+    // A warmed DSL cache or repeated score query would return 0.0 scores if 
cache hit
+    // skipped scorer collection.
+    def score_cached_1 = sql """
+        SELECT 
/*+SET_VAR(enable_common_expr_pushdown=true,enable_inverted_index_query_cache=true)
 */
+        id, score() AS s FROM ${tableName}
+        WHERE search('title:apple')
+        ORDER BY s DESC
+        LIMIT 10
+    """
+
+    def score_cached_2 = sql """
+        SELECT 
/*+SET_VAR(enable_common_expr_pushdown=true,enable_inverted_index_query_cache=true)
 */
+        id, score() AS s FROM ${tableName}
+        WHERE search('title:apple')
+        ORDER BY s DESC
+        LIMIT 10
+    """
+
+    assertEquals(score_cached_1.size(), score_cached_2.size())
+    assertTrue(score_cached_1.size() > 0)
+    assertEquals(score_cached_1.collect { it[0] as int }.sort(),
+            score_cached_2.collect { it[0] as int }.sort())
+    for (def row : score_cached_1) {
+        assertTrue(Double.parseDouble(row[1].toString()) > 0.0)
+    }
+    for (def row : score_cached_2) {
+        assertTrue(Double.parseDouble(row[1].toString()) > 0.0)
+    }
+
     sql "DROP TABLE IF EXISTS ${tableName}"
 }
diff --git a/regression-test/suites/search/test_search_score_cache.groovy 
b/regression-test/suites/search/test_search_score_cache.groovy
new file mode 100644
index 00000000000..caa3259195d
--- /dev/null
+++ b/regression-test/suites/search/test_search_score_cache.groovy
@@ -0,0 +1,254 @@
+// 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_cache", "p0") {
+    def tableName = "search_score_cache_test"
+
+    def assertPositiveScores = { result ->
+        assertTrue(result.size() > 0)
+        for (def row : result) {
+            assertTrue(Double.parseDouble(row[1].toString()) > 0.0)
+        }
+    }
+
+    def assertStablePositiveScoreQuery = { tag, warmSql, scoreSql ->
+        def warmResult1 = sql warmSql
+        def warmResult2 = sql warmSql
+        assertEquals(warmResult1, warmResult2)
+
+        def scoreResult1 = sql scoreSql
+        def scoreResult2 = sql scoreSql
+        assertEquals(scoreResult1.collect { it[0] as int }, 
scoreResult2.collect { it[0] as int })
+        assertPositiveScores(scoreResult1)
+        assertPositiveScores(scoreResult2)
+
+        quickTest(tag, """
+            SELECT id
+            FROM (${scoreSql}) t
+            ORDER BY s DESC
+        """)
+    }
+
+    sql "DROP TABLE IF EXISTS ${tableName}"
+    sql """
+        CREATE TABLE ${tableName} (
+            id INT,
+            status INT,
+            title VARCHAR(200),
+            content VARCHAR(500),
+            v VARIANT,
+            INDEX idx_title(title) USING INVERTED PROPERTIES(
+                "parser" = "english",
+                "support_phrase" = "true"
+            ),
+            INDEX idx_content(content) USING INVERTED PROPERTIES(
+                "parser" = "english",
+                "support_phrase" = "true"
+            ),
+            INDEX idx_v(v) USING INVERTED PROPERTIES(
+                "parser" = "english",
+                "support_phrase" = "true"
+            )
+        ) ENGINE=OLAP
+        DUPLICATE KEY(id)
+        DISTRIBUTED BY HASH(id) BUCKETS 1
+        PROPERTIES ("replication_num" = "1")
+    """
+
+    sql """INSERT INTO ${tableName} VALUES
+        (1, 1, 'apple apple apple banana cherry', 'red fruit sweet apple', 
'{"host":"apple banana server"}'),
+        (2, 1, 'apple apple banana date', 'fresh apple banana salad', 
'{"host":"apple server cluster"}'),
+        (3, 0, 'banana banana banana grape mango', 'yellow fruit tropical', 
'{"host":"banana server"}'),
+        (4, 1, 'apple grape kiwi', 'green fruit fresh apple', '{"host":"green 
green apple server"}'),
+        (5, 0, 'mango pineapple coconut', 'tropical fruit exotic', 
'{"host":"mango server"}'),
+        (6, 1, 'apple cherry plum apricot fig', 'mixed fruit apple salad', 
'{"host":"apple cherry node"}'),
+        (7, 0, 'banana banana coconut papaya', 'smoothie blend tropical', 
'{"host":"banana coconut node"}'),
+        (8, 1, 'grape cherry apple apple apple apple', 'wine fruit tart 
apple', '{"host":"grape apple node"}')
+    """
+    sql "sync"
+
+    sql """ set enable_common_expr_pushdown = true """
+    sql """ set enable_inverted_index_query_cache = true """
+
+    // SEARCH DSL + score() must execute scorers even when the DSL bitmap 
cache is warm.
+    assertStablePositiveScoreQuery(
+        "search_dsl_score",
+        """
+            SELECT 
/*+SET_VAR(enable_common_expr_pushdown=true,enable_inverted_index_query_cache=true)
 */
+                   id
+            FROM ${tableName}
+            WHERE search('title:apple')
+            ORDER BY id
+        """,
+        """
+            SELECT 
/*+SET_VAR(enable_common_expr_pushdown=true,enable_inverted_index_query_cache=true)
 */
+                   id, score() AS s
+            FROM ${tableName}
+            WHERE search('title:apple')
+            ORDER BY s DESC
+            LIMIT 10
+        """
+    )
+
+    // MATCH_ANY + score() must bypass bitmap-only inverted index query cache.
+    assertStablePositiveScoreQuery(
+        "match_any_score",
+        """
+            SELECT 
/*+SET_VAR(enable_common_expr_pushdown=true,enable_inverted_index_query_cache=true)
 */
+                   id
+            FROM ${tableName}
+            WHERE title MATCH_ANY 'apple'
+            ORDER BY id
+        """,
+        """
+            SELECT 
/*+SET_VAR(enable_common_expr_pushdown=true,enable_inverted_index_query_cache=true)
 */
+                   id, score() AS s
+            FROM ${tableName}
+            WHERE title MATCH_ANY 'apple'
+            ORDER BY s DESC
+            LIMIT 10
+        """
+    )
+
+    // MATCH_ALL + score() should still produce collected BM25 scores after 
cache warmup.
+    assertStablePositiveScoreQuery(
+        "match_all_score",
+        """
+            SELECT 
/*+SET_VAR(enable_common_expr_pushdown=true,enable_inverted_index_query_cache=true)
 */
+                   id
+            FROM ${tableName}
+            WHERE title MATCH_ALL 'apple banana'
+            ORDER BY id
+        """,
+        """
+            SELECT 
/*+SET_VAR(enable_common_expr_pushdown=true,enable_inverted_index_query_cache=true)
 */
+                   id, score() AS s
+            FROM ${tableName}
+            WHERE title MATCH_ALL 'apple banana'
+            ORDER BY s DESC
+            LIMIT 10
+        """
+    )
+
+    // MATCH_PHRASE + score() should not materialize default 0 scores from 
cached bitmaps.
+    assertStablePositiveScoreQuery(
+        "match_phrase_score",
+        """
+            SELECT 
/*+SET_VAR(enable_common_expr_pushdown=true,enable_inverted_index_query_cache=true)
 */
+                   id
+            FROM ${tableName}
+            WHERE title MATCH_PHRASE 'apple banana'
+            ORDER BY id
+        """,
+        """
+            SELECT 
/*+SET_VAR(enable_common_expr_pushdown=true,enable_inverted_index_query_cache=true)
 */
+                   id, score() AS s
+            FROM ${tableName}
+            WHERE title MATCH_PHRASE 'apple banana'
+            ORDER BY s DESC
+            LIMIT 10
+        """
+    )
+
+
+    // score() with ordinary filters should still execute scorers after the 
indexed bitmap cache is warm.
+    assertStablePositiveScoreQuery(
+        "match_any_with_filter_score",
+        """
+            SELECT 
/*+SET_VAR(enable_common_expr_pushdown=true,enable_inverted_index_query_cache=true)
 */
+                   id
+            FROM ${tableName}
+            WHERE title MATCH_ANY 'apple' AND status = 1
+            ORDER BY id
+        """,
+        """
+            SELECT 
/*+SET_VAR(enable_common_expr_pushdown=true,enable_inverted_index_query_cache=true)
 */
+                   id, score() AS s
+            FROM ${tableName}
+            WHERE title MATCH_ANY 'apple' AND status = 1
+            ORDER BY s DESC
+            LIMIT 10
+        """
+    )
+
+    // score() on VARIANT subfields must not fall back to default 0 after 
cache warmup.
+    assertStablePositiveScoreQuery(
+        "variant_match_phrase_score",
+        """
+            SELECT 
/*+SET_VAR(enable_common_expr_pushdown=true,enable_inverted_index_query_cache=true)
 */
+                   id
+            FROM ${tableName}
+            WHERE CAST(v["host"] AS STRING) MATCH_PHRASE 'apple server'
+            ORDER BY id
+        """,
+        """
+            SELECT 
/*+SET_VAR(enable_common_expr_pushdown=true,enable_inverted_index_query_cache=true)
 */
+                   id, score() AS s
+            FROM ${tableName}
+            WHERE CAST(v["host"] AS STRING) MATCH_PHRASE 'apple server'
+            ORDER BY s DESC
+            LIMIT 10
+        """
+    )
+
+    // UNION ALL branches each have their own score() pushdown and must 
collect non-zero scores.
+    def unionWarmSql = """
+        (
+            SELECT 
/*+SET_VAR(enable_common_expr_pushdown=true,enable_inverted_index_query_cache=true)
 */
+                   id
+            FROM ${tableName}
+            WHERE title MATCH_ANY 'apple'
+            ORDER BY id
+            LIMIT 3
+        )
+        UNION ALL
+        (
+            SELECT 
/*+SET_VAR(enable_common_expr_pushdown=true,enable_inverted_index_query_cache=true)
 */
+                   id
+            FROM ${tableName}
+            WHERE title MATCH_ANY 'banana'
+            ORDER BY id
+            LIMIT 3
+        )
+        ORDER BY id
+    """
+    def unionScoreSql = """
+        SELECT * FROM (
+            (
+                SELECT 
/*+SET_VAR(enable_common_expr_pushdown=true,enable_inverted_index_query_cache=true)
 */
+                       id, score() AS s
+                FROM ${tableName}
+                WHERE title MATCH_ANY 'apple'
+                ORDER BY s DESC
+                LIMIT 3
+            )
+            UNION ALL
+            (
+                SELECT 
/*+SET_VAR(enable_common_expr_pushdown=true,enable_inverted_index_query_cache=true)
 */
+                       id, score() AS s
+                FROM ${tableName}
+                WHERE title MATCH_ANY 'banana'
+                ORDER BY s DESC
+                LIMIT 3
+            )
+        ) t
+        ORDER BY s DESC
+    """
+    assertStablePositiveScoreQuery("union_all_score", unionWarmSql, 
unionScoreSql)
+
+    sql "DROP TABLE IF EXISTS ${tableName}"
+}


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

Reply via email to