View Javadoc
1   /*
2    * Copyright 2012-2017 CodeLibs Project and the Others.
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    *     http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
13   * either express or implied. See the License for the specific language
14   * governing permissions and limitations under the License.
15   */
16  package org.codelibs.fess.helper;
17  
18  import static org.codelibs.core.stream.StreamUtil.stream;
19  
20  import java.util.ArrayList;
21  import java.util.Collections;
22  import java.util.List;
23  import java.util.concurrent.ExecutionException;
24  import java.util.concurrent.TimeUnit;
25  
26  import javax.annotation.PostConstruct;
27  
28  import org.codelibs.core.lang.StringUtil;
29  import org.codelibs.fess.entity.SearchRequestParams.SearchRequestType;
30  import org.codelibs.fess.mylasta.direction.FessConfig;
31  import org.codelibs.fess.suggest.request.popularwords.PopularWordsRequestBuilder;
32  import org.codelibs.fess.util.ComponentUtil;
33  import org.slf4j.Logger;
34  import org.slf4j.LoggerFactory;
35  
36  import com.google.common.cache.Cache;
37  import com.google.common.cache.CacheBuilder;
38  
39  public class PopularWordHelper {
40      private static final Logger logger = LoggerFactory.getLogger(PopularWordHelper.class);
41  
42      private static final char CACHE_KEY_SPLITTER = '\n';
43  
44      private Cache<String, List<String>> cache;
45  
46      private FessConfig fessConfig;
47  
48      @PostConstruct
49      public void init() {
50          fessConfig = ComponentUtil.getFessConfig();
51          cache =
52                  CacheBuilder.newBuilder().maximumSize(fessConfig.getSuggestPopularWordCacheSizeAsInteger().longValue())
53                          .expireAfterWrite(fessConfig.getSuggestPopularWordCacheExpireAsInteger().longValue(), TimeUnit.MINUTES).build();
54      }
55  
56      public List<String> getWordList(final SearchRequestType searchRequestType, final String seed, final String[] tags,
57              final String[] roles, final String[] fields, final String[] excludes) {
58          final String baseSeed = seed != null ? seed : fessConfig.getSuggestPopularWordSeed();
59          final String[] baseTags = tags != null ? tags : fessConfig.getSuggestPopularWordTagsAsArray();
60          final String[] baseRoles =
61                  roles != null ? roles : ComponentUtil.getRoleQueryHelper().build(searchRequestType).stream().filter(StringUtil::isNotBlank)
62                          .toArray(n -> new String[n]);
63          final String[] baseFields = fields != null ? fields : fessConfig.getSuggestPopularWordFieldsAsArray();
64          final String[] baseExcludes = excludes != null ? excludes : fessConfig.getSuggestPopularWordExcludesAsArray();
65          try {
66              return cache.get(
67                      getCacheKey(baseSeed, baseTags, baseRoles, baseFields, baseExcludes),
68                      () -> {
69                          final List<String> wordList = new ArrayList<>();
70                          final SuggestHelper suggestHelper = ComponentUtil.getSuggestHelper();
71                          final PopularWordsRequestBuilder popularWordsRequestBuilder =
72                                  suggestHelper.suggester().popularWords()
73                                          .setSize(fessConfig.getSuggestPopularWordSizeAsInteger().intValue())
74                                          .setWindowSize(fessConfig.getSuggestPopularWordWindowSizeAsInteger().intValue());
75                          popularWordsRequestBuilder.setSeed(baseSeed);
76                          stream(baseTags).of(stream -> stream.forEach(tag -> popularWordsRequestBuilder.addTag(tag)));
77                          stream(baseRoles).of(stream -> stream.forEach(role -> popularWordsRequestBuilder.addRole(role)));
78                          stream(baseFields).of(stream -> stream.forEach(field -> popularWordsRequestBuilder.addField(field)));
79                          stream(baseExcludes).of(stream -> stream.forEach(exclude -> popularWordsRequestBuilder.addExcludeWord(exclude)));
80                          popularWordsRequestBuilder.execute().then(r -> {
81                              r.getItems().stream().forEach(item -> wordList.add(item.getText()));
82                          }).error(t -> logger.warn("Failed to generate popular words.", t));
83  
84                          return wordList;
85                      });
86          } catch (final ExecutionException e) {
87              logger.warn("Failed to load popular words.", e);
88          }
89          return Collections.emptyList();
90      }
91  
92      public void clearCache() {
93          cache.invalidateAll();
94      }
95  
96      protected String getCacheKey(final String seed, final String[] tags, final String[] roles, final String[] fields,
97              final String[] excludes) {
98          final StringBuilder buf = new StringBuilder(100);
99          buf.append(seed).append(CACHE_KEY_SPLITTER);
100         stream(tags).of(stream -> stream.sorted().reduce((l, r) -> l + r).ifPresent(v -> buf.append(v)));
101         buf.append(CACHE_KEY_SPLITTER);
102         stream(roles).of(stream -> stream.sorted().reduce((l, r) -> l + r).ifPresent(v -> buf.append(v)));
103         buf.append(CACHE_KEY_SPLITTER);
104         stream(fields).of(stream -> stream.sorted().reduce((l, r) -> l + r).ifPresent(v -> buf.append(v)));
105         buf.append(CACHE_KEY_SPLITTER);
106         stream(excludes).of(stream -> stream.sorted().reduce((l, r) -> l + r).ifPresent(v -> buf.append(v)));
107         return buf.toString();
108     }
109 
110 }