Skip to content

Commit

Permalink
⚗️ Experimenting performance improvements for field-ql gql typeResolv…
Browse files Browse the repository at this point in the history
…er predicate
  • Loading branch information
ujibang committed Aug 26, 2024
1 parent 2072c38 commit cb5e417
Show file tree
Hide file tree
Showing 8 changed files with 68 additions and 32 deletions.
16 changes: 16 additions & 0 deletions commons/src/main/java/org/restheart/utils/BsonUtils.java
Original file line number Diff line number Diff line change
Expand Up @@ -285,6 +285,22 @@ public static Optional<BsonValue> get(BsonDocument doc, String path) {
}
}

/**
*
* @param ctx the JxPathContext build from a BsonDocument
* @param path the path of the field, can use the dot notation
* @return
*/
public static Optional<BsonValue> get(JXPathContext ctx, String path) {
final String xpath = dotNotationToXPath(path);

try {
return Optional.of((BsonValue) ctx.getValue(xpath));
} catch(Throwable t) {
return Optional.empty();
}
}

/**
* converts the dot notation syntax to xpath
* a.b.c -> /a/b/c
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -172,8 +172,9 @@ public GraphQLApp build() throws IllegalStateException {
final Optional<Entry<String, Predicate>> match;

if (obj instanceof BsonValue value) {
final var ex = ExchangeWithBsonValue.exchange(value);
match = unionMapping.entrySet().stream()
.filter(p -> p.getValue().resolve(ExchangeWithBsonValue.exchange(value)))
.filter(p -> p.getValue().resolve(ex))
.findFirst();
} else {
// predicates can only resolve on BsonValues
Expand All @@ -199,8 +200,9 @@ public GraphQLApp build() throws IllegalStateException {
final Optional<Entry<String, Predicate>> match;

if (obj instanceof BsonValue value) {
final var ex = ExchangeWithBsonValue.exchange(value);
match = interfaceMapping.entrySet().stream()
.filter(p -> p.getValue().resolve(ExchangeWithBsonValue.exchange(value)))
.filter(p -> p.getValue().resolve(ex))
.findFirst();
} else {
// predicates can resolve on BsonValues
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@

package org.restheart.graphql.predicates;

import org.apache.commons.jxpath.JXPathContext;
import org.bson.BsonValue;

import io.undertow.server.HttpServerExchange;
Expand All @@ -44,5 +45,27 @@ public static BsonValue value(HttpServerExchange exchange) {
return exchange.getAttachment(DOC_KEY);
}

/**
* To enhance the performance of XPath expression evaluations, this method caches the
* JXPathContext object. This cached context facilitates quicker lookups and reduces the
* overhead associated with creating new contexts for each query.
*
* For more information, refer to {@link org.restheart.utils.BsonUtils#get(JXPathContext docCtx, String path)}
*
* @param exchange The exchange containing the BsonValue from which the JXPathContext is constructed.
* @return The JXPathContext built from the BsonValue attached to the exchange.
*/
public static JXPathContext jxPathCtx(HttpServerExchange exchange) {
var ctx = exchange.getAttachment(JX_PATH_CTX_KEY);

if (ctx == null) {
ctx = JXPathContext.newContext(value(exchange));
exchange.putAttachment(JX_PATH_CTX_KEY, ctx);
}

return ctx;
}

private static final AttachmentKey<BsonValue> DOC_KEY = AttachmentKey.create(BsonValue.class);
private static final AttachmentKey<JXPathContext> JX_PATH_CTX_KEY = AttachmentKey.create(JXPathContext.class);
}
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
import java.util.Map;
import java.util.Set;

import org.bson.BsonDocument;
import org.apache.commons.jxpath.JXPathContext;
import org.bson.BsonValue;
import org.restheart.utils.BsonUtils;

Expand All @@ -35,7 +35,7 @@
/**
* a predicate that resolve to true if the request contains the specified keys
*/
public class FieldEqPredicate implements PredicateOverBsonValue {
public class FieldEqPredicate implements PredicateOverJxPathCtx {
private final String key;
private final BsonValue value;

Expand All @@ -49,15 +49,11 @@ public FieldEqPredicate(String key, String value) {
}

@Override
public boolean resolve(BsonValue value) {
if (value instanceof BsonDocument doc) {
var _v = BsonUtils.get(doc, key);
public boolean resolve(JXPathContext ctx) {
var _v = BsonUtils.get(ctx, key);

if (_v.isPresent()) {
return this.value.equals(_v.get());
} else {
return false;
}
if (_v.isPresent()) {
return this.value.equals(_v.get());
} else {
return false;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,7 @@
import java.util.Map;
import java.util.Set;

import org.bson.BsonDocument;
import org.bson.BsonValue;
import org.apache.commons.jxpath.JXPathContext;
import org.restheart.utils.BsonUtils;

import com.google.common.collect.Sets;
Expand All @@ -36,7 +35,7 @@
/**
* a predicate that resolve to true if the request contains the specified keys
*/
public class FieldExists implements PredicateOverBsonValue {
public class FieldExists implements PredicateOverJxPathCtx {
private final Set<String> fields;

public FieldExists(String[] fields) {
Expand All @@ -48,12 +47,8 @@ public FieldExists(String[] fields) {
}

@Override
public boolean resolve(BsonValue value) {
if (value instanceof BsonDocument doc) {
return this.fields.stream().allMatch(f -> BsonUtils.get(doc, f).isPresent());
} else {
return false;
}
public boolean resolve(JXPathContext ctx) {
return this.fields.stream().allMatch(f -> BsonUtils.get(ctx, f).isPresent());
}

public static class Builder implements PredicateBuilder {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,12 @@
package org.restheart.graphql.predicates;

import org.bson.BsonValue;

import io.undertow.predicate.Predicate;
import io.undertow.server.HttpServerExchange;

public interface PredicateOverBsonValue extends Predicate {
@Override
default boolean resolve(HttpServerExchange exchage) {
return resolve(ExchangeWithBsonValue.value(exchage));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,7 @@
public interface PredicateOverJxPathCtx extends Predicate {
@Override
default boolean resolve(HttpServerExchange exchage) {
//return resolve(ExchangeWithBsonValue.jxPathCtx(exchage));
return true;
return resolve(ExchangeWithBsonValue.jxPathCtx(exchage));
}

boolean resolve(JXPathContext doc);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,30 +21,33 @@

package org.restheart.graphql.predicates;

import org.apache.commons.jxpath.JXPathContext;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;
import org.junit.jupiter.api.Test;
import static org.restheart.utils.BsonUtils.document;

import org.junit.jupiter.api.Test;
import io.undertow.predicate.Predicates;

public class PredicatesTest {
@Test
public void testNullVsAbsent() {
var doc = document().put("bar", 1).get();
var p = (PredicateOverBsonValue) Predicates.parse("field-exists(bar)");
var np = (PredicateOverBsonValue) Predicates.parse("field-exists(foo)");
var ctx = JXPathContext.newContext(doc);
var p = (PredicateOverJxPathCtx) Predicates.parse("field-exists(bar)");
var np = (PredicateOverJxPathCtx) Predicates.parse("field-exists(foo)");

assertTrue(p.resolve(doc));
assertFalse(np.resolve(doc));
assertTrue(p.resolve(ctx));
assertFalse(np.resolve(ctx));

var nestedDoc = document().put("bar", document().put("foo", 1)).get();
var nestedDocCtx = JXPathContext.newContext(nestedDoc);

var _p = (PredicateOverBsonValue) Predicates.parse("field-exists(bar.foo)");
var _np = (PredicateOverBsonValue) Predicates.parse("field-exists(bar.not)");
var _p = (PredicateOverJxPathCtx) Predicates.parse("field-exists(bar.foo)");
var _np = (PredicateOverJxPathCtx) Predicates.parse("field-exists(bar.not)");

assertTrue(_p.resolve(nestedDoc));
assertFalse(_np.resolve(nestedDoc));
assertTrue(_p.resolve(nestedDocCtx));
assertFalse(_np.resolve(nestedDocCtx));
}

@Test
Expand Down

0 comments on commit cb5e417

Please sign in to comment.