View Javadoc
1   /*
2    * Copyright 2012-2021 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.apache.logging.log4j.LogManager;
29  import org.apache.logging.log4j.Logger;
30  import org.codelibs.core.lang.StringUtil;
31  import org.codelibs.fess.entity.SearchRequestParams.SearchRequestType;
32  import org.codelibs.fess.mylasta.direction.FessConfig;
33  import org.codelibs.fess.suggest.exception.SuggesterException;
34  import org.codelibs.fess.suggest.request.popularwords.PopularWordsRequestBuilder;
35  import org.codelibs.fess.util.ComponentUtil;
36  
37  import com.google.common.cache.Cache;
38  import com.google.common.cache.CacheBuilder;
39  
40  public class PopularWordHelper {
41      private static final Logger logger = LogManager.getLogger(PopularWordHelper.class);
42  
43      protected static final char CACHE_KEY_SPLITTER = '\n';
44  
45      protected Cache<String, List<String>> cache;
46  
47      protected FessConfig fessConfig;
48  
49      @PostConstruct
50      public void init() {
51          if (logger.isDebugEnabled()) {
52              logger.debug("Initialize {}", this.getClass().getSimpleName());
53          }
54          fessConfig = ComponentUtil.getFessConfig();
55          cache = CacheBuilder.newBuilder().maximumSize(fessConfig.getSuggestPopularWordCacheSizeAsInteger().longValue())
56                  .expireAfterWrite(fessConfig.getSuggestPopularWordCacheExpireAsInteger().longValue(), TimeUnit.MINUTES).build();
57      }
58  
59      public List<String> getWordList(final SearchRequestType searchRequestType, final String seed, final String[] tags, final String[] roles,
60              final String[] fields, final String[] excludes) {
61          final String baseSeed = seed != null ? seed : fessConfig.getSuggestPopularWordSeed();
62          final String[] baseTags = tags != null ? tags : fessConfig.getSuggestPopularWordTagsAsArray();
63          final String[] baseRoles = roles != null ? roles
64                  : ComponentUtil.getRoleQueryHelper().build(searchRequestType).stream().filter(StringUtil::isNotBlank)
65                          .toArray(n -> new String[n]);
66          final String[] baseFields = fields != null ? fields : fessConfig.getSuggestPopularWordFieldsAsArray();
67          final String[] baseExcludes = excludes != null ? excludes : fessConfig.getSuggestPopularWordExcludesAsArray();
68          try {
69              return cache.get(getCacheKey(baseSeed, baseTags, baseRoles, baseFields, baseExcludes), () -> {
70                  final List<String> wordList = new ArrayList<>();
71                  final SuggestHelper suggestHelper = ComponentUtil.getSuggestHelper();
72                  final PopularWordsRequestBuilder popularWordsRequestBuilder =
73                          suggestHelper.suggester().popularWords().setSize(fessConfig.getSuggestPopularWordSizeAsInteger())
74                                  .setWindowSize(fessConfig.getSuggestPopularWordWindowSizeAsInteger())
75                                  .setQueryFreqThreshold(fessConfig.getSuggestPopularWordQueryFreqAsInteger());
76                  popularWordsRequestBuilder.setSeed(baseSeed);
77                  stream(baseTags).of(stream -> stream.forEach(tag -> popularWordsRequestBuilder.addTag(tag)));
78                  stream(baseRoles).of(stream -> stream.forEach(role -> popularWordsRequestBuilder.addRole(role)));
79                  stream(baseFields).of(stream -> stream.forEach(field -> popularWordsRequestBuilder.addField(field)));
80                  stream(baseExcludes).of(stream -> stream.forEach(exclude -> popularWordsRequestBuilder.addExcludeWord(exclude)));
81                  try {
82                      popularWordsRequestBuilder.execute().getResponse().getItems().stream().forEach(item -> wordList.add(item.getText()));
83                  } catch (final SuggesterException e) {
84                      logger.warn("Failed to generate popular words.", e);
85                  }
86  
87                  return wordList;
88              });
89          } catch (final ExecutionException e) {
90              logger.warn("Failed to load popular words.", e);
91          }
92          return Collections.emptyList();
93      }
94  
95      public void clearCache() {
96          cache.invalidateAll();
97      }
98  
99      protected String getCacheKey(final String seed, final String[] tags, final String[] roles, final String[] fields,
100             final String[] excludes) {
101         final StringBuilder buf = new StringBuilder(100);
102         buf.append(seed).append(CACHE_KEY_SPLITTER);
103         stream(tags).of(stream -> stream.sorted().reduce((l, r) -> l + r).ifPresent(v -> buf.append(v)));
104         buf.append(CACHE_KEY_SPLITTER);
105         stream(roles).of(stream -> stream.sorted().reduce((l, r) -> l + r).ifPresent(v -> buf.append(v)));
106         buf.append(CACHE_KEY_SPLITTER);
107         stream(fields).of(stream -> stream.sorted().reduce((l, r) -> l + r).ifPresent(v -> buf.append(v)));
108         buf.append(CACHE_KEY_SPLITTER);
109         stream(excludes).of(stream -> stream.sorted().reduce((l, r) -> l + r).ifPresent(v -> buf.append(v)));
110         return buf.toString();
111     }
112 
113 }