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 3f76154  Upgrade to JsonPath 2.7.0 (#7819)
3f76154 is described below

commit 3f761544e2fb6532558c9882f18bfd48e4c927e6
Author: Richard Startin <rich...@startree.ai>
AuthorDate: Tue Feb 1 18:48:20 2022 +0000

    Upgrade to JsonPath 2.7.0 (#7819)
---
 LICENSE-binary                                     |  2 +-
 .../common/function/scalar/JsonFunctions.java      | 60 +++++++++----------
 .../pinot/common/function/JsonFunctionsTest.java   | 67 +++++++++++++++++++---
 .../scalar/ArrayAwareJacksonJsonProviderTest.java  | 13 ++---
 .../evaluators/DefaultJsonPathEvaluator.java       | 49 ++--------------
 pom.xml                                            |  2 +-
 6 files changed, 100 insertions(+), 93 deletions(-)

diff --git a/LICENSE-binary b/LICENSE-binary
index eb25b51..2f0beb8 100644
--- a/LICENSE-binary
+++ b/LICENSE-binary
@@ -241,7 +241,7 @@ com.google.http-client:google-http-client-jackson2:1.32.1
 com.google.http-client:google-http-client:1.32.1
 com.google.j2objc:j2objc-annotations:1.3
 com.google.oauth-client:google-oauth-client:1.30.3
-com.jayway.jsonpath:json-path:2.4.0
+com.jayway.jsonpath:json-path:2.7.0
 com.lmax:disruptor:3.3.4
 com.ning:async-http-client:1.9.21
 com.squareup.okio:okio:1.6.0
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 0285f7b..e2f9403 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
@@ -22,6 +22,7 @@ import com.fasterxml.jackson.core.JsonProcessingException;
 import com.google.common.annotations.VisibleForTesting;
 import com.jayway.jsonpath.Configuration;
 import com.jayway.jsonpath.JsonPath;
+import com.jayway.jsonpath.Option;
 import com.jayway.jsonpath.ParseContext;
 import com.jayway.jsonpath.Predicate;
 import com.jayway.jsonpath.spi.cache.CacheProvider;
@@ -50,10 +51,12 @@ public class JsonFunctions {
   private JsonFunctions() {
   }
 
+  private static final Object[] EMPTY = new Object[0];
   private static final Predicate[] NO_PREDICATES = new Predicate[0];
   private static final ParseContext PARSE_CONTEXT = JsonPath.using(
       new Configuration.ConfigurationBuilder().jsonProvider(new 
ArrayAwareJacksonJsonProvider())
-          .mappingProvider(new JacksonMappingProvider()).build());
+          .mappingProvider(new 
JacksonMappingProvider()).options(Option.SUPPRESS_EXCEPTIONS)
+          .build());
 
   static {
     // Set the JsonPath cache before the cache is accessed
@@ -93,8 +96,7 @@ public class JsonFunctions {
    * Extract object array based on Json path
    */
   @ScalarFunction
-  public static Object[] jsonPathArray(Object object, String jsonPath)
-      throws JsonProcessingException {
+  public static Object[] jsonPathArray(Object object, String jsonPath) {
     if (object instanceof String) {
       return convertObjectToArray(PARSE_CONTEXT.parse((String) 
object).read(jsonPath, NO_PREDICATES));
     }
@@ -103,11 +105,8 @@ public class JsonFunctions {
 
   @ScalarFunction
   public static Object[] jsonPathArrayDefaultEmpty(Object object, String 
jsonPath) {
-    try {
-      return jsonPathArray(object, jsonPath);
-    } catch (Exception e) {
-      return new Object[0];
-    }
+    Object[] result = jsonPathArray(object, jsonPath);
+    return result == null ? EMPTY : result;
   }
 
   private static Object[] convertObjectToArray(Object arrayObject) {
@@ -115,6 +114,8 @@ public class JsonFunctions {
       return ((List) arrayObject).toArray();
     } else if (arrayObject instanceof Object[]) {
       return (Object[]) arrayObject;
+    } else if (arrayObject == null) {
+      return null;
     }
     return new Object[]{arrayObject};
   }
@@ -129,7 +130,7 @@ public class JsonFunctions {
     if (jsonValue instanceof String) {
       return (String) jsonValue;
     }
-    return JsonUtils.objectToString(jsonValue);
+    return jsonValue == null ? null : JsonUtils.objectToString(jsonValue);
   }
 
   /**
@@ -137,9 +138,13 @@ public class JsonFunctions {
    */
   @ScalarFunction
   public static String jsonPathString(Object object, String jsonPath, String 
defaultValue) {
+    Object jsonValue = jsonPath(object, jsonPath);
+    if (jsonValue instanceof String) {
+      return (String) jsonValue;
+    }
     try {
-      return jsonPathString(object, jsonPath);
-    } catch (Exception e) {
+      return jsonValue == null ? defaultValue : 
JsonUtils.objectToString(jsonValue);
+    } catch (JsonProcessingException e) {
       return defaultValue;
     }
   }
@@ -149,14 +154,7 @@ public class JsonFunctions {
    */
   @ScalarFunction
   public static long jsonPathLong(Object object, String jsonPath) {
-    final Object jsonValue = jsonPath(object, jsonPath);
-    if (jsonValue == null) {
-      return Long.MIN_VALUE;
-    }
-    if (jsonValue instanceof Number) {
-      return ((Number) jsonValue).longValue();
-    }
-    return Long.parseLong(jsonValue.toString());
+    return jsonPathLong(object, jsonPath, Long.MIN_VALUE);
   }
 
   /**
@@ -164,11 +162,14 @@ public class JsonFunctions {
    */
   @ScalarFunction
   public static long jsonPathLong(Object object, String jsonPath, long 
defaultValue) {
-    try {
-      return jsonPathLong(object, jsonPath);
-    } catch (Exception e) {
+    Object jsonValue = jsonPath(object, jsonPath);
+    if (jsonValue == null) {
       return defaultValue;
     }
+    if (jsonValue instanceof Number) {
+      return ((Number) jsonValue).longValue();
+    }
+    return Long.parseLong(jsonValue.toString());
   }
 
   /**
@@ -176,11 +177,7 @@ public class JsonFunctions {
    */
   @ScalarFunction
   public static double jsonPathDouble(Object object, String jsonPath) {
-    final Object jsonValue = jsonPath(object, jsonPath);
-    if (jsonValue instanceof Number) {
-      return ((Number) jsonValue).doubleValue();
-    }
-    return Double.parseDouble(jsonValue.toString());
+    return jsonPathDouble(object, jsonPath, Double.NaN);
   }
 
   /**
@@ -188,11 +185,14 @@ public class JsonFunctions {
    */
   @ScalarFunction
   public static double jsonPathDouble(Object object, String jsonPath, double 
defaultValue) {
-    try {
-      return jsonPathDouble(object, jsonPath);
-    } catch (Exception e) {
+    Object jsonValue = jsonPath(object, jsonPath);
+    if (jsonValue == null) {
       return defaultValue;
     }
+    if (jsonValue instanceof Number) {
+      return ((Number) jsonValue).doubleValue();
+    }
+    return Double.parseDouble(jsonValue.toString());
   }
 
   @VisibleForTesting
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 de1fb24..f9376af 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
@@ -19,18 +19,20 @@
 package org.apache.pinot.common.function;
 
 import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.ObjectMapper;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableMap;
-import com.jayway.jsonpath.PathNotFoundException;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
 import java.util.Map;
 import org.apache.pinot.common.function.scalar.JsonFunctions;
+import org.testng.annotations.DataProvider;
 import org.testng.annotations.Test;
 
 import static org.testng.Assert.assertEquals;
-import static org.testng.Assert.fail;
+import static org.testng.Assert.assertNull;
+import static org.testng.Assert.assertTrue;
 
 
 public class JsonFunctionsTest {
@@ -74,7 +76,9 @@ public class JsonFunctionsTest {
     assertEquals(JsonFunctions.jsonPathDouble(jsonString, "$.actor.id"), 
33500718.0);
     assertEquals(JsonFunctions.jsonPathString(jsonString, "$.actor.aaa", 
"null"), "null");
     assertEquals(JsonFunctions.jsonPathLong(jsonString, "$.actor.aaa", 100L), 
100L);
+    assertEquals(JsonFunctions.jsonPathLong(jsonString, "$.actor.aaa"), 
Long.MIN_VALUE);
     assertEquals(JsonFunctions.jsonPathDouble(jsonString, "$.actor.aaa", 
53.2), 53.2);
+    assertTrue(Double.isNaN(JsonFunctions.jsonPathDouble(jsonString, 
"$.actor.aaa")));
   }
 
   @Test
@@ -111,12 +115,7 @@ public class JsonFunctionsTest {
       throws JsonProcessingException {
     String jsonString = "{\"name\": \"Pete\", \"age\": 24}";
 
-    try {
-      assertEquals(JsonFunctions.jsonPathArray(jsonString, 
"$.subjects[*].name"), new String[]{});
-      fail();
-    } catch (PathNotFoundException e) {
-      assertEquals(e.getMessage(), "Missing property in path $['subjects']");
-    }
+    assertEquals(JsonFunctions.jsonPathArray(jsonString, 
"$.subjects[*].name"), new String[]{});
     assertEquals(JsonFunctions.jsonPathArrayDefaultEmpty(jsonString, 
"$.subjects[*].name"), new String[]{});
     assertEquals(JsonFunctions.jsonPathArrayDefaultEmpty(jsonString, 
"$.subjects[*].grade"), new String[]{});
     assertEquals(JsonFunctions.jsonPathArrayDefaultEmpty(jsonString, 
"$.subjects[*].homework_grades"), new Object[]{});
@@ -245,4 +244,56 @@ public class JsonFunctionsTest {
         new Object[]{Arrays.asList(80, 85, 90, 95, 100), Arrays.asList(60, 65, 
70, 85, 90)});
     assertEquals(JsonFunctions.jsonPathArray(rawData, "$.[*].score"), new 
Integer[]{90, 50});
   }
+
+  private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();
+
+  @DataProvider
+  public static Object[][] jsonPathStringTestCases() {
+    return new Object[][]{
+        {ImmutableMap.of("foo", "x", "bar", ImmutableMap.of("foo", "y")), 
"$.foo", "x"},
+        {ImmutableMap.of("foo", "x", "bar", ImmutableMap.of("foo", "y")), 
"$.qux", null},
+        {ImmutableMap.of("foo", "x", "bar", ImmutableMap.of("foo", "y")), 
"$.bar", "{\"foo\":\"y\"}"},
+    };
+  }
+
+  @Test(dataProvider = "jsonPathStringTestCases")
+  public void testJsonPathString(Map<String, Object> map, String path, String 
expected)
+      throws JsonProcessingException {
+    String value = 
JsonFunctions.jsonPathString(OBJECT_MAPPER.writeValueAsString(map), path);
+    assertEquals(value, expected);
+  }
+
+  @Test(dataProvider = "jsonPathStringTestCases")
+  public void testJsonPathStringWithDefaultValue(Map<String, Object> map, 
String path, String expected)
+      throws JsonProcessingException {
+    String value = 
JsonFunctions.jsonPathString(OBJECT_MAPPER.writeValueAsString(map), path, 
expected);
+    assertEquals(value, expected);
+  }
+
+  @DataProvider
+  public static Object[][] jsonPathArrayTestCases() {
+    return new Object[][]{
+        {ImmutableMap.of("foo", "x", "bar", ImmutableMap.of("foo", "y")), 
"$.foo", new Object[]{"x"}},
+        {ImmutableMap.of("foo", "x", "bar", ImmutableMap.of("foo", "y")), 
"$.qux", null},
+        {
+            ImmutableMap.of("foo", "x", "bar", ImmutableMap.of("foo", "y")), 
"$.bar", new Object[]{
+            ImmutableMap.of("foo", "y")
+        }
+        },
+    };
+  }
+
+  @Test(dataProvider = "jsonPathArrayTestCases")
+  public void testJsonPathArray(Map<String, Object> map, String path, Object[] 
expected)
+      throws JsonProcessingException {
+    Object[] value = 
JsonFunctions.jsonPathArray(OBJECT_MAPPER.writeValueAsString(map), path);
+    if (expected == null) {
+      assertNull(value);
+    } else {
+      assertEquals(value.length, expected.length);
+      for (int i = 0; i < value.length; i++) {
+        assertEquals(value[i], expected[i]);
+      }
+    }
+  }
 }
diff --git 
a/pinot-common/src/test/java/org/apache/pinot/common/function/scalar/ArrayAwareJacksonJsonProviderTest.java
 
b/pinot-common/src/test/java/org/apache/pinot/common/function/scalar/ArrayAwareJacksonJsonProviderTest.java
index 3f4b71e..3f16ed8 100644
--- 
a/pinot-common/src/test/java/org/apache/pinot/common/function/scalar/ArrayAwareJacksonJsonProviderTest.java
+++ 
b/pinot-common/src/test/java/org/apache/pinot/common/function/scalar/ArrayAwareJacksonJsonProviderTest.java
@@ -18,6 +18,7 @@
  */
 package org.apache.pinot.common.function.scalar;
 
+import com.jayway.jsonpath.JsonPathException;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.HashMap;
@@ -48,15 +49,11 @@ public class ArrayAwareJacksonJsonProviderTest {
 
     List<Object> dataInList = Arrays.asList("abc", "efg", "hij");
     assertEquals(jp.length(dataInList), 3);
+  }
 
-    try {
-      jp.length(null);
-      fail();
-    } catch (NullPointerException e) {
-      // It's supposed to get a JsonPathException, but JsonPath library 
actually
-      // has a bug leading to NullPointerException while creating the 
JsonPathException.
-      assertNull(e.getMessage());
-    }
+  @Test(expectedExceptions = JsonPathException.class)
+  public void testArrayLengthThrowsForNullArray() {
+    new JsonFunctions.ArrayAwareJacksonJsonProvider().length(null);
   }
 
   @Test
diff --git 
a/pinot-core/src/main/java/org/apache/pinot/core/common/evaluators/DefaultJsonPathEvaluator.java
 
b/pinot-core/src/main/java/org/apache/pinot/core/common/evaluators/DefaultJsonPathEvaluator.java
index c399b76..b45acec 100644
--- 
a/pinot-core/src/main/java/org/apache/pinot/core/common/evaluators/DefaultJsonPathEvaluator.java
+++ 
b/pinot-core/src/main/java/org/apache/pinot/core/common/evaluators/DefaultJsonPathEvaluator.java
@@ -25,7 +25,6 @@ import com.jayway.jsonpath.Option;
 import com.jayway.jsonpath.ParseContext;
 import com.jayway.jsonpath.spi.json.JacksonJsonProvider;
 import com.jayway.jsonpath.spi.mapper.JacksonMappingProvider;
-import java.nio.charset.StandardCharsets;
 import java.util.List;
 import javax.annotation.Nullable;
 import org.apache.pinot.common.function.JsonPathCache;
@@ -390,57 +389,21 @@ public final class DefaultJsonPathEvaluator implements 
JsonPathEvaluator {
   }
 
   private <T> T extractFromBytes(Dictionary dictionary, int dictId) {
-    try {
-      // TODO make JsonPath accept byte[] - Jackson can
-      return JSON_PARSER_CONTEXT.parse(new 
String(dictionary.getBytesValue(dictId), StandardCharsets.UTF_8))
-          .read(_jsonPath);
-    } catch (Exception e) {
-      // TODO JsonPath 2.7.0 will not throw here but produce null when path 
not found
-      if (_defaultValue == null) {
-        throwPathNotFoundException(e);
-      }
-      return null;
-    }
+    return 
JSON_PARSER_CONTEXT.parseUtf8(dictionary.getBytesValue(dictId)).read(_jsonPath);
   }
 
   private <T, R extends ForwardIndexReaderContext> T 
extractFromBytes(ForwardIndexReader<R> reader, R context,
       int docId) {
-    try {
-      // TODO make JsonPath accept byte[] - Jackson can
-      return JSON_PARSER_CONTEXT.parse(new String(reader.getBytes(docId, 
context), StandardCharsets.UTF_8))
-          .read(_jsonPath);
-    } catch (Exception e) {
-      // TODO JsonPath 2.7.0 will not throw here but produce null when path 
not found
-      if (_defaultValue == null) {
-        throwPathNotFoundException(e);
-      }
-      return null;
-    }
+    return JSON_PARSER_CONTEXT.parseUtf8(reader.getBytes(docId, 
context)).read(_jsonPath);
   }
 
   private <T> T extractFromString(Dictionary dictionary, int dictId) {
-    try {
-      return 
JSON_PARSER_CONTEXT.parse(dictionary.getStringValue(dictId)).read(_jsonPath);
-    } catch (Exception e) {
-      // TODO JsonPath 2.7.0 will not throw here but produce null when path 
not found
-      if (_defaultValue == null) {
-        throwPathNotFoundException(e);
-      }
-      return null;
-    }
+    return 
JSON_PARSER_CONTEXT.parse(dictionary.getStringValue(dictId)).read(_jsonPath);
   }
 
   private <T, R extends ForwardIndexReaderContext> T 
extractFromString(ForwardIndexReader<R> reader, R context,
       int docId) {
-    try {
-      return JSON_PARSER_CONTEXT.parse(reader.getString(docId, 
context)).read(_jsonPath);
-    } catch (Exception e) {
-      // TODO JsonPath 2.7.0 will not throw here but produce null when path 
not found
-      if (_defaultValue == null) {
-        throwPathNotFoundException(e);
-      }
-      return null;
-    }
+    return JSON_PARSER_CONTEXT.parseUtf8(reader.getBytes(docId, 
context)).read(_jsonPath);
   }
 
   private void processValue(int index, Object value, int defaultValue, int[] 
valueBuffer) {
@@ -582,8 +545,4 @@ public final class DefaultJsonPathEvaluator implements 
JsonPathEvaluator {
   private void throwPathNotFoundException() {
     throw new IllegalArgumentException("Illegal Json Path: " + 
_jsonPath.getPath() + " does not match document");
   }
-
-  private void throwPathNotFoundException(Exception e) {
-    throw new IllegalArgumentException("Illegal Json Path: " + 
_jsonPath.getPath() + " does not match document", e);
-  }
 }
diff --git a/pom.xml b/pom.xml
index 1d38cf3..dec133e 100644
--- a/pom.xml
+++ b/pom.xml
@@ -130,7 +130,7 @@
     <hadoop.version>2.7.0</hadoop.version>
     <scala.version>2.13.3</scala.version>
     <antlr.version>4.6</antlr.version>
-    <jsonpath.version>2.4.0</jsonpath.version>
+    <jsonpath.version>2.7.0</jsonpath.version>
     <quartz.version>2.3.2</quartz.version>
     <calcite.version>1.19.0</calcite.version>
     <lucene.version>8.2.0</lucene.version>

---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscr...@pinot.apache.org
For additional commands, e-mail: commits-h...@pinot.apache.org

Reply via email to