Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support optimizations for more expressions #727

Merged
merged 8 commits into from
Dec 5, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view

This file was deleted.

119 changes: 119 additions & 0 deletions src/main/java/org/javarosa/core/model/CompareToNodeExpression.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
package org.javarosa.core.model;

import kotlin.Pair;
import org.javarosa.core.model.condition.EvaluationContext;
import org.javarosa.core.model.instance.DataInstance;
import org.javarosa.core.model.instance.TreeReference;
import org.javarosa.xpath.expr.XPathCmpExpr;
import org.javarosa.xpath.expr.XPathEqExpr;
import org.javarosa.xpath.expr.XPathExpression;
import org.javarosa.xpath.expr.XPathFuncExpr;
import org.javarosa.xpath.expr.XPathNumericLiteral;
import org.javarosa.xpath.expr.XPathPathExpr;
import org.javarosa.xpath.expr.XPathStringLiteral;
import org.jetbrains.annotations.Nullable;

import java.util.Arrays;
import java.util.LinkedList;
import java.util.Queue;

/**
* Convenience class for identifying and dealing with expressions that compare a child node to something else in the
* form like:
* <p/>
* name = /data/search
* <p/>
* In the example above, "name" would be the "node side" (a relative expression) and "/data/search" would be the
* "context side" (an absolute or context expression). These expressions are useful to be able to identify for caching
* evaluations as nodes with the same node side evaluation and context side expression will ultimately evaluate the same
* result.
* <p/>
* This class does not support expressions comparing two relative expressions as there would be two nodes sides.
*/
public class CompareToNodeExpression {

private final XPathPathExpr nodeSide;
private final XPathExpression contextSide;
private final XPathExpression original;

public CompareToNodeExpression(XPathPathExpr nodeSide, XPathExpression contextSide, XPathExpression original) {
this.nodeSide = nodeSide;
this.contextSide = contextSide;
this.original = original;
}

public Object evalNodeSide(DataInstance sourceInstance, EvaluationContext evaluationContext, TreeReference child, int childIndex) {
EvaluationContext rescopedContext = evaluationContext.rescope(child, childIndex);
return getNodeSide().eval(sourceInstance, rescopedContext).unpack();
}

public Object evalContextSide(DataInstance sourceInstance, EvaluationContext evaluationContext) {
if (contextSide instanceof XPathPathExpr) {
return ((XPathPathExpr) getContextSide()).eval(sourceInstance, evaluationContext).unpack();
} else {
return contextSide.eval(sourceInstance, evaluationContext);
}
}

public XPathPathExpr getNodeSide() {
return nodeSide;
}

public XPathExpression getContextSide() {
return contextSide;
}

public XPathExpression getOriginal() {
return original;
}

@Nullable
public static CompareToNodeExpression parse(XPathExpression expression) {
XPathExpression a = null;
XPathExpression b = null;

if (expression instanceof XPathCmpExpr) {
a = ((XPathCmpExpr) expression).a;
b = ((XPathCmpExpr) expression).b;
} else if (expression instanceof XPathEqExpr) {
a = ((XPathEqExpr) expression).a;
b = ((XPathEqExpr) expression).b;
} else if (expression instanceof XPathFuncExpr && expression.isIdempotent()
&& ((XPathFuncExpr) expression).args.length == 2) {
a = ((XPathFuncExpr) expression).args[0];
b = ((XPathFuncExpr) expression).args[1];
}

Pair<XPathPathExpr, XPathExpression> nodeAndContextSides = getNodeAndContextSides(a, b);
if (nodeAndContextSides != null) {
return new CompareToNodeExpression(nodeAndContextSides.getFirst(), nodeAndContextSides.getSecond(), expression);
} else {
return null;
}
}

private static Pair<XPathPathExpr, XPathExpression> getNodeAndContextSides(XPathExpression a, XPathExpression b) {
XPathPathExpr node = null;
XPathExpression context = null;

Queue<XPathExpression> subExpressions = new LinkedList<>(Arrays.asList(a, b));
while (!subExpressions.isEmpty()) {
XPathExpression subExpression = subExpressions.poll();
if (subExpression instanceof XPathPathExpr) {
if (((XPathPathExpr) subExpression).init_context == XPathPathExpr.INIT_CONTEXT_RELATIVE)
node = (XPathPathExpr) subExpression;
else {
context = subExpression;
}
} else if (subExpression instanceof XPathNumericLiteral || subExpression instanceof XPathStringLiteral) {
context = subExpression;
}
}

if (node != null && context != null) {
return new Pair<>(node, context);
} else {
return null;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -29,10 +29,10 @@ public List<TreeReference> filter(@NotNull DataInstance sourceInstance, @NotNull
return next.get();
}

CompareChildToAbsoluteExpression candidate = CompareChildToAbsoluteExpression.parse(predicate);
CompareToNodeExpression candidate = CompareToNodeExpression.parse(predicate);
if (candidate != null) {
Object absoluteValue = candidate.evalAbsolute(sourceInstance, evaluationContext);
String key = nodeSet.toString() + predicate + candidate.getRelativeSide() + absoluteValue.toString();
Object absoluteValue = candidate.evalContextSide(sourceInstance, evaluationContext);
String key = nodeSet.toString() + predicate + candidate.getNodeSide() + absoluteValue.toString();

if (cachedEvaluations.containsKey(key)) {
return cachedEvaluations.get(key);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,16 +33,16 @@ public List<TreeReference> filter(@NotNull DataInstance sourceInstance, @NotNull
return next.get();
}

CompareChildToAbsoluteExpression candidate = CompareChildToAbsoluteExpression.parse(predicate);
CompareToNodeExpression candidate = CompareToNodeExpression.parse(predicate);
if (candidate != null) {
XPathEqExpr original = (XPathEqExpr) candidate.getOriginal();
if (original.isEqual()) {
String section = nodeSet + candidate.getRelativeSide().toString();
String section = nodeSet + candidate.getNodeSide().toString();
if (!index.contains(section)) {
buildIndex(sourceInstance, candidate, children, evaluationContext, section);
}

Object absoluteValue = candidate.evalAbsolute(sourceInstance, evaluationContext);
Object absoluteValue = candidate.evalContextSide(sourceInstance, evaluationContext);
return index.lookup(section, absoluteValue.toString());
} else {
return next.get();
Expand All @@ -52,12 +52,12 @@ public List<TreeReference> filter(@NotNull DataInstance sourceInstance, @NotNull
}
}

private void buildIndex(DataInstance sourceInstance, CompareChildToAbsoluteExpression predicate, List<TreeReference> children, EvaluationContext evaluationContext, String section) {
private void buildIndex(DataInstance sourceInstance, CompareToNodeExpression predicate, List<TreeReference> children, EvaluationContext evaluationContext, String section) {
for (int i = 0; i < children.size(); i++) {
TreeReference child = children.get(i);

Measure.log("IndexEvaluation");
String relativeValue = predicate.evalRelative(sourceInstance, evaluationContext, child, i).toString();
String relativeValue = predicate.evalNodeSide(sourceInstance, evaluationContext, child, i).toString();
index.add(section, relativeValue, child);
}
}
Expand Down
2 changes: 1 addition & 1 deletion src/main/java/org/javarosa/xpath/expr/XPathFuncExpr.java
Original file line number Diff line number Diff line change
Expand Up @@ -1375,7 +1375,7 @@ public boolean containsFunc(@NotNull String name) {
return name.equals(id.name) || Arrays.stream(args).anyMatch(expression -> expression.containsFunc(name));
}

private static final String[] IDEMPOTENT_FUNCTIONS = new String[]{
public static final String[] IDEMPOTENT_FUNCTIONS = new String[]{
"regex",
"starts-with",
"ends-with",
Expand Down

This file was deleted.

Loading