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.helper;
17
18 import java.util.Collections;
19 import java.util.HashMap;
20 import java.util.List;
21 import java.util.Locale;
22 import java.util.Map;
23
24 import org.apache.logging.log4j.LogManager;
25 import org.apache.logging.log4j.Logger;
26 import org.codelibs.core.lang.StringUtil;
27 import org.codelibs.fess.opensearch.config.exbhv.RelatedQueryBhv;
28 import org.codelibs.fess.opensearch.config.exentity.RelatedQuery;
29 import org.codelibs.fess.util.ComponentUtil;
30
31 import jakarta.annotation.PostConstruct;
32
33 /**
34 * Helper class for managing related query configurations.
35 * This class provides functionality to load, cache, and retrieve related queries
36 * based on search terms and virtual hosts. Related queries are used to suggest
37 * alternative or supplementary search terms to improve search results.
38 */
39 public class RelatedQueryHelper extends AbstractConfigHelper {
40 private static final Logger logger = LogManager.getLogger(RelatedQueryHelper.class);
41
42 /**
43 * Map storing related queries organized by virtual host key and search term.
44 * The outer map key is the virtual host key, the inner map key is the search term
45 * (in lowercase), and the value is an array of related query strings.
46 */
47 protected volatile Map<String, Map<String, String[]>> relatedQueryMap = Collections.emptyMap();
48
49 /**
50 * Default constructor for RelatedQueryHelper.
51 * Initializes the helper with an empty related query map.
52 */
53 public RelatedQueryHelper() {
54 super();
55 }
56
57 /**
58 * Initializes the RelatedQueryHelper after dependency injection is complete.
59 * This method is called automatically by the dependency injection framework
60 * and loads the initial related query configurations.
61 */
62 @PostConstruct
63 public void init() {
64 if (logger.isDebugEnabled()) {
65 logger.debug("Initializing {}", this.getClass().getSimpleName());
66 }
67 load();
68 }
69
70 /**
71 * Retrieves a list of all available related query entities from the data store.
72 * The results are ordered by term and limited by the configured maximum fetch size.
73 *
74 * @return a list of RelatedQuery entities containing all available related queries
75 */
76 public List<RelatedQuery> getAvailableRelatedQueryList() {
77
78 return ComponentUtil.getComponent(RelatedQueryBhv.class).selectList(cb -> {
79 cb.query().matchAll();
80 cb.query().addOrderBy_Term_Asc();
81 cb.fetchFirst(ComponentUtil.getFessConfig().getPageRelatedqueryMaxFetchSizeAsInteger());
82 });
83 }
84
85 @Override
86 public int load() {
87 final Map<String, Map<String, String[]>> relatedQueryMap = new HashMap<>();
88 getAvailableRelatedQueryList().stream().forEach(entity -> {
89 final String key = getHostKey(entity);
90 Map<String, String[]> map = relatedQueryMap.get(key);
91 if (map == null) {
92 map = new HashMap<>();
93 relatedQueryMap.put(key, map);
94 }
95 map.put(toLowerCase(entity.getTerm()), entity.getQueries());
96 });
97 this.relatedQueryMap = relatedQueryMap;
98 return relatedQueryMap.size();
99 }
100
101 /**
102 * Extracts the virtual host key from a RelatedQuery entity.
103 * If the virtual host is blank or null, returns an empty string.
104 *
105 * @param entity the RelatedQuery entity to extract the host key from
106 * @return the virtual host key, or empty string if blank or null
107 */
108 protected String getHostKey(final RelatedQuery entity) {
109 final String key = entity.getVirtualHost();
110 return StringUtil.isBlank(key) ? StringUtil.EMPTY : key;
111 }
112
113 /**
114 * Retrieves related queries for a given search term.
115 * The search is performed using the current virtual host context and
116 * the query term is converted to lowercase for case-insensitive matching.
117 *
118 * @param query the search term to find related queries for
119 * @return an array of related query strings, or empty array if none found
120 */
121 public String[] getRelatedQueries(final String query) {
122 final String key = ComponentUtil.getVirtualHostHelper().getVirtualHostKey();
123 final Map<String, String[]> map = relatedQueryMap.get(key);
124 if (map != null) {
125 final String[] queries = map.get(toLowerCase(query));
126 if (queries != null) {
127 return queries;
128 }
129 }
130 return StringUtil.EMPTY_STRINGS;
131 }
132
133 private String toLowerCase(final String term) {
134 return term != null ? term.toLowerCase(Locale.ROOT) : term;
135 }
136
137 }