-
Notifications
You must be signed in to change notification settings - Fork 181
[Feature] PPL Command: MvExpand #5144
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -63,6 +63,7 @@ | |
| import org.opensearch.sql.ast.tree.Limit; | ||
| import org.opensearch.sql.ast.tree.MinSpanBin; | ||
| import org.opensearch.sql.ast.tree.MvCombine; | ||
| import org.opensearch.sql.ast.tree.MvExpand; | ||
| import org.opensearch.sql.ast.tree.Parse; | ||
| import org.opensearch.sql.ast.tree.Patterns; | ||
| import org.opensearch.sql.ast.tree.Project; | ||
|
|
@@ -477,6 +478,10 @@ public static MvCombine mvcombine(Field field, String delim) { | |
| return new MvCombine(field, delim); | ||
| } | ||
|
|
||
| public static UnresolvedPlan mvexpand(UnresolvedPlan input, Field field, Integer limit) { | ||
| return new MvExpand(field, limit).attach(input); | ||
| } | ||
|
Comment on lines
+481
to
+483
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧩 Analysis chain🏁 Script executed: # First, locate the AstDSL.java file and examine the mvexpand method
find . -name "AstDSL.java" -type f | head -5Repository: opensearch-project/sql Length of output: 125 🏁 Script executed: # Search for test files that might cover mvexpand or MvExpand
rg -l "mvexpand|MvExpand" --type=java | grep -i testRepository: opensearch-project/sql Length of output: 770 🏁 Script executed: # Look at the actual implementation around line 481-483
fd "AstDSL.java" | xargs -I {} sh -c 'echo "=== {} ===" && sed -n "475,490p" {}'Repository: opensearch-project/sql Length of output: 659 🏁 Script executed: # Look for AstDSLTest.java and check if it covers mvexpand
fd "AstDSLTest.java" -o "AstDslTest.java" | xargs -I {} sh -c 'echo "=== {} ===" && cat {}'Repository: opensearch-project/sql Length of output: 215 🏁 Script executed: # Check if mvexpand appears in test files related to AstDSL
rg -n "mvexpand" --type=java -B 3 -A 3 core/src/test/java 2>/dev/null || echo "No tests found in core/src/test/java"Repository: opensearch-project/sql Length of output: 102 🏁 Script executed: # Examine CalcitePPLMvExpandTest.java to see test coverage
fd "CalcitePPLMvExpandTest.java" | xargs -I {} sh -c 'wc -l {} && head -60 {}'Repository: opensearch-project/sql Length of output: 2192 🏁 Script executed: # Check CalciteMvExpandCommandIT.java as well
fd "CalciteMvExpandCommandIT.java" | xargs -I {} sh -c 'wc -l {} && head -60 {}'Repository: opensearch-project/sql Length of output: 2141 🏁 Script executed: # Search for direct unit tests of AstDSL.mvexpand method
rg "AstDSL\.mvexpand|new MvExpand" --type=java core/src/test/java -A 2 2>/dev/null || echo "Searching in core tests..."Repository: opensearch-project/sql Length of output: 92 🏁 Script executed: # Find all test files in core module
fd -e java "Test\.java$" core/src/test/java | head -20Repository: opensearch-project/sql Length of output: 1553 🏁 Script executed: # Check if there's any reference to AstDSL in test files across the project
rg "class.*AstDSL.*Test|AstDSL.*mvexpand" --type=javaRepository: opensearch-project/sql Length of output: 48 🏁 Script executed: # Look at the actual file again with more context - check if JavaDoc exists above line 481
fd "AstDSL.java" | xargs -I {} sh -c 'sed -n "470,490p" {}'Repository: opensearch-project/sql Length of output: 745 🏁 Script executed: # Check for any existing JavaDoc patterns in AstDSL.java
rg "^\s*\*\s*@param|^\s*\*\s*@return|^\s*/\*\*" core/src/main/java/org/opensearch/sql/ast/dsl/AstDSL.java -cRepository: opensearch-project/sql Length of output: 68 🏁 Script executed: # Check if AstDSL methods generally have JavaDoc
fd "AstDSL.java" | xargs -I {} sh -c 'sed -n "400,450p" {}'Repository: opensearch-project/sql Length of output: 1445 🏁 Script executed: # Look for patterns showing how other public static methods are documented in AstDSL
fd "AstDSL.java" | xargs -I {} sh -c 'sed -n "200,250p" {}'Repository: opensearch-project/sql Length of output: 1425 🏁 Script executed: # Check if there's an AstDSL test file or if DSL methods are tested elsewhere
fd ".*Test.*\.java" core/src/test/java | xargs rg -l "AstDSL" 2>/dev/nullRepository: opensearch-project/sql Length of output: 1653 🏁 Script executed: # Comprehensive check - look for any test that directly uses or tests AstDSL.mvexpand
rg "AstDSL\s*\.\s*mvexpand" --type=javaRepository: opensearch-project/sql Length of output: 48 🏁 Script executed: # Count total lines in AstDSL.java and see the overall JavaDoc coverage
fd "AstDSL.java" | xargs -I {} sh -c 'wc -l {} && echo "---" && grep -c "public static" {}'Repository: opensearch-project/sql Length of output: 134 🏁 Script executed: # Check the actual structure - look for any method with JavaDoc
fd "AstDSL.java" | xargs -I {} sh -c 'grep -B 3 "public static.*mvexpand\|public static.*mvcombine" {} | head -20'Repository: opensearch-project/sql Length of output: 424 🏁 Script executed: # Check if similar DSL helper methods (like mvcombine right above) have JavaDoc
fd "AstDSL.java" | xargs -I {} sh -c 'sed -n "475,490p" {}'Repository: opensearch-project/sql Length of output: 593 🏁 Script executed: # Look for how MvExpand itself is used in the codebase
rg "new MvExpand|\.mvexpand\(" --type=java -B 2 -A 2 | head -60Repository: opensearch-project/sql Length of output: 991 🏁 Script executed: # Check if this method is actually part of the commit or existing
git log --oneline -S "mvexpand" -- core/src/main/java/org/opensearch/sql/ast/dsl/AstDSL.java 2>/dev/null | head -5 || echo "Git log not available"Repository: opensearch-project/sql Length of output: 125 Add JavaDoc and unit tests for the new This new public method in The method needs:
📝 Suggested JavaDoc+ /**
+ * Build an MVEXPAND plan node and attach it to the input plan.
+ *
+ * `@param` input input plan
+ * `@param` field field to expand
+ * `@param` limit optional per-document limit
+ * `@return` MvExpand plan attached to the input
+ */
public static UnresolvedPlan mvexpand(UnresolvedPlan input, Field field, Integer limit) {
return new MvExpand(field, limit).attach(input);
}🤖 Prompt for AI Agents |
||
|
|
||
| public static List<Argument> sortOptions() { | ||
| return exprList(argument("desc", booleanLiteral(false))); | ||
| } | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,46 @@ | ||
| /* | ||
| * Copyright OpenSearch Contributors | ||
| * SPDX-License-Identifier: Apache-2.0 | ||
| */ | ||
|
|
||
| package org.opensearch.sql.ast.tree; | ||
|
|
||
| import com.google.common.collect.ImmutableList; | ||
| import java.util.List; | ||
| import javax.annotation.Nullable; | ||
| import lombok.EqualsAndHashCode; | ||
| import lombok.Getter; | ||
| import lombok.ToString; | ||
| import org.opensearch.sql.ast.AbstractNodeVisitor; | ||
| import org.opensearch.sql.ast.expression.Field; | ||
|
|
||
| /** AST node representing the {@code mvexpand} PPL command: {@code mvexpand <field> [limit=N]}. */ | ||
| @ToString | ||
| @EqualsAndHashCode(callSuper = false) | ||
| public class MvExpand extends UnresolvedPlan { | ||
|
|
||
| private UnresolvedPlan child; | ||
| @Getter private final Field field; | ||
| @Getter @Nullable private final Integer limit; | ||
|
|
||
| public MvExpand(Field field, @Nullable Integer limit) { | ||
| this.field = field; | ||
| this.limit = limit; | ||
| } | ||
|
|
||
| @Override | ||
| public MvExpand attach(UnresolvedPlan child) { | ||
| this.child = child; | ||
| return this; | ||
| } | ||
|
|
||
| @Override | ||
| public List<UnresolvedPlan> getChild() { | ||
| return this.child == null ? ImmutableList.of() : ImmutableList.of(this.child); | ||
| } | ||
|
|
||
| @Override | ||
| public <T, C> T accept(AbstractNodeVisitor<T, C> nodeVisitor, C context) { | ||
| return nodeVisitor.visitMvExpand(this, context); | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,141 @@ | ||
| # mvexpand | ||
|
|
||
| ## Description | ||
| The `mvexpand` command expands each value in a multivalue (array) field into a separate row. For each document, every element in the specified array field is returned as a new row. | ||
|
|
||
|
|
||
| ## Syntax | ||
| ``` | ||
| mvexpand <field> [limit=<int>] | ||
| ``` | ||
|
|
||
| - `<field>`: The multivalue (array) field to expand. (Required) | ||
| - `limit`: Maximum number of values per document to expand. (Optional) | ||
|
|
||
|
|
||
| ### Output field naming | ||
| After `mvexpand`, the expanded value remains under the same field name (for example, `tags` or `ids`). | ||
| If the array contains objects, you can reference subfields (for example, `skills.name`). | ||
|
|
||
|
|
||
| ## Examples | ||
|
|
||
| ### Example 1: Basic Expansion (single document) | ||
| Input document (case "basic") contains three tag values. | ||
|
|
||
| PPL query: | ||
| ```ppl | ||
| source=people | ||
| | eval tags = array('error', 'warning', 'info') | ||
| | fields tags | ||
| | head 1 | ||
| | mvexpand tags | ||
| | fields tags | ||
| ``` | ||
|
|
||
| Expected output: | ||
| ```text | ||
| fetched rows / total rows = 3/3 | ||
| +---------+ | ||
| | tags | | ||
| |---------| | ||
| | error | | ||
| | warning | | ||
| | info | | ||
| +---------+ | ||
| ``` | ||
|
|
||
| ### Example 2: Expansion with Limit | ||
| Input document (case "ids") contains an array of integers; expand and apply limit. | ||
|
|
||
| PPL query: | ||
| ```ppl | ||
| source=people | ||
| | eval ids = array(1, 2, 3, 4, 5) | ||
| | fields ids | ||
| | head 1 | ||
| | mvexpand ids limit=3 | ||
| | fields ids | ||
| ``` | ||
|
|
||
| Expected output: | ||
| ```text | ||
| fetched rows / total rows = 3/3 | ||
| +-----+ | ||
| | ids | | ||
| |-----| | ||
| | 1 | | ||
| | 2 | | ||
| | 3 | | ||
| +-----+ | ||
| ``` | ||
|
|
||
| ### Example 3: Expand projects | ||
| This example demonstrates expanding a multivalue `projects` field into one row per project. | ||
|
|
||
| PPL query: | ||
| ```ppl | ||
| source=people | ||
| | head 1 | ||
| | fields projects | ||
| | mvexpand projects | ||
| | fields projects.name | ||
| ``` | ||
|
|
||
| Expected output: | ||
| ```text | ||
| fetched rows / total rows = 3/3 | ||
| +--------------------------------+ | ||
| | projects.name | | ||
| |--------------------------------| | ||
| | AWS Redshift Spectrum querying | | ||
| | AWS Redshift security | | ||
| | AWS Aurora security | | ||
| +--------------------------------+ | ||
| ``` | ||
|
|
||
| ### Example 4: Single-value array (case "single") | ||
| Single-element array should expand to one row. | ||
|
|
||
| PPL query: | ||
| ```ppl | ||
| source=people | ||
| | eval tags = array('error') | ||
| | fields tags | ||
| | head 1 | ||
| | mvexpand tags | ||
| | fields tags | ||
| ``` | ||
|
|
||
| Expected output: | ||
| ```text | ||
| fetched rows / total rows = 1/1 | ||
| +-------+ | ||
| | tags | | ||
| |-------| | ||
| | error | | ||
| +-------+ | ||
| ``` | ||
|
|
||
| ### Example 5: Missing Field | ||
| If the field does not exist in the input schema (for example, it is not mapped or was projected out earlier), mvexpand throws a semantic check exception. | ||
|
|
||
| PPL query: | ||
| ```ppl | ||
| source=people | ||
| | eval some_field = 'x' | ||
| | fields some_field | ||
| | head 1 | ||
| | mvexpand tags | ||
| | fields tags | ||
| ``` | ||
|
|
||
| Expected output: | ||
| ```text | ||
| {'reason': 'Invalid Query', 'details': "Field 'tags' not found in the schema", 'type': 'SemanticCheckException'} | ||
| Error: Query returned no data | ||
| ``` | ||
|
|
||
| ## Notes about these doctests | ||
| - The examples below generate deterministic multivalue fields using `eval` + `array()` so doctests are stable. | ||
| - All examples run against a single source index (`people`) and use `head 1` to keep output predictable. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Add JavaDoc for the new
visitMvExpandpublic method.This new public method in
core/src/main/javashould include@param/@return(and@throwsif applicable).📝 JavaDoc skeleton
As per coding guidelines, "core/src/main/java/**/*.java: Public methods MUST have JavaDoc with
@param,@return, and@throws."📝 Committable suggestion
🤖 Prompt for AI Agents