1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package org.codelibs.fess.query;
17
18 import static org.codelibs.core.stream.StreamUtil.split;
19 import static org.codelibs.fess.Constants.DEFAULT_FIELD;
20 import static org.codelibs.fess.query.QueryFieldConfig.INURL_FIELD;
21 import static org.codelibs.fess.query.QueryFieldConfig.SITE_FIELD;
22
23 import org.apache.logging.log4j.LogManager;
24 import org.apache.logging.log4j.Logger;
25 import org.apache.lucene.index.Term;
26 import org.apache.lucene.search.PrefixQuery;
27 import org.apache.lucene.search.Query;
28 import org.apache.lucene.search.TermQuery;
29 import org.codelibs.core.lang.StringUtil;
30 import org.codelibs.fess.Constants;
31 import org.codelibs.fess.entity.QueryContext;
32 import org.codelibs.fess.exception.InvalidQueryException;
33 import org.codelibs.fess.mylasta.direction.FessConfig;
34 import org.codelibs.fess.util.ComponentUtil;
35 import org.lastaflute.core.message.UserMessages;
36 import org.opensearch.common.unit.Fuzziness;
37 import org.opensearch.index.query.QueryBuilder;
38 import org.opensearch.index.query.QueryBuilders;
39 import org.opensearch.search.sort.SortOrder;
40
41
42
43
44
45 public class TermQueryCommand extends QueryCommand {
46 private static final Logger logger = LogManager.getLogger(TermQueryCommand.class);
47
48
49
50
51 public TermQueryCommand() {
52 super();
53 }
54
55 private static final String SORT_FIELD = "sort";
56
57 @Override
58 protected String getQueryClassName() {
59 return TermQuery.class.getSimpleName();
60 }
61
62 @Override
63 public QueryBuilder execute(final QueryContext context, final Query query, final float boost) {
64 if (query instanceof final TermQuery termQuery) {
65 if (logger.isDebugEnabled()) {
66 logger.debug("TermQuery: query={}, boost={}", query, boost);
67 }
68 return convertTermQuery(context, termQuery, boost);
69 }
70 throw new InvalidQueryException(messages -> messages.addErrorsInvalidQueryUnknown(UserMessages.GLOBAL_PROPERTY_KEY),
71 "Unknown q: " + query.getClass() + " => " + query);
72 }
73
74
75
76
77
78
79
80
81
82 protected QueryBuilder convertTermQuery(final QueryContext context, final TermQuery termQuery, final float boost) {
83 final String field = getSearchField(context.getDefaultField(), termQuery.getTerm().field());
84 final String text = termQuery.getTerm().text();
85 final FessConfig fessConfig = ComponentUtil.getFessConfig();
86 return convertTermQuery(fessConfig, context, termQuery, boost, field, text);
87 }
88
89
90
91
92
93
94
95
96
97
98
99
100 protected QueryBuilder convertTermQuery(final FessConfig fessConfig, final QueryContext context, final TermQuery termQuery,
101 final float boost, final String field, final String text) {
102 if (fessConfig.getQueryReplaceTermWithPrefixQueryAsBoolean() && text.length() > 1 && text.endsWith("*")) {
103 return convertPrefixQuery(fessConfig, context, termQuery, boost, field, text);
104 }
105 if (DEFAULT_FIELD.equals(field)) {
106 return convertDefaultTermQuery(fessConfig, context, termQuery, boost, field, text);
107 }
108 if (SORT_FIELD.equals(field)) {
109 return convertSortQuery(fessConfig, context, termQuery, boost, field, text);
110 }
111 if (SITE_FIELD.equals(field)) {
112 return convertSiteQuery(fessConfig, context, termQuery, boost, field, text);
113 }
114 if (INURL_FIELD.equals(field)
115 || StringUtil.equals(field, context.getDefaultField()) && fessConfig.getIndexFieldUrl().equals(context.getDefaultField())) {
116 return convertWildcardQuery(fessConfig, context, termQuery, boost, field, text);
117 }
118 if (!isSearchField(field)) {
119 final String origQuery = termQuery.toString();
120 return convertDefaultTermQuery(fessConfig, context, termQuery, boost, DEFAULT_FIELD, origQuery);
121 }
122 if (getQueryFieldConfig().notAnalyzedFieldSet.contains(field)) {
123 return convertKeywordQuery(fessConfig, context, termQuery, boost, field, text);
124 }
125 return convertTextQuery(fessConfig, context, termQuery, boost, field, text);
126 }
127
128
129
130
131
132
133
134
135
136
137
138
139 protected QueryBuilder convertTextQuery(final FessConfig fessConfig, final QueryContext context, final TermQuery termQuery,
140 final float boost, final String field, final String text) {
141 context.addFieldLog(field, text);
142 context.addHighlightedQuery(text);
143 return buildMatchPhraseQuery(field, text).boost(boost);
144 }
145
146
147
148
149
150
151
152
153
154
155
156
157 protected QueryBuilder convertKeywordQuery(final FessConfig fessConfig, final QueryContext context, final TermQuery termQuery,
158 final float boost, final String field, final String text) {
159 context.addFieldLog(field, text);
160 context.addHighlightedQuery(text);
161 return QueryBuilders.termQuery(field, text).boost(boost);
162 }
163
164
165
166
167
168
169
170
171
172
173
174
175 protected QueryBuilder convertWildcardQuery(final FessConfig fessConfig, final QueryContext context, final TermQuery termQuery,
176 final float boost, final String field, final String text) {
177 final String urlField = fessConfig.getIndexFieldUrl();
178 final String queryString = "*" + text + "*";
179 context.addFieldLog(urlField, queryString);
180 context.addHighlightedQuery(text);
181 return QueryBuilders.wildcardQuery(urlField, queryString).boost(boost);
182 }
183
184
185
186
187
188
189
190
191
192
193
194
195 protected QueryBuilder convertPrefixQuery(final FessConfig fessConfig, final QueryContext context, final TermQuery termQuery,
196 final float boost, final String field, final String text) {
197 return getQueryProcessor().execute(context, new PrefixQuery(new Term(field, text.substring(0, text.length() - 1))), boost);
198 }
199
200
201
202
203
204
205
206
207
208
209
210
211 protected QueryBuilder convertSortQuery(final FessConfig fessConfig, final QueryContext context, final TermQuery termQuery,
212 final float boost, final String field, final String text) {
213 split(text, ",").of(stream -> stream.filter(StringUtil::isNotBlank).forEach(t -> {
214 final String[] values = t.split("\\.");
215 if (values.length > 2) {
216 throw new InvalidQueryException(messages -> messages.addErrorsInvalidQuerySortValue(UserMessages.GLOBAL_PROPERTY_KEY, text),
217 "Invalid sort field: " + termQuery);
218 }
219 final String sortField = values[0];
220 if (!getQueryFieldConfig().isSortField(sortField)) {
221 throw new InvalidQueryException(
222 messages -> messages.addErrorsInvalidQueryUnsupportedSortField(UserMessages.GLOBAL_PROPERTY_KEY, sortField),
223 "Unsupported sort field: " + termQuery);
224 }
225 final SortOrder sortOrder;
226 if (values.length == 2) {
227 if (SortOrder.DESC.toString().equalsIgnoreCase(values[1])) {
228 sortOrder = SortOrder.DESC;
229 } else if (SortOrder.ASC.toString().equalsIgnoreCase(values[1])) {
230 sortOrder = SortOrder.ASC;
231 } else {
232 throw new InvalidQueryException(
233 messages -> messages.addErrorsInvalidQueryUnsupportedSortOrder(UserMessages.GLOBAL_PROPERTY_KEY, values[1]),
234 "Invalid sort order: " + termQuery);
235 }
236 } else {
237 sortOrder = SortOrder.ASC;
238 }
239 context.addSorts(createFieldSortBuilder(sortField, sortOrder));
240 }));
241 return null;
242 }
243
244
245
246
247
248
249
250
251
252
253
254
255 protected QueryBuilder convertDefaultTermQuery(final FessConfig fessConfig, final QueryContext context, final TermQuery termQuery,
256 final float boost, final String field, final String text) {
257 context.addFieldLog(field, text);
258 context.addHighlightedQuery(text);
259 final DefaultQueryBuilder defaultQuery =
260 buildDefaultQueryBuilder(fessConfig, context, (f, b) -> buildMatchPhraseQuery(f, text).boost(b * boost));
261 final Integer fuzzyMinLength = fessConfig.getQueryBoostFuzzyMinLengthAsInteger();
262 if (fuzzyMinLength >= 0 && text.length() >= fuzzyMinLength) {
263 defaultQuery.add(QueryBuilders.fuzzyQuery(fessConfig.getIndexFieldTitle(), text)
264 .boost(fessConfig.getQueryBoostFuzzyTitleAsDecimal().floatValue())
265 .prefixLength(fessConfig.getQueryBoostFuzzyTitlePrefixLengthAsInteger())
266 .transpositions(Constants.TRUE.equalsIgnoreCase(fessConfig.getQueryBoostFuzzyTitleTranspositions()))
267 .fuzziness(Fuzziness.build(fessConfig.getQueryBoostFuzzyTitleFuzziness()))
268 .maxExpansions(fessConfig.getQueryBoostFuzzyTitleExpansionsAsInteger()));
269 defaultQuery.add(QueryBuilders.fuzzyQuery(fessConfig.getIndexFieldContent(), text)
270 .prefixLength(fessConfig.getQueryBoostFuzzyContentPrefixLengthAsInteger())
271 .transpositions(Constants.TRUE.equalsIgnoreCase(fessConfig.getQueryBoostFuzzyContentTranspositions()))
272 .boost(fessConfig.getQueryBoostFuzzyContentAsDecimal().floatValue())
273 .fuzziness(Fuzziness.build(fessConfig.getQueryBoostFuzzyContentFuzziness()))
274 .maxExpansions(fessConfig.getQueryBoostFuzzyContentExpansionsAsInteger()));
275 }
276 return defaultQuery;
277 }
278
279
280
281
282
283
284
285
286
287
288
289
290 protected QueryBuilder convertSiteQuery(final FessConfig fessConfig, final QueryContext context, final TermQuery termQuery,
291 final float boost, final String field, final String text) {
292 final String siteField = fessConfig.getIndexFieldSite();
293 context.addFieldLog(siteField, text + "*");
294 context.addHighlightedQuery(text);
295 return QueryBuilders.prefixQuery(siteField, text).boost(boost);
296 }
297 }