http://git-wip-us.apache.org/repos/asf/incubator-rya/blob/11349b11/extras/indexing/src/test/java/org/apache/rya/indexing/pcj/matching/PCJNodeConsolidatorTest.java
----------------------------------------------------------------------
diff --git 
a/extras/indexing/src/test/java/org/apache/rya/indexing/pcj/matching/PCJNodeConsolidatorTest.java
 
b/extras/indexing/src/test/java/org/apache/rya/indexing/pcj/matching/PCJNodeConsolidatorTest.java
index 2fcb8ce..607e072 100644
--- 
a/extras/indexing/src/test/java/org/apache/rya/indexing/pcj/matching/PCJNodeConsolidatorTest.java
+++ 
b/extras/indexing/src/test/java/org/apache/rya/indexing/pcj/matching/PCJNodeConsolidatorTest.java
@@ -20,6 +20,12 @@ package org.apache.rya.indexing.pcj.matching;
 import java.util.ArrayList;
 import java.util.List;
 
+import org.apache.rya.indexing.external.matching.OptionalJoinSegment;
+import org.apache.rya.indexing.external.matching.QueryNodeConsolidator;
+import org.apache.rya.indexing.external.matching.QuerySegment;
+import org.apache.rya.indexing.external.matching.QuerySegmentFactory;
+import org.apache.rya.indexing.external.matching.TopOfQueryFilterRelocator;
+import org.apache.rya.indexing.external.tupleSet.ExternalTupleSet;
 import org.junit.Assert;
 import org.junit.Test;
 import org.openrdf.query.algebra.Filter;
@@ -33,6 +39,7 @@ import org.openrdf.query.parser.sparql.SPARQLParser;
 
 public class PCJNodeConsolidatorTest {
 
+    private final QuerySegmentFactory<ExternalTupleSet> qFactory = new 
QuerySegmentFactory<ExternalTupleSet>();
 
        @Test
        public void testBasicOptionalWithFilter() throws Exception {
@@ -66,10 +73,10 @@ public class PCJNodeConsolidatorTest {
                Filter filter1 = (Filter) ((Projection) te1).getArg();
                Filter filter2 = (Filter) ((Projection) te2).getArg();
 
-               OptionalJoinSegment seg1 = new OptionalJoinSegment(filter1);
-               OptionalJoinSegment seg2 = new OptionalJoinSegment(filter2);
-
-               PCJNodeConsolidator consolidator = new 
PCJNodeConsolidator(seg1.getOrderedNodes(), seg2.getOrderedNodes());
+               QuerySegment<ExternalTupleSet> seg1 = 
qFactory.getQuerySegment(filter1);
+        QuerySegment<ExternalTupleSet> seg2 = 
qFactory.getQuerySegment(filter2);
+        
+               QueryNodeConsolidator consolidator = new 
QueryNodeConsolidator(seg1.getOrderedNodes(), seg2.getOrderedNodes());
 
                List<QueryModelNode> queryNodes = new 
ArrayList<>(seg1.getOrderedNodes());
                QueryModelNode node = queryNodes.remove(0);
@@ -114,10 +121,10 @@ public class PCJNodeConsolidatorTest {
                Join join1 = (Join) ((Projection) te1).getArg();
                Join join2 = (Join) ((Projection) te2).getArg();
 
-               OptionalJoinSegment seg1 = new OptionalJoinSegment(join1);
-               OptionalJoinSegment seg2 = new OptionalJoinSegment(join2);
-
-               PCJNodeConsolidator consolidator = new 
PCJNodeConsolidator(seg1.getOrderedNodes(), seg2.getOrderedNodes());
+               QuerySegment<ExternalTupleSet> seg1 = 
qFactory.getQuerySegment(join1);
+        QuerySegment<ExternalTupleSet> seg2 = qFactory.getQuerySegment(join2);
+        
+               QueryNodeConsolidator consolidator = new 
QueryNodeConsolidator(seg1.getOrderedNodes(), seg2.getOrderedNodes());
 
                List<QueryModelNode> queryNodes = new 
ArrayList<>(seg1.getOrderedNodes());
                QueryModelNode node = queryNodes.remove(0);
@@ -163,10 +170,10 @@ public class PCJNodeConsolidatorTest {
                LeftJoin join1 = (LeftJoin) ((Projection) te1).getArg();
                Join join2 = (Join) ((Projection) te2).getArg();
 
-               OptionalJoinSegment seg1 = new OptionalJoinSegment(join1);
-               OptionalJoinSegment seg2 = new OptionalJoinSegment(join2);
+               QuerySegment<ExternalTupleSet> seg1 = 
qFactory.getQuerySegment(join1);
+        QuerySegment<ExternalTupleSet> seg2 = qFactory.getQuerySegment(join2);
 
-               PCJNodeConsolidator consolidator = new 
PCJNodeConsolidator(seg1.getOrderedNodes(), seg2.getOrderedNodes());
+               QueryNodeConsolidator consolidator = new 
QueryNodeConsolidator(seg1.getOrderedNodes(), seg2.getOrderedNodes());
                List<QueryModelNode> queryNodes = new 
ArrayList<>(seg1.getOrderedNodes());
 
                Assert.assertTrue(consolidator.consolidateNodes());
@@ -205,10 +212,10 @@ public class PCJNodeConsolidatorTest {
                Join join1 = (Join) ((Projection) te1).getArg();
                LeftJoin join2 = (LeftJoin) ((Projection) te2).getArg();
 
-               OptionalJoinSegment seg1 = new OptionalJoinSegment(join1);
-               OptionalJoinSegment seg2 = new OptionalJoinSegment(join2);
+               QuerySegment<ExternalTupleSet> seg1 = 
qFactory.getQuerySegment(join1);
+           QuerySegment<ExternalTupleSet> seg2 = 
qFactory.getQuerySegment(join2);
 
-               PCJNodeConsolidator consolidator = new 
PCJNodeConsolidator(seg1.getOrderedNodes(), seg2.getOrderedNodes());
+               QueryNodeConsolidator consolidator = new 
QueryNodeConsolidator(seg1.getOrderedNodes(), seg2.getOrderedNodes());
 
                Assert.assertTrue(!consolidator.consolidateNodes());
        }
@@ -243,10 +250,10 @@ public class PCJNodeConsolidatorTest {
                Join join1 = (Join) ((Projection) te1).getArg();
                Join join2 = (Join) ((Projection) te2).getArg();
 
-               OptionalJoinSegment seg1 = new OptionalJoinSegment(join1);
-               OptionalJoinSegment seg2 = new OptionalJoinSegment(join2);
+               QuerySegment<ExternalTupleSet> seg1 = 
qFactory.getQuerySegment(join1);
+           QuerySegment<ExternalTupleSet> seg2 = 
qFactory.getQuerySegment(join2);
 
-               PCJNodeConsolidator consolidator = new 
PCJNodeConsolidator(seg1.getOrderedNodes(), seg2.getOrderedNodes());
+               QueryNodeConsolidator consolidator = new 
QueryNodeConsolidator(seg1.getOrderedNodes(), seg2.getOrderedNodes());
 
                Assert.assertTrue(!consolidator.consolidateNodes());
        }
@@ -281,10 +288,10 @@ public class PCJNodeConsolidatorTest {
                Join join1 = (Join) ((Projection) te1).getArg();
                Join join2 = (Join) ((Projection) te2).getArg();
 
-               OptionalJoinSegment seg1 = new OptionalJoinSegment(join1);
-               OptionalJoinSegment seg2 = new OptionalJoinSegment(join2);
+           QuerySegment<ExternalTupleSet> seg1 = 
qFactory.getQuerySegment(join1);
+        QuerySegment<ExternalTupleSet> seg2 = qFactory.getQuerySegment(join2);
 
-               PCJNodeConsolidator consolidator = new 
PCJNodeConsolidator(seg1.getOrderedNodes(), seg2.getOrderedNodes());
+               QueryNodeConsolidator consolidator = new 
QueryNodeConsolidator(seg1.getOrderedNodes(), seg2.getOrderedNodes());
 
                List<QueryModelNode> queryNodes = new 
ArrayList<>(seg1.getOrderedNodes());
                QueryModelNode node = queryNodes.remove(5);
@@ -325,10 +332,10 @@ public class PCJNodeConsolidatorTest {
                LeftJoin join1 = (LeftJoin) ((Projection) te1).getArg();
                LeftJoin join2 = (LeftJoin) ((Projection) te2).getArg();
 
-               OptionalJoinSegment seg1 = new OptionalJoinSegment(join1);
-               OptionalJoinSegment seg2 = new OptionalJoinSegment(join2);
+               QuerySegment<ExternalTupleSet> seg1 = 
qFactory.getQuerySegment(join1);
+           QuerySegment<ExternalTupleSet> seg2 = 
qFactory.getQuerySegment(join2);
 
-               PCJNodeConsolidator consolidator = new 
PCJNodeConsolidator(seg1.getOrderedNodes(), seg2.getOrderedNodes());
+               QueryNodeConsolidator consolidator = new 
QueryNodeConsolidator(seg1.getOrderedNodes(), seg2.getOrderedNodes());
                List<QueryModelNode> queryNodes = new 
ArrayList<>(seg2.getOrderedNodes());
 
                Assert.assertTrue(consolidator.consolidateNodes());
@@ -373,10 +380,10 @@ public class PCJNodeConsolidatorTest {
                LeftJoin join1 = (LeftJoin) ((Projection) te1).getArg();
                LeftJoin join2 = (LeftJoin) ((Projection) te2).getArg();
 
-               OptionalJoinSegment seg1 = new OptionalJoinSegment(join1);
-               OptionalJoinSegment seg2 = new OptionalJoinSegment(join2);
+               QuerySegment<ExternalTupleSet> seg1 = 
qFactory.getQuerySegment(join1);
+           QuerySegment<ExternalTupleSet> seg2 = 
qFactory.getQuerySegment(join2);
 
-               PCJNodeConsolidator consolidator = new 
PCJNodeConsolidator(seg1.getOrderedNodes(), seg2.getOrderedNodes());
+               QueryNodeConsolidator consolidator = new 
QueryNodeConsolidator(seg1.getOrderedNodes(), seg2.getOrderedNodes());
                List<QueryModelNode> queryNodes = new 
ArrayList<>(seg2.getOrderedNodes());
 
                Assert.assertTrue(consolidator.consolidateNodes());
@@ -411,10 +418,10 @@ public class PCJNodeConsolidatorTest {
                Join join1 = (Join) ((Projection) te1).getArg();
                Join join2 = (Join) ((Projection) te2).getArg();
 
-               OptionalJoinSegment seg1 = new OptionalJoinSegment(join1);
-               OptionalJoinSegment seg2 = new OptionalJoinSegment(join2);
+               QuerySegment<ExternalTupleSet> seg1 = 
qFactory.getQuerySegment(join1);
+           QuerySegment<ExternalTupleSet> seg2 = 
qFactory.getQuerySegment(join2);
 
-               PCJNodeConsolidator consolidator = new 
PCJNodeConsolidator(seg1.getOrderedNodes(), seg2.getOrderedNodes());
+               QueryNodeConsolidator consolidator = new 
QueryNodeConsolidator(seg1.getOrderedNodes(), seg2.getOrderedNodes());
                List<QueryModelNode> queryNodes = new 
ArrayList<>(seg2.getOrderedNodes());
 
                Assert.assertTrue(consolidator.consolidateNodes());
@@ -454,10 +461,10 @@ public class PCJNodeConsolidatorTest {
                Join join1 = (Join) ((Projection) te1).getArg();
                Join join2 = (Join) ((Projection) te2).getArg();
 
-               OptionalJoinSegment seg1 = new OptionalJoinSegment(join1);
-               OptionalJoinSegment seg2 = new OptionalJoinSegment(join2);
+               QuerySegment<ExternalTupleSet> seg1 = 
qFactory.getQuerySegment(join1);
+           QuerySegment<ExternalTupleSet> seg2 = 
qFactory.getQuerySegment(join2);
 
-               PCJNodeConsolidator consolidator = new 
PCJNodeConsolidator(seg1.getOrderedNodes(), seg2.getOrderedNodes());
+               QueryNodeConsolidator consolidator = new 
QueryNodeConsolidator(seg1.getOrderedNodes(), seg2.getOrderedNodes());
                List<QueryModelNode> queryNodes = new 
ArrayList<>(seg2.getOrderedNodes());
 
                Assert.assertTrue(consolidator.consolidateNodes());

http://git-wip-us.apache.org/repos/asf/incubator-rya/blob/11349b11/extras/indexing/src/test/java/org/apache/rya/indexing/pcj/matching/PCJOptimizerTest.java
----------------------------------------------------------------------
diff --git 
a/extras/indexing/src/test/java/org/apache/rya/indexing/pcj/matching/PCJOptimizerTest.java
 
b/extras/indexing/src/test/java/org/apache/rya/indexing/pcj/matching/PCJOptimizerTest.java
index 261b680..31f4e7b 100644
--- 
a/extras/indexing/src/test/java/org/apache/rya/indexing/pcj/matching/PCJOptimizerTest.java
+++ 
b/extras/indexing/src/test/java/org/apache/rya/indexing/pcj/matching/PCJOptimizerTest.java
@@ -43,7 +43,7 @@ import com.google.common.collect.Sets;
 public class PCJOptimizerTest {
 
     @Test
-    public void testBasicSegment() throws MalformedQueryException {
+    public void testBasicSegment() throws Exception {
 
         String query1 = ""//
                 + "SELECT ?e ?c ?l" //
@@ -84,7 +84,7 @@ public class PCJOptimizerTest {
     }
 
     @Test
-    public void testSegmentWithUnion() throws MalformedQueryException {
+    public void testSegmentWithUnion() throws Exception {
 
         String query1 = ""//
                 + "SELECT ?e ?c ?l" //
@@ -287,7 +287,7 @@ public class PCJOptimizerTest {
     }
 
     @Test
-    public void testSegmentWithLargeUnion() throws MalformedQueryException {
+    public void testSegmentWithLargeUnion() throws Exception {
 
         String query1 = ""//
                 + "SELECT ?e ?c ?l" //
@@ -350,7 +350,7 @@ public class PCJOptimizerTest {
     }
 
     @Test
-    public void testSegmentWithUnionAndFilters() throws 
MalformedQueryException {
+    public void testSegmentWithUnionAndFilters() throws Exception {
 
         String query1 = ""//
                 + "SELECT ?e ?c ?l" //
@@ -407,7 +407,7 @@ public class PCJOptimizerTest {
     }
 
     @Test
-    public void testSegmentWithLeftJoinsAndFilters() throws 
MalformedQueryException {
+    public void testSegmentWithLeftJoinsAndFilters() throws Exception {
 
         String query1 = ""//
                 + "SELECT ?e ?c ?l" //
@@ -457,7 +457,7 @@ public class PCJOptimizerTest {
     }
 
     @Test
-    public void testJoinMatcherRejectsLeftJoinPcj() throws 
MalformedQueryException {
+    public void testJoinMatcherRejectsLeftJoinPcj() throws Exception {
 
         String query1 = ""//
                 + "SELECT ?e ?c ?l" //

http://git-wip-us.apache.org/repos/asf/incubator-rya/blob/11349b11/extras/indexing/src/test/java/org/apache/rya/indexing/statement/metadata/AccumuloStatementMetadataNodeTest.java
----------------------------------------------------------------------
diff --git 
a/extras/indexing/src/test/java/org/apache/rya/indexing/statement/metadata/AccumuloStatementMetadataNodeTest.java
 
b/extras/indexing/src/test/java/org/apache/rya/indexing/statement/metadata/AccumuloStatementMetadataNodeTest.java
new file mode 100644
index 0000000..fc6bdb2
--- /dev/null
+++ 
b/extras/indexing/src/test/java/org/apache/rya/indexing/statement/metadata/AccumuloStatementMetadataNodeTest.java
@@ -0,0 +1,379 @@
+package org.apache.rya.indexing.statement.metadata;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.accumulo.core.client.Connector;
+import org.apache.rya.accumulo.AccumuloRdfConfiguration;
+import org.apache.rya.accumulo.AccumuloRyaDAO;
+import org.apache.rya.api.RdfCloudTripleStoreConfiguration;
+import org.apache.rya.api.domain.RyaStatement;
+import org.apache.rya.api.domain.RyaType;
+import org.apache.rya.api.domain.RyaURI;
+import org.apache.rya.api.domain.StatementMetadata;
+import org.apache.rya.api.persist.RyaDAOException;
+import org.apache.rya.indexing.accumulo.ConfigUtils;
+import 
org.apache.rya.indexing.statement.metadata.matching.StatementMetadataNode;
+import org.junit.After;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.openrdf.model.impl.LiteralImpl;
+import org.openrdf.model.impl.URIImpl;
+import org.openrdf.model.vocabulary.XMLSchema;
+import org.openrdf.query.BindingSet;
+import org.openrdf.query.MalformedQueryException;
+import org.openrdf.query.QueryEvaluationException;
+import org.openrdf.query.algebra.StatementPattern;
+import org.openrdf.query.algebra.evaluation.QueryBindingSet;
+import org.openrdf.query.algebra.helpers.StatementPatternCollector;
+import org.openrdf.query.parser.ParsedQuery;
+import org.openrdf.query.parser.sparql.SPARQLParser;
+
+import info.aduna.iteration.CloseableIteration;
+
+public class AccumuloStatementMetadataNodeTest {
+
+    private AccumuloRyaDAO dao;
+    private AccumuloRdfConfiguration conf;
+    private final String query = "prefix rdf: 
<http://www.w3.org/1999/02/22-rdf-syntax-ns#> select ?x ?y where {_:blankNode 
rdf:type rdf:Statement; rdf:subject <http://Joe>; "
+            + "rdf:predicate <http://worksAt>; rdf:object ?x; 
<http://createdBy> ?y; <http://createdOn> \'2017-01-04\'^^xsd:date }";
+    private final String query2 = "prefix rdf: 
<http://www.w3.org/1999/02/22-rdf-syntax-ns#> select ?x ?y where {_:blankNode 
rdf:type rdf:Statement; rdf:subject ?x; "
+            + "rdf:predicate <http://worksAt>; rdf:object ?y; 
<http://createdBy> ?x; <http://createdOn> \'2017-01-04\'^^xsd:date }";
+
+    @Before
+    public void init() throws Exception {
+        conf = getConf();
+        Connector conn = ConfigUtils.getConnector(conf);
+        dao = new AccumuloRyaDAO();
+        dao.setConnector(conn);
+        dao.init();
+    }
+
+    @After
+    public void close() throws RyaDAOException {
+        dao.destroy();
+    }
+
+    @Test
+    public void simpleQueryWithoutBindingSet()
+            throws MalformedQueryException, QueryEvaluationException, 
RyaDAOException {
+        StatementMetadata metadata = new StatementMetadata();
+        metadata.addMetadata(new RyaURI("http://createdBy";), new 
RyaType("Joe"));
+        metadata.addMetadata(new RyaURI("http://createdOn";), new 
RyaType(XMLSchema.DATE, "2017-01-04"));
+
+        RyaStatement statement = new RyaStatement(new RyaURI("http://Joe";), 
new RyaURI("http://worksAt";),
+                new RyaType("CoffeeShop"), new RyaURI("http://context";), "", 
metadata);
+        dao.add(statement);
+
+        SPARQLParser parser = new SPARQLParser();
+        ParsedQuery pq = parser.parseQuery(query, null);
+        List<StatementPattern> spList = 
StatementPatternCollector.process(pq.getTupleExpr());
+        // RyaQueryEngine<RdfCloudTripleStoreConfiguration> engine =
+        // (RyaQueryEngine<>) dao.getQueryEngine();
+
+        StatementMetadataNode<?> node = new StatementMetadataNode<>(spList, 
conf);
+        CloseableIteration<BindingSet, QueryEvaluationException> iteration = 
node.evaluate(new QueryBindingSet());
+
+        QueryBindingSet bs = new QueryBindingSet();
+        bs.addBinding("x", new LiteralImpl("CoffeeShop"));
+        bs.addBinding("y", new LiteralImpl("Joe"));
+
+        List<BindingSet> bsList = new ArrayList<>();
+        while (iteration.hasNext()) {
+            bsList.add(iteration.next());
+        }
+
+        Assert.assertEquals(1, bsList.size());
+        Assert.assertEquals(bs, bsList.get(0));
+        dao.delete(statement, conf);
+    }
+
+    /**
+     * Tests if results are filtered correctly using the metadata properties. 
In
+     * this case, the date for the ingested RyaStatement differs from the date
+     * specified in the query.
+     * 
+     * @throws MalformedQueryException
+     * @throws QueryEvaluationException
+     * @throws RyaDAOException
+     */
+    @Test
+    public void simpleQueryWithoutBindingSetInvalidProperty()
+            throws MalformedQueryException, QueryEvaluationException, 
RyaDAOException {
+        StatementMetadata metadata = new StatementMetadata();
+        metadata.addMetadata(new RyaURI("http://createdBy";), new 
RyaType("Doug"));
+        metadata.addMetadata(new RyaURI("http://createdOn";), new 
RyaType(XMLSchema.DATE, "2017-02-15"));
+
+        RyaStatement statement = new RyaStatement(new RyaURI("http://Joe";), 
new RyaURI("http://worksAt";),
+                new RyaType("CoffeeShop"), new RyaURI("http://context";), "", 
metadata);
+        dao.add(statement);
+
+        SPARQLParser parser = new SPARQLParser();
+        ParsedQuery pq = parser.parseQuery(query, null);
+        List<StatementPattern> spList = 
StatementPatternCollector.process(pq.getTupleExpr());
+        StatementMetadataNode<AccumuloRdfConfiguration> node = new 
StatementMetadataNode<>(spList, conf);
+        CloseableIteration<BindingSet, QueryEvaluationException> iteration = 
node.evaluate(new QueryBindingSet());
+
+        List<BindingSet> bsList = new ArrayList<>();
+        while (iteration.hasNext()) {
+            bsList.add(iteration.next());
+        }
+        Assert.assertEquals(0, bsList.size());
+        dao.delete(statement, conf);
+    }
+
+    @Test
+    public void simpleQueryWithBindingSet() throws MalformedQueryException, 
QueryEvaluationException, RyaDAOException {
+
+        StatementMetadata metadata = new StatementMetadata();
+        metadata.addMetadata(new RyaURI("http://createdBy";), new 
RyaType("Joe"));
+        metadata.addMetadata(new RyaURI("http://createdOn";), new 
RyaType(XMLSchema.DATE, "2017-01-04"));
+
+        RyaStatement statement1 = new RyaStatement(new RyaURI("http://Joe";), 
new RyaURI("http://worksAt";),
+                new RyaType("CoffeeShop"), new RyaURI("http://context";), "", 
metadata);
+        RyaStatement statement2 = new RyaStatement(new RyaURI("http://Joe";), 
new RyaURI("http://worksAt";),
+                new RyaType("HardwareStore"), new RyaURI("http://context";), 
"", metadata);
+        dao.add(statement1);
+        dao.add(statement2);
+
+        SPARQLParser parser = new SPARQLParser();
+        ParsedQuery pq = parser.parseQuery(query, null);
+        List<StatementPattern> spList = 
StatementPatternCollector.process(pq.getTupleExpr());
+        StatementMetadataNode<AccumuloRdfConfiguration> node = new 
StatementMetadataNode<>(spList, conf);
+
+        QueryBindingSet bsConstraint = new QueryBindingSet();
+        bsConstraint.addBinding("x", new LiteralImpl("CoffeeShop"));
+        bsConstraint.addBinding("z", new LiteralImpl("Virginia"));
+
+        CloseableIteration<BindingSet, QueryEvaluationException> iteration = 
node.evaluate(bsConstraint);
+
+        QueryBindingSet expected = new QueryBindingSet();
+        expected.addBinding("x", new LiteralImpl("CoffeeShop"));
+        expected.addBinding("y", new LiteralImpl("Joe"));
+        expected.addBinding("z", new LiteralImpl("Virginia"));
+
+        List<BindingSet> bsList = new ArrayList<>();
+        while (iteration.hasNext()) {
+            bsList.add(iteration.next());
+        }
+
+        Assert.assertEquals(1, bsList.size());
+        Assert.assertEquals(expected, bsList.get(0));
+
+        dao.delete(statement1, conf);
+        dao.delete(statement2, conf);
+    }
+
+    /**
+     * Tests to see if correct result is passed back when a metadata statement
+     * is joined with a StatementPattern statement (i.e. a common variable
+     * appears in a StatementPattern statement and a metadata statement).
+     * StatementPattern statements have either rdf:subject, rdf:predicate, or
+     * rdf:object as the predicate while a metadata statement is any statement
+     * in the reified query whose predicate is not rdf:type and not a
+     * StatementPattern predicate.
+     * 
+     * @throws MalformedQueryException
+     * @throws QueryEvaluationException
+     * @throws RyaDAOException
+     */
+    @Test
+    public void simpleQueryWithBindingSetJoinPropertyToSubject()
+            throws MalformedQueryException, QueryEvaluationException, 
RyaDAOException {
+
+        StatementMetadata metadata = new StatementMetadata();
+        metadata.addMetadata(new RyaURI("http://createdBy";), new 
RyaURI("http://Joe";));
+        metadata.addMetadata(new RyaURI("http://createdOn";), new 
RyaType(XMLSchema.DATE, "2017-01-04"));
+
+        RyaStatement statement1 = new RyaStatement(new RyaURI("http://Joe";), 
new RyaURI("http://worksAt";),
+                new RyaType("CoffeeShop"), new RyaURI("http://context";), "", 
metadata);
+        RyaStatement statement2 = new RyaStatement(new RyaURI("http://Bob";), 
new RyaURI("http://worksAt";),
+                new RyaType("HardwareStore"), new RyaURI("http://context";), 
"", metadata);
+        dao.add(statement1);
+        dao.add(statement2);
+
+        SPARQLParser parser = new SPARQLParser();
+        ParsedQuery pq = parser.parseQuery(query2, null);
+        List<StatementPattern> spList = 
StatementPatternCollector.process(pq.getTupleExpr());
+        StatementMetadataNode<AccumuloRdfConfiguration> node = new 
StatementMetadataNode<>(spList, conf);
+
+        List<BindingSet> bsCollection = new ArrayList<>();
+        QueryBindingSet bsConstraint1 = new QueryBindingSet();
+        bsConstraint1.addBinding("y", new LiteralImpl("CoffeeShop"));
+        bsConstraint1.addBinding("z", new LiteralImpl("Virginia"));
+
+        QueryBindingSet bsConstraint2 = new QueryBindingSet();
+        bsConstraint2.addBinding("y", new LiteralImpl("HardwareStore"));
+        bsConstraint2.addBinding("z", new LiteralImpl("Maryland"));
+        bsCollection.add(bsConstraint1);
+        bsCollection.add(bsConstraint2);
+
+        CloseableIteration<BindingSet, QueryEvaluationException> iteration = 
node.evaluate(bsCollection);
+
+        QueryBindingSet expected = new QueryBindingSet();
+        expected.addBinding("y", new LiteralImpl("CoffeeShop"));
+        expected.addBinding("x", new URIImpl("http://Joe";));
+        expected.addBinding("z", new LiteralImpl("Virginia"));
+
+        List<BindingSet> bsList = new ArrayList<>();
+        while (iteration.hasNext()) {
+            bsList.add(iteration.next());
+        }
+
+        Assert.assertEquals(1, bsList.size());
+        Assert.assertEquals(expected, bsList.get(0));
+
+        dao.delete(statement1, conf);
+        dao.delete(statement2, conf);
+    }
+
+    /**
+     * Tests if the StatementMetadataNode joins BindingSet correctly for
+     * variables appearing in metadata statements. In this case, the metadata
+     * statements are (_:blankNode <http://createdOn 2017-01-04 ) and
+     * (_:blankNode <http://createdBy> ?y). The variable ?y appears as the
+     * object in the above metadata statement and its values are joined to the
+     * constraint BindingSets in the example below.
+     * 
+     * @throws MalformedQueryException
+     * @throws QueryEvaluationException
+     * @throws RyaDAOException
+     */
+    @Test
+    public void simpleQueryWithBindingSetJoinOnProperty()
+            throws MalformedQueryException, QueryEvaluationException, 
RyaDAOException {
+
+        StatementMetadata metadata = new StatementMetadata();
+        metadata.addMetadata(new RyaURI("http://createdBy";), new 
RyaType("Joe"));
+        metadata.addMetadata(new RyaURI("http://createdOn";), new 
RyaType(XMLSchema.DATE, "2017-01-04"));
+
+        RyaStatement statement1 = new RyaStatement(new RyaURI("http://Joe";), 
new RyaURI("http://worksAt";),
+                new RyaType("CoffeeShop"), new RyaURI("http://context";), "", 
metadata);
+        dao.add(statement1);
+
+        SPARQLParser parser = new SPARQLParser();
+        ParsedQuery pq = parser.parseQuery(query, null);
+        List<StatementPattern> spList = 
StatementPatternCollector.process(pq.getTupleExpr());
+        StatementMetadataNode<AccumuloRdfConfiguration> node = new 
StatementMetadataNode<>(spList, conf);
+
+        QueryBindingSet bsConstraint = new QueryBindingSet();
+        bsConstraint.addBinding("x", new LiteralImpl("CoffeeShop"));
+        bsConstraint.addBinding("y", new LiteralImpl("Doug"));
+
+        CloseableIteration<BindingSet, QueryEvaluationException> iteration = 
node.evaluate(bsConstraint);
+
+        List<BindingSet> bsList = new ArrayList<>();
+        while (iteration.hasNext()) {
+            bsList.add(iteration.next());
+        }
+
+        Assert.assertEquals(0, bsList.size());
+        dao.delete(statement1, conf);
+    }
+
+    /**
+     * Tests if StatementMetadataNode joins BindingSet values correctly for
+     * variables appearing as the object in one of the StatementPattern
+     * statements (in the case ?x appears as the Object in the statement
+     * _:blankNode rdf:object ?x). StatementPattern statements have either
+     * rdf:subject, rdf:predicate, or rdf:object as the predicate.
+     * 
+     * @throws MalformedQueryException
+     * @throws QueryEvaluationException
+     * @throws RyaDAOException
+     */
+    @Test
+    public void simpleQueryWithBindingSetCollection()
+            throws MalformedQueryException, QueryEvaluationException, 
RyaDAOException {
+
+        StatementMetadata metadata = new StatementMetadata();
+        metadata.addMetadata(new RyaURI("http://createdBy";), new 
RyaType("Joe"));
+        metadata.addMetadata(new RyaURI("http://createdOn";), new 
RyaType(XMLSchema.DATE, "2017-01-04"));
+
+        RyaStatement statement1 = new RyaStatement(new RyaURI("http://Joe";), 
new RyaURI("http://worksAt";),
+                new RyaType("CoffeeShop"), new RyaURI("http://context";), "", 
metadata);
+        RyaStatement statement2 = new RyaStatement(new RyaURI("http://Joe";), 
new RyaURI("http://worksAt";),
+                new RyaType("HardwareStore"), new RyaURI("http://context";), 
"", metadata);
+        dao.add(statement1);
+        dao.add(statement2);
+
+        SPARQLParser parser = new SPARQLParser();
+        ParsedQuery pq = parser.parseQuery(query, null);
+        List<StatementPattern> spList = 
StatementPatternCollector.process(pq.getTupleExpr());
+        StatementMetadataNode<AccumuloRdfConfiguration> node = new 
StatementMetadataNode<>(spList, conf);
+
+        List<BindingSet> bsCollection = new ArrayList<>();
+        QueryBindingSet bsConstraint1 = new QueryBindingSet();
+        bsConstraint1.addBinding("x", new LiteralImpl("CoffeeShop"));
+        bsConstraint1.addBinding("z", new LiteralImpl("Virginia"));
+
+        QueryBindingSet bsConstraint2 = new QueryBindingSet();
+        bsConstraint2.addBinding("x", new LiteralImpl("HardwareStore"));
+        bsConstraint2.addBinding("z", new LiteralImpl("Maryland"));
+
+        QueryBindingSet bsConstraint3 = new QueryBindingSet();
+        bsConstraint3.addBinding("x", new LiteralImpl("BurgerShack"));
+        bsConstraint3.addBinding("z", new LiteralImpl("Delaware"));
+        bsCollection.add(bsConstraint1);
+        bsCollection.add(bsConstraint2);
+        bsCollection.add(bsConstraint3);
+
+        CloseableIteration<BindingSet, QueryEvaluationException> iteration = 
node.evaluate(bsCollection);
+
+        QueryBindingSet expected1 = new QueryBindingSet();
+        expected1.addBinding("x", new LiteralImpl("CoffeeShop"));
+        expected1.addBinding("y", new LiteralImpl("Joe"));
+        expected1.addBinding("z", new LiteralImpl("Virginia"));
+
+        QueryBindingSet expected2 = new QueryBindingSet();
+        expected2.addBinding("x", new LiteralImpl("HardwareStore"));
+        expected2.addBinding("y", new LiteralImpl("Joe"));
+        expected2.addBinding("z", new LiteralImpl("Maryland"));
+
+        List<BindingSet> bsList = new ArrayList<>();
+        while (iteration.hasNext()) {
+            bsList.add(iteration.next());
+        }
+
+        Assert.assertEquals(2, bsList.size());
+        Assert.assertEquals(expected1, bsList.get(1));
+        Assert.assertEquals(expected2, bsList.get(0));
+
+        dao.delete(statement1, conf);
+        dao.delete(statement2, conf);
+    }
+
+    private static AccumuloRdfConfiguration getConf() {
+
+        final AccumuloRdfConfiguration conf = new AccumuloRdfConfiguration();
+
+        conf.setBoolean(ConfigUtils.USE_MOCK_INSTANCE, true);
+        conf.set(RdfCloudTripleStoreConfiguration.CONF_TBL_PREFIX, "rya_");
+        conf.set(ConfigUtils.CLOUDBASE_USER, "root");
+        conf.set(ConfigUtils.CLOUDBASE_PASSWORD, "");
+        conf.set(ConfigUtils.CLOUDBASE_INSTANCE, "instance");
+        conf.set(ConfigUtils.CLOUDBASE_AUTHS, "");
+
+        return conf;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-rya/blob/11349b11/extras/indexing/src/test/java/org/apache/rya/indexing/statement/metadata/AccumuloStatementMetadataOptimizerIT.java
----------------------------------------------------------------------
diff --git 
a/extras/indexing/src/test/java/org/apache/rya/indexing/statement/metadata/AccumuloStatementMetadataOptimizerIT.java
 
b/extras/indexing/src/test/java/org/apache/rya/indexing/statement/metadata/AccumuloStatementMetadataOptimizerIT.java
new file mode 100644
index 0000000..2353f51
--- /dev/null
+++ 
b/extras/indexing/src/test/java/org/apache/rya/indexing/statement/metadata/AccumuloStatementMetadataOptimizerIT.java
@@ -0,0 +1,284 @@
+package org.apache.rya.indexing.statement.metadata;
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import org.apache.accumulo.core.client.Connector;
+import org.apache.rya.accumulo.AccumuloRdfConfiguration;
+import org.apache.rya.accumulo.AccumuloRyaDAO;
+import org.apache.rya.api.RdfCloudTripleStoreConfiguration;
+import org.apache.rya.api.domain.RyaStatement;
+import org.apache.rya.api.domain.RyaType;
+import org.apache.rya.api.domain.RyaURI;
+import org.apache.rya.api.domain.StatementMetadata;
+import org.apache.rya.api.persist.RyaDAOException;
+import org.apache.rya.indexing.accumulo.ConfigUtils;
+import org.apache.rya.sail.config.RyaSailFactory;
+import org.junit.After;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.openrdf.model.impl.LiteralImpl;
+import org.openrdf.model.impl.URIImpl;
+import org.openrdf.model.vocabulary.XMLSchema;
+import org.openrdf.query.BindingSet;
+import org.openrdf.query.MalformedQueryException;
+import org.openrdf.query.QueryEvaluationException;
+import org.openrdf.query.QueryLanguage;
+import org.openrdf.query.TupleQueryResult;
+import org.openrdf.query.algebra.evaluation.QueryBindingSet;
+import org.openrdf.repository.sail.SailRepository;
+import org.openrdf.repository.sail.SailRepositoryConnection;
+import org.openrdf.sail.Sail;
+
+public class AccumuloStatementMetadataOptimizerIT {
+
+    private RdfCloudTripleStoreConfiguration conf;
+    private Sail sail;
+    private SailRepository repo;
+    private SailRepositoryConnection conn;
+    private AccumuloRyaDAO dao;
+    private final String query1 = "prefix rdf: 
<http://www.w3.org/1999/02/22-rdf-syntax-ns#> select ?x ?y where {_:blankNode 
rdf:type rdf:Statement; rdf:subject <http://Joe>; "
+            + "rdf:predicate <http://worksAt>; rdf:object ?x; 
<http://createdBy> ?y; <http://createdOn> \'2017-01-04\'^^xsd:date }";
+    private final String query2 = "prefix rdf: 
<http://www.w3.org/1999/02/22-rdf-syntax-ns#> select ?a ?b ?c where 
{_:blankNode1 rdf:type rdf:Statement; rdf:subject ?a; "
+            + "rdf:predicate <http://worksAt>; rdf:object 
<http://BurgerShack>; <http://createdBy> ?c; <http://createdOn> 
\'2017-01-04\'^^xsd:date. "
+            + "_:blankNode2 rdf:type rdf:Statement; rdf:subject ?a; "
+            + "rdf:predicate <http://talksTo>; rdf:object ?b; 
<http://createdBy> ?c; <http://createdOn> \'2017-01-04\'^^xsd:date }";
+
+    @Before
+    public void init() throws Exception {
+        conf = getConf();
+        sail = RyaSailFactory.getInstance(conf);
+        repo = new SailRepository(sail);
+        conn = repo.getConnection();
+
+        Connector conn = ConfigUtils.getConnector(conf);
+        dao = new AccumuloRyaDAO();
+        dao.setConnector(conn);
+        dao.init();
+    }
+
+    @After
+    public void close() throws Exception {
+        conn.close();
+        repo.shutDown();
+        sail.shutDown();
+        sail.shutDown();
+        dao.destroy();
+    }
+
+    @Test
+    public void simpleQueryWithoutBindingSet() throws Exception {
+        StatementMetadata metadata = new StatementMetadata();
+        metadata.addMetadata(new RyaURI("http://createdBy";), new 
RyaType("Joe"));
+        metadata.addMetadata(new RyaURI("http://createdOn";), new 
RyaType(XMLSchema.DATE, "2017-01-04"));
+
+        RyaStatement statement = new RyaStatement(new RyaURI("http://Joe";), 
new RyaURI("http://worksAt";),
+                new RyaType("CoffeeShop"), new RyaURI("http://context";), "", 
metadata);
+        dao.add(statement);
+
+        TupleQueryResult result = conn.prepareTupleQuery(QueryLanguage.SPARQL, 
query1).evaluate();
+
+        QueryBindingSet bs = new QueryBindingSet();
+        bs.addBinding("x", new LiteralImpl("CoffeeShop"));
+        bs.addBinding("y", new LiteralImpl("Joe"));
+
+        List<BindingSet> bsList = new ArrayList<>();
+        while (result.hasNext()) {
+            bsList.add(result.next());
+        }
+
+        System.out.println(bsList);
+        Assert.assertEquals(1, bsList.size());
+        Assert.assertEquals(bs, bsList.get(0));
+        dao.delete(statement, (AccumuloRdfConfiguration) conf);
+    }
+
+    /**
+     * Tests if results are filtered correctly using the metadata properties. 
In
+     * this case, the date for the ingested RyaStatement differs from the date
+     * specified in the query.
+     * 
+     * @throws MalformedQueryException
+     * @throws QueryEvaluationException
+     * @throws RyaDAOException
+     */
+    @Test
+    public void simpleQueryWithoutBindingSetInvalidProperty() throws Exception 
{
+        StatementMetadata metadata = new StatementMetadata();
+        metadata.addMetadata(new RyaURI("http://createdBy";), new 
RyaType("Doug"));
+        metadata.addMetadata(new RyaURI("http://createdOn";), new 
RyaType(XMLSchema.DATE, "2017-02-15"));
+
+        RyaStatement statement = new RyaStatement(new RyaURI("http://Joe";), 
new RyaURI("http://worksAt";),
+                new RyaType("CoffeeShop"), new RyaURI("http://context";), "", 
metadata);
+        dao.add(statement);
+
+        TupleQueryResult result = conn.prepareTupleQuery(QueryLanguage.SPARQL, 
query1).evaluate();
+
+        List<BindingSet> bsList = new ArrayList<>();
+        while (result.hasNext()) {
+            bsList.add(result.next());
+        }
+        Assert.assertEquals(0, bsList.size());
+        dao.delete(statement, (AccumuloRdfConfiguration) conf);
+    }
+
+//    @Test
+//    public void simpleDataTypeTest() throws Exception {
+//        StatementMetadata metadata = new StatementMetadata();
+//        metadata.addMetadata(new RyaURI("http://createdBy";), new 
RyaType("Doug"));
+//        metadata.addMetadata(new RyaURI("http://createdOn";), new 
RyaType(XMLSchema.DATE, "2017-02-15"));
+//
+//        RyaStatement statement = new RyaStatement(new RyaURI("http://Joe";), 
new RyaURI("http://worksAt";),
+//                new RyaType("http://BurgerShack";), new 
RyaURI("http://context";), "", metadata);
+//        dao.add(statement);
+//        RyaStatement statement2 = new RyaStatement(new RyaURI("http://Bob";), 
new RyaURI("http://worksAt";),
+//                new RyaURI("http://BurgerShack";), new 
RyaURI("http://context";), "", metadata);
+//        dao.add(statement2);
+//        
+//        String temp = "select ?x where { ?x <http://worksAt> 
'http://BurgerShack' . }";
+//        TupleQueryResult result = 
conn.prepareTupleQuery(QueryLanguage.SPARQL, temp).evaluate();
+//        
+//        List<BindingSet> bsList = new ArrayList<>();
+//        while (result.hasNext()) {
+//            bsList.add(result.next());
+//        }
+//        System.out.println("Bindings are: " + bsList);
+//        dao.delete(statement, (AccumuloRdfConfiguration) conf);
+//        dao.delete(statement2, (AccumuloRdfConfiguration) conf);
+//    }
+
+    
+    @Test
+    public void simpleQueryWithBindingSet() throws Exception {
+
+        StatementMetadata metadata = new StatementMetadata();
+        metadata.addMetadata(new RyaURI("http://createdBy";), new 
RyaType("Joe"));
+        metadata.addMetadata(new RyaURI("http://createdOn";), new 
RyaType(XMLSchema.DATE, "2017-01-04"));
+
+        RyaStatement statement1 = new RyaStatement(new RyaURI("http://Joe";), 
new RyaURI("http://worksAt";),
+                new RyaType("CoffeeShop"), new RyaURI("http://context";), "", 
metadata);
+        RyaStatement statement2 = new RyaStatement(new RyaURI("http://Joe";), 
new RyaURI("http://worksAt";),
+                new RyaType("HardwareStore"), new RyaURI("http://context";), 
"", metadata);
+        dao.add(statement1);
+        dao.add(statement2);
+
+        TupleQueryResult result = conn.prepareTupleQuery(QueryLanguage.SPARQL, 
query1).evaluate();
+
+        Set<BindingSet> expected = new HashSet<>();
+        QueryBindingSet expected1 = new QueryBindingSet();
+        expected1.addBinding("x", new LiteralImpl("CoffeeShop"));
+        expected1.addBinding("y", new LiteralImpl("Joe"));
+        QueryBindingSet expected2 = new QueryBindingSet();
+        expected2.addBinding("x", new LiteralImpl("HardwareStore"));
+        expected2.addBinding("y", new LiteralImpl("Joe"));
+        expected.add(expected1);
+        expected.add(expected2);
+
+        Set<BindingSet> bsSet = new HashSet<>();
+        while (result.hasNext()) {
+            bsSet.add(result.next());
+        }
+
+        Assert.assertEquals(expected, bsSet);
+
+        dao.delete(statement1, (AccumuloRdfConfiguration) conf);
+        dao.delete(statement2, (AccumuloRdfConfiguration) conf);
+    }
+
+    /**
+     * Tests to see if correct result is passed back when a metadata statement
+     * is joined with a StatementPattern statement (i.e. a common variable
+     * appears in a StatementPattern statement and a metadata statement).
+     * StatementPattern statements have either rdf:subject, rdf:predicate, or
+     * rdf:object as the predicate while a metadata statement is any statement
+     * in the reified query whose predicate is not rdf:type and not a
+     * StatementPattern predicate.
+     *
+     * @throws MalformedQueryException
+     * @throws QueryEvaluationException
+     * @throws RyaDAOException
+     */
+    @Test
+    public void simpleQueryWithBindingSetJoinPropertyToSubject() throws 
Exception {
+
+        StatementMetadata metadata1 = new StatementMetadata();
+        metadata1.addMetadata(new RyaURI("http://createdBy";), new 
RyaURI("http://Doug";));
+        metadata1.addMetadata(new RyaURI("http://createdOn";), new 
RyaType(XMLSchema.DATE, "2017-01-04"));
+        StatementMetadata metadata2 = new StatementMetadata();
+        metadata2.addMetadata(new RyaURI("http://createdBy";), new 
RyaURI("http://Bob";));
+        metadata2.addMetadata(new RyaURI("http://createdOn";), new 
RyaType(XMLSchema.DATE, "2017-02-04"));
+
+        RyaStatement statement1 = new RyaStatement(new RyaURI("http://Joe";), 
new RyaURI("http://worksAt";),
+                new RyaURI("http://BurgerShack";), new 
RyaURI("http://context";), "", metadata1);
+        RyaStatement statement2 = new RyaStatement(new RyaURI("http://Joe";), 
new RyaURI("http://talksTo";),
+                new RyaURI("http://Betty";), new RyaURI("http://context";), "", 
metadata1);
+        RyaStatement statement3 = new RyaStatement(new RyaURI("http://Fred";), 
new RyaURI("http://talksTo";),
+                new RyaType("http://Amanda";), new RyaURI("http://context";), 
"", metadata1);
+        RyaStatement statement4 = new RyaStatement(new RyaURI("http://Joe";), 
new RyaURI("http://talksTo";),
+                new RyaType("http://Wanda";), new RyaURI("http://context";), "", 
metadata2);
+        dao.add(statement1);
+        dao.add(statement2);
+        dao.add(statement3);
+        dao.add(statement4);
+
+        TupleQueryResult result = conn.prepareTupleQuery(QueryLanguage.SPARQL, 
query2).evaluate();
+
+        Set<BindingSet> expected = new HashSet<>();
+        QueryBindingSet expected1 = new QueryBindingSet();
+        expected1.addBinding("b", new URIImpl("http://Betty";));
+        expected1.addBinding("a", new URIImpl("http://Joe";));
+        expected1.addBinding("c", new URIImpl("http://Doug";));
+        expected.add(expected1);
+
+        Set<BindingSet> bsSet = new HashSet<>();
+        while (result.hasNext()) {
+            bsSet.add(result.next());
+        }
+
+        Assert.assertEquals(expected, bsSet);
+
+        dao.delete(statement1, (AccumuloRdfConfiguration) conf);
+        dao.delete(statement2, (AccumuloRdfConfiguration) conf);
+        dao.delete(statement3, (AccumuloRdfConfiguration) conf);
+        dao.delete(statement4, (AccumuloRdfConfiguration) conf);
+    }
+
+    private static RdfCloudTripleStoreConfiguration getConf() {
+
+        RdfCloudTripleStoreConfiguration conf;
+        Set<RyaURI> propertySet = new HashSet<RyaURI>(
+                Arrays.asList(new RyaURI("http://createdBy";), new 
RyaURI("http://createdOn";)));
+        conf = new AccumuloRdfConfiguration();
+        conf.setBoolean(ConfigUtils.USE_MOCK_INSTANCE, true);
+        conf.set(RdfCloudTripleStoreConfiguration.CONF_TBL_PREFIX, "rya_");
+        conf.set(ConfigUtils.CLOUDBASE_USER, "root");
+        conf.set(ConfigUtils.CLOUDBASE_PASSWORD, "");
+        conf.set(ConfigUtils.CLOUDBASE_INSTANCE, "instance");
+        conf.set(ConfigUtils.CLOUDBASE_AUTHS, "");
+        conf.setUseStatementMetadata(true);
+        conf.setStatementMetadataProperties(propertySet);
+        return conf;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-rya/blob/11349b11/extras/indexing/src/test/java/org/apache/rya/indexing/statement/metadata/MongoStatementMetadataIT.java
----------------------------------------------------------------------
diff --git 
a/extras/indexing/src/test/java/org/apache/rya/indexing/statement/metadata/MongoStatementMetadataIT.java
 
b/extras/indexing/src/test/java/org/apache/rya/indexing/statement/metadata/MongoStatementMetadataIT.java
new file mode 100644
index 0000000..8b8d1df
--- /dev/null
+++ 
b/extras/indexing/src/test/java/org/apache/rya/indexing/statement/metadata/MongoStatementMetadataIT.java
@@ -0,0 +1,275 @@
+package org.apache.rya.indexing.statement.metadata;
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import org.apache.rya.api.domain.RyaStatement;
+import org.apache.rya.api.domain.RyaType;
+import org.apache.rya.api.domain.RyaURI;
+import org.apache.rya.api.domain.StatementMetadata;
+import org.apache.rya.api.persist.RyaDAOException;
+import org.apache.rya.indexing.accumulo.ConfigUtils;
+import org.apache.rya.mongodb.MockMongoFactory;
+import org.apache.rya.mongodb.MongoConnectorFactory;
+import org.apache.rya.mongodb.MongoDBRdfConfiguration;
+import org.apache.rya.mongodb.MongoDBRyaDAO;
+import org.apache.rya.sail.config.RyaSailFactory;
+import org.junit.After;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.openrdf.model.impl.LiteralImpl;
+import org.openrdf.model.impl.URIImpl;
+import org.openrdf.model.vocabulary.XMLSchema;
+import org.openrdf.query.BindingSet;
+import org.openrdf.query.MalformedQueryException;
+import org.openrdf.query.QueryEvaluationException;
+import org.openrdf.query.QueryLanguage;
+import org.openrdf.query.TupleQueryResult;
+import org.openrdf.query.algebra.evaluation.QueryBindingSet;
+import org.openrdf.repository.sail.SailRepository;
+import org.openrdf.repository.sail.SailRepositoryConnection;
+import org.openrdf.sail.Sail;
+
+import com.mongodb.MongoClient;
+
+import de.flapdoodle.embed.mongo.distribution.Version;
+
+public class MongoStatementMetadataIT {
+
+    protected MockMongoFactory testsFactory;
+    protected MongoClient mongoClient;
+    private MongoDBRdfConfiguration conf;
+    private Sail sail;
+    private SailRepository repo;
+    private SailRepositoryConnection conn;
+    private MongoDBRyaDAO dao;
+    private final String query1 = "prefix rdf: 
<http://www.w3.org/1999/02/22-rdf-syntax-ns#> select ?x ?y where {_:blankNode 
rdf:type rdf:Statement; rdf:subject <http://Joe>; "
+            + "rdf:predicate <http://worksAt>; rdf:object ?x; 
<http://createdBy> ?y; <http://createdOn> \'2017-01-04\'^^xsd:date }";
+    private final String query2 = "prefix rdf: 
<http://www.w3.org/1999/02/22-rdf-syntax-ns#> select ?a ?b ?c where 
{_:blankNode1 rdf:type rdf:Statement; rdf:subject ?a; "
+            + "rdf:predicate <http://worksAt>; rdf:object 
<http://BurgerShack>; <http://createdBy> ?c; <http://createdOn> 
\'2017-01-04\'^^xsd:date. "
+            + "_:blankNode2 rdf:type rdf:Statement; rdf:subject ?a; "
+            + "rdf:predicate <http://talksTo>; rdf:object ?b; 
<http://createdBy> ?c; <http://createdOn> \'2017-01-04\'^^xsd:date }";
+
+    @Before
+    public void init() throws Exception {
+        testsFactory = MockMongoFactory.with(Version.Main.PRODUCTION);
+        mongoClient = testsFactory.newMongoClient();
+        conf = getConf();
+        sail = RyaSailFactory.getInstance(conf);
+        repo = new SailRepository(sail);
+        conn = repo.getConnection();
+
+        dao = new MongoDBRyaDAO(conf, mongoClient);
+        dao.init();
+    }
+
+    @After
+    public void close() throws Exception {
+        conn.close();
+        repo.shutDown();
+        sail.shutDown();
+        sail.shutDown();
+        dao.destroy();
+        
+        if (mongoClient != null) {
+            mongoClient.close();
+        }
+        if (testsFactory != null) {
+            testsFactory.shutdown();
+        }
+        MongoConnectorFactory.closeMongoClient();
+    }
+    
+
+    @Test
+    public void simpleQueryWithoutBindingSet() throws Exception {
+        StatementMetadata metadata = new StatementMetadata();
+        metadata.addMetadata(new RyaURI("http://createdBy";), new 
RyaType("Joe"));
+        metadata.addMetadata(new RyaURI("http://createdOn";), new 
RyaType(XMLSchema.DATE, "2017-01-04"));
+
+        RyaStatement statement = new RyaStatement(new RyaURI("http://Joe";), 
new RyaURI("http://worksAt";),
+                new RyaType("CoffeeShop"), new RyaURI("http://context";), "", 
metadata);
+        dao.add(statement);
+
+        TupleQueryResult result = conn.prepareTupleQuery(QueryLanguage.SPARQL, 
query1).evaluate();
+
+        QueryBindingSet bs = new QueryBindingSet();
+        bs.addBinding("x", new LiteralImpl("CoffeeShop"));
+        bs.addBinding("y", new LiteralImpl("Joe"));
+
+        List<BindingSet> bsList = new ArrayList<>();
+        while (result.hasNext()) {
+            bsList.add(result.next());
+        }
+
+        System.out.println(bsList);
+        Assert.assertEquals(1, bsList.size());
+        Assert.assertEquals(bs, bsList.get(0));
+        dao.delete(statement, conf);
+    }
+
+    /**
+     * Tests if results are filtered correctly using the metadata properties. 
In
+     * this case, the date for the ingested RyaStatement differs from the date
+     * specified in the query.
+     * 
+     * @throws MalformedQueryException
+     * @throws QueryEvaluationException
+     * @throws RyaDAOException
+     */
+    @Test
+    public void simpleQueryWithoutBindingSetInvalidProperty() throws Exception 
{
+        StatementMetadata metadata = new StatementMetadata();
+        metadata.addMetadata(new RyaURI("http://createdBy";), new 
RyaType("Doug"));
+        metadata.addMetadata(new RyaURI("http://createdOn";), new 
RyaType(XMLSchema.DATE, "2017-02-15"));
+
+        RyaStatement statement = new RyaStatement(new RyaURI("http://Joe";), 
new RyaURI("http://worksAt";),
+                new RyaType("CoffeeShop"), new RyaURI("http://context";), "", 
metadata);
+        dao.add(statement);
+
+        TupleQueryResult result = conn.prepareTupleQuery(QueryLanguage.SPARQL, 
query1).evaluate();
+
+        List<BindingSet> bsList = new ArrayList<>();
+        while (result.hasNext()) {
+            bsList.add(result.next());
+        }
+        Assert.assertEquals(0, bsList.size());
+        dao.delete(statement, conf);
+    }
+
+    @Test
+    public void simpleQueryWithBindingSet() throws Exception {
+
+        StatementMetadata metadata = new StatementMetadata();
+        metadata.addMetadata(new RyaURI("http://createdBy";), new 
RyaType("Joe"));
+        metadata.addMetadata(new RyaURI("http://createdOn";), new 
RyaType(XMLSchema.DATE, "2017-01-04"));
+
+        RyaStatement statement1 = new RyaStatement(new RyaURI("http://Joe";), 
new RyaURI("http://worksAt";),
+                new RyaType("CoffeeShop"), new RyaURI("http://context";), "", 
metadata);
+        RyaStatement statement2 = new RyaStatement(new RyaURI("http://Joe";), 
new RyaURI("http://worksAt";),
+                new RyaType("HardwareStore"), new RyaURI("http://context";), 
"", metadata);
+        dao.add(statement1);
+        dao.add(statement2);
+
+        TupleQueryResult result = conn.prepareTupleQuery(QueryLanguage.SPARQL, 
query1).evaluate();
+
+        Set<BindingSet> expected = new HashSet<>();
+        QueryBindingSet expected1 = new QueryBindingSet();
+        expected1.addBinding("x", new LiteralImpl("CoffeeShop"));
+        expected1.addBinding("y", new LiteralImpl("Joe"));
+        QueryBindingSet expected2 = new QueryBindingSet();
+        expected2.addBinding("x", new LiteralImpl("HardwareStore"));
+        expected2.addBinding("y", new LiteralImpl("Joe"));
+        expected.add(expected1);
+        expected.add(expected2);
+
+        Set<BindingSet> bsSet = new HashSet<>();
+        while (result.hasNext()) {
+            bsSet.add(result.next());
+        }
+
+        Assert.assertEquals(expected, bsSet);
+
+        dao.delete(statement1, conf);
+        dao.delete(statement2, conf);
+    }
+
+    /**
+     * Tests to see if correct result is passed back when a metadata statement
+     * is joined with a StatementPattern statement (i.e. a common variable
+     * appears in a StatementPattern statement and a metadata statement).
+     * StatementPattern statements have either rdf:subject, rdf:predicate, or
+     * rdf:object as the predicate while a metadata statement is any statement
+     * in the reified query whose predicate is not rdf:type and not a
+     * StatementPattern predicate.
+     *
+     * @throws MalformedQueryException
+     * @throws QueryEvaluationException
+     * @throws RyaDAOException
+     */
+    @Test
+    public void simpleQueryWithBindingSetJoinPropertyToSubject() throws 
Exception {
+
+        StatementMetadata metadata1 = new StatementMetadata();
+        metadata1.addMetadata(new RyaURI("http://createdBy";), new 
RyaURI("http://Doug";));
+        metadata1.addMetadata(new RyaURI("http://createdOn";), new 
RyaType(XMLSchema.DATE, "2017-01-04"));
+        StatementMetadata metadata2 = new StatementMetadata();
+        metadata2.addMetadata(new RyaURI("http://createdBy";), new 
RyaURI("http://Bob";));
+        metadata2.addMetadata(new RyaURI("http://createdOn";), new 
RyaType(XMLSchema.DATE, "2017-02-04"));
+
+        RyaStatement statement1 = new RyaStatement(new RyaURI("http://Joe";), 
new RyaURI("http://worksAt";),
+                new RyaURI("http://BurgerShack";), new 
RyaURI("http://context";), "", metadata1);
+        RyaStatement statement2 = new RyaStatement(new RyaURI("http://Joe";), 
new RyaURI("http://talksTo";),
+                new RyaURI("http://Betty";), new RyaURI("http://context";), "", 
metadata1);
+        RyaStatement statement3 = new RyaStatement(new RyaURI("http://Fred";), 
new RyaURI("http://talksTo";),
+                new RyaURI("http://Amanda";), new RyaURI("http://context";), "", 
metadata1);
+        RyaStatement statement4 = new RyaStatement(new RyaURI("http://Joe";), 
new RyaURI("http://talksTo";),
+                new RyaURI("http://Wanda";), new RyaURI("http://context";), "", 
metadata2);
+        dao.add(statement1);
+        dao.add(statement2);
+        dao.add(statement3);
+        dao.add(statement4);
+        
+        TupleQueryResult result = conn.prepareTupleQuery(QueryLanguage.SPARQL, 
query2).evaluate();
+        
+        Set<BindingSet> expected = new HashSet<>();
+        QueryBindingSet expected1 = new QueryBindingSet();
+        expected1.addBinding("b", new URIImpl("http://Betty";));
+        expected1.addBinding("a", new URIImpl("http://Joe";));
+        expected1.addBinding("c", new URIImpl("http://Doug";));
+        expected.add(expected1);
+
+        Set<BindingSet> bsSet = new HashSet<>();
+        while (result.hasNext()) {
+            bsSet.add(result.next());
+        }
+
+        Assert.assertEquals(expected, bsSet);
+
+        dao.delete(statement1, conf);
+        dao.delete(statement2, conf);
+        dao.delete(statement3, conf);
+        dao.delete(statement4, conf);
+    }
+
+    private MongoDBRdfConfiguration getConf() throws IOException {
+        
+        String host = mongoClient.getServerAddressList().get(0).getHost();
+        int port = mongoClient.getServerAddressList().get(0).getPort();
+        Set<RyaURI> propertySet = new HashSet<RyaURI>(
+                Arrays.asList(new RyaURI("http://createdBy";), new 
RyaURI("http://createdOn";)));
+        MongoDBRdfConfiguration conf = new MongoDBRdfConfiguration();
+        conf.set(ConfigUtils.USE_MONGO, "true");
+        conf.setMongoInstance(host);
+        conf.setMongoPort(Integer.toString(port));
+        conf.setMongoDBName("local");
+        conf.setCollectionName("rya");
+        conf.setTablePrefix("rya_");
+        conf.setUseStatementMetadata(true);
+        conf.setStatementMetadataProperties(propertySet);
+        return conf;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-rya/blob/11349b11/extras/indexing/src/test/java/org/apache/rya/indexing/statement/metadata/MongoStatementMetadataNodeTest.java
----------------------------------------------------------------------
diff --git 
a/extras/indexing/src/test/java/org/apache/rya/indexing/statement/metadata/MongoStatementMetadataNodeTest.java
 
b/extras/indexing/src/test/java/org/apache/rya/indexing/statement/metadata/MongoStatementMetadataNodeTest.java
new file mode 100644
index 0000000..0a3ada0
--- /dev/null
+++ 
b/extras/indexing/src/test/java/org/apache/rya/indexing/statement/metadata/MongoStatementMetadataNodeTest.java
@@ -0,0 +1,399 @@
+package org.apache.rya.indexing.statement.metadata;
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import org.apache.rya.api.domain.RyaStatement;
+import org.apache.rya.api.domain.RyaType;
+import org.apache.rya.api.domain.RyaURI;
+import org.apache.rya.api.domain.StatementMetadata;
+import org.apache.rya.api.persist.RyaDAOException;
+import org.apache.rya.indexing.accumulo.ConfigUtils;
+import 
org.apache.rya.indexing.statement.metadata.matching.StatementMetadataNode;
+import org.apache.rya.mongodb.MockMongoFactory;
+import org.apache.rya.mongodb.MongoConnectorFactory;
+import org.apache.rya.mongodb.MongoDBRdfConfiguration;
+import org.apache.rya.mongodb.MongoDBRyaDAO;
+import org.junit.After;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.openrdf.model.impl.LiteralImpl;
+import org.openrdf.model.impl.URIImpl;
+import org.openrdf.model.vocabulary.XMLSchema;
+import org.openrdf.query.BindingSet;
+import org.openrdf.query.MalformedQueryException;
+import org.openrdf.query.QueryEvaluationException;
+import org.openrdf.query.algebra.StatementPattern;
+import org.openrdf.query.algebra.evaluation.QueryBindingSet;
+import org.openrdf.query.algebra.helpers.StatementPatternCollector;
+import org.openrdf.query.parser.ParsedQuery;
+import org.openrdf.query.parser.sparql.SPARQLParser;
+
+import com.mongodb.MongoClient;
+
+import de.flapdoodle.embed.mongo.distribution.Version;
+import info.aduna.iteration.CloseableIteration;
+
+public class MongoStatementMetadataNodeTest {
+
+    protected MockMongoFactory testsFactory;
+    protected MongoClient mongoClient;
+    private MongoDBRdfConfiguration conf;
+    private MongoDBRyaDAO dao;
+    private final String query = "prefix rdf: 
<http://www.w3.org/1999/02/22-rdf-syntax-ns#> select ?x ?y where {_:blankNode 
rdf:type rdf:Statement; rdf:subject <http://Joe>; "
+            + "rdf:predicate <http://worksAt>; rdf:object ?x; 
<http://createdBy> ?y; <http://createdOn> \'2017-01-04\'^^xsd:date }";
+    private final String query2 = "prefix rdf: 
<http://www.w3.org/1999/02/22-rdf-syntax-ns#> select ?x ?y where {_:blankNode 
rdf:type rdf:Statement; rdf:subject ?x; "
+            + "rdf:predicate <http://worksAt>; rdf:object ?y; 
<http://createdBy> ?x; <http://createdOn> \'2017-01-04\'^^xsd:date }";
+
+    
+    @Before
+    public void init() throws Exception {
+        testsFactory = MockMongoFactory.with(Version.Main.PRODUCTION);
+        mongoClient = testsFactory.newMongoClient();
+        conf = getConf();
+
+        dao = new MongoDBRyaDAO(conf, mongoClient);
+        dao.init();
+    }
+
+    @After
+    public void close() throws RyaDAOException {
+        dao.destroy();
+        
+        if (mongoClient != null) {
+            mongoClient.close();
+        }
+        if (testsFactory != null) {
+            testsFactory.shutdown();
+        }
+        MongoConnectorFactory.closeMongoClient();
+    }
+
+    @Test
+    public void simpleQueryWithoutBindingSet()
+            throws MalformedQueryException, QueryEvaluationException, 
RyaDAOException {
+        StatementMetadata metadata = new StatementMetadata();
+        metadata.addMetadata(new RyaURI("http://createdBy";), new 
RyaType("Joe"));
+        metadata.addMetadata(new RyaURI("http://createdOn";), new 
RyaType(XMLSchema.DATE, "2017-01-04"));
+
+        RyaStatement statement = new RyaStatement(new RyaURI("http://Joe";), 
new RyaURI("http://worksAt";),
+                new RyaType("CoffeeShop"), new RyaURI("http://context";), "", 
metadata);
+        dao.add(statement);
+
+        SPARQLParser parser = new SPARQLParser();
+        ParsedQuery pq = parser.parseQuery(query, null);
+        List<StatementPattern> spList = 
StatementPatternCollector.process(pq.getTupleExpr());
+
+        StatementMetadataNode<?> node = new StatementMetadataNode<>(spList, 
conf);
+        CloseableIteration<BindingSet, QueryEvaluationException> iteration = 
node.evaluate(new QueryBindingSet());
+
+        QueryBindingSet bs = new QueryBindingSet();
+        bs.addBinding("x", new LiteralImpl("CoffeeShop"));
+        bs.addBinding("y", new LiteralImpl("Joe"));
+
+        List<BindingSet> bsList = new ArrayList<>();
+        while (iteration.hasNext()) {
+            bsList.add(iteration.next());
+        }
+
+        Assert.assertEquals(1, bsList.size());
+        Assert.assertEquals(bs, bsList.get(0));
+        dao.delete(statement, conf);
+    }
+
+    /**
+     * Tests if results are filtered correctly using the metadata properties. 
In
+     * this case, the date for the ingested RyaStatement differs from the date
+     * specified in the query.
+     * 
+     * @throws MalformedQueryException
+     * @throws QueryEvaluationException
+     * @throws RyaDAOException
+     */
+    @Test
+    public void simpleQueryWithoutBindingSetInvalidProperty()
+            throws MalformedQueryException, QueryEvaluationException, 
RyaDAOException {
+        StatementMetadata metadata = new StatementMetadata();
+        metadata.addMetadata(new RyaURI("http://createdBy";), new 
RyaType("Doug"));
+        metadata.addMetadata(new RyaURI("http://createdOn";), new 
RyaType(XMLSchema.DATE, "2017-02-15"));
+
+        RyaStatement statement = new RyaStatement(new RyaURI("http://Joe";), 
new RyaURI("http://worksAt";),
+                new RyaType("CoffeeShop"), new RyaURI("http://context";), "", 
metadata);
+        dao.add(statement);
+
+        SPARQLParser parser = new SPARQLParser();
+        ParsedQuery pq = parser.parseQuery(query, null);
+        List<StatementPattern> spList = 
StatementPatternCollector.process(pq.getTupleExpr());
+        StatementMetadataNode<MongoDBRdfConfiguration> node = new 
StatementMetadataNode<>(spList, conf);
+        CloseableIteration<BindingSet, QueryEvaluationException> iteration = 
node.evaluate(new QueryBindingSet());
+
+        List<BindingSet> bsList = new ArrayList<>();
+        while (iteration.hasNext()) {
+            bsList.add(iteration.next());
+        }
+        Assert.assertEquals(0, bsList.size());
+        dao.delete(statement, conf);
+    }
+
+    @Test
+    public void simpleQueryWithBindingSet() throws MalformedQueryException, 
QueryEvaluationException, RyaDAOException {
+
+        StatementMetadata metadata = new StatementMetadata();
+        metadata.addMetadata(new RyaURI("http://createdBy";), new 
RyaType("Joe"));
+        metadata.addMetadata(new RyaURI("http://createdOn";), new 
RyaType(XMLSchema.DATE, "2017-01-04"));
+
+        RyaStatement statement1 = new RyaStatement(new RyaURI("http://Joe";), 
new RyaURI("http://worksAt";),
+                new RyaType("CoffeeShop"), new RyaURI("http://context";), "", 
metadata);
+        RyaStatement statement2 = new RyaStatement(new RyaURI("http://Joe";), 
new RyaURI("http://worksAt";),
+                new RyaType("HardwareStore"), new RyaURI("http://context";), 
"", metadata);
+        dao.add(statement1);
+        dao.add(statement2);
+
+        SPARQLParser parser = new SPARQLParser();
+        ParsedQuery pq = parser.parseQuery(query, null);
+        List<StatementPattern> spList = 
StatementPatternCollector.process(pq.getTupleExpr());
+        StatementMetadataNode<MongoDBRdfConfiguration> node = new 
StatementMetadataNode<>(spList, conf);
+
+        QueryBindingSet bsConstraint = new QueryBindingSet();
+        bsConstraint.addBinding("x", new LiteralImpl("CoffeeShop"));
+        bsConstraint.addBinding("z", new LiteralImpl("Virginia"));
+
+        CloseableIteration<BindingSet, QueryEvaluationException> iteration = 
node.evaluate(bsConstraint);
+
+        QueryBindingSet expected = new QueryBindingSet();
+        expected.addBinding("x", new LiteralImpl("CoffeeShop"));
+        expected.addBinding("y", new LiteralImpl("Joe"));
+        expected.addBinding("z", new LiteralImpl("Virginia"));
+
+        List<BindingSet> bsList = new ArrayList<>();
+        while (iteration.hasNext()) {
+            bsList.add(iteration.next());
+        }
+
+        Assert.assertEquals(1, bsList.size());
+        Assert.assertEquals(expected, bsList.get(0));
+
+        dao.delete(statement1, conf);
+        dao.delete(statement2, conf);
+    }
+
+    /**
+     * Tests to see if correct result is passed back when a metadata statement
+     * is joined with a StatementPattern statement (i.e. a common variable
+     * appears in a StatementPattern statement and a metadata statement).
+     * StatementPattern statements have either rdf:subject, rdf:predicate, or
+     * rdf:object as the predicate while a metadata statement is any statement
+     * in the reified query whose predicate is not rdf:type and not a
+     * StatementPattern predicate.
+     * 
+     * @throws MalformedQueryException
+     * @throws QueryEvaluationException
+     * @throws RyaDAOException
+     */
+    @Test
+    public void simpleQueryWithBindingSetJoinPropertyToSubject()
+            throws MalformedQueryException, QueryEvaluationException, 
RyaDAOException {
+
+        StatementMetadata metadata = new StatementMetadata();
+        metadata.addMetadata(new RyaURI("http://createdBy";), new 
RyaURI("http://Joe";));
+        metadata.addMetadata(new RyaURI("http://createdOn";), new 
RyaType(XMLSchema.DATE, "2017-01-04"));
+
+        RyaStatement statement1 = new RyaStatement(new RyaURI("http://Joe";), 
new RyaURI("http://worksAt";),
+                new RyaType("CoffeeShop"), new RyaURI("http://context";), "", 
metadata);
+        RyaStatement statement2 = new RyaStatement(new RyaURI("http://Bob";), 
new RyaURI("http://worksAt";),
+                new RyaType("HardwareStore"), new RyaURI("http://context";), 
"", metadata);
+        dao.add(statement1);
+        dao.add(statement2);
+
+        SPARQLParser parser = new SPARQLParser();
+        ParsedQuery pq = parser.parseQuery(query2, null);
+        List<StatementPattern> spList = 
StatementPatternCollector.process(pq.getTupleExpr());
+        StatementMetadataNode<MongoDBRdfConfiguration> node = new 
StatementMetadataNode<>(spList, conf);
+
+        List<BindingSet> bsCollection = new ArrayList<>();
+        QueryBindingSet bsConstraint1 = new QueryBindingSet();
+        bsConstraint1.addBinding("y", new LiteralImpl("CoffeeShop"));
+        bsConstraint1.addBinding("z", new LiteralImpl("Virginia"));
+
+        QueryBindingSet bsConstraint2 = new QueryBindingSet();
+        bsConstraint2.addBinding("y", new LiteralImpl("HardwareStore"));
+        bsConstraint2.addBinding("z", new LiteralImpl("Maryland"));
+        bsCollection.add(bsConstraint1);
+        bsCollection.add(bsConstraint2);
+
+        CloseableIteration<BindingSet, QueryEvaluationException> iteration = 
node.evaluate(bsCollection);
+
+        QueryBindingSet expected = new QueryBindingSet();
+        expected.addBinding("y", new LiteralImpl("CoffeeShop"));
+        expected.addBinding("x", new URIImpl("http://Joe";));
+        expected.addBinding("z", new LiteralImpl("Virginia"));
+
+        List<BindingSet> bsList = new ArrayList<>();
+        while (iteration.hasNext()) {
+            bsList.add(iteration.next());
+        }
+
+        Assert.assertEquals(1, bsList.size());
+        Assert.assertEquals(expected, bsList.get(0));
+
+        dao.delete(statement1, conf);
+        dao.delete(statement2, conf);
+    }
+
+    /**
+     * Tests if the StatementMetadataNode joins BindingSet correctly for
+     * variables appearing in metadata statements. In this case, the metadata
+     * statements are (_:blankNode <http://createdOn 2017-01-04 ) and
+     * (_:blankNode <http://createdBy> ?y). The variable ?y appears as the
+     * object in the above metadata statement and its values are joined to the
+     * constraint BindingSets in the example below.
+     * 
+     * @throws MalformedQueryException
+     * @throws QueryEvaluationException
+     * @throws RyaDAOException
+     */
+    @Test
+    public void simpleQueryWithBindingSetJoinOnProperty()
+            throws MalformedQueryException, QueryEvaluationException, 
RyaDAOException {
+
+        StatementMetadata metadata = new StatementMetadata();
+        metadata.addMetadata(new RyaURI("http://createdBy";), new 
RyaType("Joe"));
+        metadata.addMetadata(new RyaURI("http://createdOn";), new 
RyaType(XMLSchema.DATE, "2017-01-04"));
+
+        RyaStatement statement1 = new RyaStatement(new RyaURI("http://Joe";), 
new RyaURI("http://worksAt";),
+                new RyaType("CoffeeShop"), new RyaURI("http://context";), "", 
metadata);
+        dao.add(statement1);
+
+        SPARQLParser parser = new SPARQLParser();
+        ParsedQuery pq = parser.parseQuery(query, null);
+        List<StatementPattern> spList = 
StatementPatternCollector.process(pq.getTupleExpr());
+        StatementMetadataNode<MongoDBRdfConfiguration> node = new 
StatementMetadataNode<>(spList, conf);
+
+        QueryBindingSet bsConstraint = new QueryBindingSet();
+        bsConstraint.addBinding("x", new LiteralImpl("CoffeeShop"));
+        bsConstraint.addBinding("y", new LiteralImpl("Doug"));
+
+        CloseableIteration<BindingSet, QueryEvaluationException> iteration = 
node.evaluate(bsConstraint);
+
+        List<BindingSet> bsList = new ArrayList<>();
+        while (iteration.hasNext()) {
+            bsList.add(iteration.next());
+        }
+
+        Assert.assertEquals(0, bsList.size());
+        dao.delete(statement1, conf);
+    }
+
+    /**
+     * Tests if StatementMetadataNode joins BindingSet values correctly for
+     * variables appearing as the object in one of the StatementPattern
+     * statements (in the case ?x appears as the Object in the statement
+     * _:blankNode rdf:object ?x). StatementPattern statements have either
+     * rdf:subject, rdf:predicate, or rdf:object as the predicate.
+     * 
+     * @throws MalformedQueryException
+     * @throws QueryEvaluationException
+     * @throws RyaDAOException
+     */
+    @Test
+    public void simpleQueryWithBindingSetCollection()
+            throws MalformedQueryException, QueryEvaluationException, 
RyaDAOException {
+
+        StatementMetadata metadata = new StatementMetadata();
+        metadata.addMetadata(new RyaURI("http://createdBy";), new 
RyaType("Joe"));
+        metadata.addMetadata(new RyaURI("http://createdOn";), new 
RyaType(XMLSchema.DATE, "2017-01-04"));
+
+        RyaStatement statement1 = new RyaStatement(new RyaURI("http://Joe";), 
new RyaURI("http://worksAt";),
+                new RyaType("CoffeeShop"), new RyaURI("http://context";), "", 
metadata);
+        RyaStatement statement2 = new RyaStatement(new RyaURI("http://Joe";), 
new RyaURI("http://worksAt";),
+                new RyaType("HardwareStore"), new RyaURI("http://context";), 
"", metadata);
+        dao.add(statement1);
+        dao.add(statement2);
+
+        SPARQLParser parser = new SPARQLParser();
+        ParsedQuery pq = parser.parseQuery(query, null);
+        List<StatementPattern> spList = 
StatementPatternCollector.process(pq.getTupleExpr());
+        StatementMetadataNode<MongoDBRdfConfiguration> node = new 
StatementMetadataNode<>(spList, conf);
+
+        List<BindingSet> bsCollection = new ArrayList<>();
+        QueryBindingSet bsConstraint1 = new QueryBindingSet();
+        bsConstraint1.addBinding("x", new LiteralImpl("CoffeeShop"));
+        bsConstraint1.addBinding("z", new LiteralImpl("Virginia"));
+
+        QueryBindingSet bsConstraint2 = new QueryBindingSet();
+        bsConstraint2.addBinding("x", new LiteralImpl("HardwareStore"));
+        bsConstraint2.addBinding("z", new LiteralImpl("Maryland"));
+
+        QueryBindingSet bsConstraint3 = new QueryBindingSet();
+        bsConstraint3.addBinding("x", new LiteralImpl("BurgerShack"));
+        bsConstraint3.addBinding("z", new LiteralImpl("Delaware"));
+        bsCollection.add(bsConstraint1);
+        bsCollection.add(bsConstraint2);
+        bsCollection.add(bsConstraint3);
+
+        CloseableIteration<BindingSet, QueryEvaluationException> iteration = 
node.evaluate(bsCollection);
+
+        Set<BindingSet> expected = new HashSet<>();
+        QueryBindingSet expected1 = new QueryBindingSet();
+        expected1.addBinding("x", new LiteralImpl("CoffeeShop"));
+        expected1.addBinding("y", new LiteralImpl("Joe"));
+        expected1.addBinding("z", new LiteralImpl("Virginia"));
+
+        QueryBindingSet expected2 = new QueryBindingSet();
+        expected2.addBinding("x", new LiteralImpl("HardwareStore"));
+        expected2.addBinding("y", new LiteralImpl("Joe"));
+        expected2.addBinding("z", new LiteralImpl("Maryland"));
+        expected.add(expected1);
+        expected.add(expected2);
+
+        Set<BindingSet> bsSet = new HashSet<>();
+        while (iteration.hasNext()) {
+            bsSet.add(iteration.next());
+        }
+
+        Assert.assertEquals(expected, bsSet);
+
+        dao.delete(statement1, conf);
+        dao.delete(statement2, conf);
+    }
+
+    private MongoDBRdfConfiguration getConf() throws IOException {
+        
+        String host = mongoClient.getServerAddressList().get(0).getHost();
+        int port = mongoClient.getServerAddressList().get(0).getPort();
+        Set<RyaURI> propertySet = new HashSet<RyaURI>(
+                Arrays.asList(new RyaURI("http://createdBy";), new 
RyaURI("http://createdOn";)));
+        MongoDBRdfConfiguration conf = new MongoDBRdfConfiguration();
+        conf.set(ConfigUtils.USE_MONGO, "true");
+        conf.setMongoInstance(host);
+        conf.setMongoPort(Integer.toString(port));
+        conf.setMongoDBName("local");
+        conf.setCollectionName("rya");
+        conf.setTablePrefix("rya_");
+        conf.setUseStatementMetadata(true);
+        conf.setStatementMetadataProperties(propertySet);
+        return conf;
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-rya/blob/11349b11/extras/indexing/src/test/java/org/apache/rya/indexing/statement/metadata/StatementMetadataExternalSetProviderTest.java
----------------------------------------------------------------------
diff --git 
a/extras/indexing/src/test/java/org/apache/rya/indexing/statement/metadata/StatementMetadataExternalSetProviderTest.java
 
b/extras/indexing/src/test/java/org/apache/rya/indexing/statement/metadata/StatementMetadataExternalSetProviderTest.java
new file mode 100644
index 0000000..2a41396
--- /dev/null
+++ 
b/extras/indexing/src/test/java/org/apache/rya/indexing/statement/metadata/StatementMetadataExternalSetProviderTest.java
@@ -0,0 +1,178 @@
+package org.apache.rya.indexing.statement.metadata;
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Set;
+
+import org.apache.hadoop.conf.Configuration;
+import org.apache.rya.accumulo.AccumuloRdfConfiguration;
+import org.apache.rya.api.RdfCloudTripleStoreConfiguration;
+import org.apache.rya.api.domain.RyaURI;
+import org.apache.rya.indexing.accumulo.ConfigUtils;
+import org.apache.rya.indexing.external.matching.JoinSegment;
+import 
org.apache.rya.indexing.statement.metadata.matching.StatementMetadataExternalSetProvider;
+import 
org.apache.rya.indexing.statement.metadata.matching.StatementMetadataNode;
+import org.apache.rya.mongodb.MongoDBRdfConfiguration;
+import org.junit.Assert;
+import org.junit.Test;
+import org.openrdf.query.MalformedQueryException;
+import org.openrdf.query.algebra.Filter;
+import org.openrdf.query.algebra.QueryModelNode;
+import org.openrdf.query.algebra.StatementPattern;
+import org.openrdf.query.algebra.ValueExpr;
+import org.openrdf.query.algebra.helpers.StatementPatternCollector;
+import org.openrdf.query.parser.ParsedQuery;
+import org.openrdf.query.parser.sparql.SPARQLParser;
+
+public class StatementMetadataExternalSetProviderTest {
+
+    private final String query = "prefix rdf: 
<http://www.w3.org/1999/02/22-rdf-syntax-ns#> select ?x ?y where {_:blankNode 
rdf:type rdf:Statement; rdf:subject <http://Joe>; "
+            + "rdf:predicate <http://worksAt>; rdf:object ?x; 
<http://createdBy> ?y; <http://createdOn> \'2017-01-04\'^^xsd:date }";
+    private final String query3 = "prefix rdf: 
<http://www.w3.org/1999/02/22-rdf-syntax-ns#> select ?x ?y where {_:blankNode1 
rdf:type rdf:Statement. _:blankNode2 rdf:type rdf:Statement; rdf:subject 
<http://Bob>; "
+            + "rdf:predicate <http://worksAt>; rdf:object ?x; 
<http://createdBy> ?y; <http://createdOn> \'2017-02-04\'^^xsd:date }";
+    private final String query2 = "prefix rdf: 
<http://www.w3.org/1999/02/22-rdf-syntax-ns#> select ?x ?y where {_:blankNode1 
rdf:type rdf:Statement; rdf:subject <http://Joe>; "
+            + "rdf:predicate <http://worksAt>; rdf:object ?x; 
<http://createdBy> ?y; <http://createdOn> \'2017-01-04\'^^xsd:date. "
+            + "_:blankNode2 rdf:type rdf:Statement; rdf:subject <http://Bob>; "
+            + "rdf:predicate <http://worksAt>; rdf:object ?x; 
<http://createdBy> ?y; <http://createdOn> \'2017-02-04\'^^xsd:date }";
+
+    @Test
+    public void createSingleAccumuloMetadataNode() throws 
MalformedQueryException {
+
+        AccumuloRdfConfiguration conf = (AccumuloRdfConfiguration) 
getConf(false);
+        Set<RyaURI> propertySet = new HashSet<>();
+        propertySet.add(new RyaURI("http://createdBy";));
+        conf.setStatementMetadataProperties(propertySet);
+        StatementMetadataExternalSetProvider metaProvider = new 
StatementMetadataExternalSetProvider(
+                conf);
+        SPARQLParser parser = new SPARQLParser();
+        ParsedQuery pq = parser.parseQuery(query, null);
+
+        List<QueryModelNode> patterns = new ArrayList<>();
+        List<StatementMetadataNode<?>> expected = new ArrayList<>();
+        Set<StatementPattern> sp = 
StatementMetadataTestUtils.getMetadataStatementPatterns(pq.getTupleExpr(), 
propertySet);
+
+        patterns.addAll(StatementPatternCollector.process(pq.getTupleExpr()));
+        JoinSegment<StatementMetadataNode<?>> segment = new JoinSegment<>(
+                new HashSet<QueryModelNode>(patterns), patterns, new 
HashMap<ValueExpr, Filter>());
+        List<StatementMetadataNode<?>> extSets = 
metaProvider.getExternalSets(segment);
+
+        expected.add(new StatementMetadataNode<>(sp, conf));
+
+        Assert.assertEquals(expected, extSets);
+
+    }
+    
+    @Test
+    public void createSingleMongoMetadataNode() throws MalformedQueryException 
{
+
+        MongoDBRdfConfiguration conf = (MongoDBRdfConfiguration) getConf(true);
+        Set<RyaURI> propertySet = new HashSet<>();
+        propertySet.add(new RyaURI("http://createdBy";));
+        conf.setStatementMetadataProperties(propertySet);
+        StatementMetadataExternalSetProvider metaProvider = new 
StatementMetadataExternalSetProvider(conf);
+        SPARQLParser parser = new SPARQLParser();
+        ParsedQuery pq = parser.parseQuery(query, null);
+
+        List<QueryModelNode> patterns = new ArrayList<>();
+        List<StatementMetadataNode<?>> expected = new ArrayList<>();
+        Set<StatementPattern> sp = 
StatementMetadataTestUtils.getMetadataStatementPatterns(pq.getTupleExpr(), 
propertySet);
+
+        patterns.addAll(StatementPatternCollector.process(pq.getTupleExpr()));
+        JoinSegment<StatementMetadataNode<?>> segment = new JoinSegment<>(
+                new HashSet<QueryModelNode>(patterns), patterns, new 
HashMap<ValueExpr, Filter>());
+        List<StatementMetadataNode<?>> extSets = 
metaProvider.getExternalSets(segment);
+
+        expected.add(new StatementMetadataNode<>(sp,conf));
+
+        Assert.assertEquals(expected, extSets);
+
+    }
+    
+    
+    @Test
+    public void createMultipleMetadataNode() throws MalformedQueryException {
+
+        MongoDBRdfConfiguration conf = (MongoDBRdfConfiguration) getConf(true);
+        Set<RyaURI> propertySet = new HashSet<>();
+        propertySet.add(new RyaURI("http://createdBy";));
+        propertySet.add(new RyaURI("http://createdOn";));
+        conf.setStatementMetadataProperties(propertySet);
+        StatementMetadataExternalSetProvider metaProvider = new 
StatementMetadataExternalSetProvider(conf);
+        SPARQLParser parser = new SPARQLParser();
+        ParsedQuery pq2 = parser.parseQuery(query2, null);
+        ParsedQuery pq3 = parser.parseQuery(query3, null);
+        ParsedQuery pq1 = parser.parseQuery(query, null);
+
+        List<QueryModelNode> patterns = new ArrayList<>();
+        List<StatementMetadataNode<?>> expected = new ArrayList<>();
+        Set<StatementPattern> sp1 = 
StatementMetadataTestUtils.getMetadataStatementPatterns(pq1.getTupleExpr(), 
propertySet);
+        Set<StatementPattern> sp3 = 
StatementMetadataTestUtils.getMetadataStatementPatterns(pq3.getTupleExpr(), 
propertySet);
+        //added extra blankNode into query3 to make blankNode names line up 
with query2.  Need to remove it now so that
+        //StatementMetadataNode doesn't blow up because all subjects aren't 
the same.
+        removePatternWithGivenSubject("-anon-1", sp3);
+
+        patterns.addAll(StatementPatternCollector.process(pq2.getTupleExpr()));
+        JoinSegment<StatementMetadataNode<?>> segment = new JoinSegment<>(
+                new HashSet<QueryModelNode>(patterns), patterns, new 
HashMap<ValueExpr, Filter>());
+        List<StatementMetadataNode<?>> extSets = 
metaProvider.getExternalSets(segment);
+
+        expected.add(new StatementMetadataNode<>(sp1,conf));
+        expected.add(new StatementMetadataNode<>(sp3,conf));
+
+        Assert.assertEquals(expected, extSets);
+    }
+
+    private static Configuration getConf(boolean useMongo) {
+
+        if (useMongo) {
+            MongoDBRdfConfiguration conf = new MongoDBRdfConfiguration();
+            conf.setBoolean("sc.useMongo", true);
+            conf.setMongoInstance("localhost");
+            conf.setMongoPort("27017");
+            conf.setMongoDBName("rya_");
+            return conf;
+        } else {
+
+            AccumuloRdfConfiguration conf = new AccumuloRdfConfiguration();
+            conf.setBoolean(ConfigUtils.USE_MOCK_INSTANCE, true);
+            conf.set(RdfCloudTripleStoreConfiguration.CONF_TBL_PREFIX, "rya_");
+            conf.set(ConfigUtils.CLOUDBASE_USER, "root");
+            conf.set(ConfigUtils.CLOUDBASE_PASSWORD, "");
+            conf.set(ConfigUtils.CLOUDBASE_INSTANCE, "instance");
+            conf.set(ConfigUtils.CLOUDBASE_AUTHS, "");
+
+            return conf;
+        }
+    }
+    
+    private void removePatternWithGivenSubject(String subject, 
Set<StatementPattern> patterns) {
+        Iterator<StatementPattern> spIter = patterns.iterator();
+        while(spIter.hasNext()) {
+            StatementPattern sp = spIter.next();
+            if(sp.getSubjectVar().getName().equals(subject)) {
+                spIter.remove();
+            }
+        }
+    }
+
+}

Reply via email to