@@ -192,8 +192,6 @@ public void testBoundLeftJoin_stmt_nonExclusive_boundCheck(boolean bindLeftJoinO
192192 conn .add (Values .iri ("http://other.com/p30" ), FOAF .GENDER , Values .literal ("male" ));
193193 }
194194
195- fedxRule .enableDebug ();
196-
197195 try {
198196 // run query which joins results from multiple repos
199197 // for a subset of persons there exist names
@@ -246,4 +244,99 @@ public void testBoundLeftJoin_stmt_nonExclusive_boundCheck(boolean bindLeftJoinO
246244 }
247245 }
248246
247+ @ ParameterizedTest
248+ @ ValueSource (booleans = { true , false })
249+ public void test_leftBindJoin_emptyOptional (boolean bindLeftJoinOptimizationEnabled ) throws Exception {
250+
251+ prepareTest (
252+ Arrays .asList ("/tests/basic/data_emptyStore.ttl" , "/tests/basic/data_emptyStore.ttl" ,
253+ "/tests/basic/data_emptyStore.ttl" ));
254+
255+ Repository repo1 = getRepository (1 );
256+ Repository repo2 = getRepository (2 );
257+ Repository repo3 = getRepository (3 );
258+
259+ Repository fedxRepo = fedxRule .getRepository ();
260+
261+ fedxRule .setConfig (config -> {
262+ config .withBoundJoinBlockSize (10 );
263+ config .withEnableOptionalAsBindJoin (bindLeftJoinOptimizationEnabled );
264+ });
265+
266+ // add some persons
267+ try (RepositoryConnection conn = repo1 .getConnection ()) {
268+
269+ for (int i = 1 ; i <= 30 ; i ++) {
270+ var p = Values .iri ("http://ex.com/p" + i );
271+ var otherP = Values .iri ("http://other.com/p" + i );
272+ conn .add (p , OWL .SAMEAS , otherP );
273+ }
274+ }
275+
276+ // add names for person 1, 4, 7, ...
277+ try (RepositoryConnection conn = repo2 .getConnection ()) {
278+
279+ for (int i = 1 ; i <= 30 ; i += 3 ) {
280+ var otherP = Values .iri ("http://other.com/p" + i );
281+ conn .add (otherP , FOAF .NAME , Values .literal ("Person " + i ));
282+ }
283+ }
284+
285+ // add names for person 2, 5, 8, ...
286+ try (RepositoryConnection conn = repo3 .getConnection ()) {
287+
288+ for (int i = 2 ; i <= 30 ; i += 3 ) {
289+ var otherP = Values .iri ("http://other.com/p" + i );
290+ conn .add (otherP , FOAF .NAME , Values .literal ("Person " + i ));
291+ }
292+ }
293+
294+ try {
295+ // run query which joins results from multiple repos
296+ // for a subset of persons there exist names
297+ // the age does not exist for any person
298+ try (RepositoryConnection conn = fedxRepo .getConnection ()) {
299+ String query = "PREFIX foaf: <http://xmlns.com/foaf/0.1/> " +
300+ "SELECT * WHERE { "
301+ + " ?person owl:sameAs ?otherPerson . "
302+ + " OPTIONAL { ?otherPerson foaf:name ?name . } " // # @repo2 and @repo3
303+ + " OPTIONAL { ?otherPerson foaf:age ?age . } " // # does not exist
304+ + "}" ;
305+
306+ TupleQuery tupleQuery = conn .prepareTupleQuery (query );
307+ try (TupleQueryResult tqr = tupleQuery .evaluate ()) {
308+ var bindings = Iterations .asList (tqr );
309+
310+ Assertions .assertEquals (30 , bindings .size ());
311+
312+ for (int i = 1 ; i <= 30 ; i ++) {
313+ var p = Values .iri ("http://ex.com/p" + i );
314+ var otherP = Values .iri ("http://other.com/p" + i );
315+
316+ // find the bindingset for the person in the unordered result
317+ BindingSet bs = bindings .stream ()
318+ .filter (b -> b .getValue ("person" ).equals (p ))
319+ .findFirst ()
320+ .orElseThrow ();
321+
322+ Assertions .assertEquals (otherP , bs .getValue ("otherPerson" ));
323+ if (i % 3 == 1 || i % 3 == 2 ) {
324+ // names from repo 2 or 3
325+ Assertions .assertEquals ("Person " + i , bs .getValue ("name" ).stringValue ());
326+ } else {
327+ // no name for others
328+ Assertions .assertFalse (bs .hasBinding ("name" ));
329+ }
330+
331+ Assertions .assertEquals (otherP , bs .getValue ("otherPerson" ));
332+ Assertions .assertFalse (bs .hasBinding ("age" ));
333+ }
334+ }
335+ }
336+
337+ } finally {
338+ fedxRepo .shutDown ();
339+ }
340+ }
341+
249342}
0 commit comments