Browse Source

use cache for popular words

Shinsuke Sugaya 9 years ago
parent
commit
b9b1851d38

+ 62 - 18
src/main/java/org/codelibs/fess/helper/PopularWordHelper.java

@@ -16,7 +16,13 @@
 package org.codelibs.fess.helper;
 
 import java.util.ArrayList;
+import java.util.Collections;
 import java.util.List;
+import java.util.concurrent.Callable;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.TimeUnit;
+
+import javax.annotation.PostConstruct;
 
 import org.codelibs.fess.mylasta.direction.FessConfig;
 import org.codelibs.fess.suggest.request.popularwords.PopularWordsRequestBuilder;
@@ -25,9 +31,26 @@ import org.codelibs.fess.util.StreamUtil;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import com.google.common.cache.Cache;
+import com.google.common.cache.CacheBuilder;
+
 public class PopularWordHelper {
     private static final Logger logger = LoggerFactory.getLogger(PopularWordHelper.class);
 
+    private static final char CACHE_KEY_SPLITTER = '\n';
+
+    private Cache<String, List<String>> cache;
+
+    private FessConfig fessConfig;
+
+    @PostConstruct
+    public void init() {
+        fessConfig = ComponentUtil.getFessConfig();
+        cache =
+                CacheBuilder.newBuilder().maximumSize(fessConfig.getSuggestPopularWordCacheSizeAsInteger().longValue())
+                        .expireAfterWrite(fessConfig.getSuggestPopularWordCacheExpireAsInteger().longValue(), TimeUnit.MINUTES).build();
+    }
+
     public List<String> getWordList(String seed, String[] tags, String[] fields, String[] excludes) {
         RoleQueryHelper roleQueryHelper = ComponentUtil.getRoleQueryHelper();
         String[] roles = roleQueryHelper.build().stream().toArray(n -> new String[n]);
@@ -35,25 +58,46 @@ public class PopularWordHelper {
     }
 
     public List<String> getWordList(String seed, String[] tags, String[] roles, String[] fields, String[] excludes) {
-        // TODO cache
-        final List<String> wordList = new ArrayList<String>();
-        FessConfig fessConfig = ComponentUtil.getFessConfig();
-        SuggestHelper suggestHelper = ComponentUtil.getSuggestHelper();
-        final PopularWordsRequestBuilder popularWordsRequestBuilder =
-                suggestHelper.suggester().popularWords().setSize(fessConfig.getSuggestPopularWordSizeAsInteger().intValue())
-                        .setWindowSize(fessConfig.getSuggestPopularWordWindowSizeAsInteger().intValue());
-        if (seed != null) {
-            popularWordsRequestBuilder.setSeed(seed);
+        try {
+            return cache.get(getCacheKey(seed, tags, roles, fields, excludes), new Callable<List<String>>() {
+                @Override
+                public List<String> call() throws Exception {
+                    final List<String> wordList = new ArrayList<String>();
+                    SuggestHelper suggestHelper = ComponentUtil.getSuggestHelper();
+                    final PopularWordsRequestBuilder popularWordsRequestBuilder =
+                            suggestHelper.suggester().popularWords().setSize(fessConfig.getSuggestPopularWordSizeAsInteger().intValue())
+                                    .setWindowSize(fessConfig.getSuggestPopularWordWindowSizeAsInteger().intValue());
+                    if (seed != null) {
+                        popularWordsRequestBuilder.setSeed(seed);
+                    }
+                    StreamUtil.of(tags).forEach(tag -> popularWordsRequestBuilder.addTag(tag));
+                    StreamUtil.of(roles).forEach(role -> popularWordsRequestBuilder.addRole(role));
+                    StreamUtil.of(fields).forEach(field -> popularWordsRequestBuilder.addField(field));
+                    StreamUtil.of(excludes).forEach(exclude -> popularWordsRequestBuilder.addExcludeWord(exclude));
+                    popularWordsRequestBuilder.execute().then(r -> {
+                        r.getItems().stream().forEach(item -> wordList.add(item.getText()));
+                    }).error(t -> logger.warn("Failed to generate popular words.", t));
+
+                    return wordList;
+                }
+            });
+        } catch (ExecutionException e) {
+            logger.warn("Failed to load popular words.", e);
         }
-        StreamUtil.of(tags).forEach(tag -> popularWordsRequestBuilder.addTag(tag));
-        StreamUtil.of(roles).forEach(role -> popularWordsRequestBuilder.addRole(role));
-        StreamUtil.of(fields).forEach(field -> popularWordsRequestBuilder.addField(field));
-        StreamUtil.of(excludes).forEach(exclude -> popularWordsRequestBuilder.addExcludeWord(exclude));
-        popularWordsRequestBuilder.execute().then(r -> {
-            r.getItems().stream().forEach(item -> wordList.add(item.getText()));
-        }).error(t -> logger.warn("Failed to generate popular words.", t));
-
-        return wordList;
+        return Collections.emptyList();
+    }
+
+    protected String getCacheKey(String seed, String[] tags, String[] roles, String[] fields, String[] excludes) {
+        StringBuilder buf = new StringBuilder(100);
+        buf.append(seed).append(CACHE_KEY_SPLITTER);
+        StreamUtil.of(tags).sorted().reduce((l, r) -> l + r).ifPresent(v -> buf.append(v));
+        buf.append(CACHE_KEY_SPLITTER);
+        StreamUtil.of(roles).sorted().reduce((l, r) -> l + r).ifPresent(v -> buf.append(v));
+        buf.append(CACHE_KEY_SPLITTER);
+        StreamUtil.of(fields).sorted().reduce((l, r) -> l + r).ifPresent(v -> buf.append(v));
+        buf.append(CACHE_KEY_SPLITTER);
+        StreamUtil.of(excludes).sorted().reduce((l, r) -> l + r).ifPresent(v -> buf.append(v));
+        return buf.toString();
     }
 
 }

+ 52 - 0
src/main/java/org/codelibs/fess/mylasta/direction/FessConfig.java

@@ -343,6 +343,12 @@ public interface FessConfig extends FessEnv {
     /** The key of the configuration. e.g. 1 */
     String SUGGEST_SOURCE_READER_SCROLL_SIZE = "suggest.source.reader.scroll.size";
 
+    /** The key of the configuration. e.g. 1000 */
+    String SUGGEST_POPULAR_WORD_CACHE_SIZE = "suggest.popular.word.cache.size";
+
+    /** The key of the configuration. e.g. 60 */
+    String SUGGEST_POPULAR_WORD_CACHE_EXPIRE = "suggest.popular.word.cache.expire";
+
     /** The key of the configuration. e.g. guest */
     String SUGGEST_ROLE_FILTERS = "suggest.role.filters";
 
@@ -1357,6 +1363,36 @@ public interface FessConfig extends FessEnv {
      */
     Integer getSuggestSourceReaderScrollSizeAsInteger();
 
+    /**
+     * Get the value for the key 'suggest.popular.word.cache.size'. <br>
+     * The value is, e.g. 1000 <br>
+     * @return The value of found property. (NotNull: if not found, exception but basically no way)
+     */
+    String getSuggestPopularWordCacheSize();
+
+    /**
+     * Get the value for the key 'suggest.popular.word.cache.size' as {@link Integer}. <br>
+     * The value is, e.g. 1000 <br>
+     * @return The value of found property. (NotNull: if not found, exception but basically no way)
+     * @throws NumberFormatException When the property is not integer.
+     */
+    Integer getSuggestPopularWordCacheSizeAsInteger();
+
+    /**
+     * Get the value for the key 'suggest.popular.word.cache.expire'. <br>
+     * The value is, e.g. 60 <br>
+     * @return The value of found property. (NotNull: if not found, exception but basically no way)
+     */
+    String getSuggestPopularWordCacheExpire();
+
+    /**
+     * Get the value for the key 'suggest.popular.word.cache.expire' as {@link Integer}. <br>
+     * The value is, e.g. 60 <br>
+     * @return The value of found property. (NotNull: if not found, exception but basically no way)
+     * @throws NumberFormatException When the property is not integer.
+     */
+    Integer getSuggestPopularWordCacheExpireAsInteger();
+
     /**
      * Get the value for the key 'suggest.role.filters'. <br>
      * The value is, e.g. guest <br>
@@ -1909,6 +1945,22 @@ public interface FessConfig extends FessEnv {
             return getAsInteger(FessConfig.SUGGEST_SOURCE_READER_SCROLL_SIZE);
         }
 
+        public String getSuggestPopularWordCacheSize() {
+            return get(FessConfig.SUGGEST_POPULAR_WORD_CACHE_SIZE);
+        }
+
+        public Integer getSuggestPopularWordCacheSizeAsInteger() {
+            return getAsInteger(FessConfig.SUGGEST_POPULAR_WORD_CACHE_SIZE);
+        }
+
+        public String getSuggestPopularWordCacheExpire() {
+            return get(FessConfig.SUGGEST_POPULAR_WORD_CACHE_EXPIRE);
+        }
+
+        public Integer getSuggestPopularWordCacheExpireAsInteger() {
+            return getAsInteger(FessConfig.SUGGEST_POPULAR_WORD_CACHE_EXPIRE);
+        }
+
         public String getSuggestRoleFilters() {
             return get(FessConfig.SUGGEST_ROLE_FILTERS);
         }

+ 3 - 0
src/main/resources/fess_config.properties

@@ -206,6 +206,9 @@ suggest.field.roles=role
 suggest.field.index.contents=content,title
 suggest.update.request.interval=1
 suggest.source.reader.scroll.size=1
+suggest.popular.word.cache.size=1000
+suggest.popular.word.cache.expire=60
 suggest.role.filters=\
 guest
 
+