Skip to content

Commit b6ae5ba

Browse files
committed
added multi path use case
Signed-off-by: Kenrick Yap <[email protected]>
1 parent 0d1cc28 commit b6ae5ba

File tree

5 files changed

+41
-35
lines changed

5 files changed

+41
-35
lines changed

core/src/main/java/org/opensearch/sql/expression/json/JsonFunctions.java

+3-3
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55

66
package org.opensearch.sql.expression.json;
77

8-
import static org.opensearch.sql.data.type.ExprCoreType.ARRAY;
98
import static org.opensearch.sql.data.type.ExprCoreType.BOOLEAN;
109
import static org.opensearch.sql.data.type.ExprCoreType.STRING;
1110
import static org.opensearch.sql.data.type.ExprCoreType.UNDEFINED;
@@ -41,7 +40,8 @@ private DefaultFunctionResolver jsonFunction() {
4140
private DefaultFunctionResolver jsonExtract() {
4241
return define(
4342
BuiltinFunctionName.JSON_EXTRACT.getName(),
44-
impl(JsonUtils::extractJsonPaths, UNDEFINED, STRING, ARRAY),
45-
impl(JsonUtils::extractJsonPath, UNDEFINED, STRING, STRING));
43+
impl(JsonUtils::extractJson, UNDEFINED, STRING, STRING),
44+
impl(JsonUtils::extractJson, UNDEFINED, STRING, STRING, STRING),
45+
impl(JsonUtils::extractJson, UNDEFINED, STRING, STRING, STRING, STRING));
4646
}
4747
}

core/src/main/java/org/opensearch/sql/utils/JsonUtils.java

+17-17
Original file line numberDiff line numberDiff line change
@@ -86,32 +86,32 @@ public static ExprValue castJson(ExprValue json) {
8686
* Extract value of JSON string at given JSON path.
8787
*
8888
* @param json JSON string (e.g. "{\"hello\": \"world\"}").
89-
* @param path JSON path (e.g. "$.hello")
89+
* @param paths list of JSON path (e.g. "$.hello")
9090
* @return ExprValue of value at given path of json string.
9191
*/
92-
public static ExprValue extractJsonPath(ExprValue json, ExprValue path) {
93-
if (json == LITERAL_NULL || json == LITERAL_MISSING) {
94-
return json;
95-
}
96-
97-
String jsonString = json.stringValue();
98-
String jsonPath = path.stringValue();
92+
public static ExprValue extractJson(ExprValue json, ExprValue... paths) {
93+
List<ExprValue> resultList = new ArrayList<>();
9994

100-
return extractJson(jsonString, jsonPath);
101-
}
95+
for (ExprValue path : paths) {
96+
System.out.println("Processing path: " + path);
97+
if (json == LITERAL_NULL || json == LITERAL_MISSING) {
98+
return json;
99+
}
102100

103-
public static ExprValue extractJsonPaths(ExprValue json, ExprValue paths) {
104-
List<ExprValue> pathList = paths.collectionValue();
105-
List<ExprValue> resultList = new ArrayList<>();
101+
String jsonString = json.stringValue();
102+
String jsonPath = path.stringValue();
106103

107-
for (ExprValue path : pathList) {
108-
resultList.add(extractJsonPath(json, path));
104+
resultList.add(extractJsonPath(jsonString, jsonPath));
109105
}
110106

111-
return new ExprCollectionValue(resultList);
107+
if (resultList.size() == 1) {
108+
return resultList.getFirst();
109+
} else {
110+
return new ExprCollectionValue(resultList);
111+
}
112112
}
113113

114-
private static ExprValue extractJson(String json, String path) {
114+
private static ExprValue extractJsonPath(String json, String path) {
115115
if (json.isEmpty() || json.equals("null")) {
116116
return LITERAL_NULL;
117117
}

core/src/test/java/org/opensearch/sql/expression/json/JsonFunctionsTest.java

+9-1
Original file line numberDiff line numberDiff line change
@@ -310,7 +310,15 @@ void json_extract_search_list_of_paths() {
310310
"{\"foo\": \"foo\", \"fuzz\": true, \"bar\": 1234, \"bar2\": 12.34, \"baz\": null, "
311311
+ "\"obj\": {\"internal\": \"value\"}, \"arr\": [\"string\", true, null]}";
312312

313-
execute_extract_json(LITERAL_NULL, objectJson, "($.foo, $bar2)");
313+
ExprValue expected =
314+
new ExprCollectionValue(
315+
List.of(new ExprStringValue("foo"), new ExprFloatValue(12.34), LITERAL_NULL));
316+
Expression pathExpr1 = DSL.literal(ExprValueUtils.stringValue("$.foo"));
317+
Expression pathExpr2 = DSL.literal(ExprValueUtils.stringValue("$.bar2"));
318+
Expression pathExpr3 = DSL.literal(ExprValueUtils.stringValue("$.potato"));
319+
Expression jsonExpr = DSL.literal(ExprValueUtils.stringValue(objectJson));
320+
ExprValue actual = DSL.jsonExtract(jsonExpr, pathExpr1, pathExpr2, pathExpr3).valueOf();
321+
assertEquals(expected, actual);
314322
}
315323

316324
private static void execute_extract_json(ExprValue expected, String json, String path) {

docs/user/ppl/functions/json.rst

+12-3
Original file line numberDiff line numberDiff line change
@@ -69,12 +69,13 @@ ____________
6969
Description
7070
>>>>>>>>>>>
7171

72-
Usage: `json_extract(doc, path)` Extracts a JSON value from a json document based on the path specified.
72+
Usage: `json_extract(doc, path[, path])` Extracts a JSON value from a json document based on the path specified.
7373

7474
Argument type: STRING, STRING
7575

7676
Return type: STRING/BOOLEAN/DOUBLE/INTEGER/NULL/STRUCT/ARRAY
7777

78+
- Up to 3 paths can be provided, and results of each `path` with be returned in an ARRAY.
7879
- Returns an ARRAY if `path` points to multiple results (e.g. $.a[*]) or if the `path` points to an array.
7980
- Return null if `path` is not valid, or if JSON `doc` is MISSING or NULL.
8081
- Throws SemanticCheckException if `doc` or `path` is malformed.
@@ -95,18 +96,26 @@ Example::
9596
| json empty string | | null |
9697
+---------------------+-------------------------------------+-------------------+
9798

98-
> source=json_test | where test_name="json nested list" | eval json_extract=json_extract('{"a":[{"b":1},{"b":2}]}', '$.b[1].c')
99+
os> source=json_test | where test_name="json nested list" | eval json_extract=json_extract('{"a":[{"b":1},{"b":2}]}', '$.b[1].c')
99100
fetched rows / total rows = 1/1
100101
+---------------------+-------------------------------------+--------------+
101102
| test_name | json_string | json_extract |
102103
|---------------------|-------------------------------------|--------------|
103104
| json nested list | {"a":"1","b":[{"c":"2"},{"c":"3"}]} | 3 |
104105
+---------------------+-------------------------------------+--------------+
105106

106-
> source=json_test | where test_name="json nested list" | eval json_extract=json_extract('{"a":[{"b":1},{"b":2}]}', '$.b[*].c')
107+
os> source=json_test | where test_name="json nested list" | eval json_extract=json_extract('{"a":[{"b":1},{"b":2}]}', '$.b[*].c')
107108
fetched rows / total rows = 1/1
108109
+---------------------+-------------------------------------+--------------+
109110
| test_name | json_string | json_extract |
110111
|---------------------|-------------------------------------|--------------|
111112
| json nested list | {"a":"1","b":[{"c":"2"},{"c":"3"}]} | [2,3] |
112113
+---------------------+-------------------------------------+--------------+
114+
115+
os> source=json_test | where test_name="json nested list" | eval json_extract=json_extract('{"a":[{"b":1},{"b":2}]}', '$.a', '$.b[*].c')
116+
fetched rows / total rows = 1/1
117+
+---------------------+-------------------------------------+--------------+
118+
| test_name | json_string | json_extract |
119+
|---------------------|-------------------------------------|--------------|
120+
| json nested list | {"a":"1","b":[{"c":"2"},{"c":"3"}]} | [1,[2,3]] |
121+
+---------------------+-------------------------------------+--------------+

ppl/src/main/java/org/opensearch/sql/ppl/parser/AstExpressionBuilder.java

-11
Original file line numberDiff line numberDiff line change
@@ -390,17 +390,6 @@ public UnresolvedExpression visitSpanClause(SpanClauseContext ctx) {
390390
return new Span(visit(ctx.fieldExpression()), visit(ctx.value), SpanUnit.of(unit));
391391
}
392392

393-
@Override
394-
public UnresolvedExpression visitJsonExtract(
395-
OpenSearchPPLParser.JsonExtractFunctionCallContext ctx) {}
396-
397-
@Override
398-
public UnresolvedExpression visitJsonPathString(OpenSearchPPLParser.JsonPathStringContext ctx) {}
399-
400-
@Override
401-
public List<UnresolvedExpression> visitJsonPathList(
402-
OpenSearchPPLParser.JsonPathListContext ctx) {}
403-
404393
private QualifiedName visitIdentifiers(List<? extends ParserRuleContext> ctx) {
405394
return new QualifiedName(
406395
ctx.stream()

0 commit comments

Comments
 (0)