You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@rya.apache.org by ca...@apache.org on 2018/01/11 14:52:05 UTC
[6/6] incubator-rya git commit: RYA-417 Batch forward-chaining rules
engine. Closes #255.
RYA-417 Batch forward-chaining rules engine. Closes #255.
Project: http://git-wip-us.apache.org/repos/asf/incubator-rya/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-rya/commit/9f611019
Tree: http://git-wip-us.apache.org/repos/asf/incubator-rya/tree/9f611019
Diff: http://git-wip-us.apache.org/repos/asf/incubator-rya/diff/9f611019
Branch: refs/heads/master
Commit: 9f611019fca682148483ae31a1123b8e92b5fda2
Parents: d5ebb73
Author: Jesse Hatfield <je...@parsons.com>
Authored: Fri Dec 22 12:02:33 2017 -0500
Committer: caleb <ca...@parsons.com>
Committed: Thu Jan 11 09:50:38 2018 -0500
----------------------------------------------------------------------
.../AggregationPipelineQueryNode.java | 8 +-
.../mongodb/aggregation/PipelineQueryIT.java | 32 ++
.../SparqlToPipelineTransformVisitorTest.java | 14 +
.../apache/rya/sail/config/RyaSailFactory.java | 64 ++--
extras/pom.xml | 1 +
extras/rya.forwardchain/pom.xml | 119 +++++++
.../rya/forwardchain/ForwardChainConstants.java | 37 ++
.../rya/forwardchain/ForwardChainException.java | 54 +++
.../batch/AbstractForwardChainTool.java | 148 ++++++++
.../batch/ForwardChainSpinTool.java | 77 +++++
.../rule/AbstractConstructRule.java | 65 ++++
.../rule/AbstractInconsistencyRule.java | 51 +++
.../forwardchain/rule/AbstractUpdateRule.java | 34 ++
.../forwardchain/rule/AntecedentVisitor.java | 51 +++
.../rule/ConstructConsequentVisitor.java | 138 ++++++++
.../org/apache/rya/forwardchain/rule/Rule.java | 75 ++++
.../apache/rya/forwardchain/rule/Ruleset.java | 166 +++++++++
.../forwardchain/rule/SpinConstructRule.java | 344 +++++++++++++++++++
.../strategy/AbstractForwardChainStrategy.java | 82 +++++
.../strategy/AbstractRuleExecutionStrategy.java | 108 ++++++
.../strategy/MongoPipelineStrategy.java | 276 +++++++++++++++
.../strategy/RoundRobinStrategy.java | 212 ++++++++++++
.../strategy/SailExecutionStrategy.java | 223 ++++++++++++
.../rya/forwardchain/batch/MongoSpinIT.java | 169 +++++++++
.../rule/AntecedentVisitorTest.java | 156 +++++++++
.../rule/ConstructConsequentVisitorTest.java | 164 +++++++++
.../rya/forwardchain/rule/RulesetTest.java | 137 ++++++++
.../rule/SpinConstructRuleTest.java | 213 ++++++++++++
.../src/test/resources/data.ttl | 56 +++
.../src/test/resources/owlrl.ttl | 106 ++++++
.../src/test/resources/query.sparql | 32 ++
.../src/test/resources/university.ttl | 58 ++++
32 files changed, 3442 insertions(+), 28 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/incubator-rya/blob/9f611019/dao/mongodb.rya/src/main/java/org/apache/rya/mongodb/aggregation/AggregationPipelineQueryNode.java
----------------------------------------------------------------------
diff --git a/dao/mongodb.rya/src/main/java/org/apache/rya/mongodb/aggregation/AggregationPipelineQueryNode.java b/dao/mongodb.rya/src/main/java/org/apache/rya/mongodb/aggregation/AggregationPipelineQueryNode.java
index 7a84f5d..45092e4 100644
--- a/dao/mongodb.rya/src/main/java/org/apache/rya/mongodb/aggregation/AggregationPipelineQueryNode.java
+++ b/dao/mongodb.rya/src/main/java/org/apache/rya/mongodb/aggregation/AggregationPipelineQueryNode.java
@@ -531,7 +531,9 @@ public class AggregationPipelineQueryNode extends ExternalSet {
* The number of documents produced by the pipeline after this operation
* will be the number of documents entering this stage (the number of
* intermediate results) multiplied by the number of
- * {@link ProjectionElemList}s supplied here.
+ * {@link ProjectionElemList}s supplied here. Empty projections are
+ * unsupported; if one or more projections given binds zero variables, then
+ * the pipeline will be unchanged and the method will return false.
* @param projections One or more projections, i.e. mappings from the result
* at this stage of the query into a set of variables.
* @return true if the projection(s) were added to the pipeline.
@@ -544,6 +546,10 @@ public class AggregationPipelineQueryNode extends ExternalSet {
Set<String> bindingNamesUnion = new HashSet<>();
Set<String> bindingNamesIntersection = null;
for (ProjectionElemList projection : projections) {
+ if (projection.getElements().isEmpty()) {
+ // Empty projections are unsupported -- fail when seen
+ return false;
+ }
Document valueDoc = new Document();
Document hashDoc = new Document();
Document typeDoc = new Document();
http://git-wip-us.apache.org/repos/asf/incubator-rya/blob/9f611019/dao/mongodb.rya/src/test/java/org/apache/rya/mongodb/aggregation/PipelineQueryIT.java
----------------------------------------------------------------------
diff --git a/dao/mongodb.rya/src/test/java/org/apache/rya/mongodb/aggregation/PipelineQueryIT.java b/dao/mongodb.rya/src/test/java/org/apache/rya/mongodb/aggregation/PipelineQueryIT.java
index 45855a0..0552ac0 100644
--- a/dao/mongodb.rya/src/test/java/org/apache/rya/mongodb/aggregation/PipelineQueryIT.java
+++ b/dao/mongodb.rya/src/test/java/org/apache/rya/mongodb/aggregation/PipelineQueryIT.java
@@ -49,8 +49,10 @@ import org.openrdf.model.vocabulary.RDFS;
import org.openrdf.model.vocabulary.XMLSchema;
import org.openrdf.query.BindingSet;
import org.openrdf.query.QueryEvaluationException;
+import org.openrdf.query.algebra.Projection;
import org.openrdf.query.algebra.QueryRoot;
import org.openrdf.query.algebra.evaluation.QueryBindingSet;
+import org.openrdf.query.impl.EmptyBindingSet;
import org.openrdf.query.impl.ListBindingSet;
import org.openrdf.query.parser.sparql.SPARQLParser;
@@ -135,6 +137,36 @@ public class PipelineQueryIT extends MongoITBase {
}
@Test
+ public void testNoVariableSP() throws Exception {
+ // Insert data
+ insert(OWL.THING, RDF.TYPE, OWL.CLASS);
+ insert(FOAF.PERSON, RDF.TYPE, OWL.CLASS, 1);
+ insert(FOAF.PERSON, RDFS.SUBCLASSOF, OWL.THING);
+ insert(VF.createURI("urn:Alice"), RDF.TYPE, FOAF.PERSON);
+ dao.flush();
+ // Define query and expected results
+ final String query = "SELECT * WHERE {\n"
+ + " owl:Thing a owl:Class .\n"
+ + "}";
+ Multiset<BindingSet> expectedSolutions = HashMultiset.create();
+ expectedSolutions.add(new EmptyBindingSet());
+ // Execute pipeline and verify results
+ QueryRoot queryTree = new QueryRoot(PARSER.parseQuery(query, null).getTupleExpr());
+ SparqlToPipelineTransformVisitor visitor = new SparqlToPipelineTransformVisitor(getRyaCollection());
+ queryTree.visit(visitor);
+ Assert.assertTrue(queryTree.getArg() instanceof Projection);
+ Projection projection = (Projection) queryTree.getArg();
+ Assert.assertTrue(projection.getArg() instanceof AggregationPipelineQueryNode);
+ AggregationPipelineQueryNode pipelineNode = (AggregationPipelineQueryNode) projection.getArg();
+ Multiset<BindingSet> solutions = HashMultiset.create();
+ CloseableIteration<BindingSet, QueryEvaluationException> iter = pipelineNode.evaluate(new QueryBindingSet());
+ while (iter.hasNext()) {
+ solutions.add(iter.next());
+ }
+ Assert.assertEquals(expectedSolutions, solutions);
+ }
+
+ @Test
public void testJoinTwoSharedVariables() throws Exception {
// Insert data
URI person = VF.createURI("urn:Person");
http://git-wip-us.apache.org/repos/asf/incubator-rya/blob/9f611019/dao/mongodb.rya/src/test/java/org/apache/rya/mongodb/aggregation/SparqlToPipelineTransformVisitorTest.java
----------------------------------------------------------------------
diff --git a/dao/mongodb.rya/src/test/java/org/apache/rya/mongodb/aggregation/SparqlToPipelineTransformVisitorTest.java b/dao/mongodb.rya/src/test/java/org/apache/rya/mongodb/aggregation/SparqlToPipelineTransformVisitorTest.java
index cc9349b..506b8af 100644
--- a/dao/mongodb.rya/src/test/java/org/apache/rya/mongodb/aggregation/SparqlToPipelineTransformVisitorTest.java
+++ b/dao/mongodb.rya/src/test/java/org/apache/rya/mongodb/aggregation/SparqlToPipelineTransformVisitorTest.java
@@ -29,6 +29,7 @@ import org.mockito.Mockito;
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.Extension;
import org.openrdf.query.algebra.ExtensionElem;
@@ -153,6 +154,19 @@ public class SparqlToPipelineTransformVisitorTest {
}
@Test
+ public void testEmptyProjection() throws Exception {
+ StatementPattern isClass = new StatementPattern(constant(UNDERGRAD), constant(RDF.TYPE), constant(OWL.CLASS));
+ QueryRoot queryTree = new QueryRoot(new Projection(isClass, new ProjectionElemList()));
+ SparqlToPipelineTransformVisitor visitor = new SparqlToPipelineTransformVisitor(collection);
+ queryTree.visit(visitor);
+ Assert.assertTrue(queryTree.getArg() instanceof Projection);
+ Projection projectNode = (Projection) queryTree.getArg();
+ Assert.assertTrue(projectNode.getArg() instanceof AggregationPipelineQueryNode);
+ AggregationPipelineQueryNode pipelineNode = (AggregationPipelineQueryNode) projectNode.getArg();
+ Assert.assertEquals(Sets.newHashSet(), pipelineNode.getAssuredBindingNames());
+ }
+
+ @Test
public void testMultiProjection() throws Exception {
StatementPattern isUndergrad = new StatementPattern(new Var("x"), constant(RDF.TYPE), constant(UNDERGRAD));
StatementPattern isCourse = new StatementPattern(new Var("course"), constant(RDF.TYPE), constant(COURSE));
http://git-wip-us.apache.org/repos/asf/incubator-rya/blob/9f611019/extras/indexing/src/main/java/org/apache/rya/sail/config/RyaSailFactory.java
----------------------------------------------------------------------
diff --git a/extras/indexing/src/main/java/org/apache/rya/sail/config/RyaSailFactory.java b/extras/indexing/src/main/java/org/apache/rya/sail/config/RyaSailFactory.java
index b5adee3..56af9b4 100644
--- a/extras/indexing/src/main/java/org/apache/rya/sail/config/RyaSailFactory.java
+++ b/extras/indexing/src/main/java/org/apache/rya/sail/config/RyaSailFactory.java
@@ -88,33 +88,10 @@ public class RyaSailFactory {
// Get a reference to a Mongo DB configuration object.
final MongoDBRdfConfiguration mongoConfig = (config instanceof MongoDBRdfConfiguration) ?
(MongoDBRdfConfiguration)config : new MongoDBRdfConfiguration(config);
-
- // Create the MongoClient that will be used by the Sail object's components.
- final MongoClient client = createMongoClient(mongoConfig);
-
- // Add the Indexer and Optimizer names to the configuration object that are configured to be used.
- ConfigUtils.setIndexers(mongoConfig);
-
- // Populate the configuration using previously stored Rya Details if this instance uses them.
- try {
- final MongoRyaInstanceDetailsRepository ryaDetailsRepo = new MongoRyaInstanceDetailsRepository(client, mongoConfig.getRyaInstanceName());
- RyaDetailsToConfiguration.addRyaDetailsToConfiguration(ryaDetailsRepo.getRyaInstanceDetails(), mongoConfig);
- } catch (final RyaDetailsRepositoryException e) {
- LOG.info("Instance does not have a rya details collection, skipping.");
- }
-
- // Set the configuration to the stateful configuration that is used to pass the constructed objects around.
- final StatefulMongoDBRdfConfiguration statefulConfig = new StatefulMongoDBRdfConfiguration(mongoConfig, client);
- final List<MongoSecondaryIndex> indexers = statefulConfig.getInstances(AccumuloRdfConfiguration.CONF_ADDITIONAL_INDEXERS, MongoSecondaryIndex.class);
- statefulConfig.setIndexers(indexers);
- rdfConfig = statefulConfig;
-
- // Create the DAO that is able to interact with MongoDB.
- final MongoDBRyaDAO mongoDao = new MongoDBRyaDAO();
- mongoDao.setConf(statefulConfig);
- mongoDao.init();
- dao = mongoDao;
-
+ // Instantiate a Mongo client and Mongo DAO.
+ dao = getMongoDAO(mongoConfig);
+ // Then use the DAO's newly-created stateful conf in place of the original
+ rdfConfig = dao.getConf();
} else {
rdfConfig = new AccumuloRdfConfiguration(config);
user = rdfConfig.get(ConfigUtils.CLOUDBASE_USER);
@@ -237,4 +214,37 @@ public class RyaSailFactory {
LOG.info("Instance does not have a rya details collection, skipping.");
}
}
+
+ /**
+ * Connects to MongoDB and creates a MongoDBRyaDAO.
+ * @param config - user configuration
+ * @return - MongoDBRyaDAO with Indexers configured according to user's specification
+ * @throws RyaDAOException if the DAO can't be initialized
+ */
+ public static MongoDBRyaDAO getMongoDAO(MongoDBRdfConfiguration mongoConfig) throws RyaDAOException {
+ // Create the MongoClient that will be used by the Sail object's components.
+ final MongoClient client = createMongoClient(mongoConfig);
+
+ // Add the Indexer and Optimizer names to the configuration object that are configured to be used.
+ ConfigUtils.setIndexers(mongoConfig);
+
+ // Populate the configuration using previously stored Rya Details if this instance uses them.
+ try {
+ final MongoRyaInstanceDetailsRepository ryaDetailsRepo = new MongoRyaInstanceDetailsRepository(client, mongoConfig.getRyaInstanceName());
+ RyaDetailsToConfiguration.addRyaDetailsToConfiguration(ryaDetailsRepo.getRyaInstanceDetails(), mongoConfig);
+ } catch (final RyaDetailsRepositoryException e) {
+ LOG.info("Instance does not have a rya details collection, skipping.");
+ }
+
+ // Set the configuration to the stateful configuration that is used to pass the constructed objects around.
+ final StatefulMongoDBRdfConfiguration statefulConfig = new StatefulMongoDBRdfConfiguration(mongoConfig, client);
+ final List<MongoSecondaryIndex> indexers = statefulConfig.getInstances(AccumuloRdfConfiguration.CONF_ADDITIONAL_INDEXERS, MongoSecondaryIndex.class);
+ statefulConfig.setIndexers(indexers);
+
+ // Create the DAO that is able to interact with MongoDB.
+ final MongoDBRyaDAO mongoDao = new MongoDBRyaDAO();
+ mongoDao.setConf(statefulConfig);
+ mongoDao.init();
+ return mongoDao;
+ }
}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/incubator-rya/blob/9f611019/extras/pom.xml
----------------------------------------------------------------------
diff --git a/extras/pom.xml b/extras/pom.xml
index 62220ca..4ebcb82 100644
--- a/extras/pom.xml
+++ b/extras/pom.xml
@@ -45,6 +45,7 @@ under the License.
<module>rya.merger</module>
<module>rya.giraph</module>
<module>rya.streams</module>
+ <module>rya.forwardchain</module>
</modules>
<profiles>
http://git-wip-us.apache.org/repos/asf/incubator-rya/blob/9f611019/extras/rya.forwardchain/pom.xml
----------------------------------------------------------------------
diff --git a/extras/rya.forwardchain/pom.xml b/extras/rya.forwardchain/pom.xml
new file mode 100644
index 0000000..7acabca
--- /dev/null
+++ b/extras/rya.forwardchain/pom.xml
@@ -0,0 +1,119 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+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.
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+ <parent>
+ <groupId>org.apache.rya</groupId>
+ <artifactId>rya.extras</artifactId>
+ <version>3.2.12-incubating-SNAPSHOT</version>
+ </parent>
+
+ <artifactId>rya.forwardchain</artifactId>
+ <name>Apache Rya Forward Chaining Inference</name>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.openrdf.sesame</groupId>
+ <artifactId>sesame-runtime</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.rya</groupId>
+ <artifactId>rya.api</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.rya</groupId>
+ <artifactId>rya.sail</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.rya</groupId>
+ <artifactId>rya.indexing</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.rya</groupId>
+ <artifactId>mongodb.rya</artifactId>
+ </dependency>
+
+ <!-- Testing -->
+ <dependency>
+ <groupId>junit</groupId>
+ <artifactId>junit</artifactId>
+ <scope>test</scope>
+ </dependency>
+ </dependencies>
+
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-compiler-plugin</artifactId>
+ <configuration>
+ <source>1.8</source>
+ <target>1.8</target>
+ </configuration>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-jar-plugin</artifactId>
+ <configuration>
+ <archive>
+ <index>true</index>
+ <manifest>
+ <addClasspath>true</addClasspath>
+ <mainClass>org.apache.rya.forwardchain.batch.ForwardChainSpinTool</mainClass>
+ </manifest>
+ </archive>
+ </configuration>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-shade-plugin</artifactId>
+ <configuration>
+ <shadedArtifactAttached>true</shadedArtifactAttached>
+ <filters>
+ <filter>
+ <artifact>*:*</artifact>
+ <excludes>
+ <exclude>META-INF/*.SF</exclude>
+ <exclude>META-INF/*.DSA</exclude>
+ <exclude>META-INF/*.RSA</exclude>
+ </excludes>
+ </filter>
+ </filters>
+ </configuration>
+ <executions>
+ <execution>
+ <phase>package</phase>
+ <goals>
+ <goal>shade</goal>
+ </goals>
+ <configuration>
+ <shadedArtifactAttached>true</shadedArtifactAttached>
+ <shadedClassifierName>shaded</shadedClassifierName>
+ <transformers>
+ <transformer implementation="org.apache.maven.plugins.shade.resource.ServicesResourceTransformer" />
+ </transformers>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+ </plugins>
+ </build>
+</project>
http://git-wip-us.apache.org/repos/asf/incubator-rya/blob/9f611019/extras/rya.forwardchain/src/main/java/org/apache/rya/forwardchain/ForwardChainConstants.java
----------------------------------------------------------------------
diff --git a/extras/rya.forwardchain/src/main/java/org/apache/rya/forwardchain/ForwardChainConstants.java b/extras/rya.forwardchain/src/main/java/org/apache/rya/forwardchain/ForwardChainConstants.java
new file mode 100644
index 0000000..f1fe8b3
--- /dev/null
+++ b/extras/rya.forwardchain/src/main/java/org/apache/rya/forwardchain/ForwardChainConstants.java
@@ -0,0 +1,37 @@
+/*
+ * 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.forwardchain;
+
+import org.apache.rya.api.RdfCloudTripleStoreConstants;
+import org.apache.rya.api.domain.RyaSchema;
+import org.apache.rya.api.domain.RyaURI;
+import org.apache.rya.api.resolver.RdfToRyaConversions;
+import org.openrdf.model.URI;
+import org.openrdf.model.ValueFactory;
+
+public class ForwardChainConstants {
+ private static final ValueFactory VF = RdfCloudTripleStoreConstants.VALUE_FACTORY;
+ private static final String NAMESPACE = RyaSchema.NAMESPACE;
+
+ public static final URI DERIVATION_TIME = VF.createURI(NAMESPACE, "forwardChainIteration");
+ public static final URI DERIVATION_RULE = VF.createURI(NAMESPACE, "forwardChainRule");
+
+ public static final RyaURI RYA_DERIVATION_RULE = RdfToRyaConversions.convertURI(DERIVATION_RULE);
+ public static final RyaURI RYA_DERIVATION_TIME = RdfToRyaConversions.convertURI(DERIVATION_TIME);
+}
http://git-wip-us.apache.org/repos/asf/incubator-rya/blob/9f611019/extras/rya.forwardchain/src/main/java/org/apache/rya/forwardchain/ForwardChainException.java
----------------------------------------------------------------------
diff --git a/extras/rya.forwardchain/src/main/java/org/apache/rya/forwardchain/ForwardChainException.java b/extras/rya.forwardchain/src/main/java/org/apache/rya/forwardchain/ForwardChainException.java
new file mode 100644
index 0000000..64b05a4
--- /dev/null
+++ b/extras/rya.forwardchain/src/main/java/org/apache/rya/forwardchain/ForwardChainException.java
@@ -0,0 +1,54 @@
+/*
+ * 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.forwardchain;
+
+/**
+ * Broad exception representing an error during forward chaining. Useful for
+ * wrapping the diverse kinds of exceptions that may be thrown by
+ * implementations of reasoning logic.
+ */
+public class ForwardChainException extends Exception {
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * Constructs a new ForwardChainException with a message and a cause.
+ * @param string Detail message
+ * @param e Underlying cause
+ */
+ public ForwardChainException(String string, Exception e) {
+ super(string , e);
+ }
+
+ /**
+ * Constructs a new ForwardChainException with a message only.
+ * @param string Detail message
+ */
+ public ForwardChainException(String string) {
+ super(string);
+ }
+
+ /**
+ * Constructs a new ForwardChainException with a root cause and no
+ * additional message.
+ * @param e Underlying cause
+ */
+ public ForwardChainException(Exception e) {
+ super(e);
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-rya/blob/9f611019/extras/rya.forwardchain/src/main/java/org/apache/rya/forwardchain/batch/AbstractForwardChainTool.java
----------------------------------------------------------------------
diff --git a/extras/rya.forwardchain/src/main/java/org/apache/rya/forwardchain/batch/AbstractForwardChainTool.java b/extras/rya.forwardchain/src/main/java/org/apache/rya/forwardchain/batch/AbstractForwardChainTool.java
new file mode 100644
index 0000000..db08407
--- /dev/null
+++ b/extras/rya.forwardchain/src/main/java/org/apache/rya/forwardchain/batch/AbstractForwardChainTool.java
@@ -0,0 +1,148 @@
+/*
+ * 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.forwardchain.batch;
+
+import org.apache.hadoop.conf.Configuration;
+import org.apache.hadoop.util.Tool;
+import org.apache.log4j.Logger;
+import org.apache.rya.accumulo.AccumuloRdfConfiguration;
+import org.apache.rya.api.RdfCloudTripleStoreConfiguration;
+import org.apache.rya.forwardchain.ForwardChainException;
+import org.apache.rya.forwardchain.rule.Ruleset;
+import org.apache.rya.forwardchain.strategy.AbstractForwardChainStrategy;
+import org.apache.rya.forwardchain.strategy.AbstractRuleExecutionStrategy;
+import org.apache.rya.forwardchain.strategy.MongoPipelineStrategy;
+import org.apache.rya.forwardchain.strategy.RoundRobinStrategy;
+import org.apache.rya.forwardchain.strategy.SailExecutionStrategy;
+import org.apache.rya.indexing.accumulo.ConfigUtils;
+import org.apache.rya.mongodb.MongoDBRdfConfiguration;
+import com.google.common.base.Preconditions;
+
+/**
+ * Base class for a {@link Tool} that executes forward-chaining rules until
+ * completion (when no more new information can be derived).
+ * <p>
+ * Subclasses must implement {@link #getRuleset()} to yield the specific set of
+ * {@link Rule}s to materialize.
+ * <p>
+ * Subclasses may additionally override {@link #getStrategy()} and/or
+ * {@link #getRuleStrategy()} to provide specific forward chaining execution
+ * logic.
+ */
+public abstract class AbstractForwardChainTool implements Tool {
+ private static final Logger logger = Logger.getLogger(AbstractForwardChainTool.class);
+
+ private RdfCloudTripleStoreConfiguration conf;
+
+ private long numInferences = 0;
+
+ /**
+ * Set the {@link Configuration} for this tool, which will be converted to
+ * an {@link RdfCloudTripleStoreConfiguration}.
+ * @param conf Configuration object that specifies Rya connection details.
+ * Should not be null.
+ */
+ @Override
+ public void setConf(Configuration conf) {
+ Preconditions.checkNotNull(conf);
+ if (conf.getBoolean(ConfigUtils.USE_MONGO, false)) {
+ this.conf = new MongoDBRdfConfiguration(conf);
+ }
+ else {
+ this.conf = new AccumuloRdfConfiguration(conf);
+ }
+ }
+
+ /**
+ * Get the RdfCloudTripleStoreConfiguration used by this tool.
+ * @return Rya configuration object.
+ */
+ @Override
+ public RdfCloudTripleStoreConfiguration getConf() {
+ return conf;
+ }
+
+ @Override
+ public int run(String[] args) throws Exception {
+ numInferences = getStrategy().executeAll(getRuleset());
+ logger.info("Forward chaining complete; made " + numInferences + " inferences.");
+ return 0;
+ }
+
+ /**
+ * Gets the number of inferences that have been made.
+ * @return zero before forward chaining, or the total number of inferences
+ * after.
+ */
+ public long getNumInferences() {
+ return numInferences;
+ }
+
+ /**
+ * Get the high-level {@link AbstractForwardChainStrategy} that governs how
+ * reasoning will proceed. By default, returns a {@link RoundRobinStrategy}
+ * which executes each relevant rule one-by-one, then moves to the next
+ * iteration and repeats, until no rules are still relevant. Subclasses may
+ * override this method to provide alternative strategies.
+ * @return The high-level forward chaining logic.
+ * @throws ForwardChainException if the strategy can't be instantiated.
+ */
+ protected AbstractForwardChainStrategy getStrategy() throws ForwardChainException {
+ return new RoundRobinStrategy(getRuleStrategy());
+ }
+
+ /**
+ * Get the low-level {@link AbstractRuleExecutionStrategy} that governs the
+ * application of rules on an individual basis. This is used by the default
+ * ForwardChainStrategy (RoundRobinStrategy) and may be used by any
+ * high-level strategy that executes rules individually. By default, returns
+ * a {@link MongoPipelineStrategy} if the configuration object specifies a
+ * MongoDB connection with aggregation pipelines enabled, and a
+ * {@link SailExecutionStrategy} otherwise. Subclasses may override this
+ * method to provide alternative strategies.
+ * @return The low-level rule execution logic.
+ * @throws ForwardChainExceptionthe strategy can't be instantiated.
+ */
+ protected AbstractRuleExecutionStrategy getRuleStrategy() throws ForwardChainException {
+ if (ConfigUtils.getUseMongo(conf)) {
+ final MongoDBRdfConfiguration mongoConf;
+ if (conf instanceof MongoDBRdfConfiguration) {
+ mongoConf = (MongoDBRdfConfiguration) conf;
+ }
+ else {
+ mongoConf = new MongoDBRdfConfiguration(conf);
+ }
+ if (mongoConf.getUseAggregationPipeline()) {
+ return new MongoPipelineStrategy(mongoConf);
+ }
+ }
+ return new SailExecutionStrategy(conf);
+ }
+
+ /**
+ * Get the set of rules for this tool to apply. Subclasses should implement
+ * this for their specific domains. The subclass should ensure that the
+ * ruleset returned only contains rules whose types are supported by the
+ * forward chaining strategy. The default strategy supports only CONSTRUCT
+ * rules, so the ruleset should only contain {@link AbstractConstructRule}s.
+ * @return A set of forward-chaining rules.
+ * @throws ForwardChainException if rules couldn't be retrieved.
+ */
+ protected abstract Ruleset getRuleset() throws ForwardChainException;
+}
http://git-wip-us.apache.org/repos/asf/incubator-rya/blob/9f611019/extras/rya.forwardchain/src/main/java/org/apache/rya/forwardchain/batch/ForwardChainSpinTool.java
----------------------------------------------------------------------
diff --git a/extras/rya.forwardchain/src/main/java/org/apache/rya/forwardchain/batch/ForwardChainSpinTool.java b/extras/rya.forwardchain/src/main/java/org/apache/rya/forwardchain/batch/ForwardChainSpinTool.java
new file mode 100644
index 0000000..c35f37e
--- /dev/null
+++ b/extras/rya.forwardchain/src/main/java/org/apache/rya/forwardchain/batch/ForwardChainSpinTool.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.forwardchain.batch;
+
+import org.apache.hadoop.util.Tool;
+import org.apache.hadoop.util.ToolRunner;
+import org.apache.rya.api.RdfCloudTripleStoreConfiguration;
+import org.apache.rya.forwardchain.ForwardChainException;
+import org.apache.rya.forwardchain.rule.Ruleset;
+import org.apache.rya.forwardchain.rule.SpinConstructRule;
+
+/**
+ * {@link Tool} to load SPIN Construct rules from a Rya data store, then apply
+ * those rules to the same store using forward-chaining inference
+ * (materialization), adding triples back to Rya until no more information can
+ * be derived.
+ */
+public class ForwardChainSpinTool extends AbstractForwardChainTool {
+ private Ruleset ruleset;
+
+ /**
+ * Constructor that takes in an {@link RdfCloudTripleStoreConfiguration}.
+ * @param conf Configuration object containing Rya connection information.
+ */
+ public ForwardChainSpinTool(RdfCloudTripleStoreConfiguration conf) {
+ setConf(conf);
+ }
+
+ /**
+ * Default constructor that does not take in a configuration object. Rya
+ * connection details should be provided via an
+ * RdfCloudTripleStoreConfiguration, either using
+ * {@link AbstractForwardChainTool#setConf} or a {@link ToolRunner}.
+ */
+ public ForwardChainSpinTool() { }
+
+ /**
+ * Load SPIN Construct rules from Rya.
+ * @return A set of construct query rules.
+ * @throws ForwardChainException if loading rules from Rya fails.
+ */
+ @Override
+ protected Ruleset getRuleset() throws ForwardChainException {
+ if (ruleset == null) {
+ ruleset = SpinConstructRule.loadSpinRules(getConf());
+ }
+ return ruleset;
+ }
+
+ public static void main(String[] args) throws Exception {
+ long start = System.currentTimeMillis();
+ ForwardChainSpinTool tool = new ForwardChainSpinTool();
+ ToolRunner.run(tool, args);
+ long end = System.currentTimeMillis();
+ double seconds = (end - start) / 1000.0;
+ long inferences = tool.getNumInferences();
+ long rules = tool.getRuleset().getRules().size();
+ System.out.println(String.format("ForwardChainSpinTool: %d rules, %d inferences, %.3f seconds",
+ rules, inferences, seconds));
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-rya/blob/9f611019/extras/rya.forwardchain/src/main/java/org/apache/rya/forwardchain/rule/AbstractConstructRule.java
----------------------------------------------------------------------
diff --git a/extras/rya.forwardchain/src/main/java/org/apache/rya/forwardchain/rule/AbstractConstructRule.java b/extras/rya.forwardchain/src/main/java/org/apache/rya/forwardchain/rule/AbstractConstructRule.java
new file mode 100644
index 0000000..c4c12c7
--- /dev/null
+++ b/extras/rya.forwardchain/src/main/java/org/apache/rya/forwardchain/rule/AbstractConstructRule.java
@@ -0,0 +1,65 @@
+/*
+ * 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.forwardchain.rule;
+
+import org.apache.rya.api.domain.StatementMetadata;
+import org.apache.rya.forwardchain.ForwardChainException;
+import org.apache.rya.forwardchain.strategy.AbstractRuleExecutionStrategy;
+import org.openrdf.query.algebra.StatementPattern;
+import org.openrdf.query.parser.ParsedGraphQuery;
+
+import com.google.common.base.Preconditions;
+
+/**
+ * A rule that produces new triples, and can be expressed as a graph query
+ * (SPARQL "CONSTRUCT"). Should not modify existing triples.
+ */
+public abstract class AbstractConstructRule implements Rule {
+ /**
+ * Get the query tree corresponding to this construct rule.
+ * @return The query algebra representation of this rule.
+ */
+ public abstract ParsedGraphQuery getQuery();
+
+ @Override
+ public long execute(AbstractRuleExecutionStrategy strategy,
+ StatementMetadata metadata) throws ForwardChainException {
+ Preconditions.checkNotNull(strategy);
+ Preconditions.checkNotNull(metadata);
+ return strategy.executeConstructRule(this, metadata);
+ }
+
+ /**
+ * Whether any of the possible consequents of this rule include anonymous
+ * variables. Care should be taken when executing such rules, so that
+ * repeated application doesn't continually produce new bnodes.
+ * @return true if any subject, predicate, or object variable involved in a
+ * consequent is flagged as anonymous.
+ */
+ public boolean hasAnonymousConsequent() {
+ for (StatementPattern sp : getConsequentPatterns()) {
+ if (sp.getSubjectVar().isAnonymous()
+ || sp.getPredicateVar().isAnonymous()
+ || sp.getObjectVar().isAnonymous()) {
+ return true;
+ }
+ }
+ return false;
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-rya/blob/9f611019/extras/rya.forwardchain/src/main/java/org/apache/rya/forwardchain/rule/AbstractInconsistencyRule.java
----------------------------------------------------------------------
diff --git a/extras/rya.forwardchain/src/main/java/org/apache/rya/forwardchain/rule/AbstractInconsistencyRule.java b/extras/rya.forwardchain/src/main/java/org/apache/rya/forwardchain/rule/AbstractInconsistencyRule.java
new file mode 100644
index 0000000..451c5e4
--- /dev/null
+++ b/extras/rya.forwardchain/src/main/java/org/apache/rya/forwardchain/rule/AbstractInconsistencyRule.java
@@ -0,0 +1,51 @@
+/*
+ * 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.forwardchain.rule;
+
+import java.util.Arrays;
+import java.util.Collection;
+
+import org.apache.rya.api.domain.StatementMetadata;
+import org.apache.rya.forwardchain.ForwardChainException;
+import org.apache.rya.forwardchain.strategy.AbstractRuleExecutionStrategy;
+import org.openrdf.query.algebra.StatementPattern;
+
+/**
+ * A rule that identifies an inconsistency in the data, but does not add or
+ * modify any triples.
+ */
+public abstract class AbstractInconsistencyRule implements Rule {
+
+ @Override
+ public boolean canConclude(StatementPattern sp) {
+ return false;
+ }
+
+ @Override
+ public Collection<StatementPattern> getConsequentPatterns() {
+ return Arrays.asList();
+ }
+
+ @Override
+ public long execute(AbstractRuleExecutionStrategy strategy,
+ StatementMetadata metadata) throws ForwardChainException {
+ return strategy.executeInconsistencyRule(this, metadata);
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-rya/blob/9f611019/extras/rya.forwardchain/src/main/java/org/apache/rya/forwardchain/rule/AbstractUpdateRule.java
----------------------------------------------------------------------
diff --git a/extras/rya.forwardchain/src/main/java/org/apache/rya/forwardchain/rule/AbstractUpdateRule.java b/extras/rya.forwardchain/src/main/java/org/apache/rya/forwardchain/rule/AbstractUpdateRule.java
new file mode 100644
index 0000000..d87aeae
--- /dev/null
+++ b/extras/rya.forwardchain/src/main/java/org/apache/rya/forwardchain/rule/AbstractUpdateRule.java
@@ -0,0 +1,34 @@
+/*
+ * 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.forwardchain.rule;
+
+import org.apache.rya.api.domain.StatementMetadata;
+import org.apache.rya.forwardchain.ForwardChainException;
+import org.apache.rya.forwardchain.strategy.AbstractRuleExecutionStrategy;
+
+/**
+ * A rule that modifies existing data.
+ */
+public abstract class AbstractUpdateRule implements Rule {
+ @Override
+ public long execute(AbstractRuleExecutionStrategy strategy,
+ StatementMetadata metadata) throws ForwardChainException {
+ return strategy.executeUpdateRule(this, metadata);
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-rya/blob/9f611019/extras/rya.forwardchain/src/main/java/org/apache/rya/forwardchain/rule/AntecedentVisitor.java
----------------------------------------------------------------------
diff --git a/extras/rya.forwardchain/src/main/java/org/apache/rya/forwardchain/rule/AntecedentVisitor.java b/extras/rya.forwardchain/src/main/java/org/apache/rya/forwardchain/rule/AntecedentVisitor.java
new file mode 100644
index 0000000..1f2cbba
--- /dev/null
+++ b/extras/rya.forwardchain/src/main/java/org/apache/rya/forwardchain/rule/AntecedentVisitor.java
@@ -0,0 +1,51 @@
+/*
+ * 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.forwardchain.rule;
+
+import java.util.HashSet;
+import java.util.Set;
+
+import org.openrdf.query.algebra.StatementPattern;
+import org.openrdf.query.algebra.helpers.QueryModelVisitorBase;
+
+/**
+ * Query visitor that identifies all triple patterns represented as
+ * {@link StatementPattern}s in a query, which therefore represent triples
+ * that could potentially contribute to a solution. Considers only the statement
+ * patterns themselves, i.e. the leaves of the query tree, and does not consider
+ * other constraints that may restrict the set of triples that may be relevant.
+ * This means relying on this analysis to determine whether a fact can be part
+ * of a solution can yield false positives, but not false negatives.
+ */
+class AntecedentVisitor extends QueryModelVisitorBase<RuntimeException> {
+ private Set<StatementPattern> antecedentStatementPatterns = new HashSet<>();
+
+ /**
+ * Get the StatementPatterns used by this query.
+ * @return A set of patterns that can contribute to query solutions.
+ */
+ public Set<StatementPattern> getAntecedents() {
+ return antecedentStatementPatterns;
+ }
+
+ @Override
+ public void meet(StatementPattern sp) {
+ antecedentStatementPatterns.add(sp.clone());
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-rya/blob/9f611019/extras/rya.forwardchain/src/main/java/org/apache/rya/forwardchain/rule/ConstructConsequentVisitor.java
----------------------------------------------------------------------
diff --git a/extras/rya.forwardchain/src/main/java/org/apache/rya/forwardchain/rule/ConstructConsequentVisitor.java b/extras/rya.forwardchain/src/main/java/org/apache/rya/forwardchain/rule/ConstructConsequentVisitor.java
new file mode 100644
index 0000000..e28dbe3
--- /dev/null
+++ b/extras/rya.forwardchain/src/main/java/org/apache/rya/forwardchain/rule/ConstructConsequentVisitor.java
@@ -0,0 +1,138 @@
+/*
+ * 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.forwardchain.rule;
+
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+
+import org.openrdf.model.Value;
+import org.openrdf.query.algebra.BNodeGenerator;
+import org.openrdf.query.algebra.Extension;
+import org.openrdf.query.algebra.ExtensionElem;
+import org.openrdf.query.algebra.MultiProjection;
+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.ValueConstant;
+import org.openrdf.query.algebra.Var;
+import org.openrdf.query.algebra.helpers.QueryModelVisitorBase;
+
+/**
+ * Query visitor that identifies all triple patterns produced by a "CONSTRUCT"
+ * query. Finds the topmost instance of a {@link Projection} or
+ * {@link MultiProjection}, and expects the variables projected to include
+ * "subject", "predicate", and "object". Each projection is converted to a
+ * {@link StatementPattern}, where any constant values are expected to be
+ * provided by an Extension directly underneath the projection, if applicable.
+ * <p>
+ * Undefined behavior if applied to a query other than a CONSTRUCT query.
+ * <p>
+ * Does not report any constraints on possible consequent triples beyond the
+ * constant values, where appropriate, of each part of the triple. Therefore,
+ * this analysis may produce an overly broad set of possible consequents
+ * compared to some more sophisticated method.
+ */
+public class ConstructConsequentVisitor extends QueryModelVisitorBase<RuntimeException> {
+ private Set<StatementPattern> consequentStatementPatterns = new HashSet<>();
+
+ private static final String SUBJECT_VAR_NAME = "subject";
+ private static final String PREDICATE_VAR_NAME = "predicate";
+ private static final String OBJECT_VAR_NAME = "object";
+
+ /**
+ * Get the possible conclusions of this construct rule.
+ * @return StatementPatterns representing the possible triple patterns that
+ * can be inferred.
+ */
+ public Set<StatementPattern> getConsequents() {
+ return consequentStatementPatterns;
+ }
+
+ /**
+ * Get the names of any bnodes generated by this construct rule.
+ * @return Variable names corresponding to new entities
+ */
+ public Set<StatementPattern> getBnodes() {
+ return consequentStatementPatterns;
+ }
+
+ @Override
+ public void meet(Projection projection) {
+ if (projection.getArg() instanceof Extension) {
+ recordConsequent(projection.getProjectionElemList(),
+ ((Extension) projection.getArg()).getElements());
+ }
+ else {
+ recordConsequent(projection.getProjectionElemList(), Arrays.asList());
+ }
+ }
+
+ @Override
+ public void meet(MultiProjection projection) {
+ List<ExtensionElem> bindings;
+ if (projection.getArg() instanceof Extension) {
+ bindings = ((Extension) projection.getArg()).getElements();
+ }
+ else {
+ bindings = Arrays.asList();
+ }
+ for (ProjectionElemList template : projection.getProjections()) {
+ recordConsequent(template, bindings);
+ }
+ }
+
+ private void recordConsequent(ProjectionElemList variables, List<ExtensionElem> extensionElements) {
+ Map<String, Value> bindings = new ConcurrentHashMap<>();
+ Map<String, Value> values = new ConcurrentHashMap<>();
+ Set<String> queryBnodes = new HashSet<>();
+ Set<String> projectedBnodes = new HashSet<>();
+ for (ExtensionElem ee : extensionElements) {
+ if (ee.getExpr() instanceof ValueConstant) {
+ bindings.put(ee.getName(), ((ValueConstant) ee.getExpr()).getValue());
+ }
+ else if (ee.getExpr() instanceof BNodeGenerator) {
+ queryBnodes.add(ee.getName());
+ }
+ }
+ for (ProjectionElem var : variables.getElements()) {
+ String sourceName = var.getSourceName();
+ String targetName = var.getTargetName();
+ Value constValue = bindings.get(sourceName);
+ if (constValue != null) {
+ values.put(targetName, constValue);
+ }
+ else if (queryBnodes.contains(sourceName)) {
+ projectedBnodes.add(targetName);
+ }
+ }
+ Var subjVar = new Var(SUBJECT_VAR_NAME, values.get(SUBJECT_VAR_NAME));
+ Var predVar = new Var(PREDICATE_VAR_NAME, values.get(PREDICATE_VAR_NAME));
+ Var objVar = new Var(OBJECT_VAR_NAME, values.get(OBJECT_VAR_NAME));
+ subjVar.setAnonymous(projectedBnodes.contains(SUBJECT_VAR_NAME));
+ predVar.setAnonymous(projectedBnodes.contains(PREDICATE_VAR_NAME));
+ objVar.setAnonymous(projectedBnodes.contains(OBJECT_VAR_NAME));
+ StatementPattern sp = new StatementPattern(subjVar, predVar, objVar);
+ consequentStatementPatterns.add(sp);
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-rya/blob/9f611019/extras/rya.forwardchain/src/main/java/org/apache/rya/forwardchain/rule/Rule.java
----------------------------------------------------------------------
diff --git a/extras/rya.forwardchain/src/main/java/org/apache/rya/forwardchain/rule/Rule.java b/extras/rya.forwardchain/src/main/java/org/apache/rya/forwardchain/rule/Rule.java
new file mode 100644
index 0000000..74004b9
--- /dev/null
+++ b/extras/rya.forwardchain/src/main/java/org/apache/rya/forwardchain/rule/Rule.java
@@ -0,0 +1,75 @@
+/*
+ * 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.forwardchain.rule;
+
+import java.util.Collection;
+
+import org.apache.rya.api.domain.StatementMetadata;
+import org.apache.rya.forwardchain.ForwardChainException;
+import org.apache.rya.forwardchain.strategy.AbstractRuleExecutionStrategy;
+import org.openrdf.query.algebra.StatementPattern;
+
+/**
+ * Represents a forward-chaining inference rule. A rule is triggered by some
+ * combination of triples, and may produce some combination of triples when
+ * applied. Potential triggers (antecedents) and potential results (consequents)
+ * are represented in a general form as {@link StatementPattern}s and can be
+ * used to determine relationships between rules.
+ */
+public interface Rule {
+ /**
+ * Whether this rule, if applied, could produce triples of a given form.
+ * @param sp A statement pattern describing a possible inferred triple;
+ * assumed not null.
+ * @return true if a consequent of this rule could match the pattern.
+ */
+ abstract public boolean canConclude(StatementPattern sp);
+
+ /**
+ * All {@link StatementPattern}s that can, in some combination, trigger this
+ * rule. Should be a complete set, such that if no statements matching any
+ * of the patterns exist, the rule cannot derive any new information.
+ * @return Any number of statement patterns.
+ */
+ abstract public Collection<StatementPattern> getAntecedentPatterns();
+
+ /**
+ * {@link StatementPattern}s completely describing the possible conclusions
+ * of this rule. Any derived statement should match one of these patterns.
+ * @return Any number of statement patterns.
+ */
+ abstract public Collection<StatementPattern> getConsequentPatterns();
+
+ /**
+ * Given an {@link AbstractRuleExecutionStrategy}, executes this rule.
+ * Associates any new or modified triples with the specified statement
+ * metadata.
+ * @param strategy A strategy capable of applying individual rules; should
+ * not be null.
+ * @param metadata StatementMetadata to add to any results. Can be used to
+ * record the circumstances of the derivation. Should not be null; use
+ * {@link StatementMetadata#EMPTY_METADATA} to add none. Implementing
+ * classes may add additional metadata specific to the rule.
+ * @return The number of new inferences made during rule execution.
+ * @throws ForwardChainException if an error was encountered during
+ * rule application.
+ */
+ abstract public long execute(AbstractRuleExecutionStrategy strategy,
+ StatementMetadata metadata) throws ForwardChainException;
+}
http://git-wip-us.apache.org/repos/asf/incubator-rya/blob/9f611019/extras/rya.forwardchain/src/main/java/org/apache/rya/forwardchain/rule/Ruleset.java
----------------------------------------------------------------------
diff --git a/extras/rya.forwardchain/src/main/java/org/apache/rya/forwardchain/rule/Ruleset.java b/extras/rya.forwardchain/src/main/java/org/apache/rya/forwardchain/rule/Ruleset.java
new file mode 100644
index 0000000..965d2d3
--- /dev/null
+++ b/extras/rya.forwardchain/src/main/java/org/apache/rya/forwardchain/rule/Ruleset.java
@@ -0,0 +1,166 @@
+/*
+ * 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.forwardchain.rule;
+
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+
+import org.apache.log4j.Logger;
+import org.openrdf.query.algebra.StatementPattern;
+
+import com.google.common.base.Preconditions;
+
+/**
+ * Represents a set of forward-chaining {@link Rule}s and their relationships.
+ */
+public class Ruleset {
+ private final Set<Rule> rules;
+ private final Map<Rule, Set<Rule>> successors;
+ private final Map<Rule, Set<Rule>> predecessors;
+
+ private final Logger logger = Logger.getLogger(this.getClass());
+
+ /**
+ * Constructor. Takes in a set of rules and determines their dependencies.
+ * @param rules The complete set of rules to process; should not be null.
+ */
+ public Ruleset(Collection<Rule> rules) {
+ Preconditions.checkNotNull(rules);
+ this.rules = new HashSet<>();
+ for (Rule rule : rules) {
+ if (rule != null) {
+ this.rules.add(rule);
+ }
+ }
+ successors = new ConcurrentHashMap<>();
+ predecessors = new ConcurrentHashMap<>();
+ // Build the dependency graph of all the rules, in both directions
+ for (Rule rule : rules) {
+ successors.put(rule, new HashSet<>());
+ predecessors.put(rule, new HashSet<>());
+ }
+ for (Rule rule1 : rules) {
+ for (Rule rule2 : rules) {
+ if (canTrigger(rule1, rule2)) {
+ logger.trace("\t" + rule1.toString() + " can trigger " + rule2.toString());
+ successors.get(rule1).add(rule2);
+ predecessors.get(rule2).add(rule1);
+ }
+ }
+ }
+ }
+
+ /**
+ * Get the rules associated with this ruleset.
+ * @return The complete set of rules.
+ */
+ public Set<Rule> getRules() {
+ return rules;
+ }
+
+ /**
+ * Given a rule, return the set of all rules that it may trigger. That is,
+ * if the rule were to produce inferences, those inferences might directly
+ * cause other rules to apply in turn.
+ * @param precedingRule The potentially triggering rule; not null.
+ * @return All rules that could be triggered by the given rule.
+ */
+ public Collection<Rule> getSuccessorsOf(Rule precedingRule) {
+ Preconditions.checkNotNull(precedingRule);
+ return successors.get(precedingRule);
+ }
+
+ /**
+ * Given a rule, return the set of all rules that could trigger it. That is,
+ * if any one of those rules were applied, their potential conclusions could
+ * directly cause the specified rule to apply in turn.
+ * @param dependentRule The potentially triggered rule; not null.
+ * @return All rules that could trigger the given rule.
+ */
+ public Collection<Rule> getPredecessorsOf(Rule dependentRule) {
+ Preconditions.checkNotNull(dependentRule);
+ return predecessors.get(dependentRule);
+ }
+
+ /**
+ * Given a pair of rules, determine whether a path exists from the first to
+ * the second. That is, whether the first rule precedes the second rule
+ * either directly or transitively. If either rule is null, no path exists.
+ * @param r1 The start of the path
+ * @param r2 The end of the path
+ * @return whether a forward path exists.
+ */
+ public boolean pathExists(Rule r1, Rule r2) {
+ if (r1 == null || r2 == null) {
+ return false;
+ }
+ Set<Rule> forwardFrontier = new HashSet<>();
+ Set<Rule> backwardFrontier = new HashSet<>();
+ Set<Rule> visitedForward = new HashSet<>();
+ Set<Rule> visitedBackward = new HashSet<>();
+ forwardFrontier.addAll(getSuccessorsOf(r1));
+ backwardFrontier.add(r2);
+ while (!forwardFrontier.isEmpty() && !backwardFrontier.isEmpty()) {
+ Set<Rule> currentGoals = new HashSet<>(backwardFrontier);
+ for (Rule goal : currentGoals) {
+ if (forwardFrontier.contains(goal)) {
+ return true;
+ }
+ else {
+ visitedBackward.add(goal);
+ backwardFrontier.addAll(getPredecessorsOf(goal));
+ }
+ }
+ backwardFrontier.removeAll(visitedBackward);
+ Set<Rule> currentSources = new HashSet<>(forwardFrontier);
+ for (Rule source : currentSources) {
+ if (backwardFrontier.contains(source)) {
+ return true;
+ }
+ else {
+ visitedForward.add(source);
+ forwardFrontier.addAll(getSuccessorsOf(source));
+ }
+ }
+ forwardFrontier.removeAll(visitedForward);
+ }
+ return false;
+ }
+
+ /**
+ * Whether the first rule can, in any circumstance, directly trigger the second.
+ * @param rule1 The first rule, which may produce some inferences
+ * @param rule2 The second rule, which may use the first rule's conclusions
+ * @return True if the first rule's conclusions could be used by the second.
+ */
+ private boolean canTrigger(Rule rule1, Rule rule2) {
+ if (rule1 == null || rule2 == null) {
+ return false;
+ }
+ for (StatementPattern antecedent : rule2.getAntecedentPatterns()) {
+ if (rule1.canConclude(antecedent)) {
+ return true;
+ }
+ }
+ return false;
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-rya/blob/9f611019/extras/rya.forwardchain/src/main/java/org/apache/rya/forwardchain/rule/SpinConstructRule.java
----------------------------------------------------------------------
diff --git a/extras/rya.forwardchain/src/main/java/org/apache/rya/forwardchain/rule/SpinConstructRule.java b/extras/rya.forwardchain/src/main/java/org/apache/rya/forwardchain/rule/SpinConstructRule.java
new file mode 100644
index 0000000..44e15e6
--- /dev/null
+++ b/extras/rya.forwardchain/src/main/java/org/apache/rya/forwardchain/rule/SpinConstructRule.java
@@ -0,0 +1,344 @@
+/*
+ * 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.forwardchain.rule;
+
+import java.util.Collection;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+
+import org.apache.log4j.Logger;
+import org.apache.rya.api.RdfCloudTripleStoreConfiguration;
+import org.apache.rya.api.domain.StatementMetadata;
+import org.apache.rya.api.resolver.RdfToRyaConversions;
+import org.apache.rya.forwardchain.ForwardChainConstants;
+import org.apache.rya.forwardchain.ForwardChainException;
+import org.apache.rya.forwardchain.strategy.AbstractRuleExecutionStrategy;
+import org.apache.rya.sail.config.RyaSailFactory;
+import org.openrdf.model.Literal;
+import org.openrdf.model.Resource;
+import org.openrdf.model.Value;
+import org.openrdf.model.vocabulary.OWL;
+import org.openrdf.model.vocabulary.RDF;
+import org.openrdf.model.vocabulary.RDFS;
+import org.openrdf.model.vocabulary.SP;
+import org.openrdf.model.vocabulary.SPIN;
+import org.openrdf.query.BindingSet;
+import org.openrdf.query.MalformedQueryException;
+import org.openrdf.query.QueryEvaluationException;
+import org.openrdf.query.QueryLanguage;
+import org.openrdf.query.TupleQuery;
+import org.openrdf.query.TupleQueryResultHandlerBase;
+import org.openrdf.query.TupleQueryResultHandlerException;
+import org.openrdf.query.algebra.Extension;
+import org.openrdf.query.algebra.Join;
+import org.openrdf.query.algebra.QueryModelNode;
+import org.openrdf.query.algebra.SingletonSet;
+import org.openrdf.query.algebra.StatementPattern;
+import org.openrdf.query.algebra.TupleExpr;
+import org.openrdf.query.algebra.UnaryTupleOperator;
+import org.openrdf.query.algebra.ValueExpr;
+import org.openrdf.query.algebra.Var;
+import org.openrdf.query.algebra.helpers.QueryModelVisitorBase;
+import org.openrdf.query.parser.ParsedGraphQuery;
+import org.openrdf.query.parser.ParsedQuery;
+import org.openrdf.query.parser.sparql.SPARQLParser;
+import org.openrdf.repository.RepositoryException;
+import org.openrdf.repository.sail.SailRepository;
+import org.openrdf.repository.sail.SailRepositoryConnection;
+
+import com.google.common.base.Preconditions;
+import com.google.common.collect.Sets;
+
+/**
+ * Represents a SPIN Construct rule extracted from the data store, providing
+ * access to its associated query tree and providing methods to apply the rule.
+ */
+public class SpinConstructRule extends AbstractConstructRule {
+ private static Logger logger = Logger.getLogger(SpinConstructRule.class);
+
+ private final Resource ruleId;
+ private final ParsedGraphQuery graphQuery;
+ private Set<StatementPattern> antecedentStatementPatterns = null;
+ private Set<StatementPattern> consequentStatementPatterns = null;
+
+ /**
+ * Instantiate a SPIN construct rule given its associated type, URI or bnode
+ * identifier, and construct query tree. Modifies the query tree to
+ * incorporate the fact that ?this must belong to the associated type, and
+ * traverses the modified tree to find antecedent and consequent triple
+ * patterns.
+ * @param type This rule applies to objects of this type. Should not be
+ * null. If the type is owl:Thing or rdfs:Resource, it will be applied to
+ * any objects. Otherwise, a statement pattern will be added that
+ * effectively binds ?this to members of the type. Therefore, passing
+ * owl:Thing or rdfs:Resource yields the intended behavior of
+ * sp:thisUnbound.
+ * @param ruleId The Resource representing this rule in the RDF data;
+ * should not be null.
+ * @param graphQuery The query tree corresponding to the "construct" text;
+ * should not be null.
+ */
+ public SpinConstructRule(Resource type, Resource ruleId,
+ ParsedGraphQuery graphQuery) {
+ Preconditions.checkNotNull(type);
+ Preconditions.checkNotNull(ruleId);
+ Preconditions.checkNotNull(graphQuery);
+ this.ruleId = ruleId;
+ this.graphQuery = graphQuery;
+ // Add the type requirement: ?this must belong to the type
+ graphQuery.getTupleExpr().visit(new TypeRequirementVisitor("this", type));
+ // Find all statement patterns that could trigger this rule
+ AntecedentVisitor aVisitor = new AntecedentVisitor();
+ graphQuery.getTupleExpr().visit(aVisitor);
+ antecedentStatementPatterns = aVisitor.getAntecedents();
+ // Construct statement patterns for all possible conclusions of this rule
+ ConstructConsequentVisitor cVisitor = new ConstructConsequentVisitor();
+ graphQuery.getTupleExpr().visit(cVisitor);
+ consequentStatementPatterns = cVisitor.getConsequents();
+ }
+
+ /**
+ * Get the URI or bnode associated with this rule in the data.
+ * @return The rule's identifier.
+ */
+ public Resource getId() {
+ return ruleId;
+ }
+
+ @Override
+ public String toString() {
+ return "SpinConstructRule{" + ruleId.stringValue() + "}";
+ }
+
+ @Override
+ public ParsedGraphQuery getQuery() {
+ return graphQuery;
+ }
+
+ @Override
+ public boolean canConclude(StatementPattern sp) {
+ Preconditions.checkNotNull(sp);
+ Value s1 = getVarValue(sp.getSubjectVar());
+ Value p1 = getVarValue(sp.getPredicateVar());
+ Value o1 = getVarValue(sp.getObjectVar());
+ Value c1 = getVarValue(sp.getContextVar());
+ for (StatementPattern consequent : consequentStatementPatterns) {
+ Value s2 = getVarValue(consequent.getSubjectVar());
+ Value p2 = getVarValue(consequent.getPredicateVar());
+ Value o2 = getVarValue(consequent.getObjectVar());
+ Value c2 = getVarValue(consequent.getContextVar());
+ if ((s1 == null || s2 == null || s1.equals(s2))
+ && (p1 == null || p2 == null || p1.equals(p2))
+ && (o1 == null || o2 == null || o1.equals(o2))
+ && (c1 == null || c2 == null || c1.equals(c2))) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ @Override
+ public Collection<StatementPattern> getAntecedentPatterns() {
+ return antecedentStatementPatterns;
+ }
+
+ @Override
+ public Collection<StatementPattern> getConsequentPatterns() {
+ return consequentStatementPatterns;
+ }
+
+ @Override
+ public long execute(AbstractRuleExecutionStrategy strategy,
+ StatementMetadata metadata) throws ForwardChainException {
+ metadata.addMetadata(ForwardChainConstants.RYA_DERIVATION_RULE,
+ RdfToRyaConversions.convertResource(ruleId));
+ return super.execute(strategy, metadata);
+ }
+
+ private static Value getVarValue(Var var) {
+ return var == null ? null : var.getValue();
+ }
+
+ private static class TypeRequirementVisitor extends QueryModelVisitorBase<RuntimeException> {
+ private static final Var RDF_TYPE_VAR = new Var("-const-" + RDF.TYPE.stringValue(), RDF.TYPE);
+ private static final Set<Resource> BASE_TYPES = Sets.newHashSet(RDFS.RESOURCE, OWL.THING);
+ static {
+ RDF_TYPE_VAR.setConstant(true);
+ }
+
+ private final String varName;
+ private final StatementPattern typeRequirement;
+ public TypeRequirementVisitor(String varName, Resource requiredType) {
+ final Var typeVar = new Var("-const-" + requiredType.stringValue(), requiredType);
+ typeVar.setConstant(true);
+ this.varName = varName;
+ if (BASE_TYPES.contains(requiredType)) {
+ this.typeRequirement = null;
+ }
+ else {
+ this.typeRequirement = new StatementPattern(new Var(varName), RDF_TYPE_VAR, typeVar);
+ }
+ }
+ @Override
+ public void meet(SingletonSet node) {
+ if (typeRequirement != null) {
+ node.replaceWith(typeRequirement);
+ }
+ }
+ @Override
+ public void meet(Extension node) {
+ Set<String> argBindings = node.getArg().getBindingNames();
+ if (typeRequirement != null) {
+ node.getElements().removeIf(elem -> {
+ if (varName.equals(elem.getName())) {
+ ValueExpr expr = elem.getExpr();
+ if (expr == null) {
+ return true;
+ }
+ else if (expr instanceof Var) {
+ String fromName = ((Var) expr).getName();
+ if (getVarValue((Var) expr) == null && !argBindings.contains(fromName)) {
+ return true;
+ }
+ }
+ }
+ return false;
+ });
+ meetUnaryTupleOperator(node);
+ }
+ }
+ @Override
+ public void meetNode(QueryModelNode node) {
+ if (typeRequirement != null) {
+ if (node instanceof TupleExpr && ((TupleExpr) node).getBindingNames().contains(varName)) {
+ final Join withType = new Join((TupleExpr) node.clone(), typeRequirement);
+ node.replaceWith(withType);
+ }
+ else {
+ node.visitChildren(this);
+ }
+ }
+ }
+ @Override
+ public void meetUnaryTupleOperator(UnaryTupleOperator node) {
+ if (typeRequirement != null) {
+ if (node.getArg().getBindingNames().contains(varName)) {
+ node.visitChildren(this);
+ }
+ else {
+ meetNode(node);
+ }
+ }
+ }
+ }
+
+ /**
+ * Load a set of SPIN rules from a data store.
+ * @param conf Contains the connection information. Not null.
+ * @return A map of rule identifiers to rule objects.
+ * @throws ForwardChainException if connecting, querying for rules, or
+ * parsing rules fails.
+ */
+ public static Ruleset loadSpinRules(RdfCloudTripleStoreConfiguration conf)
+ throws ForwardChainException {
+ Preconditions.checkNotNull(conf);
+ Map<Resource, Rule> rules = new ConcurrentHashMap<>();
+ // Connect to Rya
+ SailRepository repository = null;
+ SailRepositoryConnection conn = null;
+ try {
+ repository = new SailRepository(RyaSailFactory.getInstance(conf));
+ } catch (Exception e) {
+ throw new ForwardChainException("Couldn't initialize SAIL from configuration", e);
+ }
+ // Load and parse the individual SPIN rules from the data store
+ String ruleQueryString = "SELECT ?type ?rule ?text WHERE {\n"
+ + " ?type <" + SPIN.RULE_PROPERTY.stringValue() + "> ?rule .\n"
+ + " {\n"
+ + " ?rule a <" + SP.CONSTRUCT_CLASS.stringValue() + "> .\n"
+ + " ?rule <" + SP.TEXT_PROPERTY.stringValue() + "> ?text .\n"
+ + " } UNION {\n"
+ + " ?rule a ?template .\n"
+ + " ?template <" + SPIN.BODY_PROPERTY + ">? ?body .\n"
+ + " ?body a <" + SP.CONSTRUCT_CLASS.stringValue() + "> .\n"
+ + " ?body <" + SP.TEXT_PROPERTY.stringValue() + "> ?text .\n"
+ + " }\n"
+ + "}";
+ SPARQLParser parser = new SPARQLParser();
+ try {
+ conn = repository.getConnection();
+ TupleQuery ruleQuery = conn.prepareTupleQuery(QueryLanguage.SPARQL, ruleQueryString);
+ ruleQuery.evaluate(new TupleQueryResultHandlerBase() {
+ @Override
+ public void handleSolution(BindingSet bs) throws TupleQueryResultHandlerException {
+ // For each rule identifier found, instantiate a SpinRule
+ Value requiredType = bs.getValue("type");
+ Value ruleIdentifier = bs.getValue("rule");
+ Value ruleText = bs.getValue("text");
+ if (requiredType instanceof Resource
+ && ruleIdentifier instanceof Resource
+ && ruleText instanceof Literal) {
+ ParsedQuery parsedRule;
+ try {
+ parsedRule = parser.parseQuery(ruleText.stringValue(), null);
+ if (parsedRule instanceof ParsedGraphQuery) {
+ SpinConstructRule rule = new SpinConstructRule(
+ (Resource) requiredType,
+ (Resource) ruleIdentifier,
+ (ParsedGraphQuery) parsedRule);
+ if (rule.hasAnonymousConsequent()) {
+ logger.error("Skipping unsupported rule " + ruleIdentifier
+ + " -- consequent refers to bnode, which is not"
+ + " currently supported (creating new bnodes at each"
+ + " application could lead to infinite recursion).");
+ }
+ else {
+ rules.put((Resource) ruleIdentifier, rule);
+ }
+ }
+ } catch (Exception e) {
+ throw new TupleQueryResultHandlerException(e);
+ }
+ }
+ }
+ });
+ } catch (TupleQueryResultHandlerException | QueryEvaluationException
+ | MalformedQueryException | RepositoryException e) {
+ throw new ForwardChainException("Couldn't retrieve SPIN rules", e);
+ }
+ finally {
+ if (conn != null) {
+ try {
+ conn.close();
+ } catch (RepositoryException e) {
+ logger.warn("Error closing repository connection", e);
+ }
+ }
+ if (repository.isInitialized()) {
+ try {
+ repository.shutDown();
+ } catch (RepositoryException e) {
+ logger.warn("Error shutting down repository", e);
+ }
+ }
+ }
+ return new Ruleset(rules.values());
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-rya/blob/9f611019/extras/rya.forwardchain/src/main/java/org/apache/rya/forwardchain/strategy/AbstractForwardChainStrategy.java
----------------------------------------------------------------------
diff --git a/extras/rya.forwardchain/src/main/java/org/apache/rya/forwardchain/strategy/AbstractForwardChainStrategy.java b/extras/rya.forwardchain/src/main/java/org/apache/rya/forwardchain/strategy/AbstractForwardChainStrategy.java
new file mode 100644
index 0000000..fb0314e
--- /dev/null
+++ b/extras/rya.forwardchain/src/main/java/org/apache/rya/forwardchain/strategy/AbstractForwardChainStrategy.java
@@ -0,0 +1,82 @@
+/*
+ * 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.forwardchain.strategy;
+
+import org.apache.rya.forwardchain.ForwardChainException;
+import org.apache.rya.forwardchain.rule.Ruleset;
+
+/**
+ * Base class for high-level strategies which define how to conduct
+ * forward-chaining reasoning (materialization).
+ */
+public abstract class AbstractForwardChainStrategy {
+ /**
+ * A running count of new inferences so far.
+ */
+ protected long totalInferences;
+
+ /**
+ * Initializes reasoning with respect to a given ruleset.
+ * @param ruleset The complete set of rules to materialize. Should not be
+ * null.
+ * @throws ForwardChainException if initialization fails.
+ */
+ abstract public void initialize(Ruleset ruleset) throws ForwardChainException;
+
+ /**
+ * Whether forward chaining is both initialized and yet to finish.
+ * @return true if a ruleset has been provided and some rules may still
+ * yield new information.
+ */
+ abstract protected boolean isActive();
+
+ /**
+ * Execute the next step of reasoning, such as a single rule if the strategy
+ * proceeds one rule at a time.
+ * @return The number of inferences made during this step.
+ * @throws ForwardChainException if any error is encountered during rule
+ * application.
+ */
+ abstract protected long executeNext() throws ForwardChainException;
+
+ /**
+ * Execute an entire ruleset until no new rules can be derived. Initializes
+ * strategy and proceeds until completion.
+ * @param rules The complete set of rules; not null.
+ * @return The number of total inferences made.
+ * @throws ForwardChainException if any error is encountered during
+ * initialization or application.
+ */
+ public long executeAll(Ruleset rules) throws ForwardChainException {
+ initialize(rules);
+ totalInferences = 0;
+ while (isActive()) {
+ totalInferences += executeNext();
+ }
+ return totalInferences;
+ }
+
+ /**
+ * Get the running total of inferences made so far.
+ * @return The number of inferences made since initialization.
+ */
+ public long getNumInferences() {
+ return totalInferences;
+ }
+}