diff --git a/json-path-assert/src/test/resources/lotto.json b/json-path-assert/src/test/resources/lotto.json index eb4e4691..4ba13d0d 100644 --- a/json-path-assert/src/test/resources/lotto.json +++ b/json-path-assert/src/test/resources/lotto.json @@ -1,6 +1,5 @@ { "lotto": { - "maxAverage": 100, "lottoId": 5, "winning-numbers": [ 2, diff --git a/json-path/src/main/java/com/jayway/jsonpath/AggregationMapReduce.java b/json-path/src/main/java/com/jayway/jsonpath/AggregationMapReduce.java deleted file mode 100644 index f147eb57..00000000 --- a/json-path/src/main/java/com/jayway/jsonpath/AggregationMapReduce.java +++ /dev/null @@ -1,23 +0,0 @@ -package com.jayway.jsonpath; - -import java.io.File; -import java.io.IOException; -import java.util.List; - -/** - * Defines a pattern for taking a collection of input streams and executing the same path operation across all files - * map / reducing the results as they come in to provide an aggregation function on top of the - * - * Created by matt@mjgreenwood.net on 6/26/15. - */ -public class AggregationMapReduce { - - public static void main(String args[]) throws IOException { - ReadContext ctx = JsonPath.parse(new File("/home/mattg/dev/JsonPath/json-path-assert/src/test/resources/lotto.json")); - List numbers = ctx.read("$.lotto.winners..numbers.%sum()"); - - Object value = ctx.read("$.lotto.winners.[?(@.winnerId > $.lotto.winners.%length())].numbers.%avg()"); - System.out.println(numbers); - System.out.println(value); - } -} diff --git a/json-path/src/main/java/com/jayway/jsonpath/internal/function/numeric/Min.java b/json-path/src/main/java/com/jayway/jsonpath/internal/function/numeric/Min.java index d626d48e..3c57e5f2 100644 --- a/json-path/src/main/java/com/jayway/jsonpath/internal/function/numeric/Min.java +++ b/json-path/src/main/java/com/jayway/jsonpath/internal/function/numeric/Min.java @@ -10,7 +10,7 @@ public class Min extends AbstractAggregation { @Override protected void next(Number value) { - if (min < value.doubleValue()) { + if (min > value.doubleValue()) { min = value.doubleValue(); } } diff --git a/json-path/src/main/java/com/jayway/jsonpath/internal/token/FunctionPathToken.java b/json-path/src/main/java/com/jayway/jsonpath/internal/token/FunctionPathToken.java index 7765e292..5008b230 100644 --- a/json-path/src/main/java/com/jayway/jsonpath/internal/token/FunctionPathToken.java +++ b/json-path/src/main/java/com/jayway/jsonpath/internal/token/FunctionPathToken.java @@ -34,17 +34,23 @@ public class FunctionPathToken extends PathToken { @Override public void evaluate(String currentPath, PathRef parent, Object model, EvaluationContextImpl ctx) { Function function = FunctionFactory.newFunction(functionName); - ctx.addResult(currentPath, parent, function.invoke(currentPath, parent, model, ctx)); + Object result = function.invoke(currentPath, parent, model, ctx); + ctx.addResult(currentPath, parent, result); } + /** + * Return the actual value by indicating true. If this return was false then we'd return the value in an array which + * isn't what is desired - true indicates the raw value is returned. + * + * @return + */ @Override boolean isTokenDefinite() { - return false; + return true; } @Override String getPathFragment() { return pathFragment; } - } diff --git a/json-path/src/main/java/com/jayway/jsonpath/internal/token/PathToken.java b/json-path/src/main/java/com/jayway/jsonpath/internal/token/PathToken.java index bfd31564..5b6da174 100644 --- a/json-path/src/main/java/com/jayway/jsonpath/internal/token/PathToken.java +++ b/json-path/src/main/java/com/jayway/jsonpath/internal/token/PathToken.java @@ -73,9 +73,6 @@ public abstract class PathToken { PathRef pathRef = ctx.forUpdate() ? PathRef.create(model, property) : PathRef.NO_OP; if (isLeaf()) { ctx.addResult(evalPath, pathRef, propertyVal); -// } else if (isFunction()) { -// Function function = FunctionFactory.newFunction(next.getPathFragment()); -// next.invoke(function, evalPath, pathRef, propertyVal, ctx); } else { next().evaluate(evalPath, pathRef, propertyVal, ctx); @@ -153,10 +150,6 @@ public abstract class PathToken { return next == null; } -// boolean isFunction() { -// return null != next && next.getPathFragment().startsWith("['%"); -// } - boolean isRoot() { return prev == null; } diff --git a/json-path/src/test/java/com/jayway/jsonpath/functions/BaseFunctionTest.java b/json-path/src/test/java/com/jayway/jsonpath/functions/BaseFunctionTest.java index 100be413..0d50808f 100644 --- a/json-path/src/test/java/com/jayway/jsonpath/functions/BaseFunctionTest.java +++ b/json-path/src/test/java/com/jayway/jsonpath/functions/BaseFunctionTest.java @@ -2,6 +2,9 @@ package com.jayway.jsonpath.functions; import com.jayway.jsonpath.Configuration; +import java.io.IOException; +import java.util.Scanner; + import static com.jayway.jsonpath.JsonPath.using; import static org.assertj.core.api.Assertions.assertThat; @@ -33,4 +36,8 @@ public class BaseFunctionTest { protected void verifyMathFunction(String pathExpr, Object expectedValue) { verifyFunction(pathExpr, NUMBER_SERIES, expectedValue); } + + protected String getResourceAsText(String resourceName) throws IOException { + return new Scanner(BaseFunctionTest.class.getResourceAsStream(resourceName), "UTF-8").useDelimiter("\\A").next(); + } } diff --git a/json-path/src/test/java/com/jayway/jsonpath/functions/JSONEntityFunctionTest.java b/json-path/src/test/java/com/jayway/jsonpath/functions/JSONEntityFunctionTest.java index 254d2e9f..2bcf5179 100644 --- a/json-path/src/test/java/com/jayway/jsonpath/functions/JSONEntityFunctionTest.java +++ b/json-path/src/test/java/com/jayway/jsonpath/functions/JSONEntityFunctionTest.java @@ -1,7 +1,10 @@ package com.jayway.jsonpath.functions; +import net.minidev.json.JSONArray; import org.junit.Test; +import java.io.IOException; + /** * Verifies methods that are helper implementations of functions for manipulating JSON entities, i.e. * length, etc. @@ -9,6 +12,42 @@ import org.junit.Test; * Created by mattg on 6/27/15. */ public class JSONEntityFunctionTest extends BaseFunctionTest { + + private static final String BATCH_JSON = "{\n" + + " \"batches\": {\n" + + " \"minBatchSize\": 10,\n" + + " \"results\": [\n" + + " {\n" + + " \"productId\": 23,\n" + + " \"values\": [\n" + + " 2,\n" + + " 45,\n" + + " 34,\n" + + " 23,\n" + + " 3,\n" + + " 5,\n" + + " 4,\n" + + " 3,\n" + + " 2,\n" + + " 1,\n" + + " ]\n" + + " },\n" + + " {\n" + + " \"productId\": 23,\n" + + " \"values\": [\n" + + " 52,\n" + + " 3,\n" + + " 12,\n" + + " 11,\n" + + " 18,\n" + + " 22,\n" + + " 1\n" + + " ]\n" + + " }\n" + + " ]\n" + + " }\n" + + "}"; + @Test public void testLengthOfTextArray() { // The length of JSONArray is an integer @@ -20,4 +59,38 @@ public class JSONEntityFunctionTest extends BaseFunctionTest { // The length of JSONArray is an integer verifyFunction("$.numbers.%length()", NUMBER_SERIES, 10); } + + /** + * The fictitious use-case/story - is we have a collection of batches with values indicating some quality metric. + * We want to determine the average of the values for only the batch's values where the number of items in the batch + * is greater than the min batch size which is encoded in the JSON document. + * + * We use the length function in the predicate to determine the number of values in each batch and then for those + * batches where the count is greater than min we calculate the average batch value. + * + * Its completely contrived example, however, this test exercises functions within predicates. + */ + @Test + public void testPredicateWithFunctionCallSingleMatch() { + String path = "$.batches.results[?(@.values.%length() >= $.batches.minBatchSize)].values.%avg()"; + + // Its an array because in some use-cases the min size might match more than one batch and thus we'll get + // the average out for each collection + JSONArray values = new JSONArray(); + values.add(12.2d); + verifyFunction(path, BATCH_JSON, values); + } + + @Test + public void testPredicateWithFunctionCallTwoMatches() { + String path = "$.batches.results[?(@.values.%length() >= 3)].values.%avg()"; + + // Its an array because in some use-cases the min size might match more than one batch and thus we'll get + // the average out for each collection + JSONArray values = new JSONArray(); + values.add(12.2d); + values.add(17d); + verifyFunction(path, BATCH_JSON, values); + } + } diff --git a/json-path/src/test/java/com/jayway/jsonpath/functions/NumericFunctionTest.java b/json-path/src/test/java/com/jayway/jsonpath/functions/NumericFunctionTest.java index fcfa394b..4f00bda9 100644 --- a/json-path/src/test/java/com/jayway/jsonpath/functions/NumericFunctionTest.java +++ b/json-path/src/test/java/com/jayway/jsonpath/functions/NumericFunctionTest.java @@ -25,7 +25,7 @@ public class NumericFunctionTest extends BaseFunctionTest { @Test public void testAverageOfDoubles() { - verifyMathFunction("$.numbers.%average()", (10d * (10d + 1d)) / 2d); + verifyMathFunction("$.numbers.%avg()", 5.5); } @Test @@ -45,7 +45,7 @@ public class NumericFunctionTest extends BaseFunctionTest { @Test public void testStdDevOfDouble() { - verifyMathFunction("$.numbers.%stddev()", 1d); + verifyMathFunction("$.numbers.%stddev()", 2.8722813232690143d); } /**