1010 *******************************************************************************/
1111package org .eclipse .rdf4j .query .algebra .evaluation .iterator ;
1212
13- import java .util .NoSuchElementException ;
14- import java .util .Set ;
15-
1613import org .eclipse .rdf4j .common .iteration .CloseableIteration ;
1714import org .eclipse .rdf4j .common .iteration .LookAheadIteration ;
18- import org .eclipse .rdf4j .model .Value ;
19- import org .eclipse .rdf4j .query .Binding ;
2015import org .eclipse .rdf4j .query .BindingSet ;
2116import org .eclipse .rdf4j .query .QueryEvaluationException ;
2217import org .eclipse .rdf4j .query .algebra .LeftJoin ;
23- import org .eclipse .rdf4j .query .algebra .ValueExpr ;
24- import org .eclipse .rdf4j .query .algebra .evaluation .EvaluationStrategy ;
25- import org .eclipse .rdf4j .query .algebra .evaluation .QueryBindingSet ;
26- import org .eclipse .rdf4j .query .algebra .evaluation .QueryEvaluationStep ;
27- import org .eclipse .rdf4j .query .algebra .evaluation .QueryValueEvaluationStep ;
28- import org .eclipse .rdf4j .query .algebra .evaluation .ValueExprEvaluationException ;
18+ import org .eclipse .rdf4j .query .algebra .evaluation .*;
2919import org .eclipse .rdf4j .query .algebra .evaluation .impl .QueryEvaluationContext ;
30- import org .eclipse .rdf4j .query .algebra .evaluation .util .QueryEvaluationUtility ;
3120import org .eclipse .rdf4j .query .algebra .helpers .collectors .VarNameCollector ;
3221
22+ import java .util .NoSuchElementException ;
23+ import java .util .Optional ;
24+ import java .util .Set ;
25+
3326public class LeftJoinIterator extends LookAheadIteration <BindingSet > {
3427 /*-----------*
3528 * Variables *
3629 *-----------*/
3730
38- /**
39- * The set of binding names that are "in scope" for the filter. The filter must not include bindings that are (only)
40- * included because of the depth-first evaluation strategy in the evaluation of the constraint.
41- */
42- private final Set <String > scopeBindingNames ;
43-
4431 private final CloseableIteration <BindingSet > leftIter ;
45- private final LeftJoin leftJoin ;
46- private final boolean canEvaluateConditionBasedOnLeftHandSide ;
32+ private final QueryEvaluationStep rightEvaluationStep ;
4733
4834 private CloseableIteration <BindingSet > rightIter ;
4935
50- private final QueryEvaluationStep prepareRightArg ;
51-
52- private final QueryValueEvaluationStep joinCondition ;
53-
5436 /*--------------*
5537 * Constructors *
5638 *--------------*/
5739
58- public LeftJoinIterator (EvaluationStrategy strategy , LeftJoin join , BindingSet bindings ,
59- QueryEvaluationContext context )
60- throws QueryEvaluationException {
61- this .scopeBindingNames = join .getBindingNames ();
62- this .leftJoin = join ;
40+ public LeftJoinIterator (
41+ EvaluationStrategy strategy ,
42+ LeftJoin join ,
43+ BindingSet bindings ,
44+ QueryEvaluationContext context ) throws QueryEvaluationException {
45+ Set <String > scopeBindingNames = join .getBindingNames ();
6346
6447 leftIter = strategy .evaluate (join .getLeftArg (), bindings );
6548
6649 rightIter = null ;
6750
68- prepareRightArg = strategy .precompile (join .getRightArg (), context );
51+ QueryEvaluationStep prepareRightArg = strategy .precompile (join .getRightArg (), context );
6952 join .setAlgorithm (this );
70- final ValueExpr condition = join .getCondition ();
71- if (condition == null ) {
72- joinCondition = null ;
73- } else {
74- joinCondition = strategy .precompile (condition , context );
75- }
76- this .canEvaluateConditionBasedOnLeftHandSide = canEvaluateConditionBasedOnLeftHandSide (leftJoin );
53+ var joinCondition = Optional .ofNullable (join .getCondition ())
54+ .map (condition -> strategy .precompile (condition , context ));
55+
56+ rightEvaluationStep = determineRightEvaluationStep (
57+ join ,
58+ prepareRightArg ,
59+ joinCondition .orElse (null ),
60+ scopeBindingNames );
7761 }
7862
79- public LeftJoinIterator (QueryEvaluationStep left , QueryEvaluationStep right , QueryValueEvaluationStep joinCondition ,
80- BindingSet bindings , Set <String > scopeBindingNames , LeftJoin leftJoin )
81- throws QueryEvaluationException {
82- this .scopeBindingNames = scopeBindingNames ;
83- this .leftJoin = leftJoin ;
84-
85- leftIter = left .evaluate (bindings );
86-
87- // Initialize with empty iteration so that var is never null
88- rightIter = null ;
89-
90- prepareRightArg = right ;
91- this .joinCondition = joinCondition ;
92- this .canEvaluateConditionBasedOnLeftHandSide = canEvaluateConditionBasedOnLeftHandSide (leftJoin );
93-
63+ public LeftJoinIterator (
64+ QueryEvaluationStep left ,
65+ QueryEvaluationStep right ,
66+ QueryValueEvaluationStep joinCondition ,
67+ BindingSet bindings ,
68+ Set <String > scopeBindingNames ,
69+ LeftJoin leftJoin ) throws QueryEvaluationException {
70+ this (
71+ left .evaluate (bindings ),
72+ determineRightEvaluationStep (leftJoin , right , joinCondition , scopeBindingNames ));
9473 }
9574
96- public LeftJoinIterator (CloseableIteration <BindingSet > leftIter ,
97- QueryEvaluationStep prepareRightArg ,
98- QueryValueEvaluationStep joinCondition ,
99- Set <String > scopeBindingNames ,
100- LeftJoin leftJoin ) {
101- this .scopeBindingNames = scopeBindingNames ;
75+ public LeftJoinIterator (CloseableIteration <BindingSet > leftIter , QueryEvaluationStep rightEvaluationStep ) {
10276 this .leftIter = leftIter ;
103- this .leftJoin = leftJoin ;
10477 this .rightIter = null ;
105- this .prepareRightArg = prepareRightArg ;
106- this .joinCondition = joinCondition ;
107- this .canEvaluateConditionBasedOnLeftHandSide = canEvaluateConditionBasedOnLeftHandSide (leftJoin );
78+ this .rightEvaluationStep = rightEvaluationStep ;
10879 }
10980
110- public static CloseableIteration <BindingSet > getInstance (QueryEvaluationStep left ,
111- QueryEvaluationStep prepareRightArg ,
112- QueryValueEvaluationStep joinCondition ,
113- BindingSet bindings ,
114- Set <String > scopeBindingNames ,
115- LeftJoin leftJoin ) {
81+ public static CloseableIteration <BindingSet > getInstance (
82+ QueryEvaluationStep left ,
83+ QueryEvaluationStep prepareRightArg ,
84+ QueryValueEvaluationStep joinCondition ,
85+ BindingSet bindings ,
86+ Set <String > scopeBindingNames ,
87+ LeftJoin leftJoin ) {
11688
11789 CloseableIteration <BindingSet > leftIter = left .evaluate (bindings );
90+ var rightEvaluationStep = determineRightEvaluationStep (
91+ leftJoin ,
92+ prepareRightArg ,
93+ joinCondition ,
94+ scopeBindingNames );
11895
11996 if (leftIter == QueryEvaluationStep .EMPTY_ITERATION ) {
12097 return leftIter ;
12198 } else {
122- return new LeftJoinIterator (leftIter , prepareRightArg , joinCondition , scopeBindingNames , leftJoin );
99+ return new LeftJoinIterator (leftIter , rightEvaluationStep );
123100 }
124-
125101 }
126102
127103 /*---------*
@@ -140,57 +116,25 @@ protected BindingSet getNextElement() throws QueryEvaluationException {
140116 if (leftIter .hasNext ()) {
141117 // Use left arg's bindings in case join fails
142118 leftBindings = leftIter .next ();
143- if (shouldEvaluateRightHandSide (leftBindings )) {
144- nextRightIter = rightIter = prepareRightArg .evaluate (leftBindings );
145- } else {
146- return leftBindings ;
147- }
119+ nextRightIter = rightIter = rightEvaluationStep .evaluate (leftBindings );
148120 } else {
149121 return null ;
150122 }
151-
152123 } else if (!nextRightIter .hasNext ()) {
153124 // Use left arg's bindings in case join fails
154125 leftBindings = leftIter .next ();
155126
156127 nextRightIter .close ();
157- if (shouldEvaluateRightHandSide (leftBindings )) {
158- nextRightIter = rightIter = prepareRightArg .evaluate (leftBindings );
159- } else {
160- return leftBindings ;
161- }
128+ nextRightIter = rightIter = rightEvaluationStep .evaluate (leftBindings );
162129 }
163130
164131 if (nextRightIter == QueryEvaluationStep .EMPTY_ITERATION ) {
165132 rightIter = null ;
166133 return leftBindings ;
167134 }
168135
169- while (nextRightIter .hasNext ()) {
170- BindingSet rightBindings = nextRightIter .next ();
171-
172- try {
173- if (joinCondition == null || canEvaluateConditionBasedOnLeftHandSide ) {
174- return rightBindings ;
175- } else {
176- // Limit the bindings to the ones that are in scope for
177- // this filter
178-
179- QueryBindingSet scopeBindings = new QueryBindingSet (scopeBindingNames .size ());
180- for (String scopeBindingName : scopeBindingNames ) {
181- Binding binding = rightBindings .getBinding (scopeBindingName );
182- if (binding != null ) {
183- scopeBindings .addBinding (binding );
184- }
185- }
186-
187- if (isTrue (joinCondition , scopeBindings )) {
188- return rightBindings ;
189- }
190- }
191- } catch (ValueExprEvaluationException e ) {
192- // Ignore, condition not evaluated successfully
193- }
136+ if (nextRightIter .hasNext ()) {
137+ return nextRightIter .next ();
194138 }
195139
196140 if (leftBindings != null ) {
@@ -207,39 +151,6 @@ protected BindingSet getNextElement() throws QueryEvaluationException {
207151 return null ;
208152 }
209153
210- private static boolean canEvaluateConditionBasedOnLeftHandSide (LeftJoin leftJoin ) {
211- if (leftJoin .hasCondition ()) {
212- var collector = new VarNameCollector ();
213- leftJoin .getCondition ().visit (collector );
214-
215- Set <String > assuredBindingNames = leftJoin .getAssuredBindingNames ();
216- return assuredBindingNames .containsAll (collector .getVarNames ());
217- }
218-
219- return false ;
220- }
221-
222- private boolean shouldEvaluateRightHandSide (BindingSet leftBindings ) {
223- if (!canEvaluateConditionBasedOnLeftHandSide ) {
224- return true ;
225- }
226-
227- QueryBindingSet scopeBindings = new QueryBindingSet (leftJoin .getAssuredBindingNames ().size ());
228- for (String scopeBindingName : leftJoin .getAssuredBindingNames ()) {
229- Binding binding = leftBindings .getBinding (scopeBindingName );
230- if (binding != null ) {
231- scopeBindings .addBinding (binding );
232- }
233- }
234-
235- return isTrue (joinCondition , scopeBindings );
236- }
237-
238- private boolean isTrue (QueryValueEvaluationStep expr , QueryBindingSet bindings ) {
239- Value value = expr .evaluate (bindings );
240- return QueryEvaluationUtility .getEffectiveBooleanValue (value ).orElse (false );
241- }
242-
243154 @ Override
244155 protected void handleClose () throws QueryEvaluationException {
245156 try {
@@ -250,4 +161,31 @@ protected void handleClose() throws QueryEvaluationException {
250161 }
251162 }
252163 }
164+
165+ static QueryEvaluationStep determineRightEvaluationStep (
166+ LeftJoin join ,
167+ QueryEvaluationStep prepareRightArg ,
168+ QueryValueEvaluationStep joinCondition ,
169+ Set <String > scopeBindingNames ) {
170+ if (joinCondition == null ) {
171+ return prepareRightArg ;
172+ } else if (canEvaluateConditionBasedOnLeftHandSide (join )) {
173+ return new LeftJoinPreFilterQueryEvaluationStep (
174+ prepareRightArg ,
175+ new ScopeBindingsJoinConditionEvaluator (join .getAssuredBindingNames (), joinCondition ));
176+ } else {
177+ return new LeftJoinPostFilterQueryEvaluationStep (
178+ prepareRightArg ,
179+ new ScopeBindingsJoinConditionEvaluator (scopeBindingNames , joinCondition ));
180+ }
181+ }
182+
183+ private static boolean canEvaluateConditionBasedOnLeftHandSide (LeftJoin leftJoin ) {
184+ if (!leftJoin .hasCondition ()) {
185+ return false ;
186+ }
187+
188+ var varNames = VarNameCollector .process (leftJoin .getCondition ());
189+ return leftJoin .getAssuredBindingNames ().containsAll (varNames );
190+ }
253191}
0 commit comments