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 }