Skip to content

Commit 57ec9ae

Browse files
committed
Revert "Adopt appendcol, appendpipe, multisearch to spath (opensearch-project#5075)"
This reverts commit 7630db8. Signed-off-by: Chen Dai <daichen@amazon.com>
1 parent ede7d07 commit 57ec9ae

6 files changed

Lines changed: 19 additions & 197 deletions

File tree

core/src/main/java/org/opensearch/sql/ast/analysis/FieldResolutionVisitor.java

Lines changed: 12 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -351,6 +351,12 @@ public Node visitSearch(Search node, FieldResolutionContext context) {
351351
return node;
352352
}
353353

354+
@Override
355+
public Node visitAppendPipe(AppendPipe node, FieldResolutionContext context) {
356+
visitChildren(node, context);
357+
return node;
358+
}
359+
354360
@Override
355361
public Node visitRegex(Regex node, FieldResolutionContext context) {
356362
Set<String> regexFields = extractFieldsFromExpression(node.getField());
@@ -503,10 +509,8 @@ public Node visitFillNull(FillNull node, FieldResolutionContext context) {
503509

504510
@Override
505511
public Node visitAppendCol(AppendCol node, FieldResolutionContext context) {
506-
// dispatch requirements to subsearch and main
507-
acceptAndVerifyNodeVisited(node.getSubSearch(), context);
508-
visitChildren(node, context);
509-
return node;
512+
throw new IllegalArgumentException(
513+
"AppendCol command cannot be used together with spath command");
510514
}
511515

512516
@Override
@@ -518,10 +522,9 @@ public Node visitAppend(Append node, FieldResolutionContext context) {
518522
}
519523

520524
@Override
521-
public Node visitAppendPipe(AppendPipe node, FieldResolutionContext context) {
522-
acceptAndVerifyNodeVisited(node.getSubQuery(), context);
523-
visitChildren(node, context);
524-
return node;
525+
public Node visitMultisearch(Multisearch node, FieldResolutionContext context) {
526+
throw new IllegalArgumentException(
527+
"Multisearch command cannot be used together with spath command");
525528
}
526529

527530
@Override
@@ -531,16 +534,7 @@ public Node visitLookup(Lookup node, FieldResolutionContext context) {
531534

532535
@Override
533536
public Node visitValues(Values node, FieldResolutionContext context) {
534-
// do nothing
535-
return node;
536-
}
537-
538-
@Override
539-
public Node visitMultisearch(Multisearch node, FieldResolutionContext context) {
540-
// dispatch requirements to subsearches and main
541-
node.getSubsearches().forEach(subsearch -> acceptAndVerifyNodeVisited(subsearch, context));
542-
visitChildren(node, context);
543-
return node;
537+
throw new IllegalArgumentException("Values command cannot be used together with spath command");
544538
}
545539

546540
@Override

core/src/main/java/org/opensearch/sql/calcite/CalciteRelNodeVisitor.java

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2306,7 +2306,6 @@ public RelNode visitMultisearch(Multisearch node, CalcitePlanContext context) {
23062306
prunedSubSearch.accept(this, context);
23072307
subsearchNodes.add(context.relBuilder.build());
23082308
}
2309-
subsearchNodes = DynamicFieldsHelper.adjustInputsForDynamicFields(subsearchNodes, context);
23102309

23112310
// Use shared schema merging logic that handles type conflicts via field renaming
23122311
List<RelNode> alignedNodes =

core/src/main/java/org/opensearch/sql/calcite/DynamicFieldsHelper.java

Lines changed: 6 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@
1010
import java.util.ArrayList;
1111
import java.util.Collection;
1212
import java.util.Collections;
13-
import java.util.HashSet;
1413
import java.util.List;
1514
import java.util.Optional;
1615
import java.util.Set;
@@ -100,9 +99,8 @@ static void adjustJoinInputsForDynamicFields(
10099
// build once to modify the inputs already in the stack.
101100
RelNode right = context.relBuilder.build();
102101
RelNode left = context.relBuilder.build();
103-
List<RelNode> inputs = adjustInputsForDynamicFields(List.of(right, left), context);
104-
right = inputs.get(0);
105-
left = inputs.get(1);
102+
left = adjustFieldsForDynamicFields(left, right, context);
103+
right = adjustFieldsForDynamicFields(right, left, context);
106104
context.relBuilder.push(left);
107105
// `as(alias)` is needed since `build()` won't preserve alias
108106
leftAlias.map(alias -> context.relBuilder.as(alias));
@@ -121,36 +119,6 @@ static RelNode adjustFieldsForDynamicFields(
121119
return target;
122120
}
123121

124-
/** Adjust inputs to align the static/dynamic fields each other */
125-
static List<RelNode> adjustInputsForDynamicFields(
126-
List<RelNode> inputs, CalcitePlanContext context) {
127-
boolean requireAdjustment = inputs.stream().anyMatch(input -> hasDynamicFields(input));
128-
if (requireAdjustment) {
129-
List<String> requiredStaticFields = getRequiredStaticFields(inputs);
130-
return inputs.stream()
131-
.map(input -> adjustFieldsForDynamicFields(input, requiredStaticFields, context))
132-
.collect(Collectors.toList());
133-
} else {
134-
return inputs;
135-
}
136-
}
137-
138-
static List<String> getRequiredStaticFields(List<RelNode> inputs) {
139-
Set<String> requiredStaticFields = new HashSet<String>();
140-
for (RelNode input : inputs) {
141-
if (hasDynamicFields(input)) {
142-
requiredStaticFields.addAll(getStaticFields(input));
143-
}
144-
}
145-
return toSortedList(requiredStaticFields);
146-
}
147-
148-
private static List<String> toSortedList(Collection<String> collection) {
149-
ArrayList<String> result = new ArrayList<>(collection);
150-
Collections.sort(result);
151-
return result;
152-
}
153-
154122
/**
155123
* Project node's fields in `requiredFieldNames` as static field, and put other fields into `_MAP`
156124
* (dynamic fields) This projection is needed when merging an input with dynamic fields and an
@@ -160,27 +128,16 @@ private static List<String> toSortedList(Collection<String> collection) {
160128
static RelNode adjustFieldsForDynamicFields(
161129
RelNode node, List<String> staticFieldNames, CalcitePlanContext context) {
162130
context.relBuilder.push(node);
163-
List<String> existingFields = getStaticFields(node);
131+
List<String> existingFields = node.getRowType().getFieldNames();
164132
List<RexNode> project = new ArrayList<>();
165133
for (String existingField : existingFields) {
166134
if (staticFieldNames.contains(existingField)) {
167135
project.add(context.rexBuilder.makeInputRef(node, existingFields.indexOf(existingField)));
168136
}
169137
}
170-
if (hasDynamicFields(node)) {
171-
// _MAP = MAP_APPEND(_MAP, MAP(existingFields - staticFields))
172-
RexNode existingDynamicFieldsMap = context.relBuilder.field(DYNAMIC_FIELDS_MAP);
173-
RexNode additionalFieldsMap = getFieldsAsMap(existingFields, staticFieldNames, context);
174-
RexNode mapAppend =
175-
context.rexBuilder.makeCall(
176-
BuiltinFunctionName.MAP_APPEND, existingDynamicFieldsMap, additionalFieldsMap);
177-
project.add(context.relBuilder.alias(mapAppend, DYNAMIC_FIELDS_MAP));
178-
} else {
179-
// _MAP = MAP(existingFields - staticFields)
180-
project.add(
181-
context.relBuilder.alias(
182-
getFieldsAsMap(existingFields, staticFieldNames, context), DYNAMIC_FIELDS_MAP));
183-
}
138+
project.add(
139+
context.relBuilder.alias(
140+
getFieldsAsMap(existingFields, staticFieldNames, context), DYNAMIC_FIELDS_MAP));
184141
return context.relBuilder.project(project).build();
185142
}
186143

docs/user/ppl/cmd/spath.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ For more information about path syntax, see [json_extract](../functions/json.md#
4141
* **Limitation**: Field order in the result could be inconsistent with query without `spath` command, and the behavior might change in the future version.
4242
* **Limitation**: Filter with subquery (`where <field> in/exists [...]`) is not supported with `spath` command.
4343
* **Limitation**: `fillnull` command requires to specify fields when used with `spath` command.
44-
* **Limitation**: Following commands cannot be used together with `spath` command: `lookup`.
44+
* **Limitation**: Following commands cannot be used together with `spath` command: `appendcol`, `multisearch`, `lookup`.
4545
* **Performance**: Filter records before `spath` command for best performance (see Example 8)
4646

4747
* **Internal Implementation**: The auto extraction feature uses an internal `_MAP` system column to store dynamic fields during query processing. This column is automatically expanded into individual columns in the final results and users don't need to reference it directly. For more information, see [System Columns](../general/identifiers.md#system-columns).

integ-test/src/test/java/org/opensearch/sql/calcite/remote/CalcitePPLSpathCommandIT.java

Lines changed: 0 additions & 96 deletions
Original file line numberDiff line numberDiff line change
@@ -263,102 +263,6 @@ public void testAppendWithSpathInSubsearchDynamicFields() throws IOException {
263263
rows("1", "3", "2", "simple", null, sj("{'a': 1, 'b': 2, 'c': 3}")));
264264
}
265265

266-
@Test
267-
public void testAppendColWithSpathInMain() throws IOException {
268-
JSONObject result =
269-
executeQuery(
270-
"source=test_json | where category='simple' | spath input=userData | appendcol [where"
271-
+ " category='simple'] | fields a, c, *");
272-
verifySchema(
273-
result,
274-
schema("a", "string"),
275-
schema("c", "string"),
276-
schema("category", "string"),
277-
schema("userData", "string"),
278-
schema("b", "string"));
279-
verifyDataRows(
280-
result,
281-
rows("1", "3", "simple", sj("{'a': 1, 'b': 2, 'c': 3}"), "2"),
282-
rows("1", "3", "simple", sj("{'a': 1, 'b': 2, 'c': 3}"), "2"));
283-
}
284-
285-
@Test
286-
public void testAppendColWithSpathInSubsearch() throws IOException {
287-
JSONObject result =
288-
executeQuery(
289-
"source=test_json | where category='simple' | appendcol [where category='simple' |"
290-
+ " spath input=userData] | fields a, c, *");
291-
verifySchema(
292-
result,
293-
schema("a", "string"),
294-
schema("c", "string"),
295-
schema("category", "string"),
296-
schema("userData", "string"),
297-
schema("b", "string"));
298-
verifyDataRows(
299-
result,
300-
rows("1", "3", "simple", sj("{'a': 1, 'b': 2, 'c': 3}"), "2"),
301-
rows("1", "3", "simple", sj("{'a': 1, 'b': 2, 'c': 3}"), "2"));
302-
}
303-
304-
@Test
305-
public void testAppendColWithSpathInBothInputs() throws IOException {
306-
JSONObject result =
307-
executeQuery(
308-
"source=test_json | where category='simple' | spath input=userData | appendcol [where"
309-
+ " category='simple' | spath input=userData ] | fields a, c, *");
310-
verifySchema(
311-
result,
312-
schema("a", "string"),
313-
schema("c", "string"),
314-
schema("b", "string"),
315-
schema("category", "string"),
316-
schema("userData", "string"));
317-
verifyDataRows(
318-
result,
319-
rows("1", "3", "2", "simple", sj("{'a': 1, 'b': 2, 'c': 3}")),
320-
rows("1", "3", "2", "simple", sj("{'a': 1, 'b': 2, 'c': 3}")));
321-
}
322-
323-
@Test
324-
public void testAppendPipeWithSpathInMain() throws IOException {
325-
JSONObject result =
326-
executeQuery(
327-
"source=test_json | where category='simple' | spath input=userData | stats sum(a) as"
328-
+ " total by b | appendpipe [stats sum(total) as total] | head 5");
329-
verifySchema(result, schema("total", "double"), schema("b", "string"));
330-
verifyDataRows(result, rows(2, "2"), rows(2, null));
331-
}
332-
333-
@Test
334-
public void testMultisearchWithSpath() throws IOException {
335-
JSONObject result =
336-
executeQuery(
337-
"| multisearch [source=test_json | where category='simple' | spath input=userData |"
338-
+ " head 1] [source=test_json | where category='nested' | spath input=userData] |"
339-
+ " fields a, c, *");
340-
verifySchema(
341-
result,
342-
schema("a", "string"),
343-
schema("c", "string"),
344-
schema("b", "string"),
345-
schema("category", "string"),
346-
schema("nested.d{}", "string"),
347-
schema("nested.e", "string"),
348-
schema("userData", "string"));
349-
verifyDataRows(
350-
result,
351-
rows("1", "3", "2", "simple", null, null, sj("{'a': 1, 'b': 2, 'c': 3}")),
352-
rows(
353-
null,
354-
null,
355-
null,
356-
"nested",
357-
"[1, 2, 3]",
358-
"str",
359-
sj("{'nested': {'d': [1, 2, 3], 'e': 'str'}}")));
360-
}
361-
362266
@Test
363267
public void testSpathWithMvCombine() throws IOException {
364268
JSONObject result =

ppl/src/test/java/org/opensearch/sql/ppl/parser/FieldResolutionVisitorTest.java

Lines changed: 0 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -381,38 +381,6 @@ public void testAppend() {
381381
"sub", new FieldResolutionResult(Set.of("a", "c", "testCase"), "*")));
382382
}
383383

384-
@Test
385-
public void testAppendCol() {
386-
String query =
387-
"source=main | where testCase='simple' | eval c = 4 | "
388-
+ "appendcol [where testCase='simple' ] | fields a, c, *";
389-
assertMultiRelationFields(
390-
query, Map.of("main", new FieldResolutionResult(Set.of("a", "testCase"), "*")));
391-
}
392-
393-
@Test
394-
public void testAppendpipe() {
395-
String query =
396-
"source=main | where testCase='simple' | stats sum(a) as sum_a by b | "
397-
+ "appendpipe [stats sum(sum_a) as total] | head 5";
398-
assertMultiRelationFields(
399-
query, Map.of("main", new FieldResolutionResult(Set.of("a", "b", "testCase"))));
400-
}
401-
402-
@Test
403-
public void testMultisearch() {
404-
String query =
405-
"| multisearch [source=main | where testCase='simple'] [source=sub | where"
406-
+ " testCase='simple'] | fields a, c, *";
407-
assertMultiRelationFields(
408-
query,
409-
Map.of(
410-
"main",
411-
new FieldResolutionResult(Set.of("a", "c", "testCase"), "*"),
412-
"sub",
413-
new FieldResolutionResult(Set.of("a", "c", "testCase"), "*")));
414-
}
415-
416384
@Test
417385
public void testAppendWithSpathInMain() {
418386
String query =

0 commit comments

Comments
 (0)