/*
* Copyright 2009-2011 Collaborative Research Centre SFB 632
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package annis.sqlgen;
import annis.model.Join;
import annis.model.QueryAnnotation;
import annis.model.QueryNode;
import annis.model.QueryNode.TextMatching;
import annis.ql.parser.QueryData;
import static annis.sqlgen.SqlConstraints.between;
import static annis.sqlgen.SqlConstraints.isNotNull;
import static annis.sqlgen.SqlConstraints.isNull;
import static annis.sqlgen.SqlConstraints.join;
import static annis.sqlgen.SqlConstraints.numberJoin;
import static annis.sqlgen.SqlConstraints.sqlString;
import static annis.sqlgen.TableAccessStrategy.COMPONENT_TABLE;
import static annis.sqlgen.TableAccessStrategy.EDGE_ANNOTATION_TABLE;
import static annis.sqlgen.TableAccessStrategy.NODE_ANNOTATION_TABLE;
import static annis.sqlgen.TableAccessStrategy.NODE_TABLE;
import static annis.sqlgen.TableAccessStrategy.RANK_TABLE;
import annis.sqlgen.model.CommonAncestor;
import annis.sqlgen.model.Dominance;
import annis.sqlgen.model.Identical;
import annis.sqlgen.model.Inclusion;
import annis.sqlgen.model.LeftAlignment;
import annis.sqlgen.model.LeftDominance;
import annis.sqlgen.model.LeftOverlap;
import annis.sqlgen.model.Overlap;
import annis.sqlgen.model.PointingRelation;
import annis.sqlgen.model.Precedence;
import annis.sqlgen.model.RightAlignment;
import annis.sqlgen.model.RightDominance;
import annis.sqlgen.model.RightOverlap;
import annis.sqlgen.model.SameSpan;
import annis.sqlgen.model.Sibling;
import static annis.test.TestUtils.size;
import static annis.test.TestUtils.uniqueInt;
import static annis.test.TestUtils.uniqueLong;
import static annis.test.TestUtils.uniqueString;
import java.util.ArrayList;
import static java.util.Arrays.asList;
import java.util.List;
import java.util.Set;
import static org.hamcrest.Matchers.is;
import static org.junit.Assert.assertThat;
import org.junit.Before;
import org.junit.Test;
import org.junit.experimental.theories.DataPoints;
import org.junit.experimental.theories.Theories;
import org.junit.experimental.theories.Theory;
import static org.junit.matchers.JUnitMatchers.hasItem;
import org.junit.runner.RunWith;
import static org.mockito.BDDMockito.given;
import org.mockito.Mock;
import static org.mockito.MockitoAnnotations.initMocks;
/*
* FIXME: refactor tests, so they use the same condition constants everywhere
* also, get rid of stupid helper functions like join (dup code)
*/
/**
* Test generation of WHERE clause conditions for each operator and node search.
*/
@RunWith(Theories.class)
public class TestDefaultWhereClauseGenerator
{
// an example node
private QueryNode node23;
private QueryNode node42;
// object under test: the adapter to that node
private DefaultWhereClauseGenerator generator;
// which side to attach component predicates (name and edgeType)
// in an edge operation
@DataPoints
public final static String[] componentPredicates =
{ "lhs", "rhs", "both" };
// test data
@Mock private QueryData queryData;
@Before
public void setup()
{
initMocks(this);
node23 = new QueryNode(23);
node42 = new QueryNode(42);
generator = new TestWhereClauseGenerator();
generator.setAnnoCondition(new AnnotationConditionProvider());
}
private static class TestWhereClauseGenerator extends DefaultWhereClauseGenerator
{
@Override
protected TableAccessStrategy createTableAccessStrategy()
{
TableAccessStrategy tableAccessStrategy = new TableAccessStrategy();
tableAccessStrategy.addTableAlias(NODE_TABLE, "_node");
tableAccessStrategy.addTableAlias(COMPONENT_TABLE, "_component");
tableAccessStrategy.addTableAlias(RANK_TABLE, "_rank");
tableAccessStrategy.addTableAlias(NODE_ANNOTATION_TABLE, "_annotation");
tableAccessStrategy
.addTableAlias(EDGE_ANNOTATION_TABLE, "_rank_annotation");
tableAccessStrategy.addColumnAlias(NODE_ANNOTATION_TABLE, "namespace",
"node_annotation_namespace");
tableAccessStrategy.addColumnAlias(NODE_ANNOTATION_TABLE, "anno_ref",
"anno_ref");
tableAccessStrategy.addColumnAlias(NODE_ANNOTATION_TABLE, "name",
"node_annotation_name");
tableAccessStrategy.addColumnAlias(NODE_ANNOTATION_TABLE, "value",
"node_annotation_value");
tableAccessStrategy.addColumnAlias(EDGE_ANNOTATION_TABLE, "namespace",
"edge_annotation_namespace");
tableAccessStrategy.addColumnAlias(EDGE_ANNOTATION_TABLE, "name",
"edge_annotation_name");
tableAccessStrategy.addColumnAlias(EDGE_ANNOTATION_TABLE, "value",
"edge_annotation_value");
return tableAccessStrategy;
}
}
// helper method to check create component predicates (name, edgeType)
// on the expected side
private void checkEdgeConditions(String componentPredicate, String edgeType,
String componentName, String... expected)
{
List<String> expectedConditions = new ArrayList<>();
expectedConditions.add(join("=", "_component23.id", "_component42.id"));
if ("lhs".equals(componentPredicate) || "both".equals(componentPredicate))
{
expectedConditions
.add(join("=", "_component23.type", sqlString(edgeType)));
if (componentName == null)
{
expectedConditions.add(isNull("_component23.name"));
} else
{
expectedConditions.add(join("=", "_component23.name",
sqlString(componentName)));
}
}
if ("rhs".equals(componentPredicate) || "both".equals(componentPredicate))
{
expectedConditions
.add(join("=", "_component42.type", sqlString(edgeType)));
if (componentName == null)
{
expectedConditions.add(isNull("_component42.name"));
} else
{
expectedConditions.add(join("=", "_component42.name",
sqlString(componentName)));
}
}
expectedConditions.addAll(asList(expected));
generator.setComponentPredicates(componentPredicate);
checkWhereConditions(node23, expectedConditions.toArray(new String[]
{}));
}
/**
* WHERE conditions for direct dominance operator (>).
*/
@Theory
public void shouldGenerateWhereConditionsForNodeDirectDominance(
String componentPredicate)
{
// given
node23.addOutgoingJoin(new Dominance(node42, 1));
// then
checkEdgeConditions(componentPredicate, "d", null,
join("=", "_rank23.id", "_rank42.parent"));
}
/**
* WHERE conditions for named direct dominance operator (> name).
*/
@Theory
public void shouldGenerateWhereConditionsForNodeNamedDirectDominance(
String componentPredicate)
{
// given
String componentName = uniqueString();
node23.addOutgoingJoin(new Dominance(node42, componentName, 1));
// then
checkEdgeConditions(componentPredicate, "d", componentName,
join("=", "_rank23.id", "_rank42.parent"));
}
/**
* WHERE conditions for annotated named direct dominance (> name
* [annotation]).
*/
@Theory
public void shouldGenerateWhereConditionsForNamedAndAnnotatedDirectDominance(
String componentPredicate)
{
// given
String componentName = uniqueString();
Join j = new Dominance(node42, componentName, 1);
node23.addOutgoingJoin(j);
j.addEdgeAnnotation(new QueryAnnotation("namespace3", "name3",
"value3", TextMatching.REGEXP_EQUAL));
// then
checkEdgeConditions(componentPredicate, "d", componentName,
join("=", "_rank23.id", "_rank42.parent"));
checkWhereConditions(
node42,
"_rank_annotation42.qannotext ~ '^(namespace3:name3:(value3))$'"
);
}
/**
* WHERE conditions for indirect dominance (>*).
*/
@Theory
public void shouldGenerateWhereConditionsForIndirectDominance(
String componentPredicate)
{
// given
node23.addOutgoingJoin(new Dominance(node42));
// then
checkEdgeConditions(componentPredicate, "d", null,
join("<", "_rank23.pre", "_rank42.pre"),
join("<", "_rank42.pre", "_rank23.post"));
}
/**
* WHERE conditions for exact dominance (>n).
*/
@Theory
public void shouldGenerateWhereConditionsForExactDominance(
String componentPredicate)
{
// given
int distance = uniqueInt();
node23.addOutgoingJoin(new Dominance(node42, distance));
// then
checkEdgeConditions(componentPredicate, "d", null,
join("<", "_rank23.pre", "_rank42.pre"),
join("<", "_rank42.pre", "_rank23.post"),
numberJoin("=", "_rank23.level", "_rank42.level", -distance));
}
/**
* WHERE conditions for ranged dominance (>n,m).
*/
@Theory
public void shouldGenerateWhereConditionsForRangedDominance(
String componentPredicate)
{
// given
int min = uniqueInt(1, 10);
int max = min + uniqueInt(1, 10);
node23.addOutgoingJoin(new Dominance(node42, min, max));
// then
checkEdgeConditions(componentPredicate, "d", null,
join("<", "_rank23.pre", "_rank42.pre"),
join("<", "_rank42.pre", "_rank23.post"),
between("_rank23.level", "_rank42.level", -min, -max));
}
/**
* WHERE conditions for left dominance (>@l).
*/
//
@Theory
public void shouldGenerateWhereConditionsForLeftDominance(
String componentPredicate)
{
// given
node23.addOutgoingJoin(new LeftDominance(node42));
long corpusId = uniqueLong();
given(queryData.getCorpusList()).willReturn(asList(corpusId));
// then
checkEdgeConditions(
componentPredicate,
"d",
null,
join("=", "_rank23.id", "_rank42.parent"),
"_node42.left_token IN (SELECT min(lrsub.left_token) FROM facts_"
+ corpusId
+ " AS lrsub WHERE parent=_rank23.id AND "
+ "component_id = _rank23.component_id AND corpus_ref=_node42.corpus_ref AND lrsub.toplevel_corpus IN(" + corpusId + ")"
+ ")");
}
/**
* WHERE conditions for right dominance (>@r).
*/
@Theory
public void shouldGenerateWhereConditionsForRightDominance(
String componentPredicate)
{
// given
node23.addOutgoingJoin(new RightDominance(node42));
long corpusId = uniqueLong();
given(queryData.getCorpusList()).willReturn(asList(corpusId));
// then
checkEdgeConditions(
componentPredicate,
"d",
null,
join("=", "_rank23.id", "_rank42.parent"),
"_node42.right_token IN (SELECT max(lrsub.right_token) FROM facts_" + corpusId + " AS lrsub WHERE parent=_rank23.id AND "
+ "component_id = _rank23.component_id AND corpus_ref=_node42.corpus_ref AND lrsub.toplevel_corpus IN(" + corpusId + ")"
+ ")");
}
/**
* WHERE conditions for direct pointing relation (->).
*/
@Theory
public void shouldGenerateWhereConditionsForDirectPointingRelation(
String componentPredicate)
{
// given
String componentName = uniqueString();
node23.addOutgoingJoin(new PointingRelation(node42, componentName, 1));
// then
checkEdgeConditions(componentPredicate, "p", componentName,
join("=", "_rank23.id", "_rank42.parent"));
}
/**
* WHERE conditions for indirect pointing relation (->*).
*/
@Theory
public void shouldGenerateWhereConditionsForIndirectPointingRelation(
String componentPredicate)
{
// given
String componentName = uniqueString();
node23.addOutgoingJoin(new PointingRelation(node42, componentName));
// then
checkEdgeConditions(componentPredicate, "p", componentName,
join("<", "_rank23.pre", "_rank42.pre"),
join("<", "_rank42.pre", "_rank23.post"));
}
/**
* WHERE conditions for sibling ($).
*/
@Theory
public void shouldGenerateWhereConditionsForSibling(String componentPredicate)
{
// given
node23.addOutgoingJoin(new Sibling(node42));
// then
checkEdgeConditions(componentPredicate, "d", null,
join("=", "_rank23.parent", "_rank42.parent"),
join("<>", "_node23.id", "_node42.id"));
}
/**
* The sibling operator may optionally bind the same node to both operands.
*/
@Theory
public void shouldAllowIdenticalNodeForSiblingTarget(
String componentPredicate)
{
// given
generator.setAllowIdenticalSibling(true);
node23.addOutgoingJoin(new Sibling(node42));
// then
checkEdgeConditions(componentPredicate, "d", null,
join("=", "_rank23.parent", "_rank42.parent"));
}
/**
* WHERE conditions for common ancestor ($*).
*/
@Theory
public void shouldGenerateWhereConditionsForCommonAncestor(String componentPredicate)
{
// given
node23.addOutgoingJoin(new CommonAncestor(node42));
// then
checkEdgeConditions(componentPredicate, "d", null,
"EXISTS (SELECT 1 FROM _rank AS ancestor WHERE" + "\n\t" +
"ancestor.pre < _rank23.pre AND _rank23.pre < ancestor.post AND" + "\n\t" +
"ancestor.pre < _rank42.pre AND _rank42.pre < ancestor.post" +
"\n\tLIMIT 1)",
join("<>", "_node23.id", "_node42.id"));
}
/**
* The common ancestor operator may optionally bind the same node to both operands.
*/
@Theory
public void shouldAllowIdenticalNodeForCommonAncestorTarget(
String componentPredicate)
{
// given
generator.setAllowIdenticalSibling(true);
node23.addOutgoingJoin(new CommonAncestor(node42));
// then
checkEdgeConditions(componentPredicate, "d", null,
"EXISTS (SELECT 1 FROM _rank AS ancestor WHERE" + "\n\t" +
"ancestor.pre < _rank23.pre AND _rank23.pre < ancestor.post AND" + "\n\t" +
"ancestor.pre < _rank42.pre AND _rank42.pre < ancestor.post" +
"\n\tLIMIT 1)");
}
/**
* The common ancestor operator may optionally use a predicate on toplevel_corpus
* in the EXISTS subquery.
*/
@Theory
public void shouldUseToplevelCorpusPredicateForCommonAncestor(
String componentPredicate)
{
// given
generator.setUseToplevelCorpusPredicateInCommonAncestorSubquery(true);
long corpusId = uniqueLong();
given(queryData.getCorpusList()).willReturn(asList(corpusId));
node23.addOutgoingJoin(new CommonAncestor(node42));
// then
checkEdgeConditions(componentPredicate, "d", null,
"EXISTS (SELECT 1 FROM _rank AS ancestor WHERE" + "\n\t" +
"ancestor.pre < _rank23.pre AND _rank23.pre < ancestor.post AND" + "\n\t" +
"ancestor.pre < _rank42.pre AND _rank42.pre < ancestor.post AND toplevel_corpus IN(" + corpusId + ")" + "\n\t" +
"LIMIT 1)",
join("<>", "_node23.id", "_node42.id"));
}
/**
* The common ancestor operator should skip the predicate on toplevel_corpus
* in the EXISTS subquery if the corpus list is empty.
*/
@Theory
public void shouldSkipToplevelCorpusPredicateForCommonAncestorIfCorpusListIsEmpty(
String componentPredicate)
{
// given
generator.setUseToplevelCorpusPredicateInCommonAncestorSubquery(true);
given(queryData.getCorpusList()).willReturn(new ArrayList<Long>());
node23.addOutgoingJoin(new CommonAncestor(node42));
// then
checkEdgeConditions(componentPredicate, "d", null,
"EXISTS (SELECT 1 FROM _rank AS ancestor WHERE" + "\n\t" +
"ancestor.pre < _rank23.pre AND _rank23.pre < ancestor.post AND" + "\n\t" +
"ancestor.pre < _rank42.pre AND _rank42.pre < ancestor.post" +
"\n\tLIMIT 1)",
join("<>", "_node23.id", "_node42.id"));
}
/**
* The common ancestor operator should skip the predicate on toplevel_corpus
* in the EXISTS subquery if the corpus list is NULL.
*/
@Theory
public void shouldSkipToplevelCorpusPredicateForCommonAncestorIfCorpusListIsNull(
String componentPredicate)
{
// given
generator.setUseToplevelCorpusPredicateInCommonAncestorSubquery(true);
given(queryData.getCorpusList()).willReturn(null);
node23.addOutgoingJoin(new CommonAncestor(node42));
// then
checkEdgeConditions(componentPredicate, "d", null,
"EXISTS (SELECT 1 FROM _rank AS ancestor WHERE" + "\n\t" +
"ancestor.pre < _rank23.pre AND _rank23.pre < ancestor.post AND" + "\n\t" +
"ancestor.pre < _rank42.pre AND _rank42.pre < ancestor.post" + "\n\t" +
"LIMIT 1)",
join("<>", "_node23.id", "_node42.id"));
}
/**
* The common ancestor operator may optionally use a predicate on toplevel_corpus
* in the EXISTS subquery.
*/
@Theory
public void shouldUseComponentRefPredicateForCommonAncestor(
String componentPredicate)
{
// given
generator.setUseComponentRefPredicateInCommonAncestorSubquery(true);
long corpusId = uniqueLong();
given(queryData.getCorpusList()).willReturn(asList(corpusId));
node23.addOutgoingJoin(new CommonAncestor(node42));
// then
checkEdgeConditions(componentPredicate, "d", null,
join("=", "_rank23.component_ref", "_rank42.component_ref"),
"EXISTS (SELECT 1 FROM _rank AS ancestor WHERE" + "\n\t" +
"ancestor.component_ref = _rank23.component_ref AND" + "\n\t" +
"ancestor.component_ref = _rank42.component_ref AND" + "\n\t" +
"ancestor.pre < _rank23.pre AND _rank23.pre < ancestor.post AND" + "\n\t" +
"ancestor.pre < _rank42.pre AND _rank42.pre < ancestor.post\n\t" +
"LIMIT 1)",
join("<>", "_node23.id", "_node42.id"));
}
/**
* Indirect precedence on PostgreSQL may be optimized by an index on
* (leftToken - 1).
*/
@Test
public void shouldOptimizeIndirectPrecedenceForIndexOnLeftTokenMinus1()
{
// given
generator.setOptimizeIndirectPrecedence(true);
node23.addOutgoingJoin(new Precedence(node42));
// then
checkWhereConditions(join("=", "_node23.text_ref", "_node42.text_ref"),
numberJoin("<=", "_node23.right_token", "_node42.left_token", -1));
}
/**
* WHERE conditions for inclusion (_i_).
*/
@Test
public void shouldGenerateWhereConditionsForInclusion()
{
node23.addOutgoingJoin(new Inclusion(node42));
checkWhereConditions(
join("=", "_node23.text_ref", "_node42.text_ref"),
join("<=", "_node23.left_token", "_node42.left_token"),
join(">=", "_node23.right_token", "_node42.right_token"),
join("<>", "_node23.id", "_node42.id")
);
}
/**
* Inclusion benefits from two-sided boundaries for both left and right.
*/
@Test
public void shouldOptimizeInclusion()
{
// given
generator.setOptimizeInclusion(true);
// when
node23.addOutgoingJoin(new Inclusion(node42));
checkWhereConditions(join("=", "_node23.text_ref", "_node42.text_ref"),
join("<=", "_node23.left_token", "_node42.left_token"),
join("<=", "_node42.left_token", "_node23.right_token"),
join(">=", "_node23.right_token", "_node42.right_token"),
join(">=", "_node42.right_token", "_node23.left_token"),
join("<>", "_node23.id", "_node42.id")
);
}
/**
* WHERE conditions for isToken (tok).
*/
@Test
public void shouldGenerateWhereConditionsForIsToken()
{
// given
generator.setUseIsTokenColumn(true);
node23.setToken(true);
// then
checkWhereConditions("_node23.is_token IS TRUE");
}
@Test
public void shouldGenerateWhereConditionsForIsTokenSpanAlternative()
{
// given
generator.setUseIsTokenColumn(false);
node23.setToken(true);
// then
checkWhereConditions(isNotNull("_node23.span"), isNull("_node23.seg_name"));
}
// WHERE condition for root node
@Test
public void whereClauseForNodeRoot()
{
node23.setRoot(true);
checkWhereConditions("_rank23.root IS TRUE");
}
// WHERE condition for spanned text (string)
@Test
public void whereClauseForNodeSpanString()
{
node23.setSpannedText("string", TextMatching.EXACT_EQUAL);
checkWhereConditions(join("=", "_node23.span", "'string'"));
}
// WHERE condition for spanned text (regexp)
@Test
public void whereClauseForNodeSpanRegexp()
{
node23.setSpannedText("regexp", TextMatching.REGEXP_EQUAL);
checkWhereConditions(join("~", "_node23.span", "'^(regexp)$'"));
}
// WHERE condition for node annotation
@Test
public void whereClauseForNodeAnnotation()
{
node23.addNodeAnnotation(new QueryAnnotation("namespace1", "name1"));
node23.addNodeAnnotation(new QueryAnnotation("namespace2", "name2",
"value2", TextMatching.EXACT_EQUAL));
node23.addNodeAnnotation(new QueryAnnotation("namespace3", "name3",
"value3", TextMatching.REGEXP_EQUAL));
checkWhereConditions(
"_annotation23_1.qannotext LIKE 'namespace1:name1:%'",
"_annotation23_2.qannotext LIKE 'namespace2:name2:value2'",
"_annotation23_3.qannotext ~ '^(namespace3:name3:(value3))$'"
);
}
@Test
public void whereClauseForNodeAnnotation2Nodes()
{
node23.addNodeAnnotation(new QueryAnnotation("namespace1", "name1"));
node23.addNodeAnnotation(new QueryAnnotation("namespace2", "name2"));
node42.addNodeAnnotation(new QueryAnnotation("namespace3", "name3"));
node42.addNodeAnnotation(new QueryAnnotation("namespace4", "name4"));
node23.addOutgoingJoin(new Precedence(node42, 1));
checkWhereConditions(node23,
"_annotation23_1.qannotext LIKE 'namespace1:name1:%'",
"_annotation23_2.qannotext LIKE 'namespace2:name2:%'",
"_node23.right_token = _node42.left_token - 1",
"_node23.text_ref = _node42.text_ref"
);
checkWhereConditions(node42,
"_annotation42_1.qannotext LIKE 'namespace3:name3:%'",
"_annotation42_2.qannotext LIKE 'namespace4:name4:%'"
);
}
@Test
public void whereClauseForNodeAnnotationNot()
{
node23.addNodeAnnotation(new QueryAnnotation("namespace2", "name2",
"value2", TextMatching.EXACT_NOT_EQUAL));
node23.addNodeAnnotation(new QueryAnnotation("namespace3", "name3",
"value3", TextMatching.REGEXP_NOT_EQUAL));
checkWhereConditions(
"_annotation23_1.qannotext NOT LIKE 'namespace2:name2:value2'",
"_annotation23_1.qannotext LIKE 'namespace2:name2:%'",
"_annotation23_2.qannotext !~ '^(namespace3:name3:(value3))$'",
"_annotation23_2.qannotext LIKE 'namespace3:name3:%'"
);
}
// WHERE condition for node annotation
@Test
public void whereClauseForNodeEdgeAnnotation()
{
Dominance j = new Dominance(node23);
j.addEdgeAnnotation(new QueryAnnotation("namespace1", "name1"));
j.addEdgeAnnotation(new QueryAnnotation("namespace2", "name2",
"value2", TextMatching.EXACT_EQUAL));
j.addEdgeAnnotation(new QueryAnnotation("namespace3", "name3",
"value3", TextMatching.REGEXP_EQUAL));
node42.addOutgoingJoin(j);
checkWhereConditions(
"_rank_annotation23_1.qannotext LIKE 'namespace1:name1:%'",
"_rank_annotation23_2.qannotext LIKE 'namespace2:name2:value2'",
"_rank_annotation23_3.qannotext ~ '^(namespace3:name3:(value3))$'"
);
}
@Test
public void whereClauseForNodeEdgeAnnotationNot()
{
Dominance j = new Dominance(node23);
j.addEdgeAnnotation(new QueryAnnotation("namespace2", "name2",
"value2", TextMatching.EXACT_NOT_EQUAL));
j.addEdgeAnnotation(new QueryAnnotation("namespace3", "name3",
"value3", TextMatching.REGEXP_NOT_EQUAL));
node42.addOutgoingJoin(j);
checkWhereConditions(
"_rank_annotation23_1.qannotext NOT LIKE 'namespace2:name2:value2'",
"_rank_annotation23_1.qannotext LIKE 'namespace2:name2:%'",
"_rank_annotation23_2.qannotext !~ '^(namespace3:name3:(value3))$'",
"_rank_annotation23_2.qannotext LIKE 'namespace3:name3:%'"
);
}
// WHERE condition for _=_
@Test
public void whereClauseForNodeSameSpan()
{
node23.addOutgoingJoin(new SameSpan(node42));
checkWhereConditions(join("=", "_node23.text_ref", "_node42.text_ref"),
join("=", "_node23.left_token", "_node42.left_token"),
join("=", "_node23.right_token", "_node42.right_token"),
join("<>", "_node23.id", "_node42.id")
);
}
@Test
public void whereClauseForNodeSameSpanOperatorHack()
{
generator.setHackOperatorSameSpan(true);
node23.addOutgoingJoin(new SameSpan(node42));
checkWhereConditions(join("=", "_node23.text_ref", "_node42.text_ref"),
join("=", "_node23.left_token", "_node42.left_token"),
join("^=^", "_node23.right_token", "_node42.right_token"),
join("<>", "_node23.id", "_node42.id")
);
}
// WHERE condition for _l_
@Test
public void whereClauseForNodeLeftAlignment()
{
node23.addOutgoingJoin(new LeftAlignment(node42));
checkWhereConditions(join("=", "_node23.text_ref", "_node42.text_ref"),
join("=", "_node23.left_token", "_node42.left_token"),
join("<>", "_node23.id", "_node42.id")
);
}
// WHERE condition for _r_
@Test
public void whereClauseForNodeRightAlignment()
{
node23.addOutgoingJoin(new RightAlignment(node42));
checkWhereConditions(join("=", "_node23.text_ref", "_node42.text_ref"),
join("=", "_node23.right_token", "_node42.right_token"),
join("<>", "_node23.id", "_node42.id")
);
}
// WHERE condition for _ol_
@Test
public void whereClauseForNodeLeftOverlap()
{
node23.addOutgoingJoin(new LeftOverlap(node42));
checkWhereConditions(join("=", "_node23.text_ref", "_node42.text_ref"),
join("<=", "_node23.left_token", "_node42.left_token"),
join("<=", "_node42.left_token", "_node23.right_token"),
join("<=", "_node23.right_token", "_node42.right_token"),
join("<>", "_node23.id", "_node42.id")
);
}
// WHERE condition for _or_
// FIXME: unnecessary, is exchanged for #2 _ol_ #2
@Test
public void whereClauseForNodeRightOverlap()
{
node23.addOutgoingJoin(new RightOverlap(node42));
checkWhereConditions(join("=", "_node23.text_ref", "_node42.text_ref"),
join(">=", "_node23.right_token", "_node42.right_token"),
join(">=", "_node42.right_token", "_node23.left_token"),
join(">=", "_node23.left_token", "_node42.left_token"),
join("<>", "_node23.id", "_node42.id")
);
}
// WHERE condition for _o_
@Test
public void whereClauseForNodeOverlap()
{
node23.addOutgoingJoin(new Overlap(node42));
checkWhereConditions(join("=", "_node23.text_ref", "_node42.text_ref"),
join("<=", "_node23.left_token", "_node42.right_token"),
join("<=", "_node42.left_token", "_node23.right_token"),
join("<>", "_node23.id", "_node42.id")
);
}
@Test
public void whereClauseForIdentity()
{
node23.addOutgoingJoin(new Identical(node42));
checkWhereConditions(join("=", "_node23.id", "_node42.id"));
}
// /// Helper
private void checkWhereConditions(String... expected)
{
checkWhereConditions(node23, expected);
}
private void checkWhereConditions(QueryNode node, String... expected)
{
List<QueryNode> alternative = new ArrayList<>();
alternative.add(node);
Set<String> actual = generator.whereConditions(queryData, alternative, "");
for (String item : expected)
{
assertThat(actual, hasItem(item));
}
assertThat(actual, is(size(expected.length)));
}
}