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 5ba07538a70 [improvement](search) change default mode of search
function from standard to lucene (#61055)
5ba07538a70 is described below
commit 5ba07538a7061cfc4ef1655a7576857acefba94f
Author: Jack <[email protected]>
AuthorDate: Mon Mar 9 14:21:58 2026 +0800
[improvement](search) change default mode of search function from standard
to lucene (#61055)
### What problem does this PR solve?
Issue Number: close #xxx
Problem Summary:
Change the default `mode` of the `search()` function from `"standard"`
to `"lucene"`.
Previously, when users called `search(dsl)` or `search(dsl, options)`
without explicitly specifying the `mode` parameter, it defaulted to
`"standard"` mode. This PR changes the default to `"lucene"` mode
(ES/Lucene-style boolean parsing), which is more commonly expected by
users familiar with Elasticsearch query_string syntax.
Users who need the standard mode can still explicitly specify
`'{"mode":"standard"}'` in the options parameter.
---
.../trees/expressions/functions/scalar/Search.java | 2 +-
.../functions/scalar/SearchDslParser.java | 4 +-
.../functions/scalar/SearchDslParserTest.java | 98 +++++++++++-----------
.../expressions/functions/scalar/SearchTest.java | 6 +-
.../test_complex_or_null_semantics.groovy | 31 +++++--
.../test_cross_field_or_with_null.groovy | 25 ++++--
.../test_search_or_null_semantics.groovy | 25 ++++--
.../search/test_search_boundary_cases.groovy | 32 +++----
.../search/test_search_exact_multi_index.groovy | 14 ++--
.../suites/search/test_search_multi_field.groovy | 40 ++++-----
.../search/test_search_null_regression.groovy | 38 ++++-----
.../search/test_search_null_semantics.groovy | 58 ++++++-------
.../search/test_search_vs_match_consistency.groovy | 62 +++++++-------
13 files changed, 240 insertions(+), 195 deletions(-)
diff --git
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/Search.java
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/Search.java
index 3a98a6cbf05..62512c8d3d9 100644
---
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/Search.java
+++
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/Search.java
@@ -41,7 +41,7 @@ import java.util.List;
* Options parameter (JSON format):
* - default_field: default field name when DSL doesn't specify field
* - default_operator: "and" or "or" for multi-term queries (default: "and")
- * - mode: "standard" (default) or "lucene" (ES/Lucene-style boolean parsing)
+ * - mode: "lucene" (default, ES/Lucene-style boolean parsing) or "standard"
* - minimum_should_match: integer for Lucene mode (default: 0 for filter
context)
* <p>
* Example options:
'{"default_field":"title","mode":"lucene","minimum_should_match":0}'
diff --git
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/SearchDslParser.java
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/SearchDslParser.java
index 3f5072b46b6..50610b44267 100644
---
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/SearchDslParser.java
+++
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/SearchDslParser.java
@@ -1768,7 +1768,7 @@ MATCH_ALL_DOCS, // Matches all documents (used for pure
NOT query rewriting)
* Supports all configuration in a single JSON object:
* - default_field: default field name when DSL doesn't specify field
* - default_operator: "and" or "or" for multi-term queries (default: "or")
- * - mode: "standard" (default) or "lucene" (ES/Lucene-style boolean
parsing)
+ * - mode: "lucene" (default, ES/Lucene-style boolean parsing) or
"standard"
* - minimum_should_match: integer for Lucene mode (default: 0 for filter
context)
* - fields: array of field names for multi-field search (mutually
exclusive with default_field)
*/
@@ -1780,7 +1780,7 @@ MATCH_ALL_DOCS, // Matches all documents (used for pure
NOT query rewriting)
private String defaultOperator = null;
@JsonProperty("mode")
- private String mode = "standard";
+ private String mode = "lucene";
@JsonProperty("minimum_should_match")
private Integer minimumShouldMatch = null;
diff --git
a/fe/fe-core/src/test/java/org/apache/doris/nereids/trees/expressions/functions/scalar/SearchDslParserTest.java
b/fe/fe-core/src/test/java/org/apache/doris/nereids/trees/expressions/functions/scalar/SearchDslParserTest.java
index 359f3d86f8a..c4e75d26c74 100644
---
a/fe/fe-core/src/test/java/org/apache/doris/nereids/trees/expressions/functions/scalar/SearchDslParserTest.java
+++
b/fe/fe-core/src/test/java/org/apache/doris/nereids/trees/expressions/functions/scalar/SearchDslParserTest.java
@@ -160,7 +160,7 @@ public class SearchDslParserTest {
@Test
public void testAndQuery() {
String dsl = "title:hello AND content:world";
- QsPlan plan = SearchDslParser.parseDsl(dsl);
+ QsPlan plan = SearchDslParser.parseDsl(dsl, "{\"mode\":\"standard\"}");
Assertions.assertNotNull(plan);
Assertions.assertEquals(QsClauseType.AND, plan.getRoot().getType());
@@ -185,7 +185,7 @@ public class SearchDslParserTest {
@Test
public void testOrQuery() {
String dsl = "title:hello OR title:hi";
- QsPlan plan = SearchDslParser.parseDsl(dsl);
+ QsPlan plan = SearchDslParser.parseDsl(dsl, "{\"mode\":\"standard\"}");
Assertions.assertNotNull(plan);
Assertions.assertEquals(QsClauseType.OR, plan.getRoot().getType());
@@ -195,7 +195,7 @@ public class SearchDslParserTest {
@Test
public void testNotQuery() {
String dsl = "NOT title:spam";
- QsPlan plan = SearchDslParser.parseDsl(dsl);
+ QsPlan plan = SearchDslParser.parseDsl(dsl, "{\"mode\":\"standard\"}");
Assertions.assertNotNull(plan);
Assertions.assertEquals(QsClauseType.NOT, plan.getRoot().getType());
@@ -210,7 +210,7 @@ public class SearchDslParserTest {
@Test
public void testComplexQuery() {
String dsl = "(title:\"machine learning\" OR content:AI) AND NOT
category:spam";
- QsPlan plan = SearchDslParser.parseDsl(dsl);
+ QsPlan plan = SearchDslParser.parseDsl(dsl, "{\"mode\":\"standard\"}");
Assertions.assertNotNull(plan);
Assertions.assertEquals(QsClauseType.AND, plan.getRoot().getType());
@@ -864,9 +864,9 @@ public class SearchDslParserTest {
@Test
public void testStandardModeUnchanged() {
- // Test: standard mode (default) should work as before
+ // Test: standard mode (explicit) should work as before
String dsl = "field:a AND field:b OR field:c";
- QsPlan plan = SearchDslParser.parseDsl(dsl, (String) null);
+ QsPlan plan = SearchDslParser.parseDsl(dsl, "{\"mode\":\"standard\"}");
Assertions.assertNotNull(plan);
// Standard mode uses traditional boolean algebra: OR at top level
@@ -885,9 +885,9 @@ public class SearchDslParserTest {
@Test
public void testLuceneModeEmptyOptions() {
- // Test: empty options string should use standard mode
+ // Test: explicit standard mode should work
String dsl = "field:a AND field:b";
- QsPlan plan = SearchDslParser.parseDsl(dsl, "");
+ QsPlan plan = SearchDslParser.parseDsl(dsl, "{\"mode\":\"standard\"}");
Assertions.assertNotNull(plan);
Assertions.assertEquals(QsClauseType.AND, plan.getRoot().getType());
@@ -1097,7 +1097,7 @@ public class SearchDslParserTest {
public void testUppercaseAndOperator() {
// Test: uppercase AND should be treated as operator
String dsl = "field:a AND field:b";
- QsPlan plan = SearchDslParser.parseDsl(dsl);
+ QsPlan plan = SearchDslParser.parseDsl(dsl, "{\"mode\":\"standard\"}");
Assertions.assertNotNull(plan);
Assertions.assertEquals(QsClauseType.AND, plan.getRoot().getType());
@@ -1119,7 +1119,7 @@ public class SearchDslParserTest {
public void testUppercaseOrOperator() {
// Test: uppercase OR should be treated as operator
String dsl = "field:a OR field:b";
- QsPlan plan = SearchDslParser.parseDsl(dsl);
+ QsPlan plan = SearchDslParser.parseDsl(dsl, "{\"mode\":\"standard\"}");
Assertions.assertNotNull(plan);
Assertions.assertEquals(QsClauseType.OR, plan.getRoot().getType());
@@ -1141,7 +1141,7 @@ public class SearchDslParserTest {
public void testUppercaseNotOperator() {
// Test: uppercase NOT should be treated as operator
String dsl = "NOT field:spam";
- QsPlan plan = SearchDslParser.parseDsl(dsl);
+ QsPlan plan = SearchDslParser.parseDsl(dsl, "{\"mode\":\"standard\"}");
Assertions.assertNotNull(plan);
Assertions.assertEquals(QsClauseType.NOT, plan.getRoot().getType());
@@ -1162,7 +1162,7 @@ public class SearchDslParserTest {
public void testExclamationNotOperator() {
// Test: ! should be treated as NOT operator
String dsl = "!field:spam";
- QsPlan plan = SearchDslParser.parseDsl(dsl);
+ QsPlan plan = SearchDslParser.parseDsl(dsl, "{\"mode\":\"standard\"}");
Assertions.assertNotNull(plan);
// Current behavior: ! IS a NOT operator
@@ -1199,7 +1199,7 @@ public class SearchDslParserTest {
public void testMultiFieldSimpleTerm() {
// Test: "hello" + fields=["title","content"] → "(title:hello OR
content:hello)"
String dsl = "hello";
- String options = "{\"fields\":[\"title\",\"content\"]}";
+ String options = "{\"mode\": \"standard\", \"fields\": [\"title\",
\"content\"]}";
QsPlan plan = SearchDslParser.parseDsl(dsl, options);
Assertions.assertNotNull(plan);
@@ -1219,7 +1219,7 @@ public class SearchDslParserTest {
// Test: "hello world" + fields=["title","content"] +
default_operator="and" + type="cross_fields"
// → "(title:hello OR content:hello) AND (title:world OR
content:world)"
String dsl = "hello world";
- String options =
"{\"fields\":[\"title\",\"content\"],\"default_operator\":\"and\",\"type\":\"cross_fields\"}";
+ String options = "{\"mode\": \"standard\", \"fields\": [\"title\",
\"content\"], \"default_operator\": \"and\", \"type\": \"cross_fields\"}";
QsPlan plan = SearchDslParser.parseDsl(dsl, options);
Assertions.assertNotNull(plan);
@@ -1238,7 +1238,7 @@ public class SearchDslParserTest {
// Test: "hello world" + fields=["title","content"] +
default_operator="or"
// → "(title:hello OR content:hello) OR (title:world OR content:world)"
String dsl = "hello world";
- String options =
"{\"fields\":[\"title\",\"content\"],\"default_operator\":\"or\"}";
+ String options = "{\"mode\": \"standard\", \"fields\": [\"title\",
\"content\"], \"default_operator\": \"or\"}";
QsPlan plan = SearchDslParser.parseDsl(dsl, options);
Assertions.assertNotNull(plan);
@@ -1250,7 +1250,7 @@ public class SearchDslParserTest {
// Test: "hello AND world" + fields=["title","content"] + cross_fields
// → "(title:hello OR content:hello) AND (title:world OR
content:world)"
String dsl = "hello AND world";
- String options =
"{\"fields\":[\"title\",\"content\"],\"type\":\"cross_fields\"}";
+ String options = "{\"mode\": \"standard\", \"fields\": [\"title\",
\"content\"], \"type\": \"cross_fields\"}";
QsPlan plan = SearchDslParser.parseDsl(dsl, options);
Assertions.assertNotNull(plan);
@@ -1262,7 +1262,7 @@ public class SearchDslParserTest {
// Test: "hello AND category:tech" + fields=["title","content"] +
cross_fields
// → "(title:hello OR content:hello) AND category:tech"
String dsl = "hello AND category:tech";
- String options =
"{\"fields\":[\"title\",\"content\"],\"type\":\"cross_fields\"}";
+ String options = "{\"mode\": \"standard\", \"fields\": [\"title\",
\"content\"], \"type\": \"cross_fields\"}";
QsPlan plan = SearchDslParser.parseDsl(dsl, options);
Assertions.assertNotNull(plan);
@@ -1281,7 +1281,7 @@ public class SearchDslParserTest {
// "title:music AND content:history" with fields=["title","content"]
// → title:music AND content:history (NOT expanded to multi-field OR)
String dsl = "title:music AND content:history";
- String options =
"{\"fields\":[\"title\",\"content\"],\"type\":\"cross_fields\"}";
+ String options = "{\"mode\": \"standard\", \"fields\": [\"title\",
\"content\"], \"type\": \"cross_fields\"}";
QsPlan plan = SearchDslParser.parseDsl(dsl, options);
Assertions.assertNotNull(plan);
@@ -1305,7 +1305,7 @@ public class SearchDslParserTest {
public void testMultiFieldExplicitFieldInFieldsListBestFields() {
// Same test as above but with best_fields type
String dsl = "title:music AND content:history";
- String options =
"{\"fields\":[\"title\",\"content\"],\"type\":\"best_fields\"}";
+ String options = "{\"mode\": \"standard\", \"fields\": [\"title\",
\"content\"], \"type\": \"best_fields\"}";
QsPlan plan = SearchDslParser.parseDsl(dsl, options);
Assertions.assertNotNull(plan);
@@ -1335,7 +1335,7 @@ public class SearchDslParserTest {
// → title:football AND (title:american OR content:american)
// title:football should NOT be expanded; "american" (bare) should be
expanded
String dsl = "title:football AND american";
- String options =
"{\"fields\":[\"title\",\"content\"],\"type\":\"cross_fields\"}";
+ String options = "{\"mode\": \"standard\", \"fields\": [\"title\",
\"content\"], \"type\": \"cross_fields\"}";
QsPlan plan = SearchDslParser.parseDsl(dsl, options);
Assertions.assertNotNull(plan);
@@ -1384,7 +1384,7 @@ public class SearchDslParserTest {
public void testMultiFieldWithWildcard() {
// Test: "hello*" + fields=["title","content"]
String dsl = "hello*";
- String options = "{\"fields\":[\"title\",\"content\"]}";
+ String options = "{\"mode\": \"standard\", \"fields\": [\"title\",
\"content\"]}";
QsPlan plan = SearchDslParser.parseDsl(dsl, options);
Assertions.assertNotNull(plan);
@@ -1401,7 +1401,7 @@ public class SearchDslParserTest {
public void testMultiFieldWithExactFunction() {
// Test: "EXACT(foo bar)" + fields=["title","content"]
String dsl = "EXACT(foo bar)";
- String options = "{\"fields\":[\"title\",\"content\"]}";
+ String options = "{\"mode\": \"standard\", \"fields\": [\"title\",
\"content\"]}";
QsPlan plan = SearchDslParser.parseDsl(dsl, options);
Assertions.assertNotNull(plan);
@@ -1418,7 +1418,7 @@ public class SearchDslParserTest {
public void testMultiFieldThreeFields() {
// Test: "hello" + fields=["title","content","tags"]
String dsl = "hello";
- String options = "{\"fields\":[\"title\",\"content\",\"tags\"]}";
+ String options = "{\"mode\": \"standard\", \"fields\": [\"title\",
\"content\", \"tags\"]}";
QsPlan plan = SearchDslParser.parseDsl(dsl, options);
Assertions.assertNotNull(plan);
@@ -1457,7 +1457,7 @@ public class SearchDslParserTest {
// Test: "NOT hello" + fields=["title","content"] with cross_fields
type
// cross_fields: NOT (title:hello OR content:hello)
String dsl = "NOT hello";
- String options =
"{\"fields\":[\"title\",\"content\"],\"type\":\"cross_fields\"}";
+ String options = "{\"mode\": \"standard\", \"fields\": [\"title\",
\"content\"], \"type\": \"cross_fields\"}";
QsPlan plan = SearchDslParser.parseDsl(dsl, options);
Assertions.assertNotNull(plan);
@@ -1604,7 +1604,7 @@ public class SearchDslParserTest {
// "hello world" with fields ["title", "content"] and default_operator
"and"
// Expands to: (title:hello AND title:world) OR (content:hello AND
content:world)
String dsl = "hello world";
- String options =
"{\"fields\":[\"title\",\"content\"],\"default_operator\":\"and\"}";
+ String options = "{\"mode\": \"standard\", \"fields\": [\"title\",
\"content\"], \"default_operator\": \"and\"}";
QsPlan plan = SearchDslParser.parseDsl(dsl, options);
Assertions.assertNotNull(plan);
@@ -1623,7 +1623,7 @@ public class SearchDslParserTest {
public void testMultiFieldBestFieldsExplicit() {
// Test: explicitly specify type=best_fields
String dsl = "hello world";
- String options =
"{\"fields\":[\"title\",\"content\"],\"default_operator\":\"and\",\"type\":\"best_fields\"}";
+ String options = "{\"mode\": \"standard\", \"fields\": [\"title\",
\"content\"], \"default_operator\": \"and\", \"type\": \"best_fields\"}";
QsPlan plan = SearchDslParser.parseDsl(dsl, options);
Assertions.assertNotNull(plan);
@@ -1637,7 +1637,7 @@ public class SearchDslParserTest {
// "hello world" with fields ["title", "content"] and default_operator
"and"
// Expands to: (title:hello OR content:hello) AND (title:world OR
content:world)
String dsl = "hello world";
- String options =
"{\"fields\":[\"title\",\"content\"],\"default_operator\":\"and\",\"type\":\"cross_fields\"}";
+ String options = "{\"mode\": \"standard\", \"fields\": [\"title\",
\"content\"], \"default_operator\": \"and\", \"type\": \"cross_fields\"}";
QsPlan plan = SearchDslParser.parseDsl(dsl, options);
Assertions.assertNotNull(plan);
@@ -1859,7 +1859,7 @@ public class SearchDslParserTest {
// Mixed: explicit field + bare query
// "content:/[a-z]+/ AND hello" where hello uses default_field=title
String dsl = "content:/[a-z]+/ AND hello";
- String options = "{\"default_field\":\"title\"}";
+ String options = "{\"mode\": \"standard\", \"default_field\":
\"title\"}";
QsPlan plan = SearchDslParser.parseDsl(dsl, options);
Assertions.assertNotNull(plan);
@@ -1958,7 +1958,7 @@ public class SearchDslParserTest {
public void testBareAndOperatorWithDefaultField() {
// "hello AND world" with default_field
String dsl = "hello AND world";
- String options = "{\"default_field\":\"title\"}";
+ String options = "{\"mode\": \"standard\", \"default_field\":
\"title\"}";
QsPlan plan = SearchDslParser.parseDsl(dsl, options);
Assertions.assertNotNull(plan);
@@ -1975,7 +1975,7 @@ public class SearchDslParserTest {
public void testBareOrOperatorWithDefaultField() {
// "hello OR world" with default_field
String dsl = "hello OR world";
- String options = "{\"default_field\":\"title\"}";
+ String options = "{\"mode\": \"standard\", \"default_field\":
\"title\"}";
QsPlan plan = SearchDslParser.parseDsl(dsl, options);
Assertions.assertNotNull(plan);
@@ -1992,7 +1992,7 @@ public class SearchDslParserTest {
public void testBareNotOperatorWithDefaultField() {
// "NOT hello" with default_field
String dsl = "NOT hello";
- String options = "{\"default_field\":\"title\"}";
+ String options = "{\"mode\": \"standard\", \"default_field\":
\"title\"}";
QsPlan plan = SearchDslParser.parseDsl(dsl, options);
Assertions.assertNotNull(plan);
@@ -2020,7 +2020,7 @@ public class SearchDslParserTest {
public void testParenthesizedBareQuery() {
// "(hello OR world) AND foo" with default_field
String dsl = "(hello OR world) AND foo";
- String options = "{\"default_field\":\"title\"}";
+ String options = "{\"mode\": \"standard\", \"default_field\":
\"title\"}";
QsPlan plan = SearchDslParser.parseDsl(dsl, options);
Assertions.assertNotNull(plan);
@@ -2073,7 +2073,7 @@ public class SearchDslParserTest {
public void testPhraseWithImplicitAndOperator() {
// Test: '"hello world" foo' with default_operator=AND
String dsl = "\"hello world\" foo";
- String options =
"{\"default_field\":\"title\",\"default_operator\":\"AND\"}";
+ String options = "{\"mode\": \"standard\", \"default_field\":
\"title\", \"default_operator\": \"AND\"}";
QsPlan plan = SearchDslParser.parseDsl(dsl, options);
@@ -2092,7 +2092,7 @@ public class SearchDslParserTest {
public void testMultiplePhrases() {
// Test: '"hello world" "foo bar"' with default_operator=OR
String dsl = "\"hello world\" \"foo bar\"";
- String options =
"{\"default_field\":\"title\",\"default_operator\":\"OR\"}";
+ String options = "{\"mode\": \"standard\", \"default_field\":
\"title\", \"default_operator\": \"OR\"}";
QsPlan plan = SearchDslParser.parseDsl(dsl, options);
@@ -2184,7 +2184,7 @@ public class SearchDslParserTest {
// title:(rock OR jazz) → OR(TERM(title,rock), TERM(title,jazz))
// ES semantics: field prefix applies to all terms inside parentheses
String dsl = "title:(rock OR jazz)";
- QsPlan plan = SearchDslParser.parseDsl(dsl);
+ QsPlan plan = SearchDslParser.parseDsl(dsl, "{\"mode\":\"standard\"}");
Assertions.assertNotNull(plan);
QsNode root = plan.getRoot();
@@ -2213,7 +2213,7 @@ public class SearchDslParserTest {
public void testFieldGroupQueryWithAndOperator() {
// title:(rock jazz) with default_operator:AND → AND(TERM(title,rock),
TERM(title,jazz))
String dsl = "title:(rock jazz)";
- String options = "{\"default_operator\":\"and\"}";
+ String options = "{\"mode\": \"standard\", \"default_operator\":
\"and\"}";
QsPlan plan = SearchDslParser.parseDsl(dsl, options);
Assertions.assertNotNull(plan);
@@ -2234,7 +2234,7 @@ public class SearchDslParserTest {
public void testFieldGroupQueryWithPhrase() {
// title:("rock and roll" OR jazz) → OR(PHRASE(title,"rock and roll"),
TERM(title,jazz))
String dsl = "title:(\"rock and roll\" OR jazz)";
- QsPlan plan = SearchDslParser.parseDsl(dsl);
+ QsPlan plan = SearchDslParser.parseDsl(dsl, "{\"mode\":\"standard\"}");
Assertions.assertNotNull(plan);
QsNode root = plan.getRoot();
@@ -2259,7 +2259,7 @@ public class SearchDslParserTest {
public void testFieldGroupQueryWithWildcardAndRegexp() {
// title:(roc* OR /ja../) → OR(PREFIX(title,roc*), REGEXP(title,ja..))
String dsl = "title:(roc* OR /ja../)";
- QsPlan plan = SearchDslParser.parseDsl(dsl);
+ QsPlan plan = SearchDslParser.parseDsl(dsl, "{\"mode\":\"standard\"}");
Assertions.assertNotNull(plan);
QsNode root = plan.getRoot();
@@ -2283,7 +2283,7 @@ public class SearchDslParserTest {
// title:(rock OR jazz) AND music → combined query
// In standard mode with default_field=content: explicit title terms +
expanded music
String dsl = "title:(rock OR jazz) AND music";
- String options = "{\"default_field\":\"content\"}";
+ String options = "{\"mode\": \"standard\", \"default_field\":
\"content\"}";
QsPlan plan = SearchDslParser.parseDsl(dsl, options);
Assertions.assertNotNull(plan);
@@ -2384,7 +2384,7 @@ public class SearchDslParserTest {
public void testFieldGroupQuerySubcolumnPath() {
// attrs.color:(red OR blue) - field group with dot-notation path
String dsl = "attrs.color:(red OR blue)";
- QsPlan plan = SearchDslParser.parseDsl(dsl);
+ QsPlan plan = SearchDslParser.parseDsl(dsl, "{\"mode\":\"standard\"}");
Assertions.assertNotNull(plan);
QsNode root = plan.getRoot();
@@ -2402,7 +2402,7 @@ public class SearchDslParserTest {
// title:(content:foo OR bar) → content:foo stays pinned to "content",
bar gets "title"
// Inner explicit field binding must NOT be overridden by outer group
field
String dsl = "title:(content:foo OR bar)";
- QsPlan plan = SearchDslParser.parseDsl(dsl);
+ QsPlan plan = SearchDslParser.parseDsl(dsl, "{\"mode\":\"standard\"}");
Assertions.assertNotNull(plan);
QsNode root = plan.getRoot();
@@ -2428,7 +2428,7 @@ public class SearchDslParserTest {
public void testFieldGroupQueryNotOperatorInside() {
// title:(rock OR NOT jazz) → OR(title:rock, NOT(title:jazz))
String dsl = "title:(rock OR NOT jazz)";
- QsPlan plan = SearchDslParser.parseDsl(dsl);
+ QsPlan plan = SearchDslParser.parseDsl(dsl, "{\"mode\":\"standard\"}");
Assertions.assertNotNull(plan);
QsNode root = plan.getRoot();
@@ -2531,7 +2531,7 @@ public class SearchDslParserTest {
@Test
public void testNestedQuerySimple() {
String dsl = "NESTED(data, data.msg:hello)";
- QsPlan plan = SearchDslParser.parseDsl(dsl);
+ QsPlan plan = SearchDslParser.parseDsl(dsl, "{\"mode\":\"standard\"}");
Assertions.assertNotNull(plan);
Assertions.assertEquals(QsClauseType.NESTED, plan.getRoot().getType());
@@ -2545,7 +2545,7 @@ public class SearchDslParserTest {
@Test
public void testNestedQueryAnd() {
String dsl = "NESTED(data, data.msg:hello AND data.title:news)";
- QsPlan plan = SearchDslParser.parseDsl(dsl);
+ QsPlan plan = SearchDslParser.parseDsl(dsl, "{\"mode\":\"standard\"}");
Assertions.assertNotNull(plan);
Assertions.assertEquals(QsClauseType.NESTED, plan.getRoot().getType());
@@ -2560,7 +2560,7 @@ public class SearchDslParserTest {
public void testNestedQueryFieldValidation() {
String dsl = "NESTED(data, other.msg:hello)";
RuntimeException exception =
Assertions.assertThrows(RuntimeException.class, () -> {
- SearchDslParser.parseDsl(dsl);
+ SearchDslParser.parseDsl(dsl, "{\"mode\":\"standard\"}");
});
Assertions.assertTrue(exception.getMessage().contains("Fields in
NESTED query must start with nested path"));
}
@@ -2568,7 +2568,7 @@ public class SearchDslParserTest {
@Test
public void testNestedQueryPathWithDot() {
String dsl = "NESTED(data.items, data.items.msg:hello)";
- QsPlan plan = SearchDslParser.parseDsl(dsl);
+ QsPlan plan = SearchDslParser.parseDsl(dsl, "{\"mode\":\"standard\"}");
Assertions.assertNotNull(plan);
Assertions.assertEquals(QsClauseType.NESTED, plan.getRoot().getType());
@@ -2581,7 +2581,7 @@ public class SearchDslParserTest {
public void testNestedQueryMustBeTopLevelInAnd() {
String dsl = "title:hello AND NESTED(data, data.msg:hello)";
RuntimeException exception =
Assertions.assertThrows(RuntimeException.class, () -> {
- SearchDslParser.parseDsl(dsl);
+ SearchDslParser.parseDsl(dsl, "{\"mode\":\"standard\"}");
});
Assertions.assertTrue(exception.getMessage().contains("NESTED clause
must be evaluated at top level"));
}
@@ -2590,7 +2590,7 @@ public class SearchDslParserTest {
public void testNestedQueryMustBeTopLevelInOr() {
String dsl = "NESTED(data, data.msg:hello) OR title:hello";
RuntimeException exception =
Assertions.assertThrows(RuntimeException.class, () -> {
- SearchDslParser.parseDsl(dsl);
+ SearchDslParser.parseDsl(dsl, "{\"mode\":\"standard\"}");
});
Assertions.assertTrue(exception.getMessage().contains("NESTED clause
must be evaluated at top level"));
}
@@ -2599,7 +2599,7 @@ public class SearchDslParserTest {
public void testNestedQueryMustBeTopLevelInNot() {
String dsl = "NOT NESTED(data, data.msg:hello)";
RuntimeException exception =
Assertions.assertThrows(RuntimeException.class, () -> {
- SearchDslParser.parseDsl(dsl);
+ SearchDslParser.parseDsl(dsl, "{\"mode\":\"standard\"}");
});
Assertions.assertTrue(exception.getMessage().contains("NESTED clause
must be evaluated at top level"));
}
diff --git
a/fe/fe-core/src/test/java/org/apache/doris/nereids/trees/expressions/functions/scalar/SearchTest.java
b/fe/fe-core/src/test/java/org/apache/doris/nereids/trees/expressions/functions/scalar/SearchTest.java
index 919bebea252..09c539152ae 100644
---
a/fe/fe-core/src/test/java/org/apache/doris/nereids/trees/expressions/functions/scalar/SearchTest.java
+++
b/fe/fe-core/src/test/java/org/apache/doris/nereids/trees/expressions/functions/scalar/SearchTest.java
@@ -54,7 +54,8 @@ public class SearchTest {
public void testGetQsPlan() {
String dsl = "title:hello AND content:world";
StringLiteral dslLiteral = new StringLiteral(dsl);
- Search searchFunc = new Search(dslLiteral);
+ StringLiteral optionsLiteral = new
StringLiteral("{\"mode\":\"standard\"}");
+ Search searchFunc = new Search(dslLiteral, optionsLiteral);
SearchDslParser.QsPlan plan = searchFunc.getQsPlan();
Assertions.assertNotNull(plan);
@@ -147,7 +148,8 @@ public class SearchTest {
public void testComplexDslParsing() {
String complexDsl = "(title:\"machine learning\" OR content:AI) AND
NOT category:spam";
StringLiteral dslLiteral = new StringLiteral(complexDsl);
- Search searchFunc = new Search(dslLiteral);
+ StringLiteral optionsLiteral = new
StringLiteral("{\"mode\":\"standard\"}");
+ Search searchFunc = new Search(dslLiteral, optionsLiteral);
SearchDslParser.QsPlan plan = searchFunc.getQsPlan();
Assertions.assertNotNull(plan);
diff --git
a/regression-test/suites/inverted_index_p0/test_complex_or_null_semantics.groovy
b/regression-test/suites/inverted_index_p0/test_complex_or_null_semantics.groovy
index d922a00160e..de5f0efcb84 100644
---
a/regression-test/suites/inverted_index_p0/test_complex_or_null_semantics.groovy
+++
b/regression-test/suites/inverted_index_p0/test_complex_or_null_semantics.groovy
@@ -102,6 +102,8 @@ suite("test_complex_or_null_semantics") {
logger.info("Test 1 PASSED: Cross-field OR with NULL - MATCH and SEARCH
consistent")
// Test 2: Complex nested query similar to original bug report
+ // Standard mode should match MATCH behavior (SQL three-valued logic)
+ // Lucene mode uses two-valued logic (different NULL handling)
def test2_match = sql """
SELECT COUNT(*) FROM ${tableName}
WHERE content MATCH_ANY 'President of the United States'
@@ -117,7 +119,7 @@ suite("test_complex_or_null_semantics") {
)
"""
- def test2_search = sql """
+ def test2_search_standard = sql """
SELECT COUNT(*) FROM ${tableName}
WHERE SEARCH('
content:ANY("President of the United States")
@@ -131,13 +133,32 @@ suite("test_complex_or_null_semantics") {
)
)
)
- ')
+ ', '{"mode":"standard"}')
"""
- assertEquals(test2_match[0][0], test2_search[0][0],
- "Complex nested query: MATCH and SEARCH should return same count")
+ assertEquals(test2_match[0][0], test2_search_standard[0][0],
+ "Standard mode: Complex nested query MATCH and SEARCH should return
same count")
+
+ // Lucene mode: different NULL semantics, just verify it returns a valid
result
+ def test2_search_lucene = sql """
+ SELECT COUNT(*) FROM ${tableName}
+ WHERE SEARCH('
+ content:ANY("President of the United States")
+ OR NOT (
+ content:Braveheart
+ AND (
+ NOT content:ALL("List of presidents of India")
+ AND NOT (
+ title:ALL(Philosophy)
+ OR content:ALL("Disney+ Hotstar")
+ )
+ )
+ )
+ ')
+ """
+ assertTrue(test2_search_lucene[0][0] >= 0, "Lucene mode: Complex nested
query should return valid result")
- logger.info("Test 2 PASSED: Complex nested query - MATCH and SEARCH
consistent (count: ${test2_match[0][0]})")
+ logger.info("Test 2 PASSED: Complex nested query - standard mode
consistent with MATCH (count: ${test2_match[0][0]}), lucene mode:
${test2_search_lucene[0][0]}")
// Test 3: Verify the 15 critical rows are included
def test3 = sql """
diff --git
a/regression-test/suites/inverted_index_p0/test_cross_field_or_with_null.groovy
b/regression-test/suites/inverted_index_p0/test_cross_field_or_with_null.groovy
index 55b317e4329..f3454380330 100644
---
a/regression-test/suites/inverted_index_p0/test_cross_field_or_with_null.groovy
+++
b/regression-test/suites/inverted_index_p0/test_cross_field_or_with_null.groovy
@@ -118,22 +118,33 @@ suite("test_cross_field_or_with_null") {
// Test 4: NOT with cross-field OR
// NOT (title MATCH "Philosophy" OR content MATCH "Disney+ Hotstar")
- // Rows 1-16: title matches or content matches -> OR = TRUE -> NOT TRUE =
FALSE (excluded)
- // Rows 17-20: title doesn't match (FALSE) and content is NULL -> FALSE OR
NULL = NULL -> NOT NULL = NULL (excluded)
- // Expected: 0 rows (all rows are either TRUE or NULL in the OR, none are
FALSE)
+ // Standard mode (SQL three-valued logic):
+ // Rows 1-16: OR = TRUE -> NOT TRUE = FALSE (excluded)
+ // Rows 17-20: FALSE OR NULL = NULL -> NOT NULL = NULL (excluded)
+ // Expected: 0 rows
+ // Lucene mode (two-valued logic):
+ // Rows 1-16: OR = TRUE -> NOT TRUE = FALSE (excluded)
+ // Rows 17-20: no match -> NOT no_match = TRUE (included)
+ // Expected: 4 rows
def result4_match = sql """
SELECT COUNT(*) FROM ${tableName}
WHERE NOT (title MATCH_ALL 'Philosophy' OR content MATCH_ALL 'Disney+
Hotstar')
"""
- def result4_search = sql """
+ def result4_search_standard = sql """
SELECT COUNT(*) FROM ${tableName}
- WHERE SEARCH('NOT (title:ALL(Philosophy) OR content:ALL("Disney+
Hotstar"))')
+ WHERE SEARCH('NOT (title:ALL(Philosophy) OR content:ALL("Disney+
Hotstar"))', '{"mode":"standard"}')
"""
assertEquals(0, result4_match[0][0]) // All rows excluded due to NULL
semantics
- assertEquals(result4_match[0][0], result4_search[0][0])
- logger.info("Test 4 passed: NOT with cross-field OR (NULL semantics
correctly exclude rows)")
+ assertEquals(result4_match[0][0], result4_search_standard[0][0])
+
+ def result4_search_lucene = sql """
+ SELECT COUNT(*) FROM ${tableName}
+ WHERE SEARCH('NOT (title:ALL(Philosophy) OR content:ALL("Disney+
Hotstar"))')
+ """
+ assertEquals(4, result4_search_lucene[0][0]) // Lucene mode: rows 17-20
included
+ logger.info("Test 4 passed: NOT with cross-field OR works correctly in
both modes")
// Test 5: AND with cross-field OR containing NULL
// category = "Education" AND (title MATCH "Philosophy" OR content MATCH
"Disney")
diff --git
a/regression-test/suites/inverted_index_p0/test_search_or_null_semantics.groovy
b/regression-test/suites/inverted_index_p0/test_search_or_null_semantics.groovy
index c9852bccfb5..9d310115299 100644
---
a/regression-test/suites/inverted_index_p0/test_search_or_null_semantics.groovy
+++
b/regression-test/suites/inverted_index_p0/test_search_or_null_semantics.groovy
@@ -167,16 +167,27 @@ suite("test_search_or_null_semantics") {
assertTrue(test7_count >= 15, "Complex nested query should include the 15
Philosophy rows")
logger.info("Test 7 PASSED: Complex nested query (returned ${test7_count}
rows)")
- // Test 8: NOT with OR and NULL (SQL three-valued logic)
- // Rows 1-16: OR = TRUE -> NOT TRUE = FALSE (excluded)
- // Rows 17-20: OR = NULL -> NOT NULL = NULL (excluded)
- def test8 = sql """
+ // Test 8: NOT with OR and NULL
+ // Standard mode (SQL three-valued logic):
+ // Rows 1-16: OR = TRUE -> NOT TRUE = FALSE (excluded)
+ // Rows 17-20: OR = NULL -> NOT NULL = NULL (excluded)
+ // Result: 0 rows
+ // Lucene mode (two-valued logic):
+ // Rows 1-16: OR = TRUE -> NOT TRUE = FALSE (excluded)
+ // Rows 17-20: no match -> NOT no_match = TRUE (included)
+ // Result: 4 rows
+ def test8_standard = sql """
SELECT /*+SET_VAR(enable_common_expr_pushdown=true) */ COUNT(*) FROM
${tableName}
- WHERE SEARCH('NOT (title:ALL(Philosophy) OR content:ALL("Disney+
Hotstar"))')
+ WHERE SEARCH('NOT (title:ALL(Philosophy) OR content:ALL("Disney+
Hotstar"))', '{"mode":"standard"}')
"""
+ assertEquals(0, test8_standard[0][0], "Standard mode: NOT OR should
exclude all rows due to NULL semantics")
- assertEquals(0, test8[0][0], "NOT OR should exclude all rows due to NULL
semantics")
- logger.info("Test 8 PASSED: NOT OR with NULL correctly excludes all rows")
+ def test8_lucene = sql """
+ SELECT /*+SET_VAR(enable_common_expr_pushdown=true) */ COUNT(*) FROM
${tableName}
+ WHERE SEARCH('NOT (title:ALL(Philosophy) OR content:ALL("Disney+
Hotstar"))')
+ """
+ assertEquals(4, test8_lucene[0][0], "Lucene mode: NOT OR returns 4 rows
(rows 17-20, no match treated as FALSE)")
+ logger.info("Test 8 PASSED: NOT OR with NULL works correctly in both
modes")
// Test 9: Verify SEARCH works with and without pushdown
sql "SET enable_common_expr_pushdown = false"
diff --git a/regression-test/suites/search/test_search_boundary_cases.groovy
b/regression-test/suites/search/test_search_boundary_cases.groovy
index 5ab2a9386aa..30ae4aaa9a8 100644
--- a/regression-test/suites/search/test_search_boundary_cases.groovy
+++ b/regression-test/suites/search/test_search_boundary_cases.groovy
@@ -89,42 +89,42 @@ suite("test_search_boundary_cases", "p0") {
// Boundary Test 1: All NULL fields
qt_boundary_1_all_null_or """
SELECT /*+SET_VAR(enable_common_expr_pushdown=true) */ count(*) FROM
${tableName}
- WHERE search('field1:anything OR field2:anything OR field3:anything OR
field4:anything OR field5:anything')
+ WHERE search('field1:anything OR field2:anything OR field3:anything OR
field4:anything OR field5:anything', '{"mode":"standard"}')
"""
qt_boundary_1_all_null_and """
SELECT /*+SET_VAR(enable_common_expr_pushdown=true) */ count(*) FROM
${tableName}
- WHERE search('field1:anything AND field2:anything AND field3:anything
AND field4:anything AND field5:anything')
+ WHERE search('field1:anything AND field2:anything AND field3:anything
AND field4:anything AND field5:anything', '{"mode":"standard"}')
"""
// Boundary Test 2: Single field NULL vs multiple fields NULL in OR
qt_boundary_2_single_null_or """
SELECT /*+SET_VAR(enable_common_expr_pushdown=true) */ id FROM
${tableName}
- WHERE search('field1:nonexistent OR field2:test')
+ WHERE search('field1:nonexistent OR field2:test',
'{"mode":"standard"}')
ORDER BY id
"""
qt_boundary_2_multiple_null_or """
SELECT /*+SET_VAR(enable_common_expr_pushdown=true) */ id FROM
${tableName}
- WHERE search('field1:nonexistent OR field2:test OR field3:nonexistent')
+ WHERE search('field1:nonexistent OR field2:test OR
field3:nonexistent', '{"mode":"standard"}')
ORDER BY id
"""
// Boundary Test 3: NOT with various NULL combinations
qt_boundary_3_not_null_field """
SELECT /*+SET_VAR(enable_common_expr_pushdown=true) */ count(*) FROM
${tableName}
- WHERE search('NOT field1:test')
+ WHERE search('NOT field1:test', '{"mode":"standard"}')
"""
qt_boundary_3_external_not_null """
SELECT /*+SET_VAR(enable_common_expr_pushdown=true) */ count(*) FROM
${tableName}
- WHERE not search('field1:test')
+ WHERE not search('field1:test', '{"mode":"standard"}')
"""
// Boundary Test 4: Empty string vs NULL handling
qt_boundary_4_empty_string_search """
SELECT /*+SET_VAR(enable_common_expr_pushdown=true) */ id FROM
${tableName}
- WHERE search('field1:""')
+ WHERE search('field1:""', '{"mode":"standard"}')
ORDER BY id
"""
@@ -141,12 +141,12 @@ suite("test_search_boundary_cases", "p0") {
// Boundary Test 5: Complex nested boolean with NULLs
qt_boundary_5_complex_nested """
SELECT /*+SET_VAR(enable_common_expr_pushdown=true) */ count(*) FROM
${tableName}
- WHERE search('((field1:test OR field2:test) AND (field3:test OR
field4:test)) OR field5:test')
+ WHERE search('((field1:test OR field2:test) AND (field3:test OR
field4:test)) OR field5:test', '{"mode":"standard"}')
"""
qt_boundary_5_detailed_result """
SELECT /*+SET_VAR(enable_common_expr_pushdown=true) */ id, field1,
field2, field3, field4, field5 FROM ${tableName}
- WHERE search('((field1:test OR field2:test) AND (field3:test OR
field4:test)) OR field5:test')
+ WHERE search('((field1:test OR field2:test) AND (field3:test OR
field4:test)) OR field5:test', '{"mode":"standard"}')
ORDER BY id
"""
@@ -157,36 +157,36 @@ suite("test_search_boundary_cases", "p0") {
field2:"target" OR field2:"keyword" OR field2:"apple" OR
field2:"unique2" OR
field3:"target" OR field3:"keyword" OR field3:"banana" OR
field3:"unique3" OR
field4:"target" OR field4:"keyword" OR field4:"banana" OR
field4:"unique4" OR
- field5:"target" OR field5:"keyword" OR field5:"cherry" OR
field5:"unique5"')
+ field5:"target" OR field5:"keyword" OR field5:"cherry" OR
field5:"unique5"', '{"mode":"standard"}')
"""
// Boundary Test 7: Special characters and NULL interaction
qt_boundary_7_special_chars_or """
SELECT /*+SET_VAR(enable_common_expr_pushdown=true) */ count(*) FROM
${tableName}
- WHERE search('field1:special123 OR field2:nonexistent')
+ WHERE search('field1:special123 OR field2:nonexistent',
'{"mode":"standard"}')
"""
qt_boundary_7_special_chars_and """
SELECT /*+SET_VAR(enable_common_expr_pushdown=true) */ count(*) FROM
${tableName}
- WHERE search('field1:special123 AND field2:chars456')
+ WHERE search('field1:special123 AND field2:chars456',
'{"mode":"standard"}')
"""
// Boundary Test 8: Case sensitivity with NULL fields
qt_boundary_8_case_variations """
SELECT /*+SET_VAR(enable_common_expr_pushdown=true) */ id FROM
${tableName}
- WHERE search('field1:Target OR field2:TARGET OR field3:target OR
field4:TaRgEt')
+ WHERE search('field1:Target OR field2:TARGET OR field3:target OR
field4:TaRgEt', '{"mode":"standard"}')
ORDER BY id
"""
// Boundary Test 9: Multiple NOT operations
qt_boundary_9_multiple_not """
SELECT /*+SET_VAR(enable_common_expr_pushdown=true) */ count(*) FROM
${tableName}
- WHERE search('NOT (field1:nonexistent OR field2:nonexistent OR
field3:nonexistent)')
+ WHERE search('NOT (field1:nonexistent OR field2:nonexistent OR
field3:nonexistent)', '{"mode":"standard"}')
"""
qt_boundary_9_external_multiple_not """
SELECT /*+SET_VAR(enable_common_expr_pushdown=true) */ count(*) FROM
${tableName}
- WHERE not search('field1:nonexistent OR field2:nonexistent OR
field3:nonexistent')
+ WHERE not search('field1:nonexistent OR field2:nonexistent OR
field3:nonexistent', '{"mode":"standard"}')
"""
// Boundary Test 10: Performance with NULL-heavy dataset
@@ -194,6 +194,6 @@ suite("test_search_boundary_cases", "p0") {
SELECT /*+SET_VAR(enable_common_expr_pushdown=true) */ count(*) FROM
${tableName}
WHERE search('(field1:test OR field1:target OR field1:keyword) AND
(field2:test OR field2:target OR field2:keyword) AND
- NOT (field3:nonexistent OR field4:nonexistent OR
field5:nonexistent)')
+ NOT (field3:nonexistent OR field4:nonexistent OR
field5:nonexistent)', '{"mode":"standard"}')
"""
}
\ No newline at end of file
diff --git a/regression-test/suites/search/test_search_exact_multi_index.groovy
b/regression-test/suites/search/test_search_exact_multi_index.groovy
index 8c11a560970..ebe22c8e196 100644
--- a/regression-test/suites/search/test_search_exact_multi_index.groovy
+++ b/regression-test/suites/search/test_search_exact_multi_index.groovy
@@ -51,23 +51,23 @@ suite("test_search_exact_multi_index", "p0") {
Thread.sleep(3000)
// Test 1: EXACT should use untokenized index - exact match only
- qt_exact_full_match "SELECT /*+SET_VAR(enable_common_expr_pushdown=true)
*/ id, content FROM ${tableName} WHERE search('content:EXACT(machine
learning)') ORDER BY id"
+ qt_exact_full_match """SELECT /*+SET_VAR(enable_common_expr_pushdown=true)
*/ id, content FROM ${tableName} WHERE search('content:EXACT(machine
learning)', '{"mode":"standard"}') ORDER BY id"""
// Test 2: EXACT should not match partial tokens
- qt_exact_no_partial "SELECT /*+SET_VAR(enable_common_expr_pushdown=true)
*/ id, content FROM ${tableName} WHERE search('content:EXACT(machine)') ORDER
BY id"
+ qt_exact_no_partial """SELECT /*+SET_VAR(enable_common_expr_pushdown=true)
*/ id, content FROM ${tableName} WHERE search('content:EXACT(machine)',
'{"mode":"standard"}') ORDER BY id"""
// Test 3: ANY should use tokenized index - matches any token
- qt_any_token_match "SELECT /*+SET_VAR(enable_common_expr_pushdown=true) */
id, content FROM ${tableName} WHERE search('content:ANY(machine learning)')
ORDER BY id"
+ qt_any_token_match """SELECT /*+SET_VAR(enable_common_expr_pushdown=true)
*/ id, content FROM ${tableName} WHERE search('content:ANY(machine learning)',
'{"mode":"standard"}') ORDER BY id"""
// Test 4: ALL should use tokenized index - matches all tokens
- qt_all_token_match "SELECT /*+SET_VAR(enable_common_expr_pushdown=true) */
id, content FROM ${tableName} WHERE search('content:ALL(machine learning)')
ORDER BY id"
+ qt_all_token_match """SELECT /*+SET_VAR(enable_common_expr_pushdown=true)
*/ id, content FROM ${tableName} WHERE search('content:ALL(machine learning)',
'{"mode":"standard"}') ORDER BY id"""
// Test 5: Verify EXACT vs ANY behavior difference
// EXACT: only exact string match
// ANY: any token matches
- qt_exact_strict "SELECT /*+SET_VAR(enable_common_expr_pushdown=true) */
id, content FROM ${tableName} WHERE search('content:EXACT(deep learning)')
ORDER BY id"
- qt_any_loose "SELECT /*+SET_VAR(enable_common_expr_pushdown=true) */ id,
content FROM ${tableName} WHERE search('content:ANY(deep learning)') ORDER BY
id"
+ qt_exact_strict """SELECT /*+SET_VAR(enable_common_expr_pushdown=true) */
id, content FROM ${tableName} WHERE search('content:EXACT(deep learning)',
'{"mode":"standard"}') ORDER BY id"""
+ qt_any_loose """SELECT /*+SET_VAR(enable_common_expr_pushdown=true) */ id,
content FROM ${tableName} WHERE search('content:ANY(deep learning)',
'{"mode":"standard"}') ORDER BY id"""
// Test 6: Multiple conditions with EXACT and ANY
- qt_mixed_exact_any "SELECT /*+SET_VAR(enable_common_expr_pushdown=true) */
id, content FROM ${tableName} WHERE search('content:EXACT(machine learning) OR
content:ANY(intelligence)') ORDER BY id"
+ qt_mixed_exact_any """SELECT /*+SET_VAR(enable_common_expr_pushdown=true)
*/ id, content FROM ${tableName} WHERE search('content:EXACT(machine learning)
OR content:ANY(intelligence)', '{"mode":"standard"}') ORDER BY id"""
}
diff --git a/regression-test/suites/search/test_search_multi_field.groovy
b/regression-test/suites/search/test_search_multi_field.groovy
index 44f9d9afad9..f9f6ab3a42e 100644
--- a/regression-test/suites/search/test_search_multi_field.groovy
+++ b/regression-test/suites/search/test_search_multi_field.groovy
@@ -22,10 +22,10 @@
* This is similar to Elasticsearch's query_string 'fields' parameter.
*
* Example:
- * search('hello', '{"fields":["title","content"]}')
+ * search('hello', '{"mode":"standard","fields":["title","content"]}')
* -> Equivalent to: (title:hello OR content:hello)
*
- * search('hello world',
'{"fields":["title","content"],"default_operator":"and"}')
+ * search('hello world',
'{"mode":"standard","fields":["title","content"],"default_operator":"and"}')
* -> Equivalent to: (title:hello OR content:hello) AND (title:world OR
content:world)
*
* Multi-field search can also be combined with Lucene mode for
MUST/SHOULD/MUST_NOT semantics.
@@ -80,7 +80,7 @@ suite("test_search_multi_field", "p0") {
qt_multi_field_single_term """
SELECT /*+SET_VAR(enable_common_expr_pushdown=true) */ id, title
FROM ${tableName}
- WHERE search('machine', '{"fields":["title","content"]}')
+ WHERE search('machine',
'{"mode":"standard","fields":["title","content"]}')
ORDER BY id
"""
@@ -92,7 +92,7 @@ suite("test_search_multi_field", "p0") {
qt_multi_field_multi_term_and """
SELECT /*+SET_VAR(enable_common_expr_pushdown=true) */ id, title
FROM ${tableName}
- WHERE search('machine learning',
'{"fields":["title","content"],"default_operator":"and","type":"cross_fields"}')
+ WHERE search('machine learning',
'{"mode":"standard","fields":["title","content"],"default_operator":"and","type":"cross_fields"}')
ORDER BY id
"""
@@ -110,7 +110,7 @@ suite("test_search_multi_field", "p0") {
qt_multi_field_multi_term_or """
SELECT /*+SET_VAR(enable_common_expr_pushdown=true) */ id, title
FROM ${tableName}
- WHERE search('machine learning',
'{"fields":["title","content"],"default_operator":"or"}')
+ WHERE search('machine learning',
'{"mode":"standard","fields":["title","content"],"default_operator":"or"}')
ORDER BY id
"""
@@ -119,7 +119,7 @@ suite("test_search_multi_field", "p0") {
qt_multi_field_explicit_and """
SELECT /*+SET_VAR(enable_common_expr_pushdown=true) */ id, title
FROM ${tableName}
- WHERE search('machine AND learning',
'{"fields":["title","content"],"type":"cross_fields"}')
+ WHERE search('machine AND learning',
'{"mode":"standard","fields":["title","content"],"type":"cross_fields"}')
ORDER BY id
"""
@@ -127,7 +127,7 @@ suite("test_search_multi_field", "p0") {
qt_multi_field_mixed """
SELECT /*+SET_VAR(enable_common_expr_pushdown=true) */ id, title,
category
FROM ${tableName}
- WHERE search('machine AND category:tech',
'{"fields":["title","content"],"type":"cross_fields"}')
+ WHERE search('machine AND category:tech',
'{"mode":"standard","fields":["title","content"],"type":"cross_fields"}')
ORDER BY id
"""
@@ -135,7 +135,7 @@ suite("test_search_multi_field", "p0") {
qt_three_fields """
SELECT /*+SET_VAR(enable_common_expr_pushdown=true) */ id, title
FROM ${tableName}
- WHERE search('ai', '{"fields":["title","content","tags"]}')
+ WHERE search('ai',
'{"mode":"standard","fields":["title","content","tags"]}')
ORDER BY id
"""
@@ -143,7 +143,7 @@ suite("test_search_multi_field", "p0") {
qt_multi_field_wildcard """
SELECT /*+SET_VAR(enable_common_expr_pushdown=true) */ id, title
FROM ${tableName}
- WHERE search('learn*', '{"fields":["title","content","tags"]}')
+ WHERE search('learn*',
'{"mode":"standard","fields":["title","content","tags"]}')
ORDER BY id
"""
@@ -151,7 +151,7 @@ suite("test_search_multi_field", "p0") {
qt_multi_field_not """
SELECT /*+SET_VAR(enable_common_expr_pushdown=true) */ id, title
FROM ${tableName}
- WHERE search('machine AND NOT cooking',
'{"fields":["title","content"],"type":"cross_fields"}')
+ WHERE search('machine AND NOT cooking',
'{"mode":"standard","fields":["title","content"],"type":"cross_fields"}')
ORDER BY id
"""
@@ -159,7 +159,7 @@ suite("test_search_multi_field", "p0") {
qt_multi_field_complex """
SELECT /*+SET_VAR(enable_common_expr_pushdown=true) */ id, title
FROM ${tableName}
- WHERE search('(machine OR ai) AND NOT cooking',
'{"fields":["title","content"],"type":"cross_fields"}')
+ WHERE search('(machine OR ai) AND NOT cooking',
'{"mode":"standard","fields":["title","content"],"type":"cross_fields"}')
ORDER BY id
"""
@@ -167,7 +167,7 @@ suite("test_search_multi_field", "p0") {
qt_single_field_array """
SELECT /*+SET_VAR(enable_common_expr_pushdown=true) */ id, title
FROM ${tableName}
- WHERE search('machine', '{"fields":["title"]}')
+ WHERE search('machine', '{"mode":"standard","fields":["title"]}')
ORDER BY id
"""
@@ -188,7 +188,7 @@ suite("test_search_multi_field", "p0") {
qt_multi_field_cross_fields_verify """
SELECT /*+SET_VAR(enable_common_expr_pushdown=true) */ id, title,
content
FROM ${tableName}
- WHERE search('machine AND learning',
'{"fields":["title","content"],"type":"cross_fields"}')
+ WHERE search('machine AND learning',
'{"mode":"standard","fields":["title","content"],"type":"cross_fields"}')
ORDER BY id
"""
@@ -230,7 +230,7 @@ suite("test_search_multi_field", "p0") {
qt_compare_default_field """
SELECT /*+SET_VAR(enable_common_expr_pushdown=true) */ id, title
FROM ${tableName}
- WHERE search('machine', '{"default_field":"title"}')
+ WHERE search('machine', '{"mode":"standard","default_field":"title"}')
ORDER BY id
"""
@@ -238,7 +238,7 @@ suite("test_search_multi_field", "p0") {
qt_compare_fields_single """
SELECT /*+SET_VAR(enable_common_expr_pushdown=true) */ id, title
FROM ${tableName}
- WHERE search('machine', '{"fields":["title"]}')
+ WHERE search('machine', '{"mode":"standard","fields":["title"]}')
ORDER BY id
"""
@@ -246,7 +246,7 @@ suite("test_search_multi_field", "p0") {
qt_multi_field_exact """
SELECT /*+SET_VAR(enable_common_expr_pushdown=true) */ id, title
FROM ${tableName}
- WHERE search('EXACT(machine learning)',
'{"fields":["title","content"]}')
+ WHERE search('EXACT(machine learning)',
'{"mode":"standard","fields":["title","content"]}')
ORDER BY id
"""
@@ -254,7 +254,7 @@ suite("test_search_multi_field", "p0") {
qt_multi_field_any """
SELECT /*+SET_VAR(enable_common_expr_pushdown=true) */ id, title
FROM ${tableName}
- WHERE search('ANY(machine cooking)', '{"fields":["title","content"]}')
+ WHERE search('ANY(machine cooking)',
'{"mode":"standard","fields":["title","content"]}')
ORDER BY id
"""
@@ -265,7 +265,7 @@ suite("test_search_multi_field", "p0") {
qt_multi_field_best_fields_default """
SELECT /*+SET_VAR(enable_common_expr_pushdown=true) */ id, title
FROM ${tableName}
- WHERE search('machine learning',
'{"fields":["title","content"],"default_operator":"and"}')
+ WHERE search('machine learning',
'{"mode":"standard","fields":["title","content"],"default_operator":"and"}')
ORDER BY id
"""
@@ -275,7 +275,7 @@ suite("test_search_multi_field", "p0") {
qt_multi_field_cross_fields """
SELECT /*+SET_VAR(enable_common_expr_pushdown=true) */ id, title
FROM ${tableName}
- WHERE search('machine learning',
'{"fields":["title","content"],"default_operator":"and","type":"cross_fields"}')
+ WHERE search('machine learning',
'{"mode":"standard","fields":["title","content"],"default_operator":"and","type":"cross_fields"}')
ORDER BY id
"""
@@ -324,7 +324,7 @@ suite("test_search_multi_field", "p0") {
qt_multi_field_match_all_standard """
SELECT /*+SET_VAR(enable_common_expr_pushdown=true) */ count(*)
FROM ${tableName}
- WHERE search('*',
'{"fields":["title","content"],"type":"best_fields","default_operator":"AND"}')
+ WHERE search('*',
'{"mode":"standard","fields":["title","content"],"type":"best_fields","default_operator":"AND"}')
"""
// Cleanup
diff --git a/regression-test/suites/search/test_search_null_regression.groovy
b/regression-test/suites/search/test_search_null_regression.groovy
index c2acfeb51b2..dc5dc4e4215 100644
--- a/regression-test/suites/search/test_search_null_regression.groovy
+++ b/regression-test/suites/search/test_search_null_regression.groovy
@@ -68,11 +68,11 @@ suite("test_search_null_regression", "p0") {
Thread.sleep(5000)
// Regression Test 1: Original Issue - OR Query Inconsistency
- // This reproduces the original bug: search('title:"Ronald" or
(content:ALL("Selma Blair"))')
+ // This reproduces the original bug: search('title:"Ronald" or
(content:ALL("Selma Blair"))', '{"mode":"standard"}')
// vs title match "Ronald" or (content match_all "Selma Blair")
qt_regression_1_search_or """
SELECT /*+SET_VAR(enable_common_expr_pushdown=true) */ count(*) FROM
${tableName}
- WHERE search('title:Ronald OR (content:ALL(Selma Blair))')
+ WHERE search('title:Ronald OR (content:ALL(Selma Blair))',
'{"mode":"standard"}')
"""
qt_regression_1_match_or """
@@ -83,7 +83,7 @@ suite("test_search_null_regression", "p0") {
// Detailed verification - get actual matching rows for OR query
qt_regression_1_search_or_rows """
SELECT /*+SET_VAR(enable_common_expr_pushdown=true) */ id, title,
content FROM ${tableName}
- WHERE search('title:Ronald OR (content:ALL(Selma Blair))')
+ WHERE search('title:Ronald OR (content:ALL(Selma Blair))',
'{"mode":"standard"}')
ORDER BY id
"""
@@ -94,27 +94,27 @@ suite("test_search_null_regression", "p0") {
"""
// Regression Test 2: Original Issue - NOT Query Inconsistency
- // This reproduces: search('not content:"Round"') vs not
search('content:"Round"')
+ // This reproduces: search('not content:"Round"', '{"mode":"standard"}')
vs not search('content:"Round"', '{"mode":"standard"}')
qt_regression_2_internal_not """
SELECT /*+SET_VAR(enable_common_expr_pushdown=true) */ count(*) FROM
${tableName}
- WHERE search('NOT content:Round')
+ WHERE search('NOT content:Round', '{"mode":"standard"}')
"""
qt_regression_2_external_not """
SELECT /*+SET_VAR(enable_common_expr_pushdown=true) */ count(*) FROM
${tableName}
- WHERE not search('content:Round')
+ WHERE not search('content:Round', '{"mode":"standard"}')
"""
// Detailed verification for NOT query
qt_regression_2_internal_not_rows """
SELECT /*+SET_VAR(enable_common_expr_pushdown=true) */ id, title,
content FROM ${tableName}
- WHERE search('NOT content:Round')
+ WHERE search('NOT content:Round', '{"mode":"standard"}')
ORDER BY id
"""
qt_regression_2_external_not_rows """
SELECT /*+SET_VAR(enable_common_expr_pushdown=true) */ id, title,
content FROM ${tableName}
- WHERE not search('content:Round')
+ WHERE not search('content:Round', '{"mode":"standard"}')
ORDER BY id
"""
@@ -122,7 +122,7 @@ suite("test_search_null_regression", "p0") {
// Verify that OR queries properly handle NULL values according to SQL
semantics
qt_regression_3_null_or """
SELECT /*+SET_VAR(enable_common_expr_pushdown=true) */ id FROM
${tableName}
- WHERE search('title:NonExistent OR content:Ronald')
+ WHERE search('title:NonExistent OR content:Ronald',
'{"mode":"standard"}')
ORDER BY id
"""
@@ -136,58 +136,58 @@ suite("test_search_null_regression", "p0") {
// Regression Test 4: NULL Handling in AND Queries
qt_regression_4_null_and """
SELECT /*+SET_VAR(enable_common_expr_pushdown=true) */ id FROM
${tableName}
- WHERE search('title:Ronald AND content:biography')
+ WHERE search('title:Ronald AND content:biography',
'{"mode":"standard"}')
ORDER BY id
"""
// Regression Test 5: Complex Boolean Operations
qt_regression_5_complex_search """
SELECT /*+SET_VAR(enable_common_expr_pushdown=true) */ count(*) FROM
${tableName}
- WHERE search('(title:Ronald OR content:Selma) AND NOT content:Round')
+ WHERE search('(title:Ronald OR content:Selma) AND NOT content:Round',
'{"mode":"standard"}')
"""
qt_regression_5_complex_match """
SELECT /*+SET_VAR(enable_common_expr_pushdown=true) */ count(*) FROM
${tableName}
- WHERE (title match "Ronald" or content match "Selma") and not
search('content:"Round"')
+ WHERE (title match "Ronald" or content match "Selma") and not
search('content:"Round"', '{"mode":"standard"}')
"""
// Regression Test 6: Edge Case - All NULL Query
qt_regression_6_all_null """
SELECT /*+SET_VAR(enable_common_expr_pushdown=true) */ count(*) FROM
${tableName}
- WHERE search('title:NonExistent AND content:NonExistent')
+ WHERE search('title:NonExistent AND content:NonExistent',
'{"mode":"standard"}')
"""
// Regression Test 7: Case Sensitivity and Variations
qt_regression_7_case_lower """
SELECT /*+SET_VAR(enable_common_expr_pushdown=true) */ count(*) FROM
${tableName}
- WHERE search('content:round')
+ WHERE search('content:round', '{"mode":"standard"}')
"""
qt_regression_7_case_upper """
SELECT /*+SET_VAR(enable_common_expr_pushdown=true) */ count(*) FROM
${tableName}
- WHERE search('content:Round')
+ WHERE search('content:Round', '{"mode":"standard"}')
"""
// Regression Test 8: Multiple NOT operations
qt_regression_8_multiple_not """
SELECT /*+SET_VAR(enable_common_expr_pushdown=true) */ count(*) FROM
${tableName}
- WHERE search('NOT (title:nonexistent OR content:nonexistent)')
+ WHERE search('NOT (title:nonexistent OR content:nonexistent)',
'{"mode":"standard"}')
"""
qt_regression_8_external_multiple_not """
SELECT /*+SET_VAR(enable_common_expr_pushdown=true) */ count(*) FROM
${tableName}
- WHERE not search('title:nonexistent OR content:nonexistent')
+ WHERE not search('title:nonexistent OR content:nonexistent',
'{"mode":"standard"}')
"""
// Regression Test 9: Empty string handling
qt_regression_9_empty_string """
SELECT /*+SET_VAR(enable_common_expr_pushdown=true) */ count(*) FROM
${tableName}
- WHERE search('title:"" OR content:Round')
+ WHERE search('title:"" OR content:Round', '{"mode":"standard"}')
"""
// Regression Test 10: Performance test with complex query
qt_regression_10_performance """
SELECT /*+SET_VAR(enable_common_expr_pushdown=true) */ count(*) FROM
${tableName}
- WHERE search('(title:Ronald OR title:Selma OR content:Round) AND NOT
(title:NonExistent AND content:NonExistent)')
+ WHERE search('(title:Ronald OR title:Selma OR content:Round) AND NOT
(title:NonExistent AND content:NonExistent)', '{"mode":"standard"}')
"""
}
\ No newline at end of file
diff --git a/regression-test/suites/search/test_search_null_semantics.groovy
b/regression-test/suites/search/test_search_null_semantics.groovy
index 1a16adb03aa..2003c2d4c50 100644
--- a/regression-test/suites/search/test_search_null_semantics.groovy
+++ b/regression-test/suites/search/test_search_null_semantics.groovy
@@ -60,11 +60,11 @@ suite("test_search_null_semantics", "p0") {
Thread.sleep(5000)
// Test Case 1: OR query consistency - Original Issue Reproduction
- // search('title:"Ronald" or (content:ALL("Selma Blair"))') should match
+ // search('title:"Ronald" or (content:ALL("Selma Blair"))',
'{"mode":"standard"}') should match
// title match "Ronald" or (content match_all "Selma Blair")
qt_test_case_1_search """
SELECT /*+SET_VAR(enable_common_expr_pushdown=true) */ count(*) FROM
${tableName}
- WHERE search('title:Ronald OR (content:ALL(Selma Blair))')
+ WHERE search('title:Ronald OR (content:ALL(Selma Blair))',
'{"mode":"standard"}')
"""
qt_test_case_1_match """
@@ -73,21 +73,21 @@ suite("test_search_null_semantics", "p0") {
"""
// Test Case 2: NOT query consistency - Original Issue Reproduction
- // search('not content:"Round"') should match not search('content:"Round"')
+ // search('not content:"Round"', '{"mode":"standard"}') should match not
search('content:"Round"', '{"mode":"standard"}')
qt_test_case_2_internal_not """
SELECT /*+SET_VAR(enable_common_expr_pushdown=true) */ count(*) FROM
${tableName}
- WHERE search('NOT content:Round')
+ WHERE search('NOT content:Round', '{"mode":"standard"}')
"""
qt_test_case_2_external_not """
SELECT /*+SET_VAR(enable_common_expr_pushdown=true) */ count(*) FROM
${tableName}
- WHERE not search('content:Round')
+ WHERE not search('content:Round', '{"mode":"standard"}')
"""
// Test Case 2b: Phrase NOT queries must treat NULL rows as UNKNOWN
qt_test_case_2_phrase_not """
SELECT /*+SET_VAR(enable_common_expr_pushdown=true) */ id FROM
${tableName}
- WHERE NOT search('content:"Selma Blair"')
+ WHERE NOT search('content:"Selma Blair"', '{"mode":"standard"}')
ORDER BY id
"""
@@ -95,7 +95,7 @@ suite("test_search_null_semantics", "p0") {
// Verify that NULL OR TRUE = TRUE logic works
qt_test_case_3_or_with_null """
SELECT /*+SET_VAR(enable_common_expr_pushdown=true) */ id, title,
content FROM ${tableName}
- WHERE search('title:Ronald OR content:biography')
+ WHERE search('title:Ronald OR content:biography',
'{"mode":"standard"}')
ORDER BY id
"""
@@ -103,14 +103,14 @@ suite("test_search_null_semantics", "p0") {
// Verify that NULL AND TRUE = NULL logic works
qt_test_case_4_and_with_null """
SELECT /*+SET_VAR(enable_common_expr_pushdown=true) */ id, title,
content FROM ${tableName}
- WHERE search('title:Ronald AND content:biography')
+ WHERE search('title:Ronald AND content:biography',
'{"mode":"standard"}')
ORDER BY id
"""
// Test Case 5: Complex OR query with multiple NULL scenarios
qt_test_case_5_complex_or_search """
SELECT /*+SET_VAR(enable_common_expr_pushdown=true) */ count(*) FROM
${tableName}
- WHERE search('title:Unknown OR content:mascot OR category:Test')
+ WHERE search('title:Unknown OR content:mascot OR category:Test',
'{"mode":"standard"}')
"""
qt_test_case_5_complex_or_match """
@@ -121,24 +121,24 @@ suite("test_search_null_semantics", "p0") {
// Test Case 6: NOT query with different field types
qt_test_case_6_not_title_search """
SELECT /*+SET_VAR(enable_common_expr_pushdown=true) */ count(*) FROM
${tableName}
- WHERE search('NOT title:Ronald')
+ WHERE search('NOT title:Ronald', '{"mode":"standard"}')
"""
qt_test_case_6_not_title_external """
SELECT /*+SET_VAR(enable_common_expr_pushdown=true) */ count(*) FROM
${tableName}
- WHERE not search('title:Ronald')
+ WHERE not search('title:Ronald', '{"mode":"standard"}')
"""
// Test Case 7: Mixed boolean operations
qt_test_case_7_mixed """
SELECT /*+SET_VAR(enable_common_expr_pushdown=true) */ count(*) FROM
${tableName}
- WHERE search('(title:Ronald OR content:Selma) AND NOT
category:Unknown')
+ WHERE search('(title:Ronald OR content:Selma) AND NOT
category:Unknown', '{"mode":"standard"}')
"""
// Test Case 8: Edge case - all NULL fields
qt_test_case_8_all_null """
SELECT /*+SET_VAR(enable_common_expr_pushdown=true) */ count(*) FROM
${tableName}
- WHERE search('title:NonExistent OR content:NonExistent OR
category:NonExistent')
+ WHERE search('title:NonExistent OR content:NonExistent OR
category:NonExistent', '{"mode":"standard"}')
"""
// ------------------------------------------------------------------
@@ -206,7 +206,7 @@ suite("test_search_null_semantics", "p0") {
qt_nested_1_search """
SELECT /*+SET_VAR(enable_common_expr_pushdown=true) */ count(*) FROM
${nestedTable}
- WHERE search('(field_a:alpha OR field_b:beta) AND (field_c:gamma OR
field_d:delta)')
+ WHERE search('(field_a:alpha OR field_b:beta) AND (field_c:gamma OR
field_d:delta)', '{"mode":"standard"}')
"""
qt_nested_1_match """
@@ -217,13 +217,13 @@ suite("test_search_null_semantics", "p0") {
qt_nested_1_rows """
SELECT /*+SET_VAR(enable_common_expr_pushdown=true) */ id, field_a,
field_b, field_c, field_d
FROM ${nestedTable}
- WHERE search('(field_a:alpha OR field_b:beta) AND (field_c:gamma OR
field_d:delta)')
+ WHERE search('(field_a:alpha OR field_b:beta) AND (field_c:gamma OR
field_d:delta)', '{"mode":"standard"}')
ORDER BY id
"""
qt_nested_2_search """
SELECT /*+SET_VAR(enable_common_expr_pushdown=true) */ count(*) FROM
${nestedTable}
- WHERE search('field_a:target OR (field_b:beta AND (field_c:gamma OR
field_d:delta))')
+ WHERE search('field_a:target OR (field_b:beta AND (field_c:gamma OR
field_d:delta))', '{"mode":"standard"}')
"""
qt_nested_2_match """
@@ -233,7 +233,7 @@ suite("test_search_null_semantics", "p0") {
qt_nested_3_search """
SELECT /*+SET_VAR(enable_common_expr_pushdown=true) */ count(*) FROM
${nestedTable}
- WHERE search('NOT (field_a:forbidden OR field_b:forbidden)')
+ WHERE search('NOT (field_a:forbidden OR field_b:forbidden)',
'{"mode":"standard"}')
"""
qt_nested_3_match """
@@ -243,7 +243,7 @@ suite("test_search_null_semantics", "p0") {
qt_nested_4_search """
SELECT /*+SET_VAR(enable_common_expr_pushdown=true) */ count(*) FROM
${nestedTable}
- WHERE search('(field_a:alpha OR field_b:beta) AND NOT (field_c:exclude
OR field_d:exclude)')
+ WHERE search('(field_a:alpha OR field_b:beta) AND NOT (field_c:exclude
OR field_d:exclude)', '{"mode":"standard"}')
"""
qt_nested_4_match """
@@ -253,7 +253,7 @@ suite("test_search_null_semantics", "p0") {
qt_nested_5_search """
SELECT /*+SET_VAR(enable_common_expr_pushdown=true) */ count(*) FROM
${nestedTable}
- WHERE search('field_a:alpha OR field_b:beta OR field_c:gamma OR
field_d:delta')
+ WHERE search('field_a:alpha OR field_b:beta OR field_c:gamma OR
field_d:delta', '{"mode":"standard"}')
"""
qt_nested_5_match """
@@ -263,7 +263,7 @@ suite("test_search_null_semantics", "p0") {
qt_nested_6_search """
SELECT /*+SET_VAR(enable_common_expr_pushdown=true) */ count(*) FROM
${nestedTable}
- WHERE search('field_a:alpha AND (field_b:beta OR (field_c:gamma AND
field_d:delta))')
+ WHERE search('field_a:alpha AND (field_b:beta OR (field_c:gamma AND
field_d:delta))', '{"mode":"standard"}')
"""
qt_nested_6_match """
@@ -328,7 +328,7 @@ suite("test_search_null_semantics", "p0") {
qt_ternary_1_search """
SELECT /*+SET_VAR(enable_common_expr_pushdown=true) */ count(*) FROM
${ternaryTable}
- WHERE search('title:Ronald OR content:Selma')
+ WHERE search('title:Ronald OR content:Selma', '{"mode":"standard"}')
"""
qt_ternary_1_match """
@@ -338,7 +338,7 @@ suite("test_search_null_semantics", "p0") {
qt_ternary_1_search_rows """
SELECT /*+SET_VAR(enable_common_expr_pushdown=true) */ id, title,
content, category FROM ${ternaryTable}
- WHERE search('title:Ronald OR content:Selma')
+ WHERE search('title:Ronald OR content:Selma', '{"mode":"standard"}')
ORDER BY id
"""
@@ -350,7 +350,7 @@ suite("test_search_null_semantics", "p0") {
qt_ternary_2_search """
SELECT /*+SET_VAR(enable_common_expr_pushdown=true) */ count(*) FROM
${ternaryTable}
- WHERE search('title:Ronald AND category:politics')
+ WHERE search('title:Ronald AND category:politics',
'{"mode":"standard"}')
"""
qt_ternary_2_match """
@@ -360,17 +360,17 @@ suite("test_search_null_semantics", "p0") {
qt_ternary_3_search """
SELECT /*+SET_VAR(enable_common_expr_pushdown=true) */ count(*) FROM
${ternaryTable}
- WHERE search('NOT content:spam')
+ WHERE search('NOT content:spam', '{"mode":"standard"}')
"""
qt_ternary_3_external """
SELECT /*+SET_VAR(enable_common_expr_pushdown=true) */ count(*) FROM
${ternaryTable}
- WHERE NOT search('content:spam')
+ WHERE NOT search('content:spam', '{"mode":"standard"}')
"""
qt_ternary_4_search """
SELECT /*+SET_VAR(enable_common_expr_pushdown=true) */ count(*) FROM
${ternaryTable}
- WHERE search('title:Ronald OR content:Selma OR category:biography')
+ WHERE search('title:Ronald OR content:Selma OR category:biography',
'{"mode":"standard"}')
"""
qt_ternary_4_match """
@@ -380,7 +380,7 @@ suite("test_search_null_semantics", "p0") {
qt_ternary_5_search """
SELECT /*+SET_VAR(enable_common_expr_pushdown=true) */ count(*) FROM
${ternaryTable}
- WHERE search('(title:Ronald OR content:Selma) AND category:politics')
+ WHERE search('(title:Ronald OR content:Selma) AND category:politics',
'{"mode":"standard"}')
"""
qt_ternary_5_match """
@@ -390,7 +390,7 @@ suite("test_search_null_semantics", "p0") {
qt_ternary_6_search """
SELECT /*+SET_VAR(enable_common_expr_pushdown=true) */ count(*) FROM
${ternaryTable}
- WHERE search('title:Ronald OR NOT content:spam')
+ WHERE search('title:Ronald OR NOT content:spam', '{"mode":"standard"}')
"""
qt_ternary_6_match """
@@ -400,7 +400,7 @@ suite("test_search_null_semantics", "p0") {
qt_ternary_7_all_null """
SELECT /*+SET_VAR(enable_common_expr_pushdown=true) */ count(*) FROM
${ternaryTable}
- WHERE search('title:NonExistent OR content:NonExistent OR
category:NonExistent')
+ WHERE search('title:NonExistent OR content:NonExistent OR
category:NonExistent', '{"mode":"standard"}')
"""
sql "DROP TABLE IF EXISTS ${ternaryTable}"
diff --git
a/regression-test/suites/search/test_search_vs_match_consistency.groovy
b/regression-test/suites/search/test_search_vs_match_consistency.groovy
index 5b2c7b4b664..61afe470f32 100644
--- a/regression-test/suites/search/test_search_vs_match_consistency.groovy
+++ b/regression-test/suites/search/test_search_vs_match_consistency.groovy
@@ -113,14 +113,14 @@ suite("test_search_vs_match_consistency", "p0") {
qt_keyword_case_search """
SELECT /*+SET_VAR(enable_common_expr_pushdown=true) */ title FROM
${keywordTable}
- WHERE search('redirect:All("Rainbowman")')
+ WHERE search('redirect:All("Rainbowman")', '{"mode":"standard"}')
ORDER BY title
"""
// Test Suite 1: Basic OR query consistency
qt_test_1_1_search """
SELECT /*+SET_VAR(enable_common_expr_pushdown=true) */ count(*) FROM
${tableName}
- WHERE search('title:Ronald OR title:Selma')
+ WHERE search('title:Ronald OR title:Selma', '{"mode":"standard"}')
"""
qt_test_1_1_match """
@@ -131,7 +131,7 @@ suite("test_search_vs_match_consistency", "p0") {
// Test 1.2: OR across different fields
qt_test_1_2_search """
SELECT /*+SET_VAR(enable_common_expr_pushdown=true) */ count(*) FROM
${tableName}
- WHERE search('title:Ronald OR content:Selma')
+ WHERE search('title:Ronald OR content:Selma', '{"mode":"standard"}')
"""
qt_test_1_2_match """
@@ -142,7 +142,7 @@ suite("test_search_vs_match_consistency", "p0") {
// Test 1.3: Complex OR with ALL operation
qt_test_1_3_search """
SELECT /*+SET_VAR(enable_common_expr_pushdown=true) */ count(*) FROM
${tableName}
- WHERE search('title:Ronald OR (content:ALL(Selma Blair))')
+ WHERE search('title:Ronald OR (content:ALL(Selma Blair))',
'{"mode":"standard"}')
"""
qt_test_1_3_match """
@@ -153,71 +153,71 @@ suite("test_search_vs_match_consistency", "p0") {
// Test Suite 2: NOT query consistency
qt_test_2_1_internal_not """
SELECT /*+SET_VAR(enable_common_expr_pushdown=true) */ count(*) FROM
${tableName}
- WHERE search('NOT content:Round')
+ WHERE search('NOT content:Round', '{"mode":"standard"}')
"""
qt_test_2_1_external_not """
SELECT /*+SET_VAR(enable_common_expr_pushdown=true) */ count(*) FROM
${tableName}
- WHERE not search('content:Round')
+ WHERE not search('content:Round', '{"mode":"standard"}')
"""
// Test 2.2: NOT with different fields
qt_test_2_2_internal_not """
SELECT /*+SET_VAR(enable_common_expr_pushdown=true) */ count(*) FROM
${tableName}
- WHERE search('NOT title:Ronald')
+ WHERE search('NOT title:Ronald', '{"mode":"standard"}')
"""
qt_test_2_2_external_not """
SELECT /*+SET_VAR(enable_common_expr_pushdown=true) */ count(*) FROM
${tableName}
- WHERE not search('title:Ronald')
+ WHERE not search('title:Ronald', '{"mode":"standard"}')
"""
// Test 2.3: NOT with complex expression
qt_test_2_3_internal_not """
SELECT /*+SET_VAR(enable_common_expr_pushdown=true) */ count(*) FROM
${tableName}
- WHERE search('NOT (title:Ronald AND content:biography)')
+ WHERE search('NOT (title:Ronald AND content:biography)',
'{"mode":"standard"}')
"""
qt_test_2_3_external_not """
SELECT /*+SET_VAR(enable_common_expr_pushdown=true) */ count(*) FROM
${tableName}
- WHERE not search('title:Ronald AND content:biography')
+ WHERE not search('title:Ronald AND content:biography',
'{"mode":"standard"}')
"""
// Test Suite 3: NULL value behavior in OR queries
qt_test_3_1_or_with_null """
SELECT /*+SET_VAR(enable_common_expr_pushdown=true) */ id, title,
content FROM ${tableName}
- WHERE search('title:NonExistent OR content:Ronald')
+ WHERE search('title:NonExistent OR content:Ronald',
'{"mode":"standard"}')
ORDER BY id
"""
qt_test_3_2_or_multiple_null """
SELECT /*+SET_VAR(enable_common_expr_pushdown=true) */ id, title,
content FROM ${tableName}
- WHERE search('title:Mystery OR content:Round')
+ WHERE search('title:Mystery OR content:Round', '{"mode":"standard"}')
ORDER BY id
"""
// Test Suite 4: AND query behavior with NULLs
qt_test_4_1_and_with_null """
SELECT /*+SET_VAR(enable_common_expr_pushdown=true) */ id, title,
content FROM ${tableName}
- WHERE search('title:Ronald AND content:biography')
+ WHERE search('title:Ronald AND content:biography',
'{"mode":"standard"}')
ORDER BY id
"""
// Test Suite 5: Edge cases and complex scenarios
qt_test_5_1_empty_string """
SELECT /*+SET_VAR(enable_common_expr_pushdown=true) */ count(*) FROM
${tableName}
- WHERE search('title:"" OR content:Round')
+ WHERE search('title:"" OR content:Round', '{"mode":"standard"}')
"""
qt_test_5_2_complex_nested """
SELECT /*+SET_VAR(enable_common_expr_pushdown=true) */ count(*) FROM
${tableName}
- WHERE search('(title:Ronald OR title:Selma) AND NOT (content:Round AND
author:NonExistent)')
+ WHERE search('(title:Ronald OR title:Selma) AND NOT (content:Round AND
author:NonExistent)', '{"mode":"standard"}')
"""
// Test Suite 6: Performance and consistency verification
qt_test_6_1_large_or_search """
SELECT /*+SET_VAR(enable_common_expr_pushdown=true) */ count(*) FROM
${tableName}
- WHERE search('title:Ronald OR title:Selma OR content:Round OR
content:biography OR author:Smith OR tags:history')
+ WHERE search('title:Ronald OR title:Selma OR content:Round OR
content:biography OR author:Smith OR tags:history', '{"mode":"standard"}')
"""
qt_test_6_1_large_or_match """
@@ -281,7 +281,7 @@ suite("test_search_vs_match_consistency", "p0") {
// Mandy/Kesha consistency checks
qt_man_pat_1_search """
SELECT /*+SET_VAR(enable_common_expr_pushdown=true) */ count(*) FROM
${mandyTable}
- WHERE search('content:ALL("Mandy Patinkin") OR NOT
(content:ANY("Kesha"))')
+ WHERE search('content:ALL("Mandy Patinkin") OR NOT
(content:ANY("Kesha"))', '{"mode":"standard"}')
"""
qt_man_pat_1_match """
@@ -294,7 +294,7 @@ suite("test_search_vs_match_consistency", "p0") {
CASE WHEN title IS NULL THEN 'NULL' ELSE 'NOT_NULL' END AS
title_status,
CASE WHEN content IS NULL THEN 'NULL' ELSE 'NOT_NULL' END AS
content_status
FROM ${mandyTable}
- WHERE search('content:ALL("Mandy Patinkin") OR NOT
(content:ANY("Kesha"))')
+ WHERE search('content:ALL("Mandy Patinkin") OR NOT
(content:ANY("Kesha"))', '{"mode":"standard"}')
ORDER BY id
"""
@@ -309,7 +309,7 @@ suite("test_search_vs_match_consistency", "p0") {
qt_man_pat_2_search_or """
SELECT /*+SET_VAR(enable_common_expr_pushdown=true) */ count(*) FROM
${mandyTable}
- WHERE search('title:Mandy OR content:Kesha')
+ WHERE search('title:Mandy OR content:Kesha', '{"mode":"standard"}')
"""
qt_man_pat_2_match_or """
@@ -319,7 +319,7 @@ suite("test_search_vs_match_consistency", "p0") {
qt_man_pat_2_search_or_ids """
SELECT /*+SET_VAR(enable_common_expr_pushdown=true) */ id FROM
${mandyTable}
- WHERE search('title:Mandy OR content:Kesha')
+ WHERE search('title:Mandy OR content:Kesha', '{"mode":"standard"}')
ORDER BY id
"""
@@ -331,7 +331,7 @@ suite("test_search_vs_match_consistency", "p0") {
qt_man_pat_3_search_and """
SELECT /*+SET_VAR(enable_common_expr_pushdown=true) */ count(*) FROM
${mandyTable}
- WHERE search('title:Mandy AND category:biography')
+ WHERE search('title:Mandy AND category:biography',
'{"mode":"standard"}')
"""
qt_man_pat_3_match_and """
@@ -341,7 +341,7 @@ suite("test_search_vs_match_consistency", "p0") {
qt_man_pat_4_search_not """
SELECT /*+SET_VAR(enable_common_expr_pushdown=true) */ count(*) FROM
${mandyTable}
- WHERE search('NOT content:Kesha')
+ WHERE search('NOT content:Kesha', '{"mode":"standard"}')
"""
qt_man_pat_4_match_not """
@@ -351,7 +351,7 @@ suite("test_search_vs_match_consistency", "p0") {
qt_man_pat_5_search_nested """
SELECT /*+SET_VAR(enable_common_expr_pushdown=true) */ count(*) FROM
${mandyTable}
- WHERE search('(title:Mandy OR content:Kesha) AND category:music')
+ WHERE search('(title:Mandy OR content:Kesha) AND category:music',
'{"mode":"standard"}')
"""
qt_man_pat_5_match_nested """
@@ -406,7 +406,7 @@ suite("test_search_vs_match_consistency", "p0") {
qt_fred_1_search """
SELECT /*+SET_VAR(enable_common_expr_pushdown=true) */ count(*) FROM
${fredTable}
- WHERE search('title:Fred OR NOT content:ANY("Rahul Gandhi")')
+ WHERE search('title:Fred OR NOT content:ANY("Rahul Gandhi")',
'{"mode":"standard"}')
"""
qt_fred_1_match """
@@ -416,7 +416,7 @@ suite("test_search_vs_match_consistency", "p0") {
qt_fred_1_search_rows """
SELECT /*+SET_VAR(enable_common_expr_pushdown=true) */ id, title,
content FROM ${fredTable}
- WHERE search('title:Fred OR NOT content:ANY("Rahul Gandhi")')
+ WHERE search('title:Fred OR NOT content:ANY("Rahul Gandhi")',
'{"mode":"standard"}')
ORDER BY id
"""
@@ -428,22 +428,22 @@ suite("test_search_vs_match_consistency", "p0") {
qt_fred_2_title_only """
SELECT /*+SET_VAR(enable_common_expr_pushdown=true) */ count(*) FROM
${fredTable}
- WHERE search('title:Fred')
+ WHERE search('title:Fred', '{"mode":"standard"}')
"""
qt_fred_2_content_any """
SELECT /*+SET_VAR(enable_common_expr_pushdown=true) */ count(*) FROM
${fredTable}
- WHERE search('content:ANY("Rahul Gandhi")')
+ WHERE search('content:ANY("Rahul Gandhi")', '{"mode":"standard"}')
"""
qt_fred_2_not_content """
SELECT /*+SET_VAR(enable_common_expr_pushdown=true) */ count(*) FROM
${fredTable}
- WHERE search('NOT content:ANY("Rahul Gandhi")')
+ WHERE search('NOT content:ANY("Rahul Gandhi")', '{"mode":"standard"}')
"""
qt_fred_3_or_not """
SELECT /*+SET_VAR(enable_common_expr_pushdown=true) */ count(*) FROM
${fredTable}
- WHERE search('title:Fred OR NOT title:Random')
+ WHERE search('title:Fred OR NOT title:Random', '{"mode":"standard"}')
"""
qt_fred_3_or_not_match """
@@ -453,7 +453,7 @@ suite("test_search_vs_match_consistency", "p0") {
qt_fred_4_and_not """
SELECT /*+SET_VAR(enable_common_expr_pushdown=true) */ count(*) FROM
${fredTable}
- WHERE search('title:Fred AND NOT content:ANY("Rahul Gandhi")')
+ WHERE search('title:Fred AND NOT content:ANY("Rahul Gandhi")',
'{"mode":"standard"}')
"""
qt_fred_4_and_not_match """
@@ -463,7 +463,7 @@ suite("test_search_vs_match_consistency", "p0") {
qt_fred_5_nested """
SELECT /*+SET_VAR(enable_common_expr_pushdown=true) */ count(*) FROM
${fredTable}
- WHERE search('(title:Fred OR title:John) OR NOT (content:ANY("Rahul
Gandhi") OR content:ANY("politics"))')
+ WHERE search('(title:Fred OR title:John) OR NOT (content:ANY("Rahul
Gandhi") OR content:ANY("politics"))', '{"mode":"standard"}')
"""
qt_fred_5_nested_match """
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]