View Javadoc
1   /*
2    * Copyright 2012-2021 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.app.web.search;
17  
18  import static org.codelibs.core.stream.StreamUtil.stream;
19  
20  import java.util.ArrayList;
21  import java.util.Collections;
22  import java.util.HashSet;
23  import java.util.List;
24  import java.util.Locale;
25  import java.util.Map;
26  import java.util.Set;
27  
28  import javax.annotation.Resource;
29  import javax.servlet.http.HttpSession;
30  
31  import org.apache.logging.log4j.LogManager;
32  import org.apache.logging.log4j.Logger;
33  import org.codelibs.core.lang.StringUtil;
34  import org.codelibs.fess.Constants;
35  import org.codelibs.fess.app.web.base.FessSearchAction;
36  import org.codelibs.fess.app.web.base.SearchForm;
37  import org.codelibs.fess.entity.SearchRenderData;
38  import org.codelibs.fess.entity.SearchRequestParams.SearchRequestType;
39  import org.codelibs.fess.exception.InvalidQueryException;
40  import org.codelibs.fess.exception.ResultOffsetExceededException;
41  import org.codelibs.fess.helper.RelatedContentHelper;
42  import org.codelibs.fess.helper.RelatedQueryHelper;
43  import org.codelibs.fess.util.RenderDataUtil;
44  import org.lastaflute.taglib.function.LaFunctions;
45  import org.lastaflute.web.Execute;
46  import org.lastaflute.web.response.HtmlResponse;
47  import org.lastaflute.web.response.render.RenderData;
48  import org.lastaflute.web.util.LaRequestUtil;
49  
50  public class SearchAction extends FessSearchAction {
51  
52      // ===================================================================================
53      //                                                                            Constant
54      //
55      private static final Logger logger = LogManager.getLogger(SearchAction.class);
56  
57      // ===================================================================================
58      //                                                                           Attribute
59      //
60  
61      @Resource
62      protected RelatedContentHelper relatedContentHelper;
63  
64      @Resource
65      protected RelatedQueryHelper relatedQueryHelper;
66  
67      // ===================================================================================
68      //                                                                               Hook
69      //                                                                              ======
70  
71      // ===================================================================================
72      //                                                                      Search Execute
73      //                                                                      ==============
74      @Execute
75      public HtmlResponse index(final SearchForm form) {
76          return search(form);
77      }
78  
79      @Execute
80      public HtmlResponse advance(final SearchForm form) {
81          if (isLoginRequired()) {
82              return redirectToLogin();
83          }
84          validate(form, messages -> {}, () -> asHtml(virtualHost(path_IndexJsp)).renderWith(data -> {
85              buildInitParams();
86              RenderDataUtil.register(data, "notification", fessConfig.getNotificationSearchTop());
87          }));
88          if (!form.hasConditionQuery()) {
89              if (StringUtil.isNotBlank(form.q)) {
90                  form.as.put("q", new String[] { form.q });
91              } else {
92                  // TODO set default?
93              }
94          }
95          return asHtml(virtualHost(path_AdvanceJsp)).renderWith(data -> {
96              buildInitParams();
97              RenderDataUtil.register(data, "notification", fessConfig.getNotificationAdvanceSearch());
98          });
99      }
100 
101     @Execute
102     public HtmlResponse search(final SearchForm form) {
103         if (viewHelper.isUseSession()) {
104             LaRequestUtil.getOptionalRequest().ifPresent(request -> {
105                 final HttpSession session = request.getSession(false);
106                 if (session != null && form.num != null) {
107                     session.setAttribute(Constants.RESULTS_PER_PAGE, form.num);
108                 }
109             });
110         }
111         return doSearch(form);
112     }
113 
114     @Execute
115     public HtmlResponse prev(final SearchForm form) {
116         return doMove(form, -1);
117     }
118 
119     @Execute
120     public HtmlResponse next(final SearchForm form) {
121         return doMove(form, 1);
122     }
123 
124     @Execute
125     public HtmlResponse move(final SearchForm form) {
126         return doMove(form, 0);
127     }
128 
129     protected HtmlResponse doSearch(final SearchForm form) {
130         validate(form, messages -> {}, () -> asHtml(virtualHost(path_SearchJsp)));
131         if (isLoginRequired()) {
132             return redirectToLogin();
133         }
134 
135         if (viewHelper.isUseSession()) {
136             final HttpSession session = request.getSession(false);
137             if (session != null) {
138                 final Object resultsPerPage = session.getAttribute(Constants.RESULTS_PER_PAGE);
139                 if (resultsPerPage instanceof Integer) {
140                     form.num = (Integer) resultsPerPage;
141                 }
142             }
143         }
144 
145         if (StringUtil.isBlank(form.q) && form.fields.isEmpty() && !form.hasConditionQuery()) {
146             // redirect to index page
147             form.q = null;
148             return redirectToRoot();
149         }
150 
151         try {
152             buildFormParams(form);
153             form.lang = searchHelper.getLanguages(request, form);
154             final WebRenderData renderData = new WebRenderData();
155             searchHelper.search(form, renderData, getUserBean());
156             return asHtml(virtualHost(path_SearchJsp)).renderWith(data -> {
157                 if (form.hasConditionQuery()) {
158                     form.q = renderData.getSearchQuery();
159                 }
160                 renderData.register(data);
161                 RenderDataUtil.register(data, "displayQuery", getDisplayQuery(form, labelTypeHelper
162                         .getLabelTypeItemList(SearchRequestType.SEARCH, request.getLocale() == null ? Locale.ROOT : request.getLocale())));
163                 createPagingQuery(form);
164                 final String[] relatedContents = relatedContentHelper.getRelatedContents(form.getQuery());
165                 RenderDataUtil.register(data, "relatedContents", relatedContents);
166                 final String[] relatedQueries = relatedQueryHelper.getRelatedQueries(form.getQuery());
167                 if (relatedQueries.length > 0) {
168                     RenderDataUtil.register(data, "relatedQueries", relatedQueries);
169                 }
170             });
171         } catch (final InvalidQueryException e) {
172             if (logger.isDebugEnabled()) {
173                 logger.debug(e.getMessage(), e);
174             }
175             saveError(e.getMessageCode());
176             return redirectToRoot();
177         } catch (final ResultOffsetExceededException e) {
178             if (logger.isDebugEnabled()) {
179                 logger.debug(e.getMessage(), e);
180             }
181             saveError(messages -> {
182                 messages.addErrorsResultSizeExceeded(GLOBAL);
183             });
184             return redirectToRoot();
185         }
186     }
187 
188     protected HtmlResponse doMove(final SearchForm form, final int move) {
189         int start = fessConfig.getPagingSearchPageStartAsInteger();
190         if (form.pn != null) {
191             int pageNumber = form.pn;
192             if (pageNumber > 0) {
193                 pageNumber = pageNumber + move;
194                 if (pageNumber < 1) {
195                     pageNumber = 1;
196                 }
197                 start = (pageNumber - 1) * form.getPageSize();
198             }
199         }
200         form.start = start;
201 
202         return doSearch(form);
203     }
204 
205     protected String getDisplayQuery(final SearchForm form, final List<Map<String, String>> labelTypeItems) {
206         final StringBuilder buf = new StringBuilder(100);
207         buf.append(form.q);
208         if (!form.fields.isEmpty() && form.fields.containsKey(LABEL_FIELD)) {
209             final String[] values = form.fields.get(LABEL_FIELD);
210             final List<String> labelList = new ArrayList<>();
211             if (values != null) {
212                 Collections.addAll(labelList, values);
213             }
214             for (final String labelTypeValue : labelList) {
215                 for (final Map<String, String> map : labelTypeItems) {
216                     if (map.get(Constants.ITEM_VALUE).equals(labelTypeValue)) {
217                         buf.append(' ');
218                         buf.append(map.get(Constants.ITEM_LABEL));
219                         break;
220                     }
221                 }
222             }
223         }
224         return buf.toString();
225     }
226 
227     protected void createPagingQuery(final SearchForm form) {
228         final List<String> pagingQueryList = new ArrayList<>();
229         if (form.ex_q != null) {
230             stream(form.ex_q).of(stream -> stream.filter(StringUtil::isNotBlank).distinct()
231                     .forEach(q -> pagingQueryList.add("ex_q=" + LaFunctions.u(q))));
232         }
233         if (StringUtil.isNotBlank(form.sort)) {
234             pagingQueryList.add("sort=" + LaFunctions.u(form.sort));
235         }
236         if (form.lang != null) {
237             final Set<String> langSet = new HashSet<>();
238             for (final String lang : form.lang) {
239                 if (StringUtil.isNotBlank(lang) && lang.length() < 1000) {
240                     if (Constants.ALL_LANGUAGES.equals(lang)) {
241                         langSet.clear();
242                         break;
243                     }
244                     final String normalizeLang = systemHelper.normalizeLang(lang);
245                     if (normalizeLang != null) {
246                         langSet.add(normalizeLang);
247                     }
248                 }
249             }
250             if (!langSet.isEmpty()) {
251                 for (final String lang : langSet) {
252                     pagingQueryList.add("&lang=" + LaFunctions.u(lang));
253                 }
254             }
255         }
256         form.fields.entrySet().stream().filter(e -> e.getValue() != null).forEach(e -> {
257             final String key = LaFunctions.u(e.getKey());
258             stream(e.getValue()).of(stream -> stream.filter(StringUtil::isNotBlank)
259                     .forEach(s -> pagingQueryList.add("fields." + key + "=" + LaFunctions.u(s))));
260         });
261         form.as.entrySet().stream().filter(e -> e.getValue() != null).forEach(e -> {
262             final String key = LaFunctions.u(e.getKey());
263             stream(e.getValue()).of(stream -> stream.filter(StringUtil::isNotBlank)
264                     .forEach(s -> pagingQueryList.add("as." + key + "=" + LaFunctions.u(s))));
265         });
266         request.setAttribute(Constants.PAGING_QUERY_LIST, pagingQueryList);
267     }
268 
269     protected static class WebRenderData extends SearchRenderData {
270 
271         public void register(final RenderData data) {
272             RenderDataUtil.register(data, "queryId", queryId);
273             RenderDataUtil.register(data, "documentItems", documentItems);
274             RenderDataUtil.register(data, "facetResponse", facetResponse);
275             RenderDataUtil.register(data, "appendHighlightParams", appendHighlightParams);
276             RenderDataUtil.register(data, "execTime", execTime);
277             RenderDataUtil.register(data, "pageSize", pageSize);
278             RenderDataUtil.register(data, "currentPageNumber", currentPageNumber);
279             RenderDataUtil.register(data, "allRecordCount", allRecordCount);
280             RenderDataUtil.register(data, "allRecordCountRelation", allRecordCountRelation);
281             RenderDataUtil.register(data, "allPageCount", allPageCount);
282             RenderDataUtil.register(data, "existNextPage", existNextPage);
283             RenderDataUtil.register(data, "existPrevPage", existPrevPage);
284             RenderDataUtil.register(data, "currentStartRecordNumber", currentStartRecordNumber);
285             RenderDataUtil.register(data, "currentEndRecordNumber", currentEndRecordNumber);
286             RenderDataUtil.register(data, "pageNumberList", pageNumberList);
287             RenderDataUtil.register(data, "partialResults", partialResults);
288             RenderDataUtil.register(data, "queryTime", queryTime);
289             RenderDataUtil.register(data, "searchQuery", searchQuery);
290             RenderDataUtil.register(data, "requestedTime", requestedTime);
291         }
292     }
293 }