Skip to content

Commit bd40af5

Browse files
authored
Search Command Revamp (#4152)
Signed-off-by: Vamsi Manohar <[email protected]>
1 parent c518022 commit bd40af5

File tree

70 files changed

+3768
-237
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

70 files changed

+3768
-237
lines changed

core/src/main/java/org/opensearch/sql/analysis/Analyzer.java

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,7 @@
8686
import org.opensearch.sql.ast.tree.Rename;
8787
import org.opensearch.sql.ast.tree.Reverse;
8888
import org.opensearch.sql.ast.tree.Rex;
89+
import org.opensearch.sql.ast.tree.Search;
8990
import org.opensearch.sql.ast.tree.Sort;
9091
import org.opensearch.sql.ast.tree.Sort.SortOption;
9192
import org.opensearch.sql.ast.tree.SubqueryAlias;
@@ -278,6 +279,18 @@ public LogicalPlan visitLimit(Limit node, AnalysisContext context) {
278279
return new LogicalLimit(child, node.getLimit(), node.getOffset());
279280
}
280281

282+
@Override
283+
public LogicalPlan visitSearch(Search node, AnalysisContext context) {
284+
LogicalPlan child = node.getChild().get(0).accept(this, context);
285+
Function queryStringFunc =
286+
AstDSL.function(
287+
"query_string",
288+
AstDSL.unresolvedArg("query", AstDSL.stringLiteral(node.getQueryString())));
289+
290+
Expression analyzed = expressionAnalyzer.analyze(queryStringFunc, context);
291+
return new LogicalFilter(child, analyzed);
292+
}
293+
281294
@Override
282295
public LogicalPlan visitFilter(Filter node, AnalysisContext context) {
283296
LogicalPlan child = node.getChild().get(0).accept(this, context);

core/src/main/java/org/opensearch/sql/ast/AbstractNodeVisitor.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@
7575
import org.opensearch.sql.ast.tree.Reverse;
7676
import org.opensearch.sql.ast.tree.Rex;
7777
import org.opensearch.sql.ast.tree.SPath;
78+
import org.opensearch.sql.ast.tree.Search;
7879
import org.opensearch.sql.ast.tree.Sort;
7980
import org.opensearch.sql.ast.tree.SubqueryAlias;
8081
import org.opensearch.sql.ast.tree.TableFunction;
@@ -131,6 +132,10 @@ public T visitTableFunction(TableFunction node, C context) {
131132
return visitChildren(node, context);
132133
}
133134

135+
public T visitSearch(Search node, C context) {
136+
return visitChildren(node, context);
137+
}
138+
134139
public T visitFilter(Filter node, C context) {
135140
return visitChildren(node, context);
136141
}

core/src/main/java/org/opensearch/sql/ast/dsl/AstDSL.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@
7171
import org.opensearch.sql.ast.tree.RelationSubquery;
7272
import org.opensearch.sql.ast.tree.Rename;
7373
import org.opensearch.sql.ast.tree.SPath;
74+
import org.opensearch.sql.ast.tree.Search;
7475
import org.opensearch.sql.ast.tree.Sort;
7576
import org.opensearch.sql.ast.tree.Sort.SortOption;
7677
import org.opensearch.sql.ast.tree.SpanBin;
@@ -109,6 +110,10 @@ public UnresolvedPlan describe(String tableName) {
109110
return new DescribeRelation(qualifiedName(tableName));
110111
}
111112

113+
public static UnresolvedPlan search(UnresolvedPlan input, String queryString) {
114+
return new Search(input, queryString);
115+
}
116+
112117
public UnresolvedPlan subqueryAlias(UnresolvedPlan child, String alias) {
113118
return new SubqueryAlias(child, alias);
114119
}
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
/*
2+
* Copyright OpenSearch Contributors
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
package org.opensearch.sql.ast.expression;
7+
8+
import java.util.Arrays;
9+
import java.util.List;
10+
import lombok.EqualsAndHashCode;
11+
import lombok.Getter;
12+
import lombok.RequiredArgsConstructor;
13+
import lombok.ToString;
14+
15+
/** Search expression for AND operator. */
16+
@Getter
17+
@RequiredArgsConstructor
18+
@EqualsAndHashCode(callSuper = false)
19+
@ToString
20+
public class SearchAnd extends SearchExpression {
21+
22+
private final SearchExpression left;
23+
private final SearchExpression right;
24+
25+
@Override
26+
public String toQueryString() {
27+
return left.toQueryString() + " AND " + right.toQueryString();
28+
}
29+
30+
@Override
31+
public List<? extends UnresolvedExpression> getChild() {
32+
return Arrays.asList(left, right);
33+
}
34+
}
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
/*
2+
* Copyright OpenSearch Contributors
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
package org.opensearch.sql.ast.expression;
7+
8+
import java.util.Arrays;
9+
import java.util.List;
10+
import lombok.EqualsAndHashCode;
11+
import lombok.Getter;
12+
import lombok.RequiredArgsConstructor;
13+
import lombok.ToString;
14+
import org.opensearch.sql.utils.QueryStringUtils;
15+
16+
/** Search expression for field comparisons. */
17+
@Getter
18+
@RequiredArgsConstructor
19+
@EqualsAndHashCode(callSuper = false)
20+
@ToString
21+
public class SearchComparison extends SearchExpression {
22+
23+
public enum Operator {
24+
EQUALS("="),
25+
NOT_EQUALS("!="),
26+
LESS_THAN("<"),
27+
LESS_OR_EQUAL("<="),
28+
GREATER_THAN(">"),
29+
GREATER_OR_EQUAL(">=");
30+
31+
private final String symbol;
32+
33+
Operator(String symbol) {
34+
this.symbol = symbol;
35+
}
36+
37+
public String getSymbol() {
38+
return symbol;
39+
}
40+
}
41+
42+
private final Field field;
43+
private final Operator operator;
44+
private final SearchLiteral value;
45+
46+
@Override
47+
public String toQueryString() {
48+
String fieldName = QueryStringUtils.escapeFieldName(field.getField().toString());
49+
String valueStr = value.toQueryString();
50+
switch (operator) {
51+
case NOT_EQUALS:
52+
return "( _exists_:" + fieldName + " AND NOT " + fieldName + ":" + valueStr + " )";
53+
case GREATER_THAN:
54+
return fieldName + ":>" + valueStr;
55+
case GREATER_OR_EQUAL:
56+
return fieldName + ":>=" + valueStr;
57+
case LESS_THAN:
58+
return fieldName + ":<" + valueStr;
59+
case LESS_OR_EQUAL:
60+
return fieldName + ":<=" + valueStr;
61+
default:
62+
return fieldName + ":" + valueStr;
63+
}
64+
}
65+
66+
@Override
67+
public List<? extends UnresolvedExpression> getChild() {
68+
return Arrays.asList(field, value);
69+
}
70+
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
/*
2+
* Copyright OpenSearch Contributors
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
package org.opensearch.sql.ast.expression;
7+
8+
import org.opensearch.sql.ast.AbstractNodeVisitor;
9+
10+
/** Base class for search expressions that get converted to query_string syntax. */
11+
public abstract class SearchExpression extends UnresolvedExpression {
12+
13+
/**
14+
* Convert this search expression to query_string syntax.
15+
*
16+
* @return the query string representation
17+
*/
18+
public abstract String toQueryString();
19+
20+
@Override
21+
public <R, C> R accept(AbstractNodeVisitor<R, C> nodeVisitor, C context) {
22+
return nodeVisitor.visitChildren(this, context);
23+
}
24+
}
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
/*
2+
* Copyright OpenSearch Contributors
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
package org.opensearch.sql.ast.expression;
7+
8+
import java.util.Collections;
9+
import java.util.List;
10+
import lombok.EqualsAndHashCode;
11+
import lombok.Getter;
12+
import lombok.RequiredArgsConstructor;
13+
import lombok.ToString;
14+
15+
/** Search expression for grouped expressions (parentheses). */
16+
@Getter
17+
@RequiredArgsConstructor
18+
@EqualsAndHashCode(callSuper = false)
19+
@ToString
20+
public class SearchGroup extends SearchExpression {
21+
22+
private final SearchExpression expression;
23+
24+
@Override
25+
public String toQueryString() {
26+
return "(" + expression.toQueryString() + ")";
27+
}
28+
29+
@Override
30+
public List<? extends UnresolvedExpression> getChild() {
31+
return Collections.singletonList(expression);
32+
}
33+
}
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
/*
2+
* Copyright OpenSearch Contributors
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
package org.opensearch.sql.ast.expression;
7+
8+
import java.util.ArrayList;
9+
import java.util.List;
10+
import java.util.stream.Collectors;
11+
import lombok.EqualsAndHashCode;
12+
import lombok.Getter;
13+
import lombok.RequiredArgsConstructor;
14+
import lombok.ToString;
15+
import org.opensearch.sql.utils.QueryStringUtils;
16+
17+
/** Search expression for IN operator. */
18+
@Getter
19+
@RequiredArgsConstructor
20+
@EqualsAndHashCode(callSuper = false)
21+
@ToString
22+
public class SearchIn extends SearchExpression {
23+
24+
private final Field field;
25+
private final List<SearchLiteral> values;
26+
27+
@Override
28+
public String toQueryString() {
29+
String fieldName = QueryStringUtils.escapeFieldName(field.getField().toString());
30+
String valueList =
31+
values.stream().map(SearchLiteral::toQueryString).collect(Collectors.joining(" OR "));
32+
33+
return fieldName + ":( " + valueList + " )";
34+
}
35+
36+
@Override
37+
public List<? extends UnresolvedExpression> getChild() {
38+
List<UnresolvedExpression> children = new ArrayList<>();
39+
children.add(field);
40+
// SearchLiteral extends SearchExpression which extends UnresolvedExpression
41+
children.addAll(values);
42+
return children;
43+
}
44+
}
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
/*
2+
* Copyright OpenSearch Contributors
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
package org.opensearch.sql.ast.expression;
7+
8+
import java.util.Collections;
9+
import java.util.List;
10+
import lombok.AllArgsConstructor;
11+
import lombok.EqualsAndHashCode;
12+
import lombok.Getter;
13+
import lombok.ToString;
14+
import org.opensearch.sql.utils.QueryStringUtils;
15+
16+
/** Search expression for standalone literals. */
17+
@Getter
18+
@AllArgsConstructor
19+
@EqualsAndHashCode(callSuper = false)
20+
@ToString
21+
public class SearchLiteral extends SearchExpression {
22+
23+
private final UnresolvedExpression literal;
24+
private final boolean isPhrase;
25+
26+
@Override
27+
public String toQueryString() {
28+
if (literal instanceof Literal) {
29+
Literal lit = (Literal) literal;
30+
Object val = lit.getValue();
31+
32+
// Numbers don't need escaping
33+
if (val instanceof Number) {
34+
return val.toString();
35+
}
36+
37+
// Strings
38+
if (val instanceof String) {
39+
String str = (String) val;
40+
41+
// Phrase search - preserve quotes
42+
if (isPhrase) {
43+
// Escape special chars inside the phrase
44+
str = QueryStringUtils.escapeLuceneSpecialCharacters(str);
45+
return "\"" + str + "\"";
46+
}
47+
48+
// Regular string - escape special characters
49+
return QueryStringUtils.escapeLuceneSpecialCharacters(str);
50+
}
51+
}
52+
53+
// Default: escape the text representation
54+
String text = literal.toString();
55+
return QueryStringUtils.escapeLuceneSpecialCharacters(text);
56+
}
57+
58+
@Override
59+
public List<? extends UnresolvedExpression> getChild() {
60+
return Collections.singletonList(literal);
61+
}
62+
}
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
/*
2+
* Copyright OpenSearch Contributors
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
package org.opensearch.sql.ast.expression;
7+
8+
import java.util.Collections;
9+
import java.util.List;
10+
import lombok.EqualsAndHashCode;
11+
import lombok.Getter;
12+
import lombok.RequiredArgsConstructor;
13+
import lombok.ToString;
14+
15+
/** Search expression for NOT operator. */
16+
@Getter
17+
@RequiredArgsConstructor
18+
@EqualsAndHashCode(callSuper = false)
19+
@ToString
20+
public class SearchNot extends SearchExpression {
21+
22+
private final SearchExpression expression;
23+
24+
@Override
25+
public String toQueryString() {
26+
return "NOT(" + expression.toQueryString() + ")";
27+
}
28+
29+
@Override
30+
public List<? extends UnresolvedExpression> getChild() {
31+
return Collections.singletonList(expression);
32+
}
33+
}

0 commit comments

Comments
 (0)