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.ArrayList;
19 import java.util.Collections;
20 import java.util.HashMap;
21 import java.util.List;
22 import java.util.Locale;
23 import java.util.Map;
24
25 import javax.annotation.PostConstruct;
26
27 import org.apache.logging.log4j.LogManager;
28 import org.apache.logging.log4j.Logger;
29 import org.codelibs.core.lang.StringUtil;
30 import org.codelibs.core.misc.Tuple3;
31 import org.codelibs.fesen.index.query.BoolQueryBuilder;
32 import org.codelibs.fesen.index.query.QueryBuilder;
33 import org.codelibs.fesen.index.query.QueryBuilders;
34 import org.codelibs.fesen.index.query.functionscore.FunctionScoreQueryBuilder.FilterFunctionBuilder;
35 import org.codelibs.fesen.index.query.functionscore.ScoreFunctionBuilder;
36 import org.codelibs.fesen.index.query.functionscore.ScoreFunctionBuilders;
37 import org.codelibs.fess.Constants;
38 import org.codelibs.fess.entity.SearchRequestParams.SearchRequestType;
39 import org.codelibs.fess.es.client.SearchEngineClient;
40 import org.codelibs.fess.es.client.SearchEngineClient.SearchConditionBuilder;
41 import org.codelibs.fess.es.config.exbhv.KeyMatchBhv;
42 import org.codelibs.fess.es.config.exentity.KeyMatch;
43 import org.codelibs.fess.mylasta.direction.FessConfig;
44 import org.codelibs.fess.util.ComponentUtil;
45 import org.codelibs.fess.util.DocumentUtil;
46
47 public class KeyMatchHelper extends AbstractConfigHelper {
48 private static final Logger logger = LogManager.getLogger(KeyMatchHelper.class);
49
50 protected volatile Map<String, Map<String, List<Tuple3<String, QueryBuilder, ScoreFunctionBuilder<?>>>>> keyMatchQueryMap =
51 Collections.emptyMap();
52
53 @PostConstruct
54 public void init() {
55 if (logger.isDebugEnabled()) {
56 logger.debug("Initialize {}", this.getClass().getSimpleName());
57 }
58 load();
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 @Override
69 public int load() {
70 final FessConfig fessConfig = ComponentUtil.getFessConfig();
71 final Map<String, Map<String, List<Tuple3<String, QueryBuilder, ScoreFunctionBuilder<?>>>>> keyMatchQueryMap = new HashMap<>();
72 getAvailableKeyMatchList().stream().forEach(keyMatch -> {
73 try {
74 final BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();
75 if (logger.isDebugEnabled()) {
76 logger.debug("Loading KeyMatch Query: {}, Size: {}", keyMatch.getQuery(), keyMatch.getMaxSize());
77 }
78 getDocumentList(keyMatch).stream().map(doc -> {
79 if (logger.isDebugEnabled()) {
80 logger.debug("Loaded KeyMatch doc: {}", doc);
81 }
82 return DocumentUtil.getValue(doc, fessConfig.getIndexFieldDocId(), String.class);
83 }).forEach(docId -> {
84 boolQuery.should(QueryBuilders.termQuery(fessConfig.getIndexFieldDocId(), docId));
85 });
86
87 if (boolQuery.hasClauses()) {
88 if (logger.isDebugEnabled()) {
89 logger.debug("Loaded KeyMatch Boost Query: {}", boolQuery);
90 }
91 String virtualHost = keyMatch.getVirtualHost();
92 if (StringUtil.isBlank(virtualHost)) {
93 virtualHost = StringUtil.EMPTY;
94 }
95 Map<String, List<Tuple3<String, QueryBuilder, ScoreFunctionBuilder<?>>>> queryMap = keyMatchQueryMap.get(virtualHost);
96 if (queryMap == null) {
97 queryMap = new HashMap<>();
98 keyMatchQueryMap.put(virtualHost, queryMap);
99 }
100 final String termKey = toLowerCase(keyMatch.getTerm());
101 List<Tuple3<String, QueryBuilder, ScoreFunctionBuilder<?>>> boostList = queryMap.get(termKey);
102 if (boostList == null) {
103 boostList = new ArrayList<>();
104 queryMap.put(termKey, boostList);
105 }
106 boostList.add(
107 new Tuple3<>(keyMatch.getId(), boolQuery, ScoreFunctionBuilders.weightFactorFunction(keyMatch.getBoost())));
108 } else if (logger.isDebugEnabled()) {
109 logger.debug("No KeyMatch boost docs");
110 }
111
112 waitForNext();
113 } catch (final Exception e) {
114 logger.warn("Cannot load {}", keyMatch, e);
115 }
116 });
117 this.keyMatchQueryMap = keyMatchQueryMap;
118 return keyMatchQueryMap.size();
119 }
120
121 protected List<Map<String, Object>> getDocumentList(final KeyMatch keyMatch) {
122 final SearchEngineClient searchEngineClient = ComponentUtil.getSearchEngineClient();
123 final FessConfig fessConfig = ComponentUtil.getFessConfig();
124 return searchEngineClient.getDocumentList(fessConfig.getIndexDocumentSearchIndex(),
125 searchRequestBuilder -> SearchConditionBuilder
126 .builder(searchRequestBuilder.setPreference(Constants.SEARCH_PREFERENCE_LOCAL))
127 .searchRequestType(SearchRequestType.ADMIN_SEARCH).size(keyMatch.getMaxSize()).query(keyMatch.getQuery())
128 .responseFields(new String[] { fessConfig.getIndexFieldDocId() }).build());
129 }
130
131 protected Map<String, List<Tuple3<String, QueryBuilder, ScoreFunctionBuilder<?>>>> getQueryMap(final String key) {
132 final Map<String, List<Tuple3<String, QueryBuilder, ScoreFunctionBuilder<?>>>> map = keyMatchQueryMap.get(key);
133 if (map != null) {
134 return map;
135 }
136 return Collections.emptyMap();
137 }
138
139 public void buildQuery(final List<String> keywordList, final List<FilterFunctionBuilder> list) {
140 final String key = ComponentUtil.getVirtualHostHelper().getVirtualHostKey();
141 keywordList.stream().forEach(keyword -> {
142 final List<Tuple3<String, QueryBuilder, ScoreFunctionBuilder<?>>> boostList = getQueryMap(key).get(toLowerCase(keyword));
143 if (boostList != null) {
144 boostList.forEach(pair -> list.add(new FilterFunctionBuilder(pair.getValue2(), pair.getValue3())));
145 }
146 });
147 }
148
149 public List<Map<String, Object>> getBoostedDocumentList(final KeyMatch keyMatch) {
150 final SearchEngineClient searchEngineClient = ComponentUtil.getSearchEngineClient();
151 String virtualHost = keyMatch.getVirtualHost();
152 if (StringUtil.isBlank(virtualHost)) {
153 virtualHost = StringUtil.EMPTY;
154 }
155 final List<Tuple3<String, QueryBuilder, ScoreFunctionBuilder<?>>> boostList =
156 getQueryMap(virtualHost).get(toLowerCase(keyMatch.getTerm()));
157 if (boostList == null) {
158 return Collections.emptyList();
159 }
160 for (final Tuple3<String, QueryBuilder, ScoreFunctionBuilder<?>> pair : boostList) {
161 if (!keyMatch.getId().equals(pair.getValue1())) {
162 continue;
163 }
164 final FessConfig fessConfig = ComponentUtil.getFessConfig();
165 return searchEngineClient.getDocumentList(fessConfig.getIndexDocumentSearchIndex(), searchRequestBuilder -> {
166 searchRequestBuilder.setPreference(Constants.SEARCH_PREFERENCE_LOCAL).setQuery(pair.getValue2())
167 .setSize(keyMatch.getMaxSize());
168 return true;
169 });
170 }
171 return Collections.emptyList();
172 }
173
174 private String toLowerCase(final String term) {
175 return term != null ? term.toLowerCase(Locale.ROOT) : term;
176 }
177
178 }