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.api.json;
17  
18  import java.io.IOException;
19  import java.net.URLDecoder;
20  import java.util.ArrayList;
21  import java.util.Arrays;
22  import java.util.HashMap;
23  import java.util.List;
24  import java.util.Locale;
25  import java.util.Map;
26  
27  import javax.servlet.FilterChain;
28  import javax.servlet.ServletException;
29  import javax.servlet.http.HttpServletRequest;
30  import javax.servlet.http.HttpServletResponse;
31  
32  import org.codelibs.core.lang.StringUtil;
33  import org.codelibs.fess.Constants;
34  import org.codelibs.fess.api.BaseJsonApiManager;
35  import org.codelibs.fess.app.service.FavoriteLogService;
36  import org.codelibs.fess.app.service.SearchService;
37  import org.codelibs.fess.entity.FacetInfo;
38  import org.codelibs.fess.entity.GeoInfo;
39  import org.codelibs.fess.entity.PingResponse;
40  import org.codelibs.fess.entity.SearchRenderData;
41  import org.codelibs.fess.entity.SearchRequestParams;
42  import org.codelibs.fess.entity.SearchRequestParams.SearchRequestType;
43  import org.codelibs.fess.es.client.FessEsClient;
44  import org.codelibs.fess.exception.WebApiException;
45  import org.codelibs.fess.helper.LabelTypeHelper;
46  import org.codelibs.fess.helper.PopularWordHelper;
47  import org.codelibs.fess.helper.RelatedContentHelper;
48  import org.codelibs.fess.helper.RelatedQueryHelper;
49  import org.codelibs.fess.helper.SystemHelper;
50  import org.codelibs.fess.helper.UserInfoHelper;
51  import org.codelibs.fess.mylasta.direction.FessConfig;
52  import org.codelibs.fess.util.ComponentUtil;
53  import org.codelibs.fess.util.DocumentUtil;
54  import org.codelibs.fess.util.FacetResponse;
55  import org.codelibs.fess.util.FacetResponse.Field;
56  import org.dbflute.optional.OptionalThing;
57  import org.elasticsearch.common.xcontent.ToXContent;
58  import org.elasticsearch.common.xcontent.XContentBuilder;
59  import org.elasticsearch.common.xcontent.XContentFactory;
60  import org.elasticsearch.script.Script;
61  import org.slf4j.Logger;
62  import org.slf4j.LoggerFactory;
63  
64  public class JsonApiManager extends BaseJsonApiManager {
65  
66      private static final Logger logger = LoggerFactory.getLogger(JsonApiManager.class);
67  
68      public JsonApiManager() {
69          setPathPrefix("/json");
70      }
71  
72      @Override
73      public boolean matches(final HttpServletRequest request) {
74          if (!ComponentUtil.getFessConfig().isWebApiJson()) {
75              return false;
76          }
77  
78          final String servletPath = request.getServletPath();
79          return servletPath.startsWith(pathPrefix);
80      }
81  
82      @Override
83      public void process(final HttpServletRequest request, final HttpServletResponse response, final FilterChain chain) throws IOException,
84              ServletException {
85          final String formatType = request.getParameter("type");
86          switch (getFormatType(formatType)) {
87          case SEARCH:
88              processSearchRequest(request, response, chain);
89              break;
90          case LABEL:
91              processLabelRequest(request, response, chain);
92              break;
93          case POPULARWORD:
94              processPopularWordRequest(request, response, chain);
95              break;
96          case FAVORITE:
97              processFavoriteRequest(request, response, chain);
98              break;
99          case FAVORITES:
100             processFavoritesRequest(request, response, chain);
101             break;
102         case PING:
103             processPingRequest(request, response, chain);
104             break;
105         default:
106             writeJsonResponse(99, StringUtil.EMPTY, "Not found.");
107             break;
108         }
109     }
110 
111     protected void processPingRequest(final HttpServletRequest request, final HttpServletResponse response, final FilterChain chain) {
112         final FessEsClient fessEsClient = ComponentUtil.getFessEsClient();
113         int status;
114         Exception err = null;
115         try {
116             final PingResponse pingResponse = fessEsClient.ping();
117             status = pingResponse.getStatus();
118             writeJsonResponse(status, "\"message\":" + pingResponse.getMessage());
119         } catch (final Exception e) {
120             status = 9;
121             err = e;
122             if (logger.isDebugEnabled()) {
123                 logger.debug("Failed to process a ping request.", e);
124             }
125             writeJsonResponse(status, null, err);
126         }
127     }
128 
129     protected void processSearchRequest(final HttpServletRequest request, final HttpServletResponse response, final FilterChain chain) {
130         final SearchService searchService = ComponentUtil.getComponent(SearchService.class);
131         final FessConfig fessConfig = ComponentUtil.getFessConfig();
132         final RelatedQueryHelper relatedQueryHelper = ComponentUtil.getRelatedQueryHelper();
133         final RelatedContentHelper relatedContentHelper = ComponentUtil.getRelatedContentHelper();
134 
135         int status = 0;
136         Exception err = null;
137         String query = null;
138         final StringBuilder buf = new StringBuilder(1000); // TODO replace response stream
139         request.setAttribute(Constants.SEARCH_LOG_ACCESS_TYPE, Constants.SEARCH_LOG_ACCESS_TYPE_JSON);
140         try {
141             final SearchRenderData data = new SearchRenderData();
142             final JsonRequestParams params = new JsonRequestParams(request, fessConfig);
143             query = params.getQuery();
144             searchService.search(params, data, OptionalThing.empty());
145             final String execTime = data.getExecTime();
146             final String queryTime = Long.toString(data.getQueryTime());
147             final String pageSize = Integer.toString(data.getPageSize());
148             final String currentPageNumber = Integer.toString(data.getCurrentPageNumber());
149             final String allRecordCount = Long.toString(data.getAllRecordCount());
150             final String allPageCount = Integer.toString(data.getAllPageCount());
151             final List<Map<String, Object>> documentItems = data.getDocumentItems();
152             final FacetResponse facetResponse = data.getFacetResponse();
153             final String queryId = data.getQueryId();
154             final String highlightParams = data.getAppendHighlightParams();
155             final boolean nextPage = data.isExistNextPage();
156             final boolean prevPage = data.isExistPrevPage();
157             final long startRecordNumber = data.getCurrentStartRecordNumber();
158             final long endRecordNumber = data.getCurrentEndRecordNumber();
159             final List<String> pageNumbers = data.getPageNumberList();
160             final boolean partial = data.isPartialResults();
161             final String searchQuery = data.getSearchQuery();
162             final long requestedTime = data.getRequestedTime();
163 
164             buf.append("\"q\":");
165             buf.append(escapeJson(query));
166             buf.append(",\"query_id\":");
167             buf.append(escapeJson(queryId));
168             buf.append(",\"exec_time\":");
169             buf.append(execTime);
170             buf.append(",\"query_time\":");
171             buf.append(queryTime);
172             buf.append(',');
173             buf.append("\"page_size\":");
174             buf.append(pageSize);
175             buf.append(',');
176             buf.append("\"page_number\":");
177             buf.append(currentPageNumber);
178             buf.append(',');
179             buf.append("\"record_count\":");
180             buf.append(allRecordCount);
181             buf.append(',');
182             buf.append("\"page_count\":");
183             buf.append(allPageCount);
184             buf.append(",\"highlight_params\":");
185             buf.append(escapeJson(highlightParams));
186             buf.append(",\"next_page\":");
187             buf.append(escapeJson(nextPage));
188             buf.append(",\"prev_page\":");
189             buf.append(escapeJson(prevPage));
190             buf.append(",\"start_record_number\":");
191             buf.append(startRecordNumber);
192             buf.append(",\"end_record_number\":");
193             buf.append(escapeJson(endRecordNumber));
194             buf.append(",\"page_numbers\":");
195             buf.append(escapeJson(pageNumbers));
196             buf.append(",\"partial\":");
197             buf.append(escapeJson(partial));
198             buf.append(",\"search_query\":");
199             buf.append(escapeJson(searchQuery));
200             buf.append(",\"requested_time\":");
201             buf.append(requestedTime);
202             final String[] relatedQueries = relatedQueryHelper.getRelatedQueries(params.getQuery());
203             if (relatedQueries.length > 0) {
204                 buf.append(",\"related_query\":");
205                 buf.append(escapeJson(relatedQueries));
206             }
207             final String relatedContent = relatedContentHelper.getRelatedContent(params.getQuery());
208             if (StringUtil.isNotBlank(relatedContent)) {
209                 buf.append(",\"related_content\":");
210                 buf.append(escapeJson(relatedContent));
211             }
212             if (!documentItems.isEmpty()) {
213                 buf.append(',');
214                 buf.append("\"result\":[");
215                 boolean first1 = true;
216                 for (final Map<String, Object> document : documentItems) {
217                     if (!first1) {
218                         buf.append(',');
219                     } else {
220                         first1 = false;
221                     }
222                     buf.append('{');
223                     boolean first2 = true;
224                     for (final Map.Entry<String, Object> entry : document.entrySet()) {
225                         final String name = entry.getKey();
226                         if (StringUtil.isNotBlank(name) && entry.getValue() != null
227                                 && ComponentUtil.getQueryHelper().isApiResponseField(name)) {
228                             if (!first2) {
229                                 buf.append(',');
230                             } else {
231                                 first2 = false;
232                             }
233                             buf.append(escapeJson(name));
234                             buf.append(':');
235                             buf.append(escapeJson(entry.getValue()));
236                         }
237                     }
238                     buf.append('}');
239                 }
240                 buf.append(']');
241             }
242             if (facetResponse != null && facetResponse.hasFacetResponse()) {
243                 // facet field
244                 if (facetResponse.getFieldList() != null) {
245                     buf.append(',');
246                     buf.append("\"facet_field\":[");
247                     boolean first1 = true;
248                     for (final Field field : facetResponse.getFieldList()) {
249                         if (!first1) {
250                             buf.append(',');
251                         } else {
252                             first1 = false;
253                         }
254                         buf.append("{\"name\":");
255                         buf.append(escapeJson(field.getName()));
256                         buf.append(",\"result\":[");
257                         boolean first2 = true;
258                         for (final Map.Entry<String, Long> entry : field.getValueCountMap().entrySet()) {
259                             if (!first2) {
260                                 buf.append(',');
261                             } else {
262                                 first2 = false;
263                             }
264                             buf.append("{\"value\":");
265                             buf.append(escapeJson(entry.getKey()));
266                             buf.append(",\"count\":");
267                             buf.append(entry.getValue());
268                             buf.append('}');
269                         }
270                         buf.append(']');
271                         buf.append('}');
272                     }
273                     buf.append(']');
274                 }
275                 // facet q
276                 if (facetResponse.getQueryCountMap() != null) {
277                     buf.append(',');
278                     buf.append("\"facet_query\":[");
279                     boolean first1 = true;
280                     for (final Map.Entry<String, Long> entry : facetResponse.getQueryCountMap().entrySet()) {
281                         if (!first1) {
282                             buf.append(',');
283                         } else {
284                             first1 = false;
285                         }
286                         buf.append("{\"value\":");
287                         buf.append(escapeJson(entry.getKey()));
288                         buf.append(",\"count\":");
289                         buf.append(entry.getValue());
290                         buf.append('}');
291                     }
292                     buf.append(']');
293                 }
294             }
295         } catch (final Exception e) {
296             status = 1;
297             err = e;
298             if (logger.isDebugEnabled()) {
299                 logger.debug("Failed to process a search request.", e);
300             }
301         }
302 
303         writeJsonResponse(status, buf.toString(), err);
304 
305     }
306 
307     protected String toGeoRequestString(final GeoInfo geoInfo) {
308         try {
309             final XContentBuilder builder = XContentFactory.jsonBuilder();
310             return geoInfo.toQueryBuilder().toXContent(builder, ToXContent.EMPTY_PARAMS).string();
311         } catch (final Exception e) {
312             return "{\"error\":\"" + detailedMessage(e) + "\"}";
313         }
314     }
315 
316     protected String detailedMessage(final Throwable t) {
317         if (t == null) {
318             return "Unknown";
319         }
320         Throwable target = t;
321         if (target.getCause() != null) {
322             final StringBuilder sb = new StringBuilder();
323             while (target != null) {
324                 sb.append(target.getClass().getSimpleName());
325                 if (target.getMessage() != null) {
326                     sb.append("[");
327                     sb.append(target.getMessage());
328                     sb.append("]");
329                 }
330                 sb.append("; ");
331                 target = target.getCause();
332                 if (target != null) {
333                     sb.append("nested: ");
334                 }
335             }
336             return sb.toString();
337         } else {
338             return target.getClass().getSimpleName() + "[" + target.getMessage() + "]";
339         }
340     }
341 
342     protected void processLabelRequest(final HttpServletRequest request, final HttpServletResponse response, final FilterChain chain) {
343         final LabelTypeHelper labelTypeHelper = ComponentUtil.getLabelTypeHelper();
344 
345         int status = 0;
346         Exception err = null;
347         final StringBuilder buf = new StringBuilder(255); // TODO replace response stream
348         try {
349             final List<Map<String, String>> labelTypeItems = labelTypeHelper.getLabelTypeItemList(SearchRequestType.JSON);
350             buf.append("\"record_count\":");
351             buf.append(labelTypeItems.size());
352             if (!labelTypeItems.isEmpty()) {
353                 buf.append(',');
354                 buf.append("\"result\":[");
355                 boolean first1 = true;
356                 for (final Map<String, String> labelMap : labelTypeItems) {
357                     if (!first1) {
358                         buf.append(',');
359                     } else {
360                         first1 = false;
361                     }
362                     buf.append("{\"label\":");
363                     buf.append(escapeJson(labelMap.get(Constants.ITEM_LABEL)));
364                     buf.append(", \"value\":");
365                     buf.append(escapeJson(labelMap.get(Constants.ITEM_VALUE)));
366                     buf.append('}');
367                 }
368                 buf.append(']');
369             }
370         } catch (final Exception e) {
371             status = 1;
372             err = e;
373             if (logger.isDebugEnabled()) {
374                 logger.debug("Failed to process a label request.", e);
375             }
376         }
377 
378         writeJsonResponse(status, buf.toString(), err);
379 
380     }
381 
382     protected void processPopularWordRequest(final HttpServletRequest request, final HttpServletResponse response, final FilterChain chain) {
383         if (!ComponentUtil.getFessConfig().isWebApiPopularWord()) {
384             writeJsonResponse(9, null, "Unsupported operation.");
385             return;
386         }
387 
388         final String seed = request.getParameter("seed");
389         final List<String> tagList = new ArrayList<>();
390         final String[] tags = request.getParameterValues("labels");
391         if (tags != null) {
392             tagList.addAll(Arrays.asList(tags));
393         }
394         final String key = ComponentUtil.getFessConfig().getVirtualHostKey();
395         if (StringUtil.isNotBlank(key)) {
396             tagList.add(key);
397         }
398         final String[] fields = request.getParameterValues("fields");
399         final String[] excludes = StringUtil.EMPTY_STRINGS;// TODO
400 
401         final PopularWordHelper popularWordHelper = ComponentUtil.getPopularWordHelper();
402 
403         int status = 0;
404         Exception err = null;
405         final StringBuilder buf = new StringBuilder(255); // TODO replace response stream
406         try {
407             final List<String> popularWordList =
408                     popularWordHelper.getWordList(SearchRequestType.JSON, seed, tagList.toArray(new String[tagList.size()]), null, fields,
409                             excludes);
410 
411             buf.append("\"result\":[");
412             boolean first1 = true;
413             for (final String word : popularWordList) {
414                 if (!first1) {
415                     buf.append(',');
416                 } else {
417                     first1 = false;
418                 }
419                 buf.append(escapeJson(word));
420             }
421             buf.append(']');
422         } catch (final Exception e) {
423             if (e instanceof WebApiException) {
424                 status = ((WebApiException) e).getStatusCode();
425             } else {
426                 status = 1;
427             }
428             err = e;
429             if (logger.isDebugEnabled()) {
430                 logger.debug("Failed to process a popularWord request.", e);
431             }
432         }
433 
434         writeJsonResponse(status, buf.toString(), err);
435 
436     }
437 
438     protected void processFavoriteRequest(final HttpServletRequest request, final HttpServletResponse response, final FilterChain chain) {
439         if (!ComponentUtil.getFessConfig().isUserFavorite()) {
440             writeJsonResponse(9, null, "Unsupported operation.");
441             return;
442         }
443 
444         final FessConfig fessConfig = ComponentUtil.getFessConfig();
445         final UserInfoHelper userInfoHelper = ComponentUtil.getUserInfoHelper();
446         final SearchService searchService = ComponentUtil.getComponent(SearchService.class);
447         final FavoriteLogService favoriteLogService = ComponentUtil.getComponent(FavoriteLogService.class);
448         final SystemHelper systemHelper = ComponentUtil.getSystemHelper();
449 
450         try {
451             final String docId = request.getParameter("docId");
452             final String queryId = request.getParameter("queryId");
453 
454             final String[] docIds = userInfoHelper.getResultDocIds(URLDecoder.decode(queryId, Constants.UTF_8));
455             if (docIds == null) {
456                 throw new WebApiException(6, "No searched urls.");
457             }
458 
459             searchService.getDocumentByDocId(docId, new String[] { fessConfig.getIndexFieldUrl() }, OptionalThing.empty())
460                     .ifPresent(doc -> {
461                         final String favoriteUrl = DocumentUtil.getValue(doc, fessConfig.getIndexFieldUrl(), String.class);
462                         final String userCode = userInfoHelper.getUserCode();
463 
464                         if (StringUtil.isBlank(userCode)) {
465                             throw new WebApiException(2, "No user session.");
466                         } else if (StringUtil.isBlank(favoriteUrl)) {
467                             throw new WebApiException(2, "URL is null.");
468                         }
469 
470                         boolean found = false;
471                         for (final String id : docIds) {
472                             if (docId.equals(id)) {
473                                 found = true;
474                                 break;
475                             }
476                         }
477                         if (!found) {
478                             throw new WebApiException(5, "Not found: " + favoriteUrl);
479                         }
480 
481                         if (!favoriteLogService.addUrl(userCode, (userInfo, favoriteLog) -> {
482                             favoriteLog.setUserInfoId(userInfo.getId());
483                             favoriteLog.setUrl(favoriteUrl);
484                             favoriteLog.setDocId(docId);
485                             favoriteLog.setQueryId(queryId);
486                             favoriteLog.setCreatedAt(systemHelper.getCurrentTimeAsLocalDateTime());
487                         })) {
488                             throw new WebApiException(4, "Failed to add url: " + favoriteUrl);
489                         }
490 
491                         final String id = DocumentUtil.getValue(doc, fessConfig.getIndexFieldId(), String.class);
492                         searchService.update(id, builder -> {
493                             final Script script = new Script("ctx._source." + fessConfig.getIndexFieldFavoriteCount() + "+=1");
494                             builder.setScript(script);
495                             final Map<String, Object> upsertMap = new HashMap<>();
496                             upsertMap.put(fessConfig.getIndexFieldFavoriteCount(), 1);
497                             builder.setUpsert(upsertMap);
498                             builder.setRefreshPolicy(Constants.TRUE);
499                         });
500 
501                         writeJsonResponse(0, "\"result\":\"ok\"", (String) null);
502 
503                     }).orElse(() -> {
504                         throw new WebApiException(6, "Not found: " + docId);
505                     });
506 
507         } catch (final Exception e) {
508             int status;
509             if (e instanceof WebApiException) {
510                 status = ((WebApiException) e).getStatusCode();
511             } else {
512                 status = 1;
513             }
514             writeJsonResponse(status, null, e);
515             if (logger.isDebugEnabled()) {
516                 logger.debug("Failed to process a favorite request.", e);
517             }
518         }
519 
520     }
521 
522     protected void processFavoritesRequest(final HttpServletRequest request, final HttpServletResponse response, final FilterChain chain) {
523         if (!ComponentUtil.getFessConfig().isUserFavorite()) {
524             writeJsonResponse(9, null, "Unsupported operation.");
525             return;
526         }
527 
528         final UserInfoHelper userInfoHelper = ComponentUtil.getUserInfoHelper();
529         final FessConfig fessConfig = ComponentUtil.getFessConfig();
530         final SearchService searchService = ComponentUtil.getComponent(SearchService.class);
531         final FavoriteLogService favoriteLogService = ComponentUtil.getComponent(FavoriteLogService.class);
532 
533         int status = 0;
534         String body = null;
535         Exception err = null;
536 
537         try {
538             final String queryId = request.getParameter("queryId");
539             final String userCode = userInfoHelper.getUserCode();
540 
541             if (StringUtil.isBlank(userCode)) {
542                 throw new WebApiException(2, "No user session.");
543             } else if (StringUtil.isBlank(queryId)) {
544                 throw new WebApiException(3, "Query ID is null.");
545             }
546 
547             final String[] docIds = userInfoHelper.getResultDocIds(queryId);
548             final List<Map<String, Object>> docList =
549                     searchService.getDocumentListByDocIds(
550                             docIds,
551                             new String[] { fessConfig.getIndexFieldUrl(), fessConfig.getIndexFieldDocId(),
552                                     fessConfig.getIndexFieldFavoriteCount() }, OptionalThing.empty(), SearchRequestType.JSON);
553             List<String> urlList = new ArrayList<>(docList.size());
554             for (final Map<String, Object> doc : docList) {
555                 final String urlObj = DocumentUtil.getValue(doc, fessConfig.getIndexFieldUrl(), String.class);
556                 if (urlObj != null) {
557                     urlList.add(urlObj);
558                 }
559             }
560             urlList = favoriteLogService.getUrlList(userCode, urlList);
561             final List<String> docIdList = new ArrayList<>(urlList.size());
562             for (final Map<String, Object> doc : docList) {
563                 final String urlObj = DocumentUtil.getValue(doc, fessConfig.getIndexFieldUrl(), String.class);
564                 if (urlObj != null && urlList.contains(urlObj)) {
565                     final String docIdObj = DocumentUtil.getValue(doc, fessConfig.getIndexFieldDocId(), String.class);
566                     if (docIdObj != null) {
567                         docIdList.add(docIdObj);
568                     }
569                 }
570             }
571 
572             final StringBuilder buf = new StringBuilder(255); // TODO replace response stream
573             buf.append("\"num\":").append(docIdList.size());
574             if (!docIdList.isEmpty()) {
575                 buf.append(", \"doc_ids\":[");
576                 for (int i = 0; i < docIdList.size(); i++) {
577                     if (i > 0) {
578                         buf.append(',');
579                     }
580                     buf.append(escapeJson(docIdList.get(i)));
581                 }
582                 buf.append(']');
583             }
584             body = buf.toString();
585         } catch (final Exception e) {
586             if (e instanceof WebApiException) {
587                 status = ((WebApiException) e).getStatusCode();
588             } else {
589                 status = 1;
590             }
591 
592             err = e;
593             if (logger.isDebugEnabled()) {
594                 logger.debug("Failed to process a favorites request.", e);
595             }
596         }
597 
598         writeJsonResponse(status, body, err);
599 
600     }
601 
602     protected static class JsonRequestParams implements SearchRequestParams {
603 
604         private final HttpServletRequest request;
605 
606         private final FessConfig fessConfig;
607 
608         private int startPosition = -1;
609 
610         private int pageSize = -1;
611 
612         protected JsonRequestParams(final HttpServletRequest request, final FessConfig fessConfig) {
613             this.request = request;
614             this.fessConfig = fessConfig;
615         }
616 
617         @Override
618         public String getQuery() {
619             return request.getParameter("q");
620         }
621 
622         @Override
623         public String[] getExtraQueries() {
624             return getParamValueArray(request, "ex_q");
625         }
626 
627         @Override
628         public Map<String, String[]> getFields() {
629             final Map<String, String[]> fields = new HashMap<>();
630             for (final Map.Entry<String, String[]> entry : request.getParameterMap().entrySet()) {
631                 final String key = entry.getKey();
632                 if (key.startsWith("fields.")) {
633                     final String[] value = simplifyArray(entry.getValue());
634                     fields.put(key.substring("fields.".length()), value);
635                 }
636             }
637             return fields;
638         }
639 
640         @Override
641         public String[] getLanguages() {
642             return getParamValueArray(request, "lang");
643         }
644 
645         @Override
646         public GeoInfo getGeoInfo() {
647             return createGeoInfo(request);
648         }
649 
650         @Override
651         public FacetInfo getFacetInfo() {
652             return createFacetInfo(request);
653         }
654 
655         @Override
656         public String getSort() {
657             return request.getParameter("sort");
658         }
659 
660         @Override
661         public int getStartPosition() {
662             if (startPosition != -1) {
663                 return startPosition;
664             }
665 
666             final String start = request.getParameter("start");
667             if (StringUtil.isBlank(start)) {
668                 startPosition = fessConfig.getPagingSearchPageStartAsInteger();
669             } else {
670                 try {
671                     startPosition = Integer.parseInt(start);
672                 } catch (final NumberFormatException e) {
673                     startPosition = fessConfig.getPagingSearchPageStartAsInteger();
674                 }
675             }
676             return startPosition;
677         }
678 
679         @Override
680         public int getPageSize() {
681             if (pageSize != -1) {
682                 return pageSize;
683             }
684 
685             final String num = request.getParameter("num");
686             if (StringUtil.isBlank(num)) {
687                 pageSize = fessConfig.getPagingSearchPageSizeAsInteger();
688             } else {
689                 try {
690                     pageSize = Integer.parseInt(num);
691                     if (pageSize > fessConfig.getPagingSearchPageMaxSizeAsInteger().intValue() || pageSize <= 0) {
692                         pageSize = fessConfig.getPagingSearchPageMaxSizeAsInteger();
693                     }
694                 } catch (final NumberFormatException e) {
695                     pageSize = fessConfig.getPagingSearchPageSizeAsInteger();
696                 }
697             }
698             return pageSize;
699         }
700 
701         @Override
702         public Object getAttribute(final String name) {
703             return request.getAttribute(name);
704         }
705 
706         @Override
707         public Locale getLocale() {
708             return Locale.ROOT;
709         }
710 
711         @Override
712         public SearchRequestType getType() {
713             return SearchRequestType.JSON;
714         }
715 
716         @Override
717         public String getSimilarDocHash() {
718             return request.getParameter("sdh");
719         }
720 
721     }
722 }