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]

Reply via email to