Added config for total result cached and fixed #774.

Change-Id: I9be13e78c42f60664073b2b693ca90590c99cdf6
diff --git a/src/main/java/de/ids_mannheim/korap/config/KustvaktConfiguration.java b/src/main/java/de/ids_mannheim/korap/config/KustvaktConfiguration.java
index cb1cde5..1647e41 100644
--- a/src/main/java/de/ids_mannheim/korap/config/KustvaktConfiguration.java
+++ b/src/main/java/de/ids_mannheim/korap/config/KustvaktConfiguration.java
@@ -114,6 +114,7 @@
     // EM: metadata restriction
     // another variable might be needed to define which metadata fields are restricted 
     private boolean isMetadataRestricted = false;
+    private boolean totalResultCacheEnabled;
 
     // EM: Maybe needed when we support pipe registration
     @Deprecated
@@ -220,6 +221,9 @@
 
         // network endpoint
         networkEndpointURL = properties.getProperty("network.endpoint.url", "");
+        // cache
+        totalResultCacheEnabled = Boolean.valueOf(properties.getProperty(
+                "cache.total.results.enabled","true"));
     }
 
     @Deprecated
diff --git a/src/main/java/de/ids_mannheim/korap/core/service/SearchService.java b/src/main/java/de/ids_mannheim/korap/core/service/SearchService.java
index e616295..5972577 100644
--- a/src/main/java/de/ids_mannheim/korap/core/service/SearchService.java
+++ b/src/main/java/de/ids_mannheim/korap/core/service/SearchService.java
@@ -211,7 +211,7 @@
 
         int hashedKoralQuery = createTotalResultCacheKey(query);
         boolean hasCutOff = hasCutOff(query);
-        if (!hasCutOff) {
+        if (config.isTotalResultCacheEnabled() && !hasCutOff) {
             query = precheckTotalResultCache(hashedKoralQuery, query);
         }
 
@@ -229,7 +229,10 @@
         }
         // jlog.debug("Query result: " + result);
 
-        result = afterCheckTotalResultCache(hashedKoralQuery, result);
+        if (config.isTotalResultCacheEnabled()) {
+            result = afterCheckTotalResultCache(hashedKoralQuery, result);
+        }
+        
         if (!hasCutOff) {
             result = removeCutOff(result);
         }
@@ -248,7 +251,7 @@
             throws KustvaktException {
         ObjectNode queryNode = (ObjectNode) JsonUtils.readTree(query);
         queryNode.remove("meta");
-        return queryNode.hashCode();
+        return queryNode.toString().hashCode();
     }
 
     private String afterCheckTotalResultCache (int hashedKoralQuery,
diff --git a/src/test/java/de/ids_mannheim/korap/cache/TotalResultTest.java b/src/test/java/de/ids_mannheim/korap/cache/TotalResultTest.java
index 07f4485..7886ddc 100644
--- a/src/test/java/de/ids_mannheim/korap/cache/TotalResultTest.java
+++ b/src/test/java/de/ids_mannheim/korap/cache/TotalResultTest.java
@@ -1,11 +1,15 @@
 package de.ids_mannheim.korap.cache;
 
 import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotEquals;
 import static org.junit.jupiter.api.Assertions.assertTrue;
 
 import org.junit.jupiter.api.Test;
 import org.springframework.beans.factory.annotation.Autowired;
 import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+
+import de.ids_mannheim.korap.config.KustvaktConfiguration;
 import de.ids_mannheim.korap.config.SpringJerseyTest;
 import de.ids_mannheim.korap.core.service.SearchService;
 import de.ids_mannheim.korap.exceptions.KustvaktException;
@@ -17,6 +21,9 @@
 
     @Autowired
     private SearchService searchService;
+    
+    @Autowired
+    private KustvaktConfiguration config;
 
     @Test
     public void testClearCache () {
@@ -54,8 +61,7 @@
         assertEquals(Status.OK.getStatusCode(), response.getStatus());
         entity = response.readEntity(String.class);
         node = JsonUtils.readTree(entity);
-        assertTrue(node.at("/meta/totalResults").isNumber(),
-                "totalResults should be a number");
+        assertTrue(node.at("/meta/totalResults").isNumber());
         assertEquals(totalResults, node.at("/meta/totalResults").asInt());
         assertEquals(1, searchService.getTotalResultCache()
                 .getAllCacheElements().size());
@@ -108,4 +114,79 @@
         node = JsonUtils.readTree(entity);
         assertTrue(node.at("/meta/cutOff").asBoolean());
     }
+    
+    @Test
+    public void testCacheDisabled () throws KustvaktException {
+        searchService.getTotalResultCache().clearCache();
+        assertEquals(0, searchService.getTotalResultCache()
+                .getAllCacheElements().size());
+
+        config.setTotalResultCacheEnabled(false);
+        
+        Response response = target().path(API_VERSION).path("search")
+                .queryParam("q", "[orth=zu]").queryParam("ql", "poliqarp")
+                .queryParam("page", "1").request().get();
+        assertEquals(Status.OK.getStatusCode(), response.getStatus());
+        String entity = response.readEntity(String.class);
+        JsonNode node = JsonUtils.readTree(entity);
+        assertTrue(node.at("/meta/totalResults").isNumber(),
+                "totalResults should be a number");
+        assertEquals(0, searchService.getTotalResultCache()
+                .getAllCacheElements().size());
+        
+        config.setTotalResultCacheEnabled(true);
+        
+        response = target().path(API_VERSION).path("search")
+                .queryParam("q", "[orth=zu]").queryParam("ql", "poliqarp")
+                .queryParam("page", "1").request().get();
+        assertEquals(Status.OK.getStatusCode(), response.getStatus());
+
+        assertEquals(1, searchService.getTotalResultCache()
+                .getAllCacheElements().size());
+        
+        searchService.getTotalResultCache().clearCache();
+    }
+    
+    @Test
+    public void testCacheKey () throws KustvaktException {
+        Response response = target().path(API_VERSION).path("search")
+                .queryParam("q", "[orth=populistischer]")
+                .queryParam("ql", "poliqarp")
+                .queryParam("cq", "availability!=QAO-NC-LOC:ids & corpusSigle = "
+                        + "/SOL|[UTSZ][0-9][0-9]/ & pubDate in 1976")
+                //.queryParam("fields", "corpusSigle,textSigle,pubDate,pubPlace,"
+                //        + "availability,textClass")
+                .queryParam("access-rewrite-disabled", "true")
+                .queryParam("page", "1").request().get();
+        assertEquals(Status.OK.getStatusCode(), response.getStatus());
+        String entity = response.readEntity(String.class);
+        
+        ObjectNode queryNode = (ObjectNode) JsonUtils.readTree(entity);
+        queryNode.remove("meta");
+        queryNode.remove("matches");
+        int queryHashCode1 = queryNode.hashCode();
+        int queryStringHashCode1 = queryNode.toString().hashCode();
+        
+        response = target().path(API_VERSION).path("search")
+                .queryParam("q", "[orth=populistisches]")
+                .queryParam("ql", "poliqarp")
+                .queryParam("cq", "availability!=QAO-NC-LOC:ids & corpusSigle = "
+                        + "/SOL|[UTSZ][0-9][0-9]/ & pubDate in 1975")
+                //.queryParam("fields", "corpusSigle,textSigle,pubDate,pubPlace,"
+                //        + "availability,textClass")
+                .queryParam("access-rewrite-disabled", "true")
+                .queryParam("page", "1").request().get();
+        assertEquals(Status.OK.getStatusCode(), response.getStatus());
+
+        entity = response.readEntity(String.class);
+        
+        queryNode = (ObjectNode) JsonUtils.readTree(entity);
+        queryNode.remove("meta");
+        queryNode.remove("matches");
+        int queryHashCode2 = queryNode.hashCode();
+        int queryStringHashCode2 = queryNode.toString().hashCode();
+        
+        assertEquals(queryHashCode1, queryHashCode2);
+        assertNotEquals(queryStringHashCode1, queryStringHashCode2);
+    }
 }
diff --git a/src/test/resources/kustvakt-test.conf b/src/test/resources/kustvakt-test.conf
index 6185b29..7818711 100644
--- a/src/test/resources/kustvakt-test.conf
+++ b/src/test/resources/kustvakt-test.conf
@@ -28,6 +28,9 @@
 server.port=8089
 server.host=localhost
 
+## Cache
+cache.total.results.enabled = true
+
 # Default foundries for specific layers (optional)
 #
 default.foundry.partOfSpeech = tt