/**
Copyright (C) SYSTAP, LLC DBA Blazegraph 2006-2016. All rights reserved.
Contact:
SYSTAP, LLC DBA Blazegraph
2501 Calvert ST NW #106
Washington, DC 20008
licenses@blazegraph.com
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; version 2 of the License.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
/*
* Created on Aug 18, 2010
*/
package com.bigdata.bop.join;
import java.util.Properties;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
import junit.framework.TestCase2;
import com.bigdata.bop.BOp;
import com.bigdata.bop.BOpBase;
import com.bigdata.bop.BOpContext;
import com.bigdata.bop.Constant;
import com.bigdata.bop.IBindingSet;
import com.bigdata.bop.IConstant;
import com.bigdata.bop.IConstraint;
import com.bigdata.bop.IPredicate.Annotations;
import com.bigdata.bop.IVariable;
import com.bigdata.bop.IVariableOrConstant;
import com.bigdata.bop.NV;
import com.bigdata.bop.Var;
import com.bigdata.bop.ap.E;
import com.bigdata.bop.ap.Predicate;
import com.bigdata.bop.ap.R;
import com.bigdata.bop.bindingSet.ListBindingSet;
import com.bigdata.bop.constraint.Constraint;
import com.bigdata.bop.constraint.INBinarySearch;
import com.bigdata.bop.constraint.NEConstant;
import com.bigdata.bop.engine.AbstractQueryEngineTestCase;
import com.bigdata.bop.engine.BlockingBufferWithStats;
import com.bigdata.bop.engine.MockRunningQuery;
import com.bigdata.journal.BufferMode;
import com.bigdata.journal.ITx;
import com.bigdata.journal.Journal;
import com.bigdata.relation.accesspath.IAsynchronousIterator;
import com.bigdata.relation.accesspath.IBlockingBuffer;
import com.bigdata.relation.accesspath.ThickAsynchronousIterator;
import com.bigdata.striterator.ChunkedArrayIterator;
/**
* Unit tests for the {@link PipelineJoin} operator.
* <p>
* Note: The logic to map binding sets over shards is tested independently.
*
* @author <a href="mailto:thompsonbry@users.sourceforge.net">Bryan Thompson</a>
* @version $Id$
*/
public class TestPipelineJoin extends TestCase2 {
/**
*
*/
public TestPipelineJoin() {
}
/**
* @param name
*/
public TestPipelineJoin(String name) {
super(name);
}
@Override
public Properties getProperties() {
final Properties p = new Properties(super.getProperties());
p.setProperty(Journal.Options.BUFFER_MODE, BufferMode.Transient
.toString());
return p;
}
static private final String namespace = "ns";
Journal jnl;
@Override
public void setUp() throws Exception {
super.setUp();
jnl = new Journal(getProperties());
loadData(jnl);
}
/**
* Create and populate relation in the {@link #namespace}.
*/
private void loadData(final Journal store) {
// create the relation.
final R rel = new R(store, namespace, ITx.UNISOLATED, new Properties());
rel.create();
// data to insert.
final E[] a = {//
new E("John", "Mary"),//
new E("Mary", "Paul"),//
new E("Paul", "Leon"),//
new E("Leon", "Paul"),//
new E("Mary", "John"),//
};
// insert data (the records are not pre-sorted).
rel.insert(new ChunkedArrayIterator<E>(a.length, a, null/* keyOrder */));
// Do commit since not scale-out.
store.commit();
}
@Override
public void tearDown() throws Exception {
if (jnl != null) {
jnl.destroy();
jnl = null;
}
super.tearDown();
}
/**
* Return an {@link IAsynchronousIterator} that will read a single
* {@link IBindingSet}.
*
* @param bindingSet
* the binding set.
*/
protected ThickAsynchronousIterator<IBindingSet[]> newBindingSetIterator(
final IBindingSet bindingSet) {
return new ThickAsynchronousIterator<IBindingSet[]>(
new IBindingSet[][] { new IBindingSet[] { bindingSet } });
}
/**
* Unit test for a pipeline join without shared variables and fed by a
* single empty binding set.
*
* @throws ExecutionException
* @throws InterruptedException
*/
public void test_join_noSharedVariables_emptySourceSolution()
throws InterruptedException, ExecutionException {
final int joinId = 2;
final int predId = 3;
final Predicate<E> predOp = new Predicate<E>(new IVariableOrConstant[] {
new Constant<String>("Mary"), Var.var("x") }, NV
.asMap(new NV[] {//
new NV(Predicate.Annotations.RELATION_NAME,
new String[] { namespace }),//
new NV(Predicate.Annotations.BOP_ID, predId),//
new NV(Annotations.TIMESTAMP,
ITx.READ_COMMITTED),//
}));
final PipelineJoin<E> query = new PipelineJoin<E>(
new BOp[] {},// args
new NV(Predicate.Annotations.BOP_ID, joinId),//
new NV(PipelineJoin.Annotations.PREDICATE, predOp));
// the expected solutions.
final IBindingSet[] expected = new IBindingSet[] {//
new ListBindingSet(//
new IVariable[] { Var.var("x") },//
new IConstant[] { new Constant<String>("John") }//
),//
new ListBindingSet(//
new IVariable[] { Var.var("x") },//
new IConstant[] { new Constant<String>("Paul") }//
),//
};
final PipelineJoinStats stats = query.newStats();
final IAsynchronousIterator<IBindingSet[]> source = new ThickAsynchronousIterator<IBindingSet[]>(
new IBindingSet[][] { new IBindingSet[] { new ListBindingSet()} });
final IBlockingBuffer<IBindingSet[]> sink = new BlockingBufferWithStats<IBindingSet[]>(query, stats);
final BOpContext<IBindingSet> context = new BOpContext<IBindingSet>(
new MockRunningQuery(null/* fed */, jnl/* indexManager */
), -1/* partitionId */, stats,query/* op */,
false/* lastInvocation */,
source, sink, null/* sink2 */);
// get task.
final FutureTask<Void> ft = query.eval(context);
// execute task.
jnl.getExecutorService().execute(ft);
AbstractQueryEngineTestCase.assertSameSolutionsAnyOrder(expected, sink.iterator(),
ft);
// join task
assertEquals(1L, stats.chunksIn.get());
assertEquals(1L, stats.unitsIn.get());
assertEquals(2L, stats.unitsOut.get());
assertEquals(1L, stats.chunksOut.get());
// access path
assertEquals(0L, stats.accessPathDups.get());
assertEquals(1L, stats.accessPathCount.get());
assertEquals(1L, stats.accessPathChunksIn.get());
assertEquals(2L, stats.accessPathUnitsIn.get());
// assertTrue(ft.isDone());
// assertFalse(ft.isCancelled());
// ft.get(); // verify nothing thrown.
}
/**
* Unit test for a join without shared variables with multiple source
* solutions.
*
* @throws InterruptedException
* @throws ExecutionException
*/
public void test_join_noSharedVariables_multipleSourceSolutions()
throws InterruptedException, ExecutionException {
// final int startId = 1;
final int joinId = 2;
final int predId = 3;
// final BOp startOp = new CopyOp(new BOp[] {}, NV.asMap(new NV[] {//
// new NV(Predicate.Annotations.BOP_ID, startId),//
// }));
final Predicate<E> predOp = new Predicate<E>(new IVariableOrConstant[] {
new Constant<String>("Mary"), Var.var("x") }, NV
.asMap(new NV[] {//
new NV(Predicate.Annotations.RELATION_NAME,
new String[] { namespace }),//
new NV(Predicate.Annotations.BOP_ID, predId),//
new NV(Annotations.TIMESTAMP,
ITx.READ_COMMITTED),//
}));
final PipelineJoin<E> query = new PipelineJoin<E>(
new BOp[] { },// args
new NV(Predicate.Annotations.BOP_ID, joinId),//
new NV(PipelineJoin.Annotations.PREDICATE, predOp)//
// new NV(PipelineJoin.Annotations.COALESCE_DUPLICATE_ACCESS_PATHS, false)//
);
// the expected solutions.
final IBindingSet[] expected = new IBindingSet[] {//
new ListBindingSet(//
new IVariable[] { Var.var("x"), Var.var("y") },//
new IConstant[] { new Constant<String>("John"), new Constant<String>("Jack") }//
),//
new ListBindingSet(//
new IVariable[] { Var.var("x"), Var.var("y") },//
new IConstant[] { new Constant<String>("Paul"), new Constant<String>("Jack") }//
),//
new ListBindingSet(//
new IVariable[] { Var.var("x"), Var.var("z") },//
new IConstant[] { new Constant<String>("John"), new Constant<String>("Jill") }//
),//
new ListBindingSet(//
new IVariable[] { Var.var("x"), Var.var("z") },//
new IConstant[] { new Constant<String>("Paul"), new Constant<String>("Jill") }//
),//
};
final PipelineJoinStats stats = query.newStats();
final IAsynchronousIterator<IBindingSet[]> source;
{
final IBindingSet bset1 = new ListBindingSet();
final IBindingSet bset2 = new ListBindingSet();
bset1.set(Var.var("y"), new Constant<String>("Jack"));
bset2.set(Var.var("z"), new Constant<String>("Jill"));
source = new ThickAsynchronousIterator<IBindingSet[]>(
new IBindingSet[][] { new IBindingSet[] { bset1, bset2 } });
}
final IBlockingBuffer<IBindingSet[]> sink = new BlockingBufferWithStats<IBindingSet[]>(query, stats);
final BOpContext<IBindingSet> context = new BOpContext<IBindingSet>(
new MockRunningQuery(null/* fed */, jnl/* indexManager */
), -1/* partitionId */, stats,query/* op */,
false/* lastInvocation */,
source, sink, null/* sink2 */);
// get task.
final FutureTask<Void> ft = query.eval(context);
// execute task.
jnl.getExecutorService().execute(ft);
AbstractQueryEngineTestCase.assertSameSolutionsAnyOrder(expected, sink.iterator(),
ft);
// join task
assertEquals(1L, stats.chunksIn.get());
assertEquals(2L, stats.unitsIn.get());
assertEquals(4L, stats.unitsOut.get());
assertEquals(1L, stats.chunksOut.get());
// access path
assertEquals(1L, stats.accessPathDups.get());
assertEquals(1L, stats.accessPathCount.get());
assertEquals(1L, stats.accessPathChunksIn.get());
assertEquals(2L, stats.accessPathUnitsIn.get());
// assertTrue(ft.isDone());
// assertFalse(ft.isCancelled());
// ft.get(); // verify nothing thrown.
}
/**
* Unit test for a join with shared variables with multiple source solutions
* (the source solutions already have a bound value for the shared variable
* so the join turns into a point test).
*
* @throws InterruptedException
* @throws ExecutionException
*/
public void test_join_sharedVariables_multipleSourceSolutions()
throws InterruptedException, ExecutionException {
final int joinId = 2;
final int predId = 3;
final Predicate<E> predOp = new Predicate<E>(new IVariableOrConstant[] {
new Constant<String>("Mary"), Var.var("x") }, NV
.asMap(new NV[] {//
new NV(Predicate.Annotations.RELATION_NAME,
new String[] { namespace }),//
new NV(Predicate.Annotations.BOP_ID, predId),//
new NV(Annotations.TIMESTAMP,
ITx.READ_COMMITTED),//
}));
final PipelineJoin<E> query = new PipelineJoin<E>(
new BOp[] { },// args
new NV(Predicate.Annotations.BOP_ID, joinId),//
new NV(PipelineJoin.Annotations.PREDICATE, predOp)//
);
// the expected solutions.
final IBindingSet[] expected = new IBindingSet[] {//
new ListBindingSet(//
new IVariable[] { Var.var("x"), Var.var("y") },//
new IConstant[] { new Constant<String>("John"), new Constant<String>("Jack") }//
),//
new ListBindingSet(//
new IVariable[] { Var.var("x"), Var.var("z") },//
new IConstant[] { new Constant<String>("Paul"), new Constant<String>("Jill") }//
),//
};
final PipelineJoinStats stats = query.newStats();
final IAsynchronousIterator<IBindingSet[]> source;
{
final IBindingSet bset1 = new ListBindingSet();
final IBindingSet bset2 = new ListBindingSet();
bset1.set(Var.var("x"), new Constant<String>("John"));
bset1.set(Var.var("y"), new Constant<String>("Jack"));
bset2.set(Var.var("x"), new Constant<String>("Paul"));
bset2.set(Var.var("z"), new Constant<String>("Jill"));
source = new ThickAsynchronousIterator<IBindingSet[]>(
new IBindingSet[][] { new IBindingSet[] { bset1, bset2 } });
}
final IBlockingBuffer<IBindingSet[]> sink = new BlockingBufferWithStats<IBindingSet[]>(query, stats);
final BOpContext<IBindingSet> context = new BOpContext<IBindingSet>(
new MockRunningQuery(null/* fed */, jnl/* indexManager */
), -1/* partitionId */, stats,query/* op */,
false/* lastInvocation */,
source, sink, null/* sink2 */);
// get task.
final FutureTask<Void> ft = query.eval(context);
// execute task.
jnl.getExecutorService().execute(ft);
AbstractQueryEngineTestCase.assertSameSolutionsAnyOrder(expected, sink.iterator(),
ft);
// join task
assertEquals(1L, stats.chunksIn.get());
assertEquals(2L, stats.unitsIn.get());
assertEquals(2L, stats.unitsOut.get());
assertEquals(1L, stats.chunksOut.get());
// access path
assertEquals(0L, stats.accessPathDups.get());
assertEquals(2L, stats.accessPathCount.get());
assertEquals(2L, stats.accessPathChunksIn.get());
assertEquals(2L, stats.accessPathUnitsIn.get());
}
/**
* Unit test for a pipeline join in which we expect duplicate access paths to
* be eliminated.
*
* @throws ExecutionException
* @throws InterruptedException
*/
public void test_join_duplicateElimination() throws InterruptedException,
ExecutionException {
// final int startId = 1;
final int joinId = 2;
final int predId = 3;
// final BOp startOp = new CopyOp(new BOp[] {}, NV.asMap(new NV[] {//
// new NV(Predicate.Annotations.BOP_ID, startId),//
// }));
final Predicate<E> predOp = new Predicate<E>(new IVariableOrConstant[] {
new Constant<String>("Mary"), Var.var("x") }, NV
.asMap(new NV[] {//
new NV(Predicate.Annotations.RELATION_NAME,
new String[] { namespace }),//
new NV(Predicate.Annotations.BOP_ID, predId),//
new NV(Annotations.TIMESTAMP,
ITx.READ_COMMITTED),//
}));
final PipelineJoin<E> query = new PipelineJoin<E>(
new BOp[] { },// args
new NV(Predicate.Annotations.BOP_ID, joinId),//
new NV(PipelineJoin.Annotations.PREDICATE, predOp));
// the expected solutions (each solution appears twice since we feed two empty binding sets in).
final IBindingSet[] expected = new IBindingSet[] {//
new ListBindingSet(//
new IVariable[] { Var.var("x") },//
new IConstant[] { new Constant<String>("John") }//
),//
new ListBindingSet(//
new IVariable[] { Var.var("x") },//
new IConstant[] { new Constant<String>("Paul") }//
),//
new ListBindingSet(//
new IVariable[] { Var.var("x") },//
new IConstant[] { new Constant<String>("John") }//
),//
new ListBindingSet(//
new IVariable[] { Var.var("x") },//
new IConstant[] { new Constant<String>("Paul") }//
),//
};
final PipelineJoinStats stats = query.newStats();
// submit TWO (2) empty binding sets in ONE (1) chunk.
final IAsynchronousIterator<IBindingSet[]> source = new ThickAsynchronousIterator<IBindingSet[]>(
new IBindingSet[][] { new IBindingSet[] { new ListBindingSet(), new ListBindingSet()} });
final IBlockingBuffer<IBindingSet[]> sink = new BlockingBufferWithStats<IBindingSet[]>(query, stats);
final BOpContext<IBindingSet> context = new BOpContext<IBindingSet>(
new MockRunningQuery(null/* fed */, jnl/* indexManager */
), -1/* partitionId */, stats,query/* op */,
false/* lastInvocation */,
source, sink, null/* sink2 */);
// get task.
final FutureTask<Void> ft = query.eval(context);
// execute task.
jnl.getExecutorService().execute(ft);
// ft.get();// wait for completion (before showing stats), then look for errors.
//
// // show stats.
// System.err.println("stats: "+stats);
// verify solutions.
AbstractQueryEngineTestCase.assertSameSolutionsAnyOrder(expected, sink.iterator(),
ft);
// verify stats.
// join task
assertEquals(1L, stats.chunksIn.get());
assertEquals(2L, stats.unitsIn.get());
assertEquals(4L, stats.unitsOut.get());
assertEquals(1L, stats.chunksOut.get());
// access path
assertEquals(1L, stats.accessPathDups.get());
assertEquals(1L, stats.accessPathCount.get());
assertEquals(1L, stats.accessPathChunksIn.get());
assertEquals(2L, stats.accessPathUnitsIn.get());
// assertTrue(ft.isDone());
// assertFalse(ft.isCancelled());
// ft.get(); // verify nothing thrown.
}
/**
* Unit test for a join with an {@link IConstraint}. The constraint is used
* to filter out one of the solutions where "Mary" is the present in the
* first column of the relation.
*
* @throws ExecutionException
* @throws InterruptedException
*/
public void test_join_constraint() throws InterruptedException, ExecutionException {
// final Var<String> x = Var.var("x");
final Var<String> y = Var.var("y");
final IConstant<String>[] set = new IConstant[]{
new Constant<String>("John"),//
};
// final int startId = 1;
final int joinId = 2;
final int predId = 3;
// final BOp startOp = new CopyOp(new BOp[] {}, NV.asMap(new NV[] {//
// new NV(BOpBase.Annotations.BOP_ID, startId),//
// }));
final Predicate<E> predOp = new Predicate<E>(
new IVariableOrConstant[] { new Constant<String>("Mary"), y },//
NV.asMap(new NV[] {//
new NV(
Predicate.Annotations.RELATION_NAME,
new String[] { namespace }),//
new NV(Predicate.Annotations.BOP_ID,
predId),//
new NV(Annotations.TIMESTAMP, ITx.READ_COMMITTED),//
}));
final PipelineJoin<E> query = new PipelineJoin<E>(
new BOp[] { },// args
new NV(BOpBase.Annotations.BOP_ID, joinId),//
new NV(PipelineJoin.Annotations.PREDICATE, predOp),//
new NV(PipelineJoin.Annotations.CONSTRAINTS,
new IConstraint[] { Constraint.wrap(new INBinarySearch<String>(y, set)) }));
// the expected solution (just one).
final IBindingSet[] expected = new IBindingSet[] {//
new ListBindingSet(//
new IVariable[] { Var.var("y") },//
new IConstant[] { new Constant<String>("John") }//
) };
final PipelineJoinStats stats = query.newStats();
final IAsynchronousIterator<IBindingSet[]> source = new ThickAsynchronousIterator<IBindingSet[]>(
new IBindingSet[][] { new IBindingSet[] { new ListBindingSet() } });
final IBlockingBuffer<IBindingSet[]> sink = new BlockingBufferWithStats<IBindingSet[]>(query, stats);
final BOpContext<IBindingSet> context = new BOpContext<IBindingSet>(
new MockRunningQuery(null/* fed */, jnl/* indexManager */),
-1/* partitionId */, stats, query/* op */,
false/* lastInvocation */, source, sink, null/* sink2 */);
// get task.
final FutureTask<Void> ft = query.eval(context);
// execute task.
jnl.getExecutorService().execute(ft);
AbstractQueryEngineTestCase.assertSameSolutions(expected, sink.iterator(), ft);
// join task
assertEquals(1L, stats.chunksIn.get());
assertEquals(1L, stats.unitsIn.get());
assertEquals(1L, stats.unitsOut.get());
assertEquals(1L, stats.chunksOut.get());
// access path
assertEquals(0L, stats.accessPathDups.get());
assertEquals(1L, stats.accessPathCount.get());
assertEquals(1L, stats.accessPathChunksIn.get());
assertEquals(2L, stats.accessPathUnitsIn.get());
// assertTrue(ft.isDone());
// assertFalse(ft.isCancelled());
// ft.get(); // verify nothing thrown.
}
/**
* Unit test for a join which selects a subset of the variables to pass
* along.
* <p>
* Note: The order of the expected solutions for this test depends on the
* order of the keys associated with the tuples in the relation. Since the
* key is [name,value], the result order is based on this tuple order:
*
* <pre>
* E("John", "Mary")
* E("Leon", "Paul")
* E("Mary", "John")
* E("Mary", "Paul")
* E("Paul", "Leon")
* </pre>
*
* @throws ExecutionException
* @throws InterruptedException
*/
public void test_join_selectVariables() throws InterruptedException, ExecutionException {
final Var<String> x = Var.var("x");
final Var<String> y = Var.var("y");
final int joinId = 2;
final int predId = 3;
final Predicate<E> predOp = new Predicate<E>(new IVariableOrConstant[] {
x, y },//
NV.asMap(new NV[] {//
new NV(Predicate.Annotations.RELATION_NAME,
new String[] { namespace }),//
new NV(Predicate.Annotations.BOP_ID, predId),//
new NV(Annotations.TIMESTAMP,
ITx.READ_COMMITTED),//
}));
final PipelineJoin<E> query = new PipelineJoin<E>(
new BOp[] { /*startOp*/ },//
new NV(BOpBase.Annotations.BOP_ID, joinId),//
new NV(PipelineJoin.Annotations.PREDICATE, predOp),//
new NV(PipelineJoin.Annotations.SELECT, new IVariable[] { y }));
/*
* The expected solutions.
*/
final IBindingSet[] expected = new IBindingSet[] {//
new ListBindingSet(//
new IVariable[] { Var.var("y") },//
new IConstant[] { new Constant<String>("Mary") }//
),
new ListBindingSet(//
new IVariable[] { Var.var("y") },//
new IConstant[] { new Constant<String>("Paul") }//
),
new ListBindingSet(//
new IVariable[] { Var.var("y") },//
new IConstant[] { new Constant<String>("John") }//
),
new ListBindingSet(//
new IVariable[] { Var.var("y") },//
new IConstant[] { new Constant<String>("Paul") }//
),
new ListBindingSet(//
new IVariable[] { Var.var("y") },//
new IConstant[] { new Constant<String>("Leon") }//
),
};
final PipelineJoinStats stats = query.newStats();
final IAsynchronousIterator<IBindingSet[]> source = new ThickAsynchronousIterator<IBindingSet[]>(
new IBindingSet[][] { new IBindingSet[] { new ListBindingSet() } });
final IBlockingBuffer<IBindingSet[]> sink = new BlockingBufferWithStats<IBindingSet[]>(query, stats);
final BOpContext<IBindingSet> context = new BOpContext<IBindingSet>(
new MockRunningQuery(null/* fed */, jnl/* indexManager */),
-1/* partitionId */, stats, query/* op */,
false/* lastInvocation */, source, sink, null/* sink2 */);
// get task.
final FutureTask<Void> ft = query.eval(context);
// execute task.
jnl.getExecutorService().execute(ft);
AbstractQueryEngineTestCase.assertSameSolutions(expected, sink.iterator(), ft);
// join task
assertEquals(1L, stats.chunksIn.get());
assertEquals(1L, stats.unitsIn.get());
assertEquals(5L, stats.unitsOut.get());
assertEquals(1L, stats.chunksOut.get());
// access path
assertEquals(0L, stats.accessPathDups.get());
assertEquals(1L, stats.accessPathCount.get());
assertEquals(1L, stats.accessPathChunksIn.get());
assertEquals(5L, stats.accessPathUnitsIn.get());
// assertTrue(ft.isDone());
// assertFalse(ft.isCancelled());
// ft.get(); // verify nothing thrown.
}
/**
* Unit tests for optional joins. For an optional join, an alternative sink
* may be specified for the join. When specified, it is used if the join
* fails (if not specified, the binding sets which do not join are forwarded
* to the primary sink). Binding sets which join go to the primary sink
* regardless.
*
* @throws ExecutionException
* @throws InterruptedException
*/
public void test_optionalJoin() throws InterruptedException, ExecutionException {
final Var<?> x = Var.var("x");
// final int startId = 1;
final int joinId = 2;
final int predId = 3;
// final BOp startOp = new CopyOp(new BOp[] {}, NV.asMap(new NV[] {//
// new NV(Predicate.Annotations.BOP_ID, startId),//
// }));
final Predicate<E> pred = new Predicate<E>(new IVariableOrConstant[] {
new Constant<String>("Mary"), x }, NV.asMap(new NV[] {//
new NV(Predicate.Annotations.RELATION_NAME,
new String[] { namespace }),//
new NV(Predicate.Annotations.BOP_ID, predId),//
new NV(Predicate.Annotations.OPTIONAL, Boolean.TRUE),//
new NV(Annotations.TIMESTAMP,
ITx.READ_COMMITTED),//
}));
final PipelineJoin<E> query = new PipelineJoin<E>(
new BOp[] { }, // args
new NV(BOpBase.Annotations.BOP_ID, joinId),//
new NV(PipelineJoin.Annotations.PREDICATE, pred)//
// new NV(PipelineJoin.Annotations.OPTIONAL, Boolean.TRUE)
);
/*
* Setup the source with two initial binding sets. One has nothing bound
* and will join with (Mary,x:=John) and (Mary,x:=Paul). The other has
* x:=Luke which does not join. However, this is an optional join so
* x:=Luke should output anyway.
*/
final IAsynchronousIterator<IBindingSet[]> source;
{
final IBindingSet bset1 = new ListBindingSet();
final IBindingSet bset2 = new ListBindingSet();
{
bset2.set(x, new Constant<String>("Luke"));
}
source = new ThickAsynchronousIterator<IBindingSet[]>(
new IBindingSet[][] { new IBindingSet[] { bset1, bset2 } });
}
// the expected solutions.
final IBindingSet[] expected = new IBindingSet[] {//
new ListBindingSet(//
new IVariable[] { x },//
new IConstant[] { new Constant<String>("John") }//
),//
new ListBindingSet(//
new IVariable[] { x },//
new IConstant[] { new Constant<String>("Paul") }//
),//
new ListBindingSet(//
new IVariable[] { x },//
new IConstant[] { new Constant<String>("Luke") }//
),//
};
final PipelineJoinStats stats = query.newStats();
final IBlockingBuffer<IBindingSet[]> sink = new BlockingBufferWithStats<IBindingSet[]>(query, stats);
final BOpContext<IBindingSet> context = new BOpContext<IBindingSet>(
new MockRunningQuery(null/* fed */, jnl/* indexManager */),
-1/* partitionId */, stats, query/* op */,
false/* lastInvocation */, source, sink, null/* sink2 */);
// get task.
final FutureTask<Void> ft = query.eval(context);
// execute task.
jnl.getExecutorService().execute(ft);
AbstractQueryEngineTestCase.assertSameSolutions(expected, sink.iterator(), ft);
// join task
assertEquals(1L, stats.chunksIn.get());
assertEquals(2L, stats.unitsIn.get());
assertEquals(3L, stats.unitsOut.get());
assertEquals(1L, stats.chunksOut.get());
// access path
assertEquals(0L, stats.accessPathDups.get());
assertEquals(2L, stats.accessPathCount.get());
assertEquals(1L, stats.accessPathChunksIn.get());
assertEquals(2L, stats.accessPathUnitsIn.get());
// assertTrue(ft.isDone());
// assertFalse(ft.isCancelled());
// ft.get(); // verify nothing thrown.
}
/**
* Unit test for an optional {@link PipelineJoin} when the
* {@link BOpContext#getSink2() alternative sink} is specified.
*
* @throws InterruptedException
* @throws ExecutionException
*/
public void test_optionalJoin_withAltSink() throws InterruptedException,
ExecutionException {
final Var<?> x = Var.var("x");
// final int startId = 1;
final int joinId = 2;
final int predId = 3;
// final BOp startOp = new CopyOp(new BOp[] {}, NV.asMap(new NV[] {//
// new NV(Predicate.Annotations.BOP_ID, startId),//
// }));
final Predicate<E> pred = new Predicate<E>(new IVariableOrConstant[] {
new Constant<String>("Mary"), x }, NV.asMap(new NV[] {//
new NV(Predicate.Annotations.RELATION_NAME,
new String[] { namespace }),//
new NV(Predicate.Annotations.BOP_ID, predId),//
new NV(Predicate.Annotations.OPTIONAL, Boolean.TRUE),//
new NV(Annotations.TIMESTAMP,
ITx.READ_COMMITTED),//
}));
final PipelineJoin<E> query = new PipelineJoin<E>(
new BOp[] { },// args
new NV(BOpBase.Annotations.BOP_ID, joinId),//
new NV(PipelineJoin.Annotations.PREDICATE, pred)//
// new NV(PipelineJoin.Annotations.OPTIONAL, Boolean.TRUE)
);
/*
* Setup the source with two initial binding sets. One has nothing bound
* and will join with (Mary,x:=John) and (Mary,x:=Paul). The other has
* x:=Luke which does not join. However, this is an optional join so
* x:=Luke should output anyway.
*/
final IAsynchronousIterator<IBindingSet[]> source;
{
final IBindingSet bset1 = new ListBindingSet();
final IBindingSet bset2 = new ListBindingSet();
{
bset2.set(x, new Constant<String>("Luke"));
}
source = new ThickAsynchronousIterator<IBindingSet[]>(
new IBindingSet[][] { new IBindingSet[] { bset1, bset2 } });
}
// the expected solutions for the default sink.
final IBindingSet[] expected = new IBindingSet[] {//
new ListBindingSet(//
new IVariable[] { x },//
new IConstant[] { new Constant<String>("John") }//
),//
new ListBindingSet(//
new IVariable[] { x },//
new IConstant[] { new Constant<String>("Paul") }//
),//
};
// the expected solutions for the alternative sink.
final IBindingSet[] expected2 = new IBindingSet[] {//
new ListBindingSet(//
new IVariable[] { x },//
new IConstant[] { new Constant<String>("Luke") }//
),//
};
final PipelineJoinStats stats = query.newStats();
final IBlockingBuffer<IBindingSet[]> sink = new BlockingBufferWithStats<IBindingSet[]>(
query, stats);
final IBlockingBuffer<IBindingSet[]> sink2 = new BlockingBufferWithStats<IBindingSet[]>(
query, stats);
final BOpContext<IBindingSet> context = new BOpContext<IBindingSet>(
new MockRunningQuery(null/* fed */, jnl/* indexManager */),
-1/* partitionId */, stats, query/* op */,
false/* lastInvocation */, source, sink, sink2);
// get task.
final FutureTask<Void> ft = query.eval(context);
// execute task.
jnl.getExecutorService().execute(ft);
AbstractQueryEngineTestCase.assertSameSolutions(expected, sink.iterator(), ft);
AbstractQueryEngineTestCase.assertSameSolutions(expected2, sink2.iterator(), ft);
// join task
assertEquals(1L, stats.chunksIn.get());
assertEquals(2L, stats.unitsIn.get());
assertEquals(3L, stats.unitsOut.get());
assertEquals(2L, stats.chunksOut.get());
// access path
assertEquals(0L, stats.accessPathDups.get());
assertEquals(2L, stats.accessPathCount.get());
assertEquals(1L, stats.accessPathChunksIn.get());
assertEquals(2L, stats.accessPathUnitsIn.get());
// assertTrue(ft.isDone());
// assertFalse(ft.isCancelled());
// ft.get(); // verify nothing thrown.
}
/**
* Unit tests for optional joins with a constraint. The constraint is
* applied to test each solution which joins. Solutions which do not join,
* or which join but fail the constraint, are passed along as "optional"
* solutions.
*
* @throws ExecutionException
* @throws InterruptedException
*/
public void test_optionalJoin_withConstraint() throws InterruptedException,
ExecutionException {
final Var<?> x = Var.var("x");
final int joinId = 2;
final int predId = 3;
final Predicate<E> pred = new Predicate<E>(new IVariableOrConstant[] {
new Constant<String>("Mary"), x }, NV.asMap(new NV[] {//
new NV(Predicate.Annotations.RELATION_NAME,
new String[] { namespace }),//
new NV(Predicate.Annotations.BOP_ID, predId),//
new NV(Predicate.Annotations.OPTIONAL, Boolean.TRUE),//
new NV(Annotations.TIMESTAMP, ITx.READ_COMMITTED),//
}));
final PipelineJoin<E> query = new PipelineJoin<E>(
new BOp[] { }, // args
new NV(BOpBase.Annotations.BOP_ID, joinId),//
new NV(PipelineJoin.Annotations.PREDICATE, pred),//
// constraint d != Paul
new NV(PipelineJoin.Annotations.CONSTRAINTS,
new IConstraint[] { Constraint.wrap(new NEConstant(x, new Constant<String>("Paul"))) })
);
/*
* Setup the source with three source binding sets.
*
* The first source solution joins with (Mary,x:=John) and
* (Mary,x:=Paul). However, the join with (Mary,x:=Paul) is failed by
* the constraint. Since we were joining with bset1 and since we also
* have the (Mary,x:=John) solution for bset1, bset1 is NOT passed along
* as an "optional" join.
*
* The next source solution has x:=Luke which does not join. However,
* this is an optional join so x:=Luke is output anyway.
*
* The last source solution has x:=Paul. This will fail the constraint
* in the join (x!=Paul). However, since nothing joins for this source
* solution, the source solution is passed along as an optional
* solution. Note that the constraint does NOT filter the optional
* solution.
*/
final IAsynchronousIterator<IBindingSet[]> source;
{
final IBindingSet bset1 = new ListBindingSet();
final IBindingSet bset2 = new ListBindingSet();
{
bset2.set(x, new Constant<String>("Luke"));
}
final IBindingSet bset3 = new ListBindingSet();
{
bset3.set(x, new Constant<String>("Paul"));
}
source = new ThickAsynchronousIterator<IBindingSet[]>(
new IBindingSet[][] { new IBindingSet[] { bset1, bset2,
bset3 } });
}
// the expected solutions.
final IBindingSet[] expected = new IBindingSet[] {//
// bset1 : one join passes the constraint, so no optionals.
new ListBindingSet(//
new IVariable[] { x },//
new IConstant[] { new Constant<String>("John") }//
),//
// bset2 : join fails, but bset2 is output anyway as "optional".
new ListBindingSet(//
new IVariable[] { x },//
new IConstant[] { new Constant<String>("Luke") }//
),//
// bset3: join fails, but bset3 is output anyway as "optional".
new ListBindingSet(//
new IVariable[] { x },//
new IConstant[] { new Constant<String>("Paul") }//
),//
};
final PipelineJoinStats stats = query.newStats();
final IBlockingBuffer<IBindingSet[]> sink = new BlockingBufferWithStats<IBindingSet[]>(
query, stats);
final BOpContext<IBindingSet> context = new BOpContext<IBindingSet>(
new MockRunningQuery(null/* fed */, jnl/* indexManager */),
-1/* partitionId */, stats, query/* op */,
false/* lastInvocation */, source, sink, null/* sink2 */);
// get task.
final FutureTask<Void> ft = query.eval(context);
// execute task.
jnl.getExecutorService().execute(ft);
AbstractQueryEngineTestCase.assertSameSolutionsAnyOrder(expected, sink.iterator(), ft);
// join task
assertEquals(1L, stats.chunksIn.get());
assertEquals(3L, stats.unitsIn.get());
assertEquals(3L, stats.unitsOut.get());
assertEquals(1L, stats.chunksOut.get());
// access path
assertEquals(0L, stats.accessPathDups.get());
assertEquals(3L, stats.accessPathCount.get());
assertEquals(2L, stats.accessPathChunksIn.get());
assertEquals(3L, stats.accessPathUnitsIn.get());
// assertTrue(ft.isDone());
// assertFalse(ft.isCancelled());
// ft.get(); // verify nothing thrown.
}
}