-
Notifications
You must be signed in to change notification settings - Fork 138
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Support nested function in SELECT clause (#1490)
Users can now query nested fields in an index. --------- Signed-off-by: forestmvey <forestv@bitquilltech.com>
- Loading branch information
1 parent
7584f79
commit fbc72a4
Showing
62 changed files
with
2,162 additions
and
135 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
111 changes: 111 additions & 0 deletions
111
core/src/main/java/org/opensearch/sql/analysis/NestedAnalyzer.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,111 @@ | ||
/* | ||
* Copyright OpenSearch Contributors | ||
* SPDX-License-Identifier: Apache-2.0 | ||
*/ | ||
|
||
package org.opensearch.sql.analysis; | ||
|
||
import static org.opensearch.sql.data.type.ExprCoreType.STRING; | ||
|
||
import java.util.ArrayList; | ||
import java.util.Arrays; | ||
import java.util.List; | ||
import java.util.Map; | ||
import lombok.RequiredArgsConstructor; | ||
import org.opensearch.sql.ast.AbstractNodeVisitor; | ||
import org.opensearch.sql.ast.expression.Alias; | ||
import org.opensearch.sql.ast.expression.Function; | ||
import org.opensearch.sql.ast.expression.QualifiedName; | ||
import org.opensearch.sql.ast.expression.UnresolvedExpression; | ||
import org.opensearch.sql.expression.NamedExpression; | ||
import org.opensearch.sql.expression.ReferenceExpression; | ||
import org.opensearch.sql.expression.function.BuiltinFunctionName; | ||
import org.opensearch.sql.planner.logical.LogicalNested; | ||
import org.opensearch.sql.planner.logical.LogicalPlan; | ||
|
||
/** | ||
* Analyze the Nested Function in the {@link AnalysisContext} to construct the {@link | ||
* LogicalPlan}. | ||
*/ | ||
@RequiredArgsConstructor | ||
public class NestedAnalyzer extends AbstractNodeVisitor<LogicalPlan, AnalysisContext> { | ||
private final List<NamedExpression> namedExpressions; | ||
private final ExpressionAnalyzer expressionAnalyzer; | ||
private final LogicalPlan child; | ||
|
||
public LogicalPlan analyze(UnresolvedExpression projectItem, AnalysisContext context) { | ||
LogicalPlan nested = projectItem.accept(this, context); | ||
return (nested == null) ? child : nested; | ||
} | ||
|
||
@Override | ||
public LogicalPlan visitAlias(Alias node, AnalysisContext context) { | ||
return node.getDelegated().accept(this, context); | ||
} | ||
|
||
@Override | ||
public LogicalPlan visitFunction(Function node, AnalysisContext context) { | ||
if (node.getFuncName().equalsIgnoreCase(BuiltinFunctionName.NESTED.name())) { | ||
|
||
List<UnresolvedExpression> expressions = node.getFuncArgs(); | ||
validateArgs(expressions); | ||
ReferenceExpression nestedField = | ||
(ReferenceExpression)expressionAnalyzer.analyze(expressions.get(0), context); | ||
Map<String, ReferenceExpression> args; | ||
if (expressions.size() == 2) { | ||
args = Map.of( | ||
"field", nestedField, | ||
"path", (ReferenceExpression)expressionAnalyzer.analyze(expressions.get(1), context) | ||
); | ||
} else { | ||
args = Map.of( | ||
"field", (ReferenceExpression)expressionAnalyzer.analyze(expressions.get(0), context), | ||
"path", generatePath(nestedField.toString()) | ||
); | ||
} | ||
if (child instanceof LogicalNested) { | ||
((LogicalNested)child).addFields(args); | ||
return child; | ||
} else { | ||
return new LogicalNested(child, new ArrayList<>(Arrays.asList(args)), namedExpressions); | ||
} | ||
} | ||
return null; | ||
} | ||
|
||
/** | ||
* Validate each parameter used in nested function in SELECT clause. Any supplied parameter | ||
* for a nested function in a SELECT statement must be a valid qualified name, and the field | ||
* parameter must be nested at least one level. | ||
* @param args : Arguments in nested function. | ||
*/ | ||
private void validateArgs(List<UnresolvedExpression> args) { | ||
if (args.size() < 1 || args.size() > 2) { | ||
throw new IllegalArgumentException( | ||
"on nested object only allowed 2 parameters (field,path) or 1 parameter (field)" | ||
); | ||
} | ||
|
||
for (int i = 0; i < args.size(); i++) { | ||
if (!(args.get(i) instanceof QualifiedName)) { | ||
throw new IllegalArgumentException( | ||
String.format("Illegal nested field name: %s", args.get(i).toString()) | ||
); | ||
} | ||
if (i == 0 && ((QualifiedName)args.get(i)).getParts().size() < 2) { | ||
throw new IllegalArgumentException( | ||
String.format("Illegal nested field name: %s", args.get(i).toString()) | ||
); | ||
} | ||
} | ||
} | ||
|
||
/** | ||
* Generate nested path dynamically. Assumes at least one level of nesting in supplied string. | ||
* @param field : Nested field to generate path of. | ||
* @return : Path of field derived from last level of nesting. | ||
*/ | ||
private ReferenceExpression generatePath(String field) { | ||
return new ReferenceExpression(field.substring(0, field.lastIndexOf(".")), STRING); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
49 changes: 49 additions & 0 deletions
49
core/src/main/java/org/opensearch/sql/planner/logical/LogicalNested.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
/* | ||
* Copyright OpenSearch Contributors | ||
* SPDX-License-Identifier: Apache-2.0 | ||
*/ | ||
|
||
package org.opensearch.sql.planner.logical; | ||
|
||
import java.util.Collections; | ||
import java.util.List; | ||
import java.util.Map; | ||
import lombok.EqualsAndHashCode; | ||
import lombok.Getter; | ||
import lombok.ToString; | ||
import org.opensearch.sql.expression.NamedExpression; | ||
import org.opensearch.sql.expression.ReferenceExpression; | ||
|
||
/** | ||
* Logical Nested plan. | ||
*/ | ||
@EqualsAndHashCode(callSuper = true) | ||
@Getter | ||
@ToString | ||
public class LogicalNested extends LogicalPlan { | ||
private List<Map<String, ReferenceExpression>> fields; | ||
private final List<NamedExpression> projectList; | ||
|
||
/** | ||
* Constructor of LogicalNested. | ||
* | ||
*/ | ||
public LogicalNested( | ||
LogicalPlan childPlan, | ||
List<Map<String, ReferenceExpression>> fields, | ||
List<NamedExpression> projectList | ||
) { | ||
super(Collections.singletonList(childPlan)); | ||
this.fields = fields; | ||
this.projectList = projectList; | ||
} | ||
|
||
public void addFields(Map<String, ReferenceExpression> fields) { | ||
this.fields.add(fields); | ||
} | ||
|
||
@Override | ||
public <R, C> R accept(LogicalPlanNodeVisitor<R, C> visitor, C context) { | ||
return visitor.visitNested(this, context); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.