diff --git a/src/main/java/org/codelibs/fess/helper/PopularWordHelper.java b/src/main/java/org/codelibs/fess/helper/PopularWordHelper.java index bd53a6766..085a6f25c 100644 --- a/src/main/java/org/codelibs/fess/helper/PopularWordHelper.java +++ b/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> 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 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 getWordList(String seed, String[] tags, String[] roles, String[] fields, String[] excludes) { - // TODO cache - final List wordList = new ArrayList(); - 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); - } - 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)); + try { + return cache.get(getCacheKey(seed, tags, roles, fields, excludes), new Callable>() { + @Override + public List call() throws Exception { + final List wordList = new ArrayList(); + 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; + return wordList; + } + }); + } catch (ExecutionException e) { + logger.warn("Failed to load popular words.", e); + } + 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(); } } diff --git a/src/main/java/org/codelibs/fess/mylasta/direction/FessConfig.java b/src/main/java/org/codelibs/fess/mylasta/direction/FessConfig.java index df82d77b4..c2871f9da 100644 --- a/src/main/java/org/codelibs/fess/mylasta/direction/FessConfig.java +++ b/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'.
+ * The value is, e.g. 1000
+ * @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}.
+ * The value is, e.g. 1000
+ * @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'.
+ * The value is, e.g. 60
+ * @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}.
+ * The value is, e.g. 60
+ * @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'.
* The value is, e.g. guest
@@ -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); } diff --git a/src/main/resources/fess_config.properties b/src/main/resources/fess_config.properties index c7fd31248..0e47c80ac 100644 --- a/src/main/resources/fess_config.properties +++ b/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 +