handling similar bnodes
diff --git a/impl.sparql/src/main/java/org/apache/commons/rdf/impl/sparql/SparqlGraph.java b/impl.sparql/src/main/java/org/apache/commons/rdf/impl/sparql/SparqlGraph.java
index 64b6133..720f7df 100644
--- a/impl.sparql/src/main/java/org/apache/commons/rdf/impl/sparql/SparqlGraph.java
+++ b/impl.sparql/src/main/java/org/apache/commons/rdf/impl/sparql/SparqlGraph.java
@@ -30,12 +30,15 @@
import java.util.logging.Logger;
import org.apache.commons.rdf.BlankNode;
import org.apache.commons.rdf.BlankNodeOrIri;
+import org.apache.commons.rdf.Graph;
+import org.apache.commons.rdf.ImmutableGraph;
import org.apache.commons.rdf.Iri;
import org.apache.commons.rdf.Literal;
import org.apache.commons.rdf.RdfTerm;
import org.apache.commons.rdf.Triple;
import org.apache.commons.rdf.impl.utils.AbstractGraph;
import org.apache.commons.rdf.impl.utils.TripleImpl;
+import org.apache.commons.rdf.impl.utils.simple.SimpleGraph;
/**
*
@@ -47,6 +50,7 @@
private static final Logger log = Logger.getLogger(SparqlGraph.class.getName());
final SparqlClient sparqlClient;
+
/**
* Constructs a Graph representing the default graph at the specified
@@ -82,7 +86,7 @@
queryBuilder.append(" }");
final List<Map<String, RdfTerm>> sparqlResults = sparqlClient.queryResultSet(queryBuilder.toString());
//first to triples without bnode-conversion
- //rawTripkles contains the triples with the BNodes from the result set
+ //rawTriples contains the triples with the BNodes from the result set
final Collection<Triple> rawTriples = new ArrayList<>();
for (Map<String, RdfTerm> result : sparqlResults) {
rawTriples.add(new TripleImpl(filterSubject != null ? filterSubject : (BlankNodeOrIri) result.get("s"),
@@ -96,6 +100,7 @@
return (new Callable<Iterator<Triple>>() {
final Map<BlankNode, SparqlBNode> nodeMap = new HashMap<>();
+ final Set<ImmutableGraph> usedContext = new HashSet<>();
private RdfTerm useSparqlNode(RdfTerm node) throws IOException {
if (node instanceof BlankNodeOrIri) {
@@ -149,7 +154,7 @@
}
}
- private Collection<Triple> getContext(final BlankNode node) throws IOException {
+ private ImmutableGraph getContext(final BlankNode node) throws IOException {
//we need to get the cntext of the BNode
//if the filter was for (null, null, null) we have the whole
//bnode context in the reuslt set, otherwise we need to get
@@ -159,30 +164,41 @@
&& (filterObject == null)) {
return getContextInRaw(node);
} else {
- final Collection<Triple> startContext = getContextInRaw(node);
- final Set<Collection<Triple>> expandedContexts = expandContext(startContext);
+ final ImmutableGraph startContext = getContextInRaw(node);
+ final Set<ImmutableGraph> expandedContexts = expandContext(startContext);
//expand bnode context
//note that there might be different contexts for
//a bnode as present in the current result set
//in this case we just haveto make sure we don't
//pick the same context for different bnodes in the resultset
-
- //TODO make sure we don't take one that has already been used
- return expandedContexts.iterator().next();
+ ImmutableGraph result = null;
+ for (ImmutableGraph expandedContext : expandedContexts) {
+ if (!usedContext.contains(expandedContext)) {
+ result = expandedContext;
+ break;
+ }
+ }
+ if (result == null) {
+ log.warning("he underlying sparql graph seems to contain redundant triples, this might cause unexpected results");
+ result = expandedContexts.iterator().next();
+ } else {
+ usedContext.add(result);
+ }
+ return result;
}
}
- private Collection<Triple> getContextInRaw(BlankNode node) {
- final Collection<Triple> context = new ArrayList<>();
+ private ImmutableGraph getContextInRaw(BlankNode node) {
+ final Graph contextBuilder = new SimpleGraph();
for (Triple rawTriple : rawTriples) {
BlankNodeOrIri rawSubject = rawTriple.getSubject();
RdfTerm rawObject = rawTriple.getObject();
if (rawSubject.equals(node) || rawObject.equals(node)) {
- context.add(rawTriple);
+ contextBuilder.add(rawTriple);
}
}
- return context;
+ return contextBuilder.getImmutableGraph();
}
@Override
@@ -214,7 +230,7 @@
* @param startContext
* @return
*/
- private Set<Collection<Triple>> expandContext(Collection<Triple> startContext) throws IOException {
+ private Set<ImmutableGraph> expandContext(Collection<Triple> startContext) throws IOException {
final Collection<String> triplePatterns = new ArrayList<>();
int varCounter = 0;
final Map<BlankNode, String> bNodeVarNameMap = new HashMap<>();
@@ -292,7 +308,7 @@
}
queryBuilder.append(" }");
final List<Map<String, RdfTerm>> expansionQueryResults = sparqlClient.queryResultSet(queryBuilder.toString());
- Set<Collection<Triple>> expandedContexts = new HashSet<>();
+ Set<ImmutableGraph> expandedContexts = new HashSet<>();
//the query results may or may be from disjoint supergraphs
//we expand them all as if they are different which may lead
//us to the same MSG multiple times
@@ -362,7 +378,7 @@
}
if (expandedContexts.isEmpty()) {
- expandedContexts.add(startContext);
+ expandedContexts.add(new SimpleGraph(startContext).getImmutableGraph());
}
return expandedContexts;
}
diff --git a/impl.sparql/src/test/java/org/apache/commons/rdf/impl/sparql/SimilarBNodes.java b/impl.sparql/src/test/java/org/apache/commons/rdf/impl/sparql/SimilarBNodes.java
new file mode 100644
index 0000000..1f52c4b
--- /dev/null
+++ b/impl.sparql/src/test/java/org/apache/commons/rdf/impl/sparql/SimilarBNodes.java
@@ -0,0 +1,98 @@
+package org.apache.commons.rdf.impl.sparql;
+
+/*
+ * To change this license header, choose License Headers in Project Properties.
+ * To change this template file, choose Tools | Templates
+ * and open the template in the editor.
+ */
+
+
+import org.apache.commons.rdf.impl.sparql.*;
+import com.hp.hpl.jena.query.DatasetAccessor;
+import com.hp.hpl.jena.query.DatasetAccessorFactory;
+import java.io.IOException;
+import java.net.ServerSocket;
+import org.apache.jena.fuseki.EmbeddedFusekiServer;
+import com.hp.hpl.jena.rdf.model.Model;
+import com.hp.hpl.jena.rdf.model.ModelFactory;
+import java.io.InputStream;
+import java.util.Iterator;
+import org.apache.commons.rdf.BlankNode;
+import org.apache.commons.rdf.BlankNodeOrIri;
+import org.apache.commons.rdf.Graph;
+import org.apache.commons.rdf.Iri;
+import org.apache.commons.rdf.RdfTerm;
+import org.apache.commons.rdf.Triple;
+import org.junit.AfterClass;
+import org.junit.Assert;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+/**
+ *
+ * @author reto
+ */
+public class SimilarBNodes {
+
+ final static int serverPort = findFreePort();
+ static EmbeddedFusekiServer server;
+
+ @BeforeClass
+ public static void prepare() throws IOException {
+ final String serviceURI = "http://localhost:" + serverPort + "/ds/data";
+ final DatasetAccessor accessor = DatasetAccessorFactory.createHTTP(serviceURI);
+ final InputStream in = SimilarBNodes.class.getResourceAsStream("similar-bnodes.ttl");
+ final Model m = ModelFactory.createDefaultModel();
+ String base = "http://example.org/";
+ m.read(in, base, "TURTLE");
+ server = EmbeddedFusekiServer.memTDB(serverPort, "/ds");//dataSet.getAbsolutePath());
+ server.start();
+ System.out.println("Started fuseki on port " + serverPort);
+ accessor.putModel(m);
+ }
+
+ @AfterClass
+ public static void cleanup() {
+ server.stop();
+ }
+
+ @Test
+ public void graphSize() {
+ final Graph graph = new SparqlGraph("http://localhost:" + serverPort + "/ds/query");
+ Assert.assertEquals("Graph not of the exepected size", 2, graph.size());
+ }
+
+
+
+ @Test
+ public void foafKnowsFilter() {
+ final Graph graph = new SparqlGraph("http://localhost:" + serverPort + "/ds/query");
+
+ final Iri foafKnows = new Iri("http://xmlns.com/foaf/0.1/knows");
+
+ final Iterator<Triple> iter = graph.filter(null, foafKnows, null);
+ Assert.assertTrue(iter.hasNext());
+ final Triple triple1 = iter.next();
+ final BlankNodeOrIri subject1 = triple1.getSubject();
+ Assert.assertTrue(subject1 instanceof BlankNode);
+ Assert.assertTrue(iter.hasNext());
+ final Triple triple2 = iter.next();
+ final BlankNodeOrIri subject2 = triple2.getSubject();
+ Assert.assertTrue(subject2 instanceof BlankNode);
+ Assert.assertNotEquals(subject1, subject2);
+ }
+
+
+
+
+ public static int findFreePort() {
+ int port = 0;
+ try (ServerSocket server = new ServerSocket(0);) {
+ port = server.getLocalPort();
+ } catch (Exception e) {
+ throw new RuntimeException("unable to find a free port");
+ }
+ return port;
+ }
+
+}
diff --git a/impl.sparql/src/test/resources/org/apache/commons/rdf/impl/sparql/similar-bnodes.ttl b/impl.sparql/src/test/resources/org/apache/commons/rdf/impl/sparql/similar-bnodes.ttl
new file mode 100644
index 0000000..16c1ceb
--- /dev/null
+++ b/impl.sparql/src/test/resources/org/apache/commons/rdf/impl/sparql/similar-bnodes.ttl
@@ -0,0 +1,8 @@
+@base <http://example.org/> .
+@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
+@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
+@prefix foaf: <http://xmlns.com/foaf/0.1/> .
+
+
+[] foaf:knows [ foaf:name "Alice"] .
+[] foaf:knows [ foaf:name "Bob" ] .
\ No newline at end of file