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.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 }