Skip to content

Commit 6cd5e45

Browse files
committed
GH-5286 Generalize concepts for evaluation steps and condition
1 parent 8b151d4 commit 6cd5e45

File tree

12 files changed

+329
-325
lines changed

12 files changed

+329
-325
lines changed

core/queryalgebra/evaluation/src/main/java/org/eclipse/rdf4j/query/algebra/evaluation/QueryValueEvaluationStep.java

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,12 +12,14 @@
1212
package org.eclipse.rdf4j.query.algebra.evaluation;
1313

1414
import java.util.function.Function;
15+
import java.util.function.Predicate;
1516

1617
import org.eclipse.rdf4j.model.Value;
1718
import org.eclipse.rdf4j.query.BindingSet;
1819
import org.eclipse.rdf4j.query.QueryEvaluationException;
1920
import org.eclipse.rdf4j.query.algebra.ValueConstant;
2021
import org.eclipse.rdf4j.query.algebra.ValueExpr;
22+
import org.eclipse.rdf4j.query.algebra.evaluation.util.QueryEvaluationUtility;
2123

2224
/**
2325
* A step in the query evaluation that works on ValueExpresions.
@@ -26,6 +28,18 @@ public interface QueryValueEvaluationStep {
2628
Value evaluate(BindingSet bindings)
2729
throws QueryEvaluationException;
2830

31+
default Predicate<BindingSet> asPredicate() {
32+
return bs -> {
33+
try {
34+
Value value = evaluate(bs);
35+
return QueryEvaluationUtility.getEffectiveBooleanValue(value).orElse(false);
36+
} catch (ValueExprEvaluationException e) {
37+
// Ignore, condition not evaluated successfully
38+
return false;
39+
}
40+
};
41+
}
42+
2943
/**
3044
* If an value expression results in a constant then it may be executed once per query invocation. This can reduce
3145
* computation time significantly.

core/queryalgebra/evaluation/src/main/java/org/eclipse/rdf4j/query/algebra/evaluation/impl/evaluationsteps/LeftJoinQueryEvaluationStep.java

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
import org.eclipse.rdf4j.query.algebra.evaluation.QueryEvaluationStep;
2121
import org.eclipse.rdf4j.query.algebra.evaluation.QueryValueEvaluationStep;
2222
import org.eclipse.rdf4j.query.algebra.evaluation.impl.QueryEvaluationContext;
23+
import org.eclipse.rdf4j.query.algebra.evaluation.impl.evaluationsteps.values.ScopedQueryValueEvaluationStep;
2324
import org.eclipse.rdf4j.query.algebra.evaluation.iterator.*;
2425
import org.eclipse.rdf4j.query.algebra.helpers.TupleExprs;
2526
import org.eclipse.rdf4j.query.algebra.helpers.collectors.VarNameCollector;
@@ -125,13 +126,13 @@ public static QueryEvaluationStep determineRightEvaluationStep(
125126
if (joinCondition == null) {
126127
return prepareRightArg;
127128
} else if (canEvaluateConditionBasedOnLeftHandSide(join)) {
128-
return new LeftJoinPreFilterQueryEvaluationStep(
129+
return new PreFilterQueryEvaluationStep(
129130
prepareRightArg,
130-
new ScopeBindingsJoinConditionEvaluator(join.getAssuredBindingNames(), joinCondition));
131+
new ScopedQueryValueEvaluationStep(join.getAssuredBindingNames(), joinCondition));
131132
} else {
132-
return new LeftJoinPostFilterQueryEvaluationStep(
133+
return new PostFilterQueryEvaluationStep(
133134
prepareRightArg,
134-
new ScopeBindingsJoinConditionEvaluator(scopeBindingNames, joinCondition));
135+
new ScopedQueryValueEvaluationStep(scopeBindingNames, joinCondition));
135136
}
136137
}
137138

Lines changed: 13 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,23 @@
1-
package org.eclipse.rdf4j.query.algebra.evaluation.iterator;
1+
package org.eclipse.rdf4j.query.algebra.evaluation.impl.evaluationsteps;
2+
3+
import java.util.function.Predicate;
24

35
import org.eclipse.rdf4j.common.iteration.CloseableIteration;
46
import org.eclipse.rdf4j.common.iteration.FilterIteration;
57
import org.eclipse.rdf4j.query.BindingSet;
68
import org.eclipse.rdf4j.query.algebra.evaluation.QueryEvaluationStep;
9+
import org.eclipse.rdf4j.query.algebra.evaluation.QueryValueEvaluationStep;
710

8-
public class LeftJoinPostFilterQueryEvaluationStep implements QueryEvaluationStep {
11+
public class PostFilterQueryEvaluationStep implements QueryEvaluationStep {
912

10-
private final QueryEvaluationStep wrapped;
11-
private final ScopeBindingsJoinConditionEvaluator joinConditionEvaluator;
13+
private final QueryEvaluationStep wrapped;
14+
private final Predicate<BindingSet> condition;
1215

13-
public LeftJoinPostFilterQueryEvaluationStep(QueryEvaluationStep wrapped,
14-
ScopeBindingsJoinConditionEvaluator joinConditionEvaluator) {
16+
public PostFilterQueryEvaluationStep(QueryEvaluationStep wrapped,
17+
QueryValueEvaluationStep condition) {
1518
this.wrapped = wrapped;
16-
this.joinConditionEvaluator = joinConditionEvaluator;
17-
}
19+
this.condition = condition.asPredicate();
20+
}
1821

1922
@Override
2023
public CloseableIteration<BindingSet> evaluate(BindingSet leftBindings) {
@@ -28,12 +31,12 @@ public CloseableIteration<BindingSet> evaluate(BindingSet leftBindings) {
2831

2932
@Override
3033
protected boolean accept(BindingSet bindings) {
31-
return joinConditionEvaluator.evaluate(bindings);
34+
return condition.test(bindings);
3235
}
3336

3437
@Override
3538
protected void handleClose() {
36-
rightIteration.close();
39+
// Nothing to close
3740
}
3841
};
3942
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
package org.eclipse.rdf4j.query.algebra.evaluation.impl.evaluationsteps;
2+
3+
import java.util.function.Predicate;
4+
5+
import org.eclipse.rdf4j.common.iteration.CloseableIteration;
6+
import org.eclipse.rdf4j.query.BindingSet;
7+
import org.eclipse.rdf4j.query.algebra.evaluation.QueryEvaluationStep;
8+
import org.eclipse.rdf4j.query.algebra.evaluation.QueryValueEvaluationStep;
9+
10+
public class PreFilterQueryEvaluationStep implements QueryEvaluationStep {
11+
12+
private final QueryEvaluationStep wrapped;
13+
private final Predicate<BindingSet> condition;
14+
15+
public PreFilterQueryEvaluationStep(QueryEvaluationStep wrapped,
16+
QueryValueEvaluationStep condition) {
17+
this.wrapped = wrapped;
18+
this.condition = condition.asPredicate();
19+
}
20+
21+
@Override
22+
public CloseableIteration<BindingSet> evaluate(BindingSet leftBindings) {
23+
if (!condition.test(leftBindings)) {
24+
// Usage of this method assume this instance is returned
25+
return QueryEvaluationStep.EMPTY_ITERATION;
26+
}
27+
28+
return wrapped.evaluate(leftBindings);
29+
}
30+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
package org.eclipse.rdf4j.query.algebra.evaluation.impl.evaluationsteps.values;
2+
3+
import java.util.Set;
4+
5+
import org.eclipse.rdf4j.model.Value;
6+
import org.eclipse.rdf4j.query.Binding;
7+
import org.eclipse.rdf4j.query.BindingSet;
8+
import org.eclipse.rdf4j.query.algebra.evaluation.QueryBindingSet;
9+
import org.eclipse.rdf4j.query.algebra.evaluation.QueryValueEvaluationStep;
10+
11+
public class ScopedQueryValueEvaluationStep implements QueryValueEvaluationStep {
12+
13+
/**
14+
* The set of binding names that are "in scope" for the filter. The filter must not include bindings that are (only)
15+
* included because of the depth-first evaluation strategy in the evaluation of the constraint.
16+
*/
17+
private final Set<String> scopeBindingNames;
18+
private final QueryValueEvaluationStep wrapped;
19+
20+
public ScopedQueryValueEvaluationStep(Set<String> scopeBindingNames, QueryValueEvaluationStep condition) {
21+
this.scopeBindingNames = scopeBindingNames;
22+
this.wrapped = condition;
23+
}
24+
25+
@Override
26+
public Value evaluate(BindingSet bindings) {
27+
BindingSet scopeBindings = createScopeBindings(scopeBindingNames, bindings);
28+
29+
return wrapped.evaluate(scopeBindings);
30+
}
31+
32+
private BindingSet createScopeBindings(Set<String> scopeBindingNames, BindingSet bindings) {
33+
QueryBindingSet scopeBindings = new QueryBindingSet(scopeBindingNames.size());
34+
for (String scopeBindingName : scopeBindingNames) {
35+
Binding binding = bindings.getBinding(scopeBindingName);
36+
if (binding != null) {
37+
scopeBindings.addBinding(binding);
38+
}
39+
}
40+
41+
return scopeBindings;
42+
}
43+
}

core/queryalgebra/evaluation/src/main/java/org/eclipse/rdf4j/query/algebra/evaluation/iterator/LeftJoinIterator.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@
1010
*******************************************************************************/
1111
package org.eclipse.rdf4j.query.algebra.evaluation.iterator;
1212

13+
import java.util.NoSuchElementException;
14+
1315
import org.eclipse.rdf4j.common.iteration.CloseableIteration;
1416
import org.eclipse.rdf4j.common.iteration.LookAheadIteration;
1517
import org.eclipse.rdf4j.query.BindingSet;
@@ -18,8 +20,6 @@
1820
import org.eclipse.rdf4j.query.algebra.evaluation.EvaluationStrategy;
1921
import org.eclipse.rdf4j.query.algebra.evaluation.QueryEvaluationStep;
2022

21-
import java.util.NoSuchElementException;
22-
2323
public class LeftJoinIterator extends LookAheadIteration<BindingSet> {
2424
/*-----------*
2525
* Variables *

core/queryalgebra/evaluation/src/main/java/org/eclipse/rdf4j/query/algebra/evaluation/iterator/LeftJoinPreFilterQueryEvaluationStep.java

Lines changed: 0 additions & 30 deletions
This file was deleted.

core/queryalgebra/evaluation/src/main/java/org/eclipse/rdf4j/query/algebra/evaluation/iterator/ScopeBindingsJoinConditionEvaluator.java

Lines changed: 0 additions & 54 deletions
This file was deleted.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
package org.eclipse.rdf4j.query.algebra.evaluation.impl.evaluationsteps;
2+
3+
import static org.assertj.core.api.Assertions.assertThat;
4+
5+
import java.util.LinkedList;
6+
import java.util.Queue;
7+
8+
import org.eclipse.rdf4j.common.iteration.CloseableIteration;
9+
import org.eclipse.rdf4j.model.ValueFactory;
10+
import org.eclipse.rdf4j.model.impl.BooleanLiteral;
11+
import org.eclipse.rdf4j.model.impl.SimpleValueFactory;
12+
import org.eclipse.rdf4j.query.BindingSet;
13+
import org.eclipse.rdf4j.query.algebra.evaluation.QueryBindingSet;
14+
import org.eclipse.rdf4j.query.algebra.evaluation.QueryEvaluationStep;
15+
import org.eclipse.rdf4j.query.algebra.evaluation.QueryValueEvaluationStep;
16+
import org.junit.jupiter.api.BeforeEach;
17+
import org.junit.jupiter.api.DisplayName;
18+
import org.junit.jupiter.api.Test;
19+
20+
class PostFilterQueryEvaluationStepTest {
21+
22+
private static final ValueFactory VALUE_FACTORY = SimpleValueFactory.getInstance();
23+
24+
private QueryBindingSet bindingSet;
25+
26+
@BeforeEach
27+
void setUp() {
28+
bindingSet = new QueryBindingSet(1);
29+
bindingSet.addBinding("a", VALUE_FACTORY.createLiteral(42));
30+
}
31+
32+
@Test
33+
@DisplayName("when wrapped returns empty iteration, then LeftJoinPostFilterQueryEvaluationStep returns empty iteration")
34+
void emptyIteration() {
35+
QueryEvaluationStep wrapped = (bindings) -> QueryEvaluationStep.EMPTY_ITERATION;
36+
QueryEvaluationStep postFilter = new PostFilterQueryEvaluationStep(
37+
wrapped,
38+
bindings -> BooleanLiteral.valueOf(true));
39+
40+
var result = postFilter.evaluate(bindingSet);
41+
42+
assertThat(result).isEqualTo(QueryEvaluationStep.EMPTY_ITERATION);
43+
}
44+
45+
@Test
46+
@DisplayName("when wrapped returns non-empty iteration, then LeftJoinPostFilterQueryEvaluationStep returns filtered iteration")
47+
void filteredIteration() {
48+
QueryEvaluationStep wrapped = new TestQueryEvaluationStep();
49+
QueryValueEvaluationStep condition = bindings -> {
50+
var shouldAccept = bindings.getValue("b")
51+
.stringValue()
52+
.equals("abc");
53+
return BooleanLiteral.valueOf(shouldAccept);
54+
};
55+
QueryEvaluationStep postFilter = new PostFilterQueryEvaluationStep(wrapped, condition);
56+
57+
var result = postFilter.evaluate(bindingSet);
58+
59+
assertThat(result)
60+
.toIterable()
61+
.map(bindings -> bindings.getValue("b"))
62+
.containsExactly(VALUE_FACTORY.createLiteral("abc"));
63+
}
64+
65+
private static class TestQueryEvaluationStep implements QueryEvaluationStep {
66+
67+
private final Queue<String> values = new LinkedList<>();
68+
69+
private TestQueryEvaluationStep() {
70+
values.add("abc");
71+
values.add("xyz");
72+
}
73+
74+
@Override
75+
public CloseableIteration<BindingSet> evaluate(BindingSet bindings) {
76+
return new CloseableIteration<>() {
77+
@Override
78+
public void close() {
79+
// Nothing to close
80+
}
81+
82+
@Override
83+
public boolean hasNext() {
84+
return !values.isEmpty();
85+
}
86+
87+
@Override
88+
public BindingSet next() {
89+
var output = new QueryBindingSet(2);
90+
bindings.forEach(output::addBinding);
91+
output.addBinding("b", VALUE_FACTORY.createLiteral(values.poll()));
92+
return output;
93+
}
94+
};
95+
}
96+
}
97+
}

0 commit comments

Comments
 (0)