Fixed serialization bug for empty classes and added documentation
diff --git a/Changes b/Changes
index 0fc0bb3..247c82a 100644
--- a/Changes
+++ b/Changes
@@ -1,10 +1,12 @@
-0.49.3 2014-01-29
+0.49.3 2014-01-30
- [documentation] Improved documentation for API classes (diewald)
- [documentation] Improved documentation for various queries (margaretha)
- [feature] Added deserialization of SpanSubSpanQueries (margaretha,diewald)
- [bugfix] Null filters are now correctly extended (diewald)
- [cleanup] Refactoring of KorapResult, KorapResponse, KorapQuery,
deprecated operation:or in favor of operation:junction (diewald)
+ - [bugfix] Empty class segments are now correctly serialized
+ in sequences (diewald)
0.49.2 2014-12-05
- [documentation] Improved documentation for various queries (margaretha)
@@ -223,43 +225,43 @@
- Support for distinct Match retrieval (diewald)
0.25.3 2014-01-10
- - Updated Lucene to 4.3.1.
- - Eliza Margaretha has joined the team.
+ - Updated Lucene to 4.3.1 (diewald)
+ - Eliza Margaretha has joined the team
0.25.2 2013-12-29
- - Fixed highlight bug with empty reopened tags.
+ - Fixed highlight bug with empty reopened tags
0.25.1 2013-12-28
- - Support for pubDate collections.
- - Changed versioning scheme.
+ - Support for pubDate collections
+ - Changed versioning scheme
0.25 2013-12-20
- - Support for Wildcard Queries.
- - Improved support for regular expressions.
- - Introduced keyword fields that store no positions.
+ - Support for Wildcard Queries
+ - Improved support for regular expressions
+ - Introduced keyword fields that store no positions
0.24_1 2013-12-05
- This is a pseudo version for demo versions with dirty hacks,
meant to be rolled back!
- - There is now an ignored broken test in TestKorapCollection!
+ - There is now an ignored broken test in TestKorapCollection
0.24 2013-12-05
- - Json deserializer finished for virtual collections.
+ - Json deserializer finished for virtual collections
0.23 2013-12-05
- - Error handling corrected in KorapResult.
- - Json deserializer finished for queries.
+ - Error handling corrected in KorapResult
+ - Json deserializer finished for queries
0.22 2013-12-04
- - Introduced KorapSearch.
- - Json deserializer (started).
+ - Introduced KorapSearch
+ - Json deserializer (started)
0.21 2013-11-28
- Virtual collections can now be defined,
- searched, nested and extended.
+ searched, nested and extended
0.20 2013-11-18
- - Unboxing from sandbox.
+ - Unboxing from sandbox repository
0.11 2013-11-14
- JSON file importer (plain and gzip)
@@ -267,43 +269,43 @@
- [bugfix] Sequence Query
0.10 2013-11-11
- - Added JSON input to indexer.
+ - Added JSON input to indexer
0.09 2013-11-07
- - Reconstruction of all wrapper queries using an interface.
- - Finished KorapQuery (preliminary feature freeze).
+ - Reconstruction of all wrapper queries using an interface
+ - Finished KorapQuery (preliminary feature freeze)
0.08 2013-10-18
- - Changed methods to attributes in KorapMatch.
- - Changed SimpleJSON to Jackson.
- - Fixed Highlighting.
- - Some Refactoring of result classes.
- - Introduced KorapDocument.
- - Introduced KorapHTML utility.
+ - Changed methods to attributes in KorapMatch
+ - Changed SimpleJSON to Jackson
+ - Fixed Highlighting
+ - Some Refactoring of result classes
+ - Introduced KorapDocument
+ - Introduced KorapHTML utility
0.07 2013-10-14
- - Added position to offset matching.
+ - Added position to offset matching
0.06 2013-10-01
- - Added SpanElementQuery for working WithinQeries.
- - Added KorapResult and KorapMatching.
+ - Added SpanElementQuery for working WithinQeries
+ - Added KorapResult and KorapMatching
0.05 2013-09-18
- - Fixed bug in indexing (MultiTermTokenStream).
- - Fixed SpanNext.
- - Added KorapIndex.
- - First draft for KorapFilter.
+ - Fixed bug in indexing (MultiTermTokenStream)
+ - Fixed SpanNext
+ - Added KorapIndex
+ - First draft for KorapFilter
0.04 2013-09-01
- Introduced optimized SpanNext class (not working atm),
- as SpanNear does not always work with nested synonyms.
+ as SpanNear does not always work with nested synonyms
0.03 2013-08-28
- - Added within query (not working atm).
- - Pushed query part to public repo.
+ - Added within query (not working atm)
+ - Pushed query part to public repo
0.02 2013-08-26
- - Added sequences.
+ - Added sequences
0.01 2013-08-20
- - First version as maven build environment.
+ - First version as maven build environment
diff --git a/src/main/java/de/ids_mannheim/korap/query/SpanMultipleDistanceQuery.java b/src/main/java/de/ids_mannheim/korap/query/SpanMultipleDistanceQuery.java
index b8ab9fe..31b8f26 100644
--- a/src/main/java/de/ids_mannheim/korap/query/SpanMultipleDistanceQuery.java
+++ b/src/main/java/de/ids_mannheim/korap/query/SpanMultipleDistanceQuery.java
@@ -122,7 +122,7 @@
sb.append(c.getMaxDistance());
sb.append("], ");
sb.append(c.isOrdered() ? "ordered, " : "notOrdered, ");
- sb.append(c.isExclusion() ? "excluded)]" : "notExcluded)");
+ sb.append(c.isExclusion() ? "excluded)" : "notExcluded)");
if (i < size - 1)
sb.append(", ");
}
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 d276cb1..2f59079 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
@@ -9,28 +9,45 @@
import java.util.*;
+// TODO: If this.subquery.isNegative(), it may be an Expansion!
+// SpanExpansionQuery(x, y.negative, min, max. direction???, classNumber, true)
+
public class SpanClassQueryWrapper extends SpanQueryWrapper {
private SpanQueryWrapper subquery;
- private byte number = (byte) 0;
public SpanClassQueryWrapper (SpanQueryWrapper subquery, byte number) {
- this.subquery = subquery;
- this.number = number;
+ this.subquery = subquery;
+ this.number = number;
+ if (number != (byte) 0)
+ this.hasClass = true;
};
public SpanClassQueryWrapper (SpanQueryWrapper subquery, short number) {
- this.subquery = subquery;
- this.number = (byte) number;
+ this(subquery, (byte) number);
};
public SpanClassQueryWrapper (SpanQueryWrapper subquery, int number) {
- this.subquery = subquery;
- this.number = (byte) number;
+ this(subquery, (byte) number);
};
public SpanClassQueryWrapper (SpanQueryWrapper subquery) {
- this.subquery = subquery;
- this.number = (byte) 0;
+ this(subquery, (byte) 0);
+ };
+
+ public boolean isEmpty () {
+ return this.subquery.isEmpty();
+ };
+
+ public boolean isOptional () {
+ return this.subquery.isOptional();
+ };
+
+ public boolean isNull () {
+ return this.subquery.isNull();
+ };
+
+ public boolean isNegative () {
+ return this.subquery.isNegative();
};
public SpanQuery toQuery () throws QueryException {
@@ -40,25 +57,10 @@
SpanQuery sq = (SpanQuery) this.subquery.toQuery();
if (sq == null) return (SpanQuery) null;
-
- // TODO: If this.subquery.isNegative(), it may be an Expansion!
- // SpanExpansionQuery(x, y.negative, min, max. direction???, classNumber, true)
if (this.number == (byte) 0) {
return new SpanClassQuery(sq);
};
return new SpanClassQuery(sq, (byte) this.number);
};
-
- public boolean isOptional () {
- return this.subquery.isOptional();
- };
-
- public boolean isNull () {
- return this.subquery.isNull();
- };
-
- public boolean isNegative () {
- return this.subquery.isNegative();
- };
};
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 9df3c6d..b13d6bc 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
@@ -13,23 +13,31 @@
import org.slf4j.LoggerFactory;
/*
- TODO:
- Make isNegative work!
- Make isEmpty work!
- Make isExtendedToTheRight work!
+ TODO: Make isNegative work!
+ TODO: Make isEmpty work!
+ TODO: Make isExtendedToTheRight work!
+ TODO: Evaluate if spanNext(spanNext(a,b),spanNext(c,d)) is faster
+ than spanNext(spanNext(spanNext(a,b),c),d)
+ TODO: Improve support for SpanElementQueryWrapper in constraints!
*/
/**
- * Deserialize complexe sequence queries to Lucene SpanQueries.
+ * Deserialize complexe sequence queries to SpanQueries.
+ * This will try to make queries work, that by simple nesting won't
+ * (like queries with empty sequences), and will optimize queries
+ * if possible.
*
- * @author Nils Diewald
- * @version 0.03
+ * Todo: Synopsis
+ *
+ * @author diewald
*/
public class SpanSequenceQueryWrapper extends SpanQueryWrapper {
private String field;
private ArrayList<SpanQueryWrapper> segments;
private ArrayList<DistanceConstraint> constraints;
+ private QueryException constraintException = null;
+
private final String limitationError =
"Distance constraints not supported with " +
"empty or negative operands";
@@ -48,7 +56,10 @@
/**
- * Empty constructor.
+ * Constructs a new object for sequence deserialization.
+ *
+ * @param field The fields the nested SpanQueries should
+ * search in.
*/
public SpanSequenceQueryWrapper (String field) {
this.field = field;
@@ -57,7 +68,19 @@
/**
- * Constructor accepting term sequences.
+ * Constructs a new object for sequence deserialization
+ * by passing a sequence of terms.
+ *
+ * <blockquote><pre>
+ * SpanSequenceQueryWrapper ssqw =
+ * new SpanSequenceQueryWrapper("tokens", "der", "Baum");
+ * System.out.println(ssqw.toQuery());
+ * // spanNext(tokens:der, tokens:Baum)
+ * </pre></blockquote>
+ *
+ * @param field The fields the nested SpanQueries should
+ * search in.
+ * @param terms[] Arbitrary list of terms to search for.
*/
public SpanSequenceQueryWrapper (String field, String ... terms) {
this(field);
@@ -68,23 +91,30 @@
)
);
};
+ // Query can't be null anymore
this.isNull = false;
};
/**
- * Constructor accepting SpanQuery sequences.
+ * Constructs a new object for sequence deserialization
+ * by passing a single {@link SpanQuery} object.
+ *
+ * @param query Initial {@link SpanQuery} to search for.
*/
- public SpanSequenceQueryWrapper (String field, SpanQuery sq) {
- this(field);
- this.segments.add(new SpanSimpleQueryWrapper(sq));
+ public SpanSequenceQueryWrapper (SpanQuery query) {
+ this(query.getField());
+ this.segments.add(new SpanSimpleQueryWrapper(query));
this.isNull = false;
};
/**
- * Constructor accepting SpanQueryWrapper sequences.
- * These wrappers may be optional, negative or empty.
+ * Constructs a new object for sequence deserialization
+ * by passing a single {@link SpanQueryWrapper} object.
+ * These wrapper queries may be optional, negative, or empty.
+ *
+ * @param query Initial {@link SpanQueryWrapper} to search for.
*/
public SpanSequenceQueryWrapper (String field, SpanQueryWrapper sswq) {
this(field);
@@ -93,233 +123,465 @@
if (sswq.isNull())
return;
- if (DEBUG && !sswq.isEmpty) {
- try {
- log.trace("New span sequence {}", sswq.toQuery().toString());
+ // Some debugging on initiating new sequences
+ if (DEBUG) {
+ if (!sswq.isEmpty()) {
+ try {
+ log.trace("New span sequence {}", sswq.toQuery().toString());
+ }
+ catch (QueryException qe) {
+ log.trace("Unable to serialize query {}", qe.getMessage());
+ };
}
- catch (QueryException qe) {
- log.trace("Unable to serialize query {}", qe.getMessage());
+ else {
+ log.trace("New span sequence, that's initially empty");
};
};
- /*
- System.err.println("Is negative: ");
- System.err.println(sswq.isNegative());
- */
+
this.segments.add(sswq);
this.isNull = false;
};
+
/**
- * Append a term to the sequence.
+ * Append a new term to the sequence.
+ *
+ * <blockquote><pre>
+ * SpanSequenceQueryWrapper ssqw =
+ * new SpanSequenceQueryWrapper("tokens");
+ * ssqw.append("der").append("Baum");
+ * System.out.println(ssqw.toQuery());
+ * // spanNext(tokens:der, tokens:Baum)
+ * </pre></blockquote>
+ *
+ * @param term A new string to search for.
+ * @return The {@link SpanSequenceQueryWrapper} object for chaining.
*/
public SpanSequenceQueryWrapper append (String term) {
- return this.append(new SpanTermQuery(new Term(field, term)));
+ return this.append(new SpanTermQuery(new Term(field, term)));
};
/**
- * Append a SpanQuery to the sequence.
+ * Append a new {@link SpanQuery} object to the sequence.
+ *
+ * @param query A new {@link SpanQuery} to search for.
+ * @return The {@link SpanSequenceQueryWrapper} object for chaining.
*/
public SpanSequenceQueryWrapper append (SpanQuery query) {
- return this.append(new SpanSimpleQueryWrapper(query));
+ return this.append(new SpanSimpleQueryWrapper(query));
};
/**
- * Append a SpanQueryWrapper to the sequence.
+ * Append a new {@link SpanQueryWrapper} object to the sequence.
+ *
+ * @param query A new {@link SpanQueryWrapper} to search for.
+ * @return The {@link SpanSequenceQueryWrapper} object for chaining.
*/
public SpanSequenceQueryWrapper append (SpanQueryWrapper ssq) {
- if (ssq.isNull())
- return this;
- this.isSolved = false;
- this.isNull = false;
+ // The wrapper is null - ignore this in the sequence
+ if (ssq.isNull())
+ return this;
- // Embed a sequence
- if (ssq instanceof SpanSequenceQueryWrapper) {
+ // As the spanQueryWrapper is not null,
+ // the sequence can't be null as well
+ this.isNull = false;
- if (DEBUG)
- log.trace("Add SpanSequenceQueryWrapper to sequence");
+ // The sequence may be problematic
+ this.isSolved = false;
- // There are no constraints - just next spans
- // Flatten!
- SpanSequenceQueryWrapper ssqw = (SpanSequenceQueryWrapper) ssq;
- if (!this.hasConstraints() &&
- !ssqw.hasConstraints() &&
- this.isInOrder() == ssqw.isInOrder()) {
- for (int i = 0; i < ssqw.segments.size(); i++) {
- this.append(ssqw.segments.get(i));
- };
- }
+ // Embed a nested sequence
+ if (ssq instanceof SpanSequenceQueryWrapper) {
- // No flattening
- else {
- this.segments.add(ssq);
- };
- }
+ if (DEBUG)
+ log.trace("Add SpanSequenceQueryWrapper to sequence");
- // Only one segment
- else {
- this.segments.add(ssq);
- };
+ // Some casting
+ SpanSequenceQueryWrapper ssqw = (SpanSequenceQueryWrapper) ssq;
- return this;
+ // There are no constraints and the order is equal - Flatten!
+ if (!this.hasConstraints() && !ssqw.hasConstraints() &&
+ this.isInOrder() == ssqw.isInOrder()) {
+ for (int i = 0; i < ssqw.segments.size(); i++) {
+ this.append(ssqw.segments.get(i));
+ };
+ }
+
+ // Unable to flatten ... :-(
+ else {
+ this.segments.add(ssq);
+ };
+ }
+
+ // This is not a sequence
+ else {
+ this.segments.add(ssq);
+ };
+
+ return this;
};
/**
- * Prepend a term to the sequence.
+ * Prepend a new term to the sequence.
+ *
+ * <blockquote><pre>
+ * SpanSequenceQueryWrapper ssqw =
+ * new SpanSequenceQueryWrapper("tokens", "Baum");
+ * ssqw.prepend("der");
+ * System.out.println(ssqw.toQuery());
+ * // spanNext(tokens:der, tokens:Baum)
+ * </pre></blockquote>
+ *
+ * @param term A new string to search for.
+ * @return The {@link SpanSequenceQueryWrapper} object for chaining.
*/
public SpanSequenceQueryWrapper prepend (String term) {
- return this.prepend(new SpanTermQuery(new Term(field, term)));
+ return this.prepend(new SpanTermQuery(new Term(field, term)));
};
/**
- * Prepend a SpanQuery to the sequence.
+ * Prepend a new {@link SpanQuery} object to the sequence.
+ *
+ * @param query A new {@link SpanQuery} to search for.
+ * @return The {@link SpanSequenceQueryWrapper} object for chaining.
*/
public SpanSequenceQueryWrapper prepend (SpanQuery query) {
- return this.prepend(new SpanSimpleQueryWrapper(query));
+ return this.prepend(new SpanSimpleQueryWrapper(query));
};
/**
- * Prepend a SpanQueryWrapper to the sequence.
+ * Prepend a new {@link SpanQueryWrapper} object to the sequence.
+ *
+ * @param query A new {@link SpanQueryWrapper} to search for.
+ * @return The {@link SpanSequenceQueryWrapper} object for chaining.
*/
public SpanSequenceQueryWrapper prepend (SpanQueryWrapper ssq) {
- if (ssq.isNull())
- return this;
- this.isSolved = false;
- this.isNull = false;
+ // The wrapper is null - ignore this in the sequence
+ if (ssq.isNull())
+ return this;
- // Embed a sequence
- if (ssq instanceof SpanSequenceQueryWrapper) {
+ // As the spanQueryWrapper is not null,
+ // the sequence can't be null as well
+ this.isNull = false;
- // There are no constraints - just next spans
- // Flatten!
- SpanSequenceQueryWrapper ssqw = (SpanSequenceQueryWrapper) ssq;
- if (!this.hasConstraints() &&
- !ssqw.hasConstraints() &&
- this.isInOrder() == ssqw.isInOrder()) {
- for (int i = ssqw.segments.size() - 1; i >= 0; i--) {
- this.prepend(ssqw.segments.get(i));
- };
- }
+ // The sequence may be problematic
+ this.isSolved = false;
- // No flattening
- else {
- this.segments.add(0, ssq);
- };
- }
+ // Embed a nested sequence
+ if (ssq instanceof SpanSequenceQueryWrapper) {
- // Only one segment
- else {
- this.segments.add(0, ssq);
- };
+ // There are no constraints and the order is equal - Flatten!
+ SpanSequenceQueryWrapper ssqw = (SpanSequenceQueryWrapper) ssq;
+ if (!this.hasConstraints() &&
+ !ssqw.hasConstraints() &&
+ this.isInOrder() == ssqw.isInOrder()) {
+ for (int i = ssqw.segments.size() - 1; i >= 0; i--) {
+ this.prepend(ssqw.segments.get(i));
+ };
+ }
- return this;
+ // Unable to flatten ... :-(
+ else {
+ this.segments.add(0, ssq);
+ };
+ }
+
+ // This is not a sequence
+ else {
+ this.segments.add(0, ssq);
+ };
+
+ return this;
};
/**
- * Add a sequence constraint to the sequence for tokens,
- * aka distance constraints.
+ * Add a token based sequence constraint (aka distance constraint)
+ * to the sequence.
+ *
+ * Multiple constraints are supported.
+ *
+ * A minimum value of zero means, there may be an overlap,
+ * a minimum value of 1 means, there is no token between the spans.
+ * It's weird - we know and dislike that. That's why we have to say:
+ *
+ * <strong>Warning!</strong> Sequence constraints are experimental and
+ * may (hopefully) change in future versions!
+ *
+ * @param min The minimum number of tokens between the elements
+ * of the sequence.
+ * @param max The minimum number of tokens between the elements
+ * of the sequence.
+ * @return The {@link SpanSequenceQueryWrapper} object for chaining.
+ * @see DistanceConstraint
*/
public SpanSequenceQueryWrapper withConstraint (int min, int max) {
- return this.withConstraint(min, max, false);
+ return this.withConstraint(min, max, false);
};
/**
- * Add a sequence constraint to the sequence for tokens,
- * aka distance constraints (with exclusion).
+ * Add a token based sequence constraint (aka distance constraint)
+ * to the sequence with an exclusion constraint, meaning
+ * the constraint is fine in case the operands are <em>not</em>
+ * within the distance.
+ *
+ * Multiple constraints are supported.
+ *
+ * A minimum value of zero means, there may be an overlap,
+ * a minimum value of 1 means, there is no token between the spans.
+ * It's weird - we know and dislike that. That's why we have to say:
+ *
+ * <strong>Warning!</strong> Sequence constraints are experimental and
+ * may (hopefully) change in future versions!
+ *
+ * @param min The minimum number of tokens between the elements
+ * of the sequence.
+ * @param max The minimum number of tokens between the elements
+ * of the sequence.
+ * @param exclusion Boolean value indicating, the distance constraint
+ * has to fail.
+ * @return The {@link SpanSequenceQueryWrapper} object for chaining.
+ * @see DistanceConstraint
*/
public SpanSequenceQueryWrapper withConstraint (int min, int max, boolean exclusion) {
- if (this.constraints == null)
- this.constraints = new ArrayList<DistanceConstraint>(1);
- this.constraints.add(new DistanceConstraint(min, max, isInOrder, exclusion));
- return this;
+ if (this.constraints == null)
+ this.constraints = new ArrayList<DistanceConstraint>(1);
+ this.constraints.add(new DistanceConstraint(min, max, this.isInOrder, exclusion));
+ return this;
};
/**
- * Add a sequence constraint to the sequence for various units,
- * aka distance constraints.
+ * Add a sequence constraint (aka distance constraint)
+ * to the sequence based on a certain unit.
+ * The unit has to be a valid {@link SpanElementQuery} term
+ * or <tt>w</tt> for tokens.
+ *
+ * Multiple constraints are supported.
+ *
+ * A minimum value of zero means, there may be an overlap,
+ * a minimum value of 1 means, there is no token between the spans.
+ * It's weird - we know and dislike that. That's why we have to say:
+ *
+ * <strong>Warning!</strong> Sequence constraints are experimental and
+ * may (hopefully) change in future versions!
+ *
+ * @param min The minimum number of tokens between the elements
+ * of the sequence.
+ * @param max The minimum number of tokens between the elements
+ * of the sequence.
+ * @param unit Unit for distance - will be evaluated to a {@link SpanElementQuery}.
+ * @return The {@link SpanSequenceQueryWrapper} object for chaining.
+ * @see DistanceConstraint
*/
public SpanSequenceQueryWrapper withConstraint (int min, int max, String unit) {
- return this.withConstraint(min, max, unit, false);
+ return this.withConstraint(min, max, unit, false);
};
+
/**
- * Add a sequence constraint to the sequence for various units,
- * aka distance constraints (with exclusion).
+ * Add a sequence constraint (aka distance constraint)
+ * to the sequence based on a certain unit and with an
+ * exclusion constraint, meaning the constraint is fine
+ * in case the operands are <em>not</em> within the distance.
+ * The unit has to be a valid {@link SpanElementQuery} term
+ * or <tt>w</tt> for tokens.
+ *
+ * Multiple constraints are supported.
+ *
+ * A minimum value of zero means, there may be an overlap,
+ * a minimum value of 1 means, there is no token between the spans.
+ * It's weird - we know and dislike that. That's why we have to say:
+ *
+ * <strong>Warning!</strong> Sequence constraints are experimental and
+ * may (hopefully) change in future versions!
+ *
+ * @param min The minimum number of tokens between the elements
+ * of the sequence.
+ * @param max The minimum number of tokens between the elements
+ * of the sequence.
+ * @param unit Unit for distance - will be evaluated to a {@link SpanElementQuery}.
+ * @param exclusion Boolean value indicating, the distance constraint
+ * has to fail.
+ * @return The {@link SpanSequenceQueryWrapper} object for chaining.
+ * @see DistanceConstraint
*/
- public SpanSequenceQueryWrapper withConstraint (int min,
- int max,
- String unit,
- boolean exclusion) {
- if (this.constraints == null)
- this.constraints = new ArrayList<DistanceConstraint>(1);
+ public SpanSequenceQueryWrapper withConstraint
+ (int min, int max, String unit, boolean exclusion) {
- // Word unit
- if (unit.equals("w"))
- this.constraints.add(new DistanceConstraint(min, max, isInOrder, exclusion));
+ // Word unit
+ if (unit.equals("w")) {
+ if (this.constraints == null)
+ this.constraints = new ArrayList<DistanceConstraint>(1);
+ this.constraints.add(new DistanceConstraint(min, max, isInOrder, exclusion));
+ return this;
+ };
- // Element unit (sentence or paragraph)
- else
- this.constraints.add(
- new DistanceConstraint(
- new SpanElementQuery(this.field, unit), min, max, isInOrder, exclusion)
- );
- return this;
+ // Element unit (sentence or paragraph)
+ return this.withConstraint(
+ min, max, new SpanElementQueryWrapper(this.field, unit), exclusion
+ );
};
/**
- * Respect the order of distances.
+ * Add a sequence constraint (aka distance constraint)
+ * to the sequence based on a certain unit and with an
+ * exclusion constraint, meaning the constraint is fine
+ * in case the operands are <em>not</em> within the distance.
+ *
+ * Multiple constraints are supported.
+ *
+ * A minimum value of zero means, there may be an overlap,
+ * a minimum value of 1 means, there is no token between the spans.
+ * It's weird - we know and dislike that. That's why we have to say:
+ *
+ * <strong>Warning!</strong> Sequence constraints are experimental and
+ * may (hopefully) change in future versions!
+ *
+ * @param min The minimum number of tokens between the elements
+ * of the sequence.
+ * @param max The minimum number of tokens between the elements
+ * of the sequence.
+ * @param unit A {@link SpanElementQueryWrapper} as the unit for distance.
+ * @param exclusion Boolean value indicating, the distance constraint
+ * has to fail.
+ * @return The {@link SpanSequenceQueryWrapper} object for chaining.
+ * @see DistanceConstraint
*/
- public void setInOrder (boolean isInOrder) {
- this.isInOrder = isInOrder;
+ public SpanSequenceQueryWrapper withConstraint
+ (int min, int max, SpanElementQueryWrapper unit, boolean exclusion) {
+ if (this.constraints == null)
+ this.constraints = new ArrayList<DistanceConstraint>(1);
+
+ // Element unit (sentence or paragraph)
+ // Todo: This should possibly be evaluated to a query later on!
+ try {
+ this.constraints.add(
+ new DistanceConstraint(
+ (SpanElementQuery) unit.toQuery(),
+ min,
+ max,
+ isInOrder,
+ exclusion
+ )
+ );
+ }
+ catch (QueryException qe) {
+ this.constraintException = qe;
+ };
+ return this;
};
/**
- * Check if the order is relevant.
+ * Check if the sequence has to be in order.
+ *
+ * @return <tt>true</tt> in case the sequence
+ * has to be in order and <tt>false</tt>
+ * in case the order is not relevant.
*/
public boolean isInOrder () {
- return this.isInOrder;
+ return this.isInOrder;
};
/**
- * Check if there are constraints defined for the sequence.
+ * Set the boolean value indicating if the sequence
+ * has to be in order.
+ *
+ * @param order <tt>true</tt> in case the sequence
+ * has to be in order and <tt>false</tt>
+ * in case the order is not relevant.
+ */
+ public void setInOrder (boolean order) {
+ this.isInOrder = order;
+ };
+
+
+ /**
+ * Check if the sequence has constraints.
+ *
+ * @return <tt>true</tt> in case the sequence
+ * has any constraints and <tt>false</tt>
+ * in case it is a simple next query.
*/
public boolean hasConstraints () {
- if (this.constraints == null)
- return false;
- if (this.constraints.size() <= 0)
- return false;
+ if (this.constraints == null)
+ return false;
- // The constraint is in fact a next query
- if (this.constraints.size() == 1) {
- DistanceConstraint dc = this.constraints.get(0);
- if (dc.getUnit().equals("w") &&
- dc.getMinDistance() == 1 &&
- dc.getMaxDistance() == 1) {
- return false;
- };
- };
+ if (this.constraints.size() <= 0)
+ return false;
+
+ // The constraint is in fact a next query,
+ // that will be optimized away later on
+ if (this.constraints.size() == 1) {
+ DistanceConstraint dc = this.constraints.get(0);
+ if (dc.getUnit().equals("w") &&
+ dc.getMinDistance() == 1 &&
+ dc.getMaxDistance() == 1) {
+ return false;
+ };
+ };
- return true;
+ return true;
};
+
+ public boolean isEmpty () {
+ if (this.segments.size() == 1)
+ return this.segments.get(0).isEmpty();
+
+ if (!this.isSolved)
+ _solveProblematicSequence();
+ return super.isEmpty();
+ };
+
+
+ public boolean isOptional () {
+ if (this.segments.size() == 1)
+ return this.segments.get(0).isOptional();
+ if (!this.isSolved)
+ _solveProblematicSequence();
+ return super.isOptional();
+ };
+
+ public boolean isNegative () {
+ if (this.segments.size() == 1)
+ return this.segments.get(0).isNegative();
+ if (!this.isSolved)
+ _solveProblematicSequence();
+ return super.isNegative();
+ };
+
+ public boolean isExtendedToTheRight () {
+ if (!this.isSolved)
+ _solveProblematicSequence();
+ return this.isExtendedToTheRight;
+ };
+
+
/**
- * Serialize Query to Lucene SpanQueries
+ * Serialize the wrapped sequence to a {@link SpanQuery} object.
+ *
+ * @return A {@link SpanQuery} object.
+ * @throws QueryException
*/
public SpanQuery toQuery () throws QueryException {
+
+ // There was a serialization failure not yet reported
+ if (this.constraintException != null)
+ throw constraintException;
+
int size = this.segments.size();
// Nothing to do
@@ -340,13 +602,19 @@
return (SpanQuery) this.segments.get(0).toQuery();
if (this.segments.get(0).isEmpty())
- throw new QueryException(613, "Sequence is not allowed to be empty");
+ throw new QueryException(
+ 613, "Sequence is not allowed to be empty"
+ );
if (this.segments.get(0).isOptional())
- throw new QueryException(613, "Sequence is not allowed to be optional");
+ throw new QueryException(
+ 613, "Sequence is not allowed to be optional"
+ );
if (this.segments.get(0).isNegative())
- throw new QueryException(613, "Sequence is not allowed to be negative");
+ throw new QueryException(
+ 613, "Sequence is not allowed to be negative"
+ );
};
if (!this.isSolved) {
@@ -465,259 +733,202 @@
return (SpanQuery) query;
};
+
+
/*
+ Check if there are problematic segments in the sequence
+ (either negative, optional or empty) and deal with them
+ (make optional segments to or-queries and negative and empty
+ segments to extensions).
+ This has to be done as long as there are problematic segments
+ In the queries.
+
While there is a segment isNegative() or isOptional() or isEmpty() do
- look for an anchor next to it
- merge the problematic segment with the anchor
- go on
*/
private boolean _solveProblematicSequence () {
+ int size = this.segments.size();
+ // Check if there is a problematic segment
+ SpanQueryWrapper underScrutiny;
+ boolean noRemainingProblem = true;
+ int i = 0;
- int size = this.segments.size();
+ if (DEBUG)
+ log.trace("Try to solve a query of {} segments", size);
- // Check if there is a problematic segment
- SpanQueryWrapper underScrutiny;
- boolean noRemainingProblem = true;
- int i = 0;
+ // Iterate over all segments
+ for (; i < size;) {
+ underScrutiny = this.segments.get(i);
- if (DEBUG)
- log.trace("Try to solve a query of {} segments", size);
+ // Check if there is a problem with the current segment
+ if (!underScrutiny.maybeAnchor()) {
- for (; i < size;) {
- underScrutiny = this.segments.get(i);
+ if (DEBUG)
+ log.trace("segment {} is problematic", i);
- // Check if there is a problem!
- if (!underScrutiny.maybeAnchor()) {
+ // [problem][anchor]
+ if (i < (size-1) && this.segments.get(i+1).maybeAnchor()) {
+ if (DEBUG)
+ log.trace("Situation is [problem][anchor]");
- if (DEBUG)
- log.trace("segment {} is problematic", i);
+ // Insert the solution
+ try {
+ this.segments.set(
+ i+1,
+ _merge(this.segments.get(i+1), underScrutiny, false)
+ );
+ }
- // [problem][anchor]
- if (i < (size-1) && this.segments.get(i+1).maybeAnchor()) {
- if (DEBUG)
- log.trace("Situation is [problem][anchor]");
+ // An error occurred while solving the problem
+ catch (QueryException e) {
+ return false;
+ };
- // Insert the solution
- try {
- this.segments.set(
- i+1,
- _merge(this.segments.get(i+1), underScrutiny, false)
- );
- }
- catch (QueryException e) {
- return false;
- };
+ // Remove the problem
+ this.segments.remove(i);
+ size--;
- // Remove the problem
- this.segments.remove(i);
- size--;
+ if (DEBUG)
+ log.trace("Remove segment {} - now size {}", i, size);
- if (DEBUG)
- log.trace("Remove segment {} - now size {}", i, size);
+ // Restart checking
+ i = 0;
+ }
- // Restart checking
- i = 0;
- }
+ // [anchor][problem]
+ else if (i >= 1 && this.segments.get(i-1).maybeAnchor()) {
+ if (DEBUG)
+ log.trace("Situation is [anchor][problem]");
- // [anchor][problem]
- else if (i >= 1 && this.segments.get(i-1).maybeAnchor()) {
- if (DEBUG)
- log.trace("Situation is [anchor][problem]");
+ // Insert the solution
+ try {
+ this.segments.set(
+ i-1,
+ _merge(this.segments.get(i-1), underScrutiny, true)
+ );
+ }
+ catch (QueryException e) {
+ return false;
+ };
- // Insert the solution
- try {
- this.segments.set(
- i-1,
- _merge(this.segments.get(i-1), underScrutiny, true)
- );
- }
- catch (QueryException e) {
- return false;
- };
+ // Remove the problem
+ this.segments.remove(i);
+ size--;
- // Remove the problem
- this.segments.remove(i);
- size--;
+ if (DEBUG)
+ log.trace("Remove segment {} - now size {}", i, size);
- if (DEBUG)
- log.trace("Remove segment {} - now size {}", i, size);
+ // Restart checking
+ i = 0;
+ }
+ // [problem][problem]
+ else {
+ if (DEBUG)
+ log.trace("Situation is [problem][problem]");
+ noRemainingProblem = false;
+ i++;
+ };
+ }
+ else {
+ if (DEBUG)
+ log.trace("segment {} can be an anchor", i);
+ i++;
+ };
+ };
- // Restart checking
- i = 0;
- }
- // [problem][problem]
- else {
- if (DEBUG)
- log.trace("Situation is [problem][problem]");
- noRemainingProblem = false;
- i++;
- };
- }
- else {
- if (DEBUG)
- log.trace("segment {} can be an anchor", i);
- i++;
- };
- };
+ // There is still a remaining problem
+ if (!noRemainingProblem) {
- // There is still a remaining problem
- if (!noRemainingProblem) {
+ // The size has changed - retry!
+ if (size != this.segments.size())
+ return _solveProblematicSequence();
- // The size has changed - retry!
- if (size != this.segments.size())
- return _solveProblematicSequence();
+ this.isSolved = true;
+ return true;
+ };
- this.isSolved = true;
- return true;
- };
-
- this.isSolved = true;
- return false;
+ this.isSolved = true;
+ return false;
};
// Todo: Deal with negative and optional!
// [base=der][base!=Baum]?
- private SpanQueryWrapper _merge (
- SpanQueryWrapper anchor,
- SpanQueryWrapper problem,
- boolean mergeLeft) throws QueryException {
+ private SpanQueryWrapper _merge (SpanQueryWrapper anchor,
+ SpanQueryWrapper problem,
+ boolean mergeLeft) throws QueryException {
- // Extend to the right - merge to the left
- int direction = 1;
- if (!mergeLeft)
- direction = -1;
+ // Extend to the right - merge to the left
+ int direction = mergeLeft ? 1 : -1;
- if (DEBUG)
- log.trace("Will merge two spans to {}", mergeLeft ? "left" : "right");
+ if (DEBUG)
+ log.trace("Will merge two spans to {}",
+ mergeLeft ? "left" : "right");
- // Make empty extension to anchor
- if (problem.isEmpty()) {
- SpanQuery query;
+ // Make empty extension to anchor
+ if (problem.isEmpty()) {
+ SpanQuery query;
- if (DEBUG)
- log.trace("Problem is empty");
+ if (DEBUG)
+ log.trace("Problem is empty with class {}",
+ problem.getClassNumber());
- if (problem.hasClass) {
+ query = new SpanExpansionQuery(
+ anchor.toQuery(),
+ problem.getMin(),
+ problem.getMax(),
+ direction,
+ problem.hasClass() ? problem.getClassNumber() : (byte) 0,
+ true
+ );
+ return new SpanSimpleQueryWrapper(query).isExtended(true);
+ }
- if (DEBUG)
- log.trace("Problem has class {}", problem.getClassNumber());
+ // make negative extension to anchor
+ else if (problem.isNegative()) {
- query = new SpanExpansionQuery(
- anchor.toQuery(),
- problem.getMin(),
- problem.getMax(),
- direction,
- problem.getClassNumber(),
- true
- );
- }
- else {
+ SpanQuery query;
- if (DEBUG)
- log.trace("Problem has no class");
+ if (DEBUG)
+ log.trace("Problem is negative with class {}",
+ problem.getClassNumber());
- query = new SpanExpansionQuery(
- anchor.toQuery(),
- problem.getMin(),
- problem.getMax(),
- direction,
- true
- );
- };
- return new SpanSimpleQueryWrapper(query).isExtended(true);
- }
+ query = new SpanExpansionQuery(
+ anchor.toQuery(),
+ problem.toQuery(),
+ problem.getMin(),
+ problem.getMax(),
+ direction,
+ problem.hasClass() ? problem.getClassNumber() : (byte) 0,
+ true
+ );
+ return new SpanSimpleQueryWrapper(query).isExtended(true);
+ };
- // make negative extension to anchor
- else if (problem.isNegative()) {
+ if (DEBUG)
+ log.trace("Problem is optional");
- if (DEBUG)
- log.trace("Problem is negative");
+ // [base=der][base=baum]?
- SpanQuery query;
- if (problem.hasClass) {
+ // [base=der]
+ SpanAlterQueryWrapper saqw =
+ new SpanAlterQueryWrapper(this.field, anchor);
- if (DEBUG)
- log.trace("Problem has class {}", problem.getClassNumber());
+ // [base=der]
+ SpanSequenceQueryWrapper ssqw =
+ new SpanSequenceQueryWrapper(this.field, anchor);
- query = new SpanExpansionQuery(
- anchor.toQuery(),
- problem.toQuery(),
- problem.getMin(),
- problem.getMax(),
- direction,
- problem.getClassNumber(),
- true
- );
- }
- else {
- if (DEBUG)
- log.trace("Problem has no class");
-
- query = new SpanExpansionQuery(
- anchor.toQuery(),
- problem.toQuery(),
- problem.getMin(),
- problem.getMax(),
- direction,
- true
- );
- };
- return new SpanSimpleQueryWrapper(query).isExtended(true);
- };
-
- if (DEBUG)
- log.trace("Problem is optional");
-
- // [base=der][base=baum]?
-
- // [base=der]
- SpanAlterQueryWrapper saqw = new SpanAlterQueryWrapper(this.field, anchor);
-
- // [base=der]
- SpanSequenceQueryWrapper ssqw = new SpanSequenceQueryWrapper(this.field, anchor);
-
- // [base=der][base=baum]
- if (mergeLeft)
- ssqw.append(new SpanSimpleQueryWrapper(problem.toQuery()));
- // [base=baum][base=der]
- else
- ssqw.prepend(new SpanSimpleQueryWrapper(problem.toQuery()));
+ // [base=der][base=baum]
+ if (mergeLeft)
+ ssqw.append(new SpanSimpleQueryWrapper(problem.toQuery()));
+ // [base=baum][base=der]
+ else
+ ssqw.prepend(new SpanSimpleQueryWrapper(problem.toQuery()));
- saqw.or(ssqw);
+ saqw.or(ssqw);
- return (SpanQueryWrapper) saqw;
- };
-
- public boolean isEmpty () {
- if (this.segments.size() == 0)
- return this.segments.get(0).isEmpty();
- if (!this.isSolved)
- _solveProblematicSequence();
- return super.isEmpty();
- };
-
-
- public boolean isOptional () {
- if (this.segments.size() == 0)
- return this.segments.get(0).isOptional();
- if (!this.isSolved)
- _solveProblematicSequence();
- return super.isOptional();
- };
-
- public boolean isNegative () {
- if (this.segments.size() == 0)
- return this.segments.get(0).isNegative();
- if (!this.isSolved)
- _solveProblematicSequence();
- return super.isNegative();
- };
-
- public boolean isExtendedToTheRight () {
- if (!this.isSolved) {
- _solveProblematicSequence();
- };
- return this.isExtendedToTheRight;
+ return (SpanQueryWrapper) saqw;
};
};
diff --git a/src/main/java/de/ids_mannheim/korap/query/wrap/SpanSimpleQueryWrapper.java b/src/main/java/de/ids_mannheim/korap/query/wrap/SpanSimpleQueryWrapper.java
index dcc38da..39c1a7d 100644
--- a/src/main/java/de/ids_mannheim/korap/query/wrap/SpanSimpleQueryWrapper.java
+++ b/src/main/java/de/ids_mannheim/korap/query/wrap/SpanSimpleQueryWrapper.java
@@ -8,21 +8,21 @@
private SpanQuery query;
public SpanSimpleQueryWrapper (String field, String term) {
- this.isNull = false;
- this.query = new SpanTermQuery(new Term(field, term));
+ this.isNull = false;
+ this.query = new SpanTermQuery(new Term(field, term));
};
public SpanSimpleQueryWrapper (SpanQuery query) {
- this.isNull = false;
- this.query = query;
+ this.isNull = false;
+ this.query = query;
};
public SpanQuery toQuery () {
- return this.query;
+ return this.query;
};
public SpanSimpleQueryWrapper isExtended (boolean extended) {
- this.isExtended = true;
- return this;
+ this.isExtended = true;
+ return this;
};
};
diff --git a/src/test/java/de/ids_mannheim/korap/query/TestKorapQuery.java b/src/test/java/de/ids_mannheim/korap/query/TestKorapQuery.java
index c5fd542..5720aa2 100644
--- a/src/test/java/de/ids_mannheim/korap/query/TestKorapQuery.java
+++ b/src/test/java/de/ids_mannheim/korap/query/TestKorapQuery.java
@@ -12,227 +12,230 @@
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
+
+/**
+ * @author diewald
+ */
@RunWith(JUnit4.class)
public class TestKorapQuery {
@Test
public void korapQuerySegment () throws QueryException {
- SpanQuery sq = new KorapQuery("field1").seg("a").with("b").toQuery();
- assertEquals("spanSegment(field1:a, field1:b)", sq.toString());
-
- sq = new KorapQuery("field2").seg("a", "b").with("c").toQuery();
- assertEquals("spanSegment(spanSegment(field2:a, field2:b), field2:c)", sq.toString());
+ SpanQuery sq = new KorapQuery("field1").seg("a").with("b").toQuery();
+ assertEquals("spanSegment(field1:a, field1:b)", sq.toString());
+
+ sq = new KorapQuery("field2").seg("a", "b").with("c").toQuery();
+ assertEquals("spanSegment(spanSegment(field2:a, field2:b), field2:c)", sq.toString());
};
@Test
public void korapQueryRegexSegment () throws QueryException {
- KorapQuery kq = new KorapQuery("field1");
- SpanQuery sq = kq.seg("a").with(kq.re("b.*c")).toQuery();
- assertEquals("spanSegment(field1:a, SpanMultiTermQueryWrapper(field1:/b.*c/))", sq.toString());
+ KorapQuery kq = new KorapQuery("field1");
+ SpanQuery sq = kq.seg("a").with(kq.re("b.*c")).toQuery();
+ assertEquals("spanSegment(field1:a, SpanMultiTermQueryWrapper(field1:/b.*c/))", sq.toString());
- kq = new KorapQuery("field2");
- sq = kq.seg(kq.re("a.*")).with("b").toQuery();
- assertEquals("spanSegment(SpanMultiTermQueryWrapper(field2:/a.*/), field2:b)", sq.toString());
+ kq = new KorapQuery("field2");
+ sq = kq.seg(kq.re("a.*")).with("b").toQuery();
+ assertEquals("spanSegment(SpanMultiTermQueryWrapper(field2:/a.*/), field2:b)", sq.toString());
};
@Test
public void korapQueryRegexSegment2 () throws QueryException {
- KorapQuery kq = new KorapQuery("field");
- SpanQuery sq = kq.seg("a").with(kq.or("b").or("c")).toQuery();
- assertEquals("spanSegment(field:a, spanOr([field:b, field:c]))", sq.toString());
+ KorapQuery kq = new KorapQuery("field");
+ SpanQuery sq = kq.seg("a").with(kq.or("b").or("c")).toQuery();
+ assertEquals("spanSegment(field:a, spanOr([field:b, field:c]))", sq.toString());
- kq = new KorapQuery("field");
- sq = kq.seg("a").with(kq.or("b", "c")).toQuery();
- assertEquals("spanSegment(field:a, spanOr([field:b, field:c]))", sq.toString());
+ kq = new KorapQuery("field");
+ sq = kq.seg("a").with(kq.or("b", "c")).toQuery();
+ assertEquals("spanSegment(field:a, spanOr([field:b, field:c]))", sq.toString());
-
- kq = new KorapQuery("field");
- // [ a & (b | /c.*d/) ]
- sq = kq.seg("a").with(kq.or("b").or(kq.re("c.*d"))).toQuery();
- assertEquals("spanSegment(field:a, spanOr([field:b, SpanMultiTermQueryWrapper(field:/c.*d/)]))", sq.toString());
+ kq = new KorapQuery("field");
+ // [ a & (b | /c.*d/) ]
+ sq = kq.seg("a").with(kq.or("b").or(kq.re("c.*d"))).toQuery();
+ assertEquals("spanSegment(field:a, spanOr([field:b, SpanMultiTermQueryWrapper(field:/c.*d/)]))", sq.toString());
};
@Test
public void korapQuerySequenceSegment () throws QueryException {
- KorapQuery kq = new KorapQuery("field");
- SpanQuery sq = kq.seq(kq.seg("a").with(kq.or("b", "c"))).append("d").append(kq.re("e.?f")).toQuery();
- assertEquals("spanNext(spanNext(spanSegment(field:a, spanOr([field:b, field:c])), field:d), SpanMultiTermQueryWrapper(field:/e.?f/))", sq.toString());
+ KorapQuery kq = new KorapQuery("field");
+ SpanQuery sq = kq.seq(kq.seg("a").with(kq.or("b", "c"))).append("d").append(kq.re("e.?f")).toQuery();
+ assertEquals("spanNext(spanNext(spanSegment(field:a, spanOr([field:b, field:c])), field:d), SpanMultiTermQueryWrapper(field:/e.?f/))", sq.toString());
};
@Test
public void KorapTagQuery () throws QueryException {
- KorapQuery kq = new KorapQuery("field");
- SpanQuery sq = kq.tag("np").toQuery();
- assertEquals("<field:np />", sq.toString());
+ KorapQuery kq = new KorapQuery("field");
+ SpanQuery sq = kq.tag("np").toQuery();
+ assertEquals("<field:np />", sq.toString());
};
@Test
public void KorapTagQuery2 () throws QueryException {
- KorapQuery kq = new KorapQuery("field");
- SpanQuery sq = kq.or(kq.tag("np"), kq.tag("vp")).toQuery();
- assertEquals("spanOr([<field:np />, <field:vp />])", sq.toString());
+ KorapQuery kq = new KorapQuery("field");
+ SpanQuery sq = kq.or(kq.tag("np"), kq.tag("vp")).toQuery();
+ assertEquals("spanOr([<field:np />, <field:vp />])", sq.toString());
};
@Test
public void KorapTagQuery3 () throws QueryException {
- KorapQuery kq = new KorapQuery("field");
- SpanQuery sq = kq.seq(kq.tag("np"), kq.tag("vp")).toQuery();
- assertEquals("spanNext(<field:np />, <field:vp />)", sq.toString());
+ KorapQuery kq = new KorapQuery("field");
+ SpanQuery sq = kq.seq(kq.tag("np"), kq.tag("vp")).toQuery();
+ assertEquals("spanNext(<field:np />, <field:vp />)", sq.toString());
};
@Test
public void KorapTagQuery4 () throws QueryException {
- KorapQuery kq = new KorapQuery("field");
- SpanQuery sq = kq.seq(kq.tag("np"), kq.tag("vp")).append("test").toQuery();
- assertEquals("spanNext(spanNext(<field:np />, <field:vp />), field:test)", sq.toString());
+ KorapQuery kq = new KorapQuery("field");
+ SpanQuery sq = kq.seq(kq.tag("np"), kq.tag("vp")).append("test").toQuery();
+ assertEquals("spanNext(spanNext(<field:np />, <field:vp />), field:test)", sq.toString());
};
@Test
public void KorapTagQuery5 () throws QueryException {
- KorapQuery kq = new KorapQuery("field");
- SpanQuery sq = kq.contains(kq.tag("s"), kq.tag("np")).toQuery();
- assertEquals("spanContain(<field:s />, <field:np />)", sq.toString());
+ KorapQuery kq = new KorapQuery("field");
+ SpanQuery sq = kq.contains(kq.tag("s"), kq.tag("np")).toQuery();
+ assertEquals("spanContain(<field:s />, <field:np />)", sq.toString());
};
@Test
public void KorapTagQuery6 () throws QueryException {
- KorapQuery kq = new KorapQuery("field");
- SpanQuery sq = kq.seq(kq.seg("tree"), kq.contains(kq.tag("s"), kq.tag("np")), kq.re("hey.*")).toQuery();
- assertEquals("spanNext(spanNext(field:tree, spanContain(<field:s />, <field:np />)), SpanMultiTermQueryWrapper(field:/hey.*/))", sq.toString());
+ KorapQuery kq = new KorapQuery("field");
+ SpanQuery sq = kq.seq(kq.seg("tree"), kq.contains(kq.tag("s"), kq.tag("np")), kq.re("hey.*")).toQuery();
+ assertEquals("spanNext(spanNext(field:tree, spanContain(<field:s />, <field:np />)), SpanMultiTermQueryWrapper(field:/hey.*/))", sq.toString());
};
-
+
@Test
public void KorapClassQuery () throws QueryException {
- KorapQuery kq = new KorapQuery("field");
- SpanQuery sq = kq.seq(kq.seg("tree"), kq._(1, kq.contains(kq.tag("s"), kq.tag("np"))), kq.re("hey.*")).toQuery();
- assertEquals("spanNext(spanNext(field:tree, {1: spanContain(<field:s />, <field:np />)}), SpanMultiTermQueryWrapper(field:/hey.*/))", sq.toString());
+ KorapQuery kq = new KorapQuery("field");
+ SpanQuery sq = kq.seq(kq.seg("tree"), kq._(1, kq.contains(kq.tag("s"), kq.tag("np"))), kq.re("hey.*")).toQuery();
+ assertEquals("spanNext(spanNext(field:tree, {1: spanContain(<field:s />, <field:np />)}), SpanMultiTermQueryWrapper(field:/hey.*/))", sq.toString());
};
@Test
public void KorapClassQuery2 () throws QueryException {
- KorapQuery kq = new KorapQuery("field");
- SpanQuery sq = kq._(kq.seg("base:test")).toQuery();
- assertEquals("{1: field:base:test}", sq.toString());
+ KorapQuery kq = new KorapQuery("field");
+ SpanQuery sq = kq._(kq.seg("base:test")).toQuery();
+ assertEquals("{1: field:base:test}", sq.toString());
};
@Test
public void KorapClassQuery3 () throws QueryException {
- KorapQuery kq = new KorapQuery("field");
- SpanQuery sq = kq.seq(kq.seg("tree"), kq.contains(kq.tag("s"), kq._(kq.tag("np"))), kq.re("hey.*")).toQuery();
- assertEquals("spanNext(spanNext(field:tree, spanContain(<field:s />, {1: <field:np />})), SpanMultiTermQueryWrapper(field:/hey.*/))", sq.toString());
+ KorapQuery kq = new KorapQuery("field");
+ SpanQuery sq = kq.seq(kq.seg("tree"), kq.contains(kq.tag("s"), kq._(kq.tag("np"))), kq.re("hey.*")).toQuery();
+ assertEquals("spanNext(spanNext(field:tree, spanContain(<field:s />, {1: <field:np />})), SpanMultiTermQueryWrapper(field:/hey.*/))", sq.toString());
};
@Test
public void KorapShrinkQuery () throws QueryException {
- KorapQuery kq = new KorapQuery("field");
- SpanQuery sq = kq.shrink(kq.tag("np")).toQuery();
- assertEquals("shrink(1: <field:np />)", sq.toString());
+ KorapQuery kq = new KorapQuery("field");
+ SpanQuery sq = kq.shrink(kq.tag("np")).toQuery();
+ assertEquals("shrink(1: <field:np />)", sq.toString());
};
@Test
public void KorapShrinkQuery1 () throws QueryException {
- KorapQuery kq = new KorapQuery("field");
- SpanQuery sq = kq.shrink(1, kq.tag("np")).toQuery();
- assertEquals("shrink(1: <field:np />)", sq.toString());
+ KorapQuery kq = new KorapQuery("field");
+ SpanQuery sq = kq.shrink(1, kq.tag("np")).toQuery();
+ assertEquals("shrink(1: <field:np />)", sq.toString());
};
@Test
public void KorapShrinkQuery2 () throws QueryException {
- KorapQuery kq = new KorapQuery("field");
- SpanQuery sq = kq.shrink(1, kq._(1, kq.tag("np"))).toQuery();
- assertEquals("shrink(1: {1: <field:np />})", sq.toString());
+ KorapQuery kq = new KorapQuery("field");
+ SpanQuery sq = kq.shrink(1, kq._(1, kq.tag("np"))).toQuery();
+ assertEquals("shrink(1: {1: <field:np />})", sq.toString());
};
@Test
public void KorapShrinkQuery3 () throws QueryException {
- KorapQuery kq = new KorapQuery("field");
- SpanQuery sq = kq.shrink(1, kq._(1, kq.seq(kq.tag("np"), kq._(kq.seg("test").without("no"))))).toQuery();
- assertEquals("shrink(1: {1: spanNext(<field:np />, {1: spanNot(field:test, field:no, 0, 0)})})", sq.toString());
+ KorapQuery kq = new KorapQuery("field");
+ SpanQuery sq = kq.shrink(1, kq._(1, kq.seq(kq.tag("np"), kq._(kq.seg("test").without("no"))))).toQuery();
+ assertEquals("shrink(1: {1: spanNext(<field:np />, {1: spanNot(field:test, field:no, 0, 0)})})", sq.toString());
};
@Test
public void KorapShrinkQuery4 () throws QueryException {
- KorapQuery kq = new KorapQuery("field");
- SpanQuery sq = kq.seq(kq.seg("try1"), kq.shrink(1, kq._(1, kq.seg("try2"))), kq.seg("try3")).toQuery();
- assertEquals("spanNext(spanNext(field:try1, shrink(1: {1: field:try2})), field:try3)", sq.toString());
+ KorapQuery kq = new KorapQuery("field");
+ SpanQuery sq = kq.seq(kq.seg("try1"), kq.shrink(1, kq._(1, kq.seg("try2"))), kq.seg("try3")).toQuery();
+ assertEquals("spanNext(spanNext(field:try1, shrink(1: {1: field:try2})), field:try3)", sq.toString());
};
@Test
public void KorapSequenceQuery1 () throws QueryException {
- KorapQuery kq = new KorapQuery("field");
- SpanQuery sq = kq.seq(kq.seg("try1")).append(kq.seg("try2")).toQuery();
- assertEquals("spanNext(field:try1, field:try2)", sq.toString());
+ KorapQuery kq = new KorapQuery("field");
+ SpanQuery sq = kq.seq(kq.seg("try1")).append(kq.seg("try2")).toQuery();
+ assertEquals("spanNext(field:try1, field:try2)", sq.toString());
};
@Test
public void KorapSequenceQuery2 () throws QueryException {
- KorapQuery kq = new KorapQuery("field");
- SpanQuery sq = kq.seq(kq.seg("try1")).append(kq.seg("try2")).withConstraint(2,3).toQuery();
- assertEquals("spanDistance(field:try1, field:try2, [(w[2:3], ordered, notExcluded)])", sq.toString());
+ KorapQuery kq = new KorapQuery("field");
+ SpanQuery sq = kq.seq(kq.seg("try1")).append(kq.seg("try2")).withConstraint(2,3).toQuery();
+ assertEquals("spanDistance(field:try1, field:try2, [(w[2:3], ordered, notExcluded)])", sq.toString());
};
@Test
public void KorapSequenceQuery3 () throws QueryException {
- KorapQuery kq = new KorapQuery("field");
- SpanQuery sq = kq.seq(kq.seg("try1")).append(kq.seg("try2")).withConstraint(2,3, "s").toQuery();
- assertEquals("spanElementDistance(field:try1, field:try2, [(s[2:3], ordered, notExcluded)])", sq.toString());
+ KorapQuery kq = new KorapQuery("field");
+ SpanQuery sq = kq.seq(kq.seg("try1")).append(kq.seg("try2")).withConstraint(2,3, "s").toQuery();
+ assertEquals("spanElementDistance(field:try1, field:try2, [(s[2:3], ordered, notExcluded)])", sq.toString());
};
@Test
public void KorapSequenceQuery4 () throws QueryException {
- KorapQuery kq = new KorapQuery("field");
- SpanQuery sq = kq.seq(kq.seg("try1")).append(kq.seg("try2")).withConstraint(2,3,"s").withConstraint(5,6,"w").toQuery();
- assertEquals("spanMultipleDistance(field:try1, field:try2, [(s[2:3], ordered, notExcluded), (w[5:6], ordered, notExcluded)])", sq.toString());
+ KorapQuery kq = new KorapQuery("field");
+ SpanQuery sq = kq.seq(kq.seg("try1")).append(kq.seg("try2")).withConstraint(2,3,"s").withConstraint(5,6,"w").toQuery();
+ assertEquals("spanMultipleDistance(field:try1, field:try2, [(s[2:3], ordered, notExcluded), (w[5:6], ordered, notExcluded)])", sq.toString());
};
@Test
public void KorapSequenceQuery5 () throws QueryException {
- KorapQuery kq = new KorapQuery("field");
- SpanQuery sq = kq.seq(kq.seg("try1")).append(kq.seg("try2")).withConstraint(2,3,true).toQuery();
- assertEquals("spanDistance(field:try1, field:try2, [(w[2:3], ordered, excluded)])", sq.toString());
+ KorapQuery kq = new KorapQuery("field");
+ SpanQuery sq = kq.seq(kq.seg("try1")).append(kq.seg("try2")).withConstraint(2,3,true).toQuery();
+ assertEquals("spanDistance(field:try1, field:try2, [(w[2:3], ordered, excluded)])", sq.toString());
};
@Test
public void KorapSequenceQuery6 () throws QueryException {
- KorapQuery kq = new KorapQuery("field");
- SpanQuery sq = kq.seq(kq.seg("try1")).append(kq.seg("try2")).withConstraint(2,3,"s", true).toQuery();
- assertEquals("spanElementDistance(field:try1, field:try2, [(s[2:3], ordered, excluded)])", sq.toString());
+ KorapQuery kq = new KorapQuery("field");
+ SpanQuery sq = kq.seq(kq.seg("try1")).append(kq.seg("try2")).withConstraint(2,3,"s", true).toQuery();
+ assertEquals("spanElementDistance(field:try1, field:try2, [(s[2:3], ordered, excluded)])", sq.toString());
};
@Test
public void KorapSequenceQuery7 () throws QueryException {
- KorapQuery kq = new KorapQuery("field");
- SpanQuery sq = kq.seq(kq.seg("try1")).append(kq.seg("try2")).withConstraint(5,6).withConstraint(2,3,"s",true).toQuery();
- assertEquals("spanMultipleDistance(field:try1, field:try2, [(w[5:6], ordered, notExcluded), (s[2:3], ordered, excluded)]])", sq.toString());
+ KorapQuery kq = new KorapQuery("field");
+ SpanQuery sq = kq.seq(kq.seg("try1")).append(kq.seg("try2")).withConstraint(5,6).withConstraint(2,3,"s",true).toQuery();
+ assertEquals("spanMultipleDistance(field:try1, field:try2, [(w[5:6], ordered, notExcluded), (s[2:3], ordered, excluded)])", sq.toString());
};
@Test
public void KorapSequenceQuery8 () throws QueryException {
- KorapQuery kq = new KorapQuery("field");
- SpanQuery sq = kq.seq(kq.seg("try1")).append(kq.seg("try2")).append("try3").withConstraint(5,6).withConstraint(2,3,"s",true).toQuery();
- assertEquals("spanMultipleDistance(spanMultipleDistance(field:try1, field:try2, [(w[5:6], ordered, notExcluded), (s[2:3], ordered, excluded)]]), field:try3, [(w[5:6], ordered, notExcluded), (s[2:3], ordered, excluded)]])", sq.toString());
+ KorapQuery kq = new KorapQuery("field");
+ SpanQuery sq = kq.seq(kq.seg("try1")).append(kq.seg("try2")).append("try3").withConstraint(5,6).withConstraint(2,3,"s",true).toQuery();
+ assertEquals("spanMultipleDistance(spanMultipleDistance(field:try1, field:try2, [(w[5:6], ordered, notExcluded), (s[2:3], ordered, excluded)]), field:try3, [(w[5:6], ordered, notExcluded), (s[2:3], ordered, excluded)])", sq.toString());
};
@Test
public void KorapWithinQuery1 () throws QueryException {
- KorapQuery kq = new KorapQuery("field");
- SpanQuery sq = kq.contains(kq.seg("test"), kq.seg("test2")).toQuery();
- assertEquals("spanContain(field:test, field:test2)", sq.toString());
+ KorapQuery kq = new KorapQuery("field");
+ SpanQuery sq = kq.contains(kq.seg("test"), kq.seg("test2")).toQuery();
+ assertEquals("spanContain(field:test, field:test2)", sq.toString());
};
@Test
public void KorapWithinQuery2 () throws QueryException {
- KorapQuery kq = new KorapQuery("field");
- SpanQuery sq = kq.overlaps(kq.seg("test"), kq.seg("test2")).toQuery();
- assertEquals("spanOverlap(field:test, field:test2)", sq.toString());
+ KorapQuery kq = new KorapQuery("field");
+ SpanQuery sq = kq.overlaps(kq.seg("test"), kq.seg("test2")).toQuery();
+ assertEquals("spanOverlap(field:test, field:test2)", sq.toString());
};
@Test
public void KorapWithinQuery3 () throws QueryException {
- KorapQuery kq = new KorapQuery("field");
- SpanQuery sq = kq.startswith(kq.seg("test"), kq.seg("test2")).toQuery();
- assertEquals("spanStartsWith(field:test, field:test2)", sq.toString());
+ KorapQuery kq = new KorapQuery("field");
+ SpanQuery sq = kq.startswith(kq.seg("test"), kq.seg("test2")).toQuery();
+ assertEquals("spanStartsWith(field:test, field:test2)", sq.toString());
};
// kq.seg("a").append(kq.ANY).append("b:c");
diff --git a/src/test/java/de/ids_mannheim/korap/query/TestSpanSequenceQuery.java b/src/test/java/de/ids_mannheim/korap/query/TestSpanSequenceQuery.java
index f6fd664..4115e59 100644
--- a/src/test/java/de/ids_mannheim/korap/query/TestSpanSequenceQuery.java
+++ b/src/test/java/de/ids_mannheim/korap/query/TestSpanSequenceQuery.java
@@ -2,6 +2,9 @@
import java.util.*;
import de.ids_mannheim.korap.query.wrap.SpanSequenceQueryWrapper;
+import de.ids_mannheim.korap.query.wrap.SpanClassQueryWrapper;
+import de.ids_mannheim.korap.query.wrap.SpanElementQueryWrapper;
+import de.ids_mannheim.korap.query.wrap.SpanRepetitionQueryWrapper;
import de.ids_mannheim.korap.util.QueryException;
@@ -11,42 +14,162 @@
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
+
+/**
+ * @author margaretha, diewald
+ */
@RunWith(JUnit4.class)
public class TestSpanSequenceQuery {
@Test
public void spanSequenceQuery () throws QueryException {
- SpanSequenceQueryWrapper sssq = new SpanSequenceQueryWrapper("field");
- assertNull(sssq.toQuery());
- assertFalse(sssq.hasConstraints());
+ SpanSequenceQueryWrapper sssq = new SpanSequenceQueryWrapper("field");
+ assertNull(sssq.toQuery());
+ assertFalse(sssq.hasConstraints());
- sssq.append("a").append("b");
- assertEquals("spanNext(field:a, field:b)", sssq.toQuery().toString());
- assertFalse(sssq.hasConstraints());
+ sssq.append("a").append("b");
+ assertEquals("spanNext(field:a, field:b)", sssq.toQuery().toString());
+ assertFalse(sssq.hasConstraints());
- sssq.append("c");
- assertEquals("spanNext(spanNext(field:a, field:b), field:c)", sssq.toQuery().toString());
- assertFalse(sssq.hasConstraints());
+ sssq.append("c");
+ assertEquals("spanNext(spanNext(field:a, field:b), field:c)", sssq.toQuery().toString());
+ assertFalse(sssq.hasConstraints());
- sssq = new SpanSequenceQueryWrapper("field");
- sssq.append("a");
- assertEquals("field:a", sssq.toQuery().toString());
- assertFalse(sssq.hasConstraints());
+ sssq = new SpanSequenceQueryWrapper("field");
+ sssq.append("a");
+ assertEquals("field:a", sssq.toQuery().toString());
+ assertFalse(sssq.hasConstraints());
- sssq.append("b");
- assertEquals("spanNext(field:a, field:b)", sssq.toQuery().toString());
- assertFalse(sssq.hasConstraints());
+ sssq.append("b");
+ assertEquals("spanNext(field:a, field:b)", sssq.toQuery().toString());
+ assertFalse(sssq.hasConstraints());
- sssq.withConstraint(2,3);
- assertTrue(sssq.hasConstraints());
+ sssq.withConstraint(2,3);
+ assertTrue(sssq.hasConstraints());
- assertEquals("spanDistance(field:a, field:b, [(w[2:3], ordered, notExcluded)])", sssq.toQuery().toString());
+ assertEquals("spanDistance(field:a, field:b, [(w[2:3], ordered, notExcluded)])", sssq.toQuery().toString());
- sssq.append("c");
- assertEquals("spanDistance(spanDistance(field:a, field:b, [(w[2:3], ordered, notExcluded)]), field:c, [(w[2:3], ordered, notExcluded)])", sssq.toQuery().toString());
- sssq.withConstraint(6,8, "s");
- assertTrue(sssq.hasConstraints());
+ sssq.append("c");
+ assertEquals("spanDistance(spanDistance(field:a, field:b, [(w[2:3], ordered, notExcluded)]), field:c, [(w[2:3], ordered, notExcluded)])", sssq.toQuery().toString());
+ sssq.withConstraint(6,8, "s");
+ assertTrue(sssq.hasConstraints());
- assertEquals("spanMultipleDistance(spanMultipleDistance(field:a, field:b, [(w[2:3], ordered, notExcluded), (s[6:8], ordered, notExcluded)]), field:c, [(w[2:3], ordered, notExcluded), (s[6:8], ordered, notExcluded)])", sssq.toQuery().toString());
+ assertEquals("spanMultipleDistance(spanMultipleDistance(field:a, field:b, [(w[2:3], ordered, notExcluded), (s[6:8], ordered, notExcluded)]), field:c, [(w[2:3], ordered, notExcluded), (s[6:8], ordered, notExcluded)])", sssq.toQuery().toString());
+ };
+
+ @Test
+ public void spanSequenceQueryWrapper () throws QueryException {
+
+ SpanSequenceQueryWrapper ssqw, ssqw2;
+ SpanRepetitionQueryWrapper srqw;
+ SpanClassQueryWrapper scqw;
+
+ // Synopsis 1
+ ssqw =
+ new SpanSequenceQueryWrapper("tokens", "der", "Baum");
+ assertEquals("spanNext(tokens:der, tokens:Baum)", ssqw.toQuery().toString());
+
+ // Synopsis 2
+ ssqw = new SpanSequenceQueryWrapper("tokens");
+ ssqw.append("der").append("Baum");
+ assertEquals("spanNext(tokens:der, tokens:Baum)", ssqw.toQuery().toString());
+
+ // Append a sequence
+ ssqw = new SpanSequenceQueryWrapper("tokens");
+ ssqw2 = new SpanSequenceQueryWrapper("tokens");
+ ssqw.append("der").append("Baum");
+ ssqw2.append("fiel").append("still");
+ ssqw.append(ssqw2);
+ // This may not be final
+ assertEquals(
+ "spanNext(spanNext(spanNext(tokens:der, tokens:Baum), tokens:fiel), tokens:still)",
+ ssqw.toQuery().toString()
+ );
+
+ // Synopsis 3
+ ssqw = new SpanSequenceQueryWrapper("tokens", "Baum");
+ ssqw.prepend("der");
+ assertEquals("spanNext(tokens:der, tokens:Baum)", ssqw.toQuery().toString());
+
+ // Prepend a sequence
+ ssqw = new SpanSequenceQueryWrapper("tokens");
+ ssqw2 = new SpanSequenceQueryWrapper("tokens");
+ ssqw.append("fiel").append("still");
+ ssqw2.append("der").append("Baum");
+ ssqw.prepend(ssqw2);
+
+ // This may change
+ assertEquals(
+ "spanNext(spanNext(spanNext(tokens:der, tokens:Baum), tokens:fiel), tokens:still)",
+ ssqw.toQuery().toString()
+ );
+
+ // Add constraint
+ ssqw.withConstraint(2,4);
+ // This may change
+ assertEquals(
+ "spanDistance(spanDistance(spanDistance(tokens:der, "+
+ "tokens:Baum, [(w[2:4], ordered, notExcluded)]), "+
+ "tokens:fiel, [(w[2:4], ordered, notExcluded)]), "+
+ "tokens:still, [(w[2:4], ordered, notExcluded)])",
+ ssqw.toQuery().toString()
+ );
+
+ ssqw = new SpanSequenceQueryWrapper("tokens", "der", "Baum").withConstraint(1,1);
+ assertEquals("spanNext(tokens:der, tokens:Baum)", ssqw.toQuery().toString());
+
+ ssqw = new SpanSequenceQueryWrapper("tokens", "der", "Baum").withConstraint(1,2, "s");
+ assertEquals("spanElementDistance(tokens:der, tokens:Baum, [(s[1:2], ordered, notExcluded)])", ssqw.toQuery().toString());
+
+ ssqw = new SpanSequenceQueryWrapper("tokens", "der", "Baum")
+ .withConstraint(1,2, "s")
+ .withConstraint(2,3, "x");
+ assertEquals("spanMultipleDistance(tokens:der, tokens:Baum, " +
+ "[(s[1:2], ordered, notExcluded), " +
+ "(x[2:3], ordered, notExcluded)])",
+ ssqw.toQuery().toString());
+
+ ssqw = new SpanSequenceQueryWrapper("tokens")
+ .append("Baum")
+ .prepend("der")
+ .withConstraint(1,2, "s", true)
+ .withConstraint(2,3, "x");
+ assertEquals("spanMultipleDistance(tokens:der, " +
+ "tokens:Baum, [(s[1:2], ordered, excluded), " +
+ "(x[2:3], ordered, notExcluded)])",
+ ssqw.toQuery().toString());
+
+
+ // Support empty class ins sequence
+ ssqw = new SpanSequenceQueryWrapper("field", "Der");
+ srqw = new SpanRepetitionQueryWrapper();
+ scqw = new SpanClassQueryWrapper(srqw, (short) 3);
+ ssqw.append(scqw);
+ assertEquals(
+ "spanExpansion(field:Der, []{1, 1}, right, class:3)",
+ ssqw.toQuery().toString()
+ );
+
+ // Support empty class ins sequence
+ ssqw = new SpanSequenceQueryWrapper("field");
+ srqw = new SpanRepetitionQueryWrapper();
+ ssqw.append(srqw);
+ scqw = new SpanClassQueryWrapper(ssqw, (short) 2);
+ try {
+ scqw.toQuery();
+ }
+ catch (Exception e) {
+ fail(e.getMessage() + "(Known issue)");
+ };
+ // assertEquals("", scqw.toQuery().toString());
+
+ /*
+
+ sssq = new SpanSequenceQueryWrapper("field");
+ sssc = new SpanClassQueryWrapper(sssq, (short) 2);
+ SpanSequenceQueryWrapper sssq2 = new SpanSequenceQueryWrapper("field");
+ sssq2.append("hui").append(sssc);
+ assertEquals("", sssq2.toQuery().toString());
+ */
};
};
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 766d6ee..fd3f239 100644
--- a/src/test/java/de/ids_mannheim/korap/query/TestSpanSequenceQueryJSON.java
+++ b/src/test/java/de/ids_mannheim/korap/query/TestSpanSequenceQueryJSON.java
@@ -14,6 +14,10 @@
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
+/**
+ * @author diewald
+ */
+
@RunWith(JUnit4.class)
public class TestSpanSequenceQueryJSON {