Added a Krill API returning textSigles for a given corpus query

Change-Id: Ifb02a000f47cfcda00733435149f0fd202b8cfbc
diff --git a/Changes b/Changes
index d58d561..4c54d21 100644
--- a/Changes
+++ b/Changes
@@ -1,8 +1,10 @@
-0.60.2 2022-01-11
+0.60.2 2022-01-31
     - [security] More log4j updates (diewald)
     - [feature] Support for field value vector method (fixes #81; diewald)
-	- [cleanup] Moved and updated cache-tests from TestKrillCollectionIndex to
-      TestVirtualCorpusCache (resolved #44; margaretha)
+	- [cleanup] Moved and updated cache-tests from TestKrillCollectionIndex 
+	  to TestVirtualCorpusCache (resolved #44; margaretha)
+    - [feature] Added a Krill API returning textSigles for a given 
+      corpus query (margaretha)  
 
 0.60.1 2021-12-17
     - [feature] Added vc loading from classpath (margaretha)
diff --git a/src/main/java/de/ids_mannheim/korap/Krill.java b/src/main/java/de/ids_mannheim/korap/Krill.java
index dc2b350..01e20ed 100644
--- a/src/main/java/de/ids_mannheim/korap/Krill.java
+++ b/src/main/java/de/ids_mannheim/korap/Krill.java
@@ -1,6 +1,7 @@
 package de.ids_mannheim.korap;
 
 import java.io.IOException;
+import java.util.List;
 
 import org.apache.lucene.search.spans.SpanQuery;
 
@@ -11,6 +12,7 @@
 import de.ids_mannheim.korap.query.wrap.SpanQueryWrapper;
 import de.ids_mannheim.korap.response.Response;
 import de.ids_mannheim.korap.response.Result;
+import de.ids_mannheim.korap.response.VirtualCorpusResponse;
 import de.ids_mannheim.korap.util.QueryException;
 
 /**
@@ -337,4 +339,12 @@
         this.spanQuery = sq;
         
     }
+
+    public JsonNode retrieveFieldValues (String corpusQuery, KrillIndex index,
+            String fieldName) {
+        KrillCollection kc = new KrillCollection(corpusQuery);
+        List<String> fieldValues = index.getFieldVector(fieldName, kc);
+        VirtualCorpusResponse r = new VirtualCorpusResponse();
+        return r.createKoralQueryForField(fieldName, fieldValues);
+    }
 };
diff --git a/src/main/java/de/ids_mannheim/korap/response/Response.java b/src/main/java/de/ids_mannheim/korap/response/Response.java
index 3d27919..21712c7 100644
--- a/src/main/java/de/ids_mannheim/korap/response/Response.java
+++ b/src/main/java/de/ids_mannheim/korap/response/Response.java
@@ -1,20 +1,23 @@
 package de.ids_mannheim.korap.response;
 
-import java.util.*;
-import java.io.*;
+import static de.ids_mannheim.korap.util.KrillString.quote;
 
-import com.fasterxml.jackson.annotation.*;
+import java.util.HashMap;
+import java.util.List;
+
+import com.fasterxml.jackson.annotation.JsonIgnore;
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+import com.fasterxml.jackson.annotation.JsonInclude;
 import com.fasterxml.jackson.annotation.JsonInclude.Include;
 import com.fasterxml.jackson.databind.JsonNode;
 import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.node.ArrayNode;
 import com.fasterxml.jackson.databind.node.ObjectNode;
 
 import de.ids_mannheim.korap.KrillCollection;
 import de.ids_mannheim.korap.KrillMeta;
 import de.ids_mannheim.korap.KrillQuery;
 import de.ids_mannheim.korap.KrillStats;
-import de.ids_mannheim.korap.response.Notifications;
-import static de.ids_mannheim.korap.util.KrillString.quote;
 
 /**
  * Base class for objects meant to be responded by the server.
@@ -54,7 +57,7 @@
 
     private HashMap<String, ObjectNode> jsonFields;
 
-    private static final String KORAL_VERSION = "http://korap.ids-mannheim.de/ns/KoralQuery/v0.3/context.jsonld";
+    public static final String KORAL_VERSION = "http://korap.ids-mannheim.de/ns/KoralQuery/v0.3/context.jsonld";
 
 
     /**
@@ -475,8 +478,7 @@
         // Move messages from the stats
         return (Response) this.moveNotificationsFrom(stats);
     };
-
-
+    
     public void addJsonNode (String key, ObjectNode value) {
         if (this.jsonFields == null)
             this.jsonFields = new HashMap<String, ObjectNode>(4);
diff --git a/src/main/java/de/ids_mannheim/korap/response/VirtualCorpusResponse.java b/src/main/java/de/ids_mannheim/korap/response/VirtualCorpusResponse.java
new file mode 100644
index 0000000..fd8a512
--- /dev/null
+++ b/src/main/java/de/ids_mannheim/korap/response/VirtualCorpusResponse.java
@@ -0,0 +1,31 @@
+package de.ids_mannheim.korap.response;
+
+import java.util.List;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.node.ArrayNode;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+
+public class VirtualCorpusResponse extends Response {
+
+    public JsonNode createKoralQueryForField (String fieldName,
+            List<String> fieldValues) {
+
+        ArrayNode arrayNode = mapper.createArrayNode();
+        for (String v : fieldValues) {
+            arrayNode.add(v);
+        }
+
+        ObjectNode collectionNode = mapper.createObjectNode();
+        collectionNode.put("@type", "koral:doc");
+        collectionNode.put("key", fieldName);
+        collectionNode.put("type", "type:string");
+        collectionNode.set("value", arrayNode);
+
+        ObjectNode root = mapper.createObjectNode();
+        root.put("@context", KORAL_VERSION);
+        root.set("corpus", collectionNode);
+
+        return (JsonNode) root;
+    }
+}
diff --git a/src/test/java/de/ids_mannheim/korap/search/TestVcField.java b/src/test/java/de/ids_mannheim/korap/search/TestVcField.java
new file mode 100644
index 0000000..b9b24ce
--- /dev/null
+++ b/src/test/java/de/ids_mannheim/korap/search/TestVcField.java
@@ -0,0 +1,112 @@
+package de.ids_mannheim.korap.search;
+
+import static org.junit.Assert.assertEquals;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+import org.apache.commons.io.IOUtils;
+import org.junit.Test;
+
+import com.fasterxml.jackson.databind.JsonNode;
+
+import de.ids_mannheim.korap.Krill;
+import de.ids_mannheim.korap.KrillIndex;
+import de.ids_mannheim.korap.index.FieldDocument;
+import de.ids_mannheim.korap.response.Response;
+
+public class TestVcField {
+    private KrillIndex ki;
+
+    private FieldDocument createFieldDoc (int uid, String textSigle) {
+        FieldDocument fd = new FieldDocument();
+        fd.addString("textSigle", textSigle);
+        fd.setUID(uid);
+        return fd;
+
+    }
+
+
+    public TestVcField () throws IOException {
+        ki = new KrillIndex();
+        ki.addDoc(createFieldDoc(1, "WPD/AAA/00001"));
+        
+        FieldDocument fd = createFieldDoc(2, "WPD/AAA/00002");
+        fd.addString("author", "Frank");
+        ki.addDoc(fd);
+        
+        fd = createFieldDoc(3, "WPD/AAA/00003");
+        fd.addTV("tokens", "a b c", "[(0-1)s:a|i:a|_0$<i>0<i>1|-:t$<i>3]"
+                + "[(2-3)s:b|i:b|_1$<i>2<i>3]" + "[(4-5)s:c|i:c|_2$<i>4<i>5]");
+        ki.addDoc(fd);
+        
+        ki.addDoc(createFieldDoc(4, "WPD/AAA/00004"));
+        ki.commit();
+        ki.addDoc(createFieldDoc(5, "WPD/AAA/00005"));
+        ki.addDoc(createFieldDoc(6, "WPD/AAA/00006"));
+        ki.commit();
+    }
+
+
+    private String getJsonResource (String file) throws IOException {
+        InputStream is = TestVcField.class.getResourceAsStream(file);
+        return IOUtils.toString(is, "utf-8");
+    }
+
+
+    @Test
+    public void testRetrieveTextSiglesOfVc1 () throws IOException {
+
+        String file = "/queries/collections/named-vcs/named-vc1.jsonld";
+        String json = getJsonResource(file);
+        JsonNode n = new Krill().retrieveFieldValues(json, ki, "textSigle");
+
+        System.out.println(n.toPrettyString());
+        assertEquals(Response.KORAL_VERSION, n.at("/@context").asText());
+        assertEquals("koral:doc", n.at("/corpus/@type").asText());
+        assertEquals("textSigle", n.at("/corpus/key").asText());
+        assertEquals("type:string", n.at("/corpus/type").asText());
+        assertEquals(2, n.at("/corpus/value").size());
+
+        assertEquals("WPD/AAA/00002", n.at("/corpus/value/0").asText());
+        assertEquals("WPD/AAA/00003", n.at("/corpus/value/1").asText());
+        
+        testRetrieveAuthorOfVc1(json);
+        testRetrieveTokensOfVc1(json);
+    }
+
+
+    private void testRetrieveAuthorOfVc1 (String json) {
+        JsonNode n = new Krill().retrieveFieldValues(json, ki, "author");
+        assertEquals("author", n.at("/corpus/key").asText());
+        assertEquals("Frank", n.at("/corpus/value/0").asText());        
+    }
+
+
+    private void testRetrieveTokensOfVc1 (String json) {
+        JsonNode n = new Krill().retrieveFieldValues(json, ki, "tokens");
+        assertEquals("tokens", n.at("/corpus/key").asText());
+        assertEquals(1, n.at("/corpus/value").size());
+        assertEquals("a b c", n.at("/corpus/value/0").asText());
+    }
+
+
+    @Test
+    public void testRetrieveTextSiglesOfVc3 () throws IOException {
+        // uid 5000 is not in the index
+        String file = "/queries/collections/named-vcs/named-vc3.jsonld";
+        JsonNode n = new Krill().retrieveFieldValues(getJsonResource(file), ki,
+                "textSigle");
+        assertEquals(2, n.at("/corpus/value").size());
+    }
+
+
+    @Test
+    public void testRetrieveTextSiglesOfVc4 () throws IOException {
+
+        String file = "/queries/collections/named-vcs/named-vc4.jsonld";
+        JsonNode n = new Krill().retrieveFieldValues(getJsonResource(file), ki,
+                "textSigle");
+        assertEquals(3, n.at("/corpus/value").size());
+    }
+}