Browse Source

Added result cache for root ($) queries performed by predicates.

pull/57/head
Kalle Stenflo 10 years ago
parent
commit
c20917350e
  1. 16
      json-path/src/main/java/com/jayway/jsonpath/Criteria.java
  2. 8
      json-path/src/main/java/com/jayway/jsonpath/internal/token/EvaluationContextImpl.java
  3. 32
      json-path/src/main/java/com/jayway/jsonpath/internal/token/PredicateContextImpl.java
  4. 8
      json-path/src/main/java/com/jayway/jsonpath/internal/token/PredicatePathToken.java
  5. 2
      json-path/src/main/java/com/jayway/jsonpath/internal/token/ScanPathToken.java
  6. 7
      json-path/src/test/java/com/jayway/jsonpath/BaseTest.java
  7. 9
      json-path/src/test/java/com/jayway/jsonpath/InlineFilterTest.java

16
json-path/src/main/java/com/jayway/jsonpath/Criteria.java

@ -208,8 +208,9 @@ public class Criteria implements Predicate {
MATCHES { MATCHES {
@Override @Override
boolean eval(Object expected, final Object actual, final PredicateContext ctx) { boolean eval(Object expected, final Object actual, final PredicateContext ctx) {
PredicateContextImpl pci = (PredicateContextImpl) ctx;
Predicate exp = (Predicate) expected; Predicate exp = (Predicate) expected;
return exp.apply(new PredicateContextImpl(actual, ctx.root(), ctx.configuration())); return exp.apply(new PredicateContextImpl(actual, ctx.root(), ctx.configuration(), pci.documentPathCache()));
} }
}, },
NOT_EMPTY { NOT_EMPTY {
@ -286,7 +287,7 @@ public class Criteria implements Predicate {
if (CriteriaType.EXISTS == criteriaType) { if (CriteriaType.EXISTS == criteriaType) {
boolean exists = ((Boolean) expected); boolean exists = ((Boolean) expected);
try { try {
Configuration c = Configuration.builder().jsonProvider(ctx.configuration().jsonProvider()).options().build(); Configuration c = Configuration.builder().jsonProvider(ctx.configuration().jsonProvider()).options(Option.REQUIRE_PROPERTIES).build();
path.evaluate(ctx.item(), ctx.root(), c).getValue(); path.evaluate(ctx.item(), ctx.root(), c).getValue();
return exists; return exists;
} catch (PathNotFoundException e) { } catch (PathNotFoundException e) {
@ -299,8 +300,15 @@ public class Criteria implements Predicate {
Object expectedVal = expected; Object expectedVal = expected;
if(expected instanceof Path){ if(expected instanceof Path){
Path expectedPath = (Path) expected; Path expectedPath = (Path) expected;
Object doc = expectedPath.isRootPath()?ctx.root():ctx.item();
expectedVal = expectedPath.evaluate(doc, ctx.root(), ctx.configuration()).getValue(); if(ctx instanceof PredicateContextImpl){
//This will use cache for document ($) queries
PredicateContextImpl ctxi = (PredicateContextImpl) ctx;
expectedVal = ctxi.evaluate(expectedPath);
} else {
Object doc = expectedPath.isRootPath()?ctx.root():ctx.item();
expectedVal = expectedPath.evaluate(doc, ctx.root(), ctx.configuration()).getValue();
}
} }
return criteriaType.eval(expectedVal, actual, ctx); return criteriaType.eval(expectedVal, actual, ctx);

8
json-path/src/main/java/com/jayway/jsonpath/internal/token/EvaluationContextImpl.java

@ -22,7 +22,9 @@ import com.jayway.jsonpath.internal.Path;
import com.jayway.jsonpath.spi.json.JsonProvider; import com.jayway.jsonpath.spi.json.JsonProvider;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Objects;
import java.util.Set; import java.util.Set;
import static com.jayway.jsonpath.internal.Utils.notNull; import static com.jayway.jsonpath.internal.Utils.notNull;
@ -37,8 +39,10 @@ public class EvaluationContextImpl implements EvaluationContext {
private final Object pathResult; private final Object pathResult;
private final Path path; private final Path path;
private final Object rootDocument; private final Object rootDocument;
private final HashMap<Path, Object> documentEvalCache = new HashMap<Path, Object>();
private int resultIndex = 0; private int resultIndex = 0;
public EvaluationContextImpl(Path path, Object rootDocument, Configuration configuration) { public EvaluationContextImpl(Path path, Object rootDocument, Configuration configuration) {
notNull(path, "path can not be null"); notNull(path, "path can not be null");
notNull(rootDocument, "root can not be null"); notNull(rootDocument, "root can not be null");
@ -50,6 +54,10 @@ public class EvaluationContextImpl implements EvaluationContext {
this.pathResult = configuration.jsonProvider().createArray(); this.pathResult = configuration.jsonProvider().createArray();
} }
public HashMap<Path, Object> documentEvalCache() {
return documentEvalCache;
}
public void addResult(String path, Object model) { public void addResult(String path, Object model) {
configuration.jsonProvider().setProperty(valueResult, resultIndex, model); configuration.jsonProvider().setProperty(valueResult, resultIndex, model);
configuration.jsonProvider().setProperty(pathResult, resultIndex, path); configuration.jsonProvider().setProperty(pathResult, resultIndex, path);

32
json-path/src/main/java/com/jayway/jsonpath/internal/token/PredicateContextImpl.java

@ -16,17 +16,47 @@ package com.jayway.jsonpath.internal.token;
import com.jayway.jsonpath.Configuration; import com.jayway.jsonpath.Configuration;
import com.jayway.jsonpath.Predicate; import com.jayway.jsonpath.Predicate;
import com.jayway.jsonpath.internal.Path;
import com.jayway.jsonpath.spi.mapper.MappingException; import com.jayway.jsonpath.spi.mapper.MappingException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.HashMap;
public class PredicateContextImpl implements Predicate.PredicateContext { public class PredicateContextImpl implements Predicate.PredicateContext {
private static final Logger logger = LoggerFactory.getLogger(PredicateContextImpl.class);
private final Object contextDocument; private final Object contextDocument;
private final Object rootDocument; private final Object rootDocument;
private final Configuration configuration; private final Configuration configuration;
private final HashMap<Path, Object> documentPathCache;
public PredicateContextImpl(Object contextDocument, Object rootDocument, Configuration configuration) { public PredicateContextImpl(Object contextDocument, Object rootDocument, Configuration configuration, HashMap<Path, Object> documentPathCache) {
this.contextDocument = contextDocument; this.contextDocument = contextDocument;
this.rootDocument = rootDocument; this.rootDocument = rootDocument;
this.configuration = configuration; this.configuration = configuration;
this.documentPathCache = documentPathCache;
}
public Object evaluate(Path path){
Object result;
if(path.isRootPath()){
if(documentPathCache.containsKey(path)){
logger.debug("Using cached result for root path: " + path.toString());
result = documentPathCache.get(path);
} else {
result = path.evaluate(rootDocument, rootDocument, configuration).getValue();
documentPathCache.put(path, result);
}
} else {
result = path.evaluate(contextDocument, rootDocument, configuration).getValue();
}
return result;
}
public HashMap<Path, Object> documentPathCache() {
return documentPathCache;
} }
@Override @Override

8
json-path/src/main/java/com/jayway/jsonpath/internal/token/PredicatePathToken.java

@ -49,7 +49,7 @@ public class PredicatePathToken extends PathToken {
@Override @Override
public void evaluate(String currentPath, Object model, EvaluationContextImpl ctx) { public void evaluate(String currentPath, Object model, EvaluationContextImpl ctx) {
if (ctx.jsonProvider().isMap(model)) { if (ctx.jsonProvider().isMap(model)) {
if (accept(model, ctx.rootDocument(), ctx.configuration())) { if (accept(model, ctx.rootDocument(), ctx.configuration(), ctx)) {
if (isLeaf()) { if (isLeaf()) {
ctx.addResult(currentPath, model); ctx.addResult(currentPath, model);
} else { } else {
@ -61,7 +61,7 @@ public class PredicatePathToken extends PathToken {
Iterable<?> objects = ctx.jsonProvider().toIterable(model); Iterable<?> objects = ctx.jsonProvider().toIterable(model);
for (Object idxModel : objects) { for (Object idxModel : objects) {
if (accept(idxModel, ctx.rootDocument(), ctx.configuration())) { if (accept(idxModel, ctx.rootDocument(), ctx.configuration(), ctx)) {
handleArrayIndex(idx, currentPath, model, ctx); handleArrayIndex(idx, currentPath, model, ctx);
} }
idx++; idx++;
@ -71,8 +71,8 @@ public class PredicatePathToken extends PathToken {
} }
} }
public boolean accept(final Object obj, final Object root, final Configuration configuration) { public boolean accept(final Object obj, final Object root, final Configuration configuration, EvaluationContextImpl evaluationContext) {
Predicate.PredicateContext ctx = new PredicateContextImpl(obj, root, configuration); Predicate.PredicateContext ctx = new PredicateContextImpl(obj, root, configuration, evaluationContext.documentEvalCache());
for (Predicate predicate : predicates) { for (Predicate predicate : predicates) {
if (!predicate.apply (ctx)) { if (!predicate.apply (ctx)) {

2
json-path/src/main/java/com/jayway/jsonpath/internal/token/ScanPathToken.java

@ -161,7 +161,7 @@ public class ScanPathToken extends PathToken {
@Override @Override
public boolean matches(Object model) { public boolean matches(Object model) {
return predicatePathToken.accept(model, ctx.rootDocument(), ctx.configuration()); return predicatePathToken.accept(model, ctx.rootDocument(), ctx.configuration(), ctx);
} }
} }

7
json-path/src/test/java/com/jayway/jsonpath/BaseTest.java

@ -1,9 +1,13 @@
package com.jayway.jsonpath; package com.jayway.jsonpath;
import com.jayway.jsonpath.internal.Path;
import com.jayway.jsonpath.internal.spi.json.GsonJsonProvider; import com.jayway.jsonpath.internal.spi.json.GsonJsonProvider;
import com.jayway.jsonpath.internal.spi.mapper.GsonMappingProvider; import com.jayway.jsonpath.internal.spi.mapper.GsonMappingProvider;
import com.jayway.jsonpath.internal.token.EvaluationContextImpl;
import com.jayway.jsonpath.internal.token.PredicateContextImpl; import com.jayway.jsonpath.internal.token.PredicateContextImpl;
import java.util.HashMap;
public class BaseTest { public class BaseTest {
/* /*
static { static {
@ -84,6 +88,7 @@ public class BaseTest {
"}"; "}";
public Predicate.PredicateContext createPredicateContext(final Object check) { public Predicate.PredicateContext createPredicateContext(final Object check) {
return new PredicateContextImpl(check, check, Configuration.defaultConfiguration());
return new PredicateContextImpl(check, check, Configuration.defaultConfiguration(), new HashMap<Path, Object>());
} }
} }

9
json-path/src/test/java/com/jayway/jsonpath/InlineFilterTest.java

@ -37,4 +37,13 @@ public class InlineFilterTest extends BaseTest {
assertThat(none2.size()).isEqualTo(0); assertThat(none2.size()).isEqualTo(0);
} }
@Test
public void document_queries_are_cached() {
Object read = reader.read("$.store.book[?(@.display-price <= $.max-price)]");
System.out.println(read);
}
} }

Loading…
Cancel
Save