View Javadoc
1   /*
2    * Copyright 2012-2025 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.query;
17  
18  import org.apache.logging.log4j.LogManager;
19  import org.apache.logging.log4j.Logger;
20  import org.apache.lucene.index.Term;
21  import org.apache.lucene.search.Query;
22  import org.apache.lucene.search.TermRangeQuery;
23  import org.apache.lucene.util.BytesRef;
24  import org.codelibs.fess.Constants;
25  import org.codelibs.fess.entity.QueryContext;
26  import org.codelibs.fess.exception.InvalidQueryException;
27  import org.codelibs.fess.mylasta.direction.FessConfig;
28  import org.codelibs.fess.util.ComponentUtil;
29  import org.lastaflute.core.message.UserMessages;
30  import org.opensearch.index.query.QueryBuilder;
31  import org.opensearch.index.query.QueryBuilders;
32  import org.opensearch.index.query.RangeQueryBuilder;
33  
34  /**
35   * Command class for handling term range query execution and conversion.
36   * This class processes Lucene TermRangeQuery objects and converts them to OpenSearch QueryBuilder instances.
37   */
38  public class TermRangeQueryCommand extends QueryCommand {
39      private static final Logger logger = LogManager.getLogger(TermRangeQueryCommand.class);
40  
41      /**
42       * Default constructor for TermRangeQueryCommand.
43       */
44      public TermRangeQueryCommand() {
45          super();
46      }
47  
48      @Override
49      protected String getQueryClassName() {
50          return TermRangeQuery.class.getSimpleName();
51      }
52  
53      @Override
54      public QueryBuilder execute(final QueryContext context, final Query query, final float boost) {
55          if (query instanceof final TermRangeQuery termRangeQuery) {
56              if (logger.isDebugEnabled()) {
57                  logger.debug("TermRangeQuery: query={}, boost={}", query, boost);
58              }
59              return convertTermRangeQuery(context, termRangeQuery, boost);
60          }
61          throw new InvalidQueryException(messages -> messages.addErrorsInvalidQueryUnknown(UserMessages.GLOBAL_PROPERTY_KEY),
62                  "Unknown q: " + query.getClass() + " => " + query);
63      }
64  
65      /**
66       * Converts a TermRangeQuery to a QueryBuilder with the given boost value.
67       * Handles both search fields and default field queries with proper range semantics.
68       *
69       * @param context the query context
70       * @param termRangeQuery the term range query to convert
71       * @param boost the boost value to apply
72       * @return the converted QueryBuilder
73       */
74      protected QueryBuilder convertTermRangeQuery(final QueryContext context, final TermRangeQuery termRangeQuery, final float boost) {
75          final FessConfig fessConfig = ComponentUtil.getFessConfig();
76          final String field = getSearchField(context.getDefaultField(), termRangeQuery.getField());
77  
78          if (!isSearchField(field)) {
79              final StringBuilder queryBuf = new StringBuilder();
80              queryBuf.append(termRangeQuery.includesLower() ? '[' : '{');
81              final BytesRef lowerTerm = termRangeQuery.getLowerTerm();
82              queryBuf.append(lowerTerm != null ? "*".equals(Term.toString(lowerTerm)) ? "\\*" : Term.toString(lowerTerm) : "*");
83              queryBuf.append(" TO ");
84              final BytesRef upperTerm = termRangeQuery.getUpperTerm();
85              queryBuf.append(upperTerm != null ? "*".equals(Term.toString(upperTerm)) ? "\\*" : Term.toString(upperTerm) : "*");
86              queryBuf.append(termRangeQuery.includesUpper() ? ']' : '}');
87              final String origQuery = queryBuf.toString();
88              context.addFieldLog(Constants.DEFAULT_FIELD, origQuery);
89              context.addHighlightedQuery(origQuery);
90              return buildDefaultQueryBuilder(fessConfig, context, (f, b) -> QueryBuilders.matchPhraseQuery(f, origQuery).boost(b));
91          }
92  
93          context.addFieldLog(field, termRangeQuery.toString(field));
94          final RangeQueryBuilder rangeQuery = QueryBuilders.rangeQuery(field);
95          final BytesRef min = termRangeQuery.getLowerTerm();
96          if (min != null) {
97              if (termRangeQuery.includesLower()) {
98                  rangeQuery.gte(min.utf8ToString());
99              } else {
100                 rangeQuery.gt(min.utf8ToString());
101             }
102         }
103         final BytesRef max = termRangeQuery.getUpperTerm();
104         if (max != null) {
105             if (termRangeQuery.includesUpper()) {
106                 rangeQuery.lte(max.utf8ToString());
107             } else {
108                 rangeQuery.lt(max.utf8ToString());
109             }
110         }
111         rangeQuery.boost(boost);
112         return rangeQuery;
113     }
114 
115 }