From 1d871ba7416a32ea512a4dfc0f00bec2ac44672a Mon Sep 17 00:00:00 2001
From: Vikas Saurabh <vsaurabh@adobe.com>
Date: Sat, 27 Feb 2016 19:01:23 +0530
Subject: [PATCH] OAK-4066: Suggestion dictionary don't update after
 suggestUpdateFrequencyMinutes unless something else causes index update

---
 .../index/lucene/LuceneIndexEditorContext.java     | 72 +++++++++++++---------
 .../index/lucene/SuggestionIntervalTest.java       | 28 +++++++++
 2 files changed, 72 insertions(+), 28 deletions(-)

diff --git a/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/LuceneIndexEditorContext.java b/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/LuceneIndexEditorContext.java
index c49902c..477048b 100644
--- a/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/LuceneIndexEditorContext.java
+++ b/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/LuceneIndexEditorContext.java
@@ -245,6 +245,13 @@ public class LuceneIndexEditorContext {
             getWriter();
         }
 
+        boolean updateSuggestions = shouldUpdateSuggestions();
+        boolean forcedUpdateSuggester = false;
+        if (writer == null && updateSuggestions) {
+            forcedUpdateSuggester = true;
+            getWriter();
+        }
+
         if (writer != null) {
             if (log.isTraceEnabled()) {
                 trackIndexSizeInfo(writer, definition, directory);
@@ -252,8 +259,10 @@ public class LuceneIndexEditorContext {
 
             final long start = PERF_LOGGER.start();
 
-            updateSuggester(writer.getAnalyzer());
-            PERF_LOGGER.end(start, -1, "Completed suggester for directory {}", definition);
+            if (updateSuggestions) {
+                updateSuggester(writer.getAnalyzer());
+                PERF_LOGGER.end(start, -1, "Completed suggester for directory {}", definition);
+            }
 
             writer.close();
             PERF_LOGGER.end(start, -1, "Closed writer for directory {}", definition);
@@ -261,16 +270,21 @@ public class LuceneIndexEditorContext {
             directory.close();
             PERF_LOGGER.end(start, -1, "Closed directory for directory {}", definition);
 
-            //OAK-2029 Record the last updated status so
-            //as to make IndexTracker detect changes when index
-            //is stored in file system
-            NodeBuilder status = definitionBuilder.child(":status");
-            status.setProperty("lastUpdated", ISO8601.format(getCalendar()), Type.DATE);
-            status.setProperty("indexedNodes",indexedNodes);
+            if (!forcedUpdateSuggester) {
+                //OAK-2029 Record the last updated status so
+                //as to make IndexTracker detect changes when index
+                //is stored in file system
+                NodeBuilder status = definitionBuilder.child(":status");
+                status.setProperty("lastUpdated", ISO8601.format(getCalendar()), Type.DATE);
+                status.setProperty("indexedNodes", indexedNodes);
+            }
+
             PERF_LOGGER.end(start, -1, "Overall Closed IndexWriter for directory {}", definition);
 
-            textExtractionStats.log(reindex);
-            textExtractionStats.collectStats(extractedTextCache);
+            if (!forcedUpdateSuggester) {
+                textExtractionStats.log(reindex);
+                textExtractionStats.collectStats(extractedTextCache);
+            }
         }
     }
 
@@ -280,10 +294,24 @@ public class LuceneIndexEditorContext {
      * @param analyzer the analyzer used to update the suggester
      */
     private void updateSuggester(Analyzer analyzer) throws IOException {
+        NodeBuilder suggesterStatus = definitionBuilder.child(":suggesterStatus");
+        DirectoryReader reader = DirectoryReader.open(writer, false);
+        final OakDirectory suggestDirectory = new OakDirectory(definitionBuilder, ":suggest-data", definition, false);
+        try {
+            SuggestHelper.updateSuggester(suggestDirectory, analyzer, reader);
+            suggesterStatus.setProperty("lastUpdated", ISO8601.format(getCalendar()), Type.DATE);
+        } catch (Throwable e) {
+            log.warn("could not update suggester", e);
+        } finally {
+            suggestDirectory.close();
+            reader.close();
+        }
+    }
 
-        if (definition.isSuggestEnabled()) {
+    private boolean shouldUpdateSuggestions() {
+        boolean updateSuggestions = false;
 
-            boolean updateSuggester = false;
+        if (definition.isSuggestEnabled()) {
             NodeBuilder suggesterStatus = definitionBuilder.child(":suggesterStatus");
             if (suggesterStatus.hasProperty("lastUpdated")) {
                 PropertyState suggesterLastUpdatedValue = suggesterStatus.getProperty("lastUpdated");
@@ -291,26 +319,14 @@ public class LuceneIndexEditorContext {
                 int updateFrequency = definition.getSuggesterUpdateFrequencyMinutes();
                 suggesterLastUpdatedTime.add(Calendar.MINUTE, updateFrequency);
                 if (getCalendar().after(suggesterLastUpdatedTime)) {
-                    updateSuggester = true;
+                    updateSuggestions = true;
                 }
             } else {
-                updateSuggester = true;
-            }
-
-            if (updateSuggester) {
-                DirectoryReader reader = DirectoryReader.open(writer, false);
-                final OakDirectory suggestDirectory = new OakDirectory(definitionBuilder, ":suggest-data", definition, false);
-                try {
-                    SuggestHelper.updateSuggester(suggestDirectory, analyzer, reader);
-                    suggesterStatus.setProperty("lastUpdated", ISO8601.format(getCalendar()), Type.DATE);
-                } catch (Throwable e) {
-                    log.warn("could not update suggester", e);
-                } finally {
-                    suggestDirectory.close();
-                    reader.close();
-                }
+                updateSuggestions = true;
             }
         }
+
+        return updateSuggestions;
     }
 
     /** Only set for testing */
diff --git a/oak-lucene/src/test/java/org/apache/jackrabbit/oak/plugins/index/lucene/SuggestionIntervalTest.java b/oak-lucene/src/test/java/org/apache/jackrabbit/oak/plugins/index/lucene/SuggestionIntervalTest.java
index e405d1b..fbb8c4f 100644
--- a/oak-lucene/src/test/java/org/apache/jackrabbit/oak/plugins/index/lucene/SuggestionIntervalTest.java
+++ b/oak-lucene/src/test/java/org/apache/jackrabbit/oak/plugins/index/lucene/SuggestionIntervalTest.java
@@ -144,4 +144,32 @@ public class SuggestionIntervalTest {
         assertEquals(1, suggestions.size());
         assertEquals("indexedNode", suggestions.iterator().next());
     }
+
+    //OAK-4068
+    @Test
+    public void suggestionUpdateWithoutIndexChange() throws Exception {
+        final String nodeType = "nt:unstructured";
+
+        createSuggestIndex(nodeType);
+        session.save();
+
+        long currTime = clock.getTime();
+        long toTime = currTime + TimeUnit.MINUTES.toMillis(IndexDefinition.DEFAULT_SUGGESTER_UPDATE_FREQUENCY_MINUTES);
+
+        root.addNode("indexedNode", nodeType);
+        session.save();
+
+        //wait for suggestions refresh time
+        clock.waitUntil(toTime);
+        clock.getTime();//get one more tick
+
+        //push a change which should not make any change in the index but yet should help update suggestions
+        root.addNode("some-non-index-change", "oak:Unstructured");
+        session.save();
+
+        Set<String> suggestions = getSuggestions(nodeType, "indexedn");
+
+        assertEquals(1, suggestions.size());
+        assertEquals("indexedNode", suggestions.iterator().next());
+    }
 }
-- 
2.6.2

