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.entity;
17  
18  import java.util.Arrays;
19  
20  import org.apache.logging.log4j.LogManager;
21  import org.apache.logging.log4j.Logger;
22  import org.codelibs.core.lang.StringUtil;
23  import org.codelibs.core.stream.StreamUtil;
24  import org.codelibs.fess.mylasta.direction.FessConfig;
25  import org.codelibs.fess.util.ComponentUtil;
26  import org.opensearch.search.aggregations.BucketOrder;
27  
28  import jakarta.annotation.PostConstruct;
29  
30  /**
31   * Entity class representing facet configuration information for search results.
32   * This class holds configuration settings for faceted search including field facets,
33   * query facets, and various parameters that control facet behavior.
34   */
35  public class FacetInfo {
36      /** Logger instance for this class */
37      private static final Logger logger = LogManager.getLogger(FacetInfo.class);
38  
39      /** Array of field names to create facets for */
40      public String[] field;
41  
42      /** Array of query strings to create query facets for */
43      public String[] query;
44  
45      /** Maximum number of facet values to return */
46      public Integer size;
47  
48      /** Minimum document count required for a facet value to be included */
49      public Long minDocCount;
50  
51      /** Sort order for facet values (e.g., "count.desc", "term.asc") */
52      public String sort;
53  
54      /** Value to use for documents that don't have the facet field */
55      public String missing;
56  
57      /**
58       * Default constructor for FacetInfo.
59       */
60      public FacetInfo() {
61          // Default constructor
62      }
63  
64      /**
65       * Initializes the facet configuration from Fess configuration properties.
66       * This method is called after dependency injection to load default facet settings.
67       */
68      @PostConstruct
69      public void init() {
70          final FessConfig fessConfig = ComponentUtil.getFessConfig();
71          if (StringUtil.isNotBlank(fessConfig.getQueryFacetFields())) {
72              field = StreamUtil.split(fessConfig.getQueryFacetFields(), ",")
73                      .get(stream -> stream.map(String::trim).filter(StringUtil::isNotEmpty).distinct().toArray(n -> new String[n]));
74          }
75          if (StringUtil.isNotBlank(fessConfig.getQueryFacetFieldsSize())) {
76              size = fessConfig.getQueryFacetFieldsSizeAsInteger();
77          }
78          if (StringUtil.isNotBlank(fessConfig.getQueryFacetFieldsMinDocCount())) {
79              minDocCount = Long.parseLong(fessConfig.getQueryFacetFieldsMinDocCount());
80          }
81          if (StringUtil.isNotBlank(fessConfig.getQueryFacetFieldsSort())) {
82              sort = fessConfig.getQueryFacetFieldsSort();
83          }
84          if (StringUtil.isNotBlank(fessConfig.getQueryFacetFieldsMissing())) {
85              missing = fessConfig.getQueryFacetFieldsMissing();
86          }
87      }
88  
89      /**
90       * Converts the sort string into a BucketOrder object for OpenSearch aggregations.
91       * Parses sort configuration like "count.desc" or "term.asc" into appropriate bucket ordering.
92       *
93       * @return the BucketOrder instance representing the sort configuration
94       */
95      public BucketOrder getBucketOrder() {
96          if (StringUtil.isNotBlank(sort)) {
97              final String[] values = sort.split("\\.");
98              final boolean asc;
99              if (values.length > 1) {
100                 asc = !"desc".equalsIgnoreCase(values[1]);
101             } else {
102                 asc = true;
103             }
104             if (values.length > 0) {
105                 if ("term".equals(values[0]) || "key".equals(values[0])) {
106                     return BucketOrder.key(asc);
107                 }
108                 if ("count".equals(values[0])) {
109                     return BucketOrder.count(asc);
110                 }
111             }
112         }
113         return BucketOrder.count(false);
114     }
115 
116     /**
117      * Adds a query facet to the existing query array.
118      * If no queries exist, creates a new array with the provided query.
119      *
120      * @param s the query string to add as a facet
121      */
122     public void addQuery(final String s) {
123         if (query == null) {
124             query = new String[] { s };
125         } else {
126             final String[] newQuery = Arrays.copyOf(query, query.length + 1);
127             newQuery[query.length] = s;
128             query = newQuery;
129         }
130         if (logger.isDebugEnabled()) {
131             logger.debug("Loaded facet query: query={}", s);
132         }
133     }
134 
135     /**
136      * Returns a string representation of this FacetInfo object.
137      * Includes all field values in the format useful for debugging.
138      *
139      * @return string representation of this FacetInfo instance
140      */
141     @Override
142     public String toString() {
143         return "FacetInfo [field=" + Arrays.toString(field) + ", query=" + Arrays.toString(query) + ", size=" + size + ", minDocCount="
144                 + minDocCount + ", sort=" + sort + ", missing=" + missing + "]";
145     }
146 }