Skip to content

Commit 6413b02

Browse files
authored
GH-5030: fix FedXDataset support for queries with FROM clauses (#5031)
2 parents 516bf0a + 208459f commit 6413b02

3 files changed

Lines changed: 120 additions & 12 deletions

File tree

core/query/src/main/java/org/eclipse/rdf4j/query/impl/FallbackDataset.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,4 +113,12 @@ private void appendURI(StringBuilder sb, IRI uri) {
113113
}
114114
}
115115

116+
public Dataset getPrimary() {
117+
return primary;
118+
}
119+
120+
public Dataset getFallback() {
121+
return fallback;
122+
}
123+
116124
}

tools/federation/src/main/java/org/eclipse/rdf4j/federated/evaluation/FederationEvalStrategy.java

Lines changed: 30 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,7 @@
121121
import org.eclipse.rdf4j.query.algebra.helpers.TupleExprs;
122122
import org.eclipse.rdf4j.query.algebra.helpers.collectors.VarNameCollector;
123123
import org.eclipse.rdf4j.query.impl.EmptyBindingSet;
124+
import org.eclipse.rdf4j.query.impl.FallbackDataset;
124125
import org.eclipse.rdf4j.repository.RepositoryException;
125126
import org.eclipse.rdf4j.repository.sparql.federation.CollectionIteration;
126127
import org.eclipse.rdf4j.repository.sparql.federation.RepositoryFederatedService;
@@ -179,19 +180,8 @@ public TupleExpr optimize(TupleExpr expr, EvaluationStatistics evaluationStatist
179180

180181
FederationEvaluationStatistics stats = (FederationEvaluationStatistics) evaluationStatistics;
181182
QueryInfo queryInfo = stats.getQueryInfo();
182-
Dataset dataset = stats.getDataset();
183183

184-
FederationContext federationContext = queryInfo.getFederationContext();
185-
List<Endpoint> members;
186-
if (dataset instanceof FedXDataset) {
187-
// run the query against a selected set of endpoints
188-
FedXDataset ds = (FedXDataset) dataset;
189-
members = federationContext.getEndpointManager().getEndpoints(ds.getEndpoints());
190-
} else {
191-
// evaluate against entire federation
192-
FedX fed = federationContext.getFederation();
193-
members = fed.getMembers();
194-
}
184+
List<Endpoint> members = getMembersFromContext(stats);
195185

196186
// Clone the tuple expression to allow for more aggressive optimizations
197187
TupleExpr clone = expr.clone();
@@ -270,6 +260,34 @@ public TupleExpr optimize(TupleExpr expr, EvaluationStatistics evaluationStatist
270260
return query;
271261
}
272262

263+
/**
264+
* Returns the federation members that are active in the current federation. By default it is all federation
265+
* members. If the passed {@link Dataset} is a {@link FedXDataset}, the defined {@link FedXDataset#getEndpoints()}
266+
* are used.
267+
*
268+
* @param evaluationStatisticss to keep the current query context
269+
* @return
270+
*/
271+
protected List<Endpoint> getMembersFromContext(FederationEvaluationStatistics evaluationStatisticss) {
272+
Dataset queryDataset = evaluationStatisticss.getDataset();
273+
274+
// if the query uses a FROM clause, the external dataset
275+
// is wrapped as primary dataset in FallbackDataset
276+
if (queryDataset instanceof FallbackDataset) {
277+
queryDataset = ((FallbackDataset) queryDataset).getPrimary();
278+
}
279+
280+
if (queryDataset instanceof FedXDataset) {
281+
// run the query against a selected set of endpoints
282+
FedXDataset ds = (FedXDataset) queryDataset;
283+
return federationContext.getEndpointManager().getEndpoints(ds.getEndpoints());
284+
} else {
285+
// evaluate against entire federation
286+
FedX fed = federationContext.getFederation();
287+
return fed.getMembers();
288+
}
289+
}
290+
273291
/**
274292
* Perform source selection for all statements of the query. As a result of this method all statement nodes are
275293
* annotated with their relevant sources.

tools/federation/src/test/java/org/eclipse/rdf4j/federated/BasicTests.java

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@
2424
import org.eclipse.rdf4j.model.Statement;
2525
import org.eclipse.rdf4j.model.util.Values;
2626
import org.eclipse.rdf4j.model.vocabulary.DCTERMS;
27+
import org.eclipse.rdf4j.model.vocabulary.FOAF;
28+
import org.eclipse.rdf4j.model.vocabulary.RDF;
2729
import org.eclipse.rdf4j.model.vocabulary.RDFS;
2830
import org.eclipse.rdf4j.query.AbstractTupleQueryResultHandler;
2931
import org.eclipse.rdf4j.query.BindingSet;
@@ -35,6 +37,7 @@
3537
import org.eclipse.rdf4j.query.TupleQueryResult;
3638
import org.eclipse.rdf4j.query.TupleQueryResultHandlerException;
3739
import org.eclipse.rdf4j.query.impl.SimpleDataset;
40+
import org.eclipse.rdf4j.repository.Repository;
3841
import org.eclipse.rdf4j.repository.RepositoryConnection;
3942
import org.eclipse.rdf4j.repository.util.Repositories;
4043
import org.junit.jupiter.api.Assertions;
@@ -575,4 +578,83 @@ public void test_EscapingQuotedLiteral() throws Exception {
575578
}
576579

577580
}
581+
582+
@Test
583+
public void test_reduceFederation() throws Exception {
584+
585+
List<Endpoint> endpoints = prepareTest(
586+
Arrays.asList("/tests/basic/data_emptyStore.ttl", "/tests/basic/data_emptyStore.ttl"));
587+
588+
Repository repo1 = getRepository(1);
589+
Repository repo2 = getRepository(2);
590+
591+
String repo1Id = endpoints.get(0).getId();
592+
593+
IRI graph1 = Values.iri("http://example.org/graph1");
594+
IRI graph2 = Values.iri("http://example.org/graph2");
595+
596+
try (RepositoryConnection con = repo1.getConnection()) {
597+
con.add(Values.iri("http://example.org/repo1/p1"), RDF.TYPE, FOAF.PERSON, graph1);
598+
con.add(Values.iri("http://example.org/repo2/p2"), RDF.TYPE, FOAF.PERSON, graph2);
599+
}
600+
601+
try (RepositoryConnection con = repo2.getConnection()) {
602+
con.add(Values.iri("http://example.org/repo2/p3"), RDF.TYPE, FOAF.PERSON, graph1);
603+
}
604+
605+
Repository fedxRepo = fedxRule.getRepository();
606+
607+
// 1: regular federation
608+
try (RepositoryConnection con = fedxRepo.getConnection()) {
609+
TupleQuery tupleQuery = con.prepareTupleQuery(
610+
"PREFIX foaf: <http://xmlns.com/foaf/0.1/> "
611+
+ "SELECT * WHERE { "
612+
+ " ?subClass a foaf:Person. "
613+
+ " } "
614+
);
615+
616+
// expect from both repos
617+
Assertions.assertEquals(3, QueryResults.asSet(tupleQuery.evaluate()).size());
618+
619+
// now we scope it additional to graph1
620+
tupleQuery = con.prepareTupleQuery(
621+
"PREFIX foaf: <http://xmlns.com/foaf/0.1/> "
622+
+ "SELECT * FROM <http://example.org/graph1> WHERE { "
623+
+ " ?subClass a foaf:Person. "
624+
+ " } ");
625+
626+
// expect results defined in graph1 (1 in repo1, 2 from repo2)
627+
Assertions.assertEquals(2, QueryResults.asSet(tupleQuery.evaluate()).size());
628+
}
629+
630+
// 2: reduce to federation member 1 id
631+
// 2a: additionall restrict to named graph
632+
FedXDataset fedXDataset = new FedXDataset(new SimpleDataset());
633+
fedXDataset.addEndpoint(repo1Id);
634+
635+
try (RepositoryConnection con = fedxRepo.getConnection()) {
636+
TupleQuery tupleQuery = con.prepareTupleQuery(
637+
"PREFIX foaf: <http://xmlns.com/foaf/0.1/> "
638+
+ "SELECT * WHERE { "
639+
+ " ?subClass a foaf:Person. "
640+
+ " } "
641+
);
642+
tupleQuery.setDataset(fedXDataset);
643+
644+
// expect result from repo 1
645+
Assertions.assertEquals(2, QueryResults.asSet(tupleQuery.evaluate()).size());
646+
647+
// now we scope it additional to graph1
648+
tupleQuery = con.prepareTupleQuery(
649+
"PREFIX foaf: <http://xmlns.com/foaf/0.1/> "
650+
+ "SELECT * FROM <http://example.org/graph1> WHERE { "
651+
+ " ?subClass a foaf:Person. "
652+
+ " } ");
653+
tupleQuery.setDataset(fedXDataset);
654+
655+
// expect result from graph1 from repo1
656+
Assertions.assertEquals(1, QueryResults.asSet(tupleQuery.evaluate()).size());
657+
}
658+
659+
}
578660
}

0 commit comments

Comments
 (0)