Added SpanWithAttributeQueryWrapper.
diff --git a/src/main/java/de/ids_mannheim/korap/KorapQuery.java b/src/main/java/de/ids_mannheim/korap/KorapQuery.java
index a64ff13..9af942d 100644
--- a/src/main/java/de/ids_mannheim/korap/KorapQuery.java
+++ b/src/main/java/de/ids_mannheim/korap/KorapQuery.java
@@ -1,6 +1,8 @@
package de.ids_mannheim.korap;
import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
import org.apache.lucene.util.automaton.RegExp;
import org.slf4j.Logger;
@@ -23,6 +25,7 @@
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.SpanWithAttributeQueryWrapper;
import de.ids_mannheim.korap.query.wrap.SpanWithinQueryWrapper;
import de.ids_mannheim.korap.response.Notifications;
import de.ids_mannheim.korap.util.QueryException;
@@ -752,28 +755,28 @@
// Branch on type
switch (type) {
case "korap:term":
- String match = "match:eq";
- if (json.has("match"))
- match = json.get("match").asText();
-
- switch (match) {
-
- case "match:ne":
- if (DEBUG)
- log.trace("Term is negated");
-
- SpanSegmentQueryWrapper ssqw =
- (SpanSegmentQueryWrapper) this._termFromJson(json);
-
- ssqw.makeNegative();
-
- return this.seg().without(ssqw);
-
- case "match:eq":
+// String match = "match:eq";
+// if (json.has("match"))
+// match = json.get("match").asText();
+//
+// switch (match) {
+//
+// case "match:ne":
+// if (DEBUG)
+// log.trace("Term is negated");
+//
+// SpanSegmentQueryWrapper ssqw =
+// (SpanSegmentQueryWrapper) this._termFromJson(json);
+//
+// ssqw.makeNegative();
+//
+// return this.seg().without(ssqw);
+//
+// case "match:eq":
return this._termFromJson(json);
- };
-
- throw new QueryException(741, "Match relation unknown");
+// };
+//
+// throw new QueryException(741, "Match relation unknown");
case "korap:termGroup":
@@ -827,12 +830,11 @@
// Deserialize korap:term
private SpanQueryWrapper _termFromJson (JsonNode json)
throws QueryException {
-
+
if (!json.has("key") || json.get("key").asText().length() < 1) {
- throw new QueryException(
- 740,
- "Key definition is missing in term or span"
- );
+ if (!json.has("attr"))
+ throw new QueryException(740,
+ "Key definition is missing in term or span");
};
if (!json.has("@type")) {
@@ -882,12 +884,6 @@
case "const":
layer = "c";
break;
-
- /*
- case "cat":
- layer = "c";
- break;
- */
};
if (isCaseInsensitive && isTerm) {
@@ -935,22 +931,103 @@
};
};
- if (isTerm)
- return this.seg(value.toString());
+ if (isTerm){
+
+ String match = "match:eq";
+ if (json.has("match")) {
+ match = json.get("match").asText();
+ }
+
+ SpanSegmentQueryWrapper ssqw = seg(value.toString());
+ if (match.equals("match:ne")) {
+ if (DEBUG) log.trace("Term is negated");
+ ssqw.makeNegative();
+ return this.seg().without(ssqw);
+ }
+ else if (match.equals("match:eq")) {
+ return ssqw;
+ }
+ else {
+ throw new QueryException(741, "Match relation unknown");
+ }
+ }
if (json.has("attr")) {
- this.addWarning(
- 768,
- "Attributes are currently not supported - results may not be correct"
- );
+ JsonNode attrNode = json.get("attr");
+ if (!attrNode.has("@type")) {
+ throw new QueryException(701,
+ "JSON-LD group has no @type attribute");
+ }
- // SpanQueryWrapper attrQueryWrapper =
- // _attrFromJson(json.get("attr"));
- // if (attrQueryWrapper != null) {
- // return seg SpanElementWithAttributeQueryWrapper
- // }
+ if (value.toString().isEmpty()) {
+ // attribute with arbitraty elements
+
+ this.addWarning(771,
+ "Arbitraty elements with attributes are currently not supported.");
+ return null;
+ }
+
+ SpanQueryWrapper elementWithIdWrapper = tag(value.toString());
+ if (elementWithIdWrapper == null) {
+ return null;
+ }
+
+ if (attrNode.get("@type").asText().equals("korap:term")) {
+ SpanQueryWrapper attrWrapper = _attrFromJson(json.get("attr"));
+ if (attrWrapper != null) {
+ return new SpanWithAttributeQueryWrapper(
+ elementWithIdWrapper, attrWrapper);
+ }
+ else {
+ throw new QueryException(747, "Attribute is null");
+ }
+ }
+ else if (attrNode.get("@type").asText().equals("korap:termGroup")) {
+ if (!attrNode.has("relation")) {
+ throw new QueryException(743, "Term group expects a relation");
+ }
+
+ if (!attrNode.has("operands")) {
+ throw new QueryException(742, "Term group needs operand list");
+ }
+
+ String relation = attrNode.get("relation").asText();
+ JsonNode operands = attrNode.get("operands");
+
+ SpanQueryWrapper attrWrapper;
+ if ("relation:and".equals(relation)) {
+ List<SpanQueryWrapper> wrapperList = new ArrayList<SpanQueryWrapper>();
+ for (JsonNode operand : operands) {
+ attrWrapper = _termFromJson(operand);
+ if (attrWrapper == null) {
+ throw new QueryException(747, "Attribute is null");
+ }
+ wrapperList.add(attrWrapper);
+ }
+
+ return new SpanWithAttributeQueryWrapper(
+ elementWithIdWrapper, wrapperList);
+ }
+ else if ("relation:or".equals(relation)){
+ SpanAlterQueryWrapper saq = new SpanAlterQueryWrapper(field);
+ for (JsonNode operand : operands) {
+ attrWrapper = _termFromJson(operand);
+ if (attrWrapper == null) {
+ throw new QueryException(747, "Attribute is null");
+ }
+ saq.or(new SpanWithAttributeQueryWrapper(
+ elementWithIdWrapper, attrWrapper));
+ }
+ return saq;
+ }
+ else {
+ throw new QueryException(716, "Unknown relation");
+ }
+ }
+ else {
+ this.addWarning(715, "Attribute type is not supported");
+ }
};
-
return this.tag(value.toString());
};
@@ -959,39 +1036,24 @@
private SpanQueryWrapper _attrFromJson (JsonNode attrNode)
throws QueryException {
- if (!attrNode.has("@type")) {
- throw new QueryException(
- 701,
- "JSON-LD group has no @type attribute"
+ if (attrNode.has("key")) {
+ return _termFromJson(attrNode);
+ }
+ else if (attrNode.has("tokenarity") || attrNode.has("arity")) {
+ this.addWarning(770, "Arity attributes are currently not supported"
+ + " - results may not be correct"
);
- };
-
- if (attrNode.get("@type").asText().equals("korap:term")) {
- if (attrNode.has("tokenarity") || attrNode.has("arity")) {
- this.addWarning(
- 770,
- "Arity attributes are currently not supported" +
- " - results may not be correct"
+ }
+ else if (attrNode.has("root")) {
+ String rootValue = attrNode.get("root").asText();
+ if (rootValue.equals("true") || rootValue.equals("false")) {
+ return new SpanAttributeQueryWrapper(
+ new SpanSimpleQueryWrapper("tokens", "@root",
+ Boolean.valueOf(rootValue))
);
- };
-
- if (attrNode.has("root")) {
- String rootValue = attrNode.get("root").asText();
- if (rootValue.equals("true") || rootValue.equals("false")) {
- return new SpanAttributeQueryWrapper(
- new SpanSimpleQueryWrapper("tokens", "@root"),
- Boolean.valueOf(rootValue)
- );
- }
- // wrong root value
}
}
- // else if termnode
- else {
- this.addWarning(715, "Attribute type is not supported");
- }
-
- return null;
+ return null;
}
diff --git a/src/main/java/de/ids_mannheim/korap/query/SpanAttributeQuery.java b/src/main/java/de/ids_mannheim/korap/query/SpanAttributeQuery.java
index 49e3cb6..5f4c262 100644
--- a/src/main/java/de/ids_mannheim/korap/query/SpanAttributeQuery.java
+++ b/src/main/java/de/ids_mannheim/korap/query/SpanAttributeQuery.java
@@ -6,6 +6,7 @@
import org.apache.lucene.index.AtomicReaderContext;
import org.apache.lucene.index.Term;
import org.apache.lucene.index.TermContext;
+import org.apache.lucene.search.spans.SpanQuery;
import org.apache.lucene.search.spans.SpanTermQuery;
import org.apache.lucene.search.spans.Spans;
import org.apache.lucene.util.Bits;
@@ -95,9 +96,8 @@
public String toString(String field) {
StringBuilder sb = new StringBuilder();
sb.append("spanAttribute(");
+ if (negation) sb.append("!");
sb.append(firstClause.toString(field));
- if (negation)
- sb.append(", not");
sb.append(")");
sb.append(ToStringUtils.boost(getBoost()));
return sb.toString();
diff --git a/src/main/java/de/ids_mannheim/korap/query/wrap/SpanAttributeQueryWrapper.java b/src/main/java/de/ids_mannheim/korap/query/wrap/SpanAttributeQueryWrapper.java
index 07ca212..87c9d90 100644
--- a/src/main/java/de/ids_mannheim/korap/query/wrap/SpanAttributeQueryWrapper.java
+++ b/src/main/java/de/ids_mannheim/korap/query/wrap/SpanAttributeQueryWrapper.java
@@ -6,15 +6,26 @@
import de.ids_mannheim.korap.query.SpanAttributeQuery;
import de.ids_mannheim.korap.util.QueryException;
+/**
+ * @author margaretha
+ * */
public class SpanAttributeQueryWrapper extends SpanQueryWrapper {
- boolean isNegation = false;
private SpanQueryWrapper subquery;
- public SpanAttributeQueryWrapper (SpanQueryWrapper sqw, boolean inclusion) {
+ public SpanAttributeQueryWrapper(SpanQueryWrapper sqw) {
+ if (sqw == null) {
+ isNull = true;
+ return;
+ }
+ if (sqw.isEmpty()) {
+ isEmpty = true;
+ return;
+ }
+
this.subquery = sqw;
- if (!inclusion) {
- this.isNegation = true;
+ if (sqw.isNegative) {
+ this.isNegative = true;
};
if (sqw.maybeUnsorted())
@@ -22,13 +33,20 @@
};
@Override
- public SpanQuery toQuery() throws QueryException {
-
+ public SpanQuery toQuery() throws QueryException {
+ if (isNull || isEmpty) return null;
+
SpanQuery sq = subquery.retrieveNode(this.retrieveNode).toQuery();
+ if (sq == null) {
+ isNull = true;
+ return null;
+ }
+
if (sq instanceof SpanTermQuery) {
- return new SpanAttributeQuery((SpanTermQuery) sq, isNegation, true);
+ return new SpanAttributeQuery((SpanTermQuery) sq, isNegative, true);
}
-
- return null; // or exception??
+ else {
+ throw new IllegalArgumentException("The subquery is not a SpanTermQuery.");
+ }
}
}
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 39c1a7d..ad72570 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
@@ -12,6 +12,11 @@
this.query = new SpanTermQuery(new Term(field, term));
};
+ public SpanSimpleQueryWrapper(String field, String term, boolean value) {
+ this(field, term);
+ this.isNegative = !value;
+ }
+
public SpanSimpleQueryWrapper (SpanQuery query) {
this.isNull = false;
this.query = query;
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 1b3d160..2126ee0 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,9 +9,6 @@
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
*
*/
@@ -102,15 +99,10 @@
@Override
public SpanQuery toQuery() throws QueryException {
-
- if (this.isNull()) {
- // if (DEBUG) log.warn("Subquery of SpanSubspanquery is null.");
- return null;
- }
+ if (this.isNull()) { return null; }
SpanQuery sq = subquery.retrieveNode(this.retrieveNode).toQuery();
- if (sq == null)
- return null;
+ if (sq == null) return null;
if (sq instanceof SpanTermQuery) {
if (subquery.isNegative()) {
return sq;
diff --git a/src/main/java/de/ids_mannheim/korap/query/wrap/SpanWithAttributeQueryWrapper.java b/src/main/java/de/ids_mannheim/korap/query/wrap/SpanWithAttributeQueryWrapper.java
new file mode 100644
index 0000000..517e4c6
--- /dev/null
+++ b/src/main/java/de/ids_mannheim/korap/query/wrap/SpanWithAttributeQueryWrapper.java
@@ -0,0 +1,120 @@
+package de.ids_mannheim.korap.query.wrap;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.lucene.search.spans.SpanQuery;
+import org.apache.lucene.search.spans.SpanTermQuery;
+
+import de.ids_mannheim.korap.query.SpanAttributeQuery;
+import de.ids_mannheim.korap.query.SpanWithAttributeQuery;
+import de.ids_mannheim.korap.query.SpanWithIdQuery;
+import de.ids_mannheim.korap.util.QueryException;
+
+public class SpanWithAttributeQueryWrapper extends SpanQueryWrapper {
+
+ private SpanQueryWrapper withIdQueryWrapper = null;
+ private SpanQueryWrapper attrQueryWrapper = null;
+ private List<SpanQueryWrapper> queryWrapperList = null;
+
+ public SpanWithAttributeQueryWrapper(SpanQueryWrapper withIdQuery,
+ SpanQueryWrapper attrQuery) {
+
+ if (withIdQuery != null || attrQuery != null) {
+ isNull = false;
+ }
+ if (withIdQuery.isEmpty || attrQuery.isEmpty()) {
+ isEmpty = true;
+ return;
+ }
+
+ this.attrQueryWrapper = attrQuery;
+ this.withIdQueryWrapper = withIdQuery;
+ }
+
+ public SpanWithAttributeQueryWrapper(SpanQueryWrapper withIdQuery,
+ List<SpanQueryWrapper> attrList) {
+
+ if (withIdQuery != null || attrList != null) {
+ isNull = false;
+ }
+ if (withIdQuery.isEmpty) {
+ isEmpty = true;
+ return;
+ }
+
+ for (SpanQueryWrapper sqw : attrList) {
+ if (sqw == null) {
+ isNull = true;
+ return;
+ }
+ if (sqw.isEmpty) {
+ isEmpty = true;
+ return;
+ }
+ }
+ if (attrList.isEmpty()) {
+ // not withattribute query, just a normal query
+ }
+ this.queryWrapperList = attrList;
+ this.withIdQueryWrapper = withIdQuery;
+ }
+
+ public SpanAttributeQuery createSpanAttributeQuery(
+ SpanQueryWrapper attrQueryWrapper) throws QueryException {
+ SpanQuery sq = attrQueryWrapper.toQuery();
+ if (sq == null) {
+ isNull = true;
+ return null;
+ }
+ if (sq instanceof SpanTermQuery) {
+ return new SpanAttributeQuery(
+ (SpanTermQuery) sq,
+ attrQueryWrapper.isNegative, true);
+ }
+ else {
+ throw new IllegalArgumentException(
+ "The subquery is not a SpanTermQuery.");
+ }
+ }
+
+ @Override
+ public SpanQuery toQuery() throws QueryException {
+
+ if (isNull || isEmpty) return null;
+
+ SpanWithIdQuery withIdQuery = (SpanWithIdQuery) withIdQueryWrapper
+ .toQuery();
+ if (withIdQuery == null) {
+ isNull = true;
+ return null;
+ }
+
+ if (attrQueryWrapper != null){
+ SpanAttributeQuery attrQuery = createSpanAttributeQuery(attrQueryWrapper);
+ if (attrQuery == null) {
+ isNull = true;
+ return null;
+ }
+ return new SpanWithAttributeQuery(withIdQuery, attrQuery, true);
+ }
+ else if (queryWrapperList != null) {
+ if (queryWrapperList.isEmpty()) {
+ return withIdQuery;
+ }
+
+ List<SpanQuery> attrQueries = new ArrayList<SpanQuery>();
+ SpanQuery attrQuery;
+ for (SpanQueryWrapper sqw : queryWrapperList) {
+ attrQuery = createSpanAttributeQuery(sqw);
+ if (attrQuery == null) {
+ isNull = true;
+ return null;
+ }
+ attrQueries.add(attrQuery);
+ }
+ return new SpanWithAttributeQuery(withIdQuery, attrQueries, true);
+ }
+ return null;
+ }
+}
diff --git a/src/test/java/de/ids_mannheim/korap/query/TestSpanWithAttributeJSON.java b/src/test/java/de/ids_mannheim/korap/query/TestSpanWithAttributeJSON.java
new file mode 100644
index 0000000..a04e6f7
--- /dev/null
+++ b/src/test/java/de/ids_mannheim/korap/query/TestSpanWithAttributeJSON.java
@@ -0,0 +1,74 @@
+package de.ids_mannheim.korap.query;
+
+import static de.ids_mannheim.korap.TestSimple.getJSONQuery;
+import static org.junit.Assert.assertEquals;
+
+import org.apache.lucene.search.spans.SpanQuery;
+import org.junit.Test;
+
+import de.ids_mannheim.korap.query.wrap.SpanQueryWrapper;
+import de.ids_mannheim.korap.util.QueryException;
+
+public class TestSpanWithAttributeJSON {
+
+ @Test
+ public void testElementSingleAttribute() throws QueryException {
+ String filepath = getClass().getResource(
+ "/queries/attribute/element-single-attribute.jsonld").getFile();
+ SpanQueryWrapper sqwi = getJSONQuery(filepath);
+ SpanQuery sq = sqwi.toQuery();
+ assertEquals(
+ "spanElementWithAttribute(<tokens:head />, spanAttribute(tokens:type:top))",
+ sq.toString());
+ }
+
+ @Test
+ public void testElementSingleNotAttribute() throws QueryException {
+ String filepath = getClass().getResource(
+ "/queries/attribute/element-single-not-attribute.jsonld")
+ .getFile();
+ SpanQueryWrapper sqwi = getJSONQuery(filepath);
+ SpanQuery sq = sqwi.toQuery();
+ assertEquals(
+ "spanElementWithAttribute(<tokens:head />, spanAttribute(!tokens:type:top))",
+ sq.toString());
+ }
+
+ @Test
+ public void testElementMultipleAndNotAttributes() throws QueryException {
+ String filepath = getClass().getResource(
+ "/queries/attribute/element-multiple-and-not-attributes.jsonld")
+ .getFile();
+ SpanQueryWrapper sqwi = getJSONQuery(filepath);
+ SpanQuery sq = sqwi.toQuery();
+ assertEquals(
+ "spanElementWithAttribute(<tokens:div />, [spanAttribute(tokens:type:Zeitschrift), "
+ + "spanAttribute(!tokens:complete:Y), spanAttribute(tokens:n:0)])",
+ sq.toString());
+ }
+
+ @Test
+ public void testElementMultipleOrAttributes() throws QueryException {
+ String filepath = getClass().getResource(
+ "/queries/attribute/element-multiple-or-attributes.jsonld")
+ .getFile();
+ SpanQueryWrapper sqwi = getJSONQuery(filepath);
+ SpanQuery sq = sqwi.toQuery();
+ assertEquals(
+ "spanOr([spanElementWithAttribute(<tokens:div />, spanAttribute(tokens:type:Zeitschrift)), "
+ + "spanElementWithAttribute(<tokens:div />, spanAttribute(tokens:complete:Y)), "
+ + "spanElementWithAttribute(<tokens:div />, spanAttribute(tokens:n:0))])",
+ sq.toString());
+ }
+
+ @Test
+ public void testAnyElementWithAttribute() throws QueryException {
+ String filepath = getClass().getResource(
+ "/queries/attribute/any-element-with-attribute.jsonld")
+ .getFile();
+ SpanQueryWrapper sqwi = getJSONQuery(filepath);
+ // SpanQuery sq = sqwi.toQuery();
+ assertEquals(null, sqwi);
+ }
+
+}
diff --git a/src/test/resources/queries/attribute/any-element-with-attribute.jsonld b/src/test/resources/queries/attribute/any-element-with-attribute.jsonld
new file mode 100644
index 0000000..25b39b4
--- /dev/null
+++ b/src/test/resources/queries/attribute/any-element-with-attribute.jsonld
@@ -0,0 +1,19 @@
+{
+ "@context": "http://ids-mannheim.de/ns/KorAP/json-ld/v0.2/context.jsonld",
+ "errors": [],
+ "warnings": [],
+ "messages": [],
+ "collection": {},
+ "query": {
+ "@type": "korap:span",
+ "attr": {
+ "@type": "korap:term",
+ "arity": {
+ "@type": "korap:boundary",
+ "min": 2,
+ "max": 2
+ }
+ }
+ },
+ "meta": {}
+}
\ No newline at end of file
diff --git a/src/test/resources/queries/attribute/element-multiple-and-not-attributes.jsonld b/src/test/resources/queries/attribute/element-multiple-and-not-attributes.jsonld
new file mode 100644
index 0000000..cd18b96
--- /dev/null
+++ b/src/test/resources/queries/attribute/element-multiple-and-not-attributes.jsonld
@@ -0,0 +1,36 @@
+{
+ "@context": "http://ids-mannheim.de/ns/KorAP/json-ld/v0.2/context.jsonld",
+ "errors": [],
+ "warnings": [],
+ "messages": [],
+ "collection": {},
+ "query": {
+ "@type": "korap:span",
+ "key": "div",
+ "attr": {
+ "@type": "korap:termGroup",
+ "relation": "relation:and",
+ "operands": [
+ {
+ "@type": "korap:term",
+ "layer": "type",
+ "key": "Zeitschrift",
+ "match": "match:eq"
+ },
+ {
+ "@type": "korap:term",
+ "layer": "complete",
+ "key": "Y",
+ "match": "match:ne"
+ },
+ {
+ "@type": "korap:term",
+ "layer": "n",
+ "key": "0",
+ "match": "match:eq"
+ }
+ ]
+ }
+ },
+ "meta": {}
+}
diff --git a/src/test/resources/queries/attribute/element-multiple-or-attributes.jsonld b/src/test/resources/queries/attribute/element-multiple-or-attributes.jsonld
new file mode 100644
index 0000000..c194f21
--- /dev/null
+++ b/src/test/resources/queries/attribute/element-multiple-or-attributes.jsonld
@@ -0,0 +1,36 @@
+{
+ "@context": "http://ids-mannheim.de/ns/KorAP/json-ld/v0.2/context.jsonld",
+ "errors": [],
+ "warnings": [],
+ "messages": [],
+ "collection": {},
+ "query": {
+ "@type": "korap:span",
+ "key": "div",
+ "attr": {
+ "@type": "korap:termGroup",
+ "relation": "relation:or",
+ "operands": [
+ {
+ "@type": "korap:term",
+ "layer": "type",
+ "key": "Zeitschrift",
+ "match": "match:eq"
+ },
+ {
+ "@type": "korap:term",
+ "layer": "complete",
+ "key": "Y",
+ "match": "match:eq"
+ },
+ {
+ "@type": "korap:term",
+ "layer": "n",
+ "key": "0",
+ "match": "match:eq"
+ }
+ ]
+ }
+ },
+ "meta": {}
+}
diff --git a/src/test/resources/queries/attribute/element-single-attribute.jsonld b/src/test/resources/queries/attribute/element-single-attribute.jsonld
new file mode 100644
index 0000000..9f21989
--- /dev/null
+++ b/src/test/resources/queries/attribute/element-single-attribute.jsonld
@@ -0,0 +1,18 @@
+{
+ "@context": "http://ids-mannheim.de/ns/KorAP/json-ld/v0.2/context.jsonld",
+ "errors": [],
+ "warnings": [],
+ "messages": [],
+ "collection": {},
+ "query": {
+ "@type": "korap:span",
+ "key": "head",
+ "attr": {
+ "@type": "korap:term",
+ "layer": "type",
+ "key": "top",
+ "match": "match:eq"
+ }
+ },
+ "meta": {}
+}
\ No newline at end of file
diff --git a/src/test/resources/queries/attribute/element-single-not-attribute.jsonld b/src/test/resources/queries/attribute/element-single-not-attribute.jsonld
new file mode 100644
index 0000000..c4e8714
--- /dev/null
+++ b/src/test/resources/queries/attribute/element-single-not-attribute.jsonld
@@ -0,0 +1,18 @@
+{
+ "@context": "http://ids-mannheim.de/ns/KorAP/json-ld/v0.2/context.jsonld",
+ "errors": [],
+ "warnings": [],
+ "messages": [],
+ "collection": {},
+ "query": {
+ "@type": "korap:span",
+ "key": "head",
+ "attr": {
+ "@type": "korap:term",
+ "layer": "type",
+ "key": "top",
+ "match": "match:ne"
+ }
+ },
+ "meta": {}
+}
\ No newline at end of file