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();
+        }
+    }
+
+}