Skip to content

Commit 018e462

Browse files
committed
functionality implemented
Signed-off-by: Kenrick Yap <[email protected]>
1 parent 3ec16e0 commit 018e462

File tree

7 files changed

+74
-4
lines changed

7 files changed

+74
-4
lines changed

core/build.gradle

+1
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ dependencies {
5454
api "com.fasterxml.jackson.core:jackson-core:${versions.jackson}"
5555
api "com.fasterxml.jackson.core:jackson-databind:${versions.jackson_databind}"
5656
api "com.fasterxml.jackson.core:jackson-annotations:${versions.jackson}"
57+
api group: 'com.jayway.jsonpath', name: 'json-path', version: '2.9.0'
5758
api group: 'com.google.code.gson', name: 'gson', version: '2.8.9'
5859
api group: 'com.tdunning', name: 't-digest', version: '3.3'
5960
api project(':common')

core/src/main/java/org/opensearch/sql/expression/DSL.java

+4
Original file line numberDiff line numberDiff line change
@@ -687,6 +687,10 @@ public static FunctionExpression jsonValid(Expression... expressions) {
687687
return compile(FunctionProperties.None, BuiltinFunctionName.JSON_VALID, expressions);
688688
}
689689

690+
public static FunctionExpression jsonExtract(Expression... expressions) {
691+
return compile(FunctionProperties.None, BuiltinFunctionName.JSON_EXTRACT, expressions);
692+
}
693+
690694
public static Aggregator avg(Expression... expressions) {
691695
return aggregate(BuiltinFunctionName.AVG, expressions);
692696
}

core/src/main/java/org/opensearch/sql/expression/function/BuiltinFunctionName.java

+1
Original file line numberDiff line numberDiff line change
@@ -207,6 +207,7 @@ public enum BuiltinFunctionName {
207207
/** Json Functions. */
208208
JSON_VALID(FunctionName.of("json_valid")),
209209
JSON(FunctionName.of("json")),
210+
JSON_EXTRACT(FunctionName.of("json_extract")),
210211

211212
/** NULL Test. */
212213
IS_NULL(FunctionName.of("is null")),

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

+7
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ public class JsonFunctions {
2323
public void register(BuiltinFunctionRepository repository) {
2424
repository.register(jsonValid());
2525
repository.register(jsonFunction());
26+
repository.register(jsonExtract());
2627
}
2728

2829
private DefaultFunctionResolver jsonValid() {
@@ -35,4 +36,10 @@ private DefaultFunctionResolver jsonFunction() {
3536
BuiltinFunctionName.JSON.getName(),
3637
impl(nullMissingHandling(JsonUtils::castJson), UNDEFINED, STRING));
3738
}
39+
40+
private DefaultFunctionResolver jsonExtract() {
41+
return define(
42+
BuiltinFunctionName.JSON_EXTRACT.getName(),
43+
impl(nullMissingHandling(JsonUtils::extractJson), UNDEFINED, STRING, STRING));
44+
}
3845
}

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

+59-4
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,22 @@
77
import com.fasterxml.jackson.core.JsonProcessingException;
88
import com.fasterxml.jackson.databind.JsonNode;
99
import com.fasterxml.jackson.databind.ObjectMapper;
10+
import com.jayway.jsonpath.Configuration;
11+
import com.jayway.jsonpath.DocumentContext;
12+
import com.jayway.jsonpath.InvalidJsonException;
13+
import com.jayway.jsonpath.JsonPath;
14+
15+
import java.io.ObjectInputFilter;
1016
import java.util.LinkedHashMap;
1117
import java.util.LinkedList;
1218
import java.util.List;
1319
import java.util.Map;
20+
import java.util.stream.Collectors;
21+
22+
import com.jayway.jsonpath.Option;
23+
import com.jayway.jsonpath.PathNotFoundException;
24+
import com.jayway.jsonpath.spi.json.JacksonJsonProvider;
25+
import com.jayway.jsonpath.spi.mapper.JacksonMappingProvider;
1426
import lombok.experimental.UtilityClass;
1527
import org.opensearch.sql.data.model.ExprCollectionValue;
1628
import org.opensearch.sql.data.model.ExprDoubleValue;
@@ -46,16 +58,59 @@ public static ExprValue isValidJson(ExprValue jsonExprValue) {
4658

4759
/** Converts a JSON encoded string to an Expression object. */
4860
public static ExprValue castJson(ExprValue json) {
61+
JsonNode jsonNode = jsonToNode(json);
62+
return processJsonNode(jsonNode);
63+
}
64+
65+
public static ExprValue extractJson(ExprValue json, ExprValue path) {
66+
String jsonString = json.stringValue();
67+
String jsonPath = path.stringValue();
68+
69+
try {
70+
Configuration config = Configuration.builder()
71+
.options(Option.AS_PATH_LIST)
72+
.build();
73+
List<String> resultPaths = JsonPath.using(config).parse(jsonString).read(jsonPath);
74+
75+
List<ExprValue> elements = new LinkedList<>();
76+
77+
for (String resultPath : resultPaths) {
78+
Object result = JsonPath.parse(jsonString).read(resultPath);
79+
String resultJsonString = new ObjectMapper().writeValueAsString(result);
80+
try {
81+
elements.add(processJsonNode(jsonStringToNode(resultJsonString)));
82+
} catch (SemanticCheckException e) {
83+
elements.add(new ExprStringValue(resultJsonString));
84+
}
85+
}
86+
87+
if (elements.size() == 1) {
88+
return elements.get(0);
89+
} else {
90+
return new ExprCollectionValue(elements);
91+
}
92+
} catch (PathNotFoundException e) {
93+
return LITERAL_NULL;
94+
} catch (InvalidJsonException | JsonProcessingException e) {
95+
final String errorFormat = "JSON string '%s' is not valid. Error details: %s";
96+
throw new SemanticCheckException(String.format(errorFormat, json, e.getMessage()), e);
97+
}
98+
}
99+
100+
private static JsonNode jsonToNode(ExprValue json) {
101+
return jsonStringToNode(json.stringValue());
102+
}
103+
104+
private static JsonNode jsonStringToNode(String jsonString) {
49105
ObjectMapper objectMapper = new ObjectMapper();
50106
JsonNode jsonNode;
51107
try {
52-
jsonNode = objectMapper.readTree(json.stringValue());
108+
jsonNode = objectMapper.readTree(jsonString);
53109
} catch (JsonProcessingException e) {
54110
final String errorFormat = "JSON string '%s' is not valid. Error details: %s";
55-
throw new SemanticCheckException(String.format(errorFormat, json, e.getMessage()), e);
111+
throw new SemanticCheckException(String.format(errorFormat, jsonString, e.getMessage()), e);
56112
}
57-
58-
return processJsonNode(jsonNode);
113+
return jsonNode;
59114
}
60115

61116
private static ExprValue processJsonNode(JsonNode jsonNode) {

ppl/src/main/antlr/OpenSearchPPLLexer.g4

+1
Original file line numberDiff line numberDiff line change
@@ -335,6 +335,7 @@ CIDRMATCH: 'CIDRMATCH';
335335
// JSON FUNCTIONS
336336
JSON_VALID: 'JSON_VALID';
337337
JSON: 'JSON';
338+
JSON_EXTRACT: 'JSON_EXTRACT';
338339

339340
// FLOWCONTROL FUNCTIONS
340341
IFNULL: 'IFNULL';

ppl/src/main/antlr/OpenSearchPPLParser.g4

+1
Original file line numberDiff line numberDiff line change
@@ -704,6 +704,7 @@ positionFunctionName
704704

705705
jsonFunctionName
706706
: JSON
707+
| JSON_EXTRACT
707708
;
708709

709710
// operators

0 commit comments

Comments
 (0)