com.bigdata.rdf.spo.TestSPORelation Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of bigdata-rdf-test Show documentation
Show all versions of bigdata-rdf-test Show documentation
Blazegraph(TM) RDF Test Suites
/*
Copyright (C) SYSTAP, LLC DBA Blazegraph 2006-2016. All rights reserved.
Contact:
SYSTAP, LLC DBA Blazegraph
2501 Calvert ST NW #106
Washington, DC 20008
[email protected]
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 Jun 19, 2008
*/
package com.bigdata.rdf.spo;
import java.util.Map;
import java.util.Properties;
import com.bigdata.bop.BOp;
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;
import com.bigdata.bop.IVariableOrConstant;
import com.bigdata.bop.Var;
import com.bigdata.bop.bindingSet.ListBindingSet;
import com.bigdata.bop.constraint.Constraint;
import com.bigdata.bop.constraint.NE;
import com.bigdata.bop.joinGraph.IEvaluationPlan;
import com.bigdata.bop.joinGraph.IEvaluationPlanFactory;
import com.bigdata.bop.joinGraph.fast.DefaultEvaluationPlan2;
import com.bigdata.bop.joinGraph.fast.DefaultEvaluationPlanFactory2;
import com.bigdata.rdf.axioms.NoAxioms;
import com.bigdata.rdf.internal.IV;
import com.bigdata.rdf.internal.VTE;
import com.bigdata.rdf.model.StatementEnum;
import com.bigdata.rdf.rules.RuleContextEnum;
import com.bigdata.rdf.store.AbstractTripleStore;
import com.bigdata.rdf.store.AbstractTripleStoreTestCase;
import com.bigdata.relation.IRelation;
import com.bigdata.relation.accesspath.IAccessPath;
import com.bigdata.relation.accesspath.IElementFilter;
import com.bigdata.relation.rule.IRule;
import com.bigdata.relation.rule.Rule;
import com.bigdata.relation.rule.eval.ActionEnum;
import com.bigdata.relation.rule.eval.IJoinNexus;
import com.bigdata.relation.rule.eval.IRuleState;
import com.bigdata.relation.rule.eval.ISolution;
import com.bigdata.relation.rule.eval.RuleState;
import com.bigdata.striterator.ChunkedArrayIterator;
import com.bigdata.striterator.IChunkedOrderedIterator;
import com.bigdata.test.MockTermIdFactory;
/**
* Test ability to insert, update, or remove elements from a relation and the
* ability to select the right access path given a predicate for that relation
* and query for those elements (we have to test all this stuff together since
* testing query requires us to have some data in the relation).
*
* @author Bryan Thompson
* @version $Id$
*/
public class TestSPORelation extends AbstractTripleStoreTestCase {
/**
*
*/
public TestSPORelation() {
}
/**
* @param name
*/
public TestSPORelation(String name) {
super(name);
}
private Constant rdfsSubClassOf;
private Constant rdfsResource;
private Constant rdfType;
private Constant rdfsClass;
private Constant someGraph;
private MockTermIdFactory factory;
protected void setUp() throws Exception {
super.setUp();
factory = new MockTermIdFactory();
rdfsSubClassOf = new Constant(factory.newTermId(VTE.URI));
rdfsResource = new Constant(factory.newTermId(VTE.URI));
rdfType = new Constant(factory.newTermId(VTE.URI));
rdfsClass = new Constant(factory.newTermId(VTE.URI));
someGraph = new Constant(factory.newTermId(VTE.URI));
}
protected void tearDown() throws Exception {
factory = null;
rdfsSubClassOf = rdfsResource = rdfType = rdfsClass = someGraph = null;
super.tearDown();
}
// /**
// * This method was introduced to make the assigned identifiers conform with
// * the expectations for identifiers as assigned by the lexicon. This was
// * necessitated by the change to {@link ISPO#hasStatementIdentifier()} to
// * test the term id bits.
// */
// private static TermId uriId(long in) {
// return new TermId(VTE.URI, in);
// }
private IV,?> uriId(long ignored) {
return factory.newTermId(VTE.URI);
}
// private static long literalId(long in) {
// return in << ITermIdCodes.TERMID_CODE_MASK_BITS
// | ITermIdCodes.TERMID_CODE_LITERAL;
// }
//
// private static long bnodeId(long in) {
// return in << ITermIdCodes.TERMID_CODE_MASK_BITS
// | ITermIdCodes.TERMID_CODE_BNODE;
// }
// protected final static Constant rdfsSubClassOf = new Constant(
// uriId(1L));
//
// protected final static Constant rdfsResource = new Constant(
// uriId(2L));
//
// protected final static Constant rdfType = new Constant(
// uriId(3L));
//
// protected final static Constant rdfsClass = new Constant(
// uriId(4L));
//
// protected final static Constant rdfProperty = new Constant(
// uriId(5L));
/**
* this is rdfs9:
*
*
* (?u,rdfs:subClassOf,?x), (?v,rdf:type,?u) -> (?v,rdf:type,?x)
*
*
* @author Bryan Thompson
* @version $Id$
*/
@SuppressWarnings({ "serial", "unchecked" })
protected class TestRuleRdfs9 extends Rule {
// @SuppressWarnings("unchecked")
public TestRuleRdfs9(String relation) {
super( "rdfs9",//
new P(relation,var("v"), rdfType, var("x")), //
new IPredicate[] {//
new P(relation, var("u"), rdfsSubClassOf, var("x")),//
new P(relation, var("v"), rdfType, var("u")) //
},//
new IConstraint[] {
Constraint.wrap(new NE(var("u"),var("x")))
}
);
}
}
protected static class P extends SPOPredicate {
/**
* Required shallow copy constructor.
*/
public P(final BOp[] values, final Map annotations) {
super(values, annotations);
}
/**
* Constructor required for {@link com.bigdata.bop.BOpUtility#deepCopy(FilterNode)}.
*/
public P(final P op) {
super(op);
}
/**
* @param relation
* @param s
* @param p
* @param o
*/
public P(String relation, IVariableOrConstant s,
IVariableOrConstant p, IVariableOrConstant o) {
super(relation, s, p, o );
}
}
/**
* Test the ability to obtain the correct {@link IAccessPath} given a
* {@link IPredicate} and an empty {@link SPORelation}. The choice of the
* {@link IAccessPath} is made first based on the binding pattern and only
* ties are broken based on range counts. This allows us to test the choice
* of the access path in the absence of any data in the {@link SPORelation}.
*
* @todo There are some variable combinations that are not being tested.
*/
public void test_ruleState() {
final AbstractTripleStore store = getStore();
try {
final String relationIdentifier = store.getSPORelation()
.getNamespace();
final IJoinNexus joinNexus = store.newJoinNexusFactory(
RuleContextEnum.HighLevelQuery,
ActionEnum.Query, IJoinNexus.ALL, null/* filter */)
.newInstance(store.getIndexManager());
/*
* rdfs9 uses a constant in the [p] position of the for both tails
* and the other positions are unbound, so the correct index is POS
* for both tails.
*/
{
final IRule rule = new TestRuleRdfs9(relationIdentifier);
final IBindingSet bindingSet = joinNexus.newBindingSet(rule);
{
final IPredicate asBound = rule.getTail(0).asBound(
bindingSet);
final IRelation relation = joinNexus
.getTailRelationView(asBound);
// (u rdfs:subClassOf x)
assertEquals(SPOKeyOrder.POS, joinNexus.getTailAccessPath(
relation, asBound).getKeyOrder());
}
{
final IPredicate asBound = rule.getTail(1).asBound(
bindingSet);
final IRelation relation = joinNexus
.getTailRelationView(asBound);
// (v rdfs:subClassOf u)
assertEquals(SPOKeyOrder.POS, joinNexus.getTailAccessPath(
relation, asBound).getKeyOrder());
}
}
/*
* Verify that a rule with a single tail predicate that has no
* constants will select the SPO index.
*/
{
final IRule rule = new Rule("testRule",
// head
new SPOPredicate(relationIdentifier, Var.var("x"), Var
.var("y"), Var.var("z")),
// tail
new SPOPredicate[] { new SPOPredicate(relationIdentifier, Var
.var("x"), Var.var("y"), Var.var("z")) },
// constraints
new IConstraint[] {});
final IBindingSet bindingSet = joinNexus.newBindingSet(rule);
final IPredicate asBound = rule.getTail(0).asBound(bindingSet);
final IRelation relation = joinNexus.getTailRelationView(asBound);
// (x y z)
assertEquals(SPOKeyOrder.SPO, joinNexus.getTailAccessPath(
relation, asBound).getKeyOrder());
}
/*
* Verify selection of the OSP and SPO access path based on one
* bound predicates.
*/
{
final IRule rule = new Rule("testRule",
// head
new SPOPredicate(relationIdentifier, Var.var("x"), Var
.var("y"), Var.var("z")),
// tail
new SPOPredicate[] {//
new SPOPredicate(relationIdentifier,
new Constant(uriId(2L)), Var.var("y"),
Var.var("z")),//
new SPOPredicate(relationIdentifier, Var.var("x"),
Var.var("y"), new Constant(uriId(1L))) //
},
// constraints
new IConstraint[] {});
final IBindingSet bindingSet = joinNexus.newBindingSet(rule);
{
final IPredicate asBound = rule.getTail(0).asBound(
bindingSet);
final IRelation relation = joinNexus
.getTailRelationView(asBound);
// (1L y z)
assertEquals(SPOKeyOrder.SPO, joinNexus.getTailAccessPath(
relation, asBound).getKeyOrder());
}
{
final IPredicate asBound = rule.getTail(1).asBound(
bindingSet);
final IRelation relation = joinNexus
.getTailRelationView(asBound);
// (x y 1L)
assertEquals(SPOKeyOrder.OSP, joinNexus.getTailAccessPath(
relation, asBound).getKeyOrder());
}
}
} finally {
store.__tearDownUnitTest();
}
}
/**
* Test the ability insert data into a relation and pull back that data
* using a variety of access paths. The test also checks the the correct
* evaluation orders are computed based on the data actually in the relation
* and that those evaluation orders change as we add data to the relation.
* Finally, the test simulates how an {@link ISolution} would be computed
* based on incremental binding of variables.
*/
public void test_insertQuery() {
final Properties properties = super.getProperties();
// override the default axiom model.
properties.setProperty(com.bigdata.rdf.store.AbstractTripleStore.Options.AXIOMS_CLASS, NoAxioms.class.getName());
final AbstractTripleStore store = getStore(properties);
try {
final String relationIdentifier = store.getSPORelation()
.getNamespace();
final IJoinNexus joinNexus = store.newJoinNexusFactory(
RuleContextEnum.HighLevelQuery,
ActionEnum.Query, IJoinNexus.ALL, null/* filter */)
.newInstance(store.getIndexManager());
final SPORelation spoRelation = store.getSPORelation();
// define some vocabulary.
final IConstant U1 = new Constant(uriId(11L));
final IConstant U2 = new Constant(uriId(12L));
final IConstant V1 = new Constant(uriId(21L));
final IConstant V2 = new Constant(uriId(22L));
final IConstant X1 = new Constant(uriId(31L));
// final IConstant X2 = new Constant(32L);
// (?u,rdfs:subClassOf,?x), (?v,rdf:type,?u) -> (?v,rdf:type,?x)
final Rule rule = new TestRuleRdfs9(relationIdentifier);
/*
* Note: This is the original evaluation order based on NO data in
* the relation.
*
* Note: Since both tails are 2-unbound and there is NO data for
* either tail there is no preference in the evaluation order.
*/
{
final IEvaluationPlan plan = new DefaultEvaluationPlan2(
joinNexus, rule);
log.info("original plan=" + plan);
}
/*
* Obtain the access paths corresponding to each predicate in the
* body of the rule. Each access path is parameterized by the triple
* pattern described by the corresponding predicate in the body of
* the rule.
*
* Note: even when using the same access paths the range counts CAN
* differ based on what constants are bound in each predicate and on
* what positions are variables.
*
* Note: When there are shared variables the range count generally
* will be different after those variable(s) become bound.
*/
{
final IBindingSet bindingSet = joinNexus.newBindingSet(rule);
for (int i = 0; i < rule.getTailCount(); i++) {
final IPredicate pred = rule.getTail(i).asBound(bindingSet);
final IRelation relation = joinNexus
.getTailRelationView(pred);
final IAccessPath accessPath = joinNexus
.getTailAccessPath(relation, pred);
assertEquals(0, accessPath.rangeCount(true/* exact */));
assertEquals(0, accessPath.rangeCount(false/* exact */));
}
}
/*
* Add some data into the store where it is visible to those access
* paths and notice the change in the range count.
*/
{
final SPO[] a = new SPO[] {
// (u rdf:subClassOf x)
new SPO(U1, rdfsSubClassOf, X1, StatementEnum.Explicit),
// (v rdf:type u)
new SPO(V1, rdfType, U1, StatementEnum.Explicit),
new SPO(V2, rdfType, U2, StatementEnum.Explicit)
};
assertEquals(3,
spoRelation.insert(new ChunkedArrayIterator(
a.length, a, null/* keyOrder */)));
if (log.isInfoEnabled()) {
log.info("KB Dump:\n" + spoRelation.dump(SPOKeyOrder.SPO));
}
assertEquals(3, spoRelation.getAccessPath(NULL, NULL, NULL)
.rangeCount(true/*exact*/));
}
/*
* Verify range counts for the access paths for each predicate in
* the tail. These counts reflect the data that we just wrote onto
* the relation.
*/
{
// (u rdf:subClassOf x)
assertEquals(1, spoRelation.getAccessPath(rule.getTail(0))
.rangeCount(false/* exact */));
// (v rdf:type u)
assertEquals(2, spoRelation.getAccessPath(rule.getTail(1))
.rangeCount(false/* exact */));
}
/*
* Verify the evaluation plan now that one of the tail predicates is
* more selective than the other based on their range counts (they
* have the same #of unbound variables).
*/
{
final IEvaluationPlan plan = new DefaultEvaluationPlan2(
joinNexus, rule);
log.info("updated plan=" + plan);
// (u rdf:subClassOf x)
assertEquals("order", new int[] { 0, 1 }, plan.getOrder());
}
/*
* Incrementally binding the variables in the rule.
*
* First bind variables for (u rdf:subClassOf x) to known values
* from the statement in the database that matches the predicate.
*/
{
final IBindingSet bindings = joinNexus.newBindingSet(rule);
final IRuleState ruleState = new RuleState(rule, joinNexus);
bindings.set(Var.var("u"), U1);
bindings.set(Var.var("x"), X1);
assertTrue(rule.isFullyBound(0, bindings));
/*
* Now bind the last variable.
*/
bindings.set(Var.var("v"), V1);
assertTrue(rule.isFullyBound(1, bindings));
// emit the entailment
final ISolution solution = joinNexus.newSolution(rule,
bindings);
// verify the entailed statement.
assertEquals(V1.get(), solution.get().s);
assertEquals(rdfType.get(), solution.get().p);
assertEquals(X1.get(), solution.get().o);
// verify rule is reported.
assertTrue(rule == solution.getRule());
// verify correct bindings are reported.
assertTrue(bindings.equals(solution.getBindingSet()));
// verify that a copy was made of the bindings.
assertTrue(bindings != solution.getBindingSet());
}
} finally {
store.__tearDownUnitTest();
}
}
/**
* A simple test of rule execution, including query against an empty kb,
* insert of some elements into the kb, query to verify that the data is in
* the kb, insert driven by a rule set, and query to verify that insert.
*
* @throws Exception
*
* @todo the test is only verifying insert by range counts on access paths
* corresponding to the predicates in the tail of the rule. it should
* go further and verify the specific elements.
*
* @todo test rule that deletes the computed solutions.
*/
public void test_runRule() throws Exception {
final IElementFilter filter = null;
final boolean justify = false;
final boolean backchain = false;
final IEvaluationPlanFactory planFactory = DefaultEvaluationPlanFactory2.INSTANCE;
final Properties properties = super.getProperties();
// override the default axiom model.
properties.setProperty(
com.bigdata.rdf.store.AbstractTripleStore.Options.AXIOMS_CLASS,
NoAxioms.class.getName());
final AbstractTripleStore store = getStore(properties);
try {
final String relationIdentifier = store.getSPORelation()
.getNamespace();
final SPORelation spoRelation = store.getSPORelation();
// define some vocabulary.
final IConstant U1 = new Constant(uriId(11L));
final IConstant U2 = new Constant(uriId(12L));
final IConstant V1 = new Constant(uriId(21L));
final IConstant V2 = new Constant(uriId(22L));
final IConstant X1 = new Constant(uriId(31L));
// final IConstant X2 = new Constant(32L);
// (?u,rdfs:subClassOf,?x), (?v,rdf:type,?u) -> (?v,rdf:type,?x)
final Rule rule = new TestRuleRdfs9(relationIdentifier);
/*
* Verify Query with no data in the KB.
*/
{
log.info("\n\nQuery w/o data in KB\n");
final IJoinNexus joinNexus = store.newJoinNexusFactory(
RuleContextEnum.HighLevelQuery,
ActionEnum.Query, IJoinNexus.ALL, null/* filter */)
.newInstance(store.getIndexManager());
final IChunkedOrderedIterator itr = joinNexus
.runQuery(rule);
try {
assertFalse(itr.hasNext());
} finally {
itr.close();
}
}
/*
* Add some data into the store where it is visible to the access
* paths in use by the rule and notice the change in the range
* count.
*
* Note: Given the rule and the data that we add into the KB.
*
* (?u,rdfs:subClassOf,?x), (?v,rdf:type,?u) -> (?v,rdf:type,?x)
*
* there should be one solution:
*
* (U1,rdfs:subClassOf,X1), (V1,rdf:type,U1) -> (V1,rdf:type,X1)
*
* This is checked below.
*/
{
final SPO[] a = new SPO[] {
// (u rdf:subClassOf x)
new SPO(U1, rdfsSubClassOf, X1, StatementEnum.Explicit),
// (v rdf:type u)
new SPO(V1, rdfType, U1, StatementEnum.Explicit),
new SPO(V2, rdfType, U2, StatementEnum.Explicit)
};
assertEquals(3,
spoRelation.insert(new ChunkedArrayIterator(
a.length, a, null/* keyOrder */)));
if (log.isInfoEnabled()) {
log.info("KB Dump:\n" + spoRelation.dump(SPOKeyOrder.SPO));
}
assertEquals(3, spoRelation.getAccessPath(NULL, NULL, NULL)
.rangeCount(true/*exact*/));
}
/*
* Verify range counts for the access paths for each predicate in
* the tail. These counts reflect the data that we just wrote onto
* the relation.
*/
{
// (u rdf:subClassOf x)
assertEquals(1, spoRelation.getAccessPath(rule.getTail(0))
.rangeCount(false/* exact */));
// (v rdf:type u)
assertEquals(2, spoRelation.getAccessPath(rule.getTail(1))
.rangeCount(false/* exact */));
}
/*
* Execute the rule again (Query) and see what we get.
*/
{
final IJoinNexus joinNexus = store
.newJoinNexusFactory(RuleContextEnum.HighLevelQuery,
ActionEnum.Query, IJoinNexus.ALL, filter,
justify, backchain, planFactory).newInstance(
store.getIndexManager());
// /*
// * Note: We commit before running the Query since the writes
// * will not otherwise be present in the read-committed view.
// */
// store.commit();
if(log.isInfoEnabled())
log.info("\n\nQuery with data in KB\n");
final IChunkedOrderedIterator itr = joinNexus
.runQuery(rule);
// (U1,rdfs:subClassOf,X1), (V1,rdf:type,U1) -> (V1,rdf:type,X1)
final SPO expectedSPO = new SPO(V1, rdfType, X1,
StatementEnum.Inferred);
final IBindingSet expectedBindingSet =
new ListBindingSet()
// new ArrayBindingSet(rule.getVariableCount())
;
expectedBindingSet.set(Var.var("u"), U1);
expectedBindingSet.set(Var.var("v"), V1);
expectedBindingSet.set(Var.var("x"), X1);
try {
assertTrue(itr.hasNext());
final ISolution solution = itr.next();
if (!solution.get().equals(expectedSPO)) {
fail("expected: " + expectedSPO + ", actual="
+ solution.get());
}
assertTrue(solution.getRule() == rule);
if (!solution.getBindingSet().equals(expectedBindingSet)) {
fail("expected=" + expectedBindingSet + ", actual="
+ solution.getBindingSet());
}
if(itr.hasNext()) {
fail("Not expecting another solution: "+itr.next());
}
} finally {
itr.close();
}
}
/*
* Execute the rule as a mutation (insert) and then verify the
* mutation count (1L) and the data actually written on the relation
* (the SPO from the solution that we verified above).
*/
{
final IJoinNexus joinNexus = store.newJoinNexusFactory(
RuleContextEnum.DatabaseAtOnceClosure,
ActionEnum.Insert, IJoinNexus.ALL, filter, justify,
backchain, planFactory).newInstance(
store.getIndexManager());
if(log.isInfoEnabled())
log.info("\n\nRun rules as insert operations\n");
final long mutationCount = joinNexus.runMutation(rule);
assertEquals("mutationCount", 1L, mutationCount);
}
/*
* Verify range counts for the access paths for each predicate in
* the tail. These counts reflect the data that we just wrote onto
* the relation.
*/
{
// (u rdf:subClassOf x)
assertEquals(1, spoRelation.getAccessPath(rule.getTail(0))
.rangeCount(false/* exact */));
// (v rdf:type u)
assertEquals(3, spoRelation.getAccessPath(rule.getTail(1))
.rangeCount(false/* exact */));
}
} finally {
store.__tearDownUnitTest();
}
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy