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