diff --git a/src/main/config/openapi/openapi-user.yaml b/src/main/config/openapi/openapi-user.yaml
index d92a5fd45..dbe61ca1b 100644
--- a/src/main/config/openapi/openapi-user.yaml
+++ b/src/main/config/openapi/openapi-user.yaml
@@ -6,12 +6,12 @@ info:
license:
name: Apache 2.0
url: http://www.apache.org/licenses/LICENSE-2.0.html
- version: 1.0.11
+ version: 14.8.0
externalDocs:
description: API Documentation for Fess
url: https://fess.codelibs.org/14.8/api/
servers:
- - url: http://localhost:8080
+ - url: http://localhost:8080/api/v1
tags:
- name: search
description: Search operations
@@ -22,7 +22,7 @@ tags:
- name: suggest
description: Suggest operations
paths:
- /json/search:
+ /documents:
get:
tags:
- search
@@ -77,12 +77,6 @@ paths:
items:
type: string
example: [name]
- - name: callback
- in: query
- description: Callback name for using JSONP
- required: false
- schema:
- type: string
- name: facet.field
in: query
description: Facet field name
@@ -153,156 +147,157 @@ paths:
schema:
type: string
example: abc
+ - name: callback
+ in: query
+ description: Callback name for using JSONP
+ required: false
+ schema:
+ type: string
responses:
'200':
- description: successful operation
+ description: Successful operation
content:
application/json:
schema:
type: object
properties:
- response:
- type: object
- properties:
- version:
- type: string
- example: "14.8"
- status:
- type: integer
- example: 0
- q:
- type: string
- example: Fess
- query_id:
- type: string
- example: bd60f9579a494dfd8c03db7c8aa905b0
- exec_time:
- type: number
- example: 0.21
- query_time:
- type: integer
- format: int64
- example: 0
- page_size:
- type: integer
- example: 20
- page_number:
- type: integer
- example: 1
- record_count:
- type: integer
- format: int64
- example: 31625
- page_count:
- type: integer
- example: 1
- highlight_params:
- type: string
- example: "&hq=n2sm&hq=Fess"
- next_page:
- type: boolean
- example: true
- prev_page:
- type: boolean
- example: false
- start_record_number:
- type: integer
- example: 1
- end_record_number:
- type: integer
- example: 20
- page_numbers:
- type: array
- items:
+ q:
+ type: string
+ example: Fess
+ query_id:
+ type: string
+ example: bd60f9579a494dfd8c03db7c8aa905b0
+ exec_time:
+ type: number
+ example: 0.21
+ query_time:
+ type: integer
+ format: int64
+ example: 0
+ page_size:
+ type: integer
+ example: 20
+ page_number:
+ type: integer
+ example: 1
+ record_count:
+ type: integer
+ format: int64
+ example: 31625
+ page_count:
+ type: integer
+ example: 1
+ highlight_params:
+ type: string
+ example: "&hq=n2sm&hq=Fess"
+ next_page:
+ type: boolean
+ example: true
+ prev_page:
+ type: boolean
+ example: false
+ start_record_number:
+ type: integer
+ example: 1
+ end_record_number:
+ type: integer
+ example: 20
+ page_numbers:
+ type: array
+ items:
+ type: string
+ example: ["1", "2", "3", "4", "5"]
+ partial:
+ type: boolean
+ example: false
+ search_query:
+ type: string
+ example: "(Fess OR n2sm)"
+ requested_time:
+ type: integer
+ format: int64
+ example: 1507822131845
+ related_query:
+ type: array
+ items:
+ type: string
+ example: ["aaa"]
+ related_contents:
+ type: array
+ items:
+ type: string
+ example: []
+ result:
+ type: array
+ items:
+ type: object
+ properties:
+ filetype:
type: string
- example: ["1", "2", "3", "4", "5"]
- partial:
- type: boolean
- example: false
- search_query:
- type: string
- example: "(Fess OR n2sm)"
- requested_time:
- type: integer
- format: int64
- example: 1507822131845
- related_query:
- type: array
- items:
+ example: html
+ title:
type: string
- example: ["aaa"]
- related_contents:
- type: array
- items:
+ example: "Open Source Enterprise Search Server: Fess — Fess 11.0 documentation"
+ content_title:
type: string
- example: []
- result:
- type: array
- items:
- type: object
- properties:
- filetype:
- type: string
- example: html
- title:
- type: string
- example: "Open Source Enterprise Search Server: Fess — Fess 11.0 documentation"
- content_title:
- type: string
- example: "Open Source Enterprise Search Server: Fess — Fe..."
- digest:
- type: string
- example: "Docs » Open Source Enterprise Search Server: Fess Commercial Support Open Source Enterprise Search Server: Fess What is Fess ? Fess is very powerful and easily deployable Enterprise Search Server. ..."
- host:
- type: string
- format: hostname
- example: "fess.codelibs.org"
- last_modified:
- type: string
- format: date-time
- example: "2017-10-09T22:28:56.000Z"
- content_length:
- type: string
- example: "29624"
- timestamp:
- type: string
- format: date-time
- example: "2017-10-09T22:28:56.000Z"
- url_link:
- type: string
- example: "https://fess.codelibs.org/"
- created:
- type: string
- format: date-time
- example: "2017-10-10T15:00:48.609Z"
- site_path:
- type: string
- example: "fess.codelibs.org/"
- doc_id:
- type: string
- example: "e79fbfdfb09d4bffb58ec230c68f6f7e"
- url:
- type: string
- format: uri
- example: "https://fess.codelibs.org/"
- content_description:
- type: string
- example: "Enterprise Search Server: Fess Commercial Support Open...Search Server: Fess What is Fess ? Fess is very powerful...You can install and run Fess quickly on any platforms...Java runtime environment. Fess is provided under Apache...Apache license. Demo Fess is Elasticsearch-based search"
- site:
- type: string
- example: "fess.codelibs.org/"
- boost:
- type: string
- example: "10.0"
- mimetype:
- type: string
- example: "text/html"
+ example: "Open Source Enterprise Search Server: Fess — Fe..."
+ digest:
+ type: string
+ example: "Docs » Open Source Enterprise Search Server: Fess Commercial Support Open Source Enterprise Search Server: Fess What is Fess ? Fess is very powerful and easily deployable Enterprise Search Server. ..."
+ host:
+ type: string
+ format: hostname
+ example: "fess.codelibs.org"
+ last_modified:
+ type: string
+ format: date-time
+ example: "2017-10-09T22:28:56.000Z"
+ content_length:
+ type: string
+ example: "29624"
+ timestamp:
+ type: string
+ format: date-time
+ example: "2017-10-09T22:28:56.000Z"
+ url_link:
+ type: string
+ example: "https://fess.codelibs.org/"
+ created:
+ type: string
+ format: date-time
+ example: "2017-10-10T15:00:48.609Z"
+ site_path:
+ type: string
+ example: "fess.codelibs.org/"
+ doc_id:
+ type: string
+ example: "e79fbfdfb09d4bffb58ec230c68f6f7e"
+ url:
+ type: string
+ format: uri
+ example: "https://fess.codelibs.org/"
+ content_description:
+ type: string
+ example: "Enterprise Search Server: Fess Commercial Support Open...Search Server: Fess What is Fess ? Fess is very powerful...You can install and run Fess quickly on any platforms...Java runtime environment. Fess is provided under Apache...Apache license. Demo Fess is Elasticsearch-based search"
+ site:
+ type: string
+ example: "fess.codelibs.org/"
+ boost:
+ type: string
+ example: "10.0"
+ mimetype:
+ type: string
+ example: "text/html"
'400':
$ref: '#/components/responses/BadRequest'
'401':
$ref: '#/components/responses/Unauthorized'
+ '404':
+ $ref: '#/components/responses/NotFound'
+ '500':
+ $ref: '#/components/responses/InternalServerError'
- /json/label:
+ /labels:
get:
tags:
- search
@@ -311,41 +306,36 @@ paths:
operationId: listLabels
responses:
'200':
- description: successful operation
+ description: Successful operation
content:
application/json:
schema:
type: object
properties:
- response:
- type: object
- properties:
- version:
- type: string
- example: "14.8"
- status:
- type: integer
- example: 0
- record_count:
- type: integer
- example: 9
- result:
- type: array
- items:
- type: object
- properties:
- label:
- type: string
- example: AWS
- value:
- type: string
- example: aws
+ record_count:
+ type: integer
+ example: 9
+ data:
+ type: array
+ items:
+ type: object
+ properties:
+ label:
+ type: string
+ example: AWS
+ value:
+ type: string
+ example: aws
'400':
$ref: '#/components/responses/BadRequest'
'401':
$ref: '#/components/responses/Unauthorized'
+ '404':
+ $ref: '#/components/responses/NotFound'
+ '500':
+ $ref: '#/components/responses/InternalServerError'
- /json/popularword:
+ /popular-words:
get:
tags:
- popularword
@@ -360,9 +350,9 @@ paths:
schema:
type: string
example: 123
- - name: labels
+ - name: label
in: query
- description: Labels to fileter results
+ description: Filtered label name
required: false
style: form
explode: true
@@ -371,9 +361,9 @@ paths:
items:
type: string
example: [label]
- - name: fields
+ - name: field
in: query
- description: Fields to find results
+ description: Field name to generate suggest words
required: false
style: form
explode: true
@@ -381,38 +371,33 @@ paths:
type: array
items:
type: string
- example: [field]
+ example: [python]
responses:
'200':
- description: successful operation
+ description: Successful operation
content:
application/json:
schema:
type: object
properties:
- response:
- type: object
- properties:
- version:
- type: string
- example: "14.8"
- status:
- type: integer
- example: 0
- record_count:
- type: integer
- example: 9
- result:
- type: array
- items:
- type: string
- example: ["test"]
+ record_count:
+ type: integer
+ example: 9
+ data:
+ type: array
+ items:
+ type: string
+ example: ["python"]
'400':
$ref: '#/components/responses/BadRequest'
'401':
$ref: '#/components/responses/Unauthorized'
+ '404':
+ $ref: '#/components/responses/NotFound'
+ '500':
+ $ref: '#/components/responses/InternalServerError'
- /json/ping:
+ /health:
get:
tags:
- monitor
@@ -421,36 +406,47 @@ paths:
operationId: ping
responses:
'200':
- description: successful operation
+ description: Successful operation
content:
application/json:
schema:
type: object
properties:
- response:
+ data:
type: object
properties:
- version:
- type: string
- example: "14.8"
status:
- type: integer
- example: 0
- message:
- type: object
- properties:
- status:
- type: string
- example: green
- timed_out:
- type: boolean
- example: false
+ type: string
+ example: green
+ timed_out:
+ type: boolean
+ example: false
'400':
$ref: '#/components/responses/BadRequest'
'401':
$ref: '#/components/responses/Unauthorized'
+ '404':
+ $ref: '#/components/responses/NotFound'
+ '500':
+ $ref: '#/components/responses/InternalServerError'
+ '503':
+ description: Service unavailable
+ content:
+ application/json:
+ schema:
+ type: object
+ properties:
+ data:
+ type: object
+ properties:
+ status:
+ type: string
+ example: green
+ timed_out:
+ type: boolean
+ example: false
- /suggest:
+ /suggest-words:
get:
tags:
- suggest
@@ -458,7 +454,7 @@ paths:
description: Returns words for suggest
operationId: findSuggestWords
parameters:
- - name: query
+ - name: q
in: query
description: Inputting characters for search
required: true
@@ -475,70 +471,78 @@ paths:
exclusiveMinimum: false
example: 10
default: 10
- - name: tags
+ - name: label
in: query
- description: Tags to filter results
+ description: Filtered label name
required: false
style: form
- explode: false
+ explode: true
schema:
- type: string
- example: "tag1,tag2"
- - name: fields
+ type: array
+ items:
+ type: string
+ example: [label]
+ - name: field
in: query
- description: Fields to find results
+ description: Field name to generate suggest words
required: false
style: form
- explode: false
+ explode: true
schema:
- type: string
- example: "field1,field2"
+ type: array
+ items:
+ type: string
+ example: [python]
+ - name: lang
+ in: query
+ description: Target language
+ required: false
+ style: form
+ explode: true
+ schema:
+ type: array
+ items:
+ type: string
+ example: [en]
responses:
'200':
- description: successful operation
+ description: Successful operation
content:
application/json:
schema:
type: object
properties:
- response:
- type: object
- properties:
- version:
- type: string
- example: "14.8"
- status:
- type: integer
- example: 0
- result:
- type: object
- properties:
- took:
+ took:
+ type: integer
+ format: int64
+ example: 18
+ total:
+ type: integer
+ example: 355
+ num:
+ type: integer
+ example: 10
+ data:
+ type: array
+ items:
+ type: object
+ properties:
+ text:
+ type: string
+ example: fess
+ field:
+ type: array
+ items:
type: string
- example: 18
- total:
- type: string
- example: 355
- num:
- type: string
- example: 10
- hits:
- type: array
- items:
- type: object
- properties:
- text:
- type: string
- example: fess
- tags:
- type: array
- items:
- type: string
- example: [java, python]
+ example: [java, python]
'400':
$ref: '#/components/responses/BadRequest'
'401':
$ref: '#/components/responses/Unauthorized'
+ '404':
+ $ref: '#/components/responses/NotFound'
+ '500':
+ $ref: '#/components/responses/InternalServerError'
components:
responses:
@@ -554,20 +558,26 @@ components:
application/json:
schema:
$ref: '#/components/schemas/Error'
+ NotFound:
+ description: Page not found
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/Error'
+ InternalServerError:
+ description: Internal server error
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/Error'
schemas:
Error:
type: object
properties:
- response:
- type: object
- properties:
- version:
- type: string
- example: "14.8"
- status:
- type: integer
- example: 1
- message:
- type: string
- example: "error_code:bd6b0bd6-0144-40bc-9567-c5751dd8eecb"
+ error_code:
+ type: string
+ example: "bd6b0bd6-0144-40bc-9567-c5751dd8eecb"
+ message:
+ type: string
+ example: "This is an error message."
diff --git a/src/main/java/org/codelibs/fess/api/BaseApiManager.java b/src/main/java/org/codelibs/fess/api/BaseApiManager.java
index 67b1c4ee7..4ac4e26b6 100644
--- a/src/main/java/org/codelibs/fess/api/BaseApiManager.java
+++ b/src/main/java/org/codelibs/fess/api/BaseApiManager.java
@@ -35,7 +35,7 @@ public abstract class BaseApiManager implements WebApiManager {
protected String pathPrefix;
protected enum FormatType {
- SEARCH, LABEL, POPULARWORD, FAVORITE, FAVORITES, OTHER, PING, SCROLL;
+ SEARCH, LABEL, POPULARWORD, FAVORITE, FAVORITES, PING, SCROLL, SUGGEST, OTHER;
}
public String getPathPrefix() {
@@ -51,6 +51,13 @@ public abstract class BaseApiManager implements WebApiManager {
if (formatType != null) {
return formatType;
}
+
+ formatType = detectFormatType(request);
+ request.setAttribute(API_FORMAT_TYPE, formatType);
+ return formatType;
+ }
+
+ protected FormatType detectFormatType(final HttpServletRequest request) {
String value = request.getParameter("type");
if (value == null) {
final String servletPath = request.getServletPath();
@@ -60,30 +67,30 @@ public abstract class BaseApiManager implements WebApiManager {
}
}
if (value == null) {
- formatType = FormatType.SEARCH;
+ return FormatType.SEARCH;
} else {
final String type = value.toUpperCase(Locale.ROOT);
if (FormatType.SEARCH.name().equals(type)) {
- formatType = FormatType.SEARCH;
+ return FormatType.SEARCH;
} else if (FormatType.LABEL.name().equals(type)) {
- formatType = FormatType.LABEL;
+ return FormatType.LABEL;
} else if (FormatType.POPULARWORD.name().equals(type)) {
- formatType = FormatType.POPULARWORD;
+ return FormatType.POPULARWORD;
} else if (FormatType.FAVORITE.name().equals(type)) {
- formatType = FormatType.FAVORITE;
+ return FormatType.FAVORITE;
} else if (FormatType.FAVORITES.name().equals(type)) {
- formatType = FormatType.FAVORITES;
+ return FormatType.FAVORITES;
} else if (FormatType.PING.name().equals(type)) {
- formatType = FormatType.PING;
+ return FormatType.PING;
} else if (FormatType.SCROLL.name().equals(type)) {
- formatType = FormatType.SCROLL;
+ return FormatType.SCROLL;
+ } else if (FormatType.SUGGEST.name().equals(type)) {
+ return FormatType.SUGGEST;
} else {
// default
- formatType = FormatType.OTHER;
+ return FormatType.OTHER;
}
}
- request.setAttribute(API_FORMAT_TYPE, formatType);
- return formatType;
}
protected void write(final String text, final String contentType, final String encoding) {
diff --git a/src/main/java/org/codelibs/fess/api/BaseJsonApiManager.java b/src/main/java/org/codelibs/fess/api/ClassicJsonApiManager.java
similarity index 97%
rename from src/main/java/org/codelibs/fess/api/BaseJsonApiManager.java
rename to src/main/java/org/codelibs/fess/api/ClassicJsonApiManager.java
index 9a02b5cf0..ca0a701d2 100644
--- a/src/main/java/org/codelibs/fess/api/BaseJsonApiManager.java
+++ b/src/main/java/org/codelibs/fess/api/ClassicJsonApiManager.java
@@ -39,9 +39,10 @@ import org.codelibs.fess.util.ComponentUtil;
import org.lastaflute.web.util.LaRequestUtil;
import org.lastaflute.web.util.LaResponseUtil;
-public abstract class BaseJsonApiManager extends BaseApiManager {
+@Deprecated
+public abstract class ClassicJsonApiManager extends BaseApiManager {
- private static final Logger logger = LogManager.getLogger(BaseJsonApiManager.class);
+ private static final Logger logger = LogManager.getLogger(ClassicJsonApiManager.class);
protected String mimeType = "application/json";
diff --git a/src/main/java/org/codelibs/fess/api/json/JsonApiManager.java b/src/main/java/org/codelibs/fess/api/json/JsonApiManager.java
index d7d903fdd..ac358041d 100644
--- a/src/main/java/org/codelibs/fess/api/json/JsonApiManager.java
+++ b/src/main/java/org/codelibs/fess/api/json/JsonApiManager.java
@@ -35,7 +35,7 @@ import org.apache.logging.log4j.Logger;
import org.codelibs.core.exception.IORuntimeException;
import org.codelibs.core.lang.StringUtil;
import org.codelibs.fess.Constants;
-import org.codelibs.fess.api.BaseJsonApiManager;
+import org.codelibs.fess.api.ClassicJsonApiManager;
import org.codelibs.fess.app.service.FavoriteLogService;
import org.codelibs.fess.entity.FacetInfo;
import org.codelibs.fess.entity.GeoInfo;
@@ -61,7 +61,8 @@ import org.codelibs.fess.util.FacetResponse.Field;
import org.dbflute.optional.OptionalThing;
import org.opensearch.script.Script;
-public class JsonApiManager extends BaseJsonApiManager {
+@Deprecated
+public class JsonApiManager extends ClassicJsonApiManager {
private static final Logger logger = LogManager.getLogger(JsonApiManager.class);
diff --git a/src/main/java/org/codelibs/fess/api/json/SearchApiManager.java b/src/main/java/org/codelibs/fess/api/json/SearchApiManager.java
new file mode 100644
index 000000000..c73d9738a
--- /dev/null
+++ b/src/main/java/org/codelibs/fess/api/json/SearchApiManager.java
@@ -0,0 +1,1180 @@
+/*
+ * Copyright 2012-2023 CodeLibs Project and the Others.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the specific language
+ * governing permissions and limitations under the License.
+ */
+package org.codelibs.fess.api.json;
+
+import static org.codelibs.core.stream.StreamUtil.stream;
+
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.net.URLDecoder;
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+import java.util.UUID;
+import java.util.function.Supplier;
+
+import javax.annotation.PostConstruct;
+import javax.servlet.FilterChain;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.commons.lang3.ArrayUtils;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.commons.text.StringEscapeUtils;
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+import org.codelibs.core.CoreLibConstants;
+import org.codelibs.core.exception.IORuntimeException;
+import org.codelibs.core.lang.StringUtil;
+import org.codelibs.fess.Constants;
+import org.codelibs.fess.api.BaseApiManager;
+import org.codelibs.fess.app.service.FavoriteLogService;
+import org.codelibs.fess.entity.FacetInfo;
+import org.codelibs.fess.entity.GeoInfo;
+import org.codelibs.fess.entity.HighlightInfo;
+import org.codelibs.fess.entity.PingResponse;
+import org.codelibs.fess.entity.SearchRenderData;
+import org.codelibs.fess.entity.SearchRequestParams;
+import org.codelibs.fess.entity.SearchRequestParams.SearchRequestType;
+import org.codelibs.fess.es.client.SearchEngineClient;
+import org.codelibs.fess.exception.InvalidAccessTokenException;
+import org.codelibs.fess.exception.InvalidQueryException;
+import org.codelibs.fess.exception.ResultOffsetExceededException;
+import org.codelibs.fess.exception.WebApiException;
+import org.codelibs.fess.helper.LabelTypeHelper;
+import org.codelibs.fess.helper.PopularWordHelper;
+import org.codelibs.fess.helper.RelatedContentHelper;
+import org.codelibs.fess.helper.RelatedQueryHelper;
+import org.codelibs.fess.helper.RoleQueryHelper;
+import org.codelibs.fess.helper.SearchHelper;
+import org.codelibs.fess.helper.SuggestHelper;
+import org.codelibs.fess.helper.SystemHelper;
+import org.codelibs.fess.helper.UserInfoHelper;
+import org.codelibs.fess.mylasta.direction.FessConfig;
+import org.codelibs.fess.suggest.entity.SuggestItem;
+import org.codelibs.fess.suggest.request.suggest.SuggestRequestBuilder;
+import org.codelibs.fess.suggest.request.suggest.SuggestResponse;
+import org.codelibs.fess.util.ComponentUtil;
+import org.codelibs.fess.util.DocumentUtil;
+import org.codelibs.fess.util.FacetResponse;
+import org.codelibs.fess.util.FacetResponse.Field;
+import org.dbflute.optional.OptionalThing;
+import org.lastaflute.web.util.LaRequestUtil;
+import org.lastaflute.web.util.LaResponseUtil;
+import org.opensearch.script.Script;
+
+public class SearchApiManager extends BaseApiManager {
+
+ private static final Logger logger = LogManager.getLogger(SearchApiManager.class);
+
+ private static final String MESSAGE_FIELD = "message";
+
+ private static final String RESULT_FIELD = "result";
+
+ protected String mimeType = "application/json";
+
+ public SearchApiManager() {
+ setPathPrefix("/api/v1");
+ }
+
+ @PostConstruct
+ public void register() {
+ if (logger.isInfoEnabled()) {
+ logger.info("Load {}", this.getClass().getSimpleName());
+ }
+ ComponentUtil.getWebApiManagerFactory().add(this);
+ }
+
+ @Override
+ protected FormatType detectFormatType(final HttpServletRequest request) {
+ final String servletPath = request.getServletPath();
+ final String[] values = servletPath.replaceAll("/+", "/").split("/");
+ final String value = values.length > 3 ? values[3] : null;
+ if (value == null) {
+ return FormatType.SEARCH;
+ }
+ final String type = value.toLowerCase(Locale.ROOT);
+ if ("documents".equals(type)) {
+ // return FormatType.FAVORITE;
+ // return FormatType.SCROLL;
+ return FormatType.SEARCH;
+ } else if ("labels".equals(type)) {
+ return FormatType.LABEL;
+ } else if ("popular-words".equals(type)) {
+ return FormatType.POPULARWORD;
+ } else if ("favorites".equals(type)) {
+ return FormatType.FAVORITES;
+ } else if ("health".equals(type)) {
+ return FormatType.PING;
+ } else if ("suggest-words".equals(type)) {
+ return FormatType.SUGGEST;
+ } else {
+ // default
+ return FormatType.OTHER;
+ }
+ }
+
+ @Override
+ public boolean matches(final HttpServletRequest request) {
+ final FessConfig fessConfig = ComponentUtil.getFessConfig();
+ if (!fessConfig.isWebApiJson()) {
+ return false;
+ }
+
+ final String servletPath = request.getServletPath();
+ return servletPath.startsWith(pathPrefix);
+ }
+
+ @Override
+ public void process(final HttpServletRequest request, final HttpServletResponse response, final FilterChain chain)
+ throws IOException, ServletException {
+ switch (getFormatType(request)) {
+ case SEARCH:
+ processSearchRequest(request, response, chain);
+ break;
+ case LABEL:
+ processLabelRequest(request, response, chain);
+ break;
+ case POPULARWORD:
+ processPopularWordRequest(request, response, chain);
+ break;
+ case FAVORITE:
+ processFavoriteRequest(request, response, chain);
+ break;
+ case FAVORITES:
+ processFavoritesRequest(request, response, chain);
+ break;
+ case PING:
+ processPingRequest(request, response, chain);
+ break;
+ case SCROLL:
+ processScrollSearchRequest(request, response, chain);
+ break;
+ case SUGGEST:
+ processSuggestRequest(request, response, chain);
+ break;
+ default:
+ writeJsonResponse(HttpServletResponse.SC_NOT_FOUND, escapeJsonKeyValue(MESSAGE_FIELD, "Not found."));
+ break;
+ }
+ }
+
+ protected void processScrollSearchRequest(final HttpServletRequest request, final HttpServletResponse response,
+ final FilterChain chain) {
+ final SearchHelper searchHelper = ComponentUtil.getSearchHelper();
+ final FessConfig fessConfig = ComponentUtil.getFessConfig();
+
+ if (!fessConfig.isAcceptedSearchReferer(request.getHeader("referer"))) {
+ writeJsonResponse(HttpServletResponse.SC_BAD_REQUEST, escapeJsonKeyValue(MESSAGE_FIELD, "Referer is invalid."));
+ return;
+ }
+
+ if (!fessConfig.isApiSearchScroll()) {
+ writeJsonResponse(HttpServletResponse.SC_BAD_REQUEST, escapeJsonKeyValue(MESSAGE_FIELD, "Scroll Search is not available."));
+ return;
+ }
+
+ final StringBuilder buf = new StringBuilder(1000);
+ request.setAttribute(Constants.SEARCH_LOG_ACCESS_TYPE, Constants.SEARCH_LOG_ACCESS_TYPE_JSON);
+ final JsonRequestParams params = new JsonRequestParams(request, fessConfig);
+ try {
+ response.setContentType("application/x-ndjson; charset=UTF-8");
+ final long count = searchHelper.scrollSearch(params, doc -> {
+ buf.setLength(0);
+ buf.append('{');
+ boolean first2 = true;
+ for (final Map.Entry entry : doc.entrySet()) {
+ final String name = entry.getKey();
+ if (StringUtil.isNotBlank(name) && entry.getValue() != null) {
+ if (!first2) {
+ buf.append(',');
+ } else {
+ first2 = false;
+ }
+ buf.append(escapeJson(name));
+ buf.append(':');
+ buf.append(escapeJson(entry.getValue()));
+ }
+ }
+ buf.append('}');
+ buf.append('\n');
+ try {
+ response.getWriter().print(buf.toString());
+ } catch (final IOException e) {
+ throw new IORuntimeException(e);
+ }
+ return true;
+ }, OptionalThing.empty());
+ response.flushBuffer();
+ if (logger.isDebugEnabled()) {
+ logger.debug("Loaded {} docs", count);
+ }
+ } catch (final InvalidQueryException | ResultOffsetExceededException e) {
+ if (logger.isDebugEnabled()) {
+ logger.debug("Failed to process a search request.", e);
+ }
+ writeJsonResponse(HttpServletResponse.SC_BAD_REQUEST, e);
+ } catch (final Exception e) {
+ if (logger.isDebugEnabled()) {
+ logger.debug("Failed to process a search request.", e);
+ }
+ writeJsonResponse(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, e);
+ }
+
+ }
+
+ protected void processPingRequest(final HttpServletRequest request, final HttpServletResponse response, final FilterChain chain) {
+ final SearchEngineClient searchEngineClient = ComponentUtil.getSearchEngineClient();
+ try {
+ final PingResponse pingResponse = searchEngineClient.ping();
+ writeJsonResponse(pingResponse.getStatus() == 0 ? HttpServletResponse.SC_OK : HttpServletResponse.SC_SERVICE_UNAVAILABLE,
+ "\"data\":" + pingResponse.getMessage());
+ } catch (final Exception e) {
+ if (logger.isDebugEnabled()) {
+ logger.debug("Failed to process a ping request.", e);
+ }
+ writeJsonResponse(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, e);
+ }
+ }
+
+ protected void processSearchRequest(final HttpServletRequest request, final HttpServletResponse response, final FilterChain chain) {
+ final SearchHelper searchHelper = ComponentUtil.getSearchHelper();
+ final FessConfig fessConfig = ComponentUtil.getFessConfig();
+ final RelatedQueryHelper relatedQueryHelper = ComponentUtil.getRelatedQueryHelper();
+ final RelatedContentHelper relatedContentHelper = ComponentUtil.getRelatedContentHelper();
+
+ String query = null;
+ final StringBuilder buf = new StringBuilder(1000);
+ request.setAttribute(Constants.SEARCH_LOG_ACCESS_TYPE, Constants.SEARCH_LOG_ACCESS_TYPE_JSON);
+ try {
+ final SearchRenderData data = new SearchRenderData();
+ final JsonRequestParams params = new JsonRequestParams(request, fessConfig);
+ query = params.getQuery();
+ searchHelper.search(params, data, OptionalThing.empty());
+ final String execTime = data.getExecTime();
+ final String queryTime = Long.toString(data.getQueryTime());
+ final String pageSize = Integer.toString(data.getPageSize());
+ final String currentPageNumber = Integer.toString(data.getCurrentPageNumber());
+ final String allRecordCount = Long.toString(data.getAllRecordCount());
+ final String allRecordCountRelation = data.getAllRecordCountRelation();
+ final String allPageCount = Integer.toString(data.getAllPageCount());
+ final List