1
2
3
4
5
6
7
8
9
10
11
12
13
14
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 }