Skip to content

Commit 3a55f70

Browse files
branch-4.0: [feature](search) Add multi-field search support with fields parameter #59845 (#60010)
Cherry-picked from #59845 Co-authored-by: Jack <jiangkai@selectdb.com>
1 parent 0928d83 commit 3a55f70

File tree

9 files changed

+1975
-366
lines changed

9 files changed

+1975
-366
lines changed

fe/fe-core/src/main/java/org/apache/doris/analysis/SearchPredicate.java

Lines changed: 29 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -87,12 +87,12 @@ protected void toThrift(TExprNode msg) {
8787
// Print QsPlan details
8888
if (qsPlan != null) {
8989
LOG.info("SearchPredicate.toThrift: QsPlan fieldBindings.size={}",
90-
qsPlan.fieldBindings != null ? qsPlan.fieldBindings.size() : 0);
91-
if (qsPlan.fieldBindings != null) {
92-
for (int i = 0; i < qsPlan.fieldBindings.size(); i++) {
93-
SearchDslParser.QsFieldBinding binding = qsPlan.fieldBindings.get(i);
90+
qsPlan.getFieldBindings() != null ? qsPlan.getFieldBindings().size() : 0);
91+
if (qsPlan.getFieldBindings() != null) {
92+
for (int i = 0; i < qsPlan.getFieldBindings().size(); i++) {
93+
SearchDslParser.QsFieldBinding binding = qsPlan.getFieldBindings().get(i);
9494
LOG.info("SearchPredicate.toThrift: binding[{}] fieldName='{}', slotIndex={}",
95-
i, binding.fieldName, binding.slotIndex);
95+
i, binding.getFieldName(), binding.getSlotIndex());
9696
}
9797
}
9898
}
@@ -141,14 +141,14 @@ public int hashCode() {
141141
private TSearchParam buildThriftParam() {
142142
TSearchParam param = new TSearchParam();
143143
param.setOriginalDsl(dslString);
144-
param.setRoot(convertQsNodeToThrift(qsPlan.root));
144+
param.setRoot(convertQsNodeToThrift(qsPlan.getRoot()));
145145

146146
List<TSearchFieldBinding> bindings = new ArrayList<>();
147-
for (int i = 0; i < qsPlan.fieldBindings.size(); i++) {
148-
SearchDslParser.QsFieldBinding binding = qsPlan.fieldBindings.get(i);
147+
for (int i = 0; i < qsPlan.getFieldBindings().size(); i++) {
148+
SearchDslParser.QsFieldBinding binding = qsPlan.getFieldBindings().get(i);
149149
TSearchFieldBinding thriftBinding = new TSearchFieldBinding();
150150

151-
String fieldPath = binding.fieldName;
151+
String fieldPath = binding.getFieldName();
152152
thriftBinding.setFieldName(fieldPath);
153153

154154
// Check if this is a variant subcolumn (contains dot)
@@ -175,9 +175,10 @@ private TSearchParam buildThriftParam() {
175175
SlotRef slotRef = (SlotRef) this.children.get(i);
176176
int actualSlotId = slotRef.getSlotId().asInt();
177177
thriftBinding.setSlotIndex(actualSlotId);
178-
LOG.info("buildThriftParam: binding field='{}', actual slotId={}", binding.fieldName, actualSlotId);
178+
LOG.info("buildThriftParam: binding field='{}', actual slotId={}",
179+
binding.getFieldName(), actualSlotId);
179180
} else {
180-
LOG.warn("buildThriftParam: No corresponding SlotRef for field '{}'", binding.fieldName);
181+
LOG.warn("buildThriftParam: No corresponding SlotRef for field '{}'", binding.getFieldName());
181182
thriftBinding.setSlotIndex(i); // fallback to position
182183
}
183184

@@ -229,10 +230,10 @@ private boolean isExplainVerboseContext() {
229230

230231
private List<String> buildDslAstExplainLines() {
231232
List<String> lines = new ArrayList<>();
232-
if (qsPlan == null || qsPlan.root == null) {
233+
if (qsPlan == null || qsPlan.getRoot() == null) {
233234
return lines;
234235
}
235-
TSearchClause rootClause = convertQsNodeToThrift(qsPlan.root);
236+
TSearchClause rootClause = convertQsNodeToThrift(qsPlan.getRoot());
236237
appendClauseExplain(rootClause, lines, 0);
237238
return lines;
238239
}
@@ -257,11 +258,11 @@ private void appendClauseExplain(TSearchClause clause, List<String> lines, int d
257258

258259
private List<String> buildFieldBindingExplainLines() {
259260
List<String> lines = new ArrayList<>();
260-
if (qsPlan == null || qsPlan.fieldBindings == null || qsPlan.fieldBindings.isEmpty()) {
261+
if (qsPlan == null || qsPlan.getFieldBindings() == null || qsPlan.getFieldBindings().isEmpty()) {
261262
return lines;
262263
}
263-
IntStream.range(0, qsPlan.fieldBindings.size()).forEach(index -> {
264-
SearchDslParser.QsFieldBinding binding = qsPlan.fieldBindings.get(index);
264+
IntStream.range(0, qsPlan.getFieldBindings().size()).forEach(index -> {
265+
SearchDslParser.QsFieldBinding binding = qsPlan.getFieldBindings().get(index);
265266
String slotDesc = "<unbound>";
266267
if (index < children.size() && children.get(index) instanceof SlotRef) {
267268
SlotRef slotRef = (SlotRef) children.get(index);
@@ -271,7 +272,7 @@ private List<String> buildFieldBindingExplainLines() {
271272
} else if (index < children.size()) {
272273
slotDesc = children.get(index).toSqlWithoutTbl();
273274
}
274-
lines.add(binding.fieldName + " -> " + slotDesc);
275+
lines.add(binding.getFieldName() + " -> " + slotDesc);
275276
});
276277
return lines;
277278
}
@@ -303,29 +304,29 @@ private TSearchClause convertQsNodeToThrift(
303304
TSearchClause clause = new TSearchClause();
304305

305306
// Convert clause type
306-
clause.setClauseType(node.type.name());
307+
clause.setClauseType(node.getType().name());
307308

308-
if (node.field != null) {
309-
clause.setFieldName(node.field);
309+
if (node.getField() != null) {
310+
clause.setFieldName(node.getField());
310311
}
311312

312-
if (node.value != null) {
313-
clause.setValue(node.value);
313+
if (node.getValue() != null) {
314+
clause.setValue(node.getValue());
314315
}
315316

316317
// Convert occur type for Lucene-style boolean queries
317-
if (node.occur != null) {
318-
clause.setOccur(convertQsOccurToThrift(node.occur));
318+
if (node.getOccur() != null) {
319+
clause.setOccur(convertQsOccurToThrift(node.getOccur()));
319320
}
320321

321322
// Convert minimum_should_match for OCCUR_BOOLEAN
322-
if (node.minimumShouldMatch != null) {
323-
clause.setMinimumShouldMatch(node.minimumShouldMatch);
323+
if (node.getMinimumShouldMatch() != null) {
324+
clause.setMinimumShouldMatch(node.getMinimumShouldMatch());
324325
}
325326

326-
if (node.children != null && !node.children.isEmpty()) {
327+
if (node.getChildren() != null && !node.getChildren().isEmpty()) {
327328
List<TSearchClause> childClauses = new ArrayList<>();
328-
for (SearchDslParser.QsNode child : node.children) {
329+
for (SearchDslParser.QsNode child : node.getChildren()) {
329330
childClauses.add(convertQsNodeToThrift(child));
330331
}
331332
clause.setChildren(childClauses);

fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/RewriteSearchToSlots.java

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,7 @@ private Expression rewriteSearch(Search search, LogicalOlapScan scan) {
9393
try {
9494
// Parse DSL to get field bindings
9595
SearchDslParser.QsPlan qsPlan = search.getQsPlan();
96-
if (qsPlan == null || qsPlan.fieldBindings == null || qsPlan.fieldBindings.isEmpty()) {
96+
if (qsPlan == null || qsPlan.getFieldBindings() == null || qsPlan.getFieldBindings().isEmpty()) {
9797
LOG.warn("Search function has no field bindings: {}", search.getDslString());
9898
return search;
9999
}
@@ -102,8 +102,8 @@ private Expression rewriteSearch(Search search, LogicalOlapScan scan) {
102102

103103
// Create slot reference children from field bindings
104104
List<Expression> slotChildren = new ArrayList<>();
105-
for (SearchDslParser.QsFieldBinding binding : qsPlan.fieldBindings) {
106-
String originalFieldName = binding.fieldName;
105+
for (SearchDslParser.QsFieldBinding binding : qsPlan.getFieldBindings()) {
106+
String originalFieldName = binding.getFieldName();
107107
Expression childExpr;
108108
String normalizedFieldName;
109109

@@ -151,14 +151,14 @@ private Expression rewriteSearch(Search search, LogicalOlapScan scan) {
151151
}
152152

153153
normalizedFields.put(originalFieldName, normalizedFieldName);
154-
binding.fieldName = normalizedFieldName;
154+
binding.setFieldName(normalizedFieldName);
155155
slotChildren.add(childExpr);
156156
}
157157

158158
LOG.info("Rewriting search function: dsl='{}' with {} slot children",
159159
search.getDslString(), slotChildren.size());
160160

161-
normalizePlanFields(qsPlan.root, normalizedFields);
161+
normalizePlanFields(qsPlan.getRoot(), normalizedFields);
162162

163163
// Create SearchExpression with slot children
164164
return new SearchExpression(search.getDslString(), qsPlan, slotChildren);
@@ -182,16 +182,16 @@ private void normalizePlanFields(SearchDslParser.QsNode node, Map<String, String
182182
if (node == null) {
183183
return;
184184
}
185-
if (node.field != null) {
185+
if (node.getField() != null) {
186186
for (Map.Entry<String, String> entry : normalized.entrySet()) {
187-
if (entry.getKey().equalsIgnoreCase(node.field)) {
188-
node.field = entry.getValue();
187+
if (entry.getKey().equalsIgnoreCase(node.getField())) {
188+
node.setField(entry.getValue());
189189
break;
190190
}
191191
}
192192
}
193-
if (node.children != null) {
194-
for (SearchDslParser.QsNode child : node.children) {
193+
if (node.getChildren() != null) {
194+
for (SearchDslParser.QsNode child : node.getChildren()) {
195195
normalizePlanFields(child, normalized);
196196
}
197197
}

0 commit comments

Comments
 (0)