You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@marmotta.apache.org by ss...@apache.org on 2013/10/09 12:27:29 UTC
[3/3] git commit: fix MARMOTTA-340 (context-aware SPARQL connections)
fix MARMOTTA-340 (context-aware SPARQL connections)
Project: http://git-wip-us.apache.org/repos/asf/incubator-marmotta/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-marmotta/commit/fa0aef8a
Tree: http://git-wip-us.apache.org/repos/asf/incubator-marmotta/tree/fa0aef8a
Diff: http://git-wip-us.apache.org/repos/asf/incubator-marmotta/diff/fa0aef8a
Branch: refs/heads/develop
Commit: fa0aef8a05c81cd8b8998ffd2fa68715319e25db
Parents: ab4d57a
Author: Sebastian Schaffert <ss...@apache.org>
Authored: Wed Oct 9 12:26:59 2013 +0200
Committer: Sebastian Schaffert <ss...@apache.org>
Committed: Wed Oct 9 12:26:59 2013 +0200
----------------------------------------------------------------------
.../evaluation/KiWiEvaluationStrategyImpl.java | 4 +-
.../persistence/KiWiSparqlConnection.java | 116 ++++++++++++-
.../kiwi/sparql/test/ContextAwareTest.java | 164 +++++++++++++++++++
3 files changed, 275 insertions(+), 9 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/incubator-marmotta/blob/fa0aef8a/libraries/kiwi/kiwi-sparql/src/main/java/org/apache/marmotta/kiwi/sparql/evaluation/KiWiEvaluationStrategyImpl.java
----------------------------------------------------------------------
diff --git a/libraries/kiwi/kiwi-sparql/src/main/java/org/apache/marmotta/kiwi/sparql/evaluation/KiWiEvaluationStrategyImpl.java b/libraries/kiwi/kiwi-sparql/src/main/java/org/apache/marmotta/kiwi/sparql/evaluation/KiWiEvaluationStrategyImpl.java
index b6cb5e6..b5b854d 100644
--- a/libraries/kiwi/kiwi-sparql/src/main/java/org/apache/marmotta/kiwi/sparql/evaluation/KiWiEvaluationStrategyImpl.java
+++ b/libraries/kiwi/kiwi-sparql/src/main/java/org/apache/marmotta/kiwi/sparql/evaluation/KiWiEvaluationStrategyImpl.java
@@ -75,7 +75,7 @@ public class KiWiEvaluationStrategyImpl extends EvaluationStrategyImpl{
log.debug("applying KiWi JOIN optimizations on SPARQL query ...");
try {
- return new ExceptionConvertingIteration<BindingSet, QueryEvaluationException>(connection.evaluateJoin(join, bindings)) {
+ return new ExceptionConvertingIteration<BindingSet, QueryEvaluationException>(connection.evaluateJoin(join, bindings, dataset)) {
@Override
protected QueryEvaluationException convert(Exception e) {
return new QueryEvaluationException(e);
@@ -99,7 +99,7 @@ public class KiWiEvaluationStrategyImpl extends EvaluationStrategyImpl{
log.debug("applying KiWi FILTER optimizations on SPARQL query ...");
try {
- return new ExceptionConvertingIteration<BindingSet, QueryEvaluationException>(connection.evaluateJoin(join, bindings)) {
+ return new ExceptionConvertingIteration<BindingSet, QueryEvaluationException>(connection.evaluateJoin(join, bindings, dataset)) {
@Override
protected QueryEvaluationException convert(Exception e) {
return new QueryEvaluationException(e);
http://git-wip-us.apache.org/repos/asf/incubator-marmotta/blob/fa0aef8a/libraries/kiwi/kiwi-sparql/src/main/java/org/apache/marmotta/kiwi/sparql/persistence/KiWiSparqlConnection.java
----------------------------------------------------------------------
diff --git a/libraries/kiwi/kiwi-sparql/src/main/java/org/apache/marmotta/kiwi/sparql/persistence/KiWiSparqlConnection.java b/libraries/kiwi/kiwi-sparql/src/main/java/org/apache/marmotta/kiwi/sparql/persistence/KiWiSparqlConnection.java
index 61eedd2..d65b059 100644
--- a/libraries/kiwi/kiwi-sparql/src/main/java/org/apache/marmotta/kiwi/sparql/persistence/KiWiSparqlConnection.java
+++ b/libraries/kiwi/kiwi-sparql/src/main/java/org/apache/marmotta/kiwi/sparql/persistence/KiWiSparqlConnection.java
@@ -20,6 +20,7 @@ package org.apache.marmotta.kiwi.sparql.persistence;
import com.google.common.base.Preconditions;
import info.aduna.iteration.CloseableIteration;
import info.aduna.iteration.CloseableIteratorIteration;
+import info.aduna.iteration.EmptyIteration;
import info.aduna.iteration.Iterations;
import org.apache.commons.lang3.StringUtils;
import org.apache.marmotta.commons.sesame.model.Namespaces;
@@ -29,12 +30,11 @@ import org.apache.marmotta.kiwi.persistence.KiWiConnection;
import org.apache.marmotta.kiwi.persistence.util.ResultSetIteration;
import org.apache.marmotta.kiwi.persistence.util.ResultTransformerFunction;
import org.apache.marmotta.kiwi.sail.KiWiValueFactory;
-import org.openrdf.model.BNode;
-import org.openrdf.model.Literal;
-import org.openrdf.model.URI;
-import org.openrdf.model.Value;
+import org.openrdf.model.*;
+import org.openrdf.model.vocabulary.SESAME;
import org.openrdf.query.Binding;
import org.openrdf.query.BindingSet;
+import org.openrdf.query.Dataset;
import org.openrdf.query.algebra.*;
import org.openrdf.query.impl.MapBindingSet;
import org.slf4j.Logger;
@@ -79,10 +79,12 @@ public class KiWiSparqlConnection {
/**
* Evaluate a statement pattern join or filter on the database by translating it into an appropriate SQL statement.
* Copied and adapted from KiWiReasoningConnection.query()
+ *
* @param join
+ * @param dataset
* @return
*/
- public CloseableIteration<BindingSet, SQLException> evaluateJoin(TupleExpr join, final BindingSet bindings) throws SQLException, InterruptedException {
+ public CloseableIteration<BindingSet, SQLException> evaluateJoin(TupleExpr join, final BindingSet bindings, final Dataset dataset) throws SQLException, InterruptedException {
Preconditions.checkArgument(join instanceof Join || join instanceof Filter || join instanceof StatementPattern);
// some definitions
@@ -107,13 +109,66 @@ public class KiWiSparqlConnection {
int variableCount = 0;
// a map for the variable names; will look like { ?x -> "V1", ?y -> "V2", ... }
- final Map<Var,String> variableNames = new HashMap<Var, String>();
+ final Map<Var,String> variableNames = new HashMap<>();
// a map for mapping variables to field names; each variable might have one or more field names,
// depending on the number of patterns it occurs in; will look like
// { ?x -> ["P1_V1", "P2_V1"], ?y -> ["P2_V2"], ... }
- Map<Var,List<String>> queryVariables = new HashMap<Var, List<String>>();
+ Map<Var,List<String>> queryVariables = new HashMap<>();
+
+ // a map for defining alternative context values for each variable used in the context part of a pattern
+ Map<StatementPattern,List<Resource>> variableContexts = new HashMap<>();
+
for(StatementPattern p : patterns) {
+ // check graph restrictions in datasets (MARMOTTA-340)
+ Resource[] contexts;
+ Value contextValue = p.getContextVar() != null ? p.getContextVar().getValue() : null;
+
+ Set<URI> graphs = null;
+ boolean emptyGraph = false;
+
+ if (dataset != null) {
+ if (p.getScope() == StatementPattern.Scope.DEFAULT_CONTEXTS) {
+ graphs = dataset.getDefaultGraphs();
+ emptyGraph = graphs.isEmpty() && !dataset.getNamedGraphs().isEmpty();
+ }
+ else {
+ graphs = dataset.getNamedGraphs();
+ emptyGraph = graphs.isEmpty() && !dataset.getDefaultGraphs().isEmpty();
+ }
+ }
+
+ if (emptyGraph) {
+ // Search zero contexts
+ return new EmptyIteration<BindingSet, SQLException>();
+ } else if (graphs == null || graphs.isEmpty()) {
+ if (contextValue != null) {
+ contexts = new Resource[] { (Resource)contextValue };
+ } else {
+ contexts = new Resource[0];
+ }
+ } else if (contextValue != null) {
+ if (graphs.contains(contextValue)) {
+ contexts = new Resource[] { (Resource)contextValue };
+ } else {
+ // Statement pattern specifies a context that is not part of
+ // the dataset
+ return new EmptyIteration<BindingSet, SQLException>();
+ }
+ } else {
+ contexts = new Resource[graphs.size()];
+ int i = 0;
+ for (URI graph : graphs) {
+ URI context = null;
+ if (!SESAME.NIL.equals(graph)) {
+ context = graph;
+ }
+ contexts[i++] = context;
+ }
+ }
+
+
+ // build pattern
Var[] fields = new Var[] {
p.getSubjectVar(),
p.getPredicateVar(),
@@ -132,6 +187,11 @@ public class KiWiSparqlConnection {
queryVariables.get(v).add(pName + "_" + positions[i] + "_" + vName);
}
}
+
+ // build an OR query for the value of the context variable
+ if(contexts.length > 0) {
+ variableContexts.put(p, Arrays.asList(contexts));
+ }
}
// build the select clause by projecting for each query variable the first name
@@ -274,6 +334,36 @@ public class KiWiSparqlConnection {
}
+ // 6. for each context variable with a restricted list of contexts, we add a condition to the where clause
+ // of the form (V.id = R1.id OR V.id = R2.id ...)
+ for(Map.Entry<StatementPattern,List<Resource>> vctx : variableContexts.entrySet()) {
+ // the variable
+ String varName = patternNames.get(vctx.getKey());
+
+ // the string we are building
+ StringBuilder cCond = new StringBuilder();
+ cCond.append("(");
+ for(Iterator<Resource> it = vctx.getValue().iterator(); it.hasNext(); ) {
+ Value v = valueFactory.convert(it.next());
+ if(v instanceof KiWiNode) {
+ Long nodeId = ((KiWiNode) v).getId();
+
+ cCond.append(varName);
+ cCond.append(".context = ");
+ cCond.append(nodeId);
+
+ if(it.hasNext()) {
+ cCond.append(" OR ");
+ }
+ } else {
+ throw new IllegalArgumentException("the values in this query have not been created by the KiWi value factory");
+ }
+
+ }
+ cCond.append(")");
+ whereConditions.add(cCond.toString());
+ }
+
// construct the where clause
StringBuilder whereClause = new StringBuilder();
for(Iterator<String> it = whereConditions.iterator(); it.hasNext(); ) {
@@ -699,6 +789,18 @@ public class KiWiSparqlConnection {
}
+ protected Value getVarValue(Var var, BindingSet bindings) {
+ if (var == null) {
+ return null;
+ }
+ else if (var.hasValue()) {
+ return var.getValue();
+ }
+ else {
+ return bindings.getValue(var.getName());
+ }
+ }
+
private static enum OPTypes {
STRING, DOUBLE, INT, DATE, ANY
http://git-wip-us.apache.org/repos/asf/incubator-marmotta/blob/fa0aef8a/libraries/kiwi/kiwi-sparql/src/test/java/org/apache/marmotta/kiwi/sparql/test/ContextAwareTest.java
----------------------------------------------------------------------
diff --git a/libraries/kiwi/kiwi-sparql/src/test/java/org/apache/marmotta/kiwi/sparql/test/ContextAwareTest.java b/libraries/kiwi/kiwi-sparql/src/test/java/org/apache/marmotta/kiwi/sparql/test/ContextAwareTest.java
new file mode 100644
index 0000000..8f1cf2f
--- /dev/null
+++ b/libraries/kiwi/kiwi-sparql/src/test/java/org/apache/marmotta/kiwi/sparql/test/ContextAwareTest.java
@@ -0,0 +1,164 @@
+package org.apache.marmotta.kiwi.sparql.test;
+
+import info.aduna.iteration.Iterations;
+import org.apache.commons.lang3.RandomStringUtils;
+import org.apache.marmotta.kiwi.config.KiWiConfiguration;
+import org.apache.marmotta.kiwi.sail.KiWiStore;
+import org.apache.marmotta.kiwi.sparql.sail.KiWiSparqlSail;
+import org.apache.marmotta.kiwi.test.junit.KiWiDatabaseRunner;
+import org.junit.*;
+import org.junit.rules.TestWatcher;
+import org.junit.runner.Description;
+import org.junit.runner.RunWith;
+import org.openrdf.model.Literal;
+import org.openrdf.model.URI;
+import org.openrdf.query.BindingSet;
+import org.openrdf.query.QueryLanguage;
+import org.openrdf.query.TupleQuery;
+import org.openrdf.repository.Repository;
+import org.openrdf.repository.RepositoryConnection;
+import org.openrdf.repository.RepositoryException;
+import org.openrdf.repository.contextaware.ContextAwareConnection;
+import org.openrdf.repository.sail.SailRepository;
+import org.openrdf.rio.RDFParseException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.IOException;
+import java.sql.SQLException;
+import java.util.List;
+
+/**
+ * This test tests a complex situation where there is data in two contexts, and the query is carried out
+ * over a context aware repository. In this case, it should only list triples from the restricted context.
+ *
+ * @author Sebastian Schaffert (sschaffert@apache.org)
+ */
+@RunWith(KiWiDatabaseRunner.class)
+public class ContextAwareTest {
+
+ private KiWiStore store;
+
+ private KiWiSparqlSail ssail;
+
+ private Repository repository;
+
+
+ private URI context1, context2;
+
+ private URI subject, object11, object21;
+
+ private URI predicate1, predicate2;
+
+ private final KiWiConfiguration dbConfig;
+
+ public ContextAwareTest(KiWiConfiguration dbConfig) {
+ this.dbConfig = dbConfig;
+ }
+
+
+ @Before
+ public void initDatabase() throws RepositoryException, IOException, RDFParseException {
+ store = new KiWiStore(dbConfig);
+ ssail = new KiWiSparqlSail(store);
+ repository = new SailRepository(ssail);
+ repository.initialize();
+
+ context1 = repository.getValueFactory().createURI("http://localhost/test/" + RandomStringUtils.randomAlphanumeric(8));
+ context2 = repository.getValueFactory().createURI("http://localhost/test/" + RandomStringUtils.randomAlphanumeric(8));
+ subject = repository.getValueFactory().createURI("http://localhost/test/" + RandomStringUtils.randomAlphanumeric(8));
+ object11 = repository.getValueFactory().createURI("http://localhost/test/" + RandomStringUtils.randomAlphanumeric(8));
+ object21 = repository.getValueFactory().createURI("http://localhost/test/" + RandomStringUtils.randomAlphanumeric(8));
+
+ predicate1 = repository.getValueFactory().createURI("http://localhost/test/P1");
+ predicate2 = repository.getValueFactory().createURI("http://localhost/test/P1");
+
+ Literal object12 = repository.getValueFactory().createLiteral("this object should be linked only in context1");
+ Literal object22 = repository.getValueFactory().createLiteral("this object should be linked only in context2");
+
+ // load demo data
+ RepositoryConnection con = repository.getConnection();
+ try {
+ con.begin();
+
+ // create a graph in context1 as follows:
+ // subject1 -- P1 --> object111 -- P1 --> object2
+ con.add(subject,predicate1, object11,context1);
+ con.add(object11,predicate1,object12,context1);
+
+ // create a graph in context2 as follows (note different predicate)
+ // subject1 -- P2 --> object121 -- P2 --> object3
+ con.add(subject,predicate2, object21,context2);
+ con.add(object21,predicate2,object22,context2);
+
+ con.commit();
+ } finally {
+ con.close();
+ }
+ }
+
+ @After
+ public void dropDatabase() throws RepositoryException, SQLException {
+ store.getPersistence().dropDatabase();
+ repository.shutDown();
+ }
+
+ final Logger logger =
+ LoggerFactory.getLogger(this.getClass());
+
+ @Rule
+ public TestWatcher watchman = new TestWatcher() {
+ /**
+ * Invoked when a test is about to start
+ */
+ @Override
+ protected void starting(Description description) {
+ logger.info("{} being run...", description.getMethodName());
+ }
+ };
+
+ @Test
+ public void testSinglePattern() throws Exception {
+ ContextAwareConnection con = new ContextAwareConnection(repository, repository.getConnection());
+ try {
+ con.setInsertContext(context1);
+ con.setReadContexts(context1);
+ con.setRemoveContexts(context1);
+
+ Assert.assertTrue(con.hasStatement(subject,predicate1, object11));
+ Assert.assertFalse(con.hasStatement(subject, predicate2, object21));
+
+ String queryStr = "SELECT ?X ?Z WHERE { ?X ?Y ?Z }";
+
+ TupleQuery query = con.prepareTupleQuery(QueryLanguage.SPARQL, queryStr);
+ List<BindingSet> result = Iterations.asList(query.evaluate());
+ Assert.assertEquals(2,result.size());
+ con.commit();
+ } finally {
+ con.close();
+ }
+ }
+
+ @Test
+ public void testJoinPattern() throws Exception {
+ ContextAwareConnection con = new ContextAwareConnection(repository, repository.getConnection());
+ try {
+ con.setInsertContext(context1);
+ con.setReadContexts(context1);
+ con.setRemoveContexts(context1);
+
+ Assert.assertTrue(con.hasStatement(subject,predicate1, object11));
+ Assert.assertFalse(con.hasStatement(subject, predicate2, object21));
+
+ String queryStr = "SELECT ?X ?Z WHERE { ?X ?P ?Y . ?Y ?P ?Z }";
+
+ TupleQuery query = con.prepareTupleQuery(QueryLanguage.SPARQL, queryStr);
+ List<BindingSet> result = Iterations.asList(query.evaluate());
+ Assert.assertEquals(1,result.size());
+ con.commit();
+ } finally {
+ con.close();
+ }
+ }
+
+}