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