QueryHelper.java 37 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959
  1. /*
  2. * Copyright 2012-2015 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. import java.io.Serializable;
  18. import java.util.ArrayList;
  19. import java.util.HashMap;
  20. import java.util.HashSet;
  21. import java.util.List;
  22. import java.util.Locale;
  23. import java.util.Map;
  24. import java.util.Map.Entry;
  25. import java.util.Set;
  26. import java.util.function.Consumer;
  27. import java.util.function.Function;
  28. import java.util.stream.Stream;
  29. import javax.annotation.PostConstruct;
  30. import javax.annotation.Resource;
  31. import javax.servlet.http.HttpServletRequest;
  32. import org.apache.lucene.analysis.core.WhitespaceAnalyzer;
  33. import org.apache.lucene.index.Term;
  34. import org.apache.lucene.queryparser.classic.ParseException;
  35. import org.apache.lucene.queryparser.classic.QueryParser;
  36. import org.apache.lucene.queryparser.ext.ExtendableQueryParser;
  37. import org.apache.lucene.search.BooleanClause;
  38. import org.apache.lucene.search.BooleanQuery;
  39. import org.apache.lucene.search.FuzzyQuery;
  40. import org.apache.lucene.search.PrefixQuery;
  41. import org.apache.lucene.search.Query;
  42. import org.apache.lucene.search.TermQuery;
  43. import org.apache.lucene.search.TermRangeQuery;
  44. import org.apache.lucene.search.WildcardQuery;
  45. import org.apache.lucene.util.BytesRef;
  46. import org.codelibs.core.lang.StringUtil;
  47. import org.codelibs.core.misc.DynamicProperties;
  48. import org.codelibs.fess.Constants;
  49. import org.codelibs.fess.entity.FacetInfo;
  50. import org.codelibs.fess.entity.GeoInfo;
  51. import org.codelibs.fess.entity.QueryContext;
  52. import org.codelibs.fess.exception.InvalidQueryException;
  53. import org.codelibs.fess.mylasta.direction.FessConfig;
  54. import org.codelibs.fess.util.StreamUtil;
  55. import org.dbflute.optional.OptionalEntity;
  56. import org.dbflute.optional.OptionalThing;
  57. import org.elasticsearch.common.unit.Fuzziness;
  58. import org.elasticsearch.index.query.BoolQueryBuilder;
  59. import org.elasticsearch.index.query.QueryBuilder;
  60. import org.elasticsearch.index.query.QueryBuilders;
  61. import org.elasticsearch.index.query.RangeQueryBuilder;
  62. import org.elasticsearch.index.query.functionscore.ScoreFunctionBuilders;
  63. import org.elasticsearch.search.sort.SortBuilder;
  64. import org.elasticsearch.search.sort.SortBuilders;
  65. import org.elasticsearch.search.sort.SortOrder;
  66. import org.lastaflute.web.ruts.message.ActionMessages;
  67. import org.lastaflute.web.util.LaRequestUtil;
  68. public class QueryHelper implements Serializable {
  69. protected static final long serialVersionUID = 1L;
  70. private static final String DEFAULT_FIELD = "_default";
  71. protected static final String SCORE_FIELD = "score";
  72. protected static final String INURL_FIELD = "inurl";
  73. protected static final String AND = "AND";
  74. protected static final String OR = "OR";
  75. protected static final String NOT = "NOT";
  76. protected static final String _OR_ = " OR ";
  77. protected static final String _AND_ = " AND ";
  78. protected static final String DEFAULT_OPERATOR = _AND_;
  79. protected static final int DEFAULT_START_POSITION = 0;
  80. protected static final int DEFAULT_PAGE_SIZE = 20;
  81. protected static final int MAX_PAGE_SIZE = 100;
  82. @Resource
  83. protected DynamicProperties crawlerProperties;
  84. @Resource
  85. protected FessConfig fessConfig;
  86. @Resource
  87. protected RoleQueryHelper roleQueryHelper;
  88. @Resource
  89. protected SystemHelper systemHelper;
  90. @Resource
  91. protected KeyMatchHelper keyMatchHelper;
  92. protected Set<String> apiResponseFieldSet;
  93. protected Set<String> highlightFieldSet = new HashSet<>();
  94. protected String[] responseFields;
  95. protected String[] cacheResponseFields;
  96. protected String[] responseDocValuesFields;
  97. protected String[] highlightedFields;
  98. protected String[] searchFields;
  99. protected String[] facetFields;
  100. protected String sortPrefix = "sort:";
  101. protected String[] supportedSortFields;
  102. protected int highlightFragmentSize = 100;
  103. protected String additionalQuery;
  104. protected long timeAllowed = -1;
  105. protected Map<String, String[]> requestParameterMap = new HashMap<>();
  106. protected Map<String, String> fieldLanguageMap = new HashMap<>();
  107. protected int maxSearchResultOffset = 100000;
  108. protected SortBuilder[] defaultSortBuilders;
  109. protected String highlightPrefix = "hl_";
  110. protected FacetInfo defaultFacetInfo;
  111. protected GeoInfo defaultGeoInfo;
  112. protected String defaultQueryLanguage;
  113. protected Map<String, String[]> additionalQueryParamMap = new HashMap<>();
  114. protected Map<String, String> fieldBoostMap = new HashMap<>();
  115. protected int defaultPageSize = DEFAULT_PAGE_SIZE;
  116. protected int defaultStartPosition = DEFAULT_START_POSITION;
  117. @PostConstruct
  118. public void init() {
  119. if (responseFields == null) {
  120. responseFields =
  121. new String[] { SCORE_FIELD, fessConfig.getIndexFieldId(), fessConfig.getIndexFieldDocId(),
  122. fessConfig.getIndexFieldBoost(), fessConfig.getIndexFieldContentLength(), fessConfig.getIndexFieldHost(),
  123. fessConfig.getIndexFieldSite(), fessConfig.getIndexFieldLastModified(), fessConfig.getIndexFieldMimetype(),
  124. fessConfig.getIndexFieldFiletype(), fessConfig.getIndexFieldCreated(), fessConfig.getIndexFieldTitle(),
  125. fessConfig.getIndexFieldDigest(), fessConfig.getIndexFieldUrl(), fessConfig.getIndexFieldClickCount(),
  126. fessConfig.getIndexFieldFavoriteCount(), fessConfig.getIndexFieldConfigId(), fessConfig.getIndexFieldLang(),
  127. fessConfig.getIndexFieldHasCache() };
  128. }
  129. if (cacheResponseFields == null) {
  130. cacheResponseFields =
  131. new String[] { SCORE_FIELD, fessConfig.getIndexFieldId(), fessConfig.getIndexFieldDocId(),
  132. fessConfig.getIndexFieldBoost(), fessConfig.getIndexFieldContentLength(), fessConfig.getIndexFieldHost(),
  133. fessConfig.getIndexFieldSite(), fessConfig.getIndexFieldLastModified(), fessConfig.getIndexFieldMimetype(),
  134. fessConfig.getIndexFieldFiletype(), fessConfig.getIndexFieldCreated(), fessConfig.getIndexFieldTitle(),
  135. fessConfig.getIndexFieldDigest(), fessConfig.getIndexFieldUrl(), fessConfig.getIndexFieldClickCount(),
  136. fessConfig.getIndexFieldFavoriteCount(), fessConfig.getIndexFieldConfigId(), fessConfig.getIndexFieldLang(),
  137. fessConfig.getIndexFieldCache() };
  138. }
  139. if (responseDocValuesFields == null) {
  140. responseDocValuesFields = new String[] { fessConfig.getIndexFieldClickCount(), fessConfig.getIndexFieldFavoriteCount() };
  141. }
  142. if (highlightedFields == null) {
  143. highlightedFields = new String[] { fessConfig.getIndexFieldContent() };
  144. }
  145. if (searchFields == null) {
  146. searchFields =
  147. new String[] { INURL_FIELD, fessConfig.getIndexFieldUrl(), fessConfig.getIndexFieldDocId(),
  148. fessConfig.getIndexFieldHost(), fessConfig.getIndexFieldTitle(), fessConfig.getIndexFieldContent(),
  149. fessConfig.getIndexFieldContentLength(), fessConfig.getIndexFieldLastModified(),
  150. fessConfig.getIndexFieldMimetype(), fessConfig.getIndexFieldFiletype(), fessConfig.getIndexFieldLabel(),
  151. fessConfig.getIndexFieldSegment(), fessConfig.getIndexFieldClickCount(),
  152. fessConfig.getIndexFieldFavoriteCount(), fessConfig.getIndexFieldLang() };
  153. }
  154. if (facetFields == null) {
  155. facetFields =
  156. new String[] { fessConfig.getIndexFieldUrl(), fessConfig.getIndexFieldHost(), fessConfig.getIndexFieldTitle(),
  157. fessConfig.getIndexFieldContent(), fessConfig.getIndexFieldContentLength(),
  158. fessConfig.getIndexFieldLastModified(), fessConfig.getIndexFieldMimetype(), fessConfig.getIndexFieldFiletype(),
  159. fessConfig.getIndexFieldLabel(), fessConfig.getIndexFieldSegment() };
  160. }
  161. if (supportedSortFields == null) {
  162. supportedSortFields =
  163. new String[] { fessConfig.getIndexFieldCreated(), fessConfig.getIndexFieldContentLength(),
  164. fessConfig.getIndexFieldLastModified(), fessConfig.getIndexFieldClickCount(),
  165. fessConfig.getIndexFieldFavoriteCount() };
  166. }
  167. }
  168. public QueryContext build(final String query, final Consumer<QueryContext> context) {
  169. String q;
  170. if (additionalQuery != null && StringUtil.isNotBlank(query)) {
  171. q = query + " " + additionalQuery;
  172. } else {
  173. q = query;
  174. }
  175. final QueryContext queryContext = new QueryContext(q, true);
  176. buildBaseQuery(queryContext, context);
  177. buildBoostQuery(queryContext);
  178. if (keyMatchHelper != null) {
  179. final List<String> docIdQueryList = keyMatchHelper.getDocIdQueryList();
  180. if (docIdQueryList != null && !docIdQueryList.isEmpty()) {
  181. queryContext.addQuery(boolQuery -> {
  182. for (final String docIdQuery : docIdQueryList) {
  183. // TODO id query?
  184. boolQuery.should(QueryBuilders.queryStringQuery(docIdQuery));
  185. }
  186. });
  187. }
  188. }
  189. if (roleQueryHelper != null) {
  190. final Set<String> roleSet = roleQueryHelper.build();
  191. if (!roleSet.isEmpty()) {
  192. queryContext.addQuery(boolQuery -> {
  193. BoolQueryBuilder roleQuery = QueryBuilders.boolQuery();
  194. roleSet.stream().forEach(name -> {
  195. roleQuery.filter(QueryBuilders.termQuery(fessConfig.getIndexFieldRole(), name));
  196. });
  197. boolQuery.filter(roleQuery);
  198. });
  199. }
  200. }
  201. if (!queryContext.hasSorts() && defaultSortBuilders != null) {
  202. queryContext.addSorts(defaultSortBuilders);
  203. }
  204. return queryContext;
  205. }
  206. private void buildBoostQuery(final QueryContext queryContext) {
  207. queryContext.addFunctionScore(functionScoreQuery -> {
  208. functionScoreQuery.add(ScoreFunctionBuilders.fieldValueFactorFunction(fessConfig.getIndexFieldBoost()));
  209. });
  210. }
  211. public void buildBaseQuery(final QueryContext queryContext, final Consumer<QueryContext> context) {
  212. final QueryParser queryParser = getQueryParser();
  213. try {
  214. final Query query = queryParser.parse(queryContext.getQueryString());
  215. final QueryBuilder queryBuilder = convertQuery(queryContext, query);
  216. if (queryBuilder != null) {
  217. queryContext.setQueryBuilder(queryBuilder);
  218. } else {
  219. queryContext.setQueryBuilder(QueryBuilders.matchAllQuery());
  220. }
  221. context.accept(queryContext);
  222. } catch (final ParseException e) {
  223. throw new InvalidQueryException(messages -> messages.addErrorsInvalidQueryParseError(ActionMessages.GLOBAL_PROPERTY_KEY),
  224. "Invalid query: " + queryContext.getQueryString());
  225. }
  226. }
  227. protected QueryParser getQueryParser() {
  228. return new ExtendableQueryParser(DEFAULT_FIELD, new WhitespaceAnalyzer());
  229. }
  230. protected QueryBuilder convertQuery(final QueryContext context, final Query query) {
  231. if (query instanceof TermQuery) {
  232. return convertTermQuery(context, (TermQuery) query);
  233. } else if (query instanceof TermRangeQuery) {
  234. return convertTermRangeQuery(context, (TermRangeQuery) query);
  235. } else if (query instanceof FuzzyQuery) {
  236. return convertFuzzyQuery(context, (FuzzyQuery) query);
  237. } else if (query instanceof PrefixQuery) {
  238. return convertPrefixQuery(context, (PrefixQuery) query);
  239. } else if (query instanceof WildcardQuery) {
  240. return convertWildcardQuery(context, (WildcardQuery) query);
  241. } else if (query instanceof BooleanQuery) {
  242. final BooleanQuery booleanQuery = (BooleanQuery) query;
  243. return convertBooleanQuery(context, booleanQuery);
  244. }
  245. throw new InvalidQueryException(messages -> messages.addErrorsInvalidQueryUnknown(ActionMessages.GLOBAL_PROPERTY_KEY),
  246. "Unknown query: " + query.getClass() + " => " + query);
  247. }
  248. protected QueryBuilder convertBooleanQuery(final QueryContext context, final BooleanQuery booleanQuery) {
  249. final BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();
  250. for (final BooleanClause clause : booleanQuery.getClauses()) {
  251. final QueryBuilder queryBuilder = convertQuery(context, clause.getQuery());
  252. if (queryBuilder != null) {
  253. switch (clause.getOccur()) {
  254. case MUST:
  255. boolQuery.must(queryBuilder);
  256. break;
  257. case SHOULD:
  258. boolQuery.must(queryBuilder);
  259. break;
  260. case MUST_NOT:
  261. boolQuery.must(queryBuilder);
  262. break;
  263. default:
  264. break;
  265. }
  266. }
  267. }
  268. return boolQuery;
  269. }
  270. protected QueryBuilder convertWildcardQuery(final QueryContext context, final WildcardQuery wildcardQuery) {
  271. final String field = wildcardQuery.getField();
  272. if (DEFAULT_FIELD.equals(field)) {
  273. context.addFieldLog(field, wildcardQuery.getTerm().text());
  274. return buildDefaultQueryBuilder(f -> QueryBuilders.wildcardQuery(f, wildcardQuery.getTerm().text()));
  275. } else if (isSearchField(field)) {
  276. context.addFieldLog(field, wildcardQuery.getTerm().text());
  277. return QueryBuilders.wildcardQuery(field, wildcardQuery.getTerm().text()).boost(wildcardQuery.getBoost());
  278. } else {
  279. final String origQuery = wildcardQuery.getTerm().toString();
  280. context.addFieldLog(DEFAULT_FIELD, origQuery);
  281. context.addHighlightedQuery(origQuery);
  282. return buildDefaultQueryBuilder(f -> QueryBuilders.wildcardQuery(f, origQuery));
  283. }
  284. }
  285. protected QueryBuilder convertPrefixQuery(final QueryContext context, final PrefixQuery prefixQuery) {
  286. final String field = prefixQuery.getField();
  287. if (DEFAULT_FIELD.equals(field)) {
  288. context.addFieldLog(field, prefixQuery.getPrefix().text());
  289. return buildDefaultQueryBuilder(f -> QueryBuilders.prefixQuery(f, prefixQuery.getPrefix().text()));
  290. } else if (isSearchField(field)) {
  291. context.addFieldLog(field, prefixQuery.getPrefix().text());
  292. return QueryBuilders.prefixQuery(field, prefixQuery.getPrefix().text()).boost(prefixQuery.getBoost());
  293. } else {
  294. final String origQuery = prefixQuery.getPrefix().toString();
  295. context.addFieldLog(DEFAULT_FIELD, origQuery);
  296. context.addHighlightedQuery(origQuery);
  297. return buildDefaultQueryBuilder(f -> QueryBuilders.prefixQuery(f, origQuery));
  298. }
  299. }
  300. protected QueryBuilder convertFuzzyQuery(final QueryContext context, final FuzzyQuery fuzzyQuery) {
  301. final Term term = fuzzyQuery.getTerm();
  302. final String field = term.field();
  303. // TODO fuzzy value
  304. if (DEFAULT_FIELD.equals(field)) {
  305. context.addFieldLog(field, term.text());
  306. return buildDefaultQueryBuilder(f -> QueryBuilders.fuzzyQuery(f, term.text()).fuzziness(
  307. Fuzziness.fromEdits(fuzzyQuery.getMaxEdits())));
  308. } else if (isSearchField(field)) {
  309. context.addFieldLog(field, term.text());
  310. return QueryBuilders.fuzzyQuery(field, term.text()).boost(fuzzyQuery.getBoost())
  311. .fuzziness(Fuzziness.fromEdits(fuzzyQuery.getMaxEdits()));
  312. } else {
  313. final String origQuery = fuzzyQuery.toString();
  314. context.addFieldLog(DEFAULT_FIELD, origQuery);
  315. context.addHighlightedQuery(origQuery);
  316. return buildDefaultQueryBuilder(f -> QueryBuilders.fuzzyQuery(f, origQuery).fuzziness(
  317. Fuzziness.fromEdits(fuzzyQuery.getMaxEdits())));
  318. }
  319. }
  320. protected QueryBuilder convertTermRangeQuery(final QueryContext context, final TermRangeQuery termRangeQuery) {
  321. final String field = termRangeQuery.getField();
  322. if (isSearchField(field)) {
  323. context.addFieldLog(field, termRangeQuery.toString(field));
  324. final RangeQueryBuilder rangeQuery = QueryBuilders.rangeQuery(field);
  325. final BytesRef min = termRangeQuery.getLowerTerm();
  326. if (min != null) {
  327. if (termRangeQuery.includesLower()) {
  328. rangeQuery.gte(min.utf8ToString());
  329. } else {
  330. rangeQuery.gt(min.utf8ToString());
  331. }
  332. }
  333. final BytesRef max = termRangeQuery.getUpperTerm();
  334. if (max != null) {
  335. if (termRangeQuery.includesUpper()) {
  336. rangeQuery.lte(max.utf8ToString());
  337. } else {
  338. rangeQuery.lt(max.utf8ToString());
  339. }
  340. }
  341. rangeQuery.boost(termRangeQuery.getBoost());
  342. return rangeQuery;
  343. } else {
  344. final String origQuery = termRangeQuery.toString();
  345. context.addFieldLog(DEFAULT_FIELD, origQuery);
  346. context.addHighlightedQuery(origQuery);
  347. return buildDefaultQueryBuilder(f -> QueryBuilders.matchPhraseQuery(f, origQuery));
  348. }
  349. }
  350. protected QueryBuilder convertTermQuery(final QueryContext context, final TermQuery termQuery) {
  351. final String field = termQuery.getTerm().field();
  352. final String text = termQuery.getTerm().text();
  353. if (DEFAULT_FIELD.equals(field)) {
  354. context.addFieldLog(field, text);
  355. context.addHighlightedQuery(text);
  356. return buildDefaultQueryBuilder(f -> QueryBuilders.matchPhraseQuery(f, text));
  357. } else if ("sort".equals(field)) {
  358. final String[] values = text.split("\\.");
  359. if (values.length > 2) {
  360. throw new InvalidQueryException(messages -> messages.addErrorsInvalidQuerySortValue(ActionMessages.GLOBAL_PROPERTY_KEY,
  361. text), "Invalid sort field: " + termQuery);
  362. }
  363. final String sortField = values[0];
  364. if (!isSortField(sortField)) {
  365. throw new InvalidQueryException(messages -> messages.addErrorsInvalidQueryUnsupportedSortField(
  366. ActionMessages.GLOBAL_PROPERTY_KEY, sortField), "Unsupported sort field: " + termQuery);
  367. }
  368. SortOrder sortOrder;
  369. if (values.length == 2) {
  370. sortOrder = SortOrder.valueOf(values[1]);
  371. if (sortOrder == null) {
  372. throw new InvalidQueryException(messages -> messages.addErrorsInvalidQueryUnsupportedSortOrder(
  373. ActionMessages.GLOBAL_PROPERTY_KEY, values[1]), "Invalid sort order: " + termQuery);
  374. }
  375. } else {
  376. sortOrder = SortOrder.ASC;
  377. }
  378. context.addSorts(SortBuilders.fieldSort(sortField).order(sortOrder));
  379. return null;
  380. } else if (INURL_FIELD.equals(field)) {
  381. return QueryBuilders.wildcardQuery(field, text).boost(termQuery.getBoost());
  382. } else if (isSearchField(field)) {
  383. context.addFieldLog(field, text);
  384. context.addHighlightedQuery(text);
  385. return QueryBuilders.matchPhraseQuery(field, text).boost(termQuery.getBoost());
  386. } else {
  387. final String origQuery = termQuery.toString();
  388. context.addFieldLog(DEFAULT_FIELD, origQuery);
  389. context.addHighlightedQuery(origQuery);
  390. return buildDefaultQueryBuilder(f -> QueryBuilders.matchPhraseQuery(f, origQuery));
  391. }
  392. }
  393. private boolean isSearchField(final String field) {
  394. for (final String searchField : searchFields) {
  395. if (searchField.equals(field)) {
  396. return true;
  397. }
  398. }
  399. return false;
  400. }
  401. private QueryBuilder buildDefaultQueryBuilder(final Function<String, QueryBuilder> builder) {
  402. // TODO boost
  403. final BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();
  404. final QueryBuilder titleQuery = builder.apply(fessConfig.getIndexFieldTitle());
  405. boolQuery.should(titleQuery);
  406. final QueryBuilder contentQuery = builder.apply(fessConfig.getIndexFieldContent());
  407. boolQuery.should(contentQuery);
  408. getQueryLanguage().ifPresent(lang -> {
  409. final QueryBuilder titleLangQuery = builder.apply(fessConfig.getIndexFieldTitle() + "_" + lang);
  410. boolQuery.should(titleLangQuery);
  411. final QueryBuilder contentLangQuery = builder.apply(fessConfig.getIndexFieldContent() + "_" + lang);
  412. boolQuery.should(contentLangQuery);
  413. });
  414. return boolQuery;
  415. }
  416. protected OptionalThing<String> getQueryLanguage() {
  417. if (defaultQueryLanguage != null) {
  418. return OptionalEntity.of(defaultQueryLanguage);
  419. }
  420. return LaRequestUtil.getOptionalRequest().map(request -> {
  421. final Locale locale = request.getLocale();
  422. if (locale == null) {
  423. return null;
  424. }
  425. final String language = locale.getLanguage();
  426. final String country = locale.getCountry();
  427. if (StringUtil.isNotBlank(language)) {
  428. if (StringUtil.isNotBlank(country)) {
  429. final String lang = language + "-" + country;
  430. if (fieldLanguageMap.containsKey(lang)) {
  431. return fieldLanguageMap.get(lang);
  432. }
  433. }
  434. if (fieldLanguageMap.containsKey(language)) {
  435. return fieldLanguageMap.get(language);
  436. }
  437. }
  438. return null;
  439. });
  440. }
  441. public String buildOptionQuery(final Map<String, String[]> optionMap) {
  442. if (optionMap == null) {
  443. return StringUtil.EMPTY;
  444. }
  445. // TODO
  446. final StringBuilder buf = new StringBuilder();
  447. // final String[] qs = optionMap.get(Constants.OPTION_QUERY_Q);
  448. // if (qs != null) {
  449. // for (final String q : qs) {
  450. // if (StringUtil.isNotBlank(q)) {
  451. // buf.append(' ');
  452. // buf.append(q);
  453. // }
  454. // }
  455. // }
  456. //
  457. // final String[] cqs = optionMap.get(Constants.OPTION_QUERY_CQ);
  458. // if (cqs != null) {
  459. // for (final String cq : cqs) {
  460. // if (StringUtil.isNotBlank(cq)) {
  461. // buf.append(' ');
  462. // char split = 0;
  463. // final List<QueryPart> partList = splitQuery(cq.indexOf('"') >= 0 ? cq : "\"" + cq + "\"", null, null, null);
  464. // for (final QueryPart part : partList) {
  465. // if (split == 0) {
  466. // split = ' ';
  467. // } else {
  468. // buf.append(split);
  469. // }
  470. // final String value = part.getValue();
  471. // buf.append('"');
  472. // buf.append(value);
  473. // buf.append('"');
  474. // }
  475. // }
  476. // }
  477. // }
  478. //
  479. // final String[] oqs = optionMap.get(Constants.OPTION_QUERY_OQ);
  480. // if (oqs != null) {
  481. // for (final String oq : oqs) {
  482. // if (StringUtil.isNotBlank(oq)) {
  483. // buf.append(' ');
  484. // final List<QueryPart> partList = splitQuery(oq, null, null, null);
  485. // final boolean append = partList.size() > 1 && optionMap.size() > 1;
  486. // if (append) {
  487. // buf.append('(');
  488. // }
  489. // String split = null;
  490. // for (final QueryPart part : partList) {
  491. // if (split == null) {
  492. // split = _OR_;
  493. // } else {
  494. // buf.append(split);
  495. // }
  496. // final String value = part.getValue();
  497. // final boolean hasSpace = value.matches(".*\\s.*");
  498. // if (hasSpace) {
  499. // buf.append('"');
  500. // }
  501. // buf.append(value);
  502. // if (hasSpace) {
  503. // buf.append('"');
  504. // }
  505. // }
  506. // if (append) {
  507. // buf.append(')');
  508. // }
  509. // }
  510. // }
  511. // }
  512. //
  513. // final String[] nqs = optionMap.get(Constants.OPTION_QUERY_NQ);
  514. // if (nqs != null) {
  515. // for (final String nq : nqs) {
  516. // if (StringUtil.isNotBlank(nq)) {
  517. // buf.append(' ');
  518. // String split = StringUtil.EMPTY;
  519. // final List<QueryPart> partList = splitQuery(nq, null, null, null);
  520. // for (final QueryPart part : partList) {
  521. // buf.append(split);
  522. // if (split.length() == 0) {
  523. // split = " ";
  524. // }
  525. // buf.append(NOT_);
  526. // final String value = part.getValue();
  527. // final boolean hasSpace = value.matches(".*\\s.*");
  528. // if (hasSpace) {
  529. // buf.append('"');
  530. // }
  531. // buf.append(value);
  532. // if (hasSpace) {
  533. // buf.append('"');
  534. // }
  535. // }
  536. // }
  537. // }
  538. // }
  539. return buf.toString().trim();
  540. }
  541. private boolean isSortField(final String field) {
  542. for (final String f : supportedSortFields) {
  543. if (f.equals(field)) {
  544. return true;
  545. }
  546. }
  547. return false;
  548. }
  549. public boolean isFacetField(final String field) {
  550. if (StringUtil.isBlank(field)) {
  551. return false;
  552. }
  553. boolean flag = false;
  554. for (final String f : facetFields) {
  555. if (field.equals(f)) {
  556. flag = true;
  557. }
  558. }
  559. return flag;
  560. }
  561. public boolean isFacetSortValue(final String sort) {
  562. return "count".equals(sort) || "index".equals(sort);
  563. }
  564. public void setApiResponseFields(final String[] fields) {
  565. apiResponseFieldSet = new HashSet<>();
  566. for (final String field : fields) {
  567. apiResponseFieldSet.add(field);
  568. }
  569. }
  570. public boolean isApiResponseField(final String field) {
  571. if (apiResponseFieldSet == null) {
  572. return true;
  573. }
  574. return apiResponseFieldSet.contains(field);
  575. }
  576. /**
  577. * @return the responseFields
  578. */
  579. public String[] getResponseFields() {
  580. return responseFields;
  581. }
  582. /**
  583. * @param responseFields the responseFields to set
  584. */
  585. public void setResponseFields(final String[] responseFields) {
  586. this.responseFields = responseFields;
  587. }
  588. public String[] getCacheResponseFields() {
  589. return cacheResponseFields;
  590. }
  591. public void setCacheResponseFields(final String[] cacheResponseFields) {
  592. this.cacheResponseFields = cacheResponseFields;
  593. }
  594. public String[] getResponseDocValuesFields() {
  595. return responseDocValuesFields;
  596. }
  597. public void setResponseDocValuesFields(final String[] responseDocValuesFields) {
  598. this.responseDocValuesFields = responseDocValuesFields;
  599. }
  600. /**
  601. * @return the highlightedFields
  602. */
  603. public String[] getHighlightedFields() {
  604. return highlightedFields;
  605. }
  606. /**
  607. * @param highlightedFields the highlightedFields to set
  608. */
  609. public void setHighlightedFields(final String[] highlightedFields) {
  610. this.highlightedFields = highlightedFields;
  611. }
  612. public Stream<String> highlightedFields() {
  613. return StreamUtil.of(highlightedFields);
  614. }
  615. /**
  616. * @return the supportedFields
  617. */
  618. public String[] getSearchFields() {
  619. return searchFields;
  620. }
  621. /**
  622. * @param supportedFields the supportedFields to set
  623. */
  624. public void setSearchFields(final String[] supportedFields) {
  625. searchFields = supportedFields;
  626. }
  627. /**
  628. * @return the facetFields
  629. */
  630. public String[] getFacetFields() {
  631. return facetFields;
  632. }
  633. /**
  634. * @param facetFields the facetFields to set
  635. */
  636. public void setFacetFields(final String[] facetFields) {
  637. this.facetFields = facetFields;
  638. }
  639. /**
  640. * @return the sortPrefix
  641. */
  642. public String getSortPrefix() {
  643. return sortPrefix;
  644. }
  645. /**
  646. * @param sortPrefix the sortPrefix to set
  647. */
  648. public void setSortPrefix(final String sortPrefix) {
  649. this.sortPrefix = sortPrefix;
  650. }
  651. /**
  652. * @return the supportedSortFields
  653. */
  654. public String[] getSupportedSortFields() {
  655. return supportedSortFields;
  656. }
  657. /**
  658. * @param supportedSortFields the supportedSortFields to set
  659. */
  660. public void setSupportedSortFields(final String[] supportedSortFields) {
  661. this.supportedSortFields = supportedSortFields;
  662. }
  663. public void addHighlightField(final String field) {
  664. highlightFieldSet.add(field);
  665. }
  666. /**
  667. * @return the highlightFragmentSize
  668. */
  669. public int getHighlightFragmentSize() {
  670. return highlightFragmentSize;
  671. }
  672. /**
  673. * @param highlightFragmentSize the highlightFragmentSize to set
  674. */
  675. public void setHighlightFragmentSize(final int highlightFragmentSize) {
  676. this.highlightFragmentSize = highlightFragmentSize;
  677. }
  678. /**
  679. * @return the additionalQuery
  680. */
  681. public String getAdditionalQuery() {
  682. return additionalQuery;
  683. }
  684. /**
  685. * @param additionalQuery the additionalQuery to set
  686. */
  687. public void setAdditionalQuery(final String additionalQuery) {
  688. this.additionalQuery = additionalQuery;
  689. }
  690. /**
  691. * @return the timeAllowed
  692. */
  693. public long getTimeAllowed() {
  694. return timeAllowed;
  695. }
  696. /**
  697. * @param timeAllowed the timeAllowed to set
  698. */
  699. public void setTimeAllowed(final long timeAllowed) {
  700. this.timeAllowed = timeAllowed;
  701. }
  702. public void addRequestParameter(final String name, final String... values) {
  703. requestParameterMap.put(name, values);
  704. }
  705. public void addRequestParameter(final String name, final String value) {
  706. if (value != null) {
  707. requestParameterMap.put(name, new String[] { value });
  708. }
  709. }
  710. public Set<Entry<String, String[]>> getRequestParameterSet() {
  711. return requestParameterMap.entrySet();
  712. }
  713. public void addFieldLanguage(final String lang, final String fieldLang) {
  714. fieldLanguageMap.put(lang, fieldLang);
  715. }
  716. public int getMaxSearchResultOffset() {
  717. return maxSearchResultOffset;
  718. }
  719. public void setMaxSearchResultOffset(final int maxSearchResultOffset) {
  720. this.maxSearchResultOffset = maxSearchResultOffset;
  721. }
  722. public void addDefaultSort(final String fieldName, final String order) {
  723. final List<SortBuilder> list = new ArrayList<>();
  724. if (defaultSortBuilders != null) {
  725. StreamUtil.of(defaultSortBuilders).forEach(builder -> list.add(builder));
  726. }
  727. list.add(SortBuilders.fieldSort(fieldName).order(SortOrder.ASC.toString().equalsIgnoreCase(order) ? SortOrder.ASC : SortOrder.DESC));
  728. defaultSortBuilders = list.toArray(new SortBuilder[list.size()]);
  729. }
  730. public void setHighlightPrefix(final String highlightPrefix) {
  731. this.highlightPrefix = highlightPrefix;
  732. }
  733. public String getHighlightPrefix() {
  734. return highlightPrefix;
  735. }
  736. public FacetInfo getDefaultFacetInfo() {
  737. return defaultFacetInfo;
  738. }
  739. public void setDefaultFacetInfo(final FacetInfo defaultFacetInfo) {
  740. this.defaultFacetInfo = defaultFacetInfo;
  741. }
  742. public GeoInfo getDefaultGeoInfo() {
  743. return defaultGeoInfo;
  744. }
  745. public void setDefaultGeoInfo(final GeoInfo defaultGeoInfo) {
  746. this.defaultGeoInfo = defaultGeoInfo;
  747. }
  748. public String getDefaultQueryLanguage() {
  749. return defaultQueryLanguage;
  750. }
  751. public void setDefaultQueryLanguage(final String defaultQueryLanguage) {
  752. this.defaultQueryLanguage = defaultQueryLanguage;
  753. }
  754. public int getMaxPageSize() {
  755. final Object maxPageSize = crawlerProperties.get(Constants.SEARCH_RESULT_MAX_PAGE_SIZE);
  756. if (maxPageSize == null) {
  757. return MAX_PAGE_SIZE;
  758. }
  759. try {
  760. return Integer.parseInt(maxPageSize.toString());
  761. } catch (final NumberFormatException e) {
  762. return MAX_PAGE_SIZE;
  763. }
  764. }
  765. public int getDefaultPageSize() {
  766. return defaultPageSize;
  767. }
  768. public void setDefaultPageSize(final int defaultPageSize) {
  769. this.defaultPageSize = defaultPageSize;
  770. }
  771. public int getDefaultStart() {
  772. return defaultStartPosition;
  773. }
  774. public void setDefaultStart(final int defaultStartPosition) {
  775. this.defaultStartPosition = defaultStartPosition;
  776. }
  777. public Map<String, String[]> getQueryParamMap() {
  778. if (additionalQueryParamMap.isEmpty()) {
  779. return additionalQueryParamMap;
  780. }
  781. final HttpServletRequest request = LaRequestUtil.getOptionalRequest().orElse(null);
  782. final Map<String, String[]> queryParamMap = new HashMap<String, String[]>();
  783. for (final Map.Entry<String, String[]> entry : additionalQueryParamMap.entrySet()) {
  784. final String[] values = entry.getValue();
  785. final String[] newValues = new String[values.length];
  786. for (int i = 0; i < values.length; i++) {
  787. final String value = values[i];
  788. if (value.length() > 1 && value.charAt(0) == '$' && request != null) {
  789. final String param = request.getParameter(value.substring(1));
  790. if (StringUtil.isNotBlank(param)) {
  791. newValues[i] = param;
  792. } else {
  793. newValues[i] = StringUtil.EMPTY;
  794. }
  795. } else {
  796. newValues[i] = value;
  797. }
  798. }
  799. queryParamMap.put(entry.getKey(), newValues);
  800. }
  801. return queryParamMap;
  802. }
  803. public void addQueryParam(final String key, final String[] values) {
  804. additionalQueryParamMap.put(key, values);
  805. }
  806. public void addFieldBoost(final String field, final String value) {
  807. try {
  808. Float.parseFloat(value);
  809. } catch (final NumberFormatException e) {
  810. throw new IllegalArgumentException(value + " was not number.", e);
  811. }
  812. fieldBoostMap.put(field, value);
  813. }
  814. protected String getDefaultOperator() {
  815. final HttpServletRequest request = LaRequestUtil.getOptionalRequest().orElse(null);
  816. if (request != null) {
  817. final String defaultOperator = (String) request.getAttribute(Constants.DEFAULT_OPERATOR);
  818. if (AND.equalsIgnoreCase(defaultOperator)) {
  819. return _AND_;
  820. } else if (OR.equalsIgnoreCase(defaultOperator)) {
  821. return _OR_;
  822. }
  823. }
  824. return DEFAULT_OPERATOR;
  825. }
  826. protected void appendFieldBoostValue(final StringBuilder buf, final String field, final String value) {
  827. if (fieldBoostMap.containsKey(field) && value.indexOf('^') == -1 && value.indexOf('~') == -1) {
  828. buf.append('^').append(fieldBoostMap.get(field));
  829. }
  830. }
  831. }