You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@rya.apache.org by pu...@apache.org on 2015/12/04 17:46:18 UTC

[06/49] incubator-rya git commit: RYA-7 POM and License Clean-up for Apache Move

http://git-wip-us.apache.org/repos/asf/incubator-rya/blob/80faf06d/sail/src/test/java/mvm/rya/ArbitraryLengthQueryTest.java
----------------------------------------------------------------------
diff --git a/sail/src/test/java/mvm/rya/ArbitraryLengthQueryTest.java b/sail/src/test/java/mvm/rya/ArbitraryLengthQueryTest.java
new file mode 100644
index 0000000..4a5d871
--- /dev/null
+++ b/sail/src/test/java/mvm/rya/ArbitraryLengthQueryTest.java
@@ -0,0 +1,500 @@
+package mvm.rya;
+
+/*
+ * 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.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+
+import org.apache.accumulo.core.client.Connector;
+import org.apache.accumulo.core.client.Instance;
+import org.apache.accumulo.core.client.mock.MockInstance;
+import org.openrdf.model.Resource;
+import org.openrdf.query.MalformedQueryException;
+import org.openrdf.query.QueryEvaluationException;
+import org.openrdf.query.QueryLanguage;
+import org.openrdf.query.TupleQuery;
+import org.openrdf.query.TupleQueryResultHandlerException;
+import org.openrdf.query.resultio.text.tsv.SPARQLResultsTSVWriter;
+import org.openrdf.repository.Repository;
+import org.openrdf.repository.RepositoryConnection;
+import org.openrdf.repository.RepositoryException;
+import org.openrdf.rio.RDFFormat;
+import org.openrdf.rio.RDFParseException;
+
+import mvm.rya.accumulo.AccumuloRdfConfiguration;
+import mvm.rya.accumulo.AccumuloRyaDAO;
+import mvm.rya.rdftriplestore.RdfCloudTripleStore;
+import mvm.rya.rdftriplestore.RyaSailRepository;
+import mvm.rya.rdftriplestore.inference.InferenceEngine;
+import mvm.rya.rdftriplestore.namespace.NamespaceManager;
+import junit.framework.TestCase;
+
+/**
+ * The purpose of this is to provide a test case that illustrates a failure that is being encountered. A working test is
+ * provided as well to demonstrate that a successful query can be made.
+ */
+public class ArbitraryLengthQueryTest extends TestCase {
+
+    /**
+     * The repository used for the tests.
+     */
+    private Repository repository;
+
+    @Override
+    public void setUp() throws Exception {
+        super.setUp();
+
+        final RdfCloudTripleStore store = new MockRdfCloudStore();
+
+        final NamespaceManager nm = new NamespaceManager(store.getRyaDAO(), store.getConf());
+        store.setNamespaceManager(nm);
+
+        repository = new RyaSailRepository(store);
+        repository.initialize();
+
+        load();
+    }
+
+    @Override
+    public void tearDown() throws Exception {
+        super.tearDown();
+        repository.shutDown();
+    }
+
+    /**
+     * This test works. The expected result is 6 rows ranging from "Model1Class 1" through "Model1Class 6".
+     *
+     * @throws RepositoryException
+     * @throws QueryEvaluationException
+     * @throws TupleQueryResultHandlerException
+     *
+     * @throws MalformedQueryException
+     */
+    public void testWithoutSubquery() throws RepositoryException, QueryEvaluationException, TupleQueryResultHandlerException, MalformedQueryException {
+        final String query = "SELECT ?i ?i_label ?i_class ?i_v1"
+                + "WHERE {"
+                + "?i <http://www.w3.org/2000/01/rdf-schema#label> ?i_label ."
+                + "?i a ?i_class ."
+                + "?i_class <http://www.w3.org/2000/01/rdf-schema#subClassOf>* <http://dragon-research.com/cham/model/model1#Model1Class> ."
+                + "OPTIONAL { ?i <http://dragon-research.com/cham/model/model1#name> ?i_v1 } ."
+                + "}"
+                + "ORDER BY ?i_label";
+
+        final RepositoryConnection conn = repository.getConnection();
+        final TupleQuery tupleQuery = conn.prepareTupleQuery(QueryLanguage.SPARQL, query);
+        RdfCloudTripleStoreConnectionTest.CountTupleHandler countTupleHandler = new RdfCloudTripleStoreConnectionTest.CountTupleHandler();
+        tupleQuery.evaluate(countTupleHandler);
+        assertEquals(6, countTupleHandler.getCount());
+        conn.close();
+    }
+
+    /**
+     * This test fails. The expected result is 6 rows ranging from "Model1Class 1 Event" to "Model1Class 6 Event". The
+     * current result is a RejectedExecutionException.
+     *
+     * @throws RepositoryException
+     * @throws QueryEvaluationException
+     * @throws TupleQueryResultHandlerException
+     *
+     * @throws MalformedQueryException
+     */
+    public void testWithSubquery() throws RepositoryException, QueryEvaluationException, TupleQueryResultHandlerException, MalformedQueryException {
+        final String query = "SELECT ?i ?i_label ?i_class ?i_v1 ?i_v2 ?i_v2_label ?i_v2_class ?i_v2_v1"
+                + "WHERE {"
+                + "?i <http://www.w3.org/2000/01/rdf-schema#label> ?i_label ."
+                + "?i a ?i_class ."
+                + "?i_class <http://www.w3.org/2000/01/rdf-schema#subClassOf>* <http://dragon-research.com/cham/model/model1#Event> ."
+                + "OPTIONAL { ?i <http://dragon-research.com/cham/model/model1#name> ?i_v1 } ."
+                + "?i <http://dragon-research.com/cham/model/model1#hasTemporalEntity> ?i_v2 ."
+                + "{"
+                + "SELECT ?i_v2 ?i_v2_label ?i_v2_class ?i_v2_v1"
+                + "WHERE {"
+                + "?i_v2 <http://www.w3.org/2000/01/rdf-schema#label> ?i_v2_label ."
+                + "?i_v2 a ?i_v2_class ."
+                + "?i_v2_class <http://www.w3.org/2000/01/rdf-schema#subClassOf>* <http://dragon-research.com/cham/model/model1#TemporalInstant> ."
+                + "OPTIONAL { ?i_v2 <http://dragon-research.com/cham/model/model1#dateTime> ?i_v2_v1 } ."
+                + "}"
+                + "ORDER BY ?i_v2_label"
+                + "}"
+                + "}"
+                + "ORDER BY ?i_label";
+
+        final RepositoryConnection conn = repository.getConnection();
+        final TupleQuery tupleQuery = conn.prepareTupleQuery(QueryLanguage.SPARQL, query);
+        RdfCloudTripleStoreConnectionTest.CountTupleHandler countTupleHandler = new RdfCloudTripleStoreConnectionTest.CountTupleHandler();
+        tupleQuery.evaluate(countTupleHandler);
+        assertEquals(6, countTupleHandler.getCount());
+        conn.close();
+    }
+
+    /**
+     * Load the t-box and a-box turtle from strings defined within this class.
+     *
+     * @throws RepositoryException
+     * @throws RDFParseException
+     * @throws IOException
+     */
+    private void load() throws RepositoryException, RDFParseException, IOException {
+        final RepositoryConnection conn = repository.getConnection();
+
+        // T-Box
+        String ttlString = MODEL_TTL;
+        InputStream stringInput = new ByteArrayInputStream(ttlString.getBytes());
+        conn.add(stringInput, "http://dragon-research.com/cham/model/model1", RDFFormat.TURTLE, new Resource[]{});
+
+        // A-Box
+        ttlString = BUCKET_TTL;
+        stringInput = new ByteArrayInputStream(ttlString.getBytes());
+        conn.add(stringInput, "http://dragon-research.com/cham/bucket/bucket1", RDFFormat.TURTLE, new Resource[]{});
+
+        conn.commit();
+        conn.close();
+    }
+
+    /**
+     * Mock RDF cloud store for one shot testing.
+     */
+    public class MockRdfCloudStore extends RdfCloudTripleStore {
+        public MockRdfCloudStore() {
+            super();
+            final Instance instance = new MockInstance();
+            try {
+                final AccumuloRdfConfiguration conf = new AccumuloRdfConfiguration();
+                setConf(conf);
+
+                final Connector connector = instance.getConnector("", "");
+                final AccumuloRyaDAO cdao = new AccumuloRyaDAO();
+                cdao.setConf(conf);
+                cdao.setConnector(connector);
+                setRyaDAO(cdao);
+                inferenceEngine = new InferenceEngine();
+                inferenceEngine.setRyaDAO(cdao);
+                inferenceEngine.setRefreshGraphSchedule(5000); //every 5 sec
+                inferenceEngine.setConf(conf);
+                setInferenceEngine(inferenceEngine);
+            } catch (final Exception e) {
+                e.printStackTrace();
+            }
+        }
+    }
+
+    /**
+     * The ontology t-box in turtle.
+     */
+    private static String MODEL_TTL = "@prefix :        <http://dragon-research.com/cham/model/model1#> ."
+            + "@prefix cham:    <http://dragon-research.com/cham/schema#> ."
+            + "@prefix dc:      <http://purl.org/dc/elements/1.1/> ."
+            + "@prefix owl:     <http://www.w3.org/2002/07/owl#> ."
+            + "@prefix qudt:    <http://data.nasa.gov/qudt/owl/qudt#> ."
+            + "@prefix rdf:     <http://www.w3.org/1999/02/22-rdf-syntax-ns#> ."
+            + "@prefix rdfs:    <http://www.w3.org/2000/01/rdf-schema#> ."
+            + "@prefix unit:    <http://data.nasa.gov/qudt/owl/unit#> ."
+            + "@prefix xml:     <http://www.w3.org/XML/1998/namespace> ."
+            + "@prefix xsd:     <http://www.w3.org/2001/XMLSchema#> ."
+            + ""
+            + "<http://dragon-research.com/cham/model/model1>"
+            + "      rdf:type owl:Ontology ;"
+            + "      rdfs:label \"Model1 Ontology\"^^xsd:string ;"
+            + "      :versionInfo \"0.1\"^^xsd:string ;"
+            + "      dc:title \"Model1 Ontology\"^^xsd:string ."
+            + ""
+            + ":ModelClassD"
+            + "      rdf:type owl:Class ;"
+            + "      rdfs:label \"ModelClassD\"^^xsd:string ;"
+            + "      rdfs:subClassOf"
+            + "              [ rdf:type owl:Restriction ;"
+            + "                owl:maxQualifiedCardinality"
+            + "                        \"1\"^^xsd:nonNegativeInteger ;"
+            + "                owl:onDataRange xsd:string ;"
+            + "                owl:onProperty :name"
+            + "              ] ;"
+            + "      rdfs:subClassOf"
+            + "              [ rdf:type owl:Restriction ;"
+            + "                owl:allValuesFrom :Model1ClassAssoc ;"
+            + "                owl:onProperty :hasModel1ClassAssoc"
+            + "              ] ."
+            + ""
+            + ":ModelClassC"
+            + "      rdf:type owl:Class ;"
+            + "      rdfs:label \"ModelClassC\"^^xsd:string ;"
+            + "      rdfs:subClassOf :ModelClassD ."
+            + ""
+            + ":Modle1ClassB"
+            + "      rdf:type owl:Class ;"
+            + "      rdfs:label \"Modle1ClassB\"^^xsd:string ;"
+            + "      rdfs:subClassOf :ModelClassC ."
+            + ""
+            + ":Model1ClassA"
+            + "      rdf:type owl:Class ;"
+            + "      rdfs:label \"Model1ClassA\"^^xsd:string ;"
+            + "      rdfs:subClassOf :Modle1ClassB ."
+            + ""
+            + ":Model1Class"
+            + "      rdf:type owl:Class ;"
+            + "      rdfs:label \"Model1Class\"^^xsd:string ;"
+            + "      rdfs:subClassOf :Model1ClassA ;"
+            + "      rdfs:subClassOf"
+            + "              [ rdf:type owl:Restriction ;"
+            + "                owl:maxQualifiedCardinality"
+            + "                        \"1\"^^xsd:nonNegativeInteger ;"
+            + "                owl:onDataRange xsd:string ;"
+            + "                owl:onProperty :model1ClassId"
+            + "              ] ."
+            + ""
+            + ":Model1Event"
+            + "      rdf:type owl:Class ;"
+            + "      rdfs:label \"Model1Event\"^^xsd:string ;"
+            + "      rdfs:subClassOf :Event ;"
+            + "      rdfs:subClassOf"
+            + "              [ rdf:type owl:Restriction ;"
+            + "                owl:allValuesFrom :Model1ClassA ;"
+            + "                owl:onProperty :hasModel1ClassA"
+            + "              ] ."
+            + ""
+            + ":Model1ClassAssoc"
+            + "      rdf:type owl:Class ;"
+            + "      rdfs:label \"Model1ClassAssoc\"^^xsd:string ;"
+            + "      rdfs:subClassOf owl:Thing ;"
+            + "      rdfs:subClassOf"
+            + "              [ rdf:type owl:Restriction ;"
+            + "                owl:maxQualifiedCardinality"
+            + "                        \"1\"^^xsd:nonNegativeInteger ;"
+            + "                owl:onDataRange xsd:string ;"
+            + "                owl:onProperty :name"
+            + "              ] ;"
+            + "      rdfs:subClassOf"
+            + "              [ rdf:type owl:Restriction ;"
+            + "                owl:maxQualifiedCardinality"
+            + "                        \"1\"^^xsd:nonNegativeInteger ;"
+            + "                owl:onClass :ModelClassD ;"
+            + "                owl:onProperty :hasEntity"
+            + "              ] ;"
+            + "      rdfs:subClassOf"
+            + "              [ rdf:type owl:Restriction ;"
+            + "                owl:allValuesFrom :ModelClassD ;"
+            + "                owl:onProperty :hasEntity"
+            + "              ] ."
+            + ""
+            + ":TemporalEntity"
+            + "      rdf:type owl:Class ;"
+            + "      rdfs:label \"TemporalEntity\"^^xsd:string ;"
+            + "      rdfs:subClassOf owl:Thing ."
+            + ""
+            + ":TemporalInstant"
+            + "      rdf:type owl:Class ;"
+            + "      rdfs:label \"TemporalInstant\"^^xsd:string ;"
+            + "      rdfs:subClassOf :TemporalEntity ;"
+            + "      rdfs:subClassOf"
+            + "              [ rdf:type owl:Restriction ;"
+            + "                owl:maxQualifiedCardinality"
+            + "                        \"1\"^^xsd:nonNegativeInteger ;"
+            + "                owl:onDataRange xsd:dateTime ;"
+            + "                owl:onProperty :dateTime"
+            + "              ] ."
+            + ""
+            + ":model1ClassId"
+            + "      rdf:type owl:DatatypeProperty ;"
+            + "      rdfs:domain :Model1Class ;"
+            + "      rdfs:label \"model1ClassId\"^^xsd:string ;"
+            + "      rdfs:range xsd:string ."
+            + ""
+            + ":hasModel1ClassAssoc"
+            + "      rdf:type owl:ObjectProperty ;"
+            + "      rdfs:domain :ModelClassD ;"
+            + "      rdfs:label \"hasModel1ClassAssoc\"^^xsd:string ;"
+            + "      rdfs:range :Model1ClassAssoc ."
+            + ""
+            + ":name"
+            + "      rdf:type owl:DatatypeProperty ;"
+            + "      rdfs:domain :Model1ClassAssoc , :ModelClassD ;"
+            + "      rdfs:label \"name\"^^xsd:string ;"
+            + "      rdfs:range xsd:string ."
+            + ""
+            + ":hasTemporalEntity"
+            + "      rdf:type owl:ObjectProperty ;"
+            + "      rdfs:domain :ThreatAnalysis , :Event , :TrackingData , :Threat , :Vulnerability ;"
+            + "      rdfs:label \"hasTemporalEntity\"^^xsd:string ;"
+            + "      rdfs:range :TemporalEntity ."
+            + ""
+            + ":hasEntity"
+            + "      rdf:type owl:ObjectProperty ;"
+            + "      rdfs:domain :Model1ClassAssoc ;"
+            + "      rdfs:label \"hasEntity\"^^xsd:string ;"
+            + "      rdfs:range :ModelClassD ."
+            + ""
+            + ":dateTime"
+            + "      rdf:type owl:DatatypeProperty ;"
+            + "      rdfs:domain :TemporalInstant ;"
+            + "      rdfs:label \"dateTime\"^^xsd:string ;"
+            + "      rdfs:range xsd:dateTime ."
+            + ""
+            + ":Event"
+            + "      rdf:type owl:Class ;"
+            + "      rdfs:label \"Event\"^^xsd:string ;"
+            + "      rdfs:subClassOf :ModelClassD ;"
+            + "      rdfs:subClassOf"
+            + "              [ rdf:type owl:Restriction ;"
+            + "                owl:allValuesFrom :TemporalEntity ;"
+            + "                owl:onProperty :hasTemporalEntity"
+            + "              ] ;"
+            + "      rdfs:subClassOf"
+            + "              [ rdf:type owl:Restriction ;"
+            + "                owl:maxQualifiedCardinality"
+            + "                        \"1\"^^xsd:nonNegativeInteger ;"
+            + "                owl:onClass :TemporalEntity ;"
+            + "                owl:onProperty :hasTemporalEntity"
+            + "              ] ."
+            + ""
+            + ":hasModel1ClassA"
+            + "      rdf:type owl:ObjectProperty ;"
+            + "      rdfs:domain :Model1Event ;"
+            + "      rdfs:label \"hasModel1ClassA\"^^xsd:string ;"
+            + "      rdfs:range :Model1ClassA ."
+            + ""
+            + "rdfs:label"
+            + "      rdf:type owl:AnnotationProperty ."
+            + ""
+            + "xsd:date"
+            + "      rdf:type rdfs:Datatype ."
+            + ""
+            + "xsd:time"
+            + "      rdf:type rdfs:Datatype .";
+
+    /**
+     * The ontology a-box in turtle.
+     */
+    private static String BUCKET_TTL = "@prefix :        <http://dragon-research.com/cham/bucket/bucket1#> ."
+            + "@prefix rdfs:    <http://www.w3.org/2000/01/rdf-schema#> ."
+            + "@prefix owl:     <http://www.w3.org/2002/07/owl#> ."
+            + "@prefix xsd:     <http://www.w3.org/2001/XMLSchema#> ."
+            + "@prefix rdf:     <http://www.w3.org/1999/02/22-rdf-syntax-ns#> ."
+            + "@prefix model1:   <http://dragon-research.com/cham/model/model1#> ."
+            + ""
+            + ":i1   a       model1:Model1Class ;"
+            + "      rdfs:label \"Model1Class 1\"^^xsd:string ;"
+            + "      model1:name \"Model1Class 1\"^^xsd:string ;"
+            + "      model1:hasModel1ClassAssoc :i1-assoc ;"
+            + "      model1:model1ClassId \"ID01\"^^xsd:string ."
+            + "      "
+            + ":i1-assoc a model1:Model1ClassAssoc ;"
+            + "      rdfs:label \"Model1Class 1 Assoc\"^^xsd:string ;"
+            + "      model1:hasEntity :i1-event ."
+            + "      "
+            + ":i1-event a model1:Model1Event ;"
+            + "      rdfs:label \"Model1Class 1 Event\"^^xsd:string ;"
+            + "      model1:hasTemporalEntity :i1-time ."
+            + ""
+            + ":i1-time a model1:TemporalInstant ;"
+            + "      rdfs:label \"Model1Class 1 Time\"^^xsd:string ;"
+            + "      model1:dateTime \"1994-02-07T21:47:01.000Z\"^^xsd:dateTime ."
+            + "      "
+            + ":i2   a       model1:Model1Class ;"
+            + "      rdfs:label \"Model1Class 2\"^^xsd:string ;"
+            + "      model1:name \"Model1Class 2\"^^xsd:string ;"
+            + "      model1:hasModel1ClassAssoc :i2-assoc ;"
+            + "      model1:model1ClassId \"ID02\"^^xsd:string ."
+            + ""
+            + ":i2-assoc a model1:Model1ClassAssoc ;"
+            + "      rdfs:label \"Model1Class 2 Assoc\"^^xsd:string ;"
+            + "      model1:hasEntity :i2-event ."
+            + "      "
+            + ":i2-event a model1:Model1Event ;"
+            + "      rdfs:label \"Model1Class 2 Event\"^^xsd:string ;"
+            + "      model1:hasTemporalEntity :i2-time ."
+            + ""
+            + ":i2-time a model1:TemporalInstant ;"
+            + "      rdfs:label \"Model1Class 2 Time\"^^xsd:string ;"
+            + "      model1:dateTime \"1995-11-06T05:15:01.000Z\"^^xsd:dateTime ."
+            + "      "
+            + ":i3   a       model1:Model1Class ;"
+            + "      rdfs:label \"Model1Class 3\"^^xsd:string ;"
+            + "      model1:name \"Model1Class 3\"^^xsd:string ;"
+            + "      model1:hasModel1ClassAssoc :i3-assoc ;"
+            + "      model1:model1ClassId \"ID03\"^^xsd:string ."
+            + ""
+            + ":i3-assoc a model1:Model1ClassAssoc ;"
+            + "      rdfs:label \"Model1Class 3 Assoc\"^^xsd:string ;"
+            + "      model1:hasEntity :i3-event ."
+            + "      "
+            + ":i3-event a model1:Model1Event ;"
+            + "      rdfs:label \"Model1Class 3 Event\"^^xsd:string ;"
+            + "      model1:hasTemporalEntity :i3-time ."
+            + ""
+            + ":i3-time a model1:TemporalInstant ;"
+            + "      rdfs:label \"Model1Class 3 Time\"^^xsd:string ;"
+            + "      model1:dateTime \"1999-04-30T16:30:00.000Z\"^^xsd:dateTime ."
+            + "      "
+            + ":i4   a       model1:Model1Class ;"
+            + "      rdfs:label \"Model1Class 4\"^^xsd:string ;"
+            + "      model1:name \"Model1Class 4\"^^xsd:string ;"
+            + "      model1:hasModel1ClassAssoc :i4-assoc ;"
+            + "      model1:model1ClassId \"ID04\"^^xsd:string ."
+            + ""
+            + ":i4-assoc a model1:Model1ClassAssoc ;"
+            + "      rdfs:label \"Model1Class 4 Assoc\"^^xsd:string ;"
+            + "      model1:hasEntity :i4-event ."
+            + "      "
+            + ":i4-event a model1:Model1Event ;"
+            + "      rdfs:label \"Model1Class 4 Event\"^^xsd:string ;"
+            + "      model1:hasTemporalEntity :i4-time ."
+            + ""
+            + ":i4-time a model1:TemporalInstant ;"
+            + "      rdfs:label \"Model1Class 4 Time\"^^xsd:string ;"
+            + "      model1:dateTime \"2001-02-27T21:20:00.000Z\"^^xsd:dateTime ."
+            + "      "
+            + ":i5   a       model1:Model1Class ;"
+            + "      rdfs:label \"Model1Class 5\"^^xsd:string ;"
+            + "      model1:name \"Model1Class 5\"^^xsd:string ;"
+            + "      model1:hasModel1ClassAssoc :i5-assoc ;"
+            + "      model1:model1ClassId \"ID05\"^^xsd:string ."
+            + ""
+            + ":i5-assoc a model1:Model1ClassAssoc ;"
+            + "      rdfs:label \"Model1Class 5 Assoc\"^^xsd:string ;"
+            + "      model1:hasEntity :i5-event ."
+            + "      "
+            + ":i5-event a model1:Model1Event ;"
+            + "      rdfs:label \"Model1Class 5 Event\"^^xsd:string ;"
+            + "      model1:hasTemporalEntity :i5-time ."
+            + ""
+            + ":i5-time a model1:TemporalInstant ;"
+            + "      rdfs:label \"Model1Class 5 Time\"^^xsd:string ;"
+            + "      model1:dateTime \"2002-01-16T00:30:00.000Z\"^^xsd:dateTime ."
+            + "      "
+            + ":i6   a       model1:Model1Class ;"
+            + "      rdfs:label \"Model1Class 6\"^^xsd:string ;"
+            + "      model1:name \"Model1Class 6\"^^xsd:string ;"
+            + "      model1:hasModel1ClassAssoc :i6-assoc ;"
+            + "      model1:model1ClassId \"ID06\"^^xsd:string ."
+            + ""
+            + ":i6-assoc a model1:Model1ClassAssoc ;"
+            + "      rdfs:label \"Model1Class 6 Assoc\"^^xsd:string ;"
+            + "      model1:hasEntity :i6-event ."
+            + "      "
+            + ":i6-event a model1:Model1Event ;"
+            + "      rdfs:label \"Model1Class 6 Event\"^^xsd:string ;"
+            + "      model1:hasTemporalEntity :i6-time ."
+            + ""
+            + ":i6-time a model1:TemporalInstant ;"
+            + "      rdfs:label \"Model1Class 6 Time\"^^xsd:string ;"
+            + "      model1:dateTime \"2003-04-08T13:43:00.000Z\"^^xsd:dateTime .";
+}

http://git-wip-us.apache.org/repos/asf/incubator-rya/blob/80faf06d/sail/src/test/java/mvm/rya/HashJoinTest.java
----------------------------------------------------------------------
diff --git a/sail/src/test/java/mvm/rya/HashJoinTest.java b/sail/src/test/java/mvm/rya/HashJoinTest.java
new file mode 100644
index 0000000..bbcdbcd
--- /dev/null
+++ b/sail/src/test/java/mvm/rya/HashJoinTest.java
@@ -0,0 +1,374 @@
+package mvm.rya;
+
+/*
+ * 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 info.aduna.iteration.CloseableIteration;
+import junit.framework.TestCase;
+import mvm.rya.accumulo.AccumuloRdfConfiguration;
+import mvm.rya.accumulo.AccumuloRyaDAO;
+import mvm.rya.api.RdfCloudTripleStoreUtils;
+import mvm.rya.api.domain.RyaStatement;
+import mvm.rya.api.domain.RyaType;
+import mvm.rya.api.domain.RyaURI;
+import mvm.rya.api.persist.RyaDAOException;
+import mvm.rya.api.persist.query.join.HashJoin;
+import org.apache.accumulo.core.client.Connector;
+import org.apache.accumulo.core.client.mock.MockInstance;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+import java.util.HashSet;
+import java.util.Set;
+
+import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertFalse;
+import static junit.framework.Assert.assertTrue;
+
+/**
+ * Date: 7/24/12
+ * Time: 5:51 PM
+ */
+public class HashJoinTest {
+    private AccumuloRyaDAO dao;
+    static String litdupsNS = "urn:test:litdups#";
+    private Connector connector;
+    private AccumuloRdfConfiguration conf = new AccumuloRdfConfiguration();
+
+    @Before
+    public void init() throws Exception {
+        dao = new AccumuloRyaDAO();
+        connector = new MockInstance().getConnector("", "");
+        dao.setConnector(connector);
+        dao.setConf(conf);
+        dao.init();
+    }
+
+    @After
+    public void destroy() throws Exception {
+        dao.destroy();
+    }
+
+    @Test
+    public void testSimpleJoin() throws Exception {
+        //add data
+        RyaURI pred = new RyaURI(litdupsNS, "pred1");
+        RyaType one = new RyaType("1");
+        RyaType two = new RyaType("2");
+        RyaURI subj1 = new RyaURI(litdupsNS, "subj1");
+        RyaURI subj2 = new RyaURI(litdupsNS, "subj2");
+        RyaURI subj3 = new RyaURI(litdupsNS, "subj3");
+        RyaURI subj4 = new RyaURI(litdupsNS, "subj4");
+
+        dao.add(new RyaStatement(subj1, pred, one));
+        dao.add(new RyaStatement(subj1, pred, two));
+        dao.add(new RyaStatement(subj2, pred, one));
+        dao.add(new RyaStatement(subj2, pred, two));
+        dao.add(new RyaStatement(subj3, pred, one));
+        dao.add(new RyaStatement(subj3, pred, two));
+        dao.add(new RyaStatement(subj4, pred, one));
+        dao.add(new RyaStatement(subj4, pred, two));
+        
+
+        //1 join
+        HashJoin hjoin = new HashJoin(dao.getQueryEngine());
+        CloseableIteration<RyaURI, RyaDAOException> join = hjoin.join(null,
+                new RdfCloudTripleStoreUtils.CustomEntry<RyaURI, RyaType>(pred, one),
+                new RdfCloudTripleStoreUtils.CustomEntry<RyaURI, RyaType>(pred, two));
+
+        Set<RyaURI> uris = new HashSet<RyaURI>();
+        while (join.hasNext()) {
+            uris.add(join.next());
+        }
+        assertTrue(uris.contains(subj1));
+        assertTrue(uris.contains(subj2));
+        assertTrue(uris.contains(subj3));
+        assertTrue(uris.contains(subj4));
+        join.close();
+    }
+
+    @Test
+    public void testSimpleJoinMultiWay() throws Exception {
+        //add data
+        RyaURI pred = new RyaURI(litdupsNS, "pred1");
+        RyaType one = new RyaType("1");
+        RyaType two = new RyaType("2");
+        RyaType three = new RyaType("3");
+        RyaType four = new RyaType("4");
+        RyaURI subj1 = new RyaURI(litdupsNS, "subj1");
+        RyaURI subj2 = new RyaURI(litdupsNS, "subj2");
+        RyaURI subj3 = new RyaURI(litdupsNS, "subj3");
+        RyaURI subj4 = new RyaURI(litdupsNS, "subj4");
+
+        dao.add(new RyaStatement(subj1, pred, one));
+        dao.add(new RyaStatement(subj1, pred, two));
+        dao.add(new RyaStatement(subj1, pred, three));
+        dao.add(new RyaStatement(subj1, pred, four));
+        dao.add(new RyaStatement(subj2, pred, one));
+        dao.add(new RyaStatement(subj2, pred, two));
+        dao.add(new RyaStatement(subj2, pred, three));
+        dao.add(new RyaStatement(subj2, pred, four));
+        dao.add(new RyaStatement(subj3, pred, one));
+        dao.add(new RyaStatement(subj3, pred, two));
+        dao.add(new RyaStatement(subj3, pred, three));
+        dao.add(new RyaStatement(subj3, pred, four));
+        dao.add(new RyaStatement(subj4, pred, one));
+        dao.add(new RyaStatement(subj4, pred, two));
+        dao.add(new RyaStatement(subj4, pred, three));
+        dao.add(new RyaStatement(subj4, pred, four));
+        
+
+        //1 join
+        HashJoin hjoin = new HashJoin(dao.getQueryEngine());
+        CloseableIteration<RyaURI, RyaDAOException> join = hjoin.join(null,
+                new RdfCloudTripleStoreUtils.CustomEntry<RyaURI, RyaType>(pred, one),
+                new RdfCloudTripleStoreUtils.CustomEntry<RyaURI, RyaType>(pred, two),
+                new RdfCloudTripleStoreUtils.CustomEntry<RyaURI, RyaType>(pred, three),
+                new RdfCloudTripleStoreUtils.CustomEntry<RyaURI, RyaType>(pred, four)
+        );
+
+        Set<RyaURI> uris = new HashSet<RyaURI>();
+        while (join.hasNext()) {
+            uris.add(join.next());
+        }
+        assertTrue(uris.contains(subj1));
+        assertTrue(uris.contains(subj2));
+        assertTrue(uris.contains(subj3));
+        assertTrue(uris.contains(subj4));
+        join.close();
+    }
+
+    @Test
+    public void testMergeJoinMultiWay() throws Exception {
+        //add data
+        RyaURI pred = new RyaURI(litdupsNS, "pred1");
+        RyaType zero = new RyaType("0");
+        RyaType one = new RyaType("1");
+        RyaType two = new RyaType("2");
+        RyaType three = new RyaType("3");
+        RyaType four = new RyaType("4");
+        RyaURI subj1 = new RyaURI(litdupsNS, "subj1");
+        RyaURI subj2 = new RyaURI(litdupsNS, "subj2");
+        RyaURI subj3 = new RyaURI(litdupsNS, "subj3");
+        RyaURI subj4 = new RyaURI(litdupsNS, "subj4");
+
+        dao.add(new RyaStatement(subj1, pred, one));
+        dao.add(new RyaStatement(subj1, pred, two));
+        dao.add(new RyaStatement(subj1, pred, three));
+        dao.add(new RyaStatement(subj1, pred, four));
+        dao.add(new RyaStatement(subj2, pred, zero));
+        dao.add(new RyaStatement(subj2, pred, one));
+        dao.add(new RyaStatement(subj2, pred, two));
+        dao.add(new RyaStatement(subj2, pred, three));
+        dao.add(new RyaStatement(subj2, pred, four));
+        dao.add(new RyaStatement(subj3, pred, one));
+        dao.add(new RyaStatement(subj3, pred, two));
+        dao.add(new RyaStatement(subj3, pred, four));
+        dao.add(new RyaStatement(subj4, pred, one));
+        dao.add(new RyaStatement(subj4, pred, two));
+        dao.add(new RyaStatement(subj4, pred, three));
+        dao.add(new RyaStatement(subj4, pred, four));
+        
+
+        //1 join
+        HashJoin hjoin = new HashJoin(dao.getQueryEngine());
+        CloseableIteration<RyaURI, RyaDAOException> join = hjoin.join(null,
+                new RdfCloudTripleStoreUtils.CustomEntry<RyaURI, RyaType>(pred, one),
+                new RdfCloudTripleStoreUtils.CustomEntry<RyaURI, RyaType>(pred, two),
+                new RdfCloudTripleStoreUtils.CustomEntry<RyaURI, RyaType>(pred, three),
+                new RdfCloudTripleStoreUtils.CustomEntry<RyaURI, RyaType>(pred, four)
+        );
+
+        Set<RyaURI> uris = new HashSet<RyaURI>();
+        while (join.hasNext()) {
+            uris.add(join.next());
+        }
+        assertTrue(uris.contains(subj1));
+        assertTrue(uris.contains(subj2));
+        assertTrue(uris.contains(subj4));
+        join.close();
+    }
+
+    @Test
+    public void testMergeJoinMultiWayNone() throws Exception {
+        //add data
+        RyaURI pred = new RyaURI(litdupsNS, "pred1");
+        RyaType zero = new RyaType("0");
+        RyaType one = new RyaType("1");
+        RyaType two = new RyaType("2");
+        RyaType three = new RyaType("3");
+        RyaType four = new RyaType("4");
+        RyaURI subj1 = new RyaURI(litdupsNS, "subj1");
+        RyaURI subj2 = new RyaURI(litdupsNS, "subj2");
+        RyaURI subj3 = new RyaURI(litdupsNS, "subj3");
+        RyaURI subj4 = new RyaURI(litdupsNS, "subj4");
+
+        dao.add(new RyaStatement(subj1, pred, one));
+        dao.add(new RyaStatement(subj1, pred, three));
+        dao.add(new RyaStatement(subj1, pred, four));
+        dao.add(new RyaStatement(subj2, pred, zero));
+        dao.add(new RyaStatement(subj2, pred, one));
+        dao.add(new RyaStatement(subj2, pred, four));
+        dao.add(new RyaStatement(subj3, pred, two));
+        dao.add(new RyaStatement(subj3, pred, four));
+        dao.add(new RyaStatement(subj4, pred, one));
+        dao.add(new RyaStatement(subj4, pred, two));
+        dao.add(new RyaStatement(subj4, pred, three));
+        
+
+        //1 join
+        HashJoin hjoin = new HashJoin(dao.getQueryEngine());
+        CloseableIteration<RyaURI, RyaDAOException> join = hjoin.join(null,
+                new RdfCloudTripleStoreUtils.CustomEntry<RyaURI, RyaType>(pred, one),
+                new RdfCloudTripleStoreUtils.CustomEntry<RyaURI, RyaType>(pred, two),
+                new RdfCloudTripleStoreUtils.CustomEntry<RyaURI, RyaType>(pred, three),
+                new RdfCloudTripleStoreUtils.CustomEntry<RyaURI, RyaType>(pred, four)
+        );
+
+        assertFalse(join.hasNext());
+        join.close();
+    }
+
+    @Test
+    public void testMergeJoinMultiWayNone2() throws Exception {
+        //add data
+        RyaURI pred = new RyaURI(litdupsNS, "pred1");
+        RyaType zero = new RyaType("0");
+        RyaType one = new RyaType("1");
+        RyaType two = new RyaType("2");
+        RyaType three = new RyaType("3");
+        RyaType four = new RyaType("4");
+        RyaURI subj1 = new RyaURI(litdupsNS, "subj1");
+        RyaURI subj2 = new RyaURI(litdupsNS, "subj2");
+        RyaURI subj3 = new RyaURI(litdupsNS, "subj3");
+        RyaURI subj4 = new RyaURI(litdupsNS, "subj4");
+
+        dao.add(new RyaStatement(subj1, pred, one));
+        dao.add(new RyaStatement(subj1, pred, four));
+        dao.add(new RyaStatement(subj2, pred, zero));
+        dao.add(new RyaStatement(subj2, pred, one));
+        dao.add(new RyaStatement(subj2, pred, four));
+        dao.add(new RyaStatement(subj3, pred, two));
+        dao.add(new RyaStatement(subj3, pred, four));
+        dao.add(new RyaStatement(subj4, pred, one));
+        dao.add(new RyaStatement(subj4, pred, two));
+        
+
+        //1 join
+        HashJoin hjoin = new HashJoin(dao.getQueryEngine());
+        CloseableIteration<RyaURI, RyaDAOException> join = hjoin.join(null,
+                new RdfCloudTripleStoreUtils.CustomEntry<RyaURI, RyaType>(pred, one),
+                new RdfCloudTripleStoreUtils.CustomEntry<RyaURI, RyaType>(pred, two),
+                new RdfCloudTripleStoreUtils.CustomEntry<RyaURI, RyaType>(pred, three),
+                new RdfCloudTripleStoreUtils.CustomEntry<RyaURI, RyaType>(pred, four)
+        );
+
+        assertFalse(join.hasNext());
+        join.close();
+    }
+
+    @Test
+    public void testSimpleHashJoinPredicateOnly() throws Exception {
+        //add data
+        RyaURI pred1 = new RyaURI(litdupsNS, "pred1");
+        RyaURI pred2 = new RyaURI(litdupsNS, "pred2");
+        RyaType one = new RyaType("1");
+        RyaURI subj1 = new RyaURI(litdupsNS, "subj1");
+        RyaURI subj2 = new RyaURI(litdupsNS, "subj2");
+        RyaURI subj3 = new RyaURI(litdupsNS, "subj3");
+        RyaURI subj4 = new RyaURI(litdupsNS, "subj4");
+
+        dao.add(new RyaStatement(subj1, pred1, one));
+        dao.add(new RyaStatement(subj1, pred2, one));
+        dao.add(new RyaStatement(subj2, pred1, one));
+        dao.add(new RyaStatement(subj2, pred2, one));
+        dao.add(new RyaStatement(subj3, pred1, one));
+        dao.add(new RyaStatement(subj3, pred2, one));
+        dao.add(new RyaStatement(subj4, pred1, one));
+        dao.add(new RyaStatement(subj4, pred2, one));
+        
+
+        //1 join
+        HashJoin ijoin = new HashJoin(dao.getQueryEngine());
+        CloseableIteration<RyaStatement, RyaDAOException> join = ijoin.join(null, pred1, pred2);
+
+        int count = 0;
+        while (join.hasNext()) {
+            RyaStatement next = join.next();
+            count++;
+        }
+        assertEquals(4, count);
+        join.close();
+    }
+
+    @Test
+    public void testSimpleMergeJoinPredicateOnly2() throws Exception {
+        //add data
+        RyaURI pred1 = new RyaURI(litdupsNS, "pred1");
+        RyaURI pred2 = new RyaURI(litdupsNS, "pred2");
+        RyaType one = new RyaType("1");
+        RyaType two = new RyaType("2");
+        RyaType three = new RyaType("3");
+        RyaURI subj1 = new RyaURI(litdupsNS, "subj1");
+        RyaURI subj2 = new RyaURI(litdupsNS, "subj2");
+        RyaURI subj3 = new RyaURI(litdupsNS, "subj3");
+        RyaURI subj4 = new RyaURI(litdupsNS, "subj4");
+
+        dao.add(new RyaStatement(subj1, pred1, one));
+        dao.add(new RyaStatement(subj1, pred1, two));
+        dao.add(new RyaStatement(subj1, pred1, three));
+        dao.add(new RyaStatement(subj1, pred2, one));
+        dao.add(new RyaStatement(subj1, pred2, two));
+        dao.add(new RyaStatement(subj1, pred2, three));
+        dao.add(new RyaStatement(subj2, pred1, one));
+        dao.add(new RyaStatement(subj2, pred1, two));
+        dao.add(new RyaStatement(subj2, pred1, three));
+        dao.add(new RyaStatement(subj2, pred2, one));
+        dao.add(new RyaStatement(subj2, pred2, two));
+        dao.add(new RyaStatement(subj2, pred2, three));
+        dao.add(new RyaStatement(subj3, pred1, one));
+        dao.add(new RyaStatement(subj3, pred1, two));
+        dao.add(new RyaStatement(subj3, pred1, three));
+        dao.add(new RyaStatement(subj3, pred2, one));
+        dao.add(new RyaStatement(subj3, pred2, two));
+        dao.add(new RyaStatement(subj3, pred2, three));
+        dao.add(new RyaStatement(subj4, pred1, one));
+        dao.add(new RyaStatement(subj4, pred1, two));
+        dao.add(new RyaStatement(subj4, pred1, three));
+        dao.add(new RyaStatement(subj4, pred2, one));
+        dao.add(new RyaStatement(subj4, pred2, two));
+        dao.add(new RyaStatement(subj4, pred2, three));
+        
+
+        //1 join
+        HashJoin ijoin = new HashJoin(dao.getQueryEngine());
+        CloseableIteration<RyaStatement, RyaDAOException> join = ijoin.join(null, pred1, pred2);
+
+        int count = 0;
+        while (join.hasNext()) {
+            RyaStatement next = join.next();
+            count++;
+        }
+        assertEquals(12, count);
+        join.close();
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-rya/blob/80faf06d/sail/src/test/java/mvm/rya/IterativeJoinTest.java
----------------------------------------------------------------------
diff --git a/sail/src/test/java/mvm/rya/IterativeJoinTest.java b/sail/src/test/java/mvm/rya/IterativeJoinTest.java
new file mode 100644
index 0000000..610b8eb
--- /dev/null
+++ b/sail/src/test/java/mvm/rya/IterativeJoinTest.java
@@ -0,0 +1,365 @@
+package mvm.rya;
+
+/*
+ * 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 info.aduna.iteration.CloseableIteration;
+import junit.framework.TestCase;
+import mvm.rya.accumulo.AccumuloRdfConfiguration;
+import mvm.rya.accumulo.AccumuloRyaDAO;
+import mvm.rya.api.RdfCloudTripleStoreUtils;
+import mvm.rya.api.domain.RyaStatement;
+import mvm.rya.api.domain.RyaType;
+import mvm.rya.api.domain.RyaURI;
+import mvm.rya.api.persist.RyaDAOException;
+import mvm.rya.api.persist.query.join.IterativeJoin;
+import org.apache.accumulo.core.client.Connector;
+import org.apache.accumulo.core.client.mock.MockInstance;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+import java.util.HashSet;
+import java.util.Set;
+
+import static junit.framework.Assert.*;
+
+/**
+ * Date: 7/24/12
+ * Time: 5:51 PM
+ */
+public class IterativeJoinTest {
+    private AccumuloRyaDAO dao;
+    static String litdupsNS = "urn:test:litdups#";
+    private Connector connector;
+    private AccumuloRdfConfiguration conf = new AccumuloRdfConfiguration();
+
+    @Before
+    public void init() throws Exception {
+        dao = new AccumuloRyaDAO();
+        connector = new MockInstance().getConnector("", "");
+        dao.setConnector(connector);
+        dao.setConf(conf);
+        dao.init();
+    }
+
+    @After
+    public void destroy() throws Exception {
+        dao.destroy();
+    }
+
+    @Test
+    public void testSimpleIterativeJoin() throws Exception {
+        //add data
+        RyaURI pred = new RyaURI(litdupsNS, "pred1");
+        RyaType one = new RyaType("1");
+        RyaType two = new RyaType("2");
+        RyaURI subj1 = new RyaURI(litdupsNS, "subj1");
+        RyaURI subj2 = new RyaURI(litdupsNS, "subj2");
+        RyaURI subj3 = new RyaURI(litdupsNS, "subj3");
+        RyaURI subj4 = new RyaURI(litdupsNS, "subj4");
+
+        dao.add(new RyaStatement(subj1, pred, one));
+        dao.add(new RyaStatement(subj1, pred, two));
+        dao.add(new RyaStatement(subj2, pred, one));
+        dao.add(new RyaStatement(subj2, pred, two));
+        dao.add(new RyaStatement(subj3, pred, one));
+        dao.add(new RyaStatement(subj3, pred, two));
+        dao.add(new RyaStatement(subj4, pred, one));
+        dao.add(new RyaStatement(subj4, pred, two));
+
+        //1 join
+        IterativeJoin iterJoin = new IterativeJoin(dao.getQueryEngine());
+        CloseableIteration<RyaURI, RyaDAOException> join = iterJoin.join(null, new RdfCloudTripleStoreUtils.CustomEntry<RyaURI, RyaType>(pred, one),
+                new RdfCloudTripleStoreUtils.CustomEntry<RyaURI, RyaType>(pred, two));
+
+        Set<RyaURI> uris = new HashSet<RyaURI>();
+        while (join.hasNext()) {
+            uris.add(join.next());
+        }
+        assertTrue(uris.contains(subj1));
+        assertTrue(uris.contains(subj2));
+        assertTrue(uris.contains(subj3));
+        assertTrue(uris.contains(subj4));
+        join.close();
+    }
+
+    @Test
+    public void testSimpleIterativeJoinMultiWay() throws Exception {
+        //add data
+        RyaURI pred = new RyaURI(litdupsNS, "pred1");
+        RyaType one = new RyaType("1");
+        RyaType two = new RyaType("2");
+        RyaType three = new RyaType("3");
+        RyaType four = new RyaType("4");
+        RyaURI subj1 = new RyaURI(litdupsNS, "subj1");
+        RyaURI subj2 = new RyaURI(litdupsNS, "subj2");
+        RyaURI subj3 = new RyaURI(litdupsNS, "subj3");
+        RyaURI subj4 = new RyaURI(litdupsNS, "subj4");
+
+        dao.add(new RyaStatement(subj1, pred, one));
+        dao.add(new RyaStatement(subj1, pred, two));
+        dao.add(new RyaStatement(subj1, pred, three));
+        dao.add(new RyaStatement(subj1, pred, four));
+        dao.add(new RyaStatement(subj2, pred, one));
+        dao.add(new RyaStatement(subj2, pred, two));
+        dao.add(new RyaStatement(subj2, pred, three));
+        dao.add(new RyaStatement(subj2, pred, four));
+        dao.add(new RyaStatement(subj3, pred, one));
+        dao.add(new RyaStatement(subj3, pred, two));
+        dao.add(new RyaStatement(subj3, pred, three));
+        dao.add(new RyaStatement(subj3, pred, four));
+        dao.add(new RyaStatement(subj4, pred, one));
+        dao.add(new RyaStatement(subj4, pred, two));
+        dao.add(new RyaStatement(subj4, pred, three));
+        dao.add(new RyaStatement(subj4, pred, four));
+
+        //1 join
+        IterativeJoin iterativeJoin = new IterativeJoin(dao.getQueryEngine());
+        CloseableIteration<RyaURI, RyaDAOException> join = iterativeJoin.join(null,
+                new RdfCloudTripleStoreUtils.CustomEntry<RyaURI, RyaType>(pred, one),
+                new RdfCloudTripleStoreUtils.CustomEntry<RyaURI, RyaType>(pred, two),
+                new RdfCloudTripleStoreUtils.CustomEntry<RyaURI, RyaType>(pred, three),
+                new RdfCloudTripleStoreUtils.CustomEntry<RyaURI, RyaType>(pred, four)
+        );
+
+        Set<RyaURI> uris = new HashSet<RyaURI>();
+        while (join.hasNext()) {
+            uris.add(join.next());
+        }
+        assertTrue(uris.contains(subj1));
+        assertTrue(uris.contains(subj2));
+        assertTrue(uris.contains(subj3));
+        assertTrue(uris.contains(subj4));
+        join.close();
+    }
+
+    @Test
+    public void testIterativeJoinMultiWay() throws Exception {
+        //add data
+        RyaURI pred = new RyaURI(litdupsNS, "pred1");
+        RyaType zero = new RyaType("0");
+        RyaType one = new RyaType("1");
+        RyaType two = new RyaType("2");
+        RyaType three = new RyaType("3");
+        RyaType four = new RyaType("4");
+        RyaURI subj1 = new RyaURI(litdupsNS, "subj1");
+        RyaURI subj2 = new RyaURI(litdupsNS, "subj2");
+        RyaURI subj3 = new RyaURI(litdupsNS, "subj3");
+        RyaURI subj4 = new RyaURI(litdupsNS, "subj4");
+
+        dao.add(new RyaStatement(subj1, pred, one));
+        dao.add(new RyaStatement(subj1, pred, two));
+        dao.add(new RyaStatement(subj1, pred, three));
+        dao.add(new RyaStatement(subj1, pred, four));
+        dao.add(new RyaStatement(subj2, pred, zero));
+        dao.add(new RyaStatement(subj2, pred, one));
+        dao.add(new RyaStatement(subj2, pred, two));
+        dao.add(new RyaStatement(subj2, pred, three));
+        dao.add(new RyaStatement(subj2, pred, four));
+        dao.add(new RyaStatement(subj3, pred, one));
+        dao.add(new RyaStatement(subj3, pred, two));
+        dao.add(new RyaStatement(subj3, pred, four));
+        dao.add(new RyaStatement(subj4, pred, one));
+        dao.add(new RyaStatement(subj4, pred, two));
+        dao.add(new RyaStatement(subj4, pred, three));
+        dao.add(new RyaStatement(subj4, pred, four));
+
+        //1 join
+        IterativeJoin iterativeJoin = new IterativeJoin(dao.getQueryEngine());
+        CloseableIteration<RyaURI, RyaDAOException> join = iterativeJoin.join(null,
+                new RdfCloudTripleStoreUtils.CustomEntry<RyaURI, RyaType>(pred, one),
+                new RdfCloudTripleStoreUtils.CustomEntry<RyaURI, RyaType>(pred, two),
+                new RdfCloudTripleStoreUtils.CustomEntry<RyaURI, RyaType>(pred, three),
+                new RdfCloudTripleStoreUtils.CustomEntry<RyaURI, RyaType>(pred, four)
+        );
+
+        Set<RyaURI> uris = new HashSet<RyaURI>();
+        while (join.hasNext()) {
+            uris.add(join.next());
+        }
+        assertTrue(uris.contains(subj1));
+        assertTrue(uris.contains(subj2));
+        assertTrue(uris.contains(subj4));
+        join.close();
+    }
+
+    @Test
+    public void testIterativeJoinMultiWayNone() throws Exception {
+        //add data
+        RyaURI pred = new RyaURI(litdupsNS, "pred1");
+        RyaType zero = new RyaType("0");
+        RyaType one = new RyaType("1");
+        RyaType two = new RyaType("2");
+        RyaType three = new RyaType("3");
+        RyaType four = new RyaType("4");
+        RyaURI subj1 = new RyaURI(litdupsNS, "subj1");
+        RyaURI subj2 = new RyaURI(litdupsNS, "subj2");
+        RyaURI subj3 = new RyaURI(litdupsNS, "subj3");
+        RyaURI subj4 = new RyaURI(litdupsNS, "subj4");
+
+        dao.add(new RyaStatement(subj1, pred, one));
+        dao.add(new RyaStatement(subj1, pred, three));
+        dao.add(new RyaStatement(subj1, pred, four));
+        dao.add(new RyaStatement(subj2, pred, zero));
+        dao.add(new RyaStatement(subj2, pred, one));
+        dao.add(new RyaStatement(subj2, pred, four));
+        dao.add(new RyaStatement(subj3, pred, two));
+        dao.add(new RyaStatement(subj3, pred, four));
+        dao.add(new RyaStatement(subj4, pred, one));
+        dao.add(new RyaStatement(subj4, pred, two));
+        dao.add(new RyaStatement(subj4, pred, three));
+
+        //1 join
+        IterativeJoin iterativeJoin = new IterativeJoin(dao.getQueryEngine());
+        CloseableIteration<RyaURI, RyaDAOException> join = iterativeJoin.join(null,
+                new RdfCloudTripleStoreUtils.CustomEntry<RyaURI, RyaType>(pred, one),
+                new RdfCloudTripleStoreUtils.CustomEntry<RyaURI, RyaType>(pred, two),
+                new RdfCloudTripleStoreUtils.CustomEntry<RyaURI, RyaType>(pred, three),
+                new RdfCloudTripleStoreUtils.CustomEntry<RyaURI, RyaType>(pred, four)
+        );
+
+        assertFalse(join.hasNext());
+        join.close();
+    }
+
+    @Test
+    public void testIterativeJoinMultiWayNone2() throws Exception {
+        //add data
+        RyaURI pred = new RyaURI(litdupsNS, "pred1");
+        RyaType zero = new RyaType("0");
+        RyaType one = new RyaType("1");
+        RyaType two = new RyaType("2");
+        RyaType three = new RyaType("3");
+        RyaType four = new RyaType("4");
+        RyaURI subj1 = new RyaURI(litdupsNS, "subj1");
+        RyaURI subj2 = new RyaURI(litdupsNS, "subj2");
+        RyaURI subj3 = new RyaURI(litdupsNS, "subj3");
+        RyaURI subj4 = new RyaURI(litdupsNS, "subj4");
+
+        dao.add(new RyaStatement(subj1, pred, one));
+        dao.add(new RyaStatement(subj1, pred, four));
+        dao.add(new RyaStatement(subj2, pred, zero));
+        dao.add(new RyaStatement(subj2, pred, one));
+        dao.add(new RyaStatement(subj2, pred, four));
+        dao.add(new RyaStatement(subj3, pred, two));
+        dao.add(new RyaStatement(subj3, pred, four));
+        dao.add(new RyaStatement(subj4, pred, one));
+        dao.add(new RyaStatement(subj4, pred, two));
+
+        //1 join
+        IterativeJoin iterativeJoin = new IterativeJoin(dao.getQueryEngine());
+        CloseableIteration<RyaURI, RyaDAOException> join = iterativeJoin.join(null, new RdfCloudTripleStoreUtils.CustomEntry<RyaURI, RyaType>(pred, one),
+                new RdfCloudTripleStoreUtils.CustomEntry<RyaURI, RyaType>(pred, two),
+                new RdfCloudTripleStoreUtils.CustomEntry<RyaURI, RyaType>(pred, three),
+                new RdfCloudTripleStoreUtils.CustomEntry<RyaURI, RyaType>(pred, four)
+        );
+
+        assertFalse(join.hasNext());
+        join.close();
+    }
+
+    @Test
+    public void testSimpleIterativeJoinPredicateOnly() throws Exception {
+        //add data
+        RyaURI pred1 = new RyaURI(litdupsNS, "pred1");
+        RyaURI pred2 = new RyaURI(litdupsNS, "pred2");
+        RyaType one = new RyaType("1");
+        RyaURI subj1 = new RyaURI(litdupsNS, "subj1");
+        RyaURI subj2 = new RyaURI(litdupsNS, "subj2");
+        RyaURI subj3 = new RyaURI(litdupsNS, "subj3");
+        RyaURI subj4 = new RyaURI(litdupsNS, "subj4");
+
+        dao.add(new RyaStatement(subj1, pred1, one));
+        dao.add(new RyaStatement(subj1, pred2, one));
+        dao.add(new RyaStatement(subj2, pred1, one));
+        dao.add(new RyaStatement(subj2, pred2, one));
+        dao.add(new RyaStatement(subj3, pred1, one));
+        dao.add(new RyaStatement(subj3, pred2, one));
+        dao.add(new RyaStatement(subj4, pred1, one));
+        dao.add(new RyaStatement(subj4, pred2, one));
+        
+
+        //1 join
+        IterativeJoin ijoin = new IterativeJoin(dao.getQueryEngine());
+        CloseableIteration<RyaStatement, RyaDAOException> join = ijoin.join(null, pred1, pred2);
+
+        int count = 0;
+        while (join.hasNext()) {
+            RyaStatement next = join.next();
+            count++;
+        }
+        assertEquals(4, count);
+        join.close();
+    }
+
+    @Test
+    public void testSimpleIterativeJoinPredicateOnly2() throws Exception {
+        //add data
+        RyaURI pred1 = new RyaURI(litdupsNS, "pred1");
+        RyaURI pred2 = new RyaURI(litdupsNS, "pred2");
+        RyaType one = new RyaType("1");
+        RyaType two = new RyaType("2");
+        RyaType three = new RyaType("3");
+        RyaURI subj1 = new RyaURI(litdupsNS, "subj1");
+        RyaURI subj2 = new RyaURI(litdupsNS, "subj2");
+        RyaURI subj3 = new RyaURI(litdupsNS, "subj3");
+        RyaURI subj4 = new RyaURI(litdupsNS, "subj4");
+
+        dao.add(new RyaStatement(subj1, pred1, one));
+        dao.add(new RyaStatement(subj1, pred1, two));
+        dao.add(new RyaStatement(subj1, pred1, three));
+        dao.add(new RyaStatement(subj1, pred2, one));
+        dao.add(new RyaStatement(subj1, pred2, two));
+        dao.add(new RyaStatement(subj1, pred2, three));
+        dao.add(new RyaStatement(subj2, pred1, one));
+        dao.add(new RyaStatement(subj2, pred1, two));
+        dao.add(new RyaStatement(subj2, pred1, three));
+        dao.add(new RyaStatement(subj2, pred2, one));
+        dao.add(new RyaStatement(subj2, pred2, two));
+        dao.add(new RyaStatement(subj2, pred2, three));
+        dao.add(new RyaStatement(subj3, pred1, one));
+        dao.add(new RyaStatement(subj3, pred1, two));
+        dao.add(new RyaStatement(subj3, pred1, three));
+        dao.add(new RyaStatement(subj3, pred2, one));
+        dao.add(new RyaStatement(subj3, pred2, two));
+        dao.add(new RyaStatement(subj3, pred2, three));
+        dao.add(new RyaStatement(subj4, pred1, one));
+        dao.add(new RyaStatement(subj4, pred1, two));
+        dao.add(new RyaStatement(subj4, pred1, three));
+        dao.add(new RyaStatement(subj4, pred2, one));
+        dao.add(new RyaStatement(subj4, pred2, two));
+        dao.add(new RyaStatement(subj4, pred2, three));
+        
+
+        //1 join
+        IterativeJoin ijoin = new IterativeJoin(dao.getQueryEngine());
+        CloseableIteration<RyaStatement, RyaDAOException> join = ijoin.join(null, pred1, pred2);
+
+        int count = 0;
+        while (join.hasNext()) {
+            RyaStatement next = join.next();
+            count++;
+        }
+        assertEquals(12, count);
+        join.close();
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-rya/blob/80faf06d/sail/src/test/java/mvm/rya/MergeJoinTest.java
----------------------------------------------------------------------
diff --git a/sail/src/test/java/mvm/rya/MergeJoinTest.java b/sail/src/test/java/mvm/rya/MergeJoinTest.java
new file mode 100644
index 0000000..e4f07c4
--- /dev/null
+++ b/sail/src/test/java/mvm/rya/MergeJoinTest.java
@@ -0,0 +1,370 @@
+package mvm.rya;
+
+/*
+ * 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 info.aduna.iteration.CloseableIteration;
+import mvm.rya.accumulo.AccumuloRdfConfiguration;
+import mvm.rya.accumulo.AccumuloRyaDAO;
+import mvm.rya.api.domain.RyaStatement;
+import mvm.rya.api.domain.RyaType;
+import mvm.rya.api.domain.RyaURI;
+import mvm.rya.api.persist.RyaDAOException;
+import mvm.rya.api.persist.query.join.MergeJoin;
+import org.apache.accumulo.core.client.Connector;
+import org.apache.accumulo.core.client.mock.MockInstance;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Ignore;
+import org.junit.Test;
+
+import java.util.HashSet;
+import java.util.Set;
+
+import static junit.framework.Assert.*;
+import static mvm.rya.api.RdfCloudTripleStoreUtils.CustomEntry;
+
+/**
+ * TODO: Move to rya.api when we have proper mock ryaDao
+ *
+ * Date: 7/24/12
+ * Time: 9:49 AM
+ */
+public class MergeJoinTest {
+
+    private AccumuloRyaDAO dao;
+    static String litdupsNS = "urn:test:litdups#";
+    private Connector connector;
+    private AccumuloRdfConfiguration conf = new AccumuloRdfConfiguration();
+
+    @Before
+    public void init() throws Exception {
+        dao = new AccumuloRyaDAO();
+        connector = new MockInstance().getConnector("", "");
+        dao.setConnector(connector);
+        dao.setConf(conf);
+        dao.init();
+    }
+
+    @After
+    public void destroy() throws Exception {
+        dao.destroy();
+    }
+
+    @Test
+    public void testSimpleMergeJoin() throws Exception {
+        //add data
+        RyaURI pred = new RyaURI(litdupsNS, "pred1");
+        RyaType one = new RyaType("1");
+        RyaType two = new RyaType("2");
+        RyaURI subj1 = new RyaURI(litdupsNS, "subj1");
+        RyaURI subj2 = new RyaURI(litdupsNS, "subj2");
+        RyaURI subj3 = new RyaURI(litdupsNS, "subj3");
+        RyaURI subj4 = new RyaURI(litdupsNS, "subj4");
+
+        dao.add(new RyaStatement(subj1, pred, one));
+        dao.add(new RyaStatement(subj1, pred, two));
+        dao.add(new RyaStatement(subj2, pred, one));
+        dao.add(new RyaStatement(subj2, pred, two));
+        dao.add(new RyaStatement(subj3, pred, one));
+        dao.add(new RyaStatement(subj3, pred, two));
+        dao.add(new RyaStatement(subj4, pred, one));
+        dao.add(new RyaStatement(subj4, pred, two));
+        
+
+        //1 join
+        MergeJoin mergeJoin = new MergeJoin(dao.getQueryEngine());
+        CloseableIteration<RyaURI, RyaDAOException> join = mergeJoin.join(null, new CustomEntry<RyaURI, RyaType>(pred, one),
+                new CustomEntry<RyaURI, RyaType>(pred, two));
+
+        Set<RyaURI> uris = new HashSet<RyaURI>();
+        while (join.hasNext()) {
+            uris.add(join.next());
+        }
+        assertTrue(uris.contains(subj1));
+        assertTrue(uris.contains(subj2));
+        assertTrue(uris.contains(subj3));
+        assertTrue(uris.contains(subj4));
+        join.close();
+    }
+
+    @Test
+    public void testSimpleMergeJoinPredicateOnly() throws Exception {
+        //add data
+        RyaURI pred1 = new RyaURI(litdupsNS, "pred1");
+        RyaURI pred2 = new RyaURI(litdupsNS, "pred2");
+        RyaType one = new RyaType("1");
+        RyaURI subj1 = new RyaURI(litdupsNS, "subj1");
+        RyaURI subj2 = new RyaURI(litdupsNS, "subj2");
+        RyaURI subj3 = new RyaURI(litdupsNS, "subj3");
+        RyaURI subj4 = new RyaURI(litdupsNS, "subj4");
+
+        dao.add(new RyaStatement(subj1, pred1, one));
+        dao.add(new RyaStatement(subj1, pred2, one));
+        dao.add(new RyaStatement(subj2, pred1, one));
+        dao.add(new RyaStatement(subj2, pred2, one));
+        dao.add(new RyaStatement(subj3, pred1, one));
+        dao.add(new RyaStatement(subj3, pred2, one));
+        dao.add(new RyaStatement(subj4, pred1, one));
+        dao.add(new RyaStatement(subj4, pred2, one));
+        
+
+        //1 join
+        MergeJoin mergeJoin = new MergeJoin(dao.getQueryEngine());
+        CloseableIteration<RyaStatement, RyaDAOException> join = mergeJoin.join(null, pred1, pred2);
+
+        int count = 0;
+        while (join.hasNext()) {
+            RyaStatement next = join.next();
+            count++;
+        }
+        assertEquals(4, count);
+        join.close();
+    }
+
+    @Test
+    public void testSimpleMergeJoinPredicateOnly2() throws Exception {
+        //add data
+        RyaURI pred1 = new RyaURI(litdupsNS, "pred1");
+        RyaURI pred2 = new RyaURI(litdupsNS, "pred2");
+        RyaType one = new RyaType("1");
+        RyaType two = new RyaType("2");
+        RyaType three = new RyaType("3");
+        RyaURI subj1 = new RyaURI(litdupsNS, "subj1");
+        RyaURI subj2 = new RyaURI(litdupsNS, "subj2");
+        RyaURI subj3 = new RyaURI(litdupsNS, "subj3");
+        RyaURI subj4 = new RyaURI(litdupsNS, "subj4");
+
+        dao.add(new RyaStatement(subj1, pred1, one));
+        dao.add(new RyaStatement(subj1, pred1, two));
+        dao.add(new RyaStatement(subj1, pred1, three));
+        dao.add(new RyaStatement(subj1, pred2, one));
+        dao.add(new RyaStatement(subj1, pred2, two));
+        dao.add(new RyaStatement(subj1, pred2, three));
+        dao.add(new RyaStatement(subj2, pred1, one));
+        dao.add(new RyaStatement(subj2, pred1, two));
+        dao.add(new RyaStatement(subj2, pred1, three));
+        dao.add(new RyaStatement(subj2, pred2, one));
+        dao.add(new RyaStatement(subj2, pred2, two));
+        dao.add(new RyaStatement(subj2, pred2, three));
+        dao.add(new RyaStatement(subj3, pred1, one));
+        dao.add(new RyaStatement(subj3, pred1, two));
+        dao.add(new RyaStatement(subj3, pred1, three));
+        dao.add(new RyaStatement(subj3, pred2, one));
+        dao.add(new RyaStatement(subj3, pred2, two));
+        dao.add(new RyaStatement(subj3, pred2, three));
+        dao.add(new RyaStatement(subj4, pred1, one));
+        dao.add(new RyaStatement(subj4, pred1, two));
+        dao.add(new RyaStatement(subj4, pred1, three));
+        dao.add(new RyaStatement(subj4, pred2, one));
+        dao.add(new RyaStatement(subj4, pred2, two));
+        dao.add(new RyaStatement(subj4, pred2, three));
+        
+
+        //1 join
+        MergeJoin mergeJoin = new MergeJoin(dao.getQueryEngine());
+        CloseableIteration<RyaStatement, RyaDAOException> join = mergeJoin.join(null, pred1, pred2);
+
+        int count = 0;
+        while (join.hasNext()) {
+            RyaStatement next = join.next();
+            count++;
+        }
+        assertEquals(12, count);
+        join.close();
+    }
+
+    @Test
+    public void testSimpleMergeJoinMultiWay() throws Exception {
+        //add data
+        RyaURI pred = new RyaURI(litdupsNS, "pred1");
+        RyaType one = new RyaType("1");
+        RyaType two = new RyaType("2");
+        RyaType three = new RyaType("3");
+        RyaType four = new RyaType("4");
+        RyaURI subj1 = new RyaURI(litdupsNS, "subj1");
+        RyaURI subj2 = new RyaURI(litdupsNS, "subj2");
+        RyaURI subj3 = new RyaURI(litdupsNS, "subj3");
+        RyaURI subj4 = new RyaURI(litdupsNS, "subj4");
+
+        dao.add(new RyaStatement(subj1, pred, one));
+        dao.add(new RyaStatement(subj1, pred, two));
+        dao.add(new RyaStatement(subj1, pred, three));
+        dao.add(new RyaStatement(subj1, pred, four));
+        dao.add(new RyaStatement(subj2, pred, one));
+        dao.add(new RyaStatement(subj2, pred, two));
+        dao.add(new RyaStatement(subj2, pred, three));
+        dao.add(new RyaStatement(subj2, pred, four));
+        dao.add(new RyaStatement(subj3, pred, one));
+        dao.add(new RyaStatement(subj3, pred, two));
+        dao.add(new RyaStatement(subj3, pred, three));
+        dao.add(new RyaStatement(subj3, pred, four));
+        dao.add(new RyaStatement(subj4, pred, one));
+        dao.add(new RyaStatement(subj4, pred, two));
+        dao.add(new RyaStatement(subj4, pred, three));
+        dao.add(new RyaStatement(subj4, pred, four));
+        
+
+        //1 join
+        MergeJoin mergeJoin = new MergeJoin(dao.getQueryEngine());
+        CloseableIteration<RyaURI, RyaDAOException> join = mergeJoin.join(null, new CustomEntry<RyaURI, RyaType>(pred, one),
+                new CustomEntry<RyaURI, RyaType>(pred, two),
+                new CustomEntry<RyaURI, RyaType>(pred, three),
+                new CustomEntry<RyaURI, RyaType>(pred, four)
+        );
+
+        Set<RyaURI> uris = new HashSet<RyaURI>();
+        while (join.hasNext()) {
+            uris.add(join.next());
+        }
+        assertTrue(uris.contains(subj1));
+        assertTrue(uris.contains(subj2));
+        assertTrue(uris.contains(subj3));
+        assertTrue(uris.contains(subj4));
+        join.close();
+    }
+
+    @Test
+    public void testMergeJoinMultiWay() throws Exception {
+        //add data
+        RyaURI pred = new RyaURI(litdupsNS, "pred1");
+        RyaType zero = new RyaType("0");
+        RyaType one = new RyaType("1");
+        RyaType two = new RyaType("2");
+        RyaType three = new RyaType("3");
+        RyaType four = new RyaType("4");
+        RyaURI subj1 = new RyaURI(litdupsNS, "subj1");
+        RyaURI subj2 = new RyaURI(litdupsNS, "subj2");
+        RyaURI subj3 = new RyaURI(litdupsNS, "subj3");
+        RyaURI subj4 = new RyaURI(litdupsNS, "subj4");
+
+        dao.add(new RyaStatement(subj1, pred, one));
+        dao.add(new RyaStatement(subj1, pred, two));
+        dao.add(new RyaStatement(subj1, pred, three));
+        dao.add(new RyaStatement(subj1, pred, four));
+        dao.add(new RyaStatement(subj2, pred, zero));
+        dao.add(new RyaStatement(subj2, pred, one));
+        dao.add(new RyaStatement(subj2, pred, two));
+        dao.add(new RyaStatement(subj2, pred, three));
+        dao.add(new RyaStatement(subj2, pred, four));
+        dao.add(new RyaStatement(subj3, pred, one));
+        dao.add(new RyaStatement(subj3, pred, two));
+        dao.add(new RyaStatement(subj3, pred, four));
+        dao.add(new RyaStatement(subj4, pred, one));
+        dao.add(new RyaStatement(subj4, pred, two));
+        dao.add(new RyaStatement(subj4, pred, three));
+        dao.add(new RyaStatement(subj4, pred, four));
+        
+
+        //1 join
+        MergeJoin mergeJoin = new MergeJoin(dao.getQueryEngine());
+        CloseableIteration<RyaURI, RyaDAOException> join = mergeJoin.join(null, new CustomEntry<RyaURI, RyaType>(pred, one),
+                new CustomEntry<RyaURI, RyaType>(pred, two),
+                new CustomEntry<RyaURI, RyaType>(pred, three),
+                new CustomEntry<RyaURI, RyaType>(pred, four)
+        );
+
+        Set<RyaURI> uris = new HashSet<RyaURI>();
+        while (join.hasNext()) {
+            uris.add(join.next());
+        }
+        assertTrue(uris.contains(subj1));
+        assertTrue(uris.contains(subj2));
+        assertTrue(uris.contains(subj4));
+        join.close();
+    }
+
+    @Test
+    public void testMergeJoinMultiWayNone() throws Exception {
+        //add data
+        RyaURI pred = new RyaURI(litdupsNS, "pred1");
+        RyaType zero = new RyaType("0");
+        RyaType one = new RyaType("1");
+        RyaType two = new RyaType("2");
+        RyaType three = new RyaType("3");
+        RyaType four = new RyaType("4");
+        RyaURI subj1 = new RyaURI(litdupsNS, "subj1");
+        RyaURI subj2 = new RyaURI(litdupsNS, "subj2");
+        RyaURI subj3 = new RyaURI(litdupsNS, "subj3");
+        RyaURI subj4 = new RyaURI(litdupsNS, "subj4");
+
+        dao.add(new RyaStatement(subj1, pred, one));
+        dao.add(new RyaStatement(subj1, pred, three));
+        dao.add(new RyaStatement(subj1, pred, four));
+        dao.add(new RyaStatement(subj2, pred, zero));
+        dao.add(new RyaStatement(subj2, pred, one));
+        dao.add(new RyaStatement(subj2, pred, four));
+        dao.add(new RyaStatement(subj3, pred, two));
+        dao.add(new RyaStatement(subj3, pred, four));
+        dao.add(new RyaStatement(subj4, pred, one));
+        dao.add(new RyaStatement(subj4, pred, two));
+        dao.add(new RyaStatement(subj4, pred, three));
+        
+
+        //1 join
+        MergeJoin mergeJoin = new MergeJoin(dao.getQueryEngine());
+        CloseableIteration<RyaURI, RyaDAOException> join = mergeJoin.join(null, new CustomEntry<RyaURI, RyaType>(pred, one),
+                new CustomEntry<RyaURI, RyaType>(pred, two),
+                new CustomEntry<RyaURI, RyaType>(pred, three),
+                new CustomEntry<RyaURI, RyaType>(pred, four)
+        );
+
+        assertFalse(join.hasNext());
+        join.close();
+    }
+
+    @Test
+    public void testMergeJoinMultiWayNone2() throws Exception {
+        //add data
+        RyaURI pred = new RyaURI(litdupsNS, "pred1");
+        RyaType zero = new RyaType("0");
+        RyaType one = new RyaType("1");
+        RyaType two = new RyaType("2");
+        RyaType three = new RyaType("3");
+        RyaType four = new RyaType("4");
+        RyaURI subj1 = new RyaURI(litdupsNS, "subj1");
+        RyaURI subj2 = new RyaURI(litdupsNS, "subj2");
+        RyaURI subj3 = new RyaURI(litdupsNS, "subj3");
+        RyaURI subj4 = new RyaURI(litdupsNS, "subj4");
+
+        dao.add(new RyaStatement(subj1, pred, one));
+        dao.add(new RyaStatement(subj1, pred, four));
+        dao.add(new RyaStatement(subj2, pred, zero));
+        dao.add(new RyaStatement(subj2, pred, one));
+        dao.add(new RyaStatement(subj2, pred, four));
+        dao.add(new RyaStatement(subj3, pred, two));
+        dao.add(new RyaStatement(subj3, pred, four));
+        dao.add(new RyaStatement(subj4, pred, one));
+        dao.add(new RyaStatement(subj4, pred, two));
+        
+
+        //1 join
+        MergeJoin mergeJoin = new MergeJoin(dao.getQueryEngine());
+        CloseableIteration<RyaURI, RyaDAOException> join = mergeJoin.join(null, new CustomEntry<RyaURI, RyaType>(pred, one),
+                new CustomEntry<RyaURI, RyaType>(pred, two),
+                new CustomEntry<RyaURI, RyaType>(pred, three),
+                new CustomEntry<RyaURI, RyaType>(pred, four)
+        );
+
+        assertFalse(join.hasNext());
+        join.close();
+    }
+}