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 java.util.Collections;
19  import java.util.HashMap;
20  import java.util.List;
21  import java.util.Locale;
22  import java.util.Map;
23  
24  import javax.annotation.PostConstruct;
25  
26  import org.codelibs.core.misc.Pair;
27  import org.codelibs.fess.Constants;
28  import org.codelibs.fess.entity.SearchRequestParams.SearchRequestType;
29  import org.codelibs.fess.es.client.FessEsClient;
30  import org.codelibs.fess.es.client.FessEsClient.SearchConditionBuilder;
31  import org.codelibs.fess.es.config.exbhv.KeyMatchBhv;
32  import org.codelibs.fess.es.config.exentity.KeyMatch;
33  import org.codelibs.fess.mylasta.direction.FessConfig;
34  import org.codelibs.fess.util.ComponentUtil;
35  import org.codelibs.fess.util.DocumentUtil;
36  import org.elasticsearch.index.query.BoolQueryBuilder;
37  import org.elasticsearch.index.query.QueryBuilder;
38  import org.elasticsearch.index.query.QueryBuilders;
39  import org.elasticsearch.index.query.functionscore.FunctionScoreQueryBuilder.FilterFunctionBuilder;
40  import org.elasticsearch.index.query.functionscore.ScoreFunctionBuilder;
41  import org.elasticsearch.index.query.functionscore.ScoreFunctionBuilders;
42  import org.slf4j.Logger;
43  import org.slf4j.LoggerFactory;
44  
45  public class KeyMatchHelper {
46      private static final Logger logger = LoggerFactory.getLogger(KeyMatchHelper.class);
47  
48      protected volatile Map<String, Map<String, Pair<QueryBuilder, ScoreFunctionBuilder<?>>>> keyMatchQueryMap = Collections.emptyMap();
49  
50      protected long reloadInterval = 1000L;
51  
52      @PostConstruct
53      public void init() {
54          reload(0);
55      }
56  
57      public void update() {
58          new Thread(() -> reload(reloadInterval)).start();
59      }
60  
61      public List<KeyMatch> getAvailableKeyMatchList() {
62          return ComponentUtil.getComponent(KeyMatchBhv.class).selectList(cb -> {
63              cb.query().matchAll();
64              cb.fetchFirst(ComponentUtil.getFessConfig().getPageKeymatchMaxFetchSizeAsInteger());
65          });
66      }
67  
68      protected void reload(final long interval) {
69          final FessConfig fessConfig = ComponentUtil.getFessConfig();
70          final Map<String, Map<String, Pair<QueryBuilder, ScoreFunctionBuilder<?>>>> keyMatchQueryMap = new HashMap<>();
71          getAvailableKeyMatchList().stream().forEach(
72                  keyMatch -> {
73                      final BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();
74                      getDocumentList(keyMatch).stream().map(doc -> {
75                          return DocumentUtil.getValue(doc, fessConfig.getIndexFieldDocId(), String.class);
76                      }).forEach(docId -> {
77                          boolQuery.should(QueryBuilders.termQuery(fessConfig.getIndexFieldDocId(), docId));
78                      });
79  
80                      if (boolQuery.hasClauses()) {
81                          final String virtualHost = keyMatch.getVirtualHost();
82                          Map<String, Pair<QueryBuilder, ScoreFunctionBuilder<?>>> queryMap = keyMatchQueryMap.get(virtualHost);
83                          if (queryMap == null) {
84                              queryMap = new HashMap<>();
85                              keyMatchQueryMap.put(virtualHost, queryMap);
86                          }
87                          queryMap.put(toLowerCase(keyMatch.getTerm()),
88                                  new Pair<>(boolQuery, ScoreFunctionBuilders.weightFactorFunction(keyMatch.getBoost())));
89                      }
90  
91                      if (interval > 0) {
92                          try {
93                              Thread.sleep(interval);
94                          } catch (final InterruptedException e) {
95                              if (logger.isDebugEnabled()) {
96                                  logger.debug("Interrupted.", e);
97                              }
98                          }
99                      }
100                 });
101         this.keyMatchQueryMap = keyMatchQueryMap;
102     }
103 
104     protected List<Map<String, Object>> getDocumentList(final KeyMatch keyMatch) {
105         final FessEsClient fessEsClient = ComponentUtil.getFessEsClient();
106         final FessConfig fessConfig = ComponentUtil.getFessConfig();
107         return fessEsClient.getDocumentList(fessConfig.getIndexDocumentSearchIndex(), fessConfig.getIndexDocumentType(),
108                 searchRequestBuilder -> {
109                     return SearchConditionBuilder.builder(searchRequestBuilder.setPreference(Constants.SEARCH_PREFERENCE_PRIMARY))
110                             .searchRequestType(SearchRequestType.ADMIN_SEARCH).size(keyMatch.getMaxSize()).query(keyMatch.getQuery())
111                             .responseFields(new String[] { fessConfig.getIndexFieldDocId() }).build();
112                 });
113     }
114 
115     public long getReloadInterval() {
116         return reloadInterval;
117     }
118 
119     public void setReloadInterval(final long reloadInterval) {
120         this.reloadInterval = reloadInterval;
121     }
122 
123     protected Map<String, Pair<QueryBuilder, ScoreFunctionBuilder<?>>> getQueryMap() {
124         final String key = ComponentUtil.getFessConfig().getVirtualHostKey();
125         final Map<String, Pair<QueryBuilder, ScoreFunctionBuilder<?>>> map = keyMatchQueryMap.get(key);
126         if (map != null) {
127             return map;
128         }
129         return Collections.emptyMap();
130     }
131 
132     public void buildQuery(final List<String> keywordList, final List<FilterFunctionBuilder> list) {
133         keywordList.stream().forEach(keyword -> {
134             final Pair<QueryBuilder, ScoreFunctionBuilder<?>> pair = getQueryMap().get(toLowerCase(keyword));
135             if (pair != null) {
136                 list.add(new FilterFunctionBuilder(pair.getFirst(), pair.getSecond()));
137             }
138         });
139     }
140 
141     public List<Map<String, Object>> getBoostedDocumentList(final String term, final int size) {
142         final FessEsClient fessEsClient = ComponentUtil.getFessEsClient();
143         final Pair<QueryBuilder, ScoreFunctionBuilder<?>> pair = getQueryMap().get(toLowerCase(term));
144         if (pair == null) {
145             return Collections.emptyList();
146         }
147         final FessConfig fessConfig = ComponentUtil.getFessConfig();
148         return fessEsClient.getDocumentList(fessConfig.getIndexDocumentSearchIndex(), fessConfig.getIndexDocumentType(),
149                 searchRequestBuilder -> {
150                     searchRequestBuilder.setPreference(Constants.SEARCH_PREFERENCE_PRIMARY).setQuery(pair.getFirst()).setSize(size);
151                     return true;
152                 });
153     }
154 
155     private String toLowerCase(final String term) {
156         return term != null ? term.toLowerCase(Locale.ROOT) : term;
157     }
158 
159 }