Repository: incubator-rya Updated Branches: refs/heads/master 0d80871ff -> 2e88f1050
RYA-295 owl:allValuesFrom inference. Closes #201. Project: http://git-wip-us.apache.org/repos/asf/incubator-rya/repo Commit: http://git-wip-us.apache.org/repos/asf/incubator-rya/commit/2e88f105 Tree: http://git-wip-us.apache.org/repos/asf/incubator-rya/tree/2e88f105 Diff: http://git-wip-us.apache.org/repos/asf/incubator-rya/diff/2e88f105 Branch: refs/heads/master Commit: 2e88f1050b095ade37586562398cd02e162da639 Parents: 0d80871 Author: Jesse Hatfield <[email protected]> Authored: Tue Aug 8 15:58:10 2017 -0400 Committer: Caleb Meier <[email protected]> Committed: Fri Aug 18 19:03:46 2017 -0700 ---------------------------------------------------------------------- .../src/main/java/MongoRyaDirectExample.java | 52 ++++++++ .../RdfCloudTripleStoreConnection.java | 2 + .../inference/AllValuesFromVisitor.java | 112 ++++++++++++++++ .../inference/InferenceEngine.java | 81 +++++++++++- .../inference/AllValuesFromVisitorTest.java | 128 +++++++++++++++++++ .../inference/InferenceEngineTest.java | 31 +++++ .../rdftriplestore/inference/InferenceIT.java | 37 ++++++ 7 files changed, 440 insertions(+), 3 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/incubator-rya/blob/2e88f105/extras/indexingExample/src/main/java/MongoRyaDirectExample.java ---------------------------------------------------------------------- diff --git a/extras/indexingExample/src/main/java/MongoRyaDirectExample.java b/extras/indexingExample/src/main/java/MongoRyaDirectExample.java index 32de913..1294a44 100644 --- a/extras/indexingExample/src/main/java/MongoRyaDirectExample.java +++ b/extras/indexingExample/src/main/java/MongoRyaDirectExample.java @@ -26,6 +26,7 @@ import org.apache.log4j.Logger; import org.openrdf.model.Namespace; import org.openrdf.model.URI; import org.openrdf.model.ValueFactory; +import org.openrdf.model.vocabulary.OWL; import org.openrdf.model.vocabulary.RDF; import org.openrdf.model.vocabulary.RDFS; import org.openrdf.query.BindingSet; @@ -98,6 +99,7 @@ public class MongoRyaDirectExample { testInfer(conn, sail); testPropertyChainInference(conn, sail); testPropertyChainInferenceAltRepresentation(conn, sail); + testAllValuesFromInference(conn, sail); } log.info("TIME: " + (System.currentTimeMillis() - start) / 1000.); @@ -446,6 +448,56 @@ public class MongoRyaDirectExample { log.info("Result count : " + resultHandler.getCount()); } + + public static void testAllValuesFromInference(SailRepositoryConnection conn, Sail sail) throws MalformedQueryException, RepositoryException, + UpdateExecutionException, QueryEvaluationException, TupleQueryResultHandlerException, InferenceEngineException { + log.info("Adding Data"); + String insert = "INSERT DATA\n" + + "{ GRAPH <http://updated/test> {\n" + + " <urn:Alice> a <urn:Person> .\n" + + " <urn:Alice> <urn:hasParent> <urn:Bob> .\n" + + " <urn:Carol> <urn:hasParent> <urn:Dan> .\n" + + "}}"; + Update update = conn.prepareUpdate(QueryLanguage.SPARQL, insert); + update.execute(); + final String inferQuery = "select distinct ?x { GRAPH <http://updated/test> { ?x a <urn:Person> }}"; + final String explicitQuery = "select distinct ?x { GRAPH <http://updated/test> {\n" + + " { ?x a <urn:Person> }\n" + + " UNION {\n" + + " ?y a <urn:Person> .\n" + + " ?y <urn:hasParent> ?x .\n" + + " }\n" + + "}}"; + log.info("Running Explicit Query"); + CountingResultHandler resultHandler = new CountingResultHandler(); + TupleQuery tupleQuery = conn.prepareTupleQuery(QueryLanguage.SPARQL, explicitQuery); + tupleQuery.evaluate(resultHandler); + log.info("Result count : " + resultHandler.getCount()); + Validate.isTrue(resultHandler.getCount() == 2); + log.info("Running Inference-dependent Query"); + resultHandler.resetCount(); + tupleQuery = conn.prepareTupleQuery(QueryLanguage.SPARQL, inferQuery); + tupleQuery.evaluate(resultHandler); + log.info("Result count : " + resultHandler.getCount()); + Validate.isTrue(resultHandler.getCount() == 1); + log.info("Adding owl:allValuesFrom Schema"); + insert = "PREFIX rdfs: <" + RDFS.NAMESPACE + ">\n" + + "PREFIX owl: <" + OWL.NAMESPACE + ">\n" + + "INSERT DATA\n" + + "{ GRAPH <http://updated/test> {\n" + + " <urn:Person> rdfs:subClassOf [ owl:onProperty <urn:hasParent> ; owl:allValuesFrom <urn:Person> ] ." + + "}}"; + update = conn.prepareUpdate(QueryLanguage.SPARQL, insert); + update.execute(); + log.info("Refreshing InferenceEngine"); + ((RdfCloudTripleStore) sail).getInferenceEngine().refreshGraph(); + log.info("Re-running Inference-dependent Query"); + resultHandler.resetCount(); + tupleQuery = conn.prepareTupleQuery(QueryLanguage.SPARQL, inferQuery); + tupleQuery.evaluate(resultHandler); + log.info("Result count : " + resultHandler.getCount()); + Validate.isTrue(resultHandler.getCount() == 2); + } public static void testInfer(SailRepositoryConnection conn, Sail sail) throws MalformedQueryException, RepositoryException, UpdateExecutionException, QueryEvaluationException, TupleQueryResultHandlerException, InferenceEngineException { http://git-wip-us.apache.org/repos/asf/incubator-rya/blob/2e88f105/sail/src/main/java/org/apache/rya/rdftriplestore/RdfCloudTripleStoreConnection.java ---------------------------------------------------------------------- diff --git a/sail/src/main/java/org/apache/rya/rdftriplestore/RdfCloudTripleStoreConnection.java b/sail/src/main/java/org/apache/rya/rdftriplestore/RdfCloudTripleStoreConnection.java index 483d4ee..ec93da8 100644 --- a/sail/src/main/java/org/apache/rya/rdftriplestore/RdfCloudTripleStoreConnection.java +++ b/sail/src/main/java/org/apache/rya/rdftriplestore/RdfCloudTripleStoreConnection.java @@ -50,6 +50,7 @@ import org.apache.rya.rdftriplestore.evaluation.QueryJoinSelectOptimizer; import org.apache.rya.rdftriplestore.evaluation.RdfCloudTripleStoreEvaluationStatistics; import org.apache.rya.rdftriplestore.evaluation.RdfCloudTripleStoreSelectivityEvaluationStatistics; import org.apache.rya.rdftriplestore.evaluation.SeparateFilterJoinsVisitor; +import org.apache.rya.rdftriplestore.inference.AllValuesFromVisitor; import org.apache.rya.rdftriplestore.inference.DomainRangeVisitor; import org.apache.rya.rdftriplestore.inference.HasValueVisitor; import org.apache.rya.rdftriplestore.inference.InferenceEngine; @@ -351,6 +352,7 @@ public class RdfCloudTripleStoreConnection extends SailConnectionBase { ) { try { tupleExpr.visit(new DomainRangeVisitor(queryConf, inferenceEngine)); + tupleExpr.visit(new AllValuesFromVisitor(queryConf, inferenceEngine)); tupleExpr.visit(new HasValueVisitor(queryConf, inferenceEngine)); tupleExpr.visit(new PropertyChainVisitor(queryConf, inferenceEngine)); tupleExpr.visit(new TransitivePropertyVisitor(queryConf, inferenceEngine)); http://git-wip-us.apache.org/repos/asf/incubator-rya/blob/2e88f105/sail/src/main/java/org/apache/rya/rdftriplestore/inference/AllValuesFromVisitor.java ---------------------------------------------------------------------- diff --git a/sail/src/main/java/org/apache/rya/rdftriplestore/inference/AllValuesFromVisitor.java b/sail/src/main/java/org/apache/rya/rdftriplestore/inference/AllValuesFromVisitor.java new file mode 100644 index 0000000..9fc297a --- /dev/null +++ b/sail/src/main/java/org/apache/rya/rdftriplestore/inference/AllValuesFromVisitor.java @@ -0,0 +1,112 @@ +package org.apache.rya.rdftriplestore.inference; +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import java.util.Map; +import java.util.Set; +import java.util.UUID; + +import org.apache.rya.api.RdfCloudTripleStoreConfiguration; +import org.apache.rya.api.utils.NullableStatementImpl; +import org.apache.rya.rdftriplestore.utils.FixedStatementPattern; +import org.openrdf.model.Resource; +import org.openrdf.model.URI; +import org.openrdf.model.vocabulary.OWL; +import org.openrdf.model.vocabulary.RDF; +import org.openrdf.query.algebra.StatementPattern; +import org.openrdf.query.algebra.Var; + +/** + * Expands the query tree to account for any universal class expressions (property restrictions + * using owl:allValuesFrom) in the ontology known to the {@link InferenceEngine}. + * + * Operates on {@link StatementPattern} nodes whose predicate is rdf:type and whose object is a + * defined type (not a variable) which is related to an allValuesFrom expression in the ontology. + * When applicable, replaces the node with a union of itself and a subtree that matches any + * instance that can be inferred to have the type in question via the semantics of + * owl:allValuesFrom. + * + * A universal class expression references a predicate and a value class, and represents the set of + * individuals who, for any instance of the predicate, have a value belonging to the value class. + * Therefore, the value class should be inferred for any individual which is the object of a triple + * with that predicate and with a subject belonging to the class expression. This implication is + * similar to rdfs:range except that it only applies when the subject of the triple belongs to a + * specific type. + * + * (Note: Because of OWL's open world assumption, the inference in the other direction can't be + * made. That is, when an individual is explicitly declared to have the universally quantified + * restriction, then the types of its values can be inferred. But when the universal restriction + * isn't explicitly stated, it can't be inferred from the values themselves, because there's no + * guarantee that all values are known.) + */ +public class AllValuesFromVisitor extends AbstractInferVisitor { + + /** + * Creates a new {@link AllValuesFromVisitor}, which is enabled by default. + * @param conf The {@link RdfCloudTripleStoreConfiguration}. + * @param inferenceEngine The InferenceEngine containing the relevant ontology. + */ + public AllValuesFromVisitor(RdfCloudTripleStoreConfiguration conf, InferenceEngine inferenceEngine) { + super(conf, inferenceEngine); + include = true; + } + + /** + * Checks whether the StatementPattern is a type query whose solutions could be inferred + * by allValuesFrom inference, and if so, replaces the node with a union of itself and any + * possible inference. + */ + @Override + protected void meetSP(StatementPattern node) throws Exception { + final Var subjVar = node.getSubjectVar(); + final Var predVar = node.getPredicateVar(); + final Var objVar = node.getObjectVar(); + // Only applies to type queries where the type is defined + if (predVar != null && RDF.TYPE.equals(predVar.getValue()) && objVar != null && objVar.getValue() instanceof Resource) { + final Resource typeToInfer = (Resource) objVar.getValue(); + Map<Resource, Set<URI>> relevantAvfRestrictions = inferenceEngine.getAllValuesFromByValueType(typeToInfer); + if (!relevantAvfRestrictions.isEmpty()) { + // We can infer the queried type if, for an allValuesFrom restriction type + // associated with the queried type, some anonymous neighboring node belongs to the + // restriction type and has the node in question (subjVar) as a value for the + // restriction's property. + final Var avfTypeVar = new Var("t-" + UUID.randomUUID()); + final Var avfPredVar = new Var("p-" + UUID.randomUUID()); + final Var neighborVar = new Var("n-" + UUID.randomUUID()); + neighborVar.setAnonymous(true); + final StatementPattern membershipPattern = new DoNotExpandSP(neighborVar, + new Var(RDF.TYPE.stringValue(), RDF.TYPE), avfTypeVar); + final StatementPattern valuePattern = new StatementPattern(neighborVar, avfPredVar, subjVar); + final InferJoin avfPattern = new InferJoin(membershipPattern, valuePattern); + // Use a FixedStatementPattern to contain the appropriate (restriction, predicate) + // pairs, and check each one against the general pattern. + final FixedStatementPattern avfPropertyTypes = new FixedStatementPattern(avfTypeVar, + new Var(OWL.ONPROPERTY.stringValue(), OWL.ONPROPERTY), avfPredVar); + for (Resource avfRestrictionType : relevantAvfRestrictions.keySet()) { + for (URI avfProperty : relevantAvfRestrictions.get(avfRestrictionType)) { + avfPropertyTypes.statements.add(new NullableStatementImpl(avfRestrictionType, + OWL.ONPROPERTY, avfProperty)); + } + } + final InferJoin avfInferenceQuery = new InferJoin(avfPropertyTypes, avfPattern); + node.replaceWith(new InferUnion(node.clone(), avfInferenceQuery)); + } + } + } +} http://git-wip-us.apache.org/repos/asf/incubator-rya/blob/2e88f105/sail/src/main/java/org/apache/rya/rdftriplestore/inference/InferenceEngine.java ---------------------------------------------------------------------- diff --git a/sail/src/main/java/org/apache/rya/rdftriplestore/inference/InferenceEngine.java b/sail/src/main/java/org/apache/rya/rdftriplestore/inference/InferenceEngine.java index 43f00e0..05b847b 100644 --- a/sail/src/main/java/org/apache/rya/rdftriplestore/inference/InferenceEngine.java +++ b/sail/src/main/java/org/apache/rya/rdftriplestore/inference/InferenceEngine.java @@ -73,10 +73,11 @@ public class InferenceEngine { private Set<URI> symmetricPropertySet; private Map<URI, URI> inverseOfMap; private Set<URI> transitivePropertySet; - private Map<Resource, Map<URI, Value>> hasValueByType; - private Map<URI, Map<Resource, Value>> hasValueByProperty; private Map<URI, Set<URI>> domainByType; private Map<URI, Set<URI>> rangeByType; + private Map<Resource, Map<URI, Value>> hasValueByType; + private Map<URI, Map<Resource, Value>> hasValueByProperty; + private Map<Resource, Map<Resource, URI>> allValuesFromByValueType; private RyaDAO ryaDAO; private RdfCloudTripleStoreConfiguration conf; @@ -589,8 +590,9 @@ public class InferenceEngine { iter.close(); } } - // Query for the hasValue restrictions and add them to the schema + // Query for specific types of restriction and add their details to the schema refreshHasValueRestrictions(restrictions); + refreshAllValuesFromRestrictions(restrictions); } private void refreshHasValueRestrictions(Map<Resource, URI> restrictions) throws QueryEvaluationException { @@ -621,6 +623,36 @@ public class InferenceEngine { } } + private void refreshAllValuesFromRestrictions(Map<Resource, URI> restrictions) throws QueryEvaluationException { + allValuesFromByValueType = new HashMap<>(); + CloseableIteration<Statement, QueryEvaluationException> iter = RyaDAOHelper.query(ryaDAO, null, OWL.ALLVALUESFROM, null, conf); + try { + while (iter.hasNext()) { + Statement st = iter.next(); + if (restrictions.containsKey(st.getSubject()) && st.getObject() instanceof URI) { + URI property = restrictions.get(st.getSubject()); + URI valueClass = (URI) st.getObject(); + // Should also be triggered by subclasses of the property restriction + Set<Resource> restrictionClasses = new HashSet<>(); + restrictionClasses.add(st.getSubject()); + if (st.getSubject() instanceof URI) { + restrictionClasses.addAll(findParents(subClassOfGraph, (URI) st.getSubject())); + } + for (Resource restrictionClass : restrictionClasses) { + if (!allValuesFromByValueType.containsKey(valueClass)) { + allValuesFromByValueType.put(valueClass, new HashMap<>()); + } + allValuesFromByValueType.get(valueClass).put(restrictionClass, property); + } + } + } + } finally { + if (iter != null) { + iter.close(); + } + } + } + private static Vertex getVertex(Graph graph, Object id) { Iterator<Vertex> it = graph.vertices(id.toString()); if (it.hasNext()) { @@ -947,4 +979,47 @@ public class InferenceEngine { } return properties; } + + /** + * For a given type, return information about any owl:allValuesFrom restriction that could imply + * an individual's membership in that type: If the subject of a triple belongs to the type + * associated with the restriction itself, and the predicate is the one referenced by the + * restriction, then the object of the triple is implied to have the value type. + * @param valueType The type to be inferred, which is the type of the object of the triple, or + * the type from which all values are stated to belong. Takes class hierarchy into account, + * so possible inferences include any ways of inferring subtypes of the value type, and + * subject types that trigger inference include any subtypes of relevant restrictions. + * Also considers property hierarchy, so properties that trigger inference will include + * subproperties of those referenced by relevant restrictions. + * @return A map from subject type (a property restriction type or a subtype of one) to the set + * of properties (including any property referenced by such a restriction and all of its + * subproperties) such that for any individual which belongs to the subject type, all + * values it has for any of those properties belong to the value type. + */ + public Map<Resource, Set<URI>> getAllValuesFromByValueType(Resource valueType) { + Map<Resource, Set<URI>> implications = new HashMap<>(); + if (allValuesFromByValueType != null) { + // Check for any subtypes which would in turn imply the value type + HashSet<Resource> valueTypes = new HashSet<>(); + valueTypes.add(valueType); + if (valueType instanceof URI) { + valueTypes.addAll(findParents(subClassOfGraph, (URI) valueType)); + } + for (Resource valueSubType : valueTypes) { + if (allValuesFromByValueType.containsKey(valueSubType)) { + Map<Resource, URI> restrictionToProperty = allValuesFromByValueType.get(valueSubType); + for (Resource restrictionType : restrictionToProperty.keySet()) { + if (!implications.containsKey(restrictionType)) { + implications.put(restrictionType, new HashSet<>()); + } + URI property = restrictionToProperty.get(restrictionType); + implications.get(restrictionType).add(property); + // Also add subproperties that would in turn imply the property + implications.get(restrictionType).addAll(findParents(subPropertyOfGraph, property)); + } + } + } + } + return implications; + } } http://git-wip-us.apache.org/repos/asf/incubator-rya/blob/2e88f105/sail/src/test/java/org/apache/rya/rdftriplestore/inference/AllValuesFromVisitorTest.java ---------------------------------------------------------------------- diff --git a/sail/src/test/java/org/apache/rya/rdftriplestore/inference/AllValuesFromVisitorTest.java b/sail/src/test/java/org/apache/rya/rdftriplestore/inference/AllValuesFromVisitorTest.java new file mode 100644 index 0000000..d284d57 --- /dev/null +++ b/sail/src/test/java/org/apache/rya/rdftriplestore/inference/AllValuesFromVisitorTest.java @@ -0,0 +1,128 @@ +package org.apache.rya.rdftriplestore.inference; +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +import org.apache.rya.accumulo.AccumuloRdfConfiguration; +import org.apache.rya.api.utils.NullableStatementImpl; +import org.apache.rya.rdftriplestore.utils.FixedStatementPattern; +import org.junit.Assert; +import org.junit.Test; +import org.openrdf.model.Resource; +import org.openrdf.model.URI; +import org.openrdf.model.ValueFactory; +import org.openrdf.model.impl.ValueFactoryImpl; +import org.openrdf.model.vocabulary.OWL; +import org.openrdf.model.vocabulary.RDF; +import org.openrdf.query.algebra.Join; +import org.openrdf.query.algebra.Projection; +import org.openrdf.query.algebra.ProjectionElem; +import org.openrdf.query.algebra.ProjectionElemList; +import org.openrdf.query.algebra.StatementPattern; +import org.openrdf.query.algebra.TupleExpr; +import org.openrdf.query.algebra.Union; +import org.openrdf.query.algebra.Var; + +public class AllValuesFromVisitorTest { + private final AccumuloRdfConfiguration conf = new AccumuloRdfConfiguration(); + private final ValueFactory vf = new ValueFactoryImpl(); + + // Value types + private final URI person = vf.createURI("urn:Person"); + private final URI dog = vf.createURI("urn:Dog"); + // Predicates + private final URI parent = vf.createURI("urn:parent"); + private final URI relative = vf.createURI("urn:relative"); + // Restriction types + private final URI parentsAreTallPeople = vf.createURI("urn:parentsAreTallPeople"); + private final URI parentsArePeople = vf.createURI("urn:parentsArePeople"); + private final URI relativesArePeople = vf.createURI("urn:relativesArePeople"); + private final URI parentsAreDogs = vf.createURI("urn:parentsAreDogs"); + + @Test + public void testRewriteTypePattern() throws Exception { + // Configure a mock instance engine with an ontology: + final InferenceEngine inferenceEngine = mock(InferenceEngine.class); + Map<Resource, Set<URI>> personAVF = new HashMap<>(); + personAVF.put(parentsAreTallPeople, new HashSet<>()); + personAVF.put(parentsArePeople, new HashSet<>()); + personAVF.put(relativesArePeople, new HashSet<>()); + personAVF.get(parentsAreTallPeople).add(parent); + personAVF.get(parentsArePeople).add(parent); + personAVF.get(relativesArePeople).add(relative); + personAVF.get(relativesArePeople).add(parent); + Map<Resource, Set<URI>> dogAVF = new HashMap<>(); + dogAVF.put(parentsAreDogs, new HashSet<>()); + dogAVF.get(parentsAreDogs).add(parent); + when(inferenceEngine.getAllValuesFromByValueType(person)).thenReturn(personAVF); + when(inferenceEngine.getAllValuesFromByValueType(dog)).thenReturn(dogAVF); + // Query for a specific type and rewrite using the visitor: + StatementPattern originalSP = new StatementPattern(new Var("s"), new Var("p", RDF.TYPE), new Var("o", person)); + final Projection query = new Projection(originalSP, new ProjectionElemList(new ProjectionElem("s", "subject"))); + query.visit(new AllValuesFromVisitor(conf, inferenceEngine)); + // Expected structure: a union of two elements: one is equal to the original statement + // pattern, and the other one joins a list of predicate/restriction type combinations + // with another join querying for values of that predicate for members of that type. + Assert.assertTrue(query.getArg() instanceof Union); + TupleExpr left = ((Union) query.getArg()).getLeftArg(); + TupleExpr right = ((Union) query.getArg()).getRightArg(); + final Join join; + if (left instanceof StatementPattern) { + Assert.assertEquals(originalSP, left); + Assert.assertTrue(right instanceof Join); + join = (Join) right; + } + else { + Assert.assertEquals(originalSP, right); + Assert.assertTrue(left instanceof Join); + join = (Join) left; + } + Assert.assertTrue(join.getLeftArg() instanceof FixedStatementPattern); + Assert.assertTrue(join.getRightArg() instanceof Join); + FixedStatementPattern fsp = (FixedStatementPattern) join.getLeftArg(); + left = ((Join) join.getRightArg()).getLeftArg(); + right = ((Join) join.getRightArg()).getRightArg(); + Assert.assertTrue(left instanceof StatementPattern); + Assert.assertTrue(right instanceof StatementPattern); + // Verify expected predicate/restriction pairs + Assert.assertEquals(4, fsp.statements.size()); + fsp.statements.contains(new NullableStatementImpl(parentsArePeople, OWL.ONPROPERTY, parent)); + fsp.statements.contains(new NullableStatementImpl(relativesArePeople, OWL.ONPROPERTY, relative)); + fsp.statements.contains(new NullableStatementImpl(relativesArePeople, OWL.ONPROPERTY, parent)); + fsp.statements.contains(new NullableStatementImpl(parentsAreTallPeople, OWL.ONPROPERTY, parent)); + // Verify general pattern for matching instances of each pair: Join on unknown subject; left + // triple states it belongs to the restriction while right triple relates it to the original + // subject variable by the relevant property. Restriction and property variables are given + // by the FixedStatementPattern. + StatementPattern leftSP = (StatementPattern) left; + StatementPattern rightSP = (StatementPattern) right; + Assert.assertEquals(rightSP.getSubjectVar(), leftSP.getSubjectVar()); + Assert.assertEquals(RDF.TYPE, leftSP.getPredicateVar().getValue()); + Assert.assertEquals(fsp.getSubjectVar(), leftSP.getObjectVar()); + Assert.assertEquals(fsp.getObjectVar(), rightSP.getPredicateVar()); + Assert.assertEquals(originalSP.getSubjectVar(), rightSP.getObjectVar()); + } +} http://git-wip-us.apache.org/repos/asf/incubator-rya/blob/2e88f105/sail/src/test/java/org/apache/rya/rdftriplestore/inference/InferenceEngineTest.java ---------------------------------------------------------------------- diff --git a/sail/src/test/java/org/apache/rya/rdftriplestore/inference/InferenceEngineTest.java b/sail/src/test/java/org/apache/rya/rdftriplestore/inference/InferenceEngineTest.java index 31ea3e8..556413f 100644 --- a/sail/src/test/java/org/apache/rya/rdftriplestore/inference/InferenceEngineTest.java +++ b/sail/src/test/java/org/apache/rya/rdftriplestore/inference/InferenceEngineTest.java @@ -225,6 +225,37 @@ public class InferenceEngineTest extends TestCase { } @Test + public void testAllValuesFrom() throws Exception { + String insert = "INSERT DATA { GRAPH <http://updated/test> {\n" + + " <urn:Dog> owl:onProperty <urn:relative> ; owl:allValuesFrom <urn:Dog> .\n" + + " <urn:Retriever> rdfs:subClassOf <urn:Dog> .\n" + + " <urn:Terrier> rdfs:subClassOf <urn:Dog> .\n" + + " <urn:Terrier> owl:onProperty <urn:relative> ; owl:allValuesFrom <urn:Terrier> .\n" + + " <urn:Cairn_Terrier> rdfs:subClassOf <urn:Terrier> .\n" + + " <urn:parent> rdfs:subPropertyOf <urn:relative> .\n" + + " <urn:Dog> rdfs:subClassOf <urn:Mammal> .\n" + + " <urn:Person> rdfs:subClassOf <urn:Mammal> .\n" + + " <urn:Person> owl:onProperty <urn:relative> ; owl:allValuesFrom <urn:Person> .\n" + + "}}"; + conn.prepareUpdate(QueryLanguage.SPARQL, insert).execute(); + inferenceEngine.refreshGraph(); + final Map<Resource, Set<URI>> restrictionsImplyingTerrier = new HashMap<>(); + final Set<URI> properties = new HashSet<>(); + properties.add(vf.createURI("urn:parent")); + properties.add(vf.createURI("urn:relative")); + restrictionsImplyingTerrier.put(vf.createURI("urn:Terrier"), properties); + restrictionsImplyingTerrier.put(vf.createURI("urn:Cairn_Terrier"), properties); + Assert.assertEquals(restrictionsImplyingTerrier, inferenceEngine.getAllValuesFromByValueType(vf.createURI("urn:Terrier"))); + final Map<Resource, Set<URI>> restrictionsImplyingDog = new HashMap<>(restrictionsImplyingTerrier); + restrictionsImplyingDog.put(vf.createURI("urn:Dog"), properties); + restrictionsImplyingDog.put(vf.createURI("urn:Retriever"), properties); + Assert.assertEquals(restrictionsImplyingDog, inferenceEngine.getAllValuesFromByValueType(vf.createURI("urn:Dog"))); + final Map<Resource, Set<URI>> restrictionsImplyingMammal = new HashMap<>(restrictionsImplyingDog); + restrictionsImplyingMammal.put(vf.createURI("urn:Person"), properties); + Assert.assertEquals(restrictionsImplyingMammal, inferenceEngine.getAllValuesFromByValueType(vf.createURI("urn:Mammal"))); + } + + @Test public void testHasValueGivenProperty() throws Exception { String insert = "INSERT DATA { GRAPH <http://updated/test> {\n" + " <urn:Biped> owl:onProperty <urn:walksUsingLegs> . \n" http://git-wip-us.apache.org/repos/asf/incubator-rya/blob/2e88f105/sail/src/test/java/org/apache/rya/rdftriplestore/inference/InferenceIT.java ---------------------------------------------------------------------- diff --git a/sail/src/test/java/org/apache/rya/rdftriplestore/inference/InferenceIT.java b/sail/src/test/java/org/apache/rya/rdftriplestore/inference/InferenceIT.java index c43c9e0..7d6f7d1 100644 --- a/sail/src/test/java/org/apache/rya/rdftriplestore/inference/InferenceIT.java +++ b/sail/src/test/java/org/apache/rya/rdftriplestore/inference/InferenceIT.java @@ -188,6 +188,43 @@ public class InferenceIT extends TestCase { } @Test + public void testAllValuesFromQuery() throws Exception { + final String ontology = "INSERT DATA { GRAPH <http://updated/test> {\n" + + " <urn:Cairn_Terrier> rdfs:subClassOf <urn:Terrier> .\n" + + " <urn:Terrier> rdfs:subClassOf <urn:Dog> ;\n" + + " owl:onProperty <urn:relative> ;\n" + + " owl:allValuesFrom <urn:Terrier> .\n" + + " <urn:Dog> rdfs:subClassOf [\n" + + " owl:onProperty <urn:portrays> ; owl:allValuesFrom <urn:FictionalDog>\n" + + " ] .\n" + + " <urn:parent> rdfs:subPropertyOf <urn:relative> .\n" + + "}}"; + conn.prepareUpdate(QueryLanguage.SPARQL, ontology).execute(); + inferenceEngine.refreshGraph(); + final String instances = "INSERT DATA { GRAPH <http://updated/test> {\n" + + " <urn:Rommy> a <urn:Cairn_Terrier> .\n" + + " <urn:Rommy> <urn:parent> <urn:Terry> .\n" + + " <urn:Terry> <urn:portrays> <urn:Toto> .\n" + + "}}"; + conn.prepareUpdate(QueryLanguage.SPARQL, instances).execute(); + conn.prepareTupleQuery(QueryLanguage.SPARQL, "SELECT ?x { ?x a <urn:Dog> }").evaluate(resultHandler); + Assert.assertEquals(2, solutions.size()); + Set<Value> answers = new HashSet<>(); + for (BindingSet solution : solutions) { + answers.add(solution.getBinding("x").getValue()); + } + Assert.assertTrue(answers.contains(vf.createURI("urn:Terry"))); + Assert.assertTrue(answers.contains(vf.createURI("urn:Rommy"))); + // If allValuesFrom inference were applied recursively, this triple wouldn't be needed: + conn.prepareUpdate(QueryLanguage.SPARQL, "INSERT DATA { GRAPH <http://updated/test> {\n" + + " <urn:Terry> a <urn:Cairn_Terrier> .\n" + + "}}").execute(); + conn.prepareTupleQuery(QueryLanguage.SPARQL, "SELECT ?x { ?x a <urn:FictionalDog> }").evaluate(resultHandler); + Assert.assertEquals(1, solutions.size()); + Assert.assertEquals(vf.createURI("urn:Toto"), solutions.get(0).getBinding("x").getValue()); + } + + @Test public void testHasValueTypeQuery() throws Exception { final String ontology = "INSERT DATA { GRAPH <http://updated/test> {\n" + " <urn:Biped> owl:onProperty <urn:walksOnLegs> ; owl:hasValue \"2\"^^<xsd:integer> . \n"
