fix #2733 remove old search api

This commit is contained in:
Shinsuke Sugaya 2023-03-30 21:33:53 +09:00
parent 0a69c29545
commit c5510d7d99
4 changed files with 0 additions and 1325 deletions

View file

@ -1,193 +0,0 @@
/*
* 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;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.UUID;
import java.util.function.Supplier;
import javax.servlet.http.HttpServletResponse;
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.lang.StringUtil;
import org.codelibs.fess.Constants;
import org.codelibs.fess.exception.InvalidAccessTokenException;
import org.codelibs.fess.util.ComponentUtil;
import org.lastaflute.web.util.LaRequestUtil;
import org.lastaflute.web.util.LaResponseUtil;
@Deprecated
public abstract class ClassicJsonApiManager extends BaseApiManager {
private static final Logger logger = LogManager.getLogger(ClassicJsonApiManager.class);
protected String mimeType = "application/json";
protected void writeJsonResponse(final int status, final String body, final Throwable t) {
if (t == null) {
writeJsonResponse(status, body, (String) null);
return;
}
if (t instanceof final InvalidAccessTokenException e) {
final HttpServletResponse response = LaResponseUtil.getResponse();
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
response.setHeader("WWW-Authenticate", "Bearer error=\"" + e.getType() + "\"");
}
final Supplier<String> stacktraceString = () -> {
final StringBuilder sb = new StringBuilder();
if (StringUtil.isBlank(t.getMessage())) {
sb.append(t.getClass().getName());
} else {
sb.append(t.getMessage());
}
try (final StringWriter sw = new StringWriter(); final PrintWriter pw = new PrintWriter(sw)) {
t.printStackTrace(pw);
pw.flush();
sb.append(" [ ").append(sw.toString()).append(" ]");
} catch (final IOException ignore) {}
return sb.toString();
};
final String message;
if (Constants.TRUE.equalsIgnoreCase(ComponentUtil.getFessConfig().getApiJsonResponseExceptionIncluded())) {
message = stacktraceString.get();
} else {
final String errorCode = UUID.randomUUID().toString();
message = "error_code:" + errorCode;
if (logger.isDebugEnabled()) {
logger.debug("[{}] {}", errorCode, stacktraceString.get().replace("\n", "\\n"));
} else {
logger.warn("[{}] {}", errorCode, t.getMessage());
}
}
writeJsonResponse(status, body, message);
}
protected void writeJsonResponse(final int status, final String body, final String errMsg) {
String content = null;
if (status == 0) {
if (StringUtil.isNotBlank(body)) {
content = body;
}
} else {
content = "\"message\":" + escapeJson(errMsg);
}
writeJsonResponse(status, content);
}
protected void writeJsonResponse(final int status, final String body) {
final String callback = LaRequestUtil.getOptionalRequest().map(req -> req.getParameter("callback")).orElse(null);
final boolean isJsonp = ComponentUtil.getFessConfig().isApiJsonpEnabled() && StringUtil.isNotBlank(callback);
final StringBuilder buf = new StringBuilder(1000);
if (isJsonp) {
buf.append(escapeCallbackName(callback));
buf.append('(');
}
buf.append("{\"response\":");
buf.append("{\"version\":\"");
buf.append(ComponentUtil.getSystemHelper().getProductVersion());
buf.append("\",");
buf.append("\"status\":");
buf.append(status);
if (StringUtil.isNotBlank(body)) {
buf.append(',');
buf.append(body);
}
buf.append('}');
buf.append('}');
if (isJsonp) {
buf.append(')');
}
write(buf.toString(), mimeType, Constants.UTF_8);
}
protected String escapeCallbackName(final String callbackName) {
return "/**/" + callbackName.replaceAll("[^0-9a-zA-Z_\\$\\.]", StringUtil.EMPTY);
}
protected String escapeJson(final Object obj) {
if (obj == null) {
return "null";
}
final StringBuilder buf = new StringBuilder(255);
if (obj instanceof String[]) {
buf.append('[');
boolean first = true;
for (final Object child : (String[]) obj) {
if (first) {
first = false;
} else {
buf.append(',');
}
buf.append(escapeJson(child));
}
buf.append(']');
} else if (obj instanceof List<?>) {
buf.append('[');
boolean first = true;
for (final Object child : (List<?>) obj) {
if (first) {
first = false;
} else {
buf.append(',');
}
buf.append(escapeJson(child));
}
buf.append(']');
} else if (obj instanceof Map<?, ?>) {
buf.append('{');
boolean first = true;
for (final Map.Entry<?, ?> entry : ((Map<?, ?>) obj).entrySet()) {
if (first) {
first = false;
} else {
buf.append(',');
}
buf.append(escapeJson(entry.getKey())).append(':').append(escapeJson(entry.getValue()));
}
buf.append('}');
} else if ((obj instanceof Integer) || (obj instanceof Long) || (obj instanceof Float) || (obj instanceof Double)) {
buf.append((obj));
} else if (obj instanceof Boolean) {
buf.append(obj.toString());
} else if (obj instanceof Date) {
final SimpleDateFormat sdf = new SimpleDateFormat(CoreLibConstants.DATE_FORMAT_ISO_8601_EXTEND, Locale.ROOT);
buf.append('\"').append(StringEscapeUtils.escapeJson(sdf.format(obj))).append('\"');
} else {
buf.append('\"').append(StringEscapeUtils.escapeJson(obj.toString())).append('\"');
}
return buf.toString();
}
public void setMimeType(final String mimeType) {
this.mimeType = mimeType;
}
}

View file

@ -1,820 +0,0 @@
/*
* 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 java.io.IOException;
import java.net.URLDecoder;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
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.logging.log4j.LogManager;
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.ClassicJsonApiManager;
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.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.SearchHelper;
import org.codelibs.fess.helper.SystemHelper;
import org.codelibs.fess.helper.UserInfoHelper;
import org.codelibs.fess.mylasta.direction.FessConfig;
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.opensearch.script.Script;
@Deprecated
public class JsonApiManager extends ClassicJsonApiManager {
private static final Logger logger = LogManager.getLogger(JsonApiManager.class);
public JsonApiManager() {
setPathPrefix("/json");
}
@PostConstruct
public void register() {
if (logger.isInfoEnabled()) {
logger.info("Load {}", this.getClass().getSimpleName());
}
ComponentUtil.getWebApiManagerFactory().add(this);
}
@Override
public boolean matches(final HttpServletRequest request) {
final FessConfig fessConfig = ComponentUtil.getFessConfig();
if (!fessConfig.isWebApiJson()) {
switch (getFormatType(request)) {
case SEARCH:
case LABEL:
case POPULARWORD:
return false;
default:
break;
}
}
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;
default:
writeJsonResponse(99, StringUtil.EMPTY, "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(99, StringUtil.EMPTY, "Referer is invalid.");
return;
}
if (!fessConfig.isApiSearchScroll()) {
writeJsonResponse(99, StringUtil.EMPTY, "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<String, Object> 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 Exception e) {
final int status = 9;
if (logger.isDebugEnabled()) {
logger.debug("Failed to process a ping request.", e);
}
writeJsonResponse(status, null, e);
}
}
protected void processPingRequest(final HttpServletRequest request, final HttpServletResponse response, final FilterChain chain) {
final SearchEngineClient searchEngineClient = ComponentUtil.getSearchEngineClient();
int status;
Exception err = null;
try {
final PingResponse pingResponse = searchEngineClient.ping();
status = pingResponse.getStatus();
writeJsonResponse(status, "\"message\":" + pingResponse.getMessage());
} catch (final Exception e) {
status = 9;
err = e;
if (logger.isDebugEnabled()) {
logger.debug("Failed to process a ping request.", e);
}
writeJsonResponse(status, null, err);
}
}
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();
int status = 0;
Exception err = null;
String query = null;
final StringBuilder buf = new StringBuilder(1000); // TODO replace response stream
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<Map<String, Object>> documentItems = data.getDocumentItems();
final FacetResponse facetResponse = data.getFacetResponse();
final String queryId = data.getQueryId();
final String highlightParams = data.getAppendHighlightParams();
final boolean nextPage = data.isExistNextPage();
final boolean prevPage = data.isExistPrevPage();
final long startRecordNumber = data.getCurrentStartRecordNumber();
final long endRecordNumber = data.getCurrentEndRecordNumber();
final List<String> pageNumbers = data.getPageNumberList();
final boolean partial = data.isPartialResults();
final String searchQuery = data.getSearchQuery();
final long requestedTime = data.getRequestedTime();
buf.append("\"q\":");
buf.append(escapeJson(query));
buf.append(",\"query_id\":");
buf.append(escapeJson(queryId));
buf.append(",\"exec_time\":");
buf.append(execTime);
buf.append(",\"query_time\":");
buf.append(queryTime);
buf.append(',');
buf.append("\"page_size\":");
buf.append(pageSize);
buf.append(',');
buf.append("\"page_number\":");
buf.append(currentPageNumber);
buf.append(',');
buf.append("\"record_count\":");
buf.append(allRecordCount);
buf.append(',');
buf.append("\"record_count_relation\":");
buf.append(escapeJson(allRecordCountRelation));
buf.append(',');
buf.append("\"page_count\":");
buf.append(allPageCount);
buf.append(",\"highlight_params\":");
buf.append(escapeJson(highlightParams));
buf.append(",\"next_page\":");
buf.append(escapeJson(nextPage));
buf.append(",\"prev_page\":");
buf.append(escapeJson(prevPage));
buf.append(",\"start_record_number\":");
buf.append(startRecordNumber);
buf.append(",\"end_record_number\":");
buf.append(escapeJson(endRecordNumber));
buf.append(",\"page_numbers\":");
buf.append(escapeJson(pageNumbers));
buf.append(",\"partial\":");
buf.append(escapeJson(partial));
buf.append(",\"search_query\":");
buf.append(escapeJson(searchQuery));
buf.append(",\"requested_time\":");
buf.append(requestedTime);
final String[] relatedQueries = relatedQueryHelper.getRelatedQueries(params.getQuery());
buf.append(",\"related_query\":");
buf.append(escapeJson(relatedQueries));
final String[] relatedContents = relatedContentHelper.getRelatedContents(params.getQuery());
buf.append(",\"related_contents\":");
buf.append(escapeJson(relatedContents));
buf.append(',');
buf.append("\"result\":[");
if (!documentItems.isEmpty()) {
boolean first1 = true;
for (final Map<String, Object> document : documentItems) {
if (!first1) {
buf.append(',');
} else {
first1 = false;
}
buf.append('{');
boolean first2 = true;
for (final Map.Entry<String, Object> entry : document.entrySet()) {
final String name = entry.getKey();
if (StringUtil.isNotBlank(name) && entry.getValue() != null
&& ComponentUtil.getQueryFieldConfig().isApiResponseField(name)) {
if (!first2) {
buf.append(',');
} else {
first2 = false;
}
buf.append(escapeJson(name));
buf.append(':');
buf.append(escapeJson(entry.getValue()));
}
}
buf.append('}');
}
}
buf.append(']');
if (facetResponse != null && facetResponse.hasFacetResponse()) {
// facet field
buf.append(',');
buf.append("\"facet_field\":[");
if (facetResponse.getFieldList() != null) {
boolean first1 = true;
for (final Field field : facetResponse.getFieldList()) {
if (!first1) {
buf.append(',');
} else {
first1 = false;
}
buf.append("{\"name\":");
buf.append(escapeJson(field.getName()));
buf.append(",\"result\":[");
boolean first2 = true;
for (final Map.Entry<String, Long> entry : field.getValueCountMap().entrySet()) {
if (!first2) {
buf.append(',');
} else {
first2 = false;
}
buf.append("{\"value\":");
buf.append(escapeJson(entry.getKey()));
buf.append(",\"count\":");
buf.append(entry.getValue());
buf.append('}');
}
buf.append(']');
buf.append('}');
}
}
buf.append(']');
// facet q
buf.append(',');
buf.append("\"facet_query\":[");
if (facetResponse.getQueryCountMap() != null) {
boolean first1 = true;
for (final Map.Entry<String, Long> entry : facetResponse.getQueryCountMap().entrySet()) {
if (!first1) {
buf.append(',');
} else {
first1 = false;
}
buf.append("{\"value\":");
buf.append(escapeJson(entry.getKey()));
buf.append(",\"count\":");
buf.append(entry.getValue());
buf.append('}');
}
}
buf.append(']');
}
} catch (final Exception e) {
status = 1;
err = e;
if (logger.isDebugEnabled()) {
logger.debug("Failed to process a search request.", e);
}
}
writeJsonResponse(status, buf.toString(), err);
}
protected String detailedMessage(final Throwable t) {
if (t == null) {
return "Unknown";
}
Throwable target = t;
if (target.getCause() == null) {
return target.getClass().getSimpleName() + "[" + target.getMessage() + "]";
}
final StringBuilder sb = new StringBuilder();
while (target != null) {
sb.append(target.getClass().getSimpleName());
if (target.getMessage() != null) {
sb.append("[");
sb.append(target.getMessage());
sb.append("]");
}
sb.append("; ");
target = target.getCause();
if (target != null) {
sb.append("nested: ");
}
}
return sb.toString();
}
protected void processLabelRequest(final HttpServletRequest request, final HttpServletResponse response, final FilterChain chain) {
final LabelTypeHelper labelTypeHelper = ComponentUtil.getLabelTypeHelper();
int status = 0;
Exception err = null;
final StringBuilder buf = new StringBuilder(255); // TODO replace response stream
try {
final List<Map<String, String>> labelTypeItems = labelTypeHelper.getLabelTypeItemList(SearchRequestType.JSON,
request.getLocale() == null ? Locale.ROOT : request.getLocale());
buf.append("\"record_count\":");
buf.append(labelTypeItems.size());
if (!labelTypeItems.isEmpty()) {
buf.append(',');
buf.append("\"result\":[");
boolean first1 = true;
for (final Map<String, String> labelMap : labelTypeItems) {
if (!first1) {
buf.append(',');
} else {
first1 = false;
}
buf.append("{\"label\":");
buf.append(escapeJson(labelMap.get(Constants.ITEM_LABEL)));
buf.append(", \"value\":");
buf.append(escapeJson(labelMap.get(Constants.ITEM_VALUE)));
buf.append('}');
}
buf.append(']');
}
} catch (final Exception e) {
status = 1;
err = e;
if (logger.isDebugEnabled()) {
logger.debug("Failed to process a label request.", e);
}
}
writeJsonResponse(status, buf.toString(), err);
}
protected void processPopularWordRequest(final HttpServletRequest request, final HttpServletResponse response,
final FilterChain chain) {
if (!ComponentUtil.getFessConfig().isWebApiPopularWord()) {
writeJsonResponse(9, null, "Unsupported operation.");
return;
}
final String seed = request.getParameter("seed");
final List<String> tagList = new ArrayList<>();
final String[] tags = request.getParameterValues("labels");
if (tags != null) {
tagList.addAll(Arrays.asList(tags));
}
final String key = ComponentUtil.getVirtualHostHelper().getVirtualHostKey();
if (StringUtil.isNotBlank(key)) {
tagList.add(key);
}
final String[] fields = request.getParameterValues("fields");
final String[] excludes = StringUtil.EMPTY_STRINGS;// TODO
final PopularWordHelper popularWordHelper = ComponentUtil.getPopularWordHelper();
int status = 0;
Exception err = null;
final StringBuilder buf = new StringBuilder(255); // TODO replace response stream
try {
final List<String> popularWordList = popularWordHelper.getWordList(SearchRequestType.JSON, seed,
tagList.toArray(new String[tagList.size()]), null, fields, excludes);
buf.append("\"result\":[");
boolean first1 = true;
for (final String word : popularWordList) {
if (!first1) {
buf.append(',');
} else {
first1 = false;
}
buf.append(escapeJson(word));
}
buf.append(']');
} catch (final Exception e) {
if (e instanceof WebApiException) {
status = ((WebApiException) e).getStatusCode();
} else {
status = 1;
}
err = e;
if (logger.isDebugEnabled()) {
logger.debug("Failed to process a popularWord request.", e);
}
}
writeJsonResponse(status, buf.toString(), err);
}
protected void processFavoriteRequest(final HttpServletRequest request, final HttpServletResponse response, final FilterChain chain) {
if (!ComponentUtil.getFessConfig().isUserFavorite()) {
writeJsonResponse(9, null, "Unsupported operation.");
return;
}
final FessConfig fessConfig = ComponentUtil.getFessConfig();
final UserInfoHelper userInfoHelper = ComponentUtil.getUserInfoHelper();
final SearchHelper searchHelper = ComponentUtil.getSearchHelper();
final FavoriteLogService favoriteLogService = ComponentUtil.getComponent(FavoriteLogService.class);
final SystemHelper systemHelper = ComponentUtil.getSystemHelper();
try {
final String docId = request.getParameter("docId");
final String queryId = request.getParameter("queryId");
final String[] docIds = userInfoHelper.getResultDocIds(URLDecoder.decode(queryId, Constants.UTF_8));
if (docIds == null) {
throw new WebApiException(6, "No searched urls.");
}
searchHelper.getDocumentByDocId(docId, new String[] { fessConfig.getIndexFieldUrl(), fessConfig.getIndexFieldLang() },
OptionalThing.empty()).ifPresent(doc -> {
final String favoriteUrl = DocumentUtil.getValue(doc, fessConfig.getIndexFieldUrl(), String.class);
final String userCode = userInfoHelper.getUserCode();
if (StringUtil.isBlank(userCode)) {
throw new WebApiException(2, "No user session.");
}
if (StringUtil.isBlank(favoriteUrl)) {
throw new WebApiException(2, "URL is null.");
}
boolean found = false;
for (final String id : docIds) {
if (docId.equals(id)) {
found = true;
break;
}
}
if (!found) {
throw new WebApiException(5, "Not found: " + favoriteUrl);
}
if (!favoriteLogService.addUrl(userCode, (userInfo, favoriteLog) -> {
favoriteLog.setUserInfoId(userInfo.getId());
favoriteLog.setUrl(favoriteUrl);
favoriteLog.setDocId(docId);
favoriteLog.setQueryId(queryId);
favoriteLog.setCreatedAt(systemHelper.getCurrentTimeAsLocalDateTime());
})) {
throw new WebApiException(4, "Failed to add url: " + favoriteUrl);
}
final String id = DocumentUtil.getValue(doc, fessConfig.getIndexFieldId(), String.class);
searchHelper.update(id, builder -> {
final Script script = ComponentUtil.getLanguageHelper().createScript(doc,
"ctx._source." + fessConfig.getIndexFieldFavoriteCount() + "+=1");
builder.setScript(script);
final Map<String, Object> upsertMap = new HashMap<>();
upsertMap.put(fessConfig.getIndexFieldFavoriteCount(), 1);
builder.setUpsert(upsertMap);
builder.setRefreshPolicy(Constants.TRUE);
});
writeJsonResponse(0, "\"result\":\"ok\"", (String) null);
}).orElse(() -> {
throw new WebApiException(6, "Not found: " + docId);
});
} catch (final Exception e) {
int status;
if (e instanceof WebApiException) {
status = ((WebApiException) e).getStatusCode();
} else {
status = 1;
}
writeJsonResponse(status, null, e);
if (logger.isDebugEnabled()) {
logger.debug("Failed to process a favorite request.", e);
}
}
}
protected void processFavoritesRequest(final HttpServletRequest request, final HttpServletResponse response, final FilterChain chain) {
if (!ComponentUtil.getFessConfig().isUserFavorite()) {
writeJsonResponse(9, null, "Unsupported operation.");
return;
}
final UserInfoHelper userInfoHelper = ComponentUtil.getUserInfoHelper();
final FessConfig fessConfig = ComponentUtil.getFessConfig();
final SearchHelper searchHelper = ComponentUtil.getSearchHelper();
final FavoriteLogService favoriteLogService = ComponentUtil.getComponent(FavoriteLogService.class);
int status = 0;
String body = null;
Exception err = null;
try {
final String queryId = request.getParameter("queryId");
final String userCode = userInfoHelper.getUserCode();
if (StringUtil.isBlank(userCode)) {
throw new WebApiException(2, "No user session.");
}
if (StringUtil.isBlank(queryId)) {
throw new WebApiException(3, "Query ID is null.");
}
final String[] docIds = userInfoHelper.getResultDocIds(queryId);
final List<Map<String, Object>> docList = searchHelper.getDocumentListByDocIds(docIds, new String[] {
fessConfig.getIndexFieldUrl(), fessConfig.getIndexFieldDocId(), fessConfig.getIndexFieldFavoriteCount() },
OptionalThing.empty(), SearchRequestType.JSON);
List<String> urlList = new ArrayList<>(docList.size());
for (final Map<String, Object> doc : docList) {
final String urlObj = DocumentUtil.getValue(doc, fessConfig.getIndexFieldUrl(), String.class);
if (urlObj != null) {
urlList.add(urlObj);
}
}
urlList = favoriteLogService.getUrlList(userCode, urlList);
final List<String> docIdList = new ArrayList<>(urlList.size());
for (final Map<String, Object> doc : docList) {
final String urlObj = DocumentUtil.getValue(doc, fessConfig.getIndexFieldUrl(), String.class);
if (urlObj != null && urlList.contains(urlObj)) {
final String docIdObj = DocumentUtil.getValue(doc, fessConfig.getIndexFieldDocId(), String.class);
if (docIdObj != null) {
docIdList.add(docIdObj);
}
}
}
final StringBuilder buf = new StringBuilder(255); // TODO replace response stream
buf.append("\"num\":").append(docIdList.size());
buf.append(", \"doc_ids\":[");
if (!docIdList.isEmpty()) {
for (int i = 0; i < docIdList.size(); i++) {
if (i > 0) {
buf.append(',');
}
buf.append(escapeJson(docIdList.get(i)));
}
}
buf.append(']');
body = buf.toString();
} catch (final Exception e) {
if (e instanceof WebApiException) {
status = ((WebApiException) e).getStatusCode();
} else {
status = 1;
}
err = e;
if (logger.isDebugEnabled()) {
logger.debug("Failed to process a favorites request.", e);
}
}
writeJsonResponse(status, body, err);
}
protected static class JsonRequestParams extends SearchRequestParams {
private final HttpServletRequest request;
private final FessConfig fessConfig;
private int startPosition = -1;
private int pageSize = -1;
protected JsonRequestParams(final HttpServletRequest request, final FessConfig fessConfig) {
this.request = request;
this.fessConfig = fessConfig;
}
@Override
public String getTrackTotalHits() {
return request.getParameter(Constants.TRACK_TOTAL_HITS);
}
@Override
public String getQuery() {
return request.getParameter("q");
}
@Override
public String[] getExtraQueries() {
return getParamValueArray(request, "ex_q");
}
@Override
public Map<String, String[]> getFields() {
final Map<String, String[]> fields = new HashMap<>();
for (final Map.Entry<String, String[]> entry : request.getParameterMap().entrySet()) {
final String key = entry.getKey();
if (key.startsWith("fields.")) {
final String[] value = simplifyArray(entry.getValue());
fields.put(key.substring("fields.".length()), value);
}
}
return fields;
}
@Override
public Map<String, String[]> getConditions() {
final Map<String, String[]> conditions = new HashMap<>();
for (final Map.Entry<String, String[]> entry : request.getParameterMap().entrySet()) {
final String key = entry.getKey();
if (key.startsWith("as.")) {
final String[] value = simplifyArray(entry.getValue());
conditions.put(key.substring("as.".length()), value);
}
}
return conditions;
}
@Override
public String[] getLanguages() {
return getParamValueArray(request, "lang");
}
@Override
public GeoInfo getGeoInfo() {
return createGeoInfo(request);
}
@Override
public FacetInfo getFacetInfo() {
return createFacetInfo(request);
}
@Override
public String getSort() {
return request.getParameter("sort");
}
@Override
public int getStartPosition() {
if (startPosition != -1) {
return startPosition;
}
final String start = request.getParameter("start");
if (StringUtil.isBlank(start)) {
startPosition = fessConfig.getPagingSearchPageStartAsInteger();
} else {
try {
startPosition = Integer.parseInt(start);
} catch (final NumberFormatException e) {
startPosition = fessConfig.getPagingSearchPageStartAsInteger();
}
}
return startPosition;
}
@Override
public int getPageSize() {
if (pageSize != -1) {
return pageSize;
}
final String num = request.getParameter("num");
if (StringUtil.isBlank(num)) {
pageSize = fessConfig.getPagingSearchPageSizeAsInteger();
} else {
try {
pageSize = Integer.parseInt(num);
if (pageSize > fessConfig.getPagingSearchPageMaxSizeAsInteger().intValue() || pageSize <= 0) {
pageSize = fessConfig.getPagingSearchPageMaxSizeAsInteger();
}
} catch (final NumberFormatException e) {
pageSize = fessConfig.getPagingSearchPageSizeAsInteger();
}
}
return pageSize;
}
@Override
public Object getAttribute(final String name) {
return request.getAttribute(name);
}
@Override
public Locale getLocale() {
return Locale.ROOT;
}
@Override
public SearchRequestType getType() {
return SearchRequestType.JSON;
}
@Override
public String getSimilarDocHash() {
return request.getParameter("sdh");
}
@Override
public HighlightInfo getHighlightInfo() {
return ComponentUtil.getViewHelper().createHighlightInfo();
}
}
@Override
protected void writeHeaders(final HttpServletResponse response) {
ComponentUtil.getFessConfig().getApiJsonResponseHeaderList().forEach(e -> response.setHeader(e.getFirst(), e.getSecond()));
}
}

View file

@ -1,308 +0,0 @@
/*
* 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.suggest;
import static org.codelibs.core.stream.StreamUtil.stream;
import java.io.IOException;
import java.util.Collections;
import java.util.Locale;
import java.util.Map;
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.StringUtils;
import org.apache.commons.text.StringEscapeUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.codelibs.core.lang.StringUtil;
import org.codelibs.fess.api.ClassicJsonApiManager;
import org.codelibs.fess.entity.FacetInfo;
import org.codelibs.fess.entity.GeoInfo;
import org.codelibs.fess.entity.HighlightInfo;
import org.codelibs.fess.entity.SearchRequestParams;
import org.codelibs.fess.entity.SearchRequestParams.SearchRequestType;
import org.codelibs.fess.exception.InvalidAccessTokenException;
import org.codelibs.fess.helper.RoleQueryHelper;
import org.codelibs.fess.helper.SearchHelper;
import org.codelibs.fess.helper.SuggestHelper;
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;
@Deprecated
public class SuggestApiManager extends ClassicJsonApiManager {
private static final Logger logger = LogManager.getLogger(SuggestApiManager.class);
public SuggestApiManager() {
setPathPrefix("/suggest");
}
@PostConstruct
public void register() {
if (logger.isInfoEnabled()) {
logger.info("Load {}", this.getClass().getSimpleName());
}
ComponentUtil.getWebApiManagerFactory().add(this);
}
@Override
public boolean matches(final HttpServletRequest request) {
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 {
final FessConfig fessConfig = ComponentUtil.getFessConfig();
if (!fessConfig.isAcceptedSearchReferer(request.getHeader("referer"))) {
writeJsonResponse(99, StringUtil.EMPTY, "Referer is invalid.");
return;
}
int status = 0;
String errMsg = StringUtil.EMPTY;
final StringBuilder buf = new StringBuilder(255); // TODO replace response stream
final RoleQueryHelper roleQueryHelper = ComponentUtil.getRoleQueryHelper();
final SearchHelper searchHelper = ComponentUtil.getSearchHelper();
try {
final RequestParameter parameter = RequestParameter.parse(request);
final String[] langs = searchHelper.getLanguages(request, parameter);
final SuggestHelper suggestHelper = ComponentUtil.getSuggestHelper();
final SuggestRequestBuilder builder = suggestHelper.suggester().suggest();
builder.setQuery(parameter.getQuery());
stream(parameter.getSuggestFields()).of(stream -> stream.forEach(builder::addField));
roleQueryHelper.build(SearchRequestType.SUGGEST).stream().forEach(builder::addRole);
builder.setSize(parameter.getNum());
stream(langs).of(stream -> stream.forEach(builder::addLang));
stream(parameter.getTags()).of(stream -> stream.forEach(builder::addTag));
final String key = ComponentUtil.getVirtualHostHelper().getVirtualHostKey();
if (StringUtil.isNotBlank(key)) {
builder.addTag(key);
}
builder.addKind(SuggestItem.Kind.USER.toString());
if (ComponentUtil.getFessConfig().isSuggestSearchLog()) {
builder.addKind(SuggestItem.Kind.QUERY.toString());
}
if (ComponentUtil.getFessConfig().isSuggestDocuments()) {
builder.addKind(SuggestItem.Kind.DOCUMENT.toString());
}
final SuggestResponse suggestResponse = builder.execute().getResponse();
buf.append("\"result\":{");
buf.append("\"took\":\"").append(suggestResponse.getTookMs()).append('\"');
buf.append(",\"total\":\"").append(suggestResponse.getTotal()).append('\"');
buf.append(",\"num\":\"").append(suggestResponse.getNum()).append('\"');
if (!suggestResponse.getItems().isEmpty()) {
buf.append(",\"hits\":[");
boolean first = true;
for (final SuggestItem item : suggestResponse.getItems()) {
if (!first) {
buf.append(',');
}
first = false;
buf.append("{\"text\":\"").append(StringEscapeUtils.escapeJson(item.getText())).append('\"');
buf.append(",\"tags\":[");
for (int i = 0; i < item.getTags().length; i++) {
if (i > 0) {
buf.append(',');
}
buf.append('\"').append(StringEscapeUtils.escapeJson(item.getTags()[i])).append('\"');
}
buf.append(']');
buf.append('}');
}
buf.append(']');
}
buf.append('}');
} catch (final Exception e) {
status = 1;
errMsg = e.getMessage();
if (errMsg == null) {
errMsg = e.getClass().getName();
}
if (logger.isDebugEnabled()) {
logger.debug("Failed to process a suggest request.", e);
}
if (e instanceof final InvalidAccessTokenException iate) {
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
response.setHeader("WWW-Authenticate", "Bearer error=\"" + iate.getType() + "\"");
}
}
writeJsonResponse(status, buf.toString(), errMsg);
}
protected static class RequestParameter extends SearchRequestParams {
private final String query;
private final String[] fields;
private final int num;
private final HttpServletRequest request;
private final String[] tags;
protected RequestParameter(final HttpServletRequest request, final String query, final String[] tags, final String[] fields,
final int num) {
this.query = query;
this.tags = tags;
this.fields = fields;
this.num = num;
this.request = request;
}
protected static RequestParameter parse(final HttpServletRequest request) {
final String query = request.getParameter("query");
final String fieldsStr = request.getParameter("fields");
final String[] fields;
if (StringUtil.isNotBlank(fieldsStr)) {
fields = fieldsStr.split(",");
} else {
fields = new String[0];
}
final String numStr = request.getParameter("num");
final int num;
if (StringUtil.isNotBlank(numStr) && StringUtils.isNumeric(numStr)) {
num = Integer.parseInt(numStr);
} else {
num = 10;
}
final String tagsStr = request.getParameter("tags");
final String[] tags;
if (StringUtil.isNotBlank(tagsStr)) {
tags = tagsStr.split(",");
} else {
tags = new String[0];
}
return new RequestParameter(request, query, tags, fields, num);
}
@Override
public String getQuery() {
return query;
}
protected String[] getSuggestFields() {
return fields;
}
protected int getNum() {
return num;
}
@Override
public Map<String, String[]> getFields() {
return Collections.emptyMap();
}
@Override
public Map<String, String[]> getConditions() {
return Collections.emptyMap();
}
public String[] getTags() {
return tags;
}
@Override
public String[] getLanguages() {
return getParamValueArray(request, "lang");
}
@Override
public GeoInfo getGeoInfo() {
throw new UnsupportedOperationException();
}
@Override
public FacetInfo getFacetInfo() {
throw new UnsupportedOperationException();
}
@Override
public String getSort() {
throw new UnsupportedOperationException();
}
@Override
public int getStartPosition() {
throw new UnsupportedOperationException();
}
@Override
public int getPageSize() {
throw new UnsupportedOperationException();
}
@Override
public String[] getExtraQueries() {
throw new UnsupportedOperationException();
}
@Override
public Object getAttribute(final String name) {
throw new UnsupportedOperationException();
}
@Override
public Locale getLocale() {
throw new UnsupportedOperationException();
}
@Override
public SearchRequestType getType() {
return SearchRequestType.SUGGEST;
}
@Override
public String getSimilarDocHash() {
throw new UnsupportedOperationException();
}
@Override
public HighlightInfo getHighlightInfo() {
return new HighlightInfo();
}
}
@Override
protected void writeHeaders(final HttpServletResponse response) {
ComponentUtil.getFessConfig().getApiJsonResponseHeaderList().forEach(e -> response.setHeader(e.getFirst(), e.getSecond()));
}
}

View file

@ -9,12 +9,8 @@
<component name="searchApiManager" class="org.codelibs.fess.api.json.SearchApiManager">
</component>
<component name="jsonApiManager" class="org.codelibs.fess.api.json.JsonApiManager">
</component>
<component name="searchEngineApiManager" class="org.codelibs.fess.api.engine.SearchEngineApiManager">
</component>
<component name="suggestApiManager" class="org.codelibs.fess.api.suggest.SuggestApiManager">
</component>
<component name="gsaApiManager" class="org.codelibs.fess.api.gsa.GsaApiManager">
</component>