This is an automated email from the ASF dual-hosted git repository.
jackie pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/pinot.git
The following commit(s) were added to refs/heads/master by this push:
new a1973a87db Add JSON Path Exists Function (#14376)
a1973a87db is described below
commit a1973a87dbc41f01e725ab6ae18d8fef3dce3efc
Author: ashishjayamohan <[email protected]>
AuthorDate: Wed Nov 6 14:57:11 2024 -0800
Add JSON Path Exists Function (#14376)
---
.../common/function/scalar/JsonFunctions.java | 25 ++++++++++++---
.../pinot/common/function/JsonFunctionsTest.java | 37 ++++++++++++++++++++++
2 files changed, 58 insertions(+), 4 deletions(-)
diff --git
a/pinot-common/src/main/java/org/apache/pinot/common/function/scalar/JsonFunctions.java
b/pinot-common/src/main/java/org/apache/pinot/common/function/scalar/JsonFunctions.java
index 5effbe3e54..2810419f95 100644
---
a/pinot-common/src/main/java/org/apache/pinot/common/function/scalar/JsonFunctions.java
+++
b/pinot-common/src/main/java/org/apache/pinot/common/function/scalar/JsonFunctions.java
@@ -85,6 +85,7 @@ public class JsonFunctions {
/**
* Extract object based on Json path
*/
+ @Nullable
@ScalarFunction
public static Object jsonPath(Object object, String jsonPath) {
if (object instanceof String) {
@@ -96,6 +97,7 @@ public class JsonFunctions {
/**
* Extract object array based on Json path
*/
+ @Nullable
@ScalarFunction
public static Object[] jsonPathArray(Object object, String jsonPath) {
if (object instanceof String) {
@@ -114,17 +116,32 @@ public class JsonFunctions {
}
}
- private static Object[] convertObjectToArray(Object arrayObject) {
+ @Nullable
+ private static Object[] convertObjectToArray(@Nullable Object arrayObject) {
+ if (arrayObject == null) {
+ return null;
+ }
if (arrayObject instanceof List) {
return ((List) arrayObject).toArray();
- } else if (arrayObject instanceof Object[]) {
+ }
+ if (arrayObject instanceof Object[]) {
return (Object[]) arrayObject;
- } else if (arrayObject == null) {
- return null;
}
return new Object[]{arrayObject};
}
+ /**
+ * Check if path exists in Json object
+ */
+ @ScalarFunction
+ public static boolean jsonPathExists(Object object, String jsonPath) {
+ try {
+ return jsonPath(object, jsonPath) != null;
+ } catch (Exception ignore) {
+ return false;
+ }
+ }
+
/**
* Extract from Json with path to String
*/
diff --git
a/pinot-common/src/test/java/org/apache/pinot/common/function/JsonFunctionsTest.java
b/pinot-common/src/test/java/org/apache/pinot/common/function/JsonFunctionsTest.java
index a9e48053b4..8b28af5fea 100644
---
a/pinot-common/src/test/java/org/apache/pinot/common/function/JsonFunctionsTest.java
+++
b/pinot-common/src/test/java/org/apache/pinot/common/function/JsonFunctionsTest.java
@@ -33,6 +33,7 @@ import org.testng.annotations.DataProvider;
import org.testng.annotations.Test;
import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertFalse;
import static org.testng.Assert.assertNull;
import static org.testng.Assert.assertTrue;
@@ -73,9 +74,11 @@ public class JsonFunctionsTest {
"}";
// @formatter:on
// CHECKSTYLE:ON
+ assertTrue(JsonFunctions.jsonPathExists(jsonString, "$.actor.id"));
assertEquals(JsonFunctions.jsonPathString(jsonString, "$.actor.id"),
"33500718");
assertEquals(JsonFunctions.jsonPathLong(jsonString, "$.actor.id"),
33500718L);
assertEquals(JsonFunctions.jsonPathDouble(jsonString, "$.actor.id"),
33500718.0);
+ assertFalse(JsonFunctions.jsonPathExists(jsonString, "$.actor.aaa"));
assertEquals(JsonFunctions.jsonPathString(jsonString, "$.actor.aaa",
"null"), "null");
assertEquals(JsonFunctions.jsonPathString("not json", "$.actor.aaa",
"null"), "null");
assertEquals(JsonFunctions.jsonPathString(null, "$.actor.aaa", "null"),
"null");
@@ -93,9 +96,12 @@ public class JsonFunctionsTest {
public void testJsonPathStringWithDefaultValue()
throws JsonProcessingException {
String jsonString = "{\"name\": \"Pete\", \"age\": 24}";
+ assertTrue(JsonFunctions.jsonPathExists(jsonString, "$.name"));
assertEquals(JsonFunctions.jsonPathString(jsonString, "$.name",
"default"), "Pete");
+ assertFalse(JsonFunctions.jsonPathExists(jsonString, "$.missing"));
assertEquals(JsonFunctions.jsonPathString(jsonString, "$.missing",
"default"), "default");
assertNull(JsonFunctions.jsonPathString(jsonString, "$.missing", null));
+ assertTrue(JsonFunctions.jsonPathExists(jsonString, "$.age"));
assertEquals(JsonFunctions.jsonPathString(jsonString, "$.age", "default"),
"24");
assertEquals(JsonFunctions.jsonPathString(jsonString, "$.age"), "24");
assertEquals(JsonFunctions.jsonPathString(jsonString, "$.age", null),
"24");
@@ -105,9 +111,12 @@ public class JsonFunctionsTest {
public void testJsonPathStringWithoutDefaultValue()
throws JsonProcessingException {
String jsonString = "{\"name\": \"Pete\", \"age\": 24}";
+ assertTrue(JsonFunctions.jsonPathExists(jsonString, "$.name"));
assertEquals(JsonFunctions.jsonPathString(jsonString, "$.name"), "Pete");
+ assertFalse(JsonFunctions.jsonPathExists(jsonString, "$.missing"));
assertNull(JsonFunctions.jsonPathString(jsonString, "$.missing"));
assertNull(JsonFunctions.jsonPathString(jsonString, "$.missing", null));
+ assertTrue(JsonFunctions.jsonPathExists(jsonString, "$.age"));
assertEquals(JsonFunctions.jsonPathString(jsonString, "$.age"), "24");
}
@@ -171,12 +180,16 @@ public class JsonFunctionsTest {
"}";
// @formatter:on
// CHECKSTYLE:ON
+ assertTrue(JsonFunctions.jsonPathExists(jsonString, "$.subjects[*].name"));
assertEquals(JsonFunctions.jsonPathArray(jsonString,
"$.subjects[*].name"), new String[]{"maths", "english"});
assertEquals(JsonFunctions.jsonPathArray(jsonString,
"$.subjects[*].grade"), new String[]{"A", "B"});
assertEquals(JsonFunctions.jsonPathArray(jsonString,
"$.subjects[*].homework_grades"),
new Object[]{Arrays.asList(80, 85, 90, 95, 100), Arrays.asList(60, 65,
70, 85, 90)});
+ assertFalse(JsonFunctions.jsonPathExists(jsonString, null));
assertEquals(JsonFunctions.jsonPathArrayDefaultEmpty(jsonString, null),
new Object[0]);
+ assertFalse(JsonFunctions.jsonPathExists(jsonString, "not json"));
assertEquals(JsonFunctions.jsonPathArrayDefaultEmpty(jsonString, "not
json"), new Object[0]);
+ assertTrue(JsonFunctions.jsonPathExists(jsonString,
"$.subjects[*].missing"));
assertEquals(JsonFunctions.jsonPathArrayDefaultEmpty(jsonString,
"$.subjects[*].missing"), new Object[0]);
}
@@ -211,9 +224,11 @@ public class JsonFunctionsTest {
"}";
// @formatter:on
// CHECKSTYLE:ON
+ assertTrue(JsonFunctions.jsonPathExists(jsonString, "$.subjects[*].name"));
assertEquals(JsonFunctions.jsonPathArrayDefaultEmpty(jsonString,
"$.subjects[*].name"),
new String[]{"maths", "english"});
assertEquals(JsonFunctions.jsonPathArrayDefaultEmpty(jsonString,
"$.subjects[*].grade"), new String[]{"A", "B"});
+ assertTrue(JsonFunctions.jsonPathExists(jsonString,
"$.subjects[*].homework_grades"));
assertEquals(JsonFunctions.jsonPathArrayDefaultEmpty(jsonString,
"$.subjects[*].homework_grades"),
new Object[]{Arrays.asList(80, 85, 90, 95, 100), Arrays.asList(60, 65,
70, 85, 90)});
}
@@ -228,7 +243,9 @@ public class JsonFunctionsTest {
// Those failure could be reproduced by using the default
JacksonJsonProvider for JsonPath.
Map<String, Object> rawData = ImmutableMap.of("commits",
ImmutableList.of(ImmutableMap.of("sha", 123, "name", "k"),
ImmutableMap.of("sha", 456, "name", "j")));
+ assertTrue(JsonFunctions.jsonPathExists(rawData, "$.commits[*].sha"));
assertEquals(JsonFunctions.jsonPathArray(rawData, "$.commits[*].sha"), new
Integer[]{123, 456});
+ assertTrue(JsonFunctions.jsonPathExists(rawData, "$.commits[1].sha"));
assertEquals(JsonFunctions.jsonPathArray(rawData, "$.commits[1].sha"), new
Integer[]{456});
// ArrayAwareJacksonJsonProvider should fix this issue.
@@ -244,7 +261,9 @@ public class JsonFunctionsTest {
// JSON formatted string works fine with JsonPath, and we used to
serialize Object[]
// to JSON formatted string for JsonPath to work.
String rawDataInStr = "[{\"sha\": 123, \"name\": \"k\"}, {\"sha\": 456,
\"name\": \"j\"}]";
+ assertTrue(JsonFunctions.jsonPathExists(rawDataInStr, "$.[*].sha"));
assertEquals(JsonFunctions.jsonPathArray(rawDataInStr, "$.[*].sha"), new
Integer[]{123, 456});
+ assertTrue(JsonFunctions.jsonPathExists(rawDataInStr, "$.[1].sha"));
assertEquals(JsonFunctions.jsonPathArray(rawDataInStr, "$.[1].sha"), new
Integer[]{456});
// ArrayAwareJacksonJsonProvider can work with Array directly, thus no
need to serialize
@@ -277,10 +296,14 @@ public class JsonFunctionsTest {
"]";
// @formatter:on
// CHECKSTYLE:ON
+ assertTrue(JsonFunctions.jsonPathExists(jsonArrayString, "$.[*].name"));
assertEquals(JsonFunctions.jsonPathArray(jsonArrayString, "$.[*].name"),
new String[]{"maths", "english"});
+ assertTrue(JsonFunctions.jsonPathExists(jsonArrayString, "$.[*].grade"));
assertEquals(JsonFunctions.jsonPathArray(jsonArrayString, "$.[*].grade"),
new String[]{"A", "B"});
+ assertTrue(JsonFunctions.jsonPathExists(jsonArrayString,
"$.[*].homework_grades"));
assertEquals(JsonFunctions.jsonPathArray(jsonArrayString,
"$.[*].homework_grades"),
new Object[]{Arrays.asList(80, 85, 90, 95, 100), Arrays.asList(60, 65,
70, 85, 90)});
+ assertTrue(JsonFunctions.jsonPathExists(jsonArrayString, "$.[*].score"));
assertEquals(JsonFunctions.jsonPathArray(jsonArrayString, "$.[*].score"),
new Integer[]{90, 50});
}
@@ -292,10 +315,14 @@ public class JsonFunctionsTest {
.of("name", "maths", "grade", "A", "score", 90, "homework_grades",
Arrays.asList(80, 85, 90, 95, 100)));
rawData.add(ImmutableMap
.of("name", "english", "grade", "B", "score", 50, "homework_grades",
Arrays.asList(60, 65, 70, 85, 90)));
+ assertTrue(JsonFunctions.jsonPathExists(rawData, "$.[*].name"));
assertEquals(JsonFunctions.jsonPathArray(rawData, "$.[*].name"), new
String[]{"maths", "english"});
+ assertTrue(JsonFunctions.jsonPathExists(rawData, "$.[*].grade"));
assertEquals(JsonFunctions.jsonPathArray(rawData, "$.[*].grade"), new
String[]{"A", "B"});
+ assertTrue(JsonFunctions.jsonPathExists(rawData, "$.[*].homework_grades"));
assertEquals(JsonFunctions.jsonPathArray(rawData, "$.[*].homework_grades"),
new Object[]{Arrays.asList(80, 85, 90, 95, 100), Arrays.asList(60, 65,
70, 85, 90)});
+ assertTrue(JsonFunctions.jsonPathExists(rawData, "$.[*].score"));
assertEquals(JsonFunctions.jsonPathArray(rawData, "$.[*].score"), new
Integer[]{90, 50});
}
@@ -308,10 +335,14 @@ public class JsonFunctionsTest {
ImmutableMap.of("name", "english", "grade", "B", "score", 50,
"homework_grades",
Arrays.asList(60, 65, 70, 85, 90))
};
+ assertTrue(JsonFunctions.jsonPathExists(rawData, "$.[*].name"));
assertEquals(JsonFunctions.jsonPathArray(rawData, "$.[*].name"), new
String[]{"maths", "english"});
+ assertTrue(JsonFunctions.jsonPathExists(rawData, "$.[*].grade"));
assertEquals(JsonFunctions.jsonPathArray(rawData, "$.[*].grade"), new
String[]{"A", "B"});
+ assertTrue(JsonFunctions.jsonPathExists(rawData, "$.[*].homework_grades"));
assertEquals(JsonFunctions.jsonPathArray(rawData, "$.[*].homework_grades"),
new Object[]{Arrays.asList(80, 85, 90, 95, 100), Arrays.asList(60, 65,
70, 85, 90)});
+ assertTrue(JsonFunctions.jsonPathExists(rawData, "$.[*].score"));
assertEquals(JsonFunctions.jsonPathArray(rawData, "$.[*].score"), new
Integer[]{90, 50});
}
@@ -364,4 +395,10 @@ public class JsonFunctionsTest {
}
}
}
+
+ @Test
+ public void testJsonPathExistsNullObject() {
+ assertFalse(JsonFunctions.jsonPathExists(null, "$.[*].name"));
+ assertFalse(JsonFunctions.jsonPathExists(null, null));
+ }
}
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]