Query optimization for subspanquery with an empty or negative subquery.
diff --git a/src/main/java/de/ids_mannheim/korap/KorapQuery.java b/src/main/java/de/ids_mannheim/korap/KorapQuery.java
index 78d050b..71f9338 100644
--- a/src/main/java/de/ids_mannheim/korap/KorapQuery.java
+++ b/src/main/java/de/ids_mannheim/korap/KorapQuery.java
@@ -10,7 +10,20 @@
import com.fasterxml.jackson.databind.ObjectMapper;
import de.ids_mannheim.korap.query.SpanWithinQuery;
-import de.ids_mannheim.korap.query.wrap.*;
+import de.ids_mannheim.korap.query.wrap.SpanAlterQueryWrapper;
+import de.ids_mannheim.korap.query.wrap.SpanAttributeQueryWrapper;
+import de.ids_mannheim.korap.query.wrap.SpanClassQueryWrapper;
+import de.ids_mannheim.korap.query.wrap.SpanElementQueryWrapper;
+import de.ids_mannheim.korap.query.wrap.SpanMatchModifyQueryWrapper;
+import de.ids_mannheim.korap.query.wrap.SpanQueryWrapper;
+import de.ids_mannheim.korap.query.wrap.SpanRegexQueryWrapper;
+import de.ids_mannheim.korap.query.wrap.SpanRepetitionQueryWrapper;
+import de.ids_mannheim.korap.query.wrap.SpanSegmentQueryWrapper;
+import de.ids_mannheim.korap.query.wrap.SpanSequenceQueryWrapper;
+import de.ids_mannheim.korap.query.wrap.SpanSimpleQueryWrapper;
+import de.ids_mannheim.korap.query.wrap.SpanSubspanQueryWrapper;
+import de.ids_mannheim.korap.query.wrap.SpanWildcardQueryWrapper;
+import de.ids_mannheim.korap.query.wrap.SpanWithinQueryWrapper;
import de.ids_mannheim.korap.response.Notifications;
import de.ids_mannheim.korap.util.QueryException;
@@ -247,9 +260,10 @@
if (DEBUG) log.trace("Wrap span reference {},{}", startOffset, length);
- return new SpanSubspanQueryWrapper(
- this.fromJson(operands.get(0)), startOffset, length
- );
+ SpanQueryWrapper sqw = this.fromJson(operands.get(0));
+ SpanSubspanQueryWrapper ssqw = new SpanSubspanQueryWrapper(
+ sqw, startOffset, length);
+ return ssqw;
};
if (DEBUG) log.trace("Wrap class reference {}", number);
@@ -593,8 +607,8 @@
SpanQueryWrapper sqw = this.fromJson(operands.get(0));
// Problematic
- if (sqw.maybeExtension())
- return sqw.setClassNumber(number);
+ // if (sqw.maybeExtension())
+ // return sqw.setClassNumber(number);
return new SpanClassQueryWrapper(sqw, number);
};
diff --git a/src/main/java/de/ids_mannheim/korap/query/SpanSubspanQuery.java b/src/main/java/de/ids_mannheim/korap/query/SpanSubspanQuery.java
index ecb1bd9..d16042e 100644
--- a/src/main/java/de/ids_mannheim/korap/query/SpanSubspanQuery.java
+++ b/src/main/java/de/ids_mannheim/korap/query/SpanSubspanQuery.java
@@ -75,9 +75,9 @@
StringBuilder sb = new StringBuilder();
sb.append("subspan(");
sb.append(this.firstClause.toString());
- sb.append(",");
+ sb.append(", ");
sb.append(this.startOffset);
- sb.append(",");
+ sb.append(", ");
sb.append(this.length);
sb.append(")");
return sb.toString();
diff --git a/src/main/java/de/ids_mannheim/korap/query/wrap/SpanClassQueryWrapper.java b/src/main/java/de/ids_mannheim/korap/query/wrap/SpanClassQueryWrapper.java
index 2f59079..fd1ed56 100644
--- a/src/main/java/de/ids_mannheim/korap/query/wrap/SpanClassQueryWrapper.java
+++ b/src/main/java/de/ids_mannheim/korap/query/wrap/SpanClassQueryWrapper.java
@@ -3,11 +3,8 @@
import org.apache.lucene.search.spans.SpanQuery;
import de.ids_mannheim.korap.query.SpanClassQuery;
-import de.ids_mannheim.korap.query.wrap.SpanQueryWrapper;
import de.ids_mannheim.korap.util.QueryException;
-import java.util.*;
-
// TODO: If this.subquery.isNegative(), it may be an Expansion!
// SpanExpansionQuery(x, y.negative, min, max. direction???, classNumber, true)
@@ -20,6 +17,8 @@
this.number = number;
if (number != (byte) 0)
this.hasClass = true;
+ this.min = subquery.min;
+ this.max=subquery.max;
};
public SpanClassQueryWrapper (SpanQueryWrapper subquery, short number) {
diff --git a/src/main/java/de/ids_mannheim/korap/query/wrap/SpanQueryWrapper.java b/src/main/java/de/ids_mannheim/korap/query/wrap/SpanQueryWrapper.java
index 0c50ae8..06f71aa 100644
--- a/src/main/java/de/ids_mannheim/korap/query/wrap/SpanQueryWrapper.java
+++ b/src/main/java/de/ids_mannheim/korap/query/wrap/SpanQueryWrapper.java
@@ -1,6 +1,7 @@
package de.ids_mannheim.korap.query.wrap;
import org.apache.lucene.search.spans.SpanQuery;
+
import de.ids_mannheim.korap.util.QueryException;
// TODO: Add warnings and errors - using KorapQuery
@@ -187,7 +188,7 @@
public boolean maybeAnchor () {
if (this.isNegative()) return false;
if (this.isOptional()) return false;
- if (this.isEmpty()) return false;
+ if (this.isEmpty()) { return false;}
return true;
};
diff --git a/src/main/java/de/ids_mannheim/korap/query/wrap/SpanSequenceQueryWrapper.java b/src/main/java/de/ids_mannheim/korap/query/wrap/SpanSequenceQueryWrapper.java
index b13d6bc..d7f03f5 100644
--- a/src/main/java/de/ids_mannheim/korap/query/wrap/SpanSequenceQueryWrapper.java
+++ b/src/main/java/de/ids_mannheim/korap/query/wrap/SpanSequenceQueryWrapper.java
@@ -1,17 +1,21 @@
package de.ids_mannheim.korap.query.wrap;
-import java.util.*;
-import de.ids_mannheim.korap.query.*;
-import de.ids_mannheim.korap.query.wrap.*;
-
-import de.ids_mannheim.korap.util.QueryException;
+import java.util.ArrayList;
import org.apache.lucene.index.Term;
-import org.apache.lucene.search.spans.*;
-
+import org.apache.lucene.search.spans.SpanQuery;
+import org.apache.lucene.search.spans.SpanTermQuery;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import de.ids_mannheim.korap.query.DistanceConstraint;
+import de.ids_mannheim.korap.query.SpanDistanceQuery;
+import de.ids_mannheim.korap.query.SpanElementQuery;
+import de.ids_mannheim.korap.query.SpanExpansionQuery;
+import de.ids_mannheim.korap.query.SpanMultipleDistanceQuery;
+import de.ids_mannheim.korap.query.SpanNextQuery;
+import de.ids_mannheim.korap.util.QueryException;
+
/*
TODO: Make isNegative work!
TODO: Make isEmpty work!
@@ -180,11 +184,16 @@
* @param query A new {@link SpanQueryWrapper} to search for.
* @return The {@link SpanSequenceQueryWrapper} object for chaining.
*/
- public SpanSequenceQueryWrapper append (SpanQueryWrapper ssq) {
+ public SpanSequenceQueryWrapper append(SpanQueryWrapper ssq) {
// The wrapper is null - ignore this in the sequence
if (ssq.isNull())
return this;
+ if (ssq.isEmpty) {
+ this.isEmpty = true;
+ this.min = ssq.min;
+ this.max = ssq.max;
+ }
// As the spanQueryWrapper is not null,
// the sequence can't be null as well
diff --git a/src/main/java/de/ids_mannheim/korap/query/wrap/SpanSubspanQueryWrapper.java b/src/main/java/de/ids_mannheim/korap/query/wrap/SpanSubspanQueryWrapper.java
index 25cf0d6..f4b9f39 100644
--- a/src/main/java/de/ids_mannheim/korap/query/wrap/SpanSubspanQueryWrapper.java
+++ b/src/main/java/de/ids_mannheim/korap/query/wrap/SpanSubspanQueryWrapper.java
@@ -9,122 +9,128 @@
import de.ids_mannheim.korap.util.QueryException;
/**
+ * Automatically handle the length parameter if it is less than 0, then no
+ * SpanSubspanQuery is created, but a SpanQuery for the subquery ?
+ *
* @author margaretha, diewald
*
*/
public class SpanSubspanQueryWrapper extends SpanQueryWrapper {
- private SpanQueryWrapper subquery;
- private int startOffset, length;
+ private SpanQueryWrapper subquery;
+ private int startOffset, length;
- private final static Logger log =
- LoggerFactory.getLogger(SpanSubspanQueryWrapper.class);
+ private final static Logger log = LoggerFactory
+ .getLogger(SpanSubspanQueryWrapper.class);
- // This advices the java compiler to ignore all loggings
- public static final boolean DEBUG = false;
+ // This advices the java compiler to ignore all loggings
+ public static final boolean DEBUG = false;
- public SpanSubspanQueryWrapper(SpanQueryWrapper sqw,
- int startOffset,
- int length) {
- this.subquery = sqw;
- if (sqw == null) {
- this.isNull = true;
- return;
- }
- else {
- this.isNull = false;
- };
+ public SpanSubspanQueryWrapper(SpanQueryWrapper sqw, int startOffset,
+ int length) throws QueryException {
+ if (length < 0) {
+ throw new QueryException(
+ "SpanSubspanQuery cannot have length less than 0.");
+ }
- this.startOffset = startOffset;
- this.length = length;
+ this.subquery = sqw;
+ if (subquery != null) {
+ this.isNull = false;
+ } else
+ return;
- // The embedded class is empty,
- // but probably in a valid range
- // - optimize
- // subspan([]{,5}, 2) -> subspan([]{2,5}, 2)
- // subspan([]{2,}, 2,5) -> subspan([]{2,5}, 2,5)
- if (subquery.isEmpty()) {
+ this.startOffset = startOffset;
+ this.length = length;
- // Todo: Is there a possible way to deal with that?
- if (startOffset < 0) {
- this.isNull = true;
- return;
- };
+ if (subquery.isEmpty()) {
+ handleEmptySubquery();
+ } else if (subquery.isNegative) {
+ handleNegativeSubquery();
+ }
+ }
- // e.g, subspan([]{0,6}, 8)
- if (subquery.getMax() < startOffset) {
- this.isNull = true;
- return;
- };
+ private void handleNegativeSubquery() {
+ this.isNegative = true;
+ if (startOffset < 0) {
+ int max = Math.abs(startOffset) + length;
+ subquery.setMax(max);
+ startOffset = max + startOffset;
+ } else {
+ subquery.setMax(startOffset + length);
+ }
+ subquery.setMin(startOffset);
+ subquery.isOptional = false;
- // Readjust the minimum of the subquery
- if (startOffset > 0) {
- subquery.setMin(startOffset);
- subquery.isOptional = false;
- };
+ setMax(subquery.max);
+ setMin(subquery.min);
+ }
- // Readjust the maximum,
- // although the following case may be somehow disputable:
- // subspan([]{2,8}, 2, 1) -> subspan([]{2,5},2,1)
- if (length > 0) {
- int newMax = subquery.getMin() + startOffset + length;
- if (subquery.getMax() > newMax) {
- subquery.setMax(subquery.getMin() + length);
- };
- };
- };
+ private void handleEmptySubquery() {
+ if (subquery instanceof SpanRepetitionQueryWrapper) {
+ this.isEmpty = true;
+ }
+ // subspan([]{,5}, 2) -> subspan([]{2,5}, 2)
+ // e.g. subspan([]{0,6}, 8)
+ if (startOffset >= subquery.getMax()) {
+ this.isNull = true;
+ return;
+ }
+ if (startOffset < 0) {
+ startOffset = subquery.getMax() + startOffset;
+ }
+ subquery.isOptional = false;
+ subquery.setMin(startOffset);
- // Todo: What happens with negative queries?
- // submatch([base!=tree],3)
- }
+ // subspan([]{2,}, 2,5) -> subspan([]{2,5}, 2,5)
+ int endOffset = startOffset + length;
+ if (length == 0) {
+ length = subquery.getMax() - startOffset;
+ }
+ else if (subquery.getMax() > endOffset || subquery.getMax() == 0) {
+ subquery.setMax(endOffset);
+ }
+ else if (subquery.getMax() < endOffset) {
+ length = subquery.max - subquery.min;
+ }
- @Override
- public SpanQuery toQuery() throws QueryException {
+ // System.out.println(subquery.min + "," + subquery.max);
+ setMax(subquery.max);
+ setMin(subquery.min);
+ }
- if (this.isNull() || subquery.isNull()) {
- if (DEBUG)
- log.warn("Subquery of SpanSubspanquery is null.");
- return null;
- };
+ @Override
+ public SpanQuery toQuery() throws QueryException {
- if (startOffset == 0 && length == 0) {
- if (DEBUG)
- log.warn("Not SpanSubspanQuery. Creating only the subquery.");
- return subquery.toQuery();
- };
+ if (this.isNull()) {
+ // if (DEBUG) log.warn("Subquery of SpanSubspanquery is null.");
+ return null;
+ }
- // The embedded subquery may be null
- SpanQuery sq = subquery.toQuery();
- if (sq == null) return null;
-
- if (sq instanceof SpanTermQuery) {
+ SpanQuery sq = subquery.toQuery();
+ if (sq == null)
+ return null;
+ if (sq instanceof SpanTermQuery) {
+ if ((startOffset == 0 || startOffset == -1) &&
+ (length == 1 || length == 0)) {
+ // if (DEBUG) log.warn("Not SpanSubspanQuery. " +
+ // "Creating only a SpanQuery for the subquery.");
+ return sq;
+ }
+ return null;
+ }
- // No relevant subspan
- if ((startOffset == 0 || startOffset == -1) &&
- (length <= 1)) {
- if (DEBUG)
- log.warn("Not SpanSubspanQuery. " +
- "Creating only the subquery.");
- return sq;
- };
+ return new SpanSubspanQuery(sq, startOffset, length, true);
+ }
- // Subspanquery can't match (always out of scope)
- return null;
- }
+ @Override
+ public boolean isNegative() {
+ return this.subquery.isNegative();
+ };
- return new SpanSubspanQuery(sq, startOffset, length,
- true);
- }
-
- @Override
- public boolean isNegative () {
- return this.subquery.isNegative();
- };
-
- @Override
- public boolean isOptional () {
- if (startOffset > 0)
- return false;
- return this.subquery.isOptional();
- };
+ @Override
+ public boolean isOptional() {
+ if (startOffset > 0)
+ return false;
+ return this.subquery.isOptional();
+ };
}
diff --git a/src/test/java/de/ids_mannheim/korap/query/TestSpanSequenceQueryJSON.java b/src/test/java/de/ids_mannheim/korap/query/TestSpanSequenceQueryJSON.java
index fd3f239..e1378fe 100644
--- a/src/test/java/de/ids_mannheim/korap/query/TestSpanSequenceQueryJSON.java
+++ b/src/test/java/de/ids_mannheim/korap/query/TestSpanSequenceQueryJSON.java
@@ -1,19 +1,18 @@
package de.ids_mannheim.korap.query;
-import java.util.*;
-import java.io.*;
+import static de.ids_mannheim.korap.TestSimple.getJSONQuery;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
-import static de.ids_mannheim.korap.TestSimple.*;
+import org.apache.lucene.search.spans.SpanQuery;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
import de.ids_mannheim.korap.query.wrap.SpanQueryWrapper;
import de.ids_mannheim.korap.util.QueryException;
-import static org.junit.Assert.*;
-import org.junit.Test;
-import org.junit.Ignore;
-import org.junit.runner.RunWith;
-import org.junit.runners.JUnit4;
-
/**
* @author diewald
*/
@@ -192,7 +191,8 @@
public void queryJSONseqNegativeEndClass () throws QueryException {
SpanQueryWrapper sqwi = jsonQueryFile("negative-last-class.jsonld");
// [tt/p=NN]{2:[tt/p!=NN]}
- assertEquals(sqwi.toQuery().toString(), "spanExpansion(tokens:tt/p:NN, !tokens:tt/p:NN{1, 1}, right, class:2)");
+ SpanQuery sq = sqwi.toQuery();
+ assertEquals(sq.toString(), "spanExpansion(tokens:tt/p:NN, !tokens:tt/p:NN{1, 1}, right, class:2)");
};
@Test
diff --git a/src/test/java/de/ids_mannheim/korap/query/TestSpanSubspanQueryJSON.java b/src/test/java/de/ids_mannheim/korap/query/TestSpanSubspanQueryJSON.java
index 1fd3093..444ec79 100644
--- a/src/test/java/de/ids_mannheim/korap/query/TestSpanSubspanQueryJSON.java
+++ b/src/test/java/de/ids_mannheim/korap/query/TestSpanSubspanQueryJSON.java
@@ -13,71 +13,206 @@
* @author margaretha, diewald
*/
public class TestSpanSubspanQueryJSON {
+
+ @Test
+ public void testTermQuery() throws QueryException {
+ // subspan(tokens:tt/l:Haus, 0, 1)
+ String filepath = getClass().getResource(
+ "/queries/submatch/termquery.jsonld").getFile();
+ SpanQueryWrapper sqwi = getJSONQuery(filepath);
+ SpanQuery sq = sqwi.toQuery();
+ assertEquals("tokens:tt/l:Haus", sq.toString());
+ }
- @Test
- public void testCase1() throws QueryException {
- String filepath = getClass().getResource("/queries/submatch/1.jsonld")
- .getFile();
- SpanQueryWrapper sqwi = getJSONQuery(filepath);
- SpanQuery sq = sqwi.toQuery();
- assertEquals(sq.toString(),
- "subspan(spanContain(<tokens:s />, tokens:tt/l:Haus),1,4)");
- }
+ @Test
+ public void testTermStartOffset() throws QueryException {
+ // subspan(tokens:tt/l:Haus, -1, 0)
+ String filepath = getClass().getResource(
+ "/queries/submatch/term-start-offset.jsonld").getFile();
+ SpanQueryWrapper sqwi = getJSONQuery(filepath);
+ SpanQuery sq = sqwi.toQuery();
+ assertEquals("tokens:tt/l:Haus", sq.toString());
+ }
- @Test
- public void testCase2() throws QueryException {
- String filepath = getClass().getResource("/queries/submatch/2.jsonld")
- .getFile();
- SpanQueryWrapper sqwi = getJSONQuery(filepath);
- SpanQuery sq = sqwi.toQuery();
- assertEquals(sq.toString(), "subspan(<tokens:s />,1,4)");
- }
+ @Test
+ public void testTermNull() throws QueryException {
+ // subspan(tokens:tt/l:Haus, 1, 1)
+ String filepath = getClass().getResource(
+ "/queries/submatch/term-null.jsonld").getFile();
+ SpanQueryWrapper sqwi = getJSONQuery(filepath);
+ SpanQuery sq = sqwi.toQuery();
+ assertEquals(null, sq);
+ }
- @Test
- public void testCase3() throws QueryException {
- String filepath = getClass().getResource("/queries/submatch/3.jsonld")
- .getFile();
- SpanQueryWrapper sqwi = getJSONQuery(filepath);
- SpanQuery sq = sqwi.toQuery();
- assertEquals(sq.toString(), "subspan(<tokens:s />,1,0)");
- }
+ @Test
+ public void testElementQuery() throws QueryException {
+ // submatch(1,4:<s>)
+ String filepath = getClass().getResource(
+ "/queries/submatch/simpleElement.jsonld").getFile();
+ SpanQueryWrapper sqwi = getJSONQuery(filepath);
+ SpanQuery sq = sqwi.toQuery();
+ assertEquals(
+ "subspan(spanContain(<tokens:s />, tokens:tt/l:Haus), 1, 4)",
+ sq.toString());
+ }
- @Test
- public void testCaseWrapped() throws QueryException {
- String filepath = getClass().getResource("/queries/submatch/wrapped.jsonld")
- .getFile();
- SpanQueryWrapper sqwi = getJSONQuery(filepath);
- SpanQuery sq = sqwi.toQuery();
- assertEquals(sq.toString(), "focus(129: spanElementDistance({129: tokens:s:der},"+
- " {129: subspan(<tokens:s />,0,1)}, [(s[0:0], ordered, notExcluded)]))");
- }
+ @Test
+ public void testNoLength() throws QueryException {
+ // submatch(1,:<s>)
+ String filepath = getClass().getResource(
+ "/queries/submatch/noLength.jsonld").getFile();
+ SpanQueryWrapper sqwi = getJSONQuery(filepath);
+ SpanQuery sq = sqwi.toQuery();
+ assertEquals("subspan(<tokens:s />, 1, 0)", sq.toString());
+ }
+
+ @Test
+ public void testMinusStartOffset() throws QueryException {
+ // submatch(-1,4:<s>)
+ String filepath = getClass().getResource("/queries/submatch/minusStart.jsonld")
+ .getFile();
+ SpanQueryWrapper sqwi = getJSONQuery(filepath);
+ SpanQuery sq = sqwi.toQuery();
+ assertEquals("subspan(<tokens:s />, -1, 4)", sq.toString());
+ }
+
+ @Test
+ public void testEmptyMinusStartOffset() throws QueryException {
+ // subspan(spanExpansion(tokens:s:der, []{1, 8}, right),-1,4)
+ // need a spanExpansionQueryWrapper to adjust min, max
+ String filepath = getClass().getResource(
+ "/queries/submatch/empty-minusStart.jsonld").getFile();
+ SpanQueryWrapper sqwi = getJSONQuery(filepath);
+ SpanQuery sq = sqwi.toQuery();
+ // subspan(spanExpansion(tokens:s:der, []{7, 8}, right),7,1)
+ assertEquals(
+ "subspan(spanExpansion(tokens:s:der, []{1, 8}, right), 7, 1)",
+ sq.toString());
+ }
+
+ @Test
+ public void testEmptyMax() throws QueryException {
+ // need a spanExpansionQueryWrapper to adjust min, max
+ String filepath = getClass().getResource(
+ "/queries/submatch/empty-max.jsonld").getFile();
+ SpanQueryWrapper sqwi = getJSONQuery(filepath);
+ SpanQuery sq = sqwi.toQuery();
+ assertEquals(3, sqwi.getMax());
+ // subspan(spanExpansion(tokens:s:der, []{1, 3}, right),1,2)
+ assertEquals(
+ "subspan(spanExpansion(tokens:s:der, []{1, 8}, right), 1, 2)",
+ sq.toString());
+ }
+
+ @Test
+ public void testCaseEmptyWrapped() throws QueryException {
+ String filepath = getClass().getResource(
+ "/queries/submatch/wrapped.jsonld").getFile();
+ SpanQueryWrapper sqwi = getJSONQuery(filepath);
+ SpanQuery sq = sqwi.toQuery();
+ assertEquals(
+ "focus(129: spanElementDistance({129: tokens:s:der}, {129: subspan"
+ + "(<tokens:s />, 0, 1)}, [(s[0:0], ordered, notExcluded)]))",
+ sq.toString());
+ }
+
+ @Test
+ public void testCaseEmptyEmbedded() throws QueryException {
+ // die subspan(der []{1,}, 2,3)
+ String filepath = getClass().getResource(
+ "/queries/submatch/embedded.jsonld").getFile();
+ SpanQueryWrapper sqwi = getJSONQuery(filepath);
+ SpanQuery sq = sqwi.toQuery();
+ assertEquals("spanNext({1: tokens:s:die}, {1: subspan(spanExpansion("
+ + "tokens:s:der, []{1, 100}, right), 2, 3)})", sq.toString());
+ }
+
+ @Test
+ public void testCaseEmptyEmbeddedNull() throws QueryException {
+ // die subspan([],5,6)
+ // start offset is bigger than the original span
+ String filepath = getClass().getResource(
+ "/queries/submatch/embedded-null.jsonld").getFile();
+ SpanQueryWrapper sqwi = getJSONQuery(filepath);
+ SpanQuery sq = sqwi.toQuery();
+ assertEquals("tokens:s:die", sq.toString());
+ }
+
+ @Test
+ public void testCaseEmptyEmbeddedValid() throws QueryException {
+ // die subspan([]{0,5},2)
+ String filepath = getClass().getResource(
+ "/queries/submatch/embedded-valid-empty.jsonld").getFile();
+ SpanQueryWrapper sqwi = getJSONQuery(filepath);
+ SpanQuery sq = sqwi.toQuery();
+ assertEquals("spanExpansion(tokens:s:die, []{2, 5}, right)",
+ sq.toString());
+ }
+
+ @Test
+ public void testNegativeEmbeddedSequence() throws QueryException {
+ // submatch(1,1:das [base != Baum])
+ String filepath = getClass().getResource(
+ "/queries/submatch/embedded-negative-seq.jsonld").getFile();
+ SpanQueryWrapper sqwi = getJSONQuery(filepath);
+ SpanQuery sq = sqwi.toQuery();
+ assertEquals(
+ "subspan(spanExpansion(tokens:s:das, !tokens:l:Baum{1, 1}, right), 1, 1)",
+ sq.toString());
+ }
+
+ @Test
+ public void testNegativeSequence() throws QueryException {
+ // das submatch(0,1:[base != Baum])
+ String filepath = getClass().getResource(
+ "/queries/submatch/negative-seq.jsonld").getFile();
+ SpanQueryWrapper sqwi = getJSONQuery(filepath);
+ SpanQuery sq = sqwi.toQuery();
+ assertEquals(
+ "spanExpansion(tokens:s:das, !tokens:l:Baum{1, 1}, right)",
+ sq.toString());
+ }
+
+ @Test
+ public void testNegativeToken() throws QueryException {
+ // submatch(0,1:[base != Baum])
+ String filepath = getClass().getResource(
+ "/queries/submatch/negative-token.jsonld").getFile();
+ SpanQueryWrapper sqwi = getJSONQuery(filepath);
+ SpanQuery sq = sqwi.toQuery();
+ assertEquals("tokens:l:Baum", sq.toString());
+ }
+
+ @Test
+ public void testEmbeddedNegativeRepetition() throws QueryException {
+ // submatch(1,1:das [base != Baum]{1,3})
+ // need a spanExpansionQueryWrapper to handle a null notquery and
+ // a repetition of a negative query
+ String filepath = getClass().getResource(
+ "/queries/submatch/embedded-negative-repetition.jsonld")
+ .getFile();
+ SpanQueryWrapper sqwi = getJSONQuery(filepath);
+ SpanQuery sq = sqwi.toQuery();
+ assertEquals(
+ "subspan(spanExpansion(tokens:s:das, !tokens:l:Baum{1, 3}, right), 1, 1)",
+ sq.toString());
+ }
+
+ @Test
+ public void testNegativeRepetition() throws QueryException {
+ // das submatch(1,1:[base != Baum]{1,3})
+ // need a spanExpansionQueryWrapper to handle a null notquery and
+ // a repetition of a negative query
+ String filepath = getClass().getResource(
+ "/queries/submatch/negative-repetition.jsonld")
+ .getFile();
+ SpanQueryWrapper sqwi = getJSONQuery(filepath);
+ SpanQuery sq = sqwi.toQuery();
+ assertEquals(
+ "spanExpansion(tokens:s:das, !tokens:l:Baum{1, 1}, right)",
+ sq.toString());
+ }
- @Test
- public void testCaseEmbedded() throws QueryException {
- String filepath = getClass().getResource("/queries/submatch/embedded.jsonld")
- .getFile();
- SpanQueryWrapper sqwi = getJSONQuery(filepath);
- SpanQuery sq = sqwi.toQuery();
- assertEquals(sq.toString(), "spanNext({1: tokens:s:die},"+
- " {1: subspan(spanExpansion(tokens:s:der, []{1, 100}, right),2,3)})");
- }
- @Test
- public void testCaseEmbeddedNull() throws QueryException {
- String filepath = getClass().getResource("/queries/submatch/embedded-null.jsonld")
- .getFile();
- SpanQueryWrapper sqwi = getJSONQuery(filepath);
- SpanQuery sq = sqwi.toQuery();
- assertEquals(sq.toString(), "tokens:s:die");
- }
-
- @Test
- public void testCaseEmbeddedValidEmpty() throws QueryException {
- String filepath = getClass().getResource("/queries/submatch/embedded-valid-empty.jsonld")
- .getFile();
- SpanQueryWrapper sqwi = getJSONQuery(filepath);
- SpanQuery sq = sqwi.toQuery();
- assertEquals(sq.toString(), "??? (Known issue)");
- }
}
diff --git a/src/test/resources/queries/submatch/embedded-negative-repetition.jsonld b/src/test/resources/queries/submatch/embedded-negative-repetition.jsonld
new file mode 100644
index 0000000..29f676d
--- /dev/null
+++ b/src/test/resources/queries/submatch/embedded-negative-repetition.jsonld
@@ -0,0 +1,49 @@
+{
+ "@context": "http://ids-mannheim.de/ns/KorAP/json-ld/v0.2/context.jsonld",
+ "errors": [],
+ "warnings": [],
+ "messages": [],
+ "collection": {},
+ "query": {
+ "@type": "korap:reference",
+ "operation": "operation:focus",
+ "operands": [{
+ "@type": "korap:group",
+ "operation": "operation:sequence",
+ "operands": [
+ {
+ "@type": "korap:token",
+ "wrap": {
+ "@type": "korap:term",
+ "layer": "orth",
+ "key": "das",
+ "match": "match:eq"
+ }
+ },
+ {
+ "@type": "korap:group",
+ "operation": "operation:repetition",
+ "operands": [{
+ "@type": "korap:token",
+ "wrap": {
+ "@type": "korap:term",
+ "layer": "lemma",
+ "key": "Baum",
+ "match": "match:ne"
+ }
+ }],
+ "boundary": {
+ "@type": "korap:boundary",
+ "min": 1,
+ "max": 3
+ }
+ }
+ ]
+ }],
+ "spanRef": [
+ 1,
+ 1
+ ]
+ },
+ "meta": {}
+}
diff --git a/src/test/resources/queries/submatch/embedded-negative-seq.jsonld b/src/test/resources/queries/submatch/embedded-negative-seq.jsonld
new file mode 100644
index 0000000..5f9b1a8
--- /dev/null
+++ b/src/test/resources/queries/submatch/embedded-negative-seq.jsonld
@@ -0,0 +1,40 @@
+{
+ "@context": "http://ids-mannheim.de/ns/KorAP/json-ld/v0.2/context.jsonld",
+ "errors": [],
+ "warnings": [],
+ "messages": [],
+ "collection": {},
+ "query": {
+ "@type": "korap:reference",
+ "operation": "operation:focus",
+ "operands": [{
+ "@type": "korap:group",
+ "operation": "operation:sequence",
+ "operands": [
+ {
+ "@type": "korap:token",
+ "wrap": {
+ "@type": "korap:term",
+ "layer": "orth",
+ "key": "das",
+ "match": "match:eq"
+ }
+ },
+ {
+ "@type": "korap:token",
+ "wrap": {
+ "@type": "korap:term",
+ "layer": "lemma",
+ "key": "Baum",
+ "match": "match:ne"
+ }
+ }
+ ]
+ }],
+ "spanRef": [
+ 1,
+ 1
+ ]
+ },
+ "meta": {}
+}
diff --git a/src/test/resources/queries/submatch/empty-max.jsonld b/src/test/resources/queries/submatch/empty-max.jsonld
new file mode 100644
index 0000000..dc4d321
--- /dev/null
+++ b/src/test/resources/queries/submatch/empty-max.jsonld
@@ -0,0 +1,56 @@
+{
+ "@context":"http://ids-mannheim.de/ns/KorAP/json-ld/v0.2/context.jsonld",
+ "errors":[
+
+ ],
+ "warnings":[
+
+ ],
+ "messages":[
+
+ ],
+ "collection":null,
+ "query":{
+ "@type" : "korap:reference",
+ "operands" : [
+ {
+ "@type" : "korap:group",
+ "operands" : [
+ {
+ "@type" : "korap:token",
+ "wrap" : {
+ "@type" : "korap:term",
+ "foundry" : "opennlp",
+ "key" : "der",
+ "layer" : "orth",
+ "match" : "match:eq"
+ }
+ },
+ {
+ "@type" : "korap:group",
+ "boundary" : {
+ "@type" : "korap:boundary",
+ "min" : 1,
+ "max" : 8
+ },
+ "operands" : [
+ {
+ "@type" : "korap:token"
+ }
+ ],
+ "operation" : "operation:repetition"
+ }
+ ],
+ "operation" : "operation:sequence"
+ }
+ ],
+ "operation" : "operation:focus",
+ "spanRef" : [
+ 1,
+ 2
+ ]
+ },
+ "meta":{
+
+ }
+}
diff --git a/src/test/resources/queries/submatch/empty-minusStart.jsonld b/src/test/resources/queries/submatch/empty-minusStart.jsonld
new file mode 100644
index 0000000..3efaeba
--- /dev/null
+++ b/src/test/resources/queries/submatch/empty-minusStart.jsonld
@@ -0,0 +1,56 @@
+{
+ "@context":"http://ids-mannheim.de/ns/KorAP/json-ld/v0.2/context.jsonld",
+ "errors":[
+
+ ],
+ "warnings":[
+
+ ],
+ "messages":[
+
+ ],
+ "collection":null,
+ "query":{
+ "@type" : "korap:reference",
+ "operands" : [
+ {
+ "@type" : "korap:group",
+ "operands" : [
+ {
+ "@type" : "korap:token",
+ "wrap" : {
+ "@type" : "korap:term",
+ "foundry" : "opennlp",
+ "key" : "der",
+ "layer" : "orth",
+ "match" : "match:eq"
+ }
+ },
+ {
+ "@type" : "korap:group",
+ "boundary" : {
+ "@type" : "korap:boundary",
+ "min" : 1,
+ "max" : 8
+ },
+ "operands" : [
+ {
+ "@type" : "korap:token"
+ }
+ ],
+ "operation" : "operation:repetition"
+ }
+ ],
+ "operation" : "operation:sequence"
+ }
+ ],
+ "operation" : "operation:focus",
+ "spanRef" : [
+ -1,
+ 4
+ ]
+ },
+ "meta":{
+
+ }
+}
diff --git a/src/test/resources/queries/submatch/2.jsonld b/src/test/resources/queries/submatch/minusStart.jsonld
similarity index 96%
rename from src/test/resources/queries/submatch/2.jsonld
rename to src/test/resources/queries/submatch/minusStart.jsonld
index 45a3155..38c593a 100644
--- a/src/test/resources/queries/submatch/2.jsonld
+++ b/src/test/resources/queries/submatch/minusStart.jsonld
@@ -20,7 +20,7 @@
],
"operation" : "operation:focus",
"spanRef" : [
- 1,
+ -1,
4
]
},
diff --git a/src/test/resources/queries/submatch/negative-repetition.jsonld b/src/test/resources/queries/submatch/negative-repetition.jsonld
new file mode 100644
index 0000000..88cf217
--- /dev/null
+++ b/src/test/resources/queries/submatch/negative-repetition.jsonld
@@ -0,0 +1,49 @@
+{
+ "@context": "http://ids-mannheim.de/ns/KorAP/json-ld/v0.2/context.jsonld",
+ "errors": [],
+ "warnings": [],
+ "messages": [],
+ "collection": {},
+ "query": {
+ "@type": "korap:group",
+ "operation": "operation:sequence",
+ "operands": [
+ {
+ "@type": "korap:token",
+ "wrap": {
+ "@type": "korap:term",
+ "layer": "orth",
+ "key": "das",
+ "match": "match:eq"
+ }
+ },
+ {
+ "@type": "korap:reference",
+ "operation": "operation:focus",
+ "operands": [{
+ "@type": "korap:group",
+ "operation": "operation:repetition",
+ "operands": [{
+ "@type": "korap:token",
+ "wrap": {
+ "@type": "korap:term",
+ "layer": "lemma",
+ "key": "Baum",
+ "match": "match:ne"
+ }
+ }],
+ "boundary": {
+ "@type": "korap:boundary",
+ "min": 1,
+ "max": 3
+ }
+ }],
+ "spanRef": [
+ 1,
+ 1
+ ]
+ }
+ ]
+ },
+ "meta": {}
+}
diff --git a/src/test/resources/queries/submatch/negative-seq.jsonld b/src/test/resources/queries/submatch/negative-seq.jsonld
new file mode 100644
index 0000000..0d4a733
--- /dev/null
+++ b/src/test/resources/queries/submatch/negative-seq.jsonld
@@ -0,0 +1,40 @@
+{
+ "@context": "http://ids-mannheim.de/ns/KorAP/json-ld/v0.2/context.jsonld",
+ "errors": [],
+ "warnings": [],
+ "messages": [],
+ "collection": {},
+ "query": {
+ "@type": "korap:group",
+ "operation": "operation:sequence",
+ "operands": [
+ {
+ "@type": "korap:token",
+ "wrap": {
+ "@type": "korap:term",
+ "layer": "orth",
+ "key": "das",
+ "match": "match:eq"
+ }
+ },
+ {
+ "@type": "korap:reference",
+ "operation": "operation:focus",
+ "operands": [{
+ "@type": "korap:token",
+ "wrap": {
+ "@type": "korap:term",
+ "layer": "lemma",
+ "key": "Baum",
+ "match": "match:ne"
+ }
+ }],
+ "spanRef": [
+ 0,
+ 1
+ ]
+ }
+ ]
+ },
+ "meta": {}
+}
diff --git a/src/test/resources/queries/submatch/negative-token.jsonld b/src/test/resources/queries/submatch/negative-token.jsonld
new file mode 100644
index 0000000..c4c0408
--- /dev/null
+++ b/src/test/resources/queries/submatch/negative-token.jsonld
@@ -0,0 +1,25 @@
+{
+ "@context": "http://ids-mannheim.de/ns/KorAP/json-ld/v0.2/context.jsonld",
+ "errors": [],
+ "warnings": [],
+ "messages": [],
+ "collection": {},
+ "query": {
+ "@type": "korap:reference",
+ "operation": "operation:focus",
+ "operands": [{
+ "@type": "korap:token",
+ "wrap": {
+ "@type": "korap:term",
+ "layer": "lemma",
+ "key": "Baum",
+ "match": "match:ne"
+ }
+ }],
+ "spanRef": [
+ 0,
+ 1
+ ]
+ },
+ "meta": {}
+}
diff --git a/src/test/resources/queries/submatch/3.jsonld b/src/test/resources/queries/submatch/noLength.jsonld
similarity index 100%
rename from src/test/resources/queries/submatch/3.jsonld
rename to src/test/resources/queries/submatch/noLength.jsonld
diff --git a/src/test/resources/queries/submatch/1.jsonld b/src/test/resources/queries/submatch/simpleElement.jsonld
similarity index 100%
rename from src/test/resources/queries/submatch/1.jsonld
rename to src/test/resources/queries/submatch/simpleElement.jsonld
diff --git a/src/test/resources/queries/submatch/term-null.jsonld b/src/test/resources/queries/submatch/term-null.jsonld
new file mode 100644
index 0000000..71b0b11
--- /dev/null
+++ b/src/test/resources/queries/submatch/term-null.jsonld
@@ -0,0 +1,36 @@
+{
+ "@context":"http://ids-mannheim.de/ns/KorAP/json-ld/v0.2/context.jsonld",
+ "errors":[
+
+ ],
+ "warnings":[
+
+ ],
+ "messages":[
+
+ ],
+ "collection":null,
+ "query":{
+ "@type" : "korap:reference",
+ "operands" : [
+ {
+ "@type" : "korap:token",
+ "wrap" : {
+ "@type" : "korap:term",
+ "foundry" : "tt",
+ "key" : "Haus",
+ "layer" : "lemma",
+ "match" : "match:eq"
+ }
+ }
+ ],
+ "operation" : "operation:focus",
+ "spanRef" : [
+ 1,
+ 1
+ ]
+ },
+ "meta":{
+
+ }
+}
diff --git a/src/test/resources/queries/submatch/term-start-offset.jsonld b/src/test/resources/queries/submatch/term-start-offset.jsonld
new file mode 100644
index 0000000..84a6395
--- /dev/null
+++ b/src/test/resources/queries/submatch/term-start-offset.jsonld
@@ -0,0 +1,35 @@
+{
+ "@context":"http://ids-mannheim.de/ns/KorAP/json-ld/v0.2/context.jsonld",
+ "errors":[
+
+ ],
+ "warnings":[
+
+ ],
+ "messages":[
+
+ ],
+ "collection":null,
+ "query":{
+ "@type" : "korap:reference",
+ "operands" : [
+ {
+ "@type" : "korap:token",
+ "wrap" : {
+ "@type" : "korap:term",
+ "foundry" : "tt",
+ "key" : "Haus",
+ "layer" : "lemma",
+ "match" : "match:eq"
+ }
+ }
+ ],
+ "operation" : "operation:focus",
+ "spanRef" : [
+ -1
+ ]
+ },
+ "meta":{
+
+ }
+}
diff --git a/src/test/resources/queries/submatch/termquery.jsonld b/src/test/resources/queries/submatch/termquery.jsonld
new file mode 100644
index 0000000..7a831b1
--- /dev/null
+++ b/src/test/resources/queries/submatch/termquery.jsonld
@@ -0,0 +1,36 @@
+{
+ "@context":"http://ids-mannheim.de/ns/KorAP/json-ld/v0.2/context.jsonld",
+ "errors":[
+
+ ],
+ "warnings":[
+
+ ],
+ "messages":[
+
+ ],
+ "collection":null,
+ "query":{
+ "@type" : "korap:reference",
+ "operands" : [
+ {
+ "@type" : "korap:token",
+ "wrap" : {
+ "@type" : "korap:term",
+ "foundry" : "tt",
+ "key" : "Haus",
+ "layer" : "lemma",
+ "match" : "match:eq"
+ }
+ }
+ ],
+ "operation" : "operation:focus",
+ "spanRef" : [
+ 0,
+ 1
+ ]
+ },
+ "meta":{
+
+ }
+}