Resolved #50. Added statistics API with KoralQuery input.

Change-Id: Ib62a15e31ff72063e13fff88ca4e36e65ddeb7a8
diff --git a/core/Changes b/core/Changes
index c750d6d..dcaf3dc 100644
--- a/core/Changes
+++ b/core/Changes
@@ -10,6 +10,8 @@
    - Added backward compatibility support for corpusQuery parameter (margaretha)    
 28/08/2019
    - Resolved #49. Added page param check in the search api (margaretha)
+23/09/2019   
+   - Resolved #50. Added statistics API with KoralQuery input (margaretha) 
 
 # version 0.62
 18/03/2019
diff --git a/core/pom.xml b/core/pom.xml
index 0601242..4b30bac 100644
--- a/core/pom.xml
+++ b/core/pom.xml
@@ -298,7 +298,7 @@
 		<dependency>
 			<groupId>de.ids_mannheim.korap</groupId>
 			<artifactId>Krill</artifactId>
-			<version>[0.58.6,)</version>
+			<version>[0.58.7,)</version>
 			<exclusions>
 				<exclusion>
 					<groupId>org.glassfish.jersey.containers</groupId>
diff --git a/core/src/main/java/de/ids_mannheim/korap/web/SearchKrill.java b/core/src/main/java/de/ids_mannheim/korap/web/SearchKrill.java
index 0e2d966..bac5912 100644
--- a/core/src/main/java/de/ids_mannheim/korap/web/SearchKrill.java
+++ b/core/src/main/java/de/ids_mannheim/korap/web/SearchKrill.java
@@ -281,8 +281,9 @@
      * 
      * @param json
      *            JSON-LD string with potential meta filters.
+     * @throws KustvaktException 
      */
-    public String getStatistics (String json) {
+    public String getStatistics (String json) throws KustvaktException {
         if (index == null) {
             return "{\"documents\" : -1, error\" : \"No index given\" }";
         };
@@ -320,6 +321,11 @@
         catch (IOException e) {
             e.printStackTrace();
         };
+        
+        if (kc.hasErrors()) {
+            throw new KustvaktException(
+                    "{\"errors\":" + kc.getErrors().toJsonString() + "}");
+        }
         // Build json response
         StringBuilder sb = new StringBuilder("{");
         sb.append("\"documents\":").append(docs).append(",\"tokens\":")
diff --git a/core/src/main/java/de/ids_mannheim/korap/web/controller/StatisticController.java b/core/src/main/java/de/ids_mannheim/korap/web/controller/StatisticController.java
index 84c4bb8..7ef29f6 100644
--- a/core/src/main/java/de/ids_mannheim/korap/web/controller/StatisticController.java
+++ b/core/src/main/java/de/ids_mannheim/korap/web/controller/StatisticController.java
@@ -2,7 +2,9 @@
 
 import java.util.Locale;
 
+import javax.ws.rs.Consumes;
 import javax.ws.rs.GET;
+import javax.ws.rs.POST;
 import javax.ws.rs.Path;
 import javax.ws.rs.Produces;
 import javax.ws.rs.QueryParam;
@@ -16,7 +18,6 @@
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Controller;
 
-import com.fasterxml.jackson.databind.JsonNode;
 import com.fasterxml.jackson.databind.node.ObjectNode;
 import com.sun.jersey.spi.container.ResourceFilters;
 
@@ -114,4 +115,27 @@
         }
         return Response.ok(stats).build();
     }
+    
+    @POST
+    @Consumes(MediaType.APPLICATION_JSON + ";charset=utf-8")
+    public Response getStatisticsFromKoralQuery (@Context SecurityContext context,
+            @Context Locale locale, String koralQuery) {
+        if (koralQuery != null && !koralQuery.isEmpty()) {
+            String stats;
+            try {
+                stats = searchKrill.getStatistics(koralQuery);
+                if (stats.contains("-1")){
+                    throw kustvaktResponseHandler.throwit(StatusCodes.NO_RESULT_FOUND);
+                }
+                return Response.ok(stats).build();
+            }
+            catch (KustvaktException e) {
+                throw kustvaktResponseHandler.throwit(e);
+            }
+        }
+        else {
+            throw kustvaktResponseHandler.throwit(StatusCodes.NO_QUERY, 
+                    "Koral query is missing", "koralQuery");
+        }
+    }
 }
diff --git a/full/src/main/resources/ehcache.xml b/full/src/main/resources/ehcache.xml
index 2531fdb..cc06d48 100644
--- a/full/src/main/resources/ehcache.xml
+++ b/full/src/main/resources/ehcache.xml
@@ -1,7 +1,6 @@
 <ehcache xsi:noNamespaceSchemaLocation="http://www.ehcache.org/ehcache.xsd"
 	updateCheck="true" monitoring="autodetect" dynamicConfig="true">
 	
-	<sizeOfPolicy maxDepth="100" maxDepthExceededBehavior="abort" />
     <defaultCache eternal='true' overflowToDisk='false'/>
     <!--maxBytesLocalHeap="200M"-->
     <diskStore path="./cache_store"/>
@@ -60,5 +59,6 @@
 		maxBytesLocalDisk="1G"
 		diskExpiryThreadIntervalSeconds = "120" > 
 		<persistence strategy="localTempSwap"/>
+		<sizeOfPolicy maxDepth="1000" maxDepthExceededBehavior="abort" />
 	</cache>        
 </ehcache>
diff --git a/full/src/test/java/de/ids_mannheim/korap/web/controller/StatisticsControllerTest.java b/full/src/test/java/de/ids_mannheim/korap/web/controller/StatisticsControllerTest.java
index b0d16cf..8befd15 100644
--- a/full/src/test/java/de/ids_mannheim/korap/web/controller/StatisticsControllerTest.java
+++ b/full/src/test/java/de/ids_mannheim/korap/web/controller/StatisticsControllerTest.java
@@ -5,11 +5,12 @@
 
 import java.io.IOException;
 
+import javax.ws.rs.core.HttpHeaders;
+import javax.ws.rs.core.MediaType;
+
 import org.junit.Test;
 
-import com.fasterxml.jackson.core.JsonProcessingException;
 import com.fasterxml.jackson.databind.JsonNode;
-import com.fasterxml.jackson.databind.ObjectMapper;
 import com.sun.jersey.api.client.ClientResponse;
 
 import de.ids_mannheim.korap.config.SpringJerseyTest;
@@ -23,12 +24,9 @@
  */
 public class StatisticsControllerTest extends SpringJerseyTest {
 
-    private ObjectMapper mapper = new ObjectMapper();
-
-
     @Test
     public void testGetStatisticsNoResource ()
-            throws JsonProcessingException, IOException {
+            throws IOException, KustvaktException {
         String corpusQuery = "corpusSigle=WPD15";
         ClientResponse response = resource().path(API_VERSION)
                 .path("statistics")
@@ -38,7 +36,7 @@
         assert ClientResponse.Status.OK.getStatusCode() == response.getStatus();
 
         String ent = response.getEntity(String.class);
-        JsonNode node = mapper.readTree(ent);
+        JsonNode node = JsonUtils.readTree(ent);
         assertEquals(node.get("documents").asInt(),0);
         assertEquals(node.get("tokens").asInt(),0);
     }
@@ -82,7 +80,7 @@
 
     @Test
     public void testGetStatisticsWithcorpusQuery1 ()
-            throws JsonProcessingException, IOException {
+            throws IOException, KustvaktException {
         String corpusQuery = "corpusSigle=GOE";
         ClientResponse response = resource().path(API_VERSION)
                 .path("statistics")
@@ -92,7 +90,7 @@
         assert ClientResponse.Status.OK.getStatusCode() == response.getStatus();
 
         String ent = response.getEntity(String.class);
-        JsonNode node = mapper.readTree(ent);
+        JsonNode node = JsonUtils.readTree(ent);
         assertEquals(node.get("documents").asInt(),11);
         assertEquals(node.get("tokens").asInt(),665842);
         
@@ -105,13 +103,13 @@
 
     @Test
     public void testGetStatisticsWithcorpusQuery2 ()
-            throws JsonProcessingException, IOException {
+            throws IOException, KustvaktException {
         ClientResponse response = resource().path(API_VERSION)
                 .path("statistics")
                 .queryParam("corpusQuery", "creationDate since 1810")
                 .get(ClientResponse.class);
         String ent = response.getEntity(String.class);
-        JsonNode node = mapper.readTree(ent);
+        JsonNode node = JsonUtils.readTree(ent);
         assert ClientResponse.Status.OK.getStatusCode() == response.getStatus();
         assertEquals(node.get("documents").asInt(),7);
         assertEquals(node.get("tokens").asInt(),279402);
@@ -122,7 +120,7 @@
 
     @Test
     public void testGetStatisticsWithWrongcorpusQuery ()
-            throws JsonProcessingException, IOException {
+            throws IOException, KustvaktException {
         ClientResponse response = resource().path(API_VERSION)
                 .path("statistics")
                 .queryParam("corpusQuery", "creationDate geq 1810")
@@ -131,7 +129,7 @@
         assert ClientResponse.Status.BAD_REQUEST.getStatusCode() == response
                 .getStatus();
         String ent = response.getEntity(String.class);
-        JsonNode node = mapper.readTree(ent);
+        JsonNode node = JsonUtils.readTree(ent);
         assertEquals(node.at("/errors/0/0").asInt(), 302);
         assertEquals(node.at("/errors/0/1").asText(),
                 "Could not parse query >>> (creationDate geq 1810) <<<.");
@@ -142,7 +140,7 @@
 
     @Test
     public void testGetStatisticsWithWrongcorpusQuery2 ()
-            throws JsonProcessingException, IOException {
+            throws IOException, KustvaktException {
         ClientResponse response = resource().path(API_VERSION)
                 .path("statistics")
                 .queryParam("corpusQuery", "creationDate >= 1810")
@@ -152,7 +150,7 @@
         assertEquals(ClientResponse.Status.BAD_REQUEST.getStatusCode(),
                 response.getStatus());
         
-        JsonNode node = mapper.readTree(ent);
+        JsonNode node = JsonUtils.readTree(ent);
         assertEquals(node.at("/errors/0/0").asInt(), 305);
         assertEquals(node.at("/errors/0/1").asText(),
                 "Operator >= is not acceptable.");
@@ -162,7 +160,7 @@
     
     @Test
     public void testGetStatisticsWithoutcorpusQuery ()
-            throws JsonProcessingException, IOException {
+            throws IOException, KustvaktException {
         ClientResponse response = resource().path(API_VERSION)
                 .path("statistics")
                 .get(ClientResponse.class);
@@ -171,11 +169,86 @@
 					 response.getStatus());
         String ent = response.getEntity(String.class);
 
-		JsonNode node = mapper.readTree(ent);
+		JsonNode node = JsonUtils.readTree(ent);
 		assertEquals(11, node.at("/documents").asInt());
         assertEquals(665842, node.at("/tokens").asInt());
         assertEquals(25074, node.at("/sentences").asInt());
         assertEquals(772, node.at("/paragraphs").asInt());
     }
    
+    @Test
+    public void testGetStatisticsWithKoralQuery ()
+            throws IOException, KustvaktException {
+        ClientResponse response = resource().path(API_VERSION)
+                .path("statistics")
+                .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON)
+                .post(ClientResponse.class,"{ \"collection\" : {\"@type\": "
+                        + "\"koral:doc\", \"key\": \"availability\", \"match\": "
+                        + "\"match:eq\", \"type\": \"type:regex\", \"value\": "
+                        + "\"CC-BY.*\"} }");
+
+        assertEquals(ClientResponse.Status.OK.getStatusCode(),
+                     response.getStatus());
+        String ent = response.getEntity(String.class);
+        
+        JsonNode node = JsonUtils.readTree(ent);
+        assertEquals(2, node.at("/documents").asInt());
+        assertEquals(72770, node.at("/tokens").asInt());
+        assertEquals(2985, node.at("/sentences").asInt());
+        assertEquals(128, node.at("/paragraphs").asInt());
+    }
+    
+    @Test
+    public void testGetStatisticsWithEmptyCollection ()
+            throws IOException, KustvaktException {
+        ClientResponse response = resource().path(API_VERSION)
+                .path("statistics")
+                .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON)
+                .post(ClientResponse.class,"{}");
+
+        assertEquals(ClientResponse.Status.BAD_REQUEST.getStatusCode(),
+                     response.getStatus());
+        String ent = response.getEntity(String.class);
+        JsonNode node = JsonUtils.readTree(ent);
+        assertEquals(node.at("/errors/0/0").asInt(),
+                de.ids_mannheim.korap.util.StatusCodes.MISSING_COLLECTION);
+        assertEquals(node.at("/errors/0/1").asText(),
+                "Collection is not found");
+    }
+    
+    @Test
+    public void testGetStatisticsWithIncorrectJson ()
+            throws IOException, KustvaktException {
+        ClientResponse response = resource().path(API_VERSION)
+                .path("statistics")
+                .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON)
+                .post(ClientResponse.class,"{ \"collection\" : }");
+
+        assertEquals(ClientResponse.Status.BAD_REQUEST.getStatusCode(),
+                     response.getStatus());
+        String ent = response.getEntity(String.class);
+        JsonNode node = JsonUtils.readTree(ent);
+        assertEquals(node.at("/errors/0/0").asInt(),
+                de.ids_mannheim.korap.util.StatusCodes.UNABLE_TO_PARSE_JSON);
+        assertEquals(node.at("/errors/0/1").asText(),
+                "Unable to parse JSON");
+    }
+    
+    @Test
+    public void testGetStatisticsWithoutKoralQuery ()
+            throws IOException, KustvaktException {
+        ClientResponse response = resource().path(API_VERSION)
+                .path("statistics").post(ClientResponse.class);
+
+        assertEquals(ClientResponse.Status.BAD_REQUEST.getStatusCode(),
+                response.getStatus());
+        String ent = response.getEntity(String.class);
+
+        JsonNode node = JsonUtils.readTree(ent);
+        assertEquals(StatusCodes.NO_QUERY, node.at("/errors/0/0").asInt());
+        assertEquals("Koral query is missing",
+                node.at("/errors/0/1").asText());
+        assertEquals("koralQuery", node.at("/errors/0/2").asText());
+    }
+    
 }