Added KoralQuery check when updating VC (solved #676)

Change-Id: Ic7f8f565fde6c4a5bf087e27af3f47674854a0f6
diff --git a/Changes b/Changes
index 3eb7fa4..17eb04a 100644
--- a/Changes
+++ b/Changes
@@ -7,7 +7,7 @@
 - Enables inputting free-resources.json from data folder
 - Changed loading external kustvakt.conf and jdbc.properties 
   to use /data folder (#598)
-
+- Added KoralQuery check when updating VC (solved #676)
 
 
 # version 0.73
diff --git a/src/main/java/de/ids_mannheim/korap/config/NamedVCLoader.java b/src/main/java/de/ids_mannheim/korap/config/NamedVCLoader.java
index fda0ee8..968d8d2 100644
--- a/src/main/java/de/ids_mannheim/korap/config/NamedVCLoader.java
+++ b/src/main/java/de/ids_mannheim/korap/config/NamedVCLoader.java
@@ -18,6 +18,7 @@
 import de.ids_mannheim.korap.cache.VirtualCorpusCache;
 import de.ids_mannheim.korap.constant.QueryType;
 import de.ids_mannheim.korap.constant.ResourceType;
+import de.ids_mannheim.korap.entity.QueryDO;
 import de.ids_mannheim.korap.exceptions.KustvaktException;
 import de.ids_mannheim.korap.exceptions.StatusCodes;
 import de.ids_mannheim.korap.service.QueryService;
@@ -64,6 +65,11 @@
         }
     }
 
+    public void loadVCToCache (String filename, String filePath)
+            throws IOException, QueryException, KustvaktException {
+        loadVCToCache(filename,filePath,null);
+    }
+    
     /**
      * Used for testing
      * 
@@ -73,17 +79,14 @@
      * @throws QueryException
      * @throws KustvaktException
      */
-    public void loadVCToCache (String filename, String filePath)
-            throws IOException, QueryException, KustvaktException {
+    public void loadVCToCache (String filename, String filePath, String json)
+            throws IOException, QueryException {
 
-        InputStream is = NamedVCLoader.class.getResourceAsStream(filePath);
-        String json = IOUtils.toString(is, "utf-8");
-        if (json != null) {
-            cacheVC(filename, json);
-            vcService.storeQuery("system", filename, ResourceType.SYSTEM,
-                    QueryType.VIRTUAL_CORPUS, json, null, null, null, true,
-                    "system", null, null);
+        if (json==null || json.isEmpty()) {
+            InputStream is = NamedVCLoader.class.getResourceAsStream(filePath);
+            json = IOUtils.toString(is, "utf-8");
         }
+        processVC(filename, json);
     }
 
     public void loadVCToCache () throws IOException, QueryException {
@@ -109,11 +112,60 @@
             filename = strArr[0];
             String json = strArr[1];
             if (json != null) {
-                cacheVC(filename, json);
-                storeVCinDB(filename, json);
+                processVC(filename, json);
             }
         }
     }
+    
+    /**
+     * Stores and caches VC if the given VC does not exist.
+     * Updates VC in the database and re-caches it, if the given VC exists.
+     * Updates VC if there is any change in the index.
+     * 
+     * In this method, it will be checked if
+     * <ol>
+     *  <li> VC exists in the database</li>
+     *  <li> VC exists in the cache </li>
+     *  <li> KoralQuery of the given VC differs from an existing VC with
+     * the same id. </li>
+     *  <li> Index has been changed</li>
+     * </ol>
+     * 
+     * Koral Query
+     * 
+     * @param vcId
+     * @param json
+     * @throws IOException
+     * @throws QueryException
+     */
+    private void processVC (String vcId, String json)
+            throws IOException, QueryException {
+        boolean updateCache = false;
+        try {
+            // if VC exists in the DB
+            QueryDO existingVC = vcService.searchQueryByName("system", vcId, "system",
+                    QueryType.VIRTUAL_CORPUS);
+            
+            String koralQuery = existingVC.getKoralQuery();
+            // if existing VC is different from input
+            if (json.hashCode() != koralQuery.hashCode()) {
+                updateCache = true;
+                // updateVCinDB
+                storeVCinDB(vcId, json, existingVC);
+            }
+        }
+        catch (KustvaktException e) {
+            // VC doesn't exist in the DB
+            if (e.getStatusCode() == StatusCodes.NO_RESOURCE_FOUND) {
+                storeVCinDB(vcId, json, null);
+            }
+            else {
+                throw new RuntimeException(e);
+            }    
+        }
+        
+        cacheVC(vcId, json, updateCache);
+    }
 
     private String[] readFile (File file, String filename) throws IOException {
         String json = null;
@@ -152,12 +204,18 @@
      * @param koralQuery
      * @throws IOException
      * @throws QueryException
+     * @throws KustvaktException 
      */
-    private void cacheVC (String vcId, String koralQuery)
+    private void cacheVC (String vcId, String koralQuery, boolean updateVC)
             throws IOException, QueryException {
         config.setVcInCaching(vcId);
-        if (VirtualCorpusCache.contains(vcId)) {
+        if (updateVC) {
+            jlog.info("Updating {} in cache ", vcId);
+            VirtualCorpusCache.delete(vcId);
+        }
+        else if (VirtualCorpusCache.contains(vcId)) {
             jlog.info("Checking {} in cache ", vcId);
+            
         }
         else {
             jlog.info("Storing {} in cache ", vcId);
@@ -172,32 +230,22 @@
     }
 
     /**
-     * Stores the VC if it doesn't exist in the database.
+     * Stores new VC or updates existing VC
      * 
      * @param vcId
      * @param koralQuery
      */
-    private void storeVCinDB (String vcId, String koralQuery) {
+    private void storeVCinDB (String vcId, String koralQuery, QueryDO existingVC) {
         try {
-            vcService.searchQueryByName("system", vcId, "system",
-                    QueryType.VIRTUAL_CORPUS);
+            String info = (existingVC == null) ? "Storing" : "Updating";
+            jlog.info("{} {} in database ", info, vcId);
+            
+            vcService.storeQuery(existingVC, "system", vcId, ResourceType.SYSTEM,
+                    QueryType.VIRTUAL_CORPUS, koralQuery, null, null, null,
+                    true, "system", null, null);
         }
         catch (KustvaktException e) {
-            if (e.getStatusCode() == StatusCodes.NO_RESOURCE_FOUND) {
-                try {
-                    jlog.info("Storing {} in database ", vcId);
-                    vcService.storeQuery("system", vcId, ResourceType.SYSTEM,
-                            QueryType.VIRTUAL_CORPUS, koralQuery, null, null,
-                            null, true, "system", null, null);
-                }
-                catch (KustvaktException e1) {
-                    throw new RuntimeException(e1);
-                }
-            }
-            else {
-                throw new RuntimeException(e);
-            }
+            throw new RuntimeException(e);
         }
-
     }
 }
diff --git a/src/main/java/de/ids_mannheim/korap/service/QueryService.java b/src/main/java/de/ids_mannheim/korap/service/QueryService.java
index cc5f51b..7e45d30 100644
--- a/src/main/java/de/ids_mannheim/korap/service/QueryService.java
+++ b/src/main/java/de/ids_mannheim/korap/service/QueryService.java
@@ -313,6 +313,16 @@
             String definition, String description, String status,
             boolean isCached, String queryCreator, String query,
             String queryLanguage) throws KustvaktException {
+        storeQuery(null, username, queryName, type, queryType, koralQuery,
+                definition, description, status, isCached, queryCreator, query,
+                queryLanguage);
+    }
+    
+    public void storeQuery (QueryDO existingQuery, String username, String queryName,
+            ResourceType type, QueryType queryType, String koralQuery,
+            String definition, String description, String status,
+            boolean isCached, String queryCreator, String query,
+            String queryLanguage) throws KustvaktException {
         ParameterChecker.checkNameValue(queryName, "queryName");
         ParameterChecker.checkObjectValue(type, "type");
 
@@ -348,9 +358,16 @@
 
         int queryId = 0;
         try {
-            queryId = queryDao.createQuery(queryName, type, queryType,
-                    requiredAccess, koralQuery, definition, description, status,
-                    isCached, queryCreator, query, queryLanguage);
+            if (existingQuery==null) {
+                queryId = queryDao.createQuery(queryName, type, queryType,
+                        requiredAccess, koralQuery, definition, description,
+                        status, isCached, queryCreator, query, queryLanguage);
+            }
+            else {
+                queryDao.editQuery(existingQuery, queryName, type,
+                        requiredAccess, koralQuery, definition, description,
+                        status, isCached, query, queryLanguage);
+            }
 
         }
         catch (Exception e) {
diff --git a/src/test/java/de/ids_mannheim/korap/cache/NamedVCLoaderTest.java b/src/test/java/de/ids_mannheim/korap/cache/NamedVCLoaderTest.java
index 7b7db2f..4f5b013 100644
--- a/src/test/java/de/ids_mannheim/korap/cache/NamedVCLoaderTest.java
+++ b/src/test/java/de/ids_mannheim/korap/cache/NamedVCLoaderTest.java
@@ -34,10 +34,40 @@
         assertTrue(VirtualCorpusCache.contains(vcId));
         Map<String, DocBits> cachedData = VirtualCorpusCache.retrieve(vcId);
         assertTrue(cachedData.size() > 0);
-        VirtualCorpusCache.delete(vcId);
-        assertFalse(VirtualCorpusCache.contains(vcId));
+        //VirtualCorpusCache.delete(vcId);
+        //assertFalse(VirtualCorpusCache.contains(vcId));
         QueryDO vc = dao.retrieveQueryByName(vcId, "system");
         assertNotNull(vc);
+        
+        String koralQuery = vc.getKoralQuery();
+        testUpdateVC(vcId,koralQuery);
+    }
+    
+    private void testUpdateVC (String vcId, String koralQuery)
+            throws IOException, QueryException, KustvaktException {
+        String json = """
+            {"collection": {
+                "@type": "koral:doc",
+                "key": "textSigle",
+                "match": "match:eq",
+                "type" : "type:string",
+                "value": [
+                    "GOE/AGF/00000"
+                ]
+            }}""";
+       
+        vcLoader.loadVCToCache(vcId, "", json);
+        
+        Map<String, DocBits> cachedData = VirtualCorpusCache.retrieve(vcId);
+        assertTrue(cachedData.size() > 0);
+        
+        QueryDO vc = dao.retrieveQueryByName(vcId, "system");
+        String updatedKoralQuery = vc.getKoralQuery();
+        
+        assertTrue (koralQuery.hashCode() != updatedKoralQuery.hashCode());
+        
+        VirtualCorpusCache.delete(vcId);
+        assertFalse(VirtualCorpusCache.contains(vcId));
         dao.deleteQuery(vc);
         vc = dao.retrieveQueryByName(vcId, "system");
         assertNull(vc);