View Javadoc
1   /*
2    * Copyright 2012-2020 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.es.config.allcommon;
17  
18  import java.text.SimpleDateFormat;
19  import java.time.LocalDateTime;
20  import java.time.ZoneId;
21  import java.time.format.DateTimeFormatter;
22  import java.util.ArrayList;
23  import java.util.Collection;
24  import java.util.Collections;
25  import java.util.Date;
26  import java.util.List;
27  import java.util.TimeZone;
28  
29  import org.dbflute.cbean.ConditionBean;
30  import org.dbflute.cbean.ConditionQuery;
31  import org.dbflute.cbean.ckey.ConditionKey;
32  import org.dbflute.cbean.coption.ConditionOption;
33  import org.dbflute.cbean.coption.ParameterOption;
34  import org.dbflute.cbean.cvalue.ConditionValue;
35  import org.dbflute.cbean.sqlclause.SqlClause;
36  import org.dbflute.dbmeta.info.ColumnInfo;
37  import org.dbflute.dbmeta.name.ColumnRealName;
38  import org.dbflute.dbmeta.name.ColumnSqlName;
39  import org.dbflute.exception.InvalidQueryRegisteredException;
40  import org.dbflute.util.Srl;
41  import org.elasticsearch.common.unit.Fuzziness;
42  import org.elasticsearch.index.query.BoolQueryBuilder;
43  import org.elasticsearch.index.query.CommonTermsQueryBuilder;
44  import org.elasticsearch.index.query.ExistsQueryBuilder;
45  import org.elasticsearch.index.query.IdsQueryBuilder;
46  import org.elasticsearch.index.query.MatchAllQueryBuilder;
47  import org.elasticsearch.index.query.MatchPhrasePrefixQueryBuilder;
48  import org.elasticsearch.index.query.MatchPhraseQueryBuilder;
49  import org.elasticsearch.index.query.MatchQueryBuilder;
50  import org.elasticsearch.index.query.MoreLikeThisQueryBuilder;
51  import org.elasticsearch.index.query.PrefixQueryBuilder;
52  import org.elasticsearch.index.query.QueryBuilder;
53  import org.elasticsearch.index.query.QueryBuilders;
54  import org.elasticsearch.index.query.QueryStringQueryBuilder;
55  import org.elasticsearch.index.query.RangeQueryBuilder;
56  import org.elasticsearch.index.query.RegexpQueryBuilder;
57  import org.elasticsearch.index.query.SpanTermQueryBuilder;
58  import org.elasticsearch.index.query.TermQueryBuilder;
59  import org.elasticsearch.index.query.TermsQueryBuilder;
60  import org.elasticsearch.index.query.WildcardQueryBuilder;
61  import org.elasticsearch.index.query.functionscore.FunctionScoreQueryBuilder;
62  import org.elasticsearch.index.query.functionscore.FunctionScoreQueryBuilder.FilterFunctionBuilder;
63  import org.elasticsearch.index.query.functionscore.ScoreFunctionBuilder;
64  import org.elasticsearch.search.sort.FieldSortBuilder;
65  import org.elasticsearch.search.sort.SortBuilders;
66  import org.elasticsearch.search.sort.SortOrder;
67  
68  /**
69   * @author ESFlute (using FreeGen)
70   */
71  public abstract class EsAbstractConditionQuery implements ConditionQuery {
72  
73      protected static final String CQ_PROPERTY = "conditionQuery";
74  
75      // ===================================================================================
76      //                                                                           Attribute
77      //                                                                           =========
78      protected List<QueryBuilder> queryBuilderList;
79      protected List<FieldSortBuilder> fieldSortBuilderList;
80      private DocMetaCQ docMetaCQ;
81  
82      // ===================================================================================
83      //                                                                             Control
84      //                                                                             =======
85      public DocMetaCQ docMeta() {
86          if (docMetaCQ == null) {
87              docMetaCQ = new DocMetaCQ();
88          }
89          return docMetaCQ;
90      }
91  
92      public List<FieldSortBuilder> getFieldSortBuilderList() {
93          return fieldSortBuilderList == null ? Collections.emptyList() : fieldSortBuilderList;
94      }
95  
96      public boolean hasQueries() {
97          return queryBuilderList != null && !queryBuilderList.isEmpty();
98      }
99  
100     public QueryBuilder getQuery() {
101         if (queryBuilderList == null) {
102             return null;
103         } else if (queryBuilderList.size() == 1) {
104             return queryBuilderList.get(0);
105         }
106         BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();
107         queryBuilderList.forEach(query -> {
108             boolQuery.must(query);
109         });
110         return boolQuery;
111     }
112 
113     public List<QueryBuilder> getQueryBuilderList() {
114         return queryBuilderList != null ? queryBuilderList : Collections.emptyList();
115     }
116 
117     // ===================================================================================
118     //                                                                               Query
119     //                                                                               =====
120     public void addQuery(QueryBuilder queryBuilder) {
121         assertObjectNotNull("queryBuilder", queryBuilder);
122         regQ(queryBuilder);
123     }
124 
125     public void queryString(String queryString) {
126         checkEsInvalidQuery("queryString", queryString);
127         doQueryString(queryString, null);
128     }
129 
130     public void queryString(String queryString, ConditionOptionCall<QueryStringQueryBuilder> opLambda) {
131         checkEsInvalidQuery("queryString", queryString);
132         assertObjectNotNull("opLambda", opLambda);
133         doQueryString(queryString, opLambda);
134     }
135 
136     protected void doQueryString(String queryString, ConditionOptionCall<QueryStringQueryBuilder> opLambda) {
137         QueryStringQueryBuilder queryStringQuery = QueryBuilders.queryStringQuery(queryString);
138         regQ(queryStringQuery);
139         if (opLambda != null) {
140             opLambda.callback(queryStringQuery);
141         }
142     }
143 
144     public void matchAll() {
145         doMatchAll(null);
146     }
147 
148     public void matchAll(ConditionOptionCall<MatchAllQueryBuilder> opLambda) {
149         assertObjectNotNull("opLambda", opLambda);
150         doMatchAll(opLambda);
151     }
152 
153     protected void doMatchAll(ConditionOptionCall<MatchAllQueryBuilder> opLambda) {
154         MatchAllQueryBuilder builder = QueryBuilders.matchAllQuery();
155         regQ(builder);
156         if (opLambda != null) {
157             opLambda.callback(builder);
158         }
159     }
160 
161     // ===================================================================================
162     //                                                                            Register
163     //                                                                            ========
164 
165     protected FunctionScoreQueryBuilder regFunctionScoreQ(QueryBuilder queryBuilder, Collection<FilterFunctionBuilder> list) {
166         FunctionScoreQueryBuilder functionScoreQuery =
167                 QueryBuilders.functionScoreQuery(queryBuilder, list.toArray(new FilterFunctionBuilder[list.size()]));
168         regQ(functionScoreQuery);
169         return functionScoreQuery;
170     }
171 
172     protected BoolQueryBuilder regBoolCQ(List<QueryBuilder> mustList, List<QueryBuilder> shouldList, List<QueryBuilder> mustNotList,
173             List<QueryBuilder> filterList) {
174         assertObjectNotNull("mustList", mustList);
175         assertObjectNotNull("shouldList", shouldList);
176         assertObjectNotNull("mustNotList", mustNotList);
177         assertObjectNotNull("filterList", filterList);
178         BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();
179         mustList.forEach(query -> {
180             boolQuery.must(query);
181         });
182         shouldList.forEach(query -> {
183             boolQuery.should(query);
184         });
185         mustNotList.forEach(query -> {
186             boolQuery.mustNot(query);
187         });
188         filterList.forEach(query -> {
189             boolQuery.filter(query);
190         });
191         regQ(boolQuery);
192         return boolQuery;
193     }
194 
195     protected TermQueryBuilder regTermQ(String name, Object value) {
196         checkEsInvalidQuery(name, value);
197         TermQueryBuilder termQuery = QueryBuilders.termQuery(name, value);
198         regQ(termQuery);
199         return termQuery;
200     }
201 
202     protected TermsQueryBuilder regTermsQ(String name, Collection<?> values) {
203         checkEsInvalidQueryCollection(name, values);
204         TermsQueryBuilder termsQuery = QueryBuilders.termsQuery(name, values);
205         regQ(termsQuery);
206         return termsQuery;
207     }
208 
209     protected IdsQueryBuilder regIdsQ(Collection<String> values) {
210         checkEsInvalidQueryCollection("_id", values);
211         IdsQueryBuilder idsQuery = QueryBuilders.idsQuery().addIds(values.toArray(new String[values.size()]));
212         regQ(idsQuery);
213         return idsQuery;
214     }
215 
216     protected MatchQueryBuilder regMatchQ(String name, Object value) {
217         checkEsInvalidQuery(name, value);
218         MatchQueryBuilder matchQuery = QueryBuilders.matchQuery(name, value);
219         regQ(matchQuery);
220         return matchQuery;
221     }
222 
223     protected MatchPhraseQueryBuilder regMatchPhraseQ(String name, Object value) {
224         checkEsInvalidQuery(name, value);
225         MatchPhraseQueryBuilder matchQuery = QueryBuilders.matchPhraseQuery(name, value);
226         regQ(matchQuery);
227         return matchQuery;
228     }
229 
230     protected MatchPhrasePrefixQueryBuilder regMatchPhrasePrefixQ(String name, Object value) {
231         checkEsInvalidQuery(name, value);
232         MatchPhrasePrefixQueryBuilder matchQuery = QueryBuilders.matchPhrasePrefixQuery(name, value);
233         regQ(matchQuery);
234         return matchQuery;
235     }
236 
237     protected MatchQueryBuilder regFuzzyQ(String name, Object value) {
238         checkEsInvalidQuery(name, value);
239         MatchQueryBuilder fuzzyQuery = QueryBuilders.matchQuery(name, value).fuzziness(Fuzziness.AUTO);
240         regQ(fuzzyQuery);
241         return fuzzyQuery;
242     }
243 
244     protected PrefixQueryBuilder regPrefixQ(String name, String prefix) {
245         checkEsInvalidQuery(name, prefix);
246         PrefixQueryBuilder prefixQuery = QueryBuilders.prefixQuery(name, prefix);
247         regQ(prefixQuery);
248         return prefixQuery;
249     }
250 
251     protected RangeQueryBuilder regRangeQ(String name, ConditionKey ck, Object value) {
252         checkEsInvalidQuery(name, value);
253         assertObjectNotNull("ck", ck);
254         if (queryBuilderList != null) {
255             for (QueryBuilder builder : queryBuilderList) {
256                 if (builder instanceof RangeQueryBuilder) {
257                     RangeQueryBuilder rangeQueryBuilder = (RangeQueryBuilder) builder;
258                     if (rangeQueryBuilder.toString().replaceAll("\\s", "").startsWith("{\"range\":{\"" + name + "\"")) {
259                         addRangeC(rangeQueryBuilder, ck, value);
260                         return rangeQueryBuilder;
261                     }
262                 }
263             }
264         }
265         RangeQueryBuilder rangeQueryBuilder = QueryBuilders.rangeQuery(name);
266         addRangeC(rangeQueryBuilder, ck, value);
267         regQ(rangeQueryBuilder);
268         return rangeQueryBuilder;
269     }
270 
271     protected void addRangeC(RangeQueryBuilder builder, ConditionKey ck, Object value) {
272         assertObjectNotNull("ck", ck);
273         if (ck.equals(ConditionKey.CK_GREATER_THAN)) {
274             builder.gt(value);
275         } else if (ck.equals(ConditionKey.CK_GREATER_EQUAL)) {
276             builder.gte(value);
277         } else if (ck.equals(ConditionKey.CK_LESS_THAN)) {
278             builder.lt(value);
279         } else if (ck.equals(ConditionKey.CK_LESS_EQUAL)) {
280             builder.lte(value);
281         }
282     }
283 
284     protected ExistsQueryBuilder regExistsQ(String name) {
285         ExistsQueryBuilder existsQuery = QueryBuilders.existsQuery(name);
286         regQ(existsQuery);
287         return existsQuery;
288     }
289 
290     protected WildcardQueryBuilder regWildcardQ(String name, String wildcard) {
291         checkEsInvalidQuery(name, wildcard);
292         WildcardQueryBuilder wildcardQuery = QueryBuilders.wildcardQuery(name, wildcard);
293         regQ(wildcardQuery);
294         return wildcardQuery;
295     }
296 
297     protected RegexpQueryBuilder regRegexpQ(String name, String regexp) {
298         checkEsInvalidQuery(name, regexp);
299         RegexpQueryBuilder regexpQuery = QueryBuilders.regexpQuery(name, regexp);
300         regQ(regexpQuery);
301         return regexpQuery;
302     }
303 
304     protected CommonTermsQueryBuilder regCommonTermsQ(String name, Object text) {
305         checkEsInvalidQuery(name, text);
306         CommonTermsQueryBuilder commonTermsQuery = QueryBuilders.commonTermsQuery(name, text);
307         regQ(commonTermsQuery);
308         return commonTermsQuery;
309     }
310 
311     protected MoreLikeThisQueryBuilder regMoreLikeThisQueryQ(String name, String[] likeTexts) {
312         MoreLikeThisQueryBuilder moreLikeThisQuery = QueryBuilders.moreLikeThisQuery(new String[] { name }, likeTexts, null);
313         regQ(moreLikeThisQuery);
314         return moreLikeThisQuery;
315     }
316 
317     protected SpanTermQueryBuilder regSpanTermQ(String name, String value) {
318         checkEsInvalidQuery(name, value);
319         SpanTermQueryBuilder spanTermQuery = QueryBuilders.spanTermQuery(name, value);
320         regQ(spanTermQuery);
321         return spanTermQuery;
322     }
323 
324     protected void regQ(QueryBuilder builder) {
325         assertObjectNotNull("builder", builder);
326         if (queryBuilderList == null) {
327             queryBuilderList = new ArrayList<>();
328         }
329         queryBuilderList.add(builder);
330     }
331 
332     protected void regOBA(String field) {
333         registerOrderBy(field, true);
334     }
335 
336     protected void regOBD(String field) {
337         registerOrderBy(field, false);
338     }
339 
340     protected void registerOrderBy(String field, boolean ascOrDesc) {
341         assertObjectNotNull("field", field);
342         if (fieldSortBuilderList == null) {
343             fieldSortBuilderList = new ArrayList<>();
344         }
345         fieldSortBuilderList.add(SortBuilders.fieldSort(field).order(ascOrDesc ? SortOrder.ASC : SortOrder.DESC));
346     }
347 
348     // ===================================================================================
349     //                                                                       Invalid Query
350     //                                                                       =============
351     protected void checkEsInvalidQuery(String name, Object value) {
352         if (value == null || (value instanceof String && ((String) value).isEmpty())) {
353             String msg = "Cannot register null or empty query: name=" + name + " value=" + value;
354             throw new InvalidQueryRegisteredException(msg);
355         }
356     }
357 
358     protected void checkEsInvalidQueryCollection(String name, Collection<?> values) {
359         if (values == null || values.isEmpty()) {
360             String msg = "Cannot register null or empty query collection: name=" + name + " values=" + values;
361             throw new InvalidQueryRegisteredException(msg);
362         }
363     }
364 
365     // ===================================================================================
366     //                                                              DBFlute Implementation
367     //                                                              ======================
368     @Override
369     public ColumnRealName toColumnRealName(String columnDbName) {
370         return ColumnRealName.create(xgetAliasName(), toColumnSqlName(columnDbName));
371     }
372 
373     @Override
374     public ColumnRealName toColumnRealName(ColumnInfo columnInfo) {
375         return ColumnRealName.create(xgetAliasName(), columnInfo.getColumnSqlName());
376     }
377 
378     @Override
379     public ColumnSqlName toColumnSqlName(String columnDbName) {
380         return new ColumnSqlName(columnDbName);
381     }
382 
383     @Override
384     public ConditionBean xgetBaseCB() {
385         return null;
386     }
387 
388     @Override
389     public ConditionQuery xgetBaseQuery() {
390         return null;
391     }
392 
393     @Override
394     public ConditionQuery xgetReferrerQuery() {
395         return null;
396     }
397 
398     @Override
399     public SqlClause xgetSqlClause() {
400         return null;
401     }
402 
403     @Override
404     public int xgetNestLevel() {
405         return 0;
406     }
407 
408     @Override
409     public int xgetNextNestLevel() {
410         return 0;
411     }
412 
413     @Override
414     public boolean isBaseQuery() {
415         return false;
416     }
417 
418     @Override
419     public String xgetForeignPropertyName() {
420         return null;
421     }
422 
423     @Override
424     public String xgetRelationPath() {
425         return null;
426     }
427 
428     @Override
429     public String xgetLocationBase() {
430         final StringBuilder sb = new StringBuilder();
431         ConditionQuery query = this;
432         while (true) {
433             if (query.isBaseQuery()) {
434                 sb.insert(0, CQ_PROPERTY + ".");
435                 break;
436             } else {
437                 final String foreignPropertyName = query.xgetForeignPropertyName();
438                 if (foreignPropertyName == null) {
439                     String msg = "The foreignPropertyName of the query should not be null:";
440                     msg = msg + " query=" + query;
441                     throw new IllegalStateException(msg);
442                 }
443                 sb.insert(0, CQ_PROPERTY + Srl.initCap(foreignPropertyName) + ".");
444             }
445             query = query.xgetReferrerQuery();
446         }
447         return sb.toString();
448     }
449 
450     @Override
451     public ConditionValue invokeValue(String columnFlexibleName) {
452         return null;
453     }
454 
455     @Override
456     public void invokeQuery(String columnFlexibleName, String conditionKeyName, Object conditionValue) {
457         // nothing
458     }
459 
460     @Override
461     public void invokeQuery(String columnFlexibleName, String conditionKeyName, Object conditionValue, ConditionOption conditionOption) {
462         // nothing
463     }
464 
465     @Override
466     public void invokeQueryEqual(String columnFlexibleName, Object conditionValue) {
467         // nothing
468     }
469 
470     @Override
471     public void invokeQueryNotEqual(String columnFlexibleName, Object conditionValue) {
472         // nothing
473     }
474 
475     @Override
476     public void invokeOrderBy(String columnFlexibleName, boolean isAsc) {
477         // nothing
478     }
479 
480     @Override
481     public ConditionQuery invokeForeignCQ(String foreignPropertyName) {
482         return null;
483     }
484 
485     @Override
486     public boolean invokeHasForeignCQ(String foreignPropertyName) {
487         return false;
488     }
489 
490     @Override
491     public void xregisterParameterOption(ParameterOption option) {
492         // nothing
493     }
494 
495     // ===================================================================================
496     //                                                                      General Helper
497     //                                                                      ==============
498     protected void assertObjectNotNull(String variableName, Object value) {
499         if (variableName == null) {
500             String msg = "The value should not be null: variableName=null value=" + value;
501             throw new IllegalArgumentException(msg);
502         }
503         if (value == null) {
504             String msg = "The value should not be null: variableName=" + variableName;
505             throw new IllegalArgumentException(msg);
506         }
507     }
508 
509     protected String toRangeDateString(Date date, String format) {
510         if (format.contains("epoch_millis")) {
511             return Long.toString(date.getTime());
512         } else if (format.contains("date_optional_time")) {
513             final SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss");
514             sdf.setTimeZone(TimeZone.getTimeZone("UTC"));
515             return sdf.format(date);
516         } else {
517             return Long.toString(date.getTime());
518         }
519     }
520 
521     protected String toRangeLocalDateTimeString(LocalDateTime date, String format) {
522         if (format.contains("epoch_millis")) {
523             return Long.toString(date.atZone(ZoneId.systemDefault()).toInstant().toEpochMilli());
524         } else if (format.contains("date_optional_time")) {
525             return DateTimeFormatter.ISO_DATE_TIME.format(date);
526         } else {
527             return Long.toString(date.atZone(ZoneId.systemDefault()).toInstant().toEpochMilli());
528         }
529     }
530 
531     // ===================================================================================
532     //                                                                        Assist Class
533     //                                                                        ============
534     public class DocMetaCQ {
535 
536         public void setId_Equal(String id) {
537             regQ(QueryBuilders.idsQuery().addIds(id));
538         }
539     }
540 
541     @FunctionalInterface
542     public interface ConditionOptionCall<OP extends QueryBuilder> {
543 
544         /**
545          * @param op The option of condition to be set up. (NotNull)
546          */
547         void callback(OP op);
548     }
549 
550     @FunctionalInterface
551     public interface BoolCall<CQ extends EsAbstractConditionQuery> {
552 
553         void callback(CQ must, CQ should, CQ mustNot, CQ filter);
554     }
555 
556     @FunctionalInterface
557     public interface FilteredCall<CQ extends EsAbstractConditionQuery, CF extends EsAbstractConditionQuery> {
558 
559         void callback(CQ query, CF filter);
560     }
561 
562     @FunctionalInterface
563     public interface OperatorCall<CQ extends EsAbstractConditionQuery> {
564 
565         void callback(CQ query);
566     }
567 
568     @FunctionalInterface
569     public interface ScoreFunctionCall<CC extends ScoreFunctionCreator<?>> {
570 
571         void callback(CC creator);
572     }
573 
574     @FunctionalInterface
575     public interface ScoreFunctionCreator<T extends EsAbstractConditionQuery> {
576         void filter(final OperatorCall<T> cqLambda, final ScoreFunctionBuilder<?> scoreFunctionBuilder);
577     }
578 }