This is an automated email from the ASF dual-hosted git repository.
airborne pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/doris.git
The following commit(s) were added to refs/heads/master by this push:
new c4ff082d9c3 [fix](search) fix mow support for search function (#56927)
c4ff082d9c3 is described below
commit c4ff082d9c3ba9275e7ba88c881b7320029a2d19
Author: Jack <[email protected]>
AuthorDate: Wed Oct 15 11:00:03 2025 +0800
[fix](search) fix mow support for search function (#56927)
---
.../nereids/rules/analysis/CheckSearchUsage.java | 29 ++++++--
.../suites/search/test_search_mow_support.groovy | 78 ++++++++++++++++++++++
2 files changed, 101 insertions(+), 6 deletions(-)
diff --git
a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/analysis/CheckSearchUsage.java
b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/analysis/CheckSearchUsage.java
index 0fd66ef0618..40fc84e41f2 100644
---
a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/analysis/CheckSearchUsage.java
+++
b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/analysis/CheckSearchUsage.java
@@ -30,6 +30,8 @@ import
org.apache.doris.nereids.trees.plans.logical.LogicalOlapScan;
import org.apache.doris.nereids.trees.plans.logical.LogicalProject;
import com.google.common.collect.ImmutableList;
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
import java.util.List;
@@ -39,15 +41,16 @@ import java.util.List;
* Must run in analysis phase before search() gets optimized away.
*/
public class CheckSearchUsage implements AnalysisRuleFactory {
+ private static final Logger LOG =
LogManager.getLogger(CheckSearchUsage.class);
@Override
public List<Rule> buildRules() {
return ImmutableList.of(
- any().thenApply(ctx -> {
- Plan plan = ctx.root;
- checkPlanRecursively(plan);
- return plan;
- }).toRule(RuleType.CHECK_SEARCH_USAGE)
+ any().thenApply(ctx -> {
+ Plan plan = ctx.root;
+ checkPlanRecursively(plan);
+ return plan;
+ }).toRule(RuleType.CHECK_SEARCH_USAGE)
);
}
@@ -93,9 +96,10 @@ public class CheckSearchUsage implements AnalysisRuleFactory
{
}
private void validateSearchUsage(Plan plan) {
+ LOG.debug("validateSearchUsage: {}", plan.treeString());
if (plan instanceof LogicalFilter) {
Plan child = plan.child(0);
- if (!(child instanceof LogicalOlapScan)) {
+ if (!isSingleTableScanPipeline(child)) {
throw new AnalysisException("search() predicate only supports
filtering directly on a single "
+ "table scan; remove joins, subqueries, or additional
operators between search() "
+ "and the target table");
@@ -127,4 +131,17 @@ public class CheckSearchUsage implements
AnalysisRuleFactory {
}
return false;
}
+
+ private boolean isSingleTableScanPipeline(Plan plan) {
+ Plan current = plan;
+ while (true) {
+ if (current instanceof LogicalOlapScan) {
+ return true;
+ }
+ if (current.arity() != 1) {
+ return false;
+ }
+ current = current.child(0);
+ }
+ }
}
diff --git a/regression-test/suites/search/test_search_mow_support.groovy
b/regression-test/suites/search/test_search_mow_support.groovy
new file mode 100644
index 00000000000..ce759ad99c9
--- /dev/null
+++ b/regression-test/suites/search/test_search_mow_support.groovy
@@ -0,0 +1,78 @@
+// 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_mow_support") {
+ def tableName = "search_mow_support_tbl"
+ sql "DROP TABLE IF EXISTS ${tableName}"
+
+ sql """
+ CREATE TABLE ${tableName} (
+ k1 BIGINT,
+ title VARCHAR(256),
+ desc_text TEXT,
+ INDEX idx_title (title) USING INVERTED PROPERTIES("parser" =
"english")
+ ) ENGINE=OLAP
+ UNIQUE KEY(k1)
+ DISTRIBUTED BY HASH(k1) BUCKETS 1
+ PROPERTIES (
+ "replication_allocation" = "tag.location.default: 1",
+ "enable_unique_key_merge_on_write" = "true"
+ )
+ """
+
+ // initial rows
+ sql """
+ INSERT INTO ${tableName} VALUES
+ (1, 'Rainbowman', 'first version'),
+ (2, 'Other Title', 'unrelated row'),
+ (3, 'Rainbowman Story', 'should not match exact search')
+ """
+
+ // update existing key to exercise delete bitmap handling
+ sql """
+ INSERT INTO ${tableName} VALUES
+ (1, 'Rainbowman', 'updated version'),
+ (4, 'rainbowman lowercase', 'case variants')
+ """
+
+ sql "SET enable_common_expr_pushdown = true"
+ sql "SET enable_common_expr_pushdown_for_inverted_index = true"
+
+ def exactCount = sql """
+ SELECT /*+SET_VAR(enable_inverted_index_query=true) */ count(*)
+ FROM ${tableName}
+ WHERE search('title:ALL("Rainbowman")')
+ """
+ assert exactCount[0][0] == 3
+
+ def exactRows = sql """
+ SELECT /*+SET_VAR(enable_inverted_index_query=true) */ k1, title
+ FROM ${tableName}
+ WHERE search('title:ALL("Rainbowman")')
+ ORDER BY k1
+ """
+ assert exactRows.size() == 3
+ assert exactRows[0][0] == 1
+ assert exactRows[0][1] == 'Rainbowman'
+
+ def anyCount = sql """
+ SELECT /*+SET_VAR(enable_inverted_index_query=true) */ count(*)
+ FROM ${tableName}
+ WHERE search('title:ANY("rainbowman")')
+ """
+ assert anyCount[0][0] == 3
+}
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]