You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@rya.apache.org by pu...@apache.org on 2017/08/24 22:27:38 UTC

incubator-rya git commit: RYA-300 Added owl:oneOf inference. Closes #215

Repository: incubator-rya
Updated Branches:
  refs/heads/master e9488ff69 -> 8431dfb78


RYA-300 Added owl:oneOf inference. Closes #215


Project: http://git-wip-us.apache.org/repos/asf/incubator-rya/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-rya/commit/8431dfb7
Tree: http://git-wip-us.apache.org/repos/asf/incubator-rya/tree/8431dfb7
Diff: http://git-wip-us.apache.org/repos/asf/incubator-rya/diff/8431dfb7

Branch: refs/heads/master
Commit: 8431dfb783bed4e6e5d434ec5decccceccefffd1
Parents: e9488ff
Author: eric.white <Er...@parsons.com>
Authored: Wed Aug 23 11:08:58 2017 -0400
Committer: pujav65 <pu...@gmail.com>
Committed: Thu Aug 24 18:27:52 2017 -0400

----------------------------------------------------------------------
 .../api/RdfCloudTripleStoreConfiguration.java   |  20 +++
 .../src/main/java/MongoRyaDirectExample.java    | 105 +++++++++++
 .../RdfCloudTripleStoreConnection.java          |   2 +
 .../inference/InferenceEngine.java              |  76 ++++++++
 .../rdftriplestore/inference/OneOfVisitor.java  |  77 +++++++++
 .../inference/InferenceEngineTest.java          |  84 +++++++++
 .../rdftriplestore/inference/InferenceIT.java   |  99 ++++++++++-
 .../inference/OneOfVisitorTest.java             | 172 +++++++++++++++++++
 8 files changed, 633 insertions(+), 2 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-rya/blob/8431dfb7/common/rya.api/src/main/java/org/apache/rya/api/RdfCloudTripleStoreConfiguration.java
----------------------------------------------------------------------
diff --git a/common/rya.api/src/main/java/org/apache/rya/api/RdfCloudTripleStoreConfiguration.java b/common/rya.api/src/main/java/org/apache/rya/api/RdfCloudTripleStoreConfiguration.java
index ee91e6b..eeb49b5 100644
--- a/common/rya.api/src/main/java/org/apache/rya/api/RdfCloudTripleStoreConfiguration.java
+++ b/common/rya.api/src/main/java/org/apache/rya/api/RdfCloudTripleStoreConfiguration.java
@@ -74,6 +74,7 @@ public abstract class RdfCloudTripleStoreConfiguration extends Configuration {
     public static final String STATS_PUSH_EMPTY_RDFTYPE_DOWN = "conf.stats.rdftype.down";
     public static final String INFER_INCLUDE_INTERSECTION_OF = "infer.include.intersectionof";
     public static final String INFER_INCLUDE_INVERSEOF = "infer.include.inverseof";
+    public static final String INFER_INCLUDE_ONE_OF = "infer.include.oneof";
     public static final String INFER_INCLUDE_SUBCLASSOF = "infer.include.subclassof";
     public static final String INFER_INCLUDE_SUBPROPOF = "infer.include.subpropof";
     public static final String INFER_INCLUDE_SYMMPROP = "infer.include.symmprop";
@@ -358,6 +359,25 @@ public abstract class RdfCloudTripleStoreConfiguration extends Configuration {
         setBoolean(INFER_INCLUDE_INVERSEOF, val);
     }
 
+    /**
+     * @return {@code true} if owl:oneOf inferencing is enabled.
+     * {@code false} otherwise. Defaults to {@code true} if nothing is
+     * specified.
+     */
+    public Boolean isInferOneOf() {
+        return getBoolean(INFER_INCLUDE_ONE_OF, true);
+    }
+
+    /**
+     * Sets whether owl:oneOf inferencing is enabled or disabled.
+     * @param value {@code true} if owl:oneOf inferencing is enabled.
+     * {@code false} otherwise.
+     */
+    public void setInferOneOf(final Boolean value) {
+        Preconditions.checkNotNull(value);
+        setBoolean(INFER_INCLUDE_ONE_OF, value);
+    }
+
     public Boolean isInferSubClassOf() {
         return getBoolean(INFER_INCLUDE_SUBCLASSOF, true);
     }

http://git-wip-us.apache.org/repos/asf/incubator-rya/blob/8431dfb7/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 342916a..a2611d0 100644
--- a/extras/indexingExample/src/main/java/MongoRyaDirectExample.java
+++ b/extras/indexingExample/src/main/java/MongoRyaDirectExample.java
@@ -120,6 +120,7 @@ public class MongoRyaDirectExample {
                 testPropertyChainInferenceAltRepresentation(conn, sail);
                 testAllValuesFromInference(conn, sail);
                 testIntersectionOfInference(conn, sail);
+                testOneOfInference(conn, sail);
             }
 
             log.info("TIME: " + (System.currentTimeMillis() - start) / 1000.);
@@ -572,6 +573,110 @@ public class MongoRyaDirectExample {
         Validate.isTrue(resultHandler.getCount() == 2);
     }
 
+    public static void testOneOfInference(final SailRepositoryConnection conn, final Sail sail) throws MalformedQueryException, RepositoryException, UpdateExecutionException, QueryEvaluationException, TupleQueryResultHandlerException, InferenceEngineException {
+        log.info("Adding Data");
+        final String instances = "INSERT DATA"
+                + "{ GRAPH <http://updated/test> {\n"
+                + "  <urn:FlopCard1> a <urn:Card> . \n"
+                + "    <urn:FlopCard1> <urn:HasRank> <urn:Ace> . \n"
+                + "    <urn:FlopCard1> <urn:HasSuit> <urn:Diamonds> . \n"
+                + "  <urn:FlopCard2> a <urn:Card> . \n"
+                + "    <urn:FlopCard2> <urn:HasRank> <urn:Ace> . \n"
+                + "    <urn:FlopCard2> <urn:HasSuit> <urn:Hearts> . \n"
+                + "  <urn:FlopCard3> a <urn:Card> . \n"
+                + "    <urn:FlopCard3> <urn:HasRank> <urn:King> . \n"
+                + "    <urn:FlopCard3> <urn:HasSuit> <urn:Spades> . \n"
+                + "  <urn:TurnCard> a <urn:Card> . \n"
+                + "    <urn:TurnCard> <urn:HasRank> <urn:10> . \n"
+                + "    <urn:TurnCard> <urn:HasSuit> <urn:Clubs> . \n"
+                + "  <urn:RiverCard> a <urn:Card> . \n"
+                + "    <urn:RiverCard> <urn:HasRank> <urn:Queen> . \n"
+                + "    <urn:RiverCard> <urn:HasSuit> <urn:Hearts> . \n"
+                + "}}";
+        Update update = conn.prepareUpdate(QueryLanguage.SPARQL, instances);
+        update.execute();
+        final String explicitQuery = "select distinct ?card { GRAPH <http://updated/test> {\n"
+                + "  ?card a <urn:Card> . \n"
+                + "  VALUES ?suit { <urn:Clubs> <urn:Diamonds> <urn:Hearts> <urn:Spades> } . \n"
+                + "  ?card <urn:HasSuit> ?suit . \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() == 5);
+        log.info("Adding owl:oneOf Schema");
+        // ONTOLOGY - :Suits oneOf (:Clubs, :Diamonds, :Hearts, :Spades)
+        // ONTOLOGY - :Ranks oneOf (:Ace, :1, :2, :3, :4, :5, :6, :7, :8, :9, :10, :Jack, :Queen, :King)
+        final String ontology = "INSERT DATA { GRAPH <http://updated/test> {\n"
+                + "  <urn:Suits> owl:oneOf _:bnodeS1 . \n"
+                + "  _:bnodeS1 rdf:first <urn:Clubs> . \n"
+                + "  _:bnodeS1 rdf:rest _:bnodeS2 . \n"
+                + "  _:bnodeS2 rdf:first <urn:Diamonds> . \n"
+                + "  _:bnodeS2 rdf:rest _:bnodeS3 . \n"
+                + "  _:bnodeS3 rdf:first <urn:Hearts> . \n"
+                + "  _:bnodeS3 rdf:rest _:bnodeS4 . \n"
+                + "  _:bnodeS4 rdf:first <urn:Spades> . \n"
+                + "  _:bnodeS4 rdf:rest rdf:nil . \n"
+                + "  <urn:Ranks> owl:oneOf _:bnodeR1 . \n"
+                + "  _:bnodeR1 rdf:first <urn:Ace> . \n"
+                + "  _:bnodeR1 rdf:rest _:bnodeR2 . \n"
+                + "  _:bnodeR2 rdf:first <urn:2> . \n"
+                + "  _:bnodeR2 rdf:rest _:bnodeR3 . \n"
+                + "  _:bnodeR3 rdf:first <urn:3> . \n"
+                + "  _:bnodeR3 rdf:rest _:bnodeR4 . \n"
+                + "  _:bnodeR4 rdf:first <urn:4> . \n"
+                + "  _:bnodeR4 rdf:rest _:bnodeR5 . \n"
+                + "  _:bnodeR5 rdf:first <urn:5> . \n"
+                + "  _:bnodeR5 rdf:rest _:bnodeR6 . \n"
+                + "  _:bnodeR6 rdf:first <urn:6> . \n"
+                + "  _:bnodeR6 rdf:rest _:bnodeR7 . \n"
+                + "  _:bnodeR7 rdf:first <urn:7> . \n"
+                + "  _:bnodeR7 rdf:rest _:bnodeR8 . \n"
+                + "  _:bnodeR8 rdf:first <urn:8> . \n"
+                + "  _:bnodeR8 rdf:rest _:bnodeR9 . \n"
+                + "  _:bnodeR9 rdf:first <urn:9> . \n"
+                + "  _:bnodeR9 rdf:rest _:bnodeR10 . \n"
+                + "  _:bnodeR10 rdf:first <urn:10> . \n"
+                + "  _:bnodeR10 rdf:rest _:bnodeR11 . \n"
+                + "  _:bnodeR11 rdf:first <urn:Jack> . \n"
+                + "  _:bnodeR11 rdf:rest _:bnodeR12 . \n"
+                + "  _:bnodeR12 rdf:first <urn:Queen> . \n"
+                + "  _:bnodeR12 rdf:rest _:bnodeR13 . \n"
+                + "  _:bnodeR13 rdf:first <urn:King> . \n"
+                + "  _:bnodeR13 rdf:rest rdf:nil . \n"
+                + "  <urn:Card> owl:intersectionOf (\n"
+                + "    [ owl:onProperty <urn:HasRank> ; owl:someValuesFrom <urn:Ranks> ]\n"
+                + "    [ owl:onProperty <urn:HasSuit> ; owl:someValuesFrom <urn:Suits> ]\n"
+                + "  ) . \n"
+                + "  <urn:HasRank> owl:range <urn:Ranks> . \n"
+                + "  <urn:HasSuit> owl:range <urn:Suits> . \n"
+                + "}}";
+        update = conn.prepareUpdate(QueryLanguage.SPARQL, ontology);
+        update.execute();
+        log.info("Running Inference-dependent Query without refreshing InferenceEngine");
+        resultHandler.resetCount();
+        final String inferQuery = "select distinct ?card { GRAPH <http://updated/test> {\n"
+                + "  ?card a <urn:Card> . \n"
+                + "  ?suit a <urn:Suits> . \n"
+                + "  ?card <urn:HasSuit> ?suit . \n"
+                + "}}";
+        tupleQuery = conn.prepareTupleQuery(QueryLanguage.SPARQL, inferQuery);
+        tupleQuery.evaluate(resultHandler);
+        log.info("Result count : " + resultHandler.getCount());
+        Validate.isTrue(resultHandler.getCount() == 0);
+        log.info("Refreshing InferenceEngine");
+        ((RdfCloudTripleStore) sail).getInferenceEngine().refreshGraph();
+        log.info("Re-running Inference-dependent Query");
+        resultHandler.resetCount();
+        resultHandler = new CountingResultHandler();
+        tupleQuery = conn.prepareTupleQuery(QueryLanguage.SPARQL, inferQuery);
+        tupleQuery.evaluate(resultHandler);
+        log.info("Result count : " + resultHandler.getCount());
+        Validate.isTrue(resultHandler.getCount() == 5);
+    }
+
     public static void testInfer(final SailRepositoryConnection conn, final Sail sail) throws MalformedQueryException, RepositoryException,
     UpdateExecutionException, QueryEvaluationException, TupleQueryResultHandlerException, InferenceEngineException {
 

http://git-wip-us.apache.org/repos/asf/incubator-rya/blob/8431dfb7/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 a64c0ec..dee5c8d 100644
--- a/sail/src/main/java/org/apache/rya/rdftriplestore/RdfCloudTripleStoreConnection.java
+++ b/sail/src/main/java/org/apache/rya/rdftriplestore/RdfCloudTripleStoreConnection.java
@@ -57,6 +57,7 @@ import org.apache.rya.rdftriplestore.inference.HasValueVisitor;
 import org.apache.rya.rdftriplestore.inference.InferenceEngine;
 import org.apache.rya.rdftriplestore.inference.IntersectionOfVisitor;
 import org.apache.rya.rdftriplestore.inference.InverseOfVisitor;
+import org.apache.rya.rdftriplestore.inference.OneOfVisitor;
 import org.apache.rya.rdftriplestore.inference.PropertyChainVisitor;
 import org.apache.rya.rdftriplestore.inference.SameAsVisitor;
 import org.apache.rya.rdftriplestore.inference.SubClassOfVisitor;
@@ -362,6 +363,7 @@ public class RdfCloudTripleStoreConnection extends SailConnectionBase {
                     tupleExpr.visit(new SubPropertyOfVisitor(queryConf, inferenceEngine));
                     tupleExpr.visit(new SubClassOfVisitor(queryConf, inferenceEngine));
                     tupleExpr.visit(new SameAsVisitor(queryConf, inferenceEngine));
+                    tupleExpr.visit(new OneOfVisitor(queryConf, inferenceEngine));
                 } catch (final Exception e) {
                     logger.error("Error encountered while visiting query node.", e);
                 }

http://git-wip-us.apache.org/repos/asf/incubator-rya/blob/8431dfb7/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 a2f8d63..936fd41 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
@@ -85,6 +85,7 @@ public class InferenceEngine {
     private Map<URI, Map<Resource, Value>> hasValueByProperty;
     private Map<Resource, Map<Resource, URI>> allValuesFromByValueType;
     private final ConcurrentHashMap<Resource, List<Set<Resource>>> intersections = new ConcurrentHashMap<>();
+    private final ConcurrentHashMap<Resource, Set<Resource>> enumerations = new ConcurrentHashMap<>();
 
     private RyaDAO<?> ryaDAO;
     private RdfCloudTripleStoreConfiguration conf;
@@ -203,6 +204,8 @@ public class InferenceEngine {
 
             refreshIntersectionOf();
 
+            refreshOneOf();
+
             iter = RyaDAOHelper.query(ryaDAO, null, RDF.TYPE, OWL.SYMMETRICPROPERTY, conf);
             final Set<URI> symProp = new HashSet<>();
             try {
@@ -761,6 +764,46 @@ public class InferenceEngine {
         }
     }
 
+    private void refreshOneOf() throws QueryEvaluationException {
+        final Map<Resource, Set<Resource>> enumTypes = new HashMap<>();
+
+        // First query for all the owl:oneOf's.
+        // If we have the following oneOf:
+        // :A owl:oneOf (:B, :C)
+        // It will be represented by triples following a pattern similar to:
+        // <:A> owl:oneOf _:bnode1 .
+        //  _:bnode1 rdf:first <:B> .
+        //  _:bnode1 rdf:rest _:bnode2 .
+        // _:bnode2 rdf:first <:C> .
+        // _:bnode2 rdf:rest rdf:nil .
+        ryaDaoQueryWrapper.queryAll(null, OWL.ONEOF, null, new RDFHandlerBase() {
+            @Override
+            public void handleStatement(final Statement statement) throws RDFHandlerException {
+                final Resource enumType = statement.getSubject();
+                // listHead will point to a type class of the enumeration.
+                final URI listHead = (URI) statement.getObject();
+                if (!enumTypes.containsKey(enumType)) {
+                    enumTypes.put(enumType, new LinkedHashSet<Resource>());
+                }
+
+                // listHead should point to a list of items that forms the
+                // enumeration.
+                try {
+                    final Set<Resource> enumeration = new LinkedHashSet<>(getList(listHead));
+                    if (!enumeration.isEmpty()) {
+                        // Add this enumeration for this type.
+                        enumTypes.get(enumType).addAll(enumeration);
+                    }
+                } catch (final QueryEvaluationException e) {
+                    throw new RDFHandlerException("Error getting enumeration list.", e);
+                }
+            }
+        });
+
+        enumerations.clear();
+        enumerations.putAll(enumTypes);
+    }
+
     /**
      * Queries for all items that are in a list of the form:
      * <pre>
@@ -1281,4 +1324,37 @@ public class InferenceEngine {
         }
         return null;
     }
+
+    /**
+     * For a given type, return all sets of types such that owl:oneOf
+     * restrictions on those properties could imply this type. A enumeration
+     * of all the types that are part of the specified class type.
+     * @param type The type (URI or bnode) to check against the known oneOf
+     * sets.
+     * @return A {@link Set} of {@link Resource} types that represents the
+     * enumeration of resources that belong to the class type.
+     * An empty set is returned if no enumerations were found for the specified
+     * type.
+     */
+    public Set<Resource> getEnumeration(final Resource type) {
+        if (enumerations != null) {
+            final Set<Resource> oneOfSet = enumerations.get(type);
+            if (oneOfSet != null) {
+                return oneOfSet;
+            }
+        }
+        return new LinkedHashSet<>();
+
+    }
+
+    /**
+     * Checks if the specified type is an enumerated type.
+     * @param type The type (URI or bnode) to check against the known oneOf
+     * sets.
+     * @return {@code true} if the type is an enumerated type. {@code false}
+     * otherwise.
+     */
+    public boolean isEnumeratedType(final Resource type) {
+        return enumerations != null && enumerations.containsKey(type);
+    }
 }

http://git-wip-us.apache.org/repos/asf/incubator-rya/blob/8431dfb7/sail/src/main/java/org/apache/rya/rdftriplestore/inference/OneOfVisitor.java
----------------------------------------------------------------------
diff --git a/sail/src/main/java/org/apache/rya/rdftriplestore/inference/OneOfVisitor.java b/sail/src/main/java/org/apache/rya/rdftriplestore/inference/OneOfVisitor.java
new file mode 100644
index 0000000..004a4b0
--- /dev/null
+++ b/sail/src/main/java/org/apache/rya/rdftriplestore/inference/OneOfVisitor.java
@@ -0,0 +1,77 @@
+/*
+ * 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.
+ */
+package org.apache.rya.rdftriplestore.inference;
+
+import java.util.LinkedHashSet;
+import java.util.Set;
+
+import org.apache.log4j.Logger;
+import org.apache.rya.api.RdfCloudTripleStoreConfiguration;
+import org.openrdf.model.Resource;
+import org.openrdf.model.vocabulary.RDF;
+import org.openrdf.query.BindingSet;
+import org.openrdf.query.algebra.BindingSetAssignment;
+import org.openrdf.query.algebra.StatementPattern;
+import org.openrdf.query.algebra.Var;
+import org.openrdf.query.algebra.evaluation.QueryBindingSet;
+
+/**
+ * Visitor for handling owl:oneOf inferencing on a node.
+ */
+public class OneOfVisitor extends AbstractInferVisitor {
+    private static final Logger log = Logger.getLogger(OneOfVisitor.class);
+
+    /**
+     * Creates a new instance of {@link OneOfVisitor}.
+     * @param conf the {@link RdfCloudeTripleStoreConfiguration}.
+     * @param inferenceEngine the {@link InferenceEngine}.
+     */
+    public OneOfVisitor(final RdfCloudTripleStoreConfiguration conf, final InferenceEngine inferenceEngine) {
+        super(conf, inferenceEngine);
+        include = conf.isInferOneOf();
+    }
+
+    @Override
+    protected void meetSP(final StatementPattern node) throws Exception {
+        final Var subVar = node.getSubjectVar();
+        final Var predVar = node.getPredicateVar();
+        final Var objVar = node.getObjectVar();
+        final Var conVar = node.getContextVar();
+        if (predVar != null && objVar != null && objVar.getValue() != null && objVar.getValue() instanceof Resource && RDF.TYPE.equals(predVar.getValue()) && !EXPANDED.equals(conVar)) {
+            final Resource object = (Resource) objVar.getValue();
+            if (inferenceEngine.isEnumeratedType(object)) {
+                final Set<BindingSet> solutions = new LinkedHashSet<>();
+                final Set<Resource> enumeration = inferenceEngine.getEnumeration(object);
+                for (final Resource enumType : enumeration) {
+                    final QueryBindingSet qbs = new QueryBindingSet();
+                    qbs.addBinding(subVar.getName(), enumType);
+                    solutions.add(qbs);
+                }
+
+                if (!solutions.isEmpty()) {
+                    final BindingSetAssignment enumNode = new BindingSetAssignment();
+                    enumNode.setBindingSets(solutions);
+
+                    node.replaceWith(enumNode);
+                    log.trace("Replacing node with inferred one of enumeration: " + enumNode);
+                }
+            }
+        }
+    }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-rya/blob/8431dfb7/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 10edf49..f290324 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
@@ -438,4 +438,88 @@ public class InferenceEngineTest extends TestCase {
         Assert.assertTrue(momSuperClassUris.contains(mother));
         Assert.assertTrue(momSuperClassUris.contains(mom));
     }
+
+    @Test
+    public void testOneOf() throws Exception {
+        final String ontology = "INSERT DATA { GRAPH <http://updated/test> {\n"
+                + "  <urn:Suits> owl:oneOf _:bnodeS1 . \n"
+                + "  _:bnodeS1 rdf:first <urn:Clubs> . \n"
+                + "  _:bnodeS1 rdf:rest _:bnodeS2 . \n"
+                + "  _:bnodeS2 rdf:first <urn:Diamonds> . \n"
+                + "  _:bnodeS2 rdf:rest _:bnodeS3 . \n"
+                + "  _:bnodeS3 rdf:first <urn:Hearts> . \n"
+                + "  _:bnodeS3 rdf:rest _:bnodeS4 . \n"
+                + "  _:bnodeS4 rdf:first <urn:Spades> . \n"
+                + "  _:bnodeS4 rdf:rest rdf:nil . \n"
+                + "  <urn:Ranks> owl:oneOf _:bnodeR1 . \n"
+                + "  _:bnodeR1 rdf:first <urn:Ace> . \n"
+                + "  _:bnodeR1 rdf:rest _:bnodeR2 . \n"
+                + "  _:bnodeR2 rdf:first <urn:2> . \n"
+                + "  _:bnodeR2 rdf:rest _:bnodeR3 . \n"
+                + "  _:bnodeR3 rdf:first <urn:3> . \n"
+                + "  _:bnodeR3 rdf:rest _:bnodeR4 . \n"
+                + "  _:bnodeR4 rdf:first <urn:4> . \n"
+                + "  _:bnodeR4 rdf:rest _:bnodeR5 . \n"
+                + "  _:bnodeR5 rdf:first <urn:5> . \n"
+                + "  _:bnodeR5 rdf:rest _:bnodeR6 . \n"
+                + "  _:bnodeR6 rdf:first <urn:6> . \n"
+                + "  _:bnodeR6 rdf:rest _:bnodeR7 . \n"
+                + "  _:bnodeR7 rdf:first <urn:7> . \n"
+                + "  _:bnodeR7 rdf:rest _:bnodeR8 . \n"
+                + "  _:bnodeR8 rdf:first <urn:8> . \n"
+                + "  _:bnodeR8 rdf:rest _:bnodeR9 . \n"
+                + "  _:bnodeR9 rdf:first <urn:9> . \n"
+                + "  _:bnodeR9 rdf:rest _:bnodeR10 . \n"
+                + "  _:bnodeR10 rdf:first <urn:10> . \n"
+                + "  _:bnodeR10 rdf:rest _:bnodeR11 . \n"
+                + "  _:bnodeR11 rdf:first <urn:Jack> . \n"
+                + "  _:bnodeR11 rdf:rest _:bnodeR12 . \n"
+                + "  _:bnodeR12 rdf:first <urn:Queen> . \n"
+                + "  _:bnodeR12 rdf:rest _:bnodeR13 . \n"
+                + "  _:bnodeR13 rdf:first <urn:King> . \n"
+                + "  _:bnodeR13 rdf:rest rdf:nil . \n"
+                + "}}";
+
+        conn.prepareUpdate(QueryLanguage.SPARQL, ontology).execute();
+        inferenceEngine.refreshGraph();
+
+        final URI suits = vf.createURI("urn:Suits");
+        final URI ranks = vf.createURI("urn:Ranks");
+
+        final URI clubs = vf.createURI("urn:Clubs");
+        final URI diamonds = vf.createURI("urn:Diamonds");
+        final URI hearts = vf.createURI("urn:Hearts");
+        final URI spades = vf.createURI("urn:Spades");
+
+        final URI ace = vf.createURI("urn:Ace");
+        final URI two = vf.createURI("urn:2");
+        final URI three = vf.createURI("urn:3");
+        final URI four = vf.createURI("urn:4");
+        final URI five = vf.createURI("urn:5");
+        final URI six = vf.createURI("urn:6");
+        final URI seven = vf.createURI("urn:7");
+        final URI eight = vf.createURI("urn:8");
+        final URI nine = vf.createURI("urn:9");
+        final URI ten = vf.createURI("urn:10");
+        final URI jack = vf.createURI("urn:Jack");
+        final URI queen = vf.createURI("urn:Queen");
+        final URI king = vf.createURI("urn:King");
+
+        final URI joker = vf.createURI("urn:Joker");
+
+        final boolean isJokerEnumeratedType = inferenceEngine.isEnumeratedType(joker);
+        Assert.assertFalse(isJokerEnumeratedType);
+
+        final boolean isSuitsEnumeratedType = inferenceEngine.isEnumeratedType(suits);
+        Assert.assertTrue(isSuitsEnumeratedType);
+        final Set<Resource> enumerationImplyingSuits = Sets.newHashSet(clubs, diamonds, hearts, spades);
+        final Set<Resource> actualCardSuits = inferenceEngine.getEnumeration(suits);
+        Assert.assertEquals(enumerationImplyingSuits, actualCardSuits);
+
+        final boolean isRanksEnumeratedType = inferenceEngine.isEnumeratedType(ranks);
+        Assert.assertTrue(isRanksEnumeratedType);
+        final Set<Resource> enumerationImplyingRanks = Sets.newHashSet(ace, two, three, four, five, six, seven, eight, nine, ten, jack, queen, king);
+        final Set<Resource> actualCardRanks = inferenceEngine.getEnumeration(ranks);
+        Assert.assertEquals(enumerationImplyingRanks, actualCardRanks);
+   }
 }

http://git-wip-us.apache.org/repos/asf/incubator-rya/blob/8431dfb7/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 dcda8a9..1fcfa2c 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
@@ -212,8 +212,8 @@ public class InferenceIT extends TestCase {
         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) {
+        final Set<Value> answers = new HashSet<>();
+        for (final BindingSet solution : solutions) {
             answers.add(solution.getBinding("x").getValue());
         }
         Assert.assertTrue(answers.contains(vf.createURI("urn:Terry")));
@@ -400,4 +400,99 @@ public class InferenceIT extends TestCase {
         expectedMen.add(new ListBindingSet(varNames, vf.createURI("urn:Bob")));
         Assert.assertEquals(expectedMen, new HashSet<>(solutions));
     }
+
+    @Test
+    public void testOneOfQuery() throws Exception {
+        final String ontology = "INSERT DATA { GRAPH <http://updated/test> {\n"
+                + "  <urn:Suits> owl:oneOf _:bnodeS1 . \n"
+                + "  _:bnodeS1 rdf:first <urn:Clubs> . \n"
+                + "  _:bnodeS1 rdf:rest _:bnodeS2 . \n"
+                + "  _:bnodeS2 rdf:first <urn:Diamonds> . \n"
+                + "  _:bnodeS2 rdf:rest _:bnodeS3 . \n"
+                + "  _:bnodeS3 rdf:first <urn:Hearts> . \n"
+                + "  _:bnodeS3 rdf:rest _:bnodeS4 . \n"
+                + "  _:bnodeS4 rdf:first <urn:Spades> . \n"
+                + "  _:bnodeS4 rdf:rest rdf:nil . \n"
+                + "  <urn:Ranks> owl:oneOf _:bnodeR1 . \n"
+                + "  _:bnodeR1 rdf:first <urn:Ace> . \n"
+                + "  _:bnodeR1 rdf:rest _:bnodeR2 . \n"
+                + "  _:bnodeR2 rdf:first <urn:2> . \n"
+                + "  _:bnodeR2 rdf:rest _:bnodeR3 . \n"
+                + "  _:bnodeR3 rdf:first <urn:3> . \n"
+                + "  _:bnodeR3 rdf:rest _:bnodeR4 . \n"
+                + "  _:bnodeR4 rdf:first <urn:4> . \n"
+                + "  _:bnodeR4 rdf:rest _:bnodeR5 . \n"
+                + "  _:bnodeR5 rdf:first <urn:5> . \n"
+                + "  _:bnodeR5 rdf:rest _:bnodeR6 . \n"
+                + "  _:bnodeR6 rdf:first <urn:6> . \n"
+                + "  _:bnodeR6 rdf:rest _:bnodeR7 . \n"
+                + "  _:bnodeR7 rdf:first <urn:7> . \n"
+                + "  _:bnodeR7 rdf:rest _:bnodeR8 . \n"
+                + "  _:bnodeR8 rdf:first <urn:8> . \n"
+                + "  _:bnodeR8 rdf:rest _:bnodeR9 . \n"
+                + "  _:bnodeR9 rdf:first <urn:9> . \n"
+                + "  _:bnodeR9 rdf:rest _:bnodeR10 . \n"
+                + "  _:bnodeR10 rdf:first <urn:10> . \n"
+                + "  _:bnodeR10 rdf:rest _:bnodeR11 . \n"
+                + "  _:bnodeR11 rdf:first <urn:Jack> . \n"
+                + "  _:bnodeR11 rdf:rest _:bnodeR12 . \n"
+                + "  _:bnodeR12 rdf:first <urn:Queen> . \n"
+                + "  _:bnodeR12 rdf:rest _:bnodeR13 . \n"
+                + "  _:bnodeR13 rdf:first <urn:King> . \n"
+                + "  _:bnodeR13 rdf:rest rdf:nil . \n"
+                + "  <urn:Card> owl:intersectionOf (\n"
+                + "    [ owl:onProperty <urn:HasRank> ; owl:someValuesFrom <urn:Ranks> ]\n"
+                + "    [ owl:onProperty <urn:HasSuit> ; owl:someValuesFrom <urn:Suits> ]\n"
+                + "  ) . \n"
+                + "  <urn:HasRank> owl:range <urn:Ranks> . \n"
+                + "  <urn:HasSuit> owl:range <urn:Suits> . \n"
+                + "}}";
+        final String instances = "INSERT DATA { GRAPH <http://updated/test> {\n"
+                + "  <urn:FlopCard1> a <urn:Card> . \n"
+                + "    <urn:FlopCard1> <urn:HasRank> <urn:Ace> . \n"
+                + "    <urn:FlopCard1> <urn:HasSuit> <urn:Diamonds> . \n"
+                + "  <urn:FlopCard2> a <urn:Card> . \n"
+                + "    <urn:FlopCard2> <urn:HasRank> <urn:Ace> . \n"
+                + "    <urn:FlopCard2> <urn:HasSuit> <urn:Hearts> . \n"
+                + "  <urn:FlopCard3> a <urn:Card> . \n"
+                + "    <urn:FlopCard3> <urn:HasRank> <urn:King> . \n"
+                + "    <urn:FlopCard3> <urn:HasSuit> <urn:Spades> . \n"
+                + "  <urn:TurnCard> a <urn:Card> . \n"
+                + "    <urn:TurnCard> <urn:HasRank> <urn:10> . \n"
+                + "    <urn:TurnCard> <urn:HasSuit> <urn:Clubs> . \n"
+                + "  <urn:RiverCard> a <urn:Card> . \n"
+                + "    <urn:RiverCard> <urn:HasRank> <urn:Queen> . \n"
+                + "    <urn:RiverCard> <urn:HasSuit> <urn:Hearts> . \n"
+                + "}}";
+        conn.prepareUpdate(QueryLanguage.SPARQL, ontology).execute();
+        conn.prepareUpdate(QueryLanguage.SPARQL, instances).execute();
+        inferenceEngine.refreshGraph();
+
+        final List<String> varNames = new LinkedList<>();
+        varNames.add("card");
+
+        // Find all cards with a <urn:Suits> type (expect 5 results)
+        final String cardSuitQuery = "SELECT ?card { GRAPH <http://updated/test> { ?card a <urn:Card> . ?suit a <urn:Suits> . ?card <urn:HasSuit> ?suit} } \n";
+        conn.prepareTupleQuery(QueryLanguage.SPARQL, cardSuitQuery).evaluate(resultHandler);
+        final Set<BindingSet> expectedCardSuits = new HashSet<>();
+        expectedCardSuits.add(new ListBindingSet(varNames, vf.createURI("urn:FlopCard1")));
+        expectedCardSuits.add(new ListBindingSet(varNames, vf.createURI("urn:FlopCard2")));
+        expectedCardSuits.add(new ListBindingSet(varNames, vf.createURI("urn:FlopCard3")));
+        expectedCardSuits.add(new ListBindingSet(varNames, vf.createURI("urn:TurnCard")));
+        expectedCardSuits.add(new ListBindingSet(varNames, vf.createURI("urn:RiverCard")));
+        Assert.assertEquals(expectedCardSuits.size(), solutions.size());
+        Assert.assertEquals(expectedCardSuits, new HashSet<>(solutions));
+
+        // Find all cards with a <urn:Ranks> type (expect 5 results)
+        final String cardRankQuery = "SELECT ?card { GRAPH <http://updated/test> { ?card a <urn:Card> . ?rank a <urn:Ranks> . ?card <urn:HasRank> ?rank} } \n";
+        conn.prepareTupleQuery(QueryLanguage.SPARQL, cardRankQuery).evaluate(resultHandler);
+        final Set<BindingSet> expectedCardRanks = new HashSet<>();
+        expectedCardRanks.add(new ListBindingSet(varNames, vf.createURI("urn:FlopCard1")));
+        expectedCardRanks.add(new ListBindingSet(varNames, vf.createURI("urn:FlopCard2")));
+        expectedCardRanks.add(new ListBindingSet(varNames, vf.createURI("urn:FlopCard3")));
+        expectedCardRanks.add(new ListBindingSet(varNames, vf.createURI("urn:TurnCard")));
+        expectedCardRanks.add(new ListBindingSet(varNames, vf.createURI("urn:RiverCard")));
+        Assert.assertEquals(expectedCardRanks.size(), solutions.size());
+        Assert.assertEquals(expectedCardRanks, new HashSet<>(solutions));
+    }
 }

http://git-wip-us.apache.org/repos/asf/incubator-rya/blob/8431dfb7/sail/src/test/java/org/apache/rya/rdftriplestore/inference/OneOfVisitorTest.java
----------------------------------------------------------------------
diff --git a/sail/src/test/java/org/apache/rya/rdftriplestore/inference/OneOfVisitorTest.java b/sail/src/test/java/org/apache/rya/rdftriplestore/inference/OneOfVisitorTest.java
new file mode 100644
index 0000000..484c8bc
--- /dev/null
+++ b/sail/src/test/java/org/apache/rya/rdftriplestore/inference/OneOfVisitorTest.java
@@ -0,0 +1,172 @@
+/*
+ * 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.
+ */
+package org.apache.rya.rdftriplestore.inference;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import java.util.Iterator;
+import java.util.Set;
+
+import org.apache.rya.accumulo.AccumuloRdfConfiguration;
+import org.junit.Test;
+import org.openrdf.model.Resource;
+import org.openrdf.model.URI;
+import org.openrdf.model.Value;
+import org.openrdf.model.ValueFactory;
+import org.openrdf.model.impl.ValueFactoryImpl;
+import org.openrdf.model.vocabulary.RDF;
+import org.openrdf.query.Binding;
+import org.openrdf.query.BindingSet;
+import org.openrdf.query.algebra.BindingSetAssignment;
+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.Var;
+import org.openrdf.query.algebra.evaluation.QueryBindingSet;
+
+import com.beust.jcommander.internal.Lists;
+import com.google.common.collect.Sets;
+
+/**
+ * Tests the methods of {@link OneOfVisitor}.
+ */
+public class OneOfVisitorTest {
+    private final AccumuloRdfConfiguration conf = new AccumuloRdfConfiguration();
+    private static final ValueFactory VF = new ValueFactoryImpl();
+
+    private static final URI SUITS = VF.createURI("urn:Suits");
+    private static final URI RANKS = VF.createURI("urn:Ranks");
+
+    // Definition #1: :Suits owl:oneOf(:Clubs, :Diamonds, :Hearts, :Spades)
+    private static final URI CLUBS = VF.createURI("urn:Clubs");
+    private static final URI DIAMONDS = VF.createURI("urn:Diamonds");
+    private static final URI HEARTS = VF.createURI("urn:Hearts");
+    private static final URI SPADES = VF.createURI("urn:Spades");
+
+    // Definition #2: :Ranks owl:oneOf(:Ace, :2, :3, :4, :5, :6, :7, :8, :9, :10, :Jack, :Queen, :King)
+    private static final URI ACE = VF.createURI("urn:Ace");
+    private static final URI TWO = VF.createURI("urn:2");
+    private static final URI THREE = VF.createURI("urn:3");
+    private static final URI FOUR = VF.createURI("urn:4");
+    private static final URI FIVE = VF.createURI("urn:5");
+    private static final URI SIX = VF.createURI("urn:6");
+    private static final URI SEVEN = VF.createURI("urn:7");
+    private static final URI EIGHT = VF.createURI("urn:8");
+    private static final URI NINE = VF.createURI("urn:9");
+    private static final URI TEN = VF.createURI("urn:10");
+    private static final URI JACK = VF.createURI("urn:Jack");
+    private static final URI QUEEN = VF.createURI("urn:Queen");
+    private static final URI KING = VF.createURI("urn:King");
+
+    private static final Set<Resource> CARD_SUIT_ENUMERATION =
+        Sets.newLinkedHashSet(
+            Lists.newArrayList(CLUBS, DIAMONDS, HEARTS, SPADES)
+        );
+    private static final Set<Resource> CARD_RANK_ENUMERATION =
+        Sets.newLinkedHashSet(
+            Lists.newArrayList(
+                ACE, TWO, THREE, FOUR, FIVE, SIX, SEVEN, EIGHT, NINE, TEN,
+                JACK, QUEEN, KING
+            )
+        );
+
+    @Test
+    public void testOneOf() throws Exception {
+        // Configure a mock instance engine with an ontology:
+        final InferenceEngine inferenceEngine = mock(InferenceEngine.class);
+        when(inferenceEngine.isEnumeratedType(SUITS)).thenReturn(true);
+        when(inferenceEngine.getEnumeration(SUITS)).thenReturn(CARD_SUIT_ENUMERATION);
+        when(inferenceEngine.isEnumeratedType(RANKS)).thenReturn(true);
+        when(inferenceEngine.getEnumeration(RANKS)).thenReturn(CARD_RANK_ENUMERATION);
+        // Query for a  Suits and rewrite using the visitor:
+        final Projection query = new Projection(
+                new StatementPattern(new Var("s"), new Var("p", RDF.TYPE), new Var("o", SUITS)),
+                new ProjectionElemList(new ProjectionElem("s", "subject")));
+        query.visit(new OneOfVisitor(conf, inferenceEngine));
+        // Expected structure: BindingSetAssignment containing the enumeration:
+        // BindingSetAssignment(CLUBS, DIAMONDS, HEARTS, SPADES)
+        // Collect the arguments to the BindingSetAssignment:
+        assertTrue(query.getArg() instanceof BindingSetAssignment);
+        final BindingSetAssignment bsa = (BindingSetAssignment) query.getArg();
+        final Iterable<BindingSet> iterable = bsa.getBindingSets();
+        final Iterator<BindingSet> iter = iterable.iterator();
+
+        assertBindingSet(iter, CARD_SUIT_ENUMERATION.iterator());
+
+        // Query for a Ranks and rewrite using the visitor:
+        final Projection query2 = new Projection(
+                new StatementPattern(new Var("s"), new Var("p", RDF.TYPE), new Var("o", RANKS)),
+                new ProjectionElemList(new ProjectionElem("s", "subject")));
+        query2.visit(new OneOfVisitor(conf, inferenceEngine));
+        // Expected structure: BindingSetAssignment containing the enumeration:
+        // BindingSetAssignment(ACE, 2, 3, 4, 5, 6, 7, 8, 9, 10, JACK, QUEEN, KING)
+        // Collect the arguments to the BindingSetAssignment:
+        assertTrue(query2.getArg() instanceof BindingSetAssignment);
+        final BindingSetAssignment bsa2 = (BindingSetAssignment) query2.getArg();
+        final Iterable<BindingSet> iterable2 = bsa2.getBindingSets();
+        final Iterator<BindingSet> iter2 = iterable2.iterator();
+
+        assertBindingSet(iter2, CARD_RANK_ENUMERATION.iterator());
+    }
+
+    @Test
+    public void testOneOfDisabled() throws Exception {
+        // Configure a mock instance engine with an ontology:
+        final InferenceEngine inferenceEngine = mock(InferenceEngine.class);
+        when(inferenceEngine.isEnumeratedType(SUITS)).thenReturn(true);
+        when(inferenceEngine.getEnumeration(SUITS)).thenReturn(CARD_SUIT_ENUMERATION);
+        when(inferenceEngine.isEnumeratedType(RANKS)).thenReturn(true);
+        when(inferenceEngine.getEnumeration(RANKS)).thenReturn(CARD_RANK_ENUMERATION);
+
+        // Query for a Suits and rewrite using the visitor:
+        final Projection query = new Projection(
+                new StatementPattern(new Var("s"), new Var("p", RDF.TYPE), new Var("o", SUITS)),
+                new ProjectionElemList(new ProjectionElem("s", "subject")));
+
+        final AccumuloRdfConfiguration disabledConf = conf.clone();
+        disabledConf.setInferOneOf(false);
+
+        query.visit(new OneOfVisitor(disabledConf, inferenceEngine));
+
+        // Expected structure: the original statement:
+        assertTrue(query.getArg() instanceof StatementPattern);
+        final StatementPattern actualCardSuitSp = (StatementPattern) query.getArg();
+        final StatementPattern expectedCardSuitSp = new StatementPattern(new Var("s"), new Var("p", RDF.TYPE), new Var("o", SUITS));
+        assertEquals(expectedCardSuitSp, actualCardSuitSp);
+    }
+
+    private static void assertBindingSet(final Iterator<BindingSet> bindingSetIter, final Iterator<Resource> expectedValues) {
+        while (expectedValues.hasNext()) {
+            final Resource expectedValue = expectedValues.next();
+            assertTrue(bindingSetIter.hasNext());
+            final BindingSet bindingSet = bindingSetIter.next();
+            assertTrue(bindingSet instanceof QueryBindingSet);
+            assertEquals(1, bindingSet.getBindingNames().size());
+            final Binding binding = bindingSet.getBinding("s");
+            assertNotNull(binding);
+            final Value actualValue = binding.getValue();
+            assertEquals(expectedValue, actualValue);
+        }
+    }
+}
\ No newline at end of file