Updates to collection queries

Change-Id: I8cdc07f9d6358ae29337c4b6fe5dd828d1efd1cc
diff --git a/Changes b/Changes
index 78efcbf..e9644b4 100644
--- a/Changes
+++ b/Changes
@@ -1,4 +1,4 @@
-0.52 2015-06-24
+0.52 2015-06-25
         - [bugfix] Fixed payload filtering in FocusSpans (margaretha)
 	- [workaround] Reintroduced empty collection support,
 	  as Koral still creates them (diewald)
@@ -10,6 +10,11 @@
 	- [bugfix] Mirror collection (diewald)
 	- [bugfix] Updated default fields for meta data (diewald)
 	- [bugfix] Updated match identifier for "sigle" data (diewald)
+	- [workaround] Support corpusID/docID and textSigle match strings
+	  (diewald)
+	- [workaround] Support matches starting with "contains"
+	  (Kustvakt bug/diewald)
+	- [bugfix] Fixed treatment of several collection types (diewald)
 
 0.51 2015-03-17
         - This is a major version (prepared for the GitHub release)
diff --git a/src/main/java/de/ids_mannheim/korap/KrillCollection.java b/src/main/java/de/ids_mannheim/korap/KrillCollection.java
index 5d7d736..85a82f1 100644
--- a/src/main/java/de/ids_mannheim/korap/KrillCollection.java
+++ b/src/main/java/de/ids_mannheim/korap/KrillCollection.java
@@ -7,6 +7,7 @@
 import de.ids_mannheim.korap.util.KrillDate;
 import de.ids_mannheim.korap.util.QueryException;
 import de.ids_mannheim.korap.collection.BooleanFilter;
+import de.ids_mannheim.korap.collection.RegexFilter;
 import de.ids_mannheim.korap.collection.FilterOperation;
 import de.ids_mannheim.korap.collection.CollectionBuilder;
 import de.ids_mannheim.korap.response.Notifications;
@@ -218,19 +219,54 @@
                         bfilter.till(dateStr);
                         break;
                 };
+
                 // No good reason for gt or lt
                 return bfilter;
             }
 
+            // Filter based on string
             else if (valtype.equals("type:string")) {
                 if (json.has("match"))
                     match = json.get("match").asText();
 
-                if (match.equals("match:eq"))
+                if (match.equals("match:eq")) {
                     bfilter.and(key, json.get("value").asText());
+                }
+                else if (match.equals("match:ne")) {
+                    bfilter.andNot(key, json.get("value").asText());
+                }
+                else {
+                    // TODO!
+                    throw new QueryException(0,"Unknown match type");
+                };
 
                 return bfilter;
+            }
+
+            // Filter based on regex
+            else if (valtype.equals("type:regex")) {
+                if (json.has("match"))
+                    match = json.get("match").asText();
+
+                if (match.equals("match:eq")) {
+                    return bfilter.and(
+                        key,
+                        new RegexFilter(json.get("value").asText())
+                    );
+                }
+                else if (match.equals("match:ne")) {
+                    return bfilter.andNot(
+                        key,
+                        new RegexFilter(json.get("value").asText())
+                    );
+                };
+
+                // TODO! for excludes and contains
+                throw new QueryException(0,"Unknown document type");
             };
+
+            // TODO!
+            throw new QueryException(0,"Unknown document operation");
         }
 
         // nested group
@@ -264,7 +300,7 @@
             throw new QueryException(613,
                     "Collection query type has to be doc or docGroup");
 
-        return new BooleanFilter();
+        // return new BooleanFilter();
     };
 
 
diff --git a/src/main/java/de/ids_mannheim/korap/KrillIndex.java b/src/main/java/de/ids_mannheim/korap/KrillIndex.java
index 25dfd0d..b0120e7 100644
--- a/src/main/java/de/ids_mannheim/korap/KrillIndex.java
+++ b/src/main/java/de/ids_mannheim/korap/KrillIndex.java
@@ -815,10 +815,20 @@
 
         // Create a filter based on the corpusID and the docID
         BooleanQuery bool = new BooleanQuery();
-        bool.add(new TermQuery(new Term("ID", match.getDocID())),
-                BooleanClause.Occur.MUST);
-        bool.add(new TermQuery(new Term("corpusID", match.getCorpusID())),
-                BooleanClause.Occur.MUST);
+        if (match.getTextSigle() != null) {
+            bool.add(new TermQuery(
+                new Term("textSigle", match.getTextSigle())
+            ), BooleanClause.Occur.MUST);
+        }
+
+        // LEGACY
+        else {
+            bool.add(new TermQuery(new Term("ID", match.getDocID())),
+                     BooleanClause.Occur.MUST);
+            bool.add(new TermQuery(new Term("corpusID", match.getCorpusID())),
+                     BooleanClause.Occur.MUST);
+        };
+
         Filter filter = (Filter) new QueryWrapperFilter(bool);
 
         CompiledAutomaton fst = null;
@@ -839,6 +849,8 @@
             if (includeSpans)
                 regex.append("((\">\"|\"<\"\">\")\":\")?");
 
+System.err.println("1 >>>>>>> " + regex);
+
             // There is a foundry given
             if (foundry != null && foundry.size() > 0) {
 
@@ -851,6 +863,8 @@
                     };
                 };
 
+System.err.println("2 >>>>>>> " + regex);
+
                 // Build regex for multiple foundries
                 if (foundry.size() > 0) {
                     regex.append("(");
@@ -874,6 +888,8 @@
                             };
                         };
 
+System.err.println("3 >>>>>>> " + regex);
+
                         // Build regex for multiple layers
                         if (layer.size() > 0) {
                             regex.append("(");
@@ -898,14 +914,21 @@
             };
             regex.append("(.){1,}|_[0-9]+");
 
+System.err.println("5 >>>>>>> " + regex);
+
             if (DEBUG)
                 log.trace("The final regexString is {}", regex.toString());
+
+System.err.println("6 >>>>>>> " + regex.toString());
+
             RegExp regexObj = new RegExp(regex.toString(), RegExp.COMPLEMENT);
             fst = new CompiledAutomaton(regexObj.toAutomaton());
             if (DEBUG)
                 log.trace("The final regexObj is {}", regexObj.toString());
         };
 
+        System.err.println("++++++++++++++++++++++++++++++++++++++++++");
+
         try {
             // Iterate over all atomic indices and find the matching document
             for (AtomicReaderContext atomic : this.reader().leaves()) {
diff --git a/src/main/java/de/ids_mannheim/korap/collection/BooleanFilter.java b/src/main/java/de/ids_mannheim/korap/collection/BooleanFilter.java
index 2f6853d..19693c6 100644
--- a/src/main/java/de/ids_mannheim/korap/collection/BooleanFilter.java
+++ b/src/main/java/de/ids_mannheim/korap/collection/BooleanFilter.java
@@ -20,7 +20,6 @@
 
 
 /*
-  Todo: !not
   THIS IS LIMITED TO PUBDATE AT THE MOMENT AND COMPLETELY LEGACY!
 */
 
@@ -114,6 +113,33 @@
     };
 
 
+    public BooleanFilter andNot (String type, String ... terms) {
+        for (String term : terms) {
+            bool.add(new TermQuery(new Term(type, term)),
+                    BooleanClause.Occur.MUST_NOT);
+        };
+        return this;
+    };
+
+
+    public BooleanFilter andNot (String type, RegexFilter value) {
+        bool.add(value.toQuery(type), BooleanClause.Occur.MUST_NOT);
+        return this;
+    };
+
+
+    public BooleanFilter andNot (BooleanFilter bf) {
+        if (bf.bool.clauses().size() == 1) {
+            BooleanClause bc = bf.bool.getClauses()[0];
+            bc.setOccur(BooleanClause.Occur.MUST_NOT);
+            bool.add(bc);
+            return this;
+        }
+        bool.add(bf.toQuery(), BooleanClause.Occur.MUST_NOT);
+        return this;
+    };
+
+
     public BooleanFilter since (String dateStr) {
         int since = new KrillDate(dateStr).floor();
 
diff --git a/src/main/java/de/ids_mannheim/korap/index/AbstractDocument.java b/src/main/java/de/ids_mannheim/korap/index/AbstractDocument.java
index bf5f243..234dded 100644
--- a/src/main/java/de/ids_mannheim/korap/index/AbstractDocument.java
+++ b/src/main/java/de/ids_mannheim/korap/index/AbstractDocument.java
@@ -86,8 +86,12 @@
      */
     @JsonProperty("pubDate")
     public String getPubDateString () {
-        if (this.pubDate != null)
-            return this.pubDate.toDisplay();
+        if (this.pubDate != null) {
+            String date = this.pubDate.toDisplay();
+            if (date.length() == 0)
+                return null;
+            return date;
+        };
         return null;
     };
 
diff --git a/src/main/java/de/ids_mannheim/korap/response/Match.java b/src/main/java/de/ids_mannheim/korap/response/Match.java
index f68e389..3afc4e5 100644
--- a/src/main/java/de/ids_mannheim/korap/response/Match.java
+++ b/src/main/java/de/ids_mannheim/korap/response/Match.java
@@ -151,8 +151,15 @@
     public Match (String idString, boolean includeHighlights) {
         MatchIdentifier id = new MatchIdentifier(idString);
         if (id.getStartPos() > -1) {
+
+            if (id.getTextSigle() != null)
+                this.setTextSigle(id.getTextSigle());
+
+            // <legacy>
             this.setCorpusID(id.getCorpusID());
             this.setDocID(id.getDocID());
+            // </legacy>
+
             this.setStartPos(id.getStartPos());
             this.setEndPos(id.getEndPos());
 
@@ -640,7 +647,7 @@
      * @see MatchIdentifier
      */
     @Override
-    @JsonProperty("ID")
+    @JsonProperty("matchID")
     public String getID () {
 
         // Identifier already given
@@ -1429,6 +1436,8 @@
 
 
     // Return match as token list
+    // TODO: This will be retrieved in case "tokenList" is
+    //       requested in "fields"
     public ObjectNode toTokenList () {
         ObjectNode json = mapper.createObjectNode();
 
diff --git a/src/main/java/de/ids_mannheim/korap/response/Message.java b/src/main/java/de/ids_mannheim/korap/response/Message.java
index cef0150..f8f598b 100644
--- a/src/main/java/de/ids_mannheim/korap/response/Message.java
+++ b/src/main/java/de/ids_mannheim/korap/response/Message.java
@@ -21,6 +21,7 @@
  * @author Nils Diewald
  * @see de.ids_mannheim.korap.response.Messages
  */
+@JsonAutoDetect
 public class Message implements Cloneable {
     // Mapper for JSON serialization
     ObjectMapper mapper = new ObjectMapper();
diff --git a/src/main/java/de/ids_mannheim/korap/response/SearchContext.java b/src/main/java/de/ids_mannheim/korap/response/SearchContext.java
index 91ebaa6..223fbd2 100644
--- a/src/main/java/de/ids_mannheim/korap/response/SearchContext.java
+++ b/src/main/java/de/ids_mannheim/korap/response/SearchContext.java
@@ -56,12 +56,14 @@
     public SearchContext setSpanContext (String spanContext) {
         this.spanType = true;
 
+        // <LEGACY>
         if (spanContext.equals("sentence")) {
             spanContext = "s";
         }
         else if (spanContext.equals("paragraph")) {
             spanContext = "p";
         };
+        // </LEGACY>
 
         this.spanContext = spanContext;
         return this;
diff --git a/src/main/java/de/ids_mannheim/korap/response/match/MatchIdentifier.java b/src/main/java/de/ids_mannheim/korap/response/match/MatchIdentifier.java
index b559b67..37a6c03 100644
--- a/src/main/java/de/ids_mannheim/korap/response/match/MatchIdentifier.java
+++ b/src/main/java/de/ids_mannheim/korap/response/match/MatchIdentifier.java
@@ -8,7 +8,8 @@
 
     private ArrayList<int[]> pos = new ArrayList<>(8);
 
-    Pattern idRegex = Pattern.compile("^match-(?:([^!]+?)[!\\.])?"
+    // TODO: "contains" is necessary for a compatibility bug in Kustvakt
+    Pattern idRegex = Pattern.compile("^(?:match-|contains-)(?:([^!]+?)[!\\.])?"
             + "([^!]+)-p([0-9]+)-([0-9]+)"
             + "((?:\\(-?[0-9]+\\)-?[0-9]+--?[0-9]+)*)" + "(?:c.+?)?$");
     Pattern posRegex = Pattern.compile("\\(([0-9]+)\\)([0-9]+)-([0-9]+)");
@@ -18,13 +19,24 @@
 
 
     public MatchIdentifier (String id) {
+
+        // Replace for legacy reasons with incompatible versions of Kustvakt
+        id = id.replaceAll("^(contains-|match-)([^-!_\\.]+?)!\\2_", "$1$2_");
+
         Matcher matcher = idRegex.matcher(id);
         if (matcher.matches()) {
-            this.setCorpusID(matcher.group(1));
-            this.setDocID(matcher.group(2));
-            // TODO! FIXME!
 
-            this.setTextSigle(this.getCorpusID() + "." + this.getDocID());
+            // <legacy>
+            // and test compatibility
+            if (id.contains("!") || !id.contains("_")) {
+                this.setCorpusID(matcher.group(1));
+                this.setDocID(matcher.group(2));
+            }
+            // </legacy>
+            else {
+                // this.getCorpusID() + "." + this.getDocID()
+                this.setTextSigle(matcher.group(1) + '.' + matcher.group(2));
+            };
 
             this.setStartPos(Integer.parseInt(matcher.group(3)));
             this.setEndPos(Integer.parseInt(matcher.group(4)));
diff --git a/src/test/java/de/ids_mannheim/korap/collection/TestKrillCollectionJSON.java b/src/test/java/de/ids_mannheim/korap/collection/TestKrillCollectionJSON.java
index 41ce2dd..ba5a8f6 100644
--- a/src/test/java/de/ids_mannheim/korap/collection/TestKrillCollectionJSON.java
+++ b/src/test/java/de/ids_mannheim/korap/collection/TestKrillCollectionJSON.java
@@ -24,7 +24,6 @@
 
     final String path = "/queries/collections/";
 
-
     @Test
     public void collection1 () {
         String metaQuery = _getJSONString("collection_1.jsonld");
@@ -43,7 +42,6 @@
                         + "[19900000 TO 99999999] +pubDate:[0 TO 20061099])); ");
     };
 
-
     @Test
     public void collection3 () {
         String metaQuery = _getJSONString("collection_3.jsonld");
@@ -61,6 +59,39 @@
     };
 
 
+    @Test
+    public void collectionWithRegex () {
+        String query = _getJSONString("collection_7.jsonld");
+        Krill ks = new Krill(query);
+        assertFalse(ks.hasErrors());
+        assertFalse(ks.hasWarnings());
+        assertFalse(ks.hasMessages());
+        assertEquals("filter with QueryWrapperFilter(+author:/Goethe/); ", ks.getCollection().toString());
+    };
+
+
+    @Test
+    public void collectionWithNegativeRegex () {
+        String query = _getJSONString("collection_negregex.jsonld");
+        Krill ks = new Krill(query);
+        assertFalse(ks.hasErrors());
+        assertFalse(ks.hasWarnings());
+        assertFalse(ks.hasMessages());
+        assertEquals("filter with QueryWrapperFilter(-author:/Goethe/); ", ks.getCollection().toString());
+    };
+
+    @Test
+    public void collectionWithNegativeString () {
+        String query = _getJSONString("collection_ne.jsonld");
+        Krill ks = new Krill(query);
+        assertFalse(ks.hasErrors());
+        assertFalse(ks.hasWarnings());
+        assertFalse(ks.hasMessages());
+        assertEquals("filter with QueryWrapperFilter(-author:Goethe); ", ks.getCollection().toString());
+    };
+
+
+
     @Ignore
     public void nocollectiontypegiven () {
         String metaQuery = _getJSONString("multiterm_rewrite_collection.jsonld");
diff --git a/src/test/java/de/ids_mannheim/korap/index/TestMatchIdentifier.java b/src/test/java/de/ids_mannheim/korap/index/TestMatchIdentifier.java
index a77a82b..9258455 100644
--- a/src/test/java/de/ids_mannheim/korap/index/TestMatchIdentifier.java
+++ b/src/test/java/de/ids_mannheim/korap/index/TestMatchIdentifier.java
@@ -20,6 +20,9 @@
 import de.ids_mannheim.korap.response.Match;
 import de.ids_mannheim.korap.util.QueryException;
 
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.JsonNode;
+
 import de.ids_mannheim.korap.index.FieldDocument;
 
 @RunWith(JUnit4.class)
@@ -135,17 +138,15 @@
                 "... [{f/m:acht:b}{f/m:neun:a}] ...", km.getSnippetBrackets());
 
 
-        /*
         km = ki.getMatchInfo("match-c1!d1-p7-9(0)8-8(2)7-8",
         		     "tokens",
         		     "f",
         		     null,
         		     false,
         		     false);
-
-        System.err.println(km.toJSON());
-        */
-
+        assertEquals("SnippetBrackets (1b)",
+                "... [{f/m:acht:{f/y:eight:b}}{f/m:neun:{f/y:nine:a}}] ...",
+                km.getSnippetBrackets());
 
         km = ki.getMatchInfo("match-c1!d1-p7-9(0)8-8(2)7-8", "tokens", "f",
                 "m", false, true);
@@ -180,6 +181,16 @@
                 + "</mark>" + "<span class=\"context-right\">"
                 + "<span class=\"more\">" + "</span>" + "</span>",
                 km.getSnippetHTML());
+
+        ObjectMapper mapper = new ObjectMapper();
+        JsonNode res = mapper.readTree(km.toJsonString());
+        assertEquals("tokens", res.at("/field").asText());
+        assertTrue(res.at("/startMore").asBoolean());
+        assertTrue(res.at("/endMore").asBoolean());
+        assertEquals("c1", res.at("/corpusID").asText());
+        assertEquals("d1", res.at("/docID").asText());
+        assertEquals("match-c1!d1-p7-9(4)8-8(2)7-8", res.at("/matchID").asText());
+        assertTrue(res.at("/pubDate").isMissingNode());
     };
 
 
diff --git a/src/test/java/de/ids_mannheim/korap/search/TestMetaFields.java b/src/test/java/de/ids_mannheim/korap/search/TestMetaFields.java
index 7f74b95..c43c77a 100644
--- a/src/test/java/de/ids_mannheim/korap/search/TestMetaFields.java
+++ b/src/test/java/de/ids_mannheim/korap/search/TestMetaFields.java
@@ -86,8 +86,8 @@
         assertEquals("A", res.at("/matches/0/title").asText());
         assertEquals("WPD_AAA.00001", res.at("/matches/0/docID").asText());
         assertTrue(res.at("/matches/0/textSigle").isMissingNode());
-        assertEquals("match-WPD_AAA.00001-p6-7", res.at("/matches/0/ID")
-                .asText());
+        assertEquals("match-WPD_AAA.00001-p6-7", res.at("/matches/0/matchID").asText());
+        // assertEquals("p6-7", res.at("/matches/0/matchID").asText());
         assertEquals("", res.at("/matches/0/subTitle").asText());
         assertEquals("", res.at("/matches/0/textClass").asText());
         assertEquals("", res.at("/matches/0/pubPlace").asText());
@@ -129,6 +129,9 @@
                 "base/s=spans cnx/c=spans cnx/l=tokens cnx/m=tokens cnx/p=tokens cnx/s=spans cnx/syn=tokens corenlp/c=spans corenlp/ne=tokens corenlp/p=tokens corenlp/s=spans glemm/l=tokens mate/l=tokens mate/m=tokens mate/p=tokens opennlp/p=tokens opennlp/s=spans tt/l=tokens tt/p=tokens tt/s=spans xip/c=spans xip/l=tokens xip/p=tokens xip/s=spans",
                 res.at("/matches/0/layerInfos").asText());
         assertTrue(res.at("/matches/0/textType").isMissingNode());
+        assertEquals("match-GOE_AGX.00002-p7-8", res.at("/matches/0/matchID")
+                .asText());
+
 
         // All fields
         jsonString = getString(getClass().getResource(
@@ -157,7 +160,20 @@
                 res.at("/matches/0/docTitle").asText());
         assertEquals("1827", res.at("/matches/0/creationDate").asText());
         assertEquals("372-377", res.at("/matches/0/pages").asText());
-        assertEquals("match-GOE_AGX.00002-p7-8", res.at("/matches/0/ID")
+        assertEquals("match-GOE_AGX.00002-p7-8", res.at("/matches/0/matchID")
                 .asText());
     };
+
+
+    @Test
+    public void searchMetaContext () throws IOException {
+
+        // All fields
+        String jsonString = getString(getClass().getResource(
+                "/queries/metas/context_paragraph.jsonld").getFile());
+
+        Krill ks = new Krill(jsonString);
+        assertTrue(ks.getMeta().getContext().isSpanDefined());
+        assertEquals("base/p", ks.getMeta().getContext().getSpanContext());
+    };
 };
diff --git a/src/test/java/de/ids_mannheim/korap/search/TestResult.java b/src/test/java/de/ids_mannheim/korap/search/TestResult.java
index 6d9a4d5..88e3240 100644
--- a/src/test/java/de/ids_mannheim/korap/search/TestResult.java
+++ b/src/test/java/de/ids_mannheim/korap/search/TestResult.java
@@ -75,7 +75,7 @@
         */
         assertEquals(1, res.at("/matches/0/UID").asInt());
         assertEquals("doc-1", res.at("/matches/0/docID").asText());
-        assertEquals("match-doc-1-p0-1(1)0-0", res.at("/matches/0/ID").asText());
+        assertEquals("match-doc-1-p0-1(1)0-0", res.at("/matches/0/matchID").asText());
         assertEquals(
                 "<span class=\"context-left\"></span><mark><mark class=\"class-1 level-0\">a</mark></mark><span class=\"context-right\">bab</span>",
                 res.at("/matches/0/snippet").asText());
@@ -88,7 +88,7 @@
         */
         assertEquals(2, res.at("/matches/6/UID").asInt());
         assertEquals("doc-2", res.at("/matches/6/docID").asText());
-        assertEquals("match-doc-2-p2-3(1)2-2", res.at("/matches/6/ID").asText());
+        assertEquals("match-doc-2-p2-3(1)2-2", res.at("/matches/6/matchID").asText());
         assertEquals(
                 "<span class=\"context-left\">ab</span><mark><mark class=\"class-1 level-0\">a</mark></mark><span class=\"context-right\"></span>",
                 res.at("/matches/6/snippet").asText());
@@ -195,7 +195,7 @@
         // Matches
         assertEquals(1, res.at("/matches/0/UID").asInt());
         assertEquals("doc-1", res.at("/matches/0/docID").asText());
-        assertEquals("match-doc-1-p0-1", res.at("/matches/0/ID").asText());
+        assertEquals("match-doc-1-p0-1", res.at("/matches/0/matchID").asText());
         assertEquals(
                 "<span class=\"context-left\"></span><mark>a</mark><span class=\"context-right\">bab</span>",
                 res.at("/matches/0/snippet").asText());
diff --git a/src/test/resources/queries/collections/collection_7.jsonld b/src/test/resources/queries/collections/collection_7.jsonld
new file mode 100644
index 0000000..f78b14c
--- /dev/null
+++ b/src/test/resources/queries/collections/collection_7.jsonld
@@ -0,0 +1,26 @@
+{
+  "@context":"http://korap.ids-mannheim.de/ns/koral/0.3/context.jsonld",
+  "errors":[],
+  "warnings":[],
+  "messages":[],
+  "query":{
+    "@type":"koral:token",
+    "wrap":{
+      "@type":"koral:term",
+      "layer":"orth",
+      "key":"der",
+      "match":"match:eq",	
+      "foundry":"opennlp"
+    }
+  },
+  "collection":{
+    "value":"Goethe",
+    "match":"match:eq",
+    "type":"type:regex",
+    "key":"author",
+    "@type":"koral:doc"
+  },
+  "meta":{
+    "cutOff":null
+  }
+}
diff --git a/src/test/resources/queries/collections/collection_8.jsonld b/src/test/resources/queries/collections/collection_8.jsonld
new file mode 100644
index 0000000..06ba1ab
--- /dev/null
+++ b/src/test/resources/queries/collections/collection_8.jsonld
@@ -0,0 +1,26 @@
+{
+  "@context":"http://korap.ids-mannheim.de/ns/koral/0.3/context.jsonld",
+  "errors":[],
+  "warnings":[],
+  "messages":[],
+  "query":{
+    "@type":"koral:token",
+    "wrap":{
+      "@type":"koral:term",
+      "layer":"orth",
+      "key":"der",
+      "match":"match:eq",	
+      "foundry":"opennlp"
+    }
+  },
+  "collection":{
+    "value":"Goethe",
+    "match":"match:contains",
+    "type":"type:regex",
+    "key":"author",
+    "@type":"koral:doc"
+  },
+  "meta":{
+    "cutOff":null
+  }
+}
diff --git a/src/test/resources/queries/collections/collection_ne.jsonld b/src/test/resources/queries/collections/collection_ne.jsonld
new file mode 100644
index 0000000..f9b0e79
--- /dev/null
+++ b/src/test/resources/queries/collections/collection_ne.jsonld
@@ -0,0 +1,26 @@
+{
+  "@context":"http://korap.ids-mannheim.de/ns/koral/0.3/context.jsonld",
+  "errors":[],
+  "warnings":[],
+  "messages":[],
+  "query":{
+    "@type":"koral:token",
+    "wrap":{
+      "@type":"koral:term",
+      "layer":"orth",
+      "key":"der",
+      "match":"match:eq",	
+      "foundry":"opennlp"
+    }
+  },
+  "collection":{
+    "value":"Goethe",
+    "match":"match:ne",
+    "type":"type:string",
+    "key":"author",
+    "@type":"koral:doc"
+  },
+  "meta":{
+    "cutOff":null
+  }
+}
diff --git a/src/test/resources/queries/collections/collection_negregex.jsonld b/src/test/resources/queries/collections/collection_negregex.jsonld
new file mode 100644
index 0000000..fe1ecc3
--- /dev/null
+++ b/src/test/resources/queries/collections/collection_negregex.jsonld
@@ -0,0 +1,26 @@
+{
+  "@context":"http://korap.ids-mannheim.de/ns/koral/0.3/context.jsonld",
+  "errors":[],
+  "warnings":[],
+  "messages":[],
+  "query":{
+    "@type":"koral:token",
+    "wrap":{
+      "@type":"koral:term",
+      "layer":"orth",
+      "key":"der",
+      "match":"match:eq",	
+      "foundry":"opennlp"
+    }
+  },
+  "collection":{
+    "value":"Goethe",
+    "match":"match:ne",
+    "type":"type:regex",
+    "key":"author",
+    "@type":"koral:doc"
+  },
+  "meta":{
+    "cutOff":null
+  }
+}
diff --git a/src/test/resources/queries/metas/context_paragraph.jsonld b/src/test/resources/queries/metas/context_paragraph.jsonld
new file mode 100644
index 0000000..7a5e7f3
--- /dev/null
+++ b/src/test/resources/queries/metas/context_paragraph.jsonld
@@ -0,0 +1 @@
+{"@context":"http://korap.ids-mannheim.de/ns/koral/0.3/context.jsonld","errors":[],"warnings":[],"messages":[],"collection":{},"query":{"@type":"koral:token","wrap":{"@type":"koral:term","layer":"orth","key":"frage","match":"match:eq","flags":["flags:caseInsensitive"],"foundry":"opennlp"}},"meta":{"startPage":1,"count":25,"cutOff":true,"context":"base/p"}}
\ No newline at end of file