diff --git a/src/main/java/org/codelibs/fess/api/BaseApiManager.java b/src/main/java/org/codelibs/fess/api/BaseApiManager.java index 75a216acc..965caf935 100644 --- a/src/main/java/org/codelibs/fess/api/BaseApiManager.java +++ b/src/main/java/org/codelibs/fess/api/BaseApiManager.java @@ -33,14 +33,12 @@ public abstract class BaseApiManager implements WebApiManager { protected static final String HOT_SEARCH_WORD_API = "/hotSearchWordApi"; - protected static final String SUGGEST_API = "/suggestApi"; - protected static final String SEARCH_API = "/searchApi"; protected String pathPrefix; protected static enum FormatType { - SEARCH, LABEL, SUGGEST, HOTSEARCHWORD, FAVORITE, FAVORITES, OTHER, PING; + SEARCH, LABEL, HOTSEARCHWORD, FAVORITE, FAVORITES, OTHER, PING; } public String getPathPrefix() { @@ -60,8 +58,6 @@ public abstract class BaseApiManager implements WebApiManager { return FormatType.SEARCH; } else if (FormatType.LABEL.name().equals(type)) { return FormatType.LABEL; - } else if (FormatType.SUGGEST.name().equals(type)) { - return FormatType.SUGGEST; } else if (FormatType.HOTSEARCHWORD.name().equals(type)) { return FormatType.HOTSEARCHWORD; } else if (FormatType.FAVORITE.name().equals(type)) { diff --git a/src/main/java/org/codelibs/fess/api/es/EsApiManager.java b/src/main/java/org/codelibs/fess/api/es/EsApiManager.java index 8edfacf1e..ab974851c 100644 --- a/src/main/java/org/codelibs/fess/api/es/EsApiManager.java +++ b/src/main/java/org/codelibs/fess/api/es/EsApiManager.java @@ -39,23 +39,24 @@ public class EsApiManager extends BaseApiManager { } @Override - public boolean matches(HttpServletRequest request) { + public boolean matches(final HttpServletRequest request) { final String servletPath = request.getServletPath(); if (servletPath.startsWith(pathPrefix)) { - FessLoginAssist loginAssist = ComponentUtil.getLoginAssist(); + final FessLoginAssist loginAssist = ComponentUtil.getLoginAssist(); return loginAssist.getSessionUserBean().map(user -> user.hasRoles(acceptedRoles)).orElseGet(() -> Boolean.FALSE).booleanValue(); } return false; } @Override - public void process(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException { + public void process(final HttpServletRequest request, final HttpServletResponse response, final FilterChain chain) throws IOException, + ServletException { String path = request.getServletPath().substring(pathPrefix.length()); if (!path.startsWith("/")) { path = "/" + path; } - Method httpMethod = Method.valueOf(request.getMethod().toUpperCase(Locale.ROOT)); - CurlRequest curlRequest = new CurlRequest(httpMethod, getUrl() + path); + final Method httpMethod = Method.valueOf(request.getMethod().toUpperCase(Locale.ROOT)); + final CurlRequest curlRequest = new CurlRequest(httpMethod, getUrl() + path); request.getParameterMap().entrySet().stream().forEach(entry -> { if (entry.getValue().length > 1) { curlRequest.param(entry.getKey(), String.join(",", entry.getValue())); @@ -68,7 +69,7 @@ public class EsApiManager extends BaseApiManager { if (httpMethod != Method.GET) { try (ServletInputStream in = request.getInputStream(); OutputStream out = con.getOutputStream()) { CopyUtil.copy(in, out); - } catch (IOException e) { + } catch (final IOException e) { throw new IORuntimeException(e); } } @@ -76,17 +77,17 @@ public class EsApiManager extends BaseApiManager { try (InputStream in = con.getInputStream(); ServletOutputStream out = response.getOutputStream()) { response.setStatus(con.getResponseCode()); CopyUtil.copy(in, out); - } catch (IOException e) { + } catch (final IOException e) { try (InputStream err = con.getErrorStream()) { logger.error(new String(InputStreamUtil.getBytes(err), Constants.CHARSET_UTF_8)); - } catch (IOException e1) {} + } catch (final IOException e1) {} throw new IORuntimeException(e); } }); // TODO exception } - public void setAcceptedRoles(String[] acceptedRoles) { + public void setAcceptedRoles(final String[] acceptedRoles) { this.acceptedRoles = acceptedRoles; } 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 8c13bec66..c365333ce 100644 --- a/src/main/java/org/codelibs/fess/api/json/JsonApiManager.java +++ b/src/main/java/org/codelibs/fess/api/json/JsonApiManager.java @@ -18,11 +18,15 @@ package org.codelibs.fess.api.json; import java.io.IOException; import java.io.StringWriter; +import java.net.URLDecoder; import java.text.SimpleDateFormat; +import java.util.ArrayList; import java.util.Date; +import java.util.HashMap; import java.util.List; import java.util.Map; +import javax.annotation.Resource; import javax.servlet.FilterChain; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; @@ -31,18 +35,28 @@ import javax.servlet.http.HttpServletResponse; import org.apache.commons.lang3.StringEscapeUtils; import org.codelibs.core.CoreLibConstants; import org.codelibs.core.lang.StringUtil; +import org.codelibs.core.misc.DynamicProperties; import org.codelibs.fess.Constants; import org.codelibs.fess.api.BaseApiManager; -import org.codelibs.fess.api.WebApiRequest; -import org.codelibs.fess.api.WebApiResponse; +import org.codelibs.fess.app.service.FavoriteLogService; +import org.codelibs.fess.app.service.SearchService; +import org.codelibs.fess.entity.FacetInfo; +import org.codelibs.fess.entity.GeoInfo; import org.codelibs.fess.entity.PingResponse; +import org.codelibs.fess.entity.SearchRenderData; +import org.codelibs.fess.entity.SearchRequestParams; import org.codelibs.fess.es.client.FessEsClient; import org.codelibs.fess.exception.WebApiException; +import org.codelibs.fess.helper.FieldHelper; +import org.codelibs.fess.helper.HotSearchWordHelper; +import org.codelibs.fess.helper.HotSearchWordHelper.Range; +import org.codelibs.fess.helper.LabelTypeHelper; +import org.codelibs.fess.helper.QueryHelper; +import org.codelibs.fess.helper.UserInfoHelper; 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.codelibs.fess.util.MoreLikeThisResponse; -import org.codelibs.fess.util.WebApiUtil; import org.lastaflute.web.util.LaRequestUtil; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -51,6 +65,9 @@ public class JsonApiManager extends BaseApiManager { private static final Logger logger = LoggerFactory.getLogger(JsonApiManager.class); + @Resource + protected DynamicProperties crawlerProperties; + public JsonApiManager() { setPathPrefix("/json"); } @@ -76,9 +93,6 @@ public class JsonApiManager extends BaseApiManager { case LABEL: processLabelRequest(request, response, chain); break; - case SUGGEST: - processSuggestRequest(request, response, chain); - break; case HOTSEARCHWORD: processHotSearchWordRequest(request, response, chain); break; @@ -119,26 +133,26 @@ public class JsonApiManager extends BaseApiManager { } protected void processSearchRequest(final HttpServletRequest request, final HttpServletResponse response, final FilterChain chain) { + final SearchService searchService = ComponentUtil.getComponent(SearchService.class); + int status = 0; String errMsg = StringUtil.EMPTY; String query = null; final StringBuilder buf = new StringBuilder(1000); request.setAttribute(Constants.SEARCH_LOG_ACCESS_TYPE, Constants.SEARCH_LOG_ACCESS_TYPE_JSON); - final String queryId = request.getParameter("queryId"); try { - chain.doFilter(new WebApiRequest(request, SEARCH_API), new WebApiResponse(response)); - WebApiUtil.validate(); - query = WebApiUtil.getObject("searchQuery"); - final String execTime = WebApiUtil.getObject("execTime"); - final String queryTime = WebApiUtil.getObject("queryTime"); - final String searchTime = WebApiUtil.getObject("searchTime"); - final String pageSize = WebApiUtil.getObject("pageSize"); - final String currentPageNumber = WebApiUtil.getObject("currentPageNumber"); - final String allRecordCount = WebApiUtil.getObject("allRecordCount"); - final String allPageCount = WebApiUtil.getObject("allPageCount"); - final List> documentItems = WebApiUtil.getObject("documentItems"); - final FacetResponse facetResponse = WebApiUtil.getObject("facetResponse"); - final MoreLikeThisResponse moreLikeThisResponse = WebApiUtil.getObject("moreLikeThisResponse"); + final SearchRenderData data = new SearchRenderData(); + final SearchApiRequestParams params = new SearchApiRequestParams(request); + searchService.search(request, params, data); + query = params.getQuery(); + 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 allPageCount = Integer.toString(data.getAllPageCount()); + final List> documentItems = data.getDocumentItems(); + final FacetResponse facetResponse = data.getFacetResponse(); buf.append("\"query\":"); buf.append(escapeJson(query)); @@ -146,14 +160,7 @@ public class JsonApiManager extends BaseApiManager { buf.append(execTime); buf.append(",\"queryTime\":"); buf.append(queryTime); - buf.append(",\"searchTime\":"); - buf.append(searchTime); buf.append(','); - if (StringUtil.isNotBlank(queryId)) { - buf.append("\"queryId\":"); - buf.append(escapeJson(queryId)); - buf.append(','); - } buf.append("\"pageSize\":"); buf.append(pageSize); buf.append(','); @@ -248,46 +255,6 @@ public class JsonApiManager extends BaseApiManager { buf.append(']'); } } - if (moreLikeThisResponse != null && !moreLikeThisResponse.isEmpty()) { - buf.append(','); - buf.append("\"moreLikeThis\":["); - boolean first = true; - for (final Map.Entry>> mltEntry : moreLikeThisResponse.entrySet()) { - if (!first) { - buf.append(','); - } else { - first = false; - } - buf.append("{\"id\":"); - buf.append(escapeJson(mltEntry.getKey())); - buf.append(",\"result\":["); - boolean first1 = true; - for (final Map document : mltEntry.getValue()) { - if (!first1) { - buf.append(','); - } else { - first1 = false; - } - buf.append('{'); - boolean first2 = true; - for (final Map.Entry entry : document.entrySet()) { - if (StringUtil.isNotBlank(entry.getKey()) && entry.getValue() != null) { - if (!first2) { - buf.append(','); - } else { - first2 = false; - } - buf.append(escapeJson(entry.getKey())); - buf.append(':'); - buf.append(escapeJson(entry.getValue())); - } - } - buf.append('}'); - } - buf.append("]}"); - } - buf.append(']'); - } } catch (final Exception e) { status = 1; errMsg = e.getMessage(); @@ -304,11 +271,13 @@ public class JsonApiManager extends BaseApiManager { } protected void processLabelRequest(final HttpServletRequest request, final HttpServletResponse response, final FilterChain chain) { + final LabelTypeHelper labelTypeHelper = ComponentUtil.getLabelTypeHelper(); + int status = 0; String errMsg = StringUtil.EMPTY; final StringBuilder buf = new StringBuilder(255); try { - final List> labelTypeItems = ComponentUtil.getLabelTypeHelper().getLabelTypeItemList(); + final List> labelTypeItems = labelTypeHelper.getLabelTypeItemList(); buf.append("\"recordCount\":"); buf.append(labelTypeItems.size()); if (!labelTypeItems.isEmpty()) { @@ -341,91 +310,20 @@ public class JsonApiManager extends BaseApiManager { } - protected void processSuggestRequest(final HttpServletRequest request, final HttpServletResponse response, final FilterChain chain) { - // TODO - // int status = 0; - // String errMsg = StringUtil.EMPTY; - // final StringBuilder buf = new StringBuilder(255); - // try { - // chain.doFilter(new WebApiRequest(request, SUGGEST_API), new WebApiResponse(response)); - // WebApiUtil.validate(); - // final Integer suggestRecordCount = WebApiUtil.getObject("suggestRecordCount"); - // final List suggestResultList = WebApiUtil.getObject("suggestResultList"); - // final List suggestFieldName = WebApiUtil.getObject("suggestFieldName"); - // - // buf.append("\"recordCount\":"); - // buf.append(suggestRecordCount); - // - // if (suggestResultList.size() > 0) { - // buf.append(','); - // buf.append("\"result\":["); - // boolean first1 = true; - // for (int i = 0; i < suggestResultList.size(); i++) { - // - // final SuggestResponse suggestResponse = suggestResultList.get(i); - // - // for (final Map.Entry> entry : suggestResponse.entrySet()) { - // final String fn = suggestFieldName.get(i); - // if (!first1) { - // buf.append(','); - // } else { - // first1 = false; - // } - // - // final SuggestResponseList srList = (SuggestResponseList) entry.getValue(); - // - // buf.append("{\"token\":"); - // buf.append(escapeJson(entry.getKey())); - // buf.append(", \"fn\":"); - // buf.append(escapeJson(fn)); - // buf.append(", \"startOffset\":"); - // buf.append(Integer.toString(srList.getStartOffset())); - // buf.append(", \"endOffset\":"); - // buf.append(Integer.toString(srList.getEndOffset())); - // buf.append(", \"numFound\":"); - // buf.append(Integer.toString(srList.getNumFound())); - // buf.append(", "); - // buf.append("\"result\":["); - // boolean first2 = true; - // for (final String value : srList) { - // if (!first2) { - // buf.append(','); - // } else { - // first2 = false; - // } - // buf.append(escapeJson(value)); - // } - // buf.append("]}"); - // } - // - // } - // buf.append(']'); - // } - // } catch (final Exception e) { - // if (e instanceof WebApiException) { - // status = ((WebApiException) e).getStatusCode(); - // } else { - // status = 1; - // } - // errMsg = e.getMessage(); - // if (logger.isDebugEnabled()) { - // logger.debug("Failed to process a suggest request.", e); - // } - // } - // - // writeJsonResponse(status, buf.toString(), errMsg); - - } - protected void processHotSearchWordRequest(final HttpServletRequest request, final HttpServletResponse response, final FilterChain chain) { + if (Constants.FALSE.equals(crawlerProperties.getProperty(Constants.WEB_API_HOT_SEARCH_WORD_PROPERTY, Constants.TRUE))) { + writeJsonResponse(9, null, "Unsupported operation."); + return; + } + + final HotSearchWordHelper hotSearchWordHelper = ComponentUtil.getHotSearchWordHelper(); int status = 0; String errMsg = StringUtil.EMPTY; final StringBuilder buf = new StringBuilder(255); try { - chain.doFilter(new WebApiRequest(request, HOT_SEARCH_WORD_API), new WebApiResponse(response)); - WebApiUtil.validate(); - final List hotSearchWordList = WebApiUtil.getObject("hotSearchWordList"); + final List hotSearchWordList = + hotSearchWordHelper.getHotSearchWordList(Range.parseRange(request.getParameter("range"))); buf.append("\"result\":["); boolean first1 = true; @@ -455,39 +353,128 @@ public class JsonApiManager extends BaseApiManager { } protected void processFavoriteRequest(final HttpServletRequest request, final HttpServletResponse response, final FilterChain chain) { - int status = 0; - String body = null; - String errMsg = null; - try { - chain.doFilter(new WebApiRequest(request, FAVORITE_API), new WebApiResponse(response)); - WebApiUtil.validate(); + if (Constants.FALSE.equals(crawlerProperties.getProperty(Constants.USER_FAVORITE_PROPERTY, Constants.FALSE))) { + writeJsonResponse(9, null, "Unsupported operation."); + return; + } - body = "\"result\":\"ok\""; + final UserInfoHelper userInfoHelper = ComponentUtil.getUserInfoHelper(); + final FieldHelper fieldHelper = ComponentUtil.getFieldHelper(); + final SearchService searchService = ComponentUtil.getComponent(SearchService.class); + final FavoriteLogService favoriteLogService = ComponentUtil.getComponent(FavoriteLogService.class); + + 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."); + } + + searchService + .getDocumentByDocId(docId, new String[] { fieldHelper.idField, fieldHelper.urlField, fieldHelper.favoriteCountField }) + .ifPresent(doc -> { + final String favoriteUrl = doc == null ? null : DocumentUtil.getValue(doc, fieldHelper.urlField, String.class); + final String userCode = userInfoHelper.getUserCode(); + + if (StringUtil.isBlank(userCode)) { + throw new WebApiException(2, "No user session."); + } else 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, favoriteUrl)) { + throw new WebApiException(4, "Failed to add url: " + favoriteUrl); + } + + final String id = DocumentUtil.getValue(doc, fieldHelper.idField, String.class); + final Long count = DocumentUtil.getValue(doc, fieldHelper.favoriteCountField, Long.class); + if (count != null) { + searchService.update(id, fieldHelper.favoriteCountField, count.longValue() + 1); + } else { + throw new WebApiException(7, "Failed to update count: " + favoriteUrl); + } + + writeJsonResponse(0, "\"result\":\"ok\"", 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; } - errMsg = e.getMessage(); + writeJsonResponse(status, null, e.getMessage()); if (logger.isDebugEnabled()) { logger.debug("Failed to process a favorite request.", e); } } - writeJsonResponse(status, body, errMsg); } protected void processFavoritesRequest(final HttpServletRequest request, final HttpServletResponse response, final FilterChain chain) { + if (Constants.FALSE.equals(crawlerProperties.getProperty(Constants.USER_FAVORITE_PROPERTY, Constants.FALSE))) { + writeJsonResponse(9, null, "Unsupported operation."); + return; + } + + final UserInfoHelper userInfoHelper = ComponentUtil.getUserInfoHelper(); + final FieldHelper fieldHelper = ComponentUtil.getFieldHelper(); + final SearchService searchService = ComponentUtil.getComponent(SearchService.class); + final FavoriteLogService favoriteLogService = ComponentUtil.getComponent(FavoriteLogService.class); + int status = 0; String body = null; String errMsg = null; try { - chain.doFilter(new WebApiRequest(request, FAVORITES_API), new WebApiResponse(response)); - WebApiUtil.validate(); - final List docIdList = WebApiUtil.getObject("docIdList"); + final String queryId = request.getParameter("queryId"); + final String userCode = userInfoHelper.getUserCode(); + + if (StringUtil.isBlank(userCode)) { + throw new WebApiException(2, "No user session."); + } else if (StringUtil.isBlank(queryId)) { + throw new WebApiException(3, "Query ID is null."); + } + + final String[] docIds = userInfoHelper.getResultDocIds(queryId); + final List> docList = + searchService.getDocumentListByDocIds(docIds, new String[] { fieldHelper.urlField, fieldHelper.docIdField, + fieldHelper.favoriteCountField }); + List urlList = new ArrayList<>(docList.size()); + for (final Map doc : docList) { + final String urlObj = DocumentUtil.getValue(doc, fieldHelper.urlField, String.class); + if (urlObj != null) { + urlList.add(urlObj.toString()); + } + } + urlList = favoriteLogService.getUrlList(userCode, urlList); + final List docIdList = new ArrayList<>(urlList.size()); + for (final Map doc : docList) { + final String urlObj = DocumentUtil.getValue(doc, fieldHelper.urlField, String.class); + if (urlObj != null && urlList.contains(urlObj)) { + final String docIdObj = DocumentUtil.getValue(doc, fieldHelper.docIdField, String.class); + if (docIdObj != null) { + docIdList.add(docIdObj); + } + } + } final StringBuilder buf = new StringBuilder(); buf.append("\"num\":").append(docIdList.size()); @@ -671,4 +658,103 @@ public class JsonApiManager extends BaseApiManager { return Integer.toHexString(ch).toUpperCase(); } + protected static class SearchApiRequestParams implements SearchRequestParams { + + private final HttpServletRequest request; + + private int startPosition = -1; + + private int pageSize = -1; + + protected SearchApiRequestParams(final HttpServletRequest request) { + this.request = request; + } + + @Override + public String getQuery() { + return request.getParameter("query"); + } + + @Override + public String getOperator() { + return request.getParameter("op"); + } + + @Override + public String[] getAdditional() { + return request.getParameterValues("additional"); + } + + @Override + public Map getFields() { + // TODO Auto-generated method stub + return new HashMap<>(); + } + + @Override + public String[] getLanguages() { + return request.getParameterValues("lang"); + } + + @Override + public GeoInfo getGeoInfo() { + // TODO Auto-generated method stub + return null; + } + + @Override + public FacetInfo getFacetInfo() { + // TODO Auto-generated method stub + return null; + } + + @Override + public String getSort() { + return request.getParameter("sort"); + } + + @Override + public int getStartPosition() { + if (startPosition != -1) { + return startPosition; + } + + final String start = request.getParameter("start"); + final QueryHelper queryHelper = ComponentUtil.getQueryHelper(); + if (StringUtil.isBlank(start)) { + startPosition = queryHelper.getDefaultStart(); + } else { + try { + startPosition = Integer.parseInt(start); + } catch (final NumberFormatException e) { + startPosition = queryHelper.getDefaultStart(); + } + } + return startPosition; + } + + @Override + public int getPageSize() { + if (pageSize != -1) { + return pageSize; + } + + final String num = request.getParameter("num"); + final QueryHelper queryHelper = ComponentUtil.getQueryHelper(); + if (StringUtil.isBlank(num)) { + pageSize = queryHelper.getDefaultPageSize(); + } else { + try { + pageSize = Integer.parseInt(num); + if (pageSize > queryHelper.getMaxPageSize() || pageSize <= 0) { + pageSize = queryHelper.getMaxPageSize(); + } + } catch (final NumberFormatException e) { + pageSize = queryHelper.getDefaultPageSize(); + } + } + return pageSize; + } + + } } diff --git a/src/main/java/org/codelibs/fess/api/xml/XmlApiManager.java b/src/main/java/org/codelibs/fess/api/xml/XmlApiManager.java deleted file mode 100644 index 3a3518dc8..000000000 --- a/src/main/java/org/codelibs/fess/api/xml/XmlApiManager.java +++ /dev/null @@ -1,407 +0,0 @@ -/* - * Copyright 2009-2015 the 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.xml; - -import java.io.IOException; -import java.text.SimpleDateFormat; -import java.util.Date; -import java.util.List; -import java.util.Map; - -import javax.servlet.FilterChain; -import javax.servlet.ServletException; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; - -import org.apache.commons.lang3.StringEscapeUtils; -import org.codelibs.core.CoreLibConstants; -import org.codelibs.core.lang.StringUtil; -import org.codelibs.fess.Constants; -import org.codelibs.fess.api.BaseApiManager; -import org.codelibs.fess.api.WebApiRequest; -import org.codelibs.fess.api.WebApiResponse; -import org.codelibs.fess.entity.PingResponse; -import org.codelibs.fess.es.client.FessEsClient; -import org.codelibs.fess.util.ComponentUtil; -import org.codelibs.fess.util.FacetResponse; -import org.codelibs.fess.util.FacetResponse.Field; -import org.codelibs.fess.util.MoreLikeThisResponse; -import org.codelibs.fess.util.WebApiUtil; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -public class XmlApiManager extends BaseApiManager { - private static final Logger logger = LoggerFactory.getLogger(XmlApiManager.class); - - public XmlApiManager() { - setPathPrefix("/xml"); - } - - @Override - public boolean matches(final HttpServletRequest request) { - if (Constants.FALSE.equals(ComponentUtil.getCrawlerProperties().getProperty(Constants.WEB_API_XML_PROPERTY, Constants.TRUE))) { - 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 { - final String formatType = request.getParameter("type"); - switch (getFormatType(formatType)) { - case SEARCH: - processSearchRequest(request, response, chain); - break; - case LABEL: - processLabelRequest(request, response, chain); - break; - case SUGGEST: - processSuggestRequest(request, response, chain); - break; - case PING: - processPingRequest(request, response, chain); - break; - default: - writeXmlResponse(-1, StringUtil.EMPTY, "Not found."); - break; - } - - } - - protected void processPingRequest(final HttpServletRequest request, final HttpServletResponse response, final FilterChain chain) { - final FessEsClient fessEsClient = ComponentUtil.getElasticsearchClient(); - int status; - String errMsg = null; - try { - final PingResponse pingResponse = fessEsClient.ping(); - status = pingResponse.getStatus(); - } catch (final Exception e) { - status = 9; - errMsg = e.getMessage(); - if (errMsg == null) { - errMsg = e.getClass().getName(); - } - if (logger.isDebugEnabled()) { - logger.debug("Failed to process a ping request.", e); - } - } - - writeXmlResponse(status, null, errMsg); - } - - protected void processSearchRequest(final HttpServletRequest request, final HttpServletResponse response, final FilterChain chain) { - int status = 0; - String errMsg = StringUtil.EMPTY; - final StringBuilder buf = new StringBuilder(1000); - String query = null; - request.setAttribute(Constants.SEARCH_LOG_ACCESS_TYPE, Constants.SEARCH_LOG_ACCESS_TYPE_XML); - final String queryId = request.getParameter("queryId"); - try { - chain.doFilter(new WebApiRequest(request, SEARCH_API), new WebApiResponse(response)); - WebApiUtil.validate(); - query = WebApiUtil.getObject("searchQuery"); - final String execTime = WebApiUtil.getObject("execTime"); - final String queryTime = WebApiUtil.getObject("queryTime"); - final String searchTime = WebApiUtil.getObject("searchTime"); - final String pageSize = WebApiUtil.getObject("pageSize"); - final String currentPageNumber = WebApiUtil.getObject("currentPageNumber"); - final String allRecordCount = WebApiUtil.getObject("allRecordCount"); - final String allPageCount = WebApiUtil.getObject("allPageCount"); - final List> documentItems = WebApiUtil.getObject("documentItems"); - final FacetResponse facetResponse = WebApiUtil.getObject("facetResponse"); - final MoreLikeThisResponse moreLikeThisResponse = WebApiUtil.getObject("moreLikeThisResponse"); - - buf.append(""); - buf.append(escapeXml(query)); - buf.append(""); - buf.append(""); - buf.append(execTime); - buf.append(""); - buf.append(""); - buf.append(queryTime); - buf.append(""); - buf.append(""); - buf.append(searchTime); - buf.append(""); - if (StringUtil.isNotBlank(queryId)) { - buf.append(""); - buf.append(escapeXml(queryId)); - buf.append(""); - } - buf.append(""); - buf.append(pageSize); - buf.append(""); - buf.append(""); - buf.append(currentPageNumber); - buf.append(""); - buf.append(""); - buf.append(allRecordCount); - buf.append(""); - buf.append(""); - buf.append(allPageCount); - buf.append(""); - buf.append(""); - for (final Map document : documentItems) { - buf.append(""); - for (final Map.Entry entry : document.entrySet()) { - final String name = entry.getKey(); - if (StringUtil.isNotBlank(name) && entry.getValue() != null && ComponentUtil.getQueryHelper().isApiResponseField(name)) { - final String tagName = convertTagName(name); - buf.append('<'); - buf.append(tagName); - buf.append('>'); - buf.append(escapeXml(entry.getValue())); - buf.append("'); - } - } - buf.append(""); - } - buf.append(""); - if (facetResponse != null && facetResponse.hasFacetResponse()) { - buf.append(""); - // facet field - if (facetResponse.getFieldList() != null) { - for (final Field field : facetResponse.getFieldList()) { - buf.append(""); - for (final Map.Entry entry : field.getValueCountMap().entrySet()) { - buf.append(""); - buf.append(escapeXml(entry.getKey())); - buf.append(""); - } - buf.append(""); - } - } - // facet query - if (facetResponse.getQueryCountMap() != null) { - buf.append(""); - for (final Map.Entry entry : facetResponse.getQueryCountMap().entrySet()) { - buf.append(""); - buf.append(escapeXml(entry.getKey())); - buf.append(""); - } - buf.append(""); - } - buf.append(""); - } - if (moreLikeThisResponse != null && !moreLikeThisResponse.isEmpty()) { - buf.append(""); - for (final Map.Entry>> mltEntry : moreLikeThisResponse.entrySet()) { - buf.append(""); - for (final Map document : mltEntry.getValue()) { - buf.append(""); - for (final Map.Entry entry : document.entrySet()) { - if (StringUtil.isNotBlank(entry.getKey()) && entry.getValue() != null) { - final String tagName = convertTagName(entry.getKey()); - buf.append('<'); - buf.append(tagName); - buf.append('>'); - buf.append(escapeXml(entry.getValue().toString())); - 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 search request.", e); - } - } - - writeXmlResponse(status, buf.toString(), errMsg); - } - - private String convertTagName(final String name) { - final String tagName = StringUtil.decamelize(name).replaceAll("_", "-").toLowerCase(); - return tagName; - } - - protected void processLabelRequest(final HttpServletRequest request, final HttpServletResponse response, final FilterChain chain) { - int status = 0; - String errMsg = StringUtil.EMPTY; - final StringBuilder buf = new StringBuilder(255); - try { - final List> labelTypeItems = ComponentUtil.getLabelTypeHelper().getLabelTypeItemList(); - buf.append(""); - buf.append(labelTypeItems.size()); - buf.append(""); - buf.append(""); - for (final Map labelMap : labelTypeItems) { - buf.append(""); - } - buf.append(""); - } catch (final Exception e) { - status = 1; - errMsg = e.getMessage(); - if (logger.isDebugEnabled()) { - logger.debug("Failed to process a label request.", e); - } - - } - - writeXmlResponse(status, buf.toString(), errMsg); - } - - protected void processSuggestRequest(final HttpServletRequest request, final HttpServletResponse response, final FilterChain chain) { - // TODO - // int status = 0; - // String errMsg = StringUtil.EMPTY; - // final StringBuilder buf = new StringBuilder(255); - // try { - // chain.doFilter(new WebApiRequest(request, SUGGEST_API), new WebApiResponse(response)); - // WebApiUtil.validate(); - // final Integer suggestRecordCount = WebApiUtil.getObject("suggestRecordCount"); - // final List suggestResultList = WebApiUtil.getObject("suggestResultList"); - // final List suggestFieldName = WebApiUtil.getObject("suggestFieldName"); - // - // buf.append(""); - // buf.append(suggestRecordCount); - // buf.append(""); - // if (suggestResultList.size() > 0) { - // buf.append(""); - // - // for (int i = 0; i < suggestResultList.size(); i++) { - // - // final SuggestResponse suggestResponse = suggestResultList.get(i); - // - // for (final Map.Entry> entry : suggestResponse.entrySet()) { - // final SuggestResponseList srList = (SuggestResponseList) entry.getValue(); - // final String fn = suggestFieldName.get(i); - // buf.append(""); - // buf.append(""); - // buf.append(escapeXml(entry.getKey())); - // buf.append(""); - // buf.append(""); - // buf.append(escapeXml(fn)); - // buf.append(""); - // buf.append(""); - // buf.append(escapeXml(Integer.toString(srList.getStartOffset()))); - // buf.append(""); - // buf.append(""); - // buf.append(escapeXml(Integer.toString(srList.getEndOffset()))); - // buf.append(""); - // buf.append(""); - // buf.append(escapeXml(Integer.toString(srList.getNumFound()))); - // buf.append(""); - // buf.append(""); - // for (final String value : srList) { - // buf.append(""); - // buf.append(escapeXml(value)); - // buf.append(""); - // } - // buf.append(""); - // buf.append(""); - // - // } - // } - // buf.append(""); - // } - // } catch (final Exception e) { - // if (e instanceof WebApiException) { - // status = ((WebApiException) e).getStatusCode(); - // } else { - // status = 1; - // } - // errMsg = e.getMessage(); - // if (logger.isDebugEnabled()) { - // logger.debug("Failed to process a suggest request.", e); - // } - // } - // - // writeXmlResponse(status, buf.toString(), errMsg); - } - - protected void writeXmlResponse(final int status, final String body, final String errMsg) { - final StringBuilder buf = new StringBuilder(1000); - buf.append(""); - buf.append(""); - buf.append(""); - buf.append(Constants.WEB_API_VERSION); - buf.append(""); - buf.append(""); - buf.append(status); - buf.append(""); - if (status == 0) { - if (StringUtil.isNotBlank(body)) { - buf.append(body); - } - } else { - buf.append(""); - buf.append(escapeXml(errMsg)); - buf.append(""); - } - buf.append(""); - write(buf.toString(), "text/xml", Constants.UTF_8); - - } - - protected String escapeXml(final Object obj) { - final StringBuilder buf = new StringBuilder(255); - if (obj instanceof List) { - buf.append(""); - for (final Object child : (List) obj) { - buf.append("").append(escapeXml(child)).append(""); - } - buf.append(""); - } else if (obj instanceof Map) { - buf.append(""); - for (final Map.Entry entry : ((Map) obj).entrySet()) { - - buf.append("").append(escapeXml(entry.getKey())).append("").append(escapeXml(entry.getValue())) - .append(""); - } - buf.append(""); - } else if (obj instanceof Date) { - final SimpleDateFormat sdf = new SimpleDateFormat(CoreLibConstants.DATE_FORMAT_ISO_8601_EXTEND); - buf.append(StringEscapeUtils.escapeXml(sdf.format(obj))); - } else if (obj != null) { - buf.append(StringEscapeUtils.escapeXml(obj.toString())); - } - return buf.toString(); - } - -} diff --git a/src/main/java/org/codelibs/fess/app/service/FavoriteLogService.java b/src/main/java/org/codelibs/fess/app/service/FavoriteLogService.java new file mode 100644 index 000000000..e88597ae2 --- /dev/null +++ b/src/main/java/org/codelibs/fess/app/service/FavoriteLogService.java @@ -0,0 +1,62 @@ +package org.codelibs.fess.app.service; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +import javax.annotation.Resource; + +import org.codelibs.fess.es.exbhv.FavoriteLogBhv; +import org.codelibs.fess.es.exbhv.UserInfoBhv; +import org.codelibs.fess.es.exentity.FavoriteLog; +import org.codelibs.fess.helper.SystemHelper; +import org.dbflute.cbean.result.ListResultBean; + +public class FavoriteLogService { + @Resource + protected SystemHelper systemHelper; + + @Resource + protected UserInfoBhv userInfoBhv; + + @Resource + protected FavoriteLogBhv favoriteLogBhv; + + public boolean addUrl(final String userCode, final String url) { + return userInfoBhv.selectEntity(cb -> { + cb.query().setCode_Equal(userCode); + }).map(userInfo -> { + final FavoriteLog favoriteLog = new FavoriteLog(); + favoriteLog.setUserInfoId(userInfo.getId()); + favoriteLog.setUrl(url); + favoriteLog.setCreatedTime(systemHelper.getCurrentTimeAsLong()); + favoriteLogBhv.insert(favoriteLog); + return true; + }).orElse(false); + } + + public List getUrlList(final String userCode, final List urlList) { + if (urlList.isEmpty()) { + return urlList; + } + + return userInfoBhv.selectEntity(cb -> { + cb.query().setCode_Equal(userCode); + }).map(userInfo -> { + final ListResultBean list = favoriteLogBhv.selectList(cb2 -> { + cb2.query().setUserInfoId_Equal(userInfo.getId()); + cb2.query().setUrl_InScope(urlList); + }); + if (!list.isEmpty()) { + final List newUrlList = new ArrayList<>(list.size()); + for (final FavoriteLog favoriteLog : list) { + newUrlList.add(favoriteLog.getUrl()); + } + return newUrlList; + } + return Collections. emptyList(); + }).orElse(Collections. emptyList()); + + } + +} diff --git a/src/main/java/org/codelibs/fess/app/service/SearchService.java b/src/main/java/org/codelibs/fess/app/service/SearchService.java new file mode 100644 index 000000000..c8a409c95 --- /dev/null +++ b/src/main/java/org/codelibs/fess/app/service/SearchService.java @@ -0,0 +1,324 @@ +package org.codelibs.fess.app.service; + +import java.text.NumberFormat; +import java.util.Enumeration; +import java.util.HashSet; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.Set; +import java.util.function.Consumer; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import javax.annotation.Resource; +import javax.servlet.http.HttpServletRequest; + +import org.apache.commons.lang3.StringUtils; +import org.codelibs.core.lang.StringUtil; +import org.codelibs.core.misc.DynamicProperties; +import org.codelibs.fess.Constants; +import org.codelibs.fess.entity.SearchRenderData; +import org.codelibs.fess.entity.SearchRequestParams; +import org.codelibs.fess.es.client.FessEsClient; +import org.codelibs.fess.es.client.FessEsClient.SearchConditionBuilder; +import org.codelibs.fess.es.exentity.SearchLog; +import org.codelibs.fess.es.exentity.UserInfo; +import org.codelibs.fess.helper.FieldHelper; +import org.codelibs.fess.helper.QueryHelper; +import org.codelibs.fess.helper.SearchLogHelper; +import org.codelibs.fess.helper.SystemHelper; +import org.codelibs.fess.helper.UserInfoHelper; +import org.codelibs.fess.util.ComponentUtil; +import org.codelibs.fess.util.QueryResponseList; +import org.dbflute.optional.OptionalEntity; +import org.elasticsearch.index.query.QueryBuilders; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class SearchService { + + // =================================================================================== + // Constant + // + private static final Logger logger = LoggerFactory.getLogger(SearchService.class); + + protected static final Pattern FIELD_EXTRACTION_PATTERN = Pattern.compile("^([a-zA-Z0-9_]+):.*"); + + // =================================================================================== + // Attribute + // + @Resource + protected DynamicProperties crawlerProperties; + + @Resource + protected FessEsClient fessEsClient; + + @Resource + protected SystemHelper systemHelper; + + @Resource + protected FieldHelper fieldHelper; + + @Resource + protected QueryHelper queryHelper; + + @Resource + protected UserInfoHelper userInfoHelper; + + // =================================================================================== + // Method + // ============== + + public void search(final HttpServletRequest request, final SearchRequestParams params, final SearchRenderData data) { + final long startTime = System.currentTimeMillis(); + final boolean searchLogSupport = + Constants.TRUE.equals(crawlerProperties.getProperty(Constants.SEARCH_LOG_PROPERTY, Constants.TRUE)); + + if (StringUtil.isNotBlank(params.getOperator())) { + request.setAttribute(Constants.DEFAULT_OPERATOR, params.getOperator()); + } + + final StringBuilder queryBuf = new StringBuilder(255); + if (StringUtil.isNotBlank(params.getQuery())) { + if (params.getQuery().indexOf(" OR ") >= 0) { + queryBuf.append('(').append(params.getQuery()).append(')'); + } else { + queryBuf.append(params.getQuery()); + } + } + if (params.getAdditional() != null) { + appendAdditionalQuery(params.getAdditional(), additional -> { + queryBuf.append(' ').append(additional); + }); + } + params.getFields().entrySet().stream().forEach(entry -> { + appendQueries(queryBuf, entry.getKey(), entry.getValue()); + }); + if (StringUtil.isNotBlank(params.getSort())) { + queryBuf.append(" sort:").append(params.getSort()); + } + if (params.getLanguages() != null) { + appendQueries(queryBuf, fieldHelper.langField, params.getLanguages()); + } + + final String query = queryBuf.toString().trim(); + + final int pageStart = params.getStartPosition(); + final int pageSize = params.getPageSize(); + final List> documentItems = + fessEsClient.search( + fieldHelper.docIndex, + fieldHelper.docType, + searchRequestBuilder -> { + return SearchConditionBuilder.builder(searchRequestBuilder).query(query).offset(pageStart).size(pageSize) + .facetInfo(params.getFacetInfo()).geoInfo(params.getGeoInfo()) + .responseFields(queryHelper.getResponseFields()).build(); + }, (searchRequestBuilder, execTime, searchResponse) -> { + final QueryResponseList queryResponseList = ComponentUtil.getQueryResponseList(); + queryResponseList.init(searchResponse, pageStart, pageSize); + return queryResponseList; + }); + data.setDocumentItems(documentItems); + + // search + final QueryResponseList queryResponseList = (QueryResponseList) documentItems; + data.setFacetResponse(queryResponseList.getFacetResponse()); + + final String[] highlightQueries = (String[]) request.getAttribute(Constants.HIGHLIGHT_QUERIES); + if (highlightQueries != null) { + final StringBuilder buf = new StringBuilder(100); + for (final String q : highlightQueries) { + buf.append("&hq=").append(q); + } + data.setAppendHighlightParams(buf.toString()); + } + + // search log + if (searchLogSupport) { + storeSearchLog(request, query, pageStart, pageSize, queryResponseList); + } + + queryResponseList.setExecTime(System.currentTimeMillis() - startTime); + final NumberFormat nf = NumberFormat.getInstance(request.getLocale()); + nf.setMaximumIntegerDigits(2); + nf.setMaximumFractionDigits(2); + String execTime; + try { + execTime = nf.format((double) queryResponseList.getExecTime() / 1000); + } catch (final Exception e) { + execTime = StringUtil.EMPTY; + } + data.setExecTime(execTime); + + data.setPageSize(queryResponseList.getPageSize()); + data.setCurrentPageNumber(queryResponseList.getCurrentPageNumber()); + data.setAllRecordCount(queryResponseList.getAllRecordCount()); + data.setAllPageCount(queryResponseList.getAllPageCount()); + data.setExistNextPage(queryResponseList.isExistNextPage()); + data.setExistPrevPage(queryResponseList.isExistPrevPage()); + data.setCurrentStartRecordNumber(queryResponseList.getCurrentStartRecordNumber()); + data.setCurrentEndRecordNumber(queryResponseList.getCurrentEndRecordNumber()); + data.setPageNumberList(queryResponseList.getPageNumberList()); + data.setPartialResults(queryResponseList.isPartialResults()); + data.setQueryTime(queryResponseList.getQueryTime()); + data.setSearchQuery(query); + } + + protected void storeSearchLog(final HttpServletRequest request, final String query, final int pageStart, final int pageSize, + final QueryResponseList queryResponseList) { + final long now = systemHelper.getCurrentTimeAsLong(); + + final SearchLogHelper searchLogHelper = ComponentUtil.getSearchLogHelper(); + final SearchLog searchLog = new SearchLog(); + + String userCode = null; + if (Constants.TRUE.equals(crawlerProperties.getProperty(Constants.USER_INFO_PROPERTY, Constants.TRUE))) { + userCode = userInfoHelper.getUserCode(); + if (StringUtil.isNotBlank(userCode)) { + final UserInfo userInfo = new UserInfo(); + userInfo.setCode(userCode); + userInfo.setCreatedTime(now); + userInfo.setUpdatedTime(now); + searchLog.setUserInfo(OptionalEntity.of(userInfo)); + } + } + + searchLog.setHitCount(queryResponseList.getAllRecordCount()); + searchLog.setResponseTime(Integer.valueOf((int) queryResponseList.getExecTime())); + searchLog.setSearchWord(StringUtils.abbreviate(query, 1000)); + searchLog.setSearchQuery(StringUtils.abbreviate(queryResponseList.getSearchQuery(), 1000)); + searchLog.setRequestedTime(now); + searchLog.setQueryOffset(pageStart); + searchLog.setQueryPageSize(pageSize); + + searchLog.setClientIp(StringUtils.abbreviate(request.getRemoteAddr(), 50)); + searchLog.setReferer(StringUtils.abbreviate(request.getHeader("referer"), 1000)); + searchLog.setUserAgent(StringUtils.abbreviate(request.getHeader("user-agent"), 255)); + if (userCode != null) { + searchLog.setUserSessionId(userCode); + } + final Object accessType = request.getAttribute(Constants.SEARCH_LOG_ACCESS_TYPE); + if (Constants.SEARCH_LOG_ACCESS_TYPE_JSON.equals(accessType)) { + searchLog.setAccessType(Constants.SEARCH_LOG_ACCESS_TYPE_JSON); + } else if (Constants.SEARCH_LOG_ACCESS_TYPE_XML.equals(accessType)) { + searchLog.setAccessType(Constants.SEARCH_LOG_ACCESS_TYPE_XML); + } else if (Constants.SEARCH_LOG_ACCESS_TYPE_OTHER.equals(accessType)) { + searchLog.setAccessType(Constants.SEARCH_LOG_ACCESS_TYPE_OTHER); + } else { + searchLog.setAccessType(Constants.SEARCH_LOG_ACCESS_TYPE_WEB); + } + + @SuppressWarnings("unchecked") + final Map> fieldLogMap = (Map>) request.getAttribute(Constants.FIELD_LOGS); + if (fieldLogMap != null) { + for (final Map.Entry> logEntry : fieldLogMap.entrySet()) { + for (final String value : logEntry.getValue()) { + searchLog.addSearchFieldLogValue(logEntry.getKey(), StringUtils.abbreviate(value, 1000)); + } + } + } + + searchLogHelper.addSearchLog(searchLog); + } + + public String[] getLanguages(final HttpServletRequest request, final SearchRequestParams params) { + if (params.getLanguages() != null) { + final Set langSet = new HashSet<>(); + for (final String lang : params.getLanguages()) { + if (StringUtil.isNotBlank(lang) && lang.length() < 1000) { + if (Constants.ALL_LANGUAGES.equalsIgnoreCase(lang)) { + langSet.add(Constants.ALL_LANGUAGES); + } else { + final String normalizeLang = systemHelper.normalizeLang(lang); + if (normalizeLang != null) { + langSet.add(normalizeLang); + } + } + } + } + if (langSet.size() > 1 && langSet.contains(Constants.ALL_LANGUAGES)) { + return new String[] { Constants.ALL_LANGUAGES }; + } else { + langSet.remove(Constants.ALL_LANGUAGES); + } + return langSet.toArray(new String[langSet.size()]); + } else if (Constants.TRUE.equals(crawlerProperties.getProperty(Constants.USE_BROWSER_LOCALE_FOR_SEARCH_PROPERTY, Constants.FALSE))) { + final Set langSet = new HashSet<>(); + final Enumeration locales = request.getLocales(); + if (locales != null) { + while (locales.hasMoreElements()) { + final Locale locale = locales.nextElement(); + final String normalizeLang = systemHelper.normalizeLang(locale.toString()); + if (normalizeLang != null) { + langSet.add(normalizeLang); + } + } + if (!langSet.isEmpty()) { + return langSet.toArray(new String[langSet.size()]); + } + } + } + return StringUtil.EMPTY_STRINGS; + } + + protected void appendQueries(final StringBuilder queryBuf, final String key, final String[] values) { + if (values.length == 1) { + queryBuf.append(' ').append(key).append(":\"").append(values[0]).append('\"'); + } else if (values.length > 1) { + boolean first = true; + queryBuf.append(" ("); + for (final String value : values) { + if (first) { + first = false; + } else { + queryBuf.append(" OR "); + } + queryBuf.append(key).append(":\"").append(value).append('\"'); + } + queryBuf.append(')'); + } + } + + public void appendAdditionalQuery(final String[] additionalQueries, final Consumer consumer) { + final Set fieldSet = new HashSet<>(); + for (final String additional : additionalQueries) { + if (StringUtil.isNotBlank(additional) && additional.length() < 1000 && !hasFieldInQuery(fieldSet, additional)) { + consumer.accept(additional); + } + } + } + + protected boolean hasFieldInQuery(final Set fieldSet, final String query) { + final Matcher matcher = FIELD_EXTRACTION_PATTERN.matcher(query); + if (matcher.matches()) { + final String field = matcher.replaceFirst("$1"); + if (fieldSet.contains(field)) { + return true; + } + fieldSet.add(field); + } + return false; + } + + public OptionalEntity> getDocumentByDocId(final String docId, final String[] fields) { + return fessEsClient.getDocument(fieldHelper.docIndex, fieldHelper.docType, builder -> { + builder.setQuery(QueryBuilders.termQuery(fieldHelper.docIdField, docId)); + builder.addFields(fields); + return true; + }); + } + + public List> getDocumentListByDocIds(final String[] docIds, final String[] fields) { + return fessEsClient.getDocumentList(fieldHelper.docIndex, fieldHelper.docType, builder -> { + builder.setQuery(QueryBuilders.termsQuery(fieldHelper.docIdField, docIds)); + builder.setSize(queryHelper.getMaxPageSize()); + builder.addFields(fields); + return true; + }); + } + + public boolean update(final String id, final String field, final Object value) { + return fessEsClient.update(fieldHelper.docIndex, fieldHelper.docType, id, field, value); + } +} diff --git a/src/main/java/org/codelibs/fess/app/web/admin/dataconfig/AdminDataconfigAction.java b/src/main/java/org/codelibs/fess/app/web/admin/dataconfig/AdminDataconfigAction.java index 2ae39edc3..b6409985e 100644 --- a/src/main/java/org/codelibs/fess/app/web/admin/dataconfig/AdminDataconfigAction.java +++ b/src/main/java/org/codelibs/fess/app/web/admin/dataconfig/AdminDataconfigAction.java @@ -30,9 +30,6 @@ import org.codelibs.fess.app.service.DataConfigService; import org.codelibs.fess.app.service.LabelTypeService; import org.codelibs.fess.app.service.RoleTypeService; import org.codelibs.fess.app.web.CrudMode; -import org.codelibs.fess.app.web.admin.dataconfig.CreateForm; -import org.codelibs.fess.app.web.admin.dataconfig.EditForm; -import org.codelibs.fess.app.web.admin.dataconfig.SearchForm; import org.codelibs.fess.app.web.base.FessAdminAction; import org.codelibs.fess.ds.DataStoreFactory; import org.codelibs.fess.es.exentity.DataConfig; diff --git a/src/main/java/org/codelibs/fess/app/web/admin/dict/AdminDictAction.java b/src/main/java/org/codelibs/fess/app/web/admin/dict/AdminDictAction.java index 0292fb539..a06a9fe52 100644 --- a/src/main/java/org/codelibs/fess/app/web/admin/dict/AdminDictAction.java +++ b/src/main/java/org/codelibs/fess/app/web/admin/dict/AdminDictAction.java @@ -55,7 +55,7 @@ public class AdminDictAction extends FessAdminAction { @Execute public HtmlResponse index(final ListForm form) { return asHtml(path_AdminDict_IndexJsp).renderWith(data -> { - DictionaryFile[] dictFiles = dictionaryManager.getDictionaryFiles(); + final DictionaryFile[] dictFiles = dictionaryManager.getDictionaryFiles(); data.register("dictFiles", dictFiles); }); } diff --git a/src/main/java/org/codelibs/fess/app/web/admin/dict/kuromoji/AdminDictKuromojiAction.java b/src/main/java/org/codelibs/fess/app/web/admin/dict/kuromoji/AdminDictKuromojiAction.java index 24c42d811..85c968138 100644 --- a/src/main/java/org/codelibs/fess/app/web/admin/dict/kuromoji/AdminDictKuromojiAction.java +++ b/src/main/java/org/codelibs/fess/app/web/admin/dict/kuromoji/AdminDictKuromojiAction.java @@ -311,7 +311,7 @@ public class AdminDictKuromojiAction extends FessAdminAction { return kuromojiService.getKuromojiFile(form.dictId).map(file -> { try (InputStream inputStream = form.kuromojiFile.getInputStream()) { file.update(inputStream); - } catch (IOException e) { + } catch (final IOException e) { throwValidationError(messages -> messages.addErrorsFailedToUploadKuromojiFile(GLOBAL), () -> { return redirectWith(getClass(), moreUrl("uploadpage/" + form.dictId)); }); @@ -381,7 +381,7 @@ public class AdminDictKuromojiAction extends FessAdminAction { // Assist Logic // ============ - protected OptionalEntity createKuromojiItem(CreateForm form) { + protected OptionalEntity createKuromojiItem(final CreateForm form) { switch (form.crudMode) { case CrudMode.CREATE: if (form instanceof CreateForm) { diff --git a/src/main/java/org/codelibs/fess/app/web/admin/dict/synonym/AdminDictSynonymAction.java b/src/main/java/org/codelibs/fess/app/web/admin/dict/synonym/AdminDictSynonymAction.java index 9a2ebf4b8..c61559d9f 100644 --- a/src/main/java/org/codelibs/fess/app/web/admin/dict/synonym/AdminDictSynonymAction.java +++ b/src/main/java/org/codelibs/fess/app/web/admin/dict/synonym/AdminDictSynonymAction.java @@ -323,7 +323,7 @@ public class AdminDictSynonymAction extends FessAdminAction { return synonymService.getSynonymFile(form.dictId).map(file -> { try (InputStream inputStream = form.synonymFile.getInputStream()) { file.update(inputStream); - } catch (IOException e) { + } catch (final IOException e) { throwValidationError(messages -> messages.addErrorsFailedToUploadSynonymFile(GLOBAL), () -> { return redirectWith(getClass(), moreUrl("uploadpage/" + form.dictId)); }); @@ -397,7 +397,7 @@ public class AdminDictSynonymAction extends FessAdminAction { // Assist Logic // ============ - protected OptionalEntity createSynonymItem(CreateForm form) { + protected OptionalEntity createSynonymItem(final CreateForm form) { switch (form.crudMode) { case CrudMode.CREATE: if (form instanceof CreateForm) { @@ -439,11 +439,11 @@ public class AdminDictSynonymAction extends FessAdminAction { }; } - private void validateSynonymString(String[] values, VaErrorHook hook) { + private void validateSynonymString(final String[] values, final VaErrorHook hook) { if (values.length == 0) { return; } - for (String value : values) { + for (final String value : values) { if (value.indexOf(',') >= 0) { throwValidationError(messages -> { messages.addErrorsInvalidStrIsIncluded(GLOBAL, value, ","); diff --git a/src/main/java/org/codelibs/fess/app/web/admin/fileconfig/AdminFileconfigAction.java b/src/main/java/org/codelibs/fess/app/web/admin/fileconfig/AdminFileconfigAction.java index 8b83f685a..f1785a7e9 100644 --- a/src/main/java/org/codelibs/fess/app/web/admin/fileconfig/AdminFileconfigAction.java +++ b/src/main/java/org/codelibs/fess/app/web/admin/fileconfig/AdminFileconfigAction.java @@ -25,9 +25,6 @@ import org.codelibs.fess.app.service.FileConfigService; import org.codelibs.fess.app.service.LabelTypeService; import org.codelibs.fess.app.service.RoleTypeService; import org.codelibs.fess.app.web.CrudMode; -import org.codelibs.fess.app.web.admin.fileconfig.CreateForm; -import org.codelibs.fess.app.web.admin.fileconfig.EditForm; -import org.codelibs.fess.app.web.admin.fileconfig.SearchForm; import org.codelibs.fess.app.web.base.FessAdminAction; import org.codelibs.fess.es.exentity.FileConfig; import org.codelibs.fess.helper.SystemHelper; diff --git a/src/main/java/org/codelibs/fess/app/web/admin/general/AdminGeneralAction.java b/src/main/java/org/codelibs/fess/app/web/admin/general/AdminGeneralAction.java index 77019277c..ce026587c 100644 --- a/src/main/java/org/codelibs/fess/app/web/admin/general/AdminGeneralAction.java +++ b/src/main/java/org/codelibs/fess/app/web/admin/general/AdminGeneralAction.java @@ -148,12 +148,12 @@ public class AdminGeneralAction extends FessAdminAction { form.esHttpUrl = crawlerProperties.getProperty(Constants.ELASTICSEARCH_WEB_URL_PROPERTY, Constants.ELASTICSEARCH_WEB_URL); } - private Integer getPropertyAsInteger(String key, int defaultValue) { - String value = crawlerProperties.getProperty(Constants.CRAWLING_THREAD_COUNT_PROPERTY); + private Integer getPropertyAsInteger(final String key, final int defaultValue) { + final String value = crawlerProperties.getProperty(Constants.CRAWLING_THREAD_COUNT_PROPERTY); if (value != null) { try { return Integer.valueOf(value); - } catch (NumberFormatException e) { + } catch (final NumberFormatException e) { // ignore } } diff --git a/src/main/java/org/codelibs/fess/app/web/admin/keymatch/AdminKeymatchAction.java b/src/main/java/org/codelibs/fess/app/web/admin/keymatch/AdminKeymatchAction.java index 1f5bcf466..a866e40fe 100644 --- a/src/main/java/org/codelibs/fess/app/web/admin/keymatch/AdminKeymatchAction.java +++ b/src/main/java/org/codelibs/fess/app/web/admin/keymatch/AdminKeymatchAction.java @@ -163,7 +163,7 @@ public class AdminKeymatchAction extends FessAdminAction { public HtmlResponse editfromconfirm(final EditForm form) { validate(form, messages -> {}, toEditHtml()); form.crudMode = CrudMode.EDIT; - String id = form.id; + final String id = form.id; keyMatchService.getKeyMatch(id).ifPresent(entity -> { copyBeanToBean(entity, form, op -> {}); }).orElse(() -> { @@ -195,7 +195,7 @@ public class AdminKeymatchAction extends FessAdminAction { public HtmlResponse deletefromconfirm(final EditForm form) { form.crudMode = CrudMode.DELETE; validate(form, messages -> {}, toEditHtml()); - String id = form.id; + final String id = form.id; keyMatchService.getKeyMatch(id).ifPresent(entity -> { copyBeanToBean(entity, form, op -> {}); }).orElse(() -> { @@ -279,7 +279,7 @@ public class AdminKeymatchAction extends FessAdminAction { public HtmlResponse delete(final EditForm form) { verifyCrudMode(form.crudMode, CrudMode.DELETE); validate(form, messages -> {}, toEditHtml()); - String id = form.id; + final String id = form.id; keyMatchService.getKeyMatch(id).ifPresent(entity -> { keyMatchService.delete(entity); saveInfo(messages -> messages.addSuccessCrudDeleteCrudTable(GLOBAL)); @@ -300,7 +300,7 @@ public class AdminKeymatchAction extends FessAdminAction { switch (form.crudMode) { case CrudMode.CREATE: if (form instanceof CreateForm) { - KeyMatch entity = new KeyMatch(); + final KeyMatch entity = new KeyMatch(); entity.setCreatedBy(username); entity.setCreatedTime(currentTime); entity.setUpdatedBy(username); diff --git a/src/main/java/org/codelibs/fess/app/web/admin/labeltype/AdminLabeltypeAction.java b/src/main/java/org/codelibs/fess/app/web/admin/labeltype/AdminLabeltypeAction.java index 7b8642dbd..f76cb1643 100644 --- a/src/main/java/org/codelibs/fess/app/web/admin/labeltype/AdminLabeltypeAction.java +++ b/src/main/java/org/codelibs/fess/app/web/admin/labeltype/AdminLabeltypeAction.java @@ -24,9 +24,6 @@ import org.codelibs.fess.app.pager.LabelTypePager; import org.codelibs.fess.app.service.LabelTypeService; import org.codelibs.fess.app.service.RoleTypeService; import org.codelibs.fess.app.web.CrudMode; -import org.codelibs.fess.app.web.admin.labeltype.CreateForm; -import org.codelibs.fess.app.web.admin.labeltype.EditForm; -import org.codelibs.fess.app.web.admin.labeltype.SearchForm; import org.codelibs.fess.app.web.base.FessAdminAction; import org.codelibs.fess.es.exentity.LabelType; import org.codelibs.fess.helper.SystemHelper; diff --git a/src/main/java/org/codelibs/fess/app/web/admin/requestheader/AdminRequestheaderAction.java b/src/main/java/org/codelibs/fess/app/web/admin/requestheader/AdminRequestheaderAction.java index b20cb2f80..e4c28a275 100644 --- a/src/main/java/org/codelibs/fess/app/web/admin/requestheader/AdminRequestheaderAction.java +++ b/src/main/java/org/codelibs/fess/app/web/admin/requestheader/AdminRequestheaderAction.java @@ -30,9 +30,6 @@ import org.codelibs.fess.app.pager.RequestHeaderPager; import org.codelibs.fess.app.service.RequestHeaderService; import org.codelibs.fess.app.service.WebConfigService; import org.codelibs.fess.app.web.CrudMode; -import org.codelibs.fess.app.web.admin.requestheader.CreateForm; -import org.codelibs.fess.app.web.admin.requestheader.EditForm; -import org.codelibs.fess.app.web.admin.requestheader.SearchForm; import org.codelibs.fess.app.web.base.FessAdminAction; import org.codelibs.fess.es.exentity.RequestHeader; import org.codelibs.fess.es.exentity.WebConfig; diff --git a/src/main/java/org/codelibs/fess/app/web/admin/webconfig/AdminWebconfigAction.java b/src/main/java/org/codelibs/fess/app/web/admin/webconfig/AdminWebconfigAction.java index c575d0b08..74c00ce21 100644 --- a/src/main/java/org/codelibs/fess/app/web/admin/webconfig/AdminWebconfigAction.java +++ b/src/main/java/org/codelibs/fess/app/web/admin/webconfig/AdminWebconfigAction.java @@ -21,13 +21,10 @@ import javax.annotation.Resource; import org.codelibs.fess.Constants; import org.codelibs.fess.annotation.Token; import org.codelibs.fess.app.pager.WebConfigPager; -import org.codelibs.fess.app.service.WebConfigService; import org.codelibs.fess.app.service.LabelTypeService; import org.codelibs.fess.app.service.RoleTypeService; +import org.codelibs.fess.app.service.WebConfigService; import org.codelibs.fess.app.web.CrudMode; -import org.codelibs.fess.app.web.admin.webconfig.CreateForm; -import org.codelibs.fess.app.web.admin.webconfig.EditForm; -import org.codelibs.fess.app.web.admin.webconfig.SearchForm; import org.codelibs.fess.app.web.base.FessAdminAction; import org.codelibs.fess.es.exentity.WebConfig; import org.codelibs.fess.helper.SystemHelper; diff --git a/src/main/java/org/codelibs/fess/app/web/go/GoForm.java b/src/main/java/org/codelibs/fess/app/web/go/GoForm.java index a1925d237..da1426e42 100644 --- a/src/main/java/org/codelibs/fess/app/web/go/GoForm.java +++ b/src/main/java/org/codelibs/fess/app/web/go/GoForm.java @@ -1,8 +1,6 @@ package org.codelibs.fess.app.web.go; public class GoForm { - private static final long serialVersionUID = 1L; - //@Required(target = "go,cache") //@Maxbytelength(maxbytelength = 100) public String docId; diff --git a/src/main/java/org/codelibs/fess/app/web/search/SearchAction.java b/src/main/java/org/codelibs/fess/app/web/search/SearchAction.java index 054da6035..d6c967578 100644 --- a/src/main/java/org/codelibs/fess/app/web/search/SearchAction.java +++ b/src/main/java/org/codelibs/fess/app/web/search/SearchAction.java @@ -16,34 +16,24 @@ package org.codelibs.fess.app.web.search; -import java.text.NumberFormat; -import java.time.Clock; import java.util.ArrayList; -import java.util.Enumeration; import java.util.HashSet; import java.util.List; -import java.util.Locale; import java.util.Map; import java.util.Set; -import java.util.regex.Matcher; -import java.util.regex.Pattern; +import javax.annotation.Resource; import javax.servlet.http.HttpSession; -import org.apache.commons.lang3.StringUtils; import org.codelibs.core.lang.StringUtil; import org.codelibs.fess.Constants; +import org.codelibs.fess.app.service.SearchService; import org.codelibs.fess.app.web.RootAction; import org.codelibs.fess.app.web.base.FessSearchAction; -import org.codelibs.fess.es.client.FessEsClient.SearchConditionBuilder; -import org.codelibs.fess.es.exentity.SearchLog; -import org.codelibs.fess.es.exentity.UserInfo; +import org.codelibs.fess.entity.SearchRenderData; import org.codelibs.fess.exception.InvalidQueryException; import org.codelibs.fess.exception.ResultOffsetExceededException; -import org.codelibs.fess.helper.SearchLogHelper; -import org.codelibs.fess.util.ComponentUtil; -import org.codelibs.fess.util.QueryResponseList; -import org.dbflute.optional.OptionalEntity; +import org.codelibs.fess.util.FacetResponse; import org.lastaflute.taglib.function.LaFunctions; import org.lastaflute.web.Execute; import org.lastaflute.web.response.HtmlResponse; @@ -59,17 +49,11 @@ public class SearchAction extends FessSearchAction { // private static final Logger logger = LoggerFactory.getLogger(SearchAction.class); - protected static final long DEFAULT_START_COUNT = 0; - - protected static final int MAX_PAGE_SIZE = 100; - - private static final int DEFAULT_PAGE_SIZE = 20; - - protected static final Pattern FIELD_EXTRACTION_PATTERN = Pattern.compile("^([a-zA-Z0-9_]+):.*"); - // =================================================================================== // Attribute // + @Resource + protected SearchService searchService; // =================================================================================== // Hook @@ -85,15 +69,16 @@ public class SearchAction extends FessSearchAction { @Execute public HtmlResponse search(final SearchForm form) { - if (viewHelper.isUseSession() && StringUtil.isNotBlank(form.num)) { - normalizePageNum(form); - final HttpSession session = request.getSession(); - if (session != null) { - session.setAttribute(Constants.RESULTS_PER_PAGE, form.num); - } + final HtmlResponse response = doSearch(form); + if (viewHelper.isUseSession()) { + LaRequestUtil.getOptionalRequest().ifPresent(request -> { + final HttpSession session = request.getSession(false); + if (session != null) { + session.setAttribute(Constants.RESULTS_PER_PAGE, form.num); + } + }); } - - return doSearch(form); + return response; } @Execute @@ -145,299 +130,60 @@ public class SearchAction extends FessSearchAction { return asHtml(path_SearchJsp).renderWith(data -> { updateSearchParams(form); buildLabelParams(form.fields); - doSearchInternal(data, form); + form.lang = searchService.getLanguages(request, form); + try { + final WebRenderData renderData = new WebRenderData(data); + searchService.search(request, form, renderData); + // favorite or screenshot + if (favoriteSupport || screenShotManager != null) { + final String searchQuery = renderData.getSearchQuery(); + final List> documentItems = renderData.getDocumentItems(); + form.queryId = userInfoHelper.generateQueryId(searchQuery, documentItems); + if (screenShotManager != null) { + screenShotManager.storeRequest(form.queryId, documentItems); + data.register("screenShotSupport", true); + } + } + } catch (final InvalidQueryException e) { + if (logger.isDebugEnabled()) { + logger.debug(e.getMessage(), e); + } + throwValidationError(e.getMessageCode(), () -> asHtml(path_ErrorJsp)); + } catch (final ResultOffsetExceededException e) { + if (logger.isDebugEnabled()) { + logger.debug(e.getMessage(), e); + } + throwValidationError(messages -> { + messages.addErrorsResultSizeExceeded(GLOBAL); + }, () -> asHtml(path_ErrorJsp)); + } + form.rt = Long.toString(systemHelper.getCurrentTimeAsLong()); data.register("displayQuery", getDisplayQuery(form, labelTypeHelper.getLabelTypeItemList())); data.register("pagingQuery", getPagingQuery(form)); }); } protected HtmlResponse doMove(final SearchForm form, final int move) { - int pageNum = getDefaultPageSize(); - if (StringUtil.isBlank(form.num)) { - form.num = String.valueOf(getDefaultPageSize()); - } else { + int start = queryHelper.getDefaultStart(); + if (StringUtil.isNotBlank(form.pn)) { try { - pageNum = Integer.parseInt(form.num); - } catch (final NumberFormatException e) { - form.num = String.valueOf(getDefaultPageSize()); - } - } - - if (StringUtil.isBlank(form.pn)) { - form.start = String.valueOf(DEFAULT_START_COUNT); - } else { - Integer pageNumber = Integer.parseInt(form.pn); - if (pageNumber != null && pageNumber > 0) { - pageNumber = pageNumber + move; - if (pageNumber < 1) { - pageNumber = 1; + int pageNumber = Integer.parseInt(form.pn); + if (pageNumber > 0) { + pageNumber = pageNumber + move; + if (pageNumber < 1) { + pageNumber = 1; + } + start = (pageNumber - 1) * form.getPageSize(); } - form.start = String.valueOf((pageNumber - 1) * pageNum); - } else { - form.start = String.valueOf(DEFAULT_START_COUNT); + } catch (final NumberFormatException e) { + // ignore } } + form.start = String.valueOf(start); return doSearch(form); } - protected String doSearchInternal(final RenderData data, final SearchForm form) { - final StringBuilder queryBuf = new StringBuilder(255); - if (StringUtil.isNotBlank(form.query)) { - queryBuf.append(form.query); - } - if (StringUtil.isNotBlank(form.op)) { - request.setAttribute(Constants.DEFAULT_OPERATOR, form.op); - } - if (queryBuf.indexOf(" OR ") >= 0) { - queryBuf.insert(0, '(').append(')'); - } - if (form.additional != null) { - final Set fieldSet = new HashSet(); - for (final String additional : form.additional) { - if (StringUtil.isNotBlank(additional) && additional.length() < 1000 && !hasFieldInQuery(fieldSet, additional)) { - queryBuf.append(' ').append(additional); - } - } - } - if (!form.fields.isEmpty()) { - for (final Map.Entry entry : form.fields.entrySet()) { - final List valueList = new ArrayList(); - final String[] values = entry.getValue(); - if (values != null) { - for (final String v : values) { - valueList.add(v); - } - } - if (valueList.size() == 1) { - queryBuf.append(' ').append(entry.getKey()).append(":\"").append(valueList.get(0)).append('\"'); - } else if (valueList.size() > 1) { - queryBuf.append(" ("); - for (int i = 0; i < valueList.size(); i++) { - if (i != 0) { - queryBuf.append(" OR"); - } - queryBuf.append(' ').append(entry.getKey()).append(":\"").append(valueList.get(i)).append('\"'); - } - queryBuf.append(')'); - } - - } - } - if (StringUtil.isNotBlank(form.sort)) { - queryBuf.append(" sort:").append(form.sort); - } - if (form.lang != null) { - final Set langSet = new HashSet<>(); - for (final String lang : form.lang) { - if (StringUtil.isNotBlank(lang) && lang.length() < 1000) { - if (Constants.ALL_LANGUAGES.equalsIgnoreCase(lang)) { - langSet.add(Constants.ALL_LANGUAGES); - } else { - final String normalizeLang = systemHelper.normalizeLang(lang); - if (normalizeLang != null) { - langSet.add(normalizeLang); - } - } - } - } - if (langSet.size() > 1 && langSet.contains(Constants.ALL_LANGUAGES)) { - langSet.clear(); - form.lang = new String[] { Constants.ALL_LANGUAGES }; - } else { - langSet.remove(Constants.ALL_LANGUAGES); - } - appendLangQuery(queryBuf, langSet); - } else if (Constants.TRUE.equals(crawlerProperties.getProperty(Constants.USE_BROWSER_LOCALE_FOR_SEARCH_PROPERTY, Constants.FALSE))) { - final Set langSet = new HashSet<>(); - final Enumeration locales = request.getLocales(); - if (locales != null) { - while (locales.hasMoreElements()) { - final Locale locale = locales.nextElement(); - final String normalizeLang = systemHelper.normalizeLang(locale.toString()); - if (normalizeLang != null) { - langSet.add(normalizeLang); - } - } - if (!langSet.isEmpty()) { - appendLangQuery(queryBuf, langSet); - } - } - } - - final String query = queryBuf.toString().trim(); - - // init pager - if (StringUtil.isBlank(form.start)) { - form.start = String.valueOf(DEFAULT_START_COUNT); - } else { - try { - Integer.parseInt(form.start); - } catch (final NumberFormatException e) { - form.start = String.valueOf(DEFAULT_START_COUNT); - } - } - if (StringUtil.isBlank(form.num)) { - form.num = String.valueOf(getDefaultPageSize()); - } - normalizePageNum(form); - - final int pageStart = Integer.parseInt(form.start); - final int pageNum = Integer.parseInt(form.num); - List> documentItems = null; - try { - documentItems = - fessEsClient.search(fieldHelper.docIndex, fieldHelper.docType, - searchRequestBuilder -> { - return SearchConditionBuilder.builder(searchRequestBuilder).query(query).offset(pageStart).size(pageNum) - .facetInfo(form.facet).geoInfo(form.geo).responseFields(queryHelper.getResponseFields()).build(); - }, (searchRequestBuilder, execTime, searchResponse) -> { - final QueryResponseList queryResponseList = ComponentUtil.getQueryResponseList(); - queryResponseList.init(searchResponse, pageStart, pageNum); - return queryResponseList; - }); - } catch (final InvalidQueryException e) { - if (logger.isDebugEnabled()) { - logger.debug(e.getMessage(), e); - } - throwValidationError(e.getMessageCode(), () -> asHtml(path_ErrorJsp)); - } catch (final ResultOffsetExceededException e) { - if (logger.isDebugEnabled()) { - logger.debug(e.getMessage(), e); - } - throwValidationError(messages -> { - messages.addErrorsResultSizeExceeded(GLOBAL); - }, () -> asHtml(path_ErrorJsp)); - } - data.register("documentItems", documentItems); - - // search - final QueryResponseList queryResponseList = (QueryResponseList) documentItems; - data.register("facetResponse", queryResponseList.getFacetResponse()); - final NumberFormat nf = NumberFormat.getInstance(LaRequestUtil.getRequest().getLocale()); - nf.setMaximumIntegerDigits(2); - nf.setMaximumFractionDigits(2); - String execTime; - try { - execTime = nf.format((double) queryResponseList.getExecTime() / 1000); - } catch (final Exception e) { - execTime = StringUtil.EMPTY; - } - data.register("execTime", execTime); - - final Clock clock = Clock.systemDefaultZone(); - form.rt = Long.toString(clock.millis()); - - // favorite - if (favoriteSupport || screenShotManager != null) { - form.queryId = userInfoHelper.generateQueryId(query, documentItems); - if (screenShotManager != null) { - screenShotManager.storeRequest(form.queryId, documentItems); - data.register("screenShotSupport", true); - } - } - - // search log - if (searchLogSupport) { - final long now = systemHelper.getCurrentTimeAsLong(); - - final SearchLogHelper searchLogHelper = ComponentUtil.getSearchLogHelper(); - final SearchLog searchLog = new SearchLog(); - - String userCode = null; - if (Constants.TRUE.equals(crawlerProperties.getProperty(Constants.USER_INFO_PROPERTY, Constants.TRUE))) { - userCode = userInfoHelper.getUserCode(); - if (StringUtil.isNotBlank(userCode)) { - final UserInfo userInfo = new UserInfo(); - userInfo.setCode(userCode); - userInfo.setCreatedTime(now); - userInfo.setUpdatedTime(now); - searchLog.setUserInfo(OptionalEntity.of(userInfo)); - } - } - - searchLog.setHitCount(queryResponseList.getAllRecordCount()); - searchLog.setResponseTime(Integer.valueOf((int) queryResponseList.getExecTime())); - searchLog.setSearchWord(StringUtils.abbreviate(query, 1000)); - searchLog.setSearchQuery(StringUtils.abbreviate(queryResponseList.getSearchQuery(), 1000)); - searchLog.setRequestedTime(now); - searchLog.setQueryOffset(pageStart); - searchLog.setQueryPageSize(pageNum); - - searchLog.setClientIp(StringUtils.abbreviate(request.getRemoteAddr(), 50)); - searchLog.setReferer(StringUtils.abbreviate(request.getHeader("referer"), 1000)); - searchLog.setUserAgent(StringUtils.abbreviate(request.getHeader("user-agent"), 255)); - if (userCode != null) { - searchLog.setUserSessionId(userCode); - } - final Object accessType = request.getAttribute(Constants.SEARCH_LOG_ACCESS_TYPE); - if (Constants.SEARCH_LOG_ACCESS_TYPE_JSON.equals(accessType)) { - searchLog.setAccessType(Constants.SEARCH_LOG_ACCESS_TYPE_JSON); - } else if (Constants.SEARCH_LOG_ACCESS_TYPE_XML.equals(accessType)) { - searchLog.setAccessType(Constants.SEARCH_LOG_ACCESS_TYPE_XML); - } else if (Constants.SEARCH_LOG_ACCESS_TYPE_OTHER.equals(accessType)) { - searchLog.setAccessType(Constants.SEARCH_LOG_ACCESS_TYPE_OTHER); - } else { - searchLog.setAccessType(Constants.SEARCH_LOG_ACCESS_TYPE_WEB); - } - - @SuppressWarnings("unchecked") - final Map> fieldLogMap = (Map>) request.getAttribute(Constants.FIELD_LOGS); - if (fieldLogMap != null) { - for (final Map.Entry> logEntry : fieldLogMap.entrySet()) { - for (final String value : logEntry.getValue()) { - searchLog.addSearchFieldLogValue(logEntry.getKey(), StringUtils.abbreviate(value, 1000)); - } - } - } - - searchLogHelper.addSearchLog(searchLog); - } - - final String[] highlightQueries = (String[]) request.getAttribute(Constants.HIGHLIGHT_QUERIES); - if (highlightQueries != null) { - final StringBuilder buf = new StringBuilder(100); - for (final String q : highlightQueries) { - buf.append("&hq=").append(q); - } - data.register("appendHighlightQueries", buf.toString()); - } - - data.register("pageSize", queryResponseList.getPageSize()); - data.register("currentPageNumber", queryResponseList.getCurrentPageNumber()); - data.register("allRecordCount", queryResponseList.getAllRecordCount()); - data.register("allPageCount", queryResponseList.getAllPageCount()); - data.register("existNextPage", queryResponseList.isExistNextPage()); - data.register("existPrevPage", queryResponseList.isExistPrevPage()); - data.register("currentStartRecordNumber", queryResponseList.getCurrentStartRecordNumber()); - data.register("currentEndRecordNumber", queryResponseList.getCurrentEndRecordNumber()); - data.register("pageNumberList", queryResponseList.getPageNumberList()); - data.register("partialResults", queryResponseList.isPartialResults()); - // TODO - // data.register("queryTime", queryResponseList.get); - // data.register("searchTime", queryResponseList.get); - - return query; - } - - protected void appendLangQuery(final StringBuilder queryBuf, final Set langSet) { - if (langSet.size() == 1) { - queryBuf.append(' ').append(fieldHelper.langField).append(':').append(langSet.iterator().next()); - } else if (langSet.size() > 1) { - boolean first = true; - for (final String lang : langSet) { - if (first) { - queryBuf.append(" ("); - first = false; - } else { - queryBuf.append(" OR "); - } - queryBuf.append(fieldHelper.langField).append(':').append(lang); - } - queryBuf.append(')'); - } - } - protected void updateSearchParams(final SearchForm form) { if (form.facet == null) { form.facet = queryHelper.getDefaultFacetInfo(); @@ -472,57 +218,12 @@ public class SearchAction extends FessSearchAction { return buf.toString(); } - protected void normalizePageNum(final SearchForm form) { - try { - final int num = Integer.parseInt(form.num); - if (num > getMaxPageSize()) { - // max page size - form.num = String.valueOf(getMaxPageSize()); - } else if (num <= 0) { - form.num = String.valueOf(getDefaultPageSize()); - } - } catch (final NumberFormatException e) { - form.num = String.valueOf(getDefaultPageSize()); - } - } - - protected int getDefaultPageSize() { - return DEFAULT_PAGE_SIZE; - } - - protected int getMaxPageSize() { - final Object maxPageSize = crawlerProperties.get(Constants.SEARCH_RESULT_MAX_PAGE_SIZE); - if (maxPageSize == null) { - return MAX_PAGE_SIZE; - } - try { - return Integer.parseInt(maxPageSize.toString()); - } catch (final NumberFormatException e) { - return MAX_PAGE_SIZE; - } - } - - protected boolean hasFieldInQuery(final Set fieldSet, final String query) { - final Matcher matcher = FIELD_EXTRACTION_PATTERN.matcher(query); - if (matcher.matches()) { - final String field = matcher.replaceFirst("$1"); - if (fieldSet.contains(field)) { - return true; - } - fieldSet.add(field); - } - return false; - } - protected String getPagingQuery(final SearchForm form) { final StringBuilder buf = new StringBuilder(200); if (form.additional != null) { - final Set fieldSet = new HashSet(); - for (final String additional : form.additional) { - if (StringUtil.isNotBlank(additional) && additional.length() < 1000 && !hasFieldInQuery(fieldSet, additional)) { - buf.append("&additional=").append(LaFunctions.u(additional)); - } - } + searchService.appendAdditionalQuery(form.additional, additional -> { + buf.append("&additional=").append(LaFunctions.u(additional)); + }); } if (StringUtil.isNotBlank(form.sort)) { buf.append("&sort=").append(LaFunctions.u(form.sort)); @@ -566,4 +267,107 @@ public class SearchAction extends FessSearchAction { return buf.toString(); } + protected static class WebRenderData extends SearchRenderData { + private final RenderData data; + + WebRenderData(final RenderData data) { + this.data = data; + } + + @Override + public void setDocumentItems(final List> documentItems) { + data.register("documentItems", documentItems); + super.setDocumentItems(documentItems); + } + + @Override + public void setFacetResponse(final FacetResponse facetResponse) { + data.register("facetResponse", facetResponse); + super.setFacetResponse(facetResponse); + } + + @Override + public void setAppendHighlightParams(final String appendHighlightParams) { + data.register("appendHighlightParams", appendHighlightParams); + super.setAppendHighlightParams(appendHighlightParams); + } + + @Override + public void setExecTime(final String execTime) { + data.register("execTime", execTime); + super.setExecTime(execTime); + } + + @Override + public void setPageSize(final int pageSize) { + data.register("pageSize", pageSize); + super.setPageSize(pageSize); + } + + @Override + public void setCurrentPageNumber(final int currentPageNumber) { + data.register("currentPageNumber", currentPageNumber); + super.setCurrentPageNumber(currentPageNumber); + } + + @Override + public void setAllRecordCount(final long allRecordCount) { + data.register("allRecordCount", allRecordCount); + super.setAllRecordCount(allRecordCount); + } + + @Override + public void setAllPageCount(final int allPageCount) { + data.register("allPageCount", allPageCount); + super.setAllPageCount(allPageCount); + } + + @Override + public void setExistNextPage(final boolean existNextPage) { + data.register("existNextPage", existNextPage); + super.setExistNextPage(existNextPage); + } + + @Override + public void setExistPrevPage(final boolean existPrevPage) { + data.register("existPrevPage", existPrevPage); + super.setExistPrevPage(existPrevPage); + } + + @Override + public void setCurrentStartRecordNumber(final long currentStartRecordNumber) { + data.register("currentStartRecordNumber", currentStartRecordNumber); + super.setCurrentStartRecordNumber(currentStartRecordNumber); + } + + @Override + public void setCurrentEndRecordNumber(final long currentEndRecordNumber) { + data.register("currentEndRecordNumber", currentEndRecordNumber); + super.setCurrentEndRecordNumber(currentEndRecordNumber); + } + + @Override + public void setPageNumberList(final List pageNumberList) { + data.register("pageNumberList", pageNumberList); + super.setPageNumberList(pageNumberList); + } + + @Override + public void setPartialResults(final boolean partialResults) { + data.register("partialResults", partialResults); + super.setPartialResults(partialResults); + } + + @Override + public void setQueryTime(final long queryTime) { + data.register("queryTime", queryTime); + super.setQueryTime(queryTime); + } + + @Override + public void setSearchQuery(final String searchQuery) { + data.register("searchQuery", searchQuery); + super.setSearchQuery(searchQuery); + } + } } \ No newline at end of file diff --git a/src/main/java/org/codelibs/fess/app/web/search/SearchForm.java b/src/main/java/org/codelibs/fess/app/web/search/SearchForm.java index 13825d254..c058a9624 100644 --- a/src/main/java/org/codelibs/fess/app/web/search/SearchForm.java +++ b/src/main/java/org/codelibs/fess/app/web/search/SearchForm.java @@ -20,10 +20,14 @@ import java.io.Serializable; import java.util.HashMap; import java.util.Map; +import org.codelibs.core.lang.StringUtil; import org.codelibs.fess.entity.FacetInfo; import org.codelibs.fess.entity.GeoInfo; +import org.codelibs.fess.entity.SearchRequestParams; +import org.codelibs.fess.helper.QueryHelper; +import org.codelibs.fess.util.ComponentUtil; -public class SearchForm implements Serializable { +public class SearchForm implements SearchRequestParams, Serializable { private static final long serialVersionUID = 1L; @@ -94,4 +98,90 @@ public class SearchForm implements Serializable { public Map options = new HashMap<>(); + private int startPosition = -1; + + private int pageSize = -1; + + @Override + public int getStartPosition() { + if (startPosition != -1) { + return startPosition; + } + + final QueryHelper queryHelper = ComponentUtil.getQueryHelper(); + if (StringUtil.isBlank(start)) { + startPosition = queryHelper.getDefaultStart(); + } else { + try { + startPosition = Integer.parseInt(start); + } catch (final NumberFormatException e) { + startPosition = queryHelper.getDefaultStart(); + } + } + start = String.valueOf(startPosition); + return startPosition; + } + + @Override + public int getPageSize() { + if (pageSize != -1) { + return pageSize; + } + + final QueryHelper queryHelper = ComponentUtil.getQueryHelper(); + if (StringUtil.isBlank(num)) { + pageSize = queryHelper.getDefaultPageSize(); + } else { + try { + pageSize = Integer.parseInt(num); + if (pageSize > queryHelper.getMaxPageSize() || pageSize <= 0) { + pageSize = queryHelper.getMaxPageSize(); + } + } catch (final NumberFormatException e) { + pageSize = queryHelper.getDefaultPageSize(); + } + } + num = String.valueOf(pageSize); + return pageSize; + } + + @Override + public String getQuery() { + return query; + } + + @Override + public String getOperator() { + return op; + } + + @Override + public String[] getAdditional() { + return additional; + } + + @Override + public Map getFields() { + return fields; + } + + @Override + public String[] getLanguages() { + return lang; + } + + @Override + public GeoInfo getGeoInfo() { + return geo; + } + + @Override + public FacetInfo getFacetInfo() { + return facet; + } + + @Override + public String getSort() { + return sort; + } } diff --git a/src/main/java/org/codelibs/fess/dict/DictionaryCreator.java b/src/main/java/org/codelibs/fess/dict/DictionaryCreator.java index b1b9d2d42..2761958fe 100644 --- a/src/main/java/org/codelibs/fess/dict/DictionaryCreator.java +++ b/src/main/java/org/codelibs/fess/dict/DictionaryCreator.java @@ -11,11 +11,11 @@ public abstract class DictionaryCreator { protected DictionaryManager dictionaryManager; - public DictionaryCreator(String pattern) { + public DictionaryCreator(final String pattern) { this.pattern = Pattern.compile(pattern); } - public DictionaryFile create(String path, Date timestamp) { + public DictionaryFile create(final String path, final Date timestamp) { if (!isTarget(path)) { return null; } @@ -23,17 +23,17 @@ public abstract class DictionaryCreator { return newDictionaryFile(encodePath(path), path, timestamp); } - protected String encodePath(String path) { + protected String encodePath(final String path) { return Base64.getEncoder().encodeToString(path.getBytes(Constants.CHARSET_UTF_8)); } - protected boolean isTarget(String path) { + protected boolean isTarget(final String path) { return pattern.matcher(path).find(); } protected abstract DictionaryFile newDictionaryFile(String id, String path, Date timestamp); - public void setDictionaryManager(DictionaryManager dictionaryManager) { + public void setDictionaryManager(final DictionaryManager dictionaryManager) { this.dictionaryManager = dictionaryManager; } } diff --git a/src/main/java/org/codelibs/fess/dict/DictionaryFile.java b/src/main/java/org/codelibs/fess/dict/DictionaryFile.java index 5ae99f946..13f989483 100644 --- a/src/main/java/org/codelibs/fess/dict/DictionaryFile.java +++ b/src/main/java/org/codelibs/fess/dict/DictionaryFile.java @@ -34,7 +34,7 @@ public abstract class DictionaryFile { protected Date timestamp; - public DictionaryFile(String id, String path, Date timestamp) { + public DictionaryFile(final String id, final String path, final Date timestamp) { this.id = id; this.path = path; this.timestamp = timestamp; @@ -52,7 +52,7 @@ public abstract class DictionaryFile { return timestamp; } - public DictionaryFile manager(DictionaryManager dictionaryManager) { + public DictionaryFile manager(final DictionaryManager dictionaryManager) { this.dictionaryManager = dictionaryManager; return this; } diff --git a/src/main/java/org/codelibs/fess/dict/DictionaryManager.java b/src/main/java/org/codelibs/fess/dict/DictionaryManager.java index 644e3907e..1940dcc6a 100644 --- a/src/main/java/org/codelibs/fess/dict/DictionaryManager.java +++ b/src/main/java/org/codelibs/fess/dict/DictionaryManager.java @@ -54,35 +54,35 @@ public class DictionaryManager { public DictionaryFile[] getDictionaryFiles() { try (CurlResponse response = Curl.get(getUrl() + "/_configsync/file").param("fields", "path,@timestamp").execute()) { - Map contentMap = response.getContentAsMap(); + final Map contentMap = response.getContentAsMap(); @SuppressWarnings("unchecked") - List> fileList = (List>) contentMap.get("file"); + final List> fileList = (List>) contentMap.get("file"); return fileList .stream() .map(fileMap -> { try { - String path = fileMap.get("path").toString(); - Date timestamp = + final String path = fileMap.get("path").toString(); + final Date timestamp = new SimpleDateFormat(Constants.DATE_FORMAT_ISO_8601_EXTEND_UTC).parse(fileMap.get("@timestamp") .toString()); for (final DictionaryCreator creator : creatorList) { - DictionaryFile file = creator.create(path, timestamp); + final DictionaryFile file = creator.create(path, timestamp); if (file != null) { return file; } } - } catch (Exception e) { + } catch (final Exception e) { logger.warn("Failed to load " + fileMap, e); } return null; }).filter(file -> file != null).toArray(n -> new DictionaryFile[n]); - } catch (IOException e) { + } catch (final IOException e) { throw new DictionaryException("Failed to access dictionaries", e); } } public OptionalEntity> getDictionaryFile(final String id) { - for (DictionaryFile dictFile : getDictionaryFiles()) { + for (final DictionaryFile dictFile : getDictionaryFiles()) { if (dictFile.getId().equals(id)) { return OptionalEntity.of(dictFile); } @@ -90,7 +90,7 @@ public class DictionaryManager { return OptionalEntity.empty(); } - public void store(DictionaryFile dictFile, File file) { + public void store(final DictionaryFile dictFile, final File file) { getDictionaryFile(dictFile.getId()) .ifPresent(currentFile -> { if (currentFile.getTimestamp().getTime() > dictFile.getTimestamp().getTime()) { @@ -101,11 +101,11 @@ public class DictionaryManager { try (CurlResponse response = Curl.post(getUrl() + "/_configsync/file").param("path", dictFile.getPath()).body(FileUtil.readUTF8(file)) .execute()) { - Map contentMap = response.getContentAsMap(); + final Map contentMap = response.getContentAsMap(); if (!Constants.TRUE.equalsIgnoreCase(contentMap.get("acknowledged").toString())) { throw new DictionaryException("Failed to update " + dictFile.getPath()); } - } catch (IOException e) { + } catch (final IOException e) { throw new DictionaryException("Failed to update " + dictFile.getPath(), e); } @@ -114,15 +114,15 @@ public class DictionaryManager { }); } - public InputStream getContentInputStream(DictionaryFile dictFile) { + public InputStream getContentInputStream(final DictionaryFile dictFile) { try { return Curl.get(getUrl() + "/_configsync/file").param("path", dictFile.getPath()).execute().getContentAsStream(); - } catch (IOException e) { + } catch (final IOException e) { throw new DictionaryException("Failed to access " + dictFile.getPath(), e); } } - public void addCreator(DictionaryCreator creator) { + public void addCreator(final DictionaryCreator creator) { creatorList.add(creator); } diff --git a/src/main/java/org/codelibs/fess/dict/kuromoji/KuromojiCreator.java b/src/main/java/org/codelibs/fess/dict/kuromoji/KuromojiCreator.java index f10d162b2..a784ccf6b 100644 --- a/src/main/java/org/codelibs/fess/dict/kuromoji/KuromojiCreator.java +++ b/src/main/java/org/codelibs/fess/dict/kuromoji/KuromojiCreator.java @@ -12,12 +12,12 @@ public class KuromojiCreator extends DictionaryCreator { super("kuromoji.*\\.txt"); } - public KuromojiCreator(String pattern) { + public KuromojiCreator(final String pattern) { super(pattern); } @Override - protected DictionaryFile newDictionaryFile(String id, String path, Date timestamp) { + protected DictionaryFile newDictionaryFile(final String id, final String path, final Date timestamp) { return new KuromojiFile(id, path, timestamp).manager(dictionaryManager); } diff --git a/src/main/java/org/codelibs/fess/dict/kuromoji/KuromojiFile.java b/src/main/java/org/codelibs/fess/dict/kuromoji/KuromojiFile.java index 81ea1717f..62373f371 100644 --- a/src/main/java/org/codelibs/fess/dict/kuromoji/KuromojiFile.java +++ b/src/main/java/org/codelibs/fess/dict/kuromoji/KuromojiFile.java @@ -45,7 +45,7 @@ public class KuromojiFile extends DictionaryFile { List kuromojiItemList; - public KuromojiFile(String id, String path, Date timestamp) { + public KuromojiFile(final String id, final String path, final Date timestamp) { super(id, path, timestamp); } @@ -114,7 +114,7 @@ public class KuromojiFile extends DictionaryFile { } } - protected void reload(final KuromojiUpdater updater, InputStream in) { + protected void reload(final KuromojiUpdater updater, final InputStream in) { final List itemList = new ArrayList(); try (BufferedReader reader = new BufferedReader(new InputStreamReader(in != null ? in : dictionaryManager.getContentInputStream(this), Constants.UTF_8))) { @@ -164,7 +164,7 @@ public class KuromojiFile extends DictionaryFile { } } if (updater != null) { - KuromojiItem item = updater.commit(); + final KuromojiItem item = updater.commit(); if (item != null) { itemList.add(item); } diff --git a/src/main/java/org/codelibs/fess/dict/synonym/SynonymCreator.java b/src/main/java/org/codelibs/fess/dict/synonym/SynonymCreator.java index daec4cf5e..771f4b43e 100644 --- a/src/main/java/org/codelibs/fess/dict/synonym/SynonymCreator.java +++ b/src/main/java/org/codelibs/fess/dict/synonym/SynonymCreator.java @@ -12,12 +12,12 @@ public class SynonymCreator extends DictionaryCreator { super("synonym.*\\.txt"); } - public SynonymCreator(String pattern) { + public SynonymCreator(final String pattern) { super(pattern); } @Override - protected DictionaryFile newDictionaryFile(String id, String path, Date timestamp) { + protected DictionaryFile newDictionaryFile(final String id, final String path, final Date timestamp) { return new SynonymFile(id, path, timestamp).manager(dictionaryManager); } diff --git a/src/main/java/org/codelibs/fess/dict/synonym/SynonymFile.java b/src/main/java/org/codelibs/fess/dict/synonym/SynonymFile.java index a76c667d4..58895c07f 100644 --- a/src/main/java/org/codelibs/fess/dict/synonym/SynonymFile.java +++ b/src/main/java/org/codelibs/fess/dict/synonym/SynonymFile.java @@ -44,7 +44,7 @@ public class SynonymFile extends DictionaryFile { List synonymItemList; - public SynonymFile(String id, String path, Date timestamp) { + public SynonymFile(final String id, final String path, final Date timestamp) { super(id, path, timestamp); } @@ -114,7 +114,7 @@ public class SynonymFile extends DictionaryFile { } } - protected void reload(final SynonymUpdater updater, InputStream in) { + protected void reload(final SynonymUpdater updater, final InputStream in) { final List itemList = new ArrayList(); try (BufferedReader reader = new BufferedReader(new InputStreamReader(in != null ? in : dictionaryManager.getContentInputStream(this), Constants.UTF_8))) { @@ -186,7 +186,7 @@ public class SynonymFile extends DictionaryFile { } } if (updater != null) { - SynonymItem item = updater.commit(); + final SynonymItem item = updater.commit(); if (item != null) { itemList.add(item); } diff --git a/src/main/java/org/codelibs/fess/entity/SearchRenderData.java b/src/main/java/org/codelibs/fess/entity/SearchRenderData.java new file mode 100644 index 000000000..addb3b8c4 --- /dev/null +++ b/src/main/java/org/codelibs/fess/entity/SearchRenderData.java @@ -0,0 +1,170 @@ +package org.codelibs.fess.entity; + +import java.util.List; +import java.util.Map; + +import org.codelibs.fess.util.FacetResponse; + +public class SearchRenderData { + + private List> documentItems; + + private FacetResponse facetResponse; + + private String appendHighlightParams; + + private String execTime; + + private int pageSize; + + private int currentPageNumber; + + private long allRecordCount; + + private int allPageCount; + + private boolean existNextPage; + + private boolean existPrevPage; + + private long currentStartRecordNumber; + + private long currentEndRecordNumber; + + private List pageNumberList; + + private boolean partialResults; + + private String searchQuery; + + private long queryTime; + + public void setDocumentItems(final List> documentItems) { + this.documentItems = documentItems; + } + + public void setFacetResponse(final FacetResponse facetResponse) { + this.facetResponse = facetResponse; + } + + public void setAppendHighlightParams(final String appendHighlightParams) { + this.appendHighlightParams = appendHighlightParams; + } + + public void setExecTime(final String execTime) { + this.execTime = execTime; + } + + public void setPageSize(final int pageSize) { + this.pageSize = pageSize; + } + + public void setCurrentPageNumber(final int currentPageNumber) { + this.currentPageNumber = currentPageNumber; + } + + public void setAllRecordCount(final long allRecordCount) { + this.allRecordCount = allRecordCount; + } + + public void setAllPageCount(final int allPageCount) { + this.allPageCount = allPageCount; + } + + public void setExistNextPage(final boolean existNextPage) { + this.existNextPage = existNextPage; + } + + public void setExistPrevPage(final boolean existPrevPage) { + this.existPrevPage = existPrevPage; + } + + public void setCurrentStartRecordNumber(final long currentStartRecordNumber) { + this.currentStartRecordNumber = currentStartRecordNumber; + } + + public void setCurrentEndRecordNumber(final long currentEndRecordNumber) { + this.currentEndRecordNumber = currentEndRecordNumber; + } + + public void setPageNumberList(final List pageNumberList) { + this.pageNumberList = pageNumberList; + } + + public void setPartialResults(final boolean partialResults) { + this.partialResults = partialResults; + } + + public void setQueryTime(final long queryTime) { + this.queryTime = queryTime; + } + + public void setSearchQuery(final String searchQuery) { + this.searchQuery = searchQuery; + } + + public List> getDocumentItems() { + return documentItems; + } + + public FacetResponse getFacetResponse() { + return facetResponse; + } + + public String getAppendHighlightParams() { + return appendHighlightParams; + } + + public String getExecTime() { + return execTime; + } + + public int getPageSize() { + return pageSize; + } + + public int getCurrentPageNumber() { + return currentPageNumber; + } + + public long getAllRecordCount() { + return allRecordCount; + } + + public int getAllPageCount() { + return allPageCount; + } + + public boolean isExistNextPage() { + return existNextPage; + } + + public boolean isExistPrevPage() { + return existPrevPage; + } + + public long getCurrentStartRecordNumber() { + return currentStartRecordNumber; + } + + public long getCurrentEndRecordNumber() { + return currentEndRecordNumber; + } + + public List getPageNumberList() { + return pageNumberList; + } + + public boolean isPartialResults() { + return partialResults; + } + + public String getSearchQuery() { + return searchQuery; + } + + public long getQueryTime() { + return queryTime; + } + +} diff --git a/src/main/java/org/codelibs/fess/entity/SearchRequestParams.java b/src/main/java/org/codelibs/fess/entity/SearchRequestParams.java new file mode 100644 index 000000000..9f4616dd5 --- /dev/null +++ b/src/main/java/org/codelibs/fess/entity/SearchRequestParams.java @@ -0,0 +1,27 @@ +package org.codelibs.fess.entity; + +import java.util.Map; + +public interface SearchRequestParams { + + String getQuery(); + + String getOperator(); + + String[] getAdditional(); + + Map getFields(); + + String[] getLanguages(); + + GeoInfo getGeoInfo(); + + FacetInfo getFacetInfo(); + + String getSort(); + + int getStartPosition(); + + int getPageSize(); + +} diff --git a/src/main/java/org/codelibs/fess/es/client/FessEsClient.java b/src/main/java/org/codelibs/fess/es/client/FessEsClient.java index d7f8a9004..73feeec2f 100644 --- a/src/main/java/org/codelibs/fess/es/client/FessEsClient.java +++ b/src/main/java/org/codelibs/fess/es/client/FessEsClient.java @@ -10,7 +10,6 @@ import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Map.Entry; -import java.util.Optional; import java.util.Set; import java.util.stream.Collectors; @@ -36,6 +35,7 @@ import org.codelibs.fess.exception.ResultOffsetExceededException; import org.codelibs.fess.helper.QueryHelper; import org.codelibs.fess.indexer.FessSearchQueryException; import org.codelibs.fess.util.ComponentUtil; +import org.dbflute.optional.OptionalEntity; import org.elasticsearch.ElasticsearchException; import org.elasticsearch.action.Action; import org.elasticsearch.action.ActionFuture; @@ -471,7 +471,7 @@ public class FessEsClient implements Client { } final long execTime = System.currentTimeMillis() - startTime; - return searchResult.build(requestBuilder, execTime, Optional.ofNullable(response)); + return searchResult.build(requestBuilder, execTime, OptionalEntity.ofNullable(response, () -> {/* TODO */})); } public T search(final String index, final String type, final SearchCondition condition, @@ -504,10 +504,10 @@ public class FessEsClient implements Client { } final long execTime = System.currentTimeMillis() - startTime; - return searchResult.build(searchRequestBuilder, execTime, Optional.ofNullable(searchResponse)); + return searchResult.build(searchRequestBuilder, execTime, OptionalEntity.ofNullable(searchResponse, () -> {/* TODO */})); } - public Optional> getDocument(final String index, final String type, + public OptionalEntity> getDocument(final String index, final String type, final SearchCondition condition) { return getDocument(index, type, condition, (response, hit) -> { final Map source = hit.getSource(); @@ -522,7 +522,7 @@ public class FessEsClient implements Client { }); } - public Optional getDocument(final String index, final String type, final SearchCondition condition, + public OptionalEntity getDocument(final String index, final String type, final SearchCondition condition, final EntityCreator creator) { return search(index, type, condition, (queryBuilder, execTime, searchResponse) -> { return searchResponse.map(response -> { @@ -535,7 +535,7 @@ public class FessEsClient implements Client { }); } - public Optional> getDocument(final String index, final String type, final String id, + public OptionalEntity> getDocument(final String index, final String type, final String id, final SearchCondition condition) { return getDocument(index, type, id, condition, (response, result) -> { final Map source = response.getSource(); @@ -550,7 +550,7 @@ public class FessEsClient implements Client { }); } - public Optional getDocument(final String index, final String type, final String id, + public OptionalEntity getDocument(final String index, final String type, final String id, final SearchCondition condition, final EntityCreator creator) { return get(index, type, id, condition, (queryBuilder, execTime, getResponse) -> { return getResponse.map(response -> { @@ -562,7 +562,15 @@ public class FessEsClient implements Client { public List> getDocumentList(final String index, final String type, final SearchCondition condition) { return getDocumentList(index, type, condition, (response, hit) -> { - return hit.getSource(); + final Map source = hit.getSource(); + if (source != null) { + return source; + } + final Map fields = hit.getFields(); + if (fields != null) { + return fields.entrySet().stream().collect(Collectors.toMap(e -> e.getKey(), e -> (Object) e.getValue().getValues())); + } + return null; }); } @@ -875,7 +883,7 @@ public class FessEsClient implements Client { } public interface SearchResult { - T build(B requestBuilder, long execTime, Optional response); + T build(B requestBuilder, long execTime, OptionalEntity response); } public interface EntityCreator { diff --git a/src/main/java/org/codelibs/fess/helper/HotSearchWordHelper.java b/src/main/java/org/codelibs/fess/helper/HotSearchWordHelper.java index 3eba43da0..647a32a32 100644 --- a/src/main/java/org/codelibs/fess/helper/HotSearchWordHelper.java +++ b/src/main/java/org/codelibs/fess/helper/HotSearchWordHelper.java @@ -77,5 +77,23 @@ public class HotSearchWordHelper { public long getTime() { return time; } + + public static Range parseRange(final String value) { + Range range; + if (value == null) { + range = Range.ENTIRE; + } else if ("day".equals(value) || "1".equals(value)) { + range = Range.ONE_DAY; + } else if ("week".equals(value) || "7".equals(value)) { + range = Range.ONE_DAY; + } else if ("month".equals(value) || "30".equals(value)) { + range = Range.ONE_DAY; + } else if ("year".equals(value) || "365".equals(value)) { + range = Range.ONE_DAY; + } else { + range = Range.ENTIRE; + } + return range; + } } } diff --git a/src/main/java/org/codelibs/fess/helper/QueryHelper.java b/src/main/java/org/codelibs/fess/helper/QueryHelper.java index c846591b3..15717521e 100644 --- a/src/main/java/org/codelibs/fess/helper/QueryHelper.java +++ b/src/main/java/org/codelibs/fess/helper/QueryHelper.java @@ -33,6 +33,7 @@ import javax.servlet.http.HttpServletRequest; import org.apache.commons.lang3.StringUtils; import org.codelibs.core.lang.StringUtil; +import org.codelibs.core.misc.DynamicProperties; import org.codelibs.fess.Constants; import org.codelibs.fess.entity.FacetInfo; import org.codelibs.fess.entity.GeoInfo; @@ -45,27 +46,36 @@ import org.lastaflute.web.util.LaRequestUtil; public class QueryHelper implements Serializable { - private static final String SCORE_FIELD = "score"; + protected static final long serialVersionUID = 1L; - private static final String INURL_FIELD = "inurl"; + protected static final String SCORE_FIELD = "score"; - private static final String NOT_ = "NOT "; + protected static final String INURL_FIELD = "inurl"; - private static final String AND = "AND"; + protected static final String NOT_ = "NOT "; - private static final String OR = "OR"; + protected static final String AND = "AND"; - private static final String NOT = "NOT"; + protected static final String OR = "OR"; - private static final String _TO_ = " TO "; + protected static final String NOT = "NOT"; - private static final String _OR_ = " OR "; + protected static final String _TO_ = " TO "; - private static final String _AND_ = " AND "; + protected static final String _OR_ = " OR "; - private static final String DEFAULT_OPERATOR = _AND_; + protected static final String _AND_ = " AND "; - private static final long serialVersionUID = 1L; + protected static final String DEFAULT_OPERATOR = _AND_; + + protected static final int DEFAULT_START_POSITION = 0; + + protected static final int DEFAULT_PAGE_SIZE = 20; + + protected static final int MAX_PAGE_SIZE = 100; + + @Resource + protected DynamicProperties crawlerProperties; @Resource protected RoleQueryHelper roleQueryHelper; @@ -113,9 +123,9 @@ public class QueryHelper implements Serializable { protected long timeAllowed = -1; - protected Map requestParameterMap = new HashMap(); + protected Map requestParameterMap = new HashMap<>(); - protected Map fieldLanguageMap = new HashMap(); + protected Map fieldLanguageMap = new HashMap<>(); protected int maxSearchResultOffset = 100000; @@ -133,9 +143,13 @@ public class QueryHelper implements Serializable { protected String defaultQueryLanguage; - protected Map additionalQueryParamMap = new HashMap(); + protected Map additionalQueryParamMap = new HashMap<>(); - protected Map fieldBoostMap = new HashMap(); + protected Map fieldBoostMap = new HashMap<>(); + + protected int defaultPageSize = DEFAULT_PAGE_SIZE; + + protected int defaultStartPosition = DEFAULT_START_POSITION; @PostConstruct public void init() { @@ -1378,6 +1392,34 @@ public class QueryHelper implements Serializable { this.defaultQueryLanguage = defaultQueryLanguage; } + public int getMaxPageSize() { + final Object maxPageSize = crawlerProperties.get(Constants.SEARCH_RESULT_MAX_PAGE_SIZE); + if (maxPageSize == null) { + return MAX_PAGE_SIZE; + } + try { + return Integer.parseInt(maxPageSize.toString()); + } catch (final NumberFormatException e) { + return MAX_PAGE_SIZE; + } + } + + public int getDefaultPageSize() { + return defaultPageSize; + } + + public void setDefaultPageSize(final int defaultPageSize) { + this.defaultPageSize = defaultPageSize; + } + + public int getDefaultStart() { + return defaultStartPosition; + } + + public void setDefaultStart(final int defaultStartPosition) { + this.defaultStartPosition = defaultStartPosition; + } + public Map getQueryParamMap() { if (additionalQueryParamMap.isEmpty()) { return additionalQueryParamMap; diff --git a/src/main/java/org/codelibs/fess/mylasta/action/FessHtmlPath.java b/src/main/java/org/codelibs/fess/mylasta/action/FessHtmlPath.java index 3bcb073ac..9d51704e3 100644 --- a/src/main/java/org/codelibs/fess/mylasta/action/FessHtmlPath.java +++ b/src/main/java/org/codelibs/fess/mylasta/action/FessHtmlPath.java @@ -347,9 +347,6 @@ public interface FessHtmlPath { /** The path of the HTML: /error/badRequest.jsp */ HtmlNext path_Error_BadRequestJsp = new HtmlNext("/error/badRequest.jsp"); - /** The path of the HTML: /error/error_message.jsp */ - HtmlNext path_Error_ErrorMessageJsp = new HtmlNext("/error/error_message.jsp"); - /** The path of the HTML: /error/footer.jsp */ HtmlNext path_Error_FooterJsp = new HtmlNext("/error/footer.jsp"); diff --git a/src/main/java/org/codelibs/fess/mylasta/action/FessUserBean.java b/src/main/java/org/codelibs/fess/mylasta/action/FessUserBean.java index 3613691b5..df8072f68 100644 --- a/src/main/java/org/codelibs/fess/mylasta/action/FessUserBean.java +++ b/src/main/java/org/codelibs/fess/mylasta/action/FessUserBean.java @@ -30,7 +30,7 @@ public class FessUserBean extends TypicalUserBean { // #change_it also L // ========== /** The serial version UID for object serialization. (Default) */ private static final long serialVersionUID = 1L; - private User user; + private final User user; // =================================================================================== // Attribute @@ -62,19 +62,19 @@ public class FessUserBean extends TypicalUserBean { // #change_it also L return user.getGroups(); } - public boolean hasRole(String role) { + public boolean hasRole(final String role) { return Stream.of(user.getRoleNames()).anyMatch(s -> s.equals(role)); } - public boolean hasRoles(String[] acceptedRoles) { + public boolean hasRoles(final String[] acceptedRoles) { return Stream.of(user.getRoleNames()).anyMatch(s1 -> Stream.of(acceptedRoles).anyMatch(s2 -> s2.equals(s1))); } - public boolean hasGroup(String group) { + public boolean hasGroup(final String group) { return Stream.of(user.getGroupNames()).anyMatch(s -> s.equals(group)); } - public boolean hasGroups(String[] acceptedGroups) { + public boolean hasGroups(final String[] acceptedGroups) { return Stream.of(user.getGroupNames()).anyMatch(s1 -> Stream.of(acceptedGroups).anyMatch(s2 -> s2.equals(s1))); } } diff --git a/src/main/java/org/codelibs/fess/util/ComponentUtil.java b/src/main/java/org/codelibs/fess/util/ComponentUtil.java index 3e3404d17..fe2a8b481 100644 --- a/src/main/java/org/codelibs/fess/util/ComponentUtil.java +++ b/src/main/java/org/codelibs/fess/util/ComponentUtil.java @@ -45,6 +45,7 @@ import org.codelibs.fess.helper.SambaHelper; import org.codelibs.fess.helper.SearchLogHelper; import org.codelibs.fess.helper.SystemHelper; import org.codelibs.fess.helper.UserAgentHelper; +import org.codelibs.fess.helper.UserInfoHelper; import org.codelibs.fess.helper.ViewHelper; import org.codelibs.fess.indexer.IndexUpdater; import org.codelibs.fess.job.JobExecutor; @@ -54,6 +55,8 @@ import org.lastaflute.di.core.factory.SingletonLaContainerFactory; import org.lastaflute.web.servlet.session.SessionManager; public final class ComponentUtil { + private static final String FESS_ES_CLIENT = "fessEsClient"; + private static final String DICTIONARY_MANAGER = "dictionaryManager"; private static final String DATA_SERVICE = "dataService"; @@ -76,6 +79,8 @@ public final class ComponentUtil { private static final String USER_AGENT_HELPER = "userAgentHelper"; + private static final String USER_INFO_HELPER = "userInfoHelper"; + private static final String WEB_API_MANAGER_FACTORY = "webApiManagerFactory"; private static final String JOB_HELPER = "jobHelper"; @@ -118,7 +123,7 @@ public final class ComponentUtil { private static final String FIELD_HELPER = "fieldHelper"; - private static final String ELASTICSEARCH_CLIENT = "fessEsClient"; + private static final String ELASTICSEARCH_CLIENT = FESS_ES_CLIENT; private ComponentUtil() { } @@ -243,6 +248,10 @@ public final class ComponentUtil { return SingletonLaContainer.getComponent(FIELD_HELPER); } + public static UserInfoHelper getUserInfoHelper() { + return SingletonLaContainer.getComponent(USER_INFO_HELPER); + } + public static FessEsClient getElasticsearchClient() { return SingletonLaContainer.getComponent(ELASTICSEARCH_CLIENT); } @@ -253,13 +262,16 @@ public final class ComponentUtil { public static DictionaryManager getDictionaryManager() { return SingletonLaContainer.getComponent(DICTIONARY_MANAGER); - } public static DataService getDataService() { return SingletonLaContainer.getComponent(DATA_SERVICE); } + public static FessEsClient getFessEsClient() { + return SingletonLaContainer.getComponent(FESS_ES_CLIENT); + } + public static FessLoginAssist getLoginAssist() { return getComponent(FessLoginAssist.class); } diff --git a/src/main/java/org/codelibs/fess/util/DocumentUtil.java b/src/main/java/org/codelibs/fess/util/DocumentUtil.java index 0dd94840b..c3689327b 100644 --- a/src/main/java/org/codelibs/fess/util/DocumentUtil.java +++ b/src/main/java/org/codelibs/fess/util/DocumentUtil.java @@ -38,7 +38,7 @@ public final class DocumentUtil { } @SuppressWarnings("unchecked") - private static T convertObj(Object value, final Class clazz) { + private static T convertObj(final Object value, final Class clazz) { if (value == null) { return null; } diff --git a/src/main/java/org/codelibs/fess/util/QueryResponseList.java b/src/main/java/org/codelibs/fess/util/QueryResponseList.java index dd30a6227..24f04988d 100644 --- a/src/main/java/org/codelibs/fess/util/QueryResponseList.java +++ b/src/main/java/org/codelibs/fess/util/QueryResponseList.java @@ -23,11 +23,11 @@ import java.util.Iterator; import java.util.List; import java.util.ListIterator; import java.util.Map; -import java.util.Optional; import org.apache.commons.lang3.StringUtils; import org.codelibs.fess.helper.QueryHelper; import org.codelibs.fess.helper.ViewHelper; +import org.dbflute.optional.OptionalEntity; import org.elasticsearch.action.search.SearchResponse; import org.elasticsearch.common.text.Text; import org.elasticsearch.search.SearchHit; @@ -82,7 +82,7 @@ public class QueryResponseList implements List> { this.parent = parent; } - public void init(final Optional searchResponseOpt, final int start, final int pageSize) { + public void init(final OptionalEntity searchResponseOpt, final int start, final int pageSize) { searchResponseOpt.ifPresent(searchResponse -> { final SearchHits searchHits = searchResponse.getHits(); allRecordCount = searchHits.getTotalHits(); @@ -362,4 +362,8 @@ public class QueryResponseList implements List> { return partialResults; } + public long getQueryTime() { + return queryTime; + } + } diff --git a/src/main/java/org/codelibs/fess/util/ResourceUtil.java b/src/main/java/org/codelibs/fess/util/ResourceUtil.java index 959655914..3f247752e 100644 --- a/src/main/java/org/codelibs/fess/util/ResourceUtil.java +++ b/src/main/java/org/codelibs/fess/util/ResourceUtil.java @@ -59,13 +59,13 @@ public class ResourceUtil { return getPath("dict", names); } - protected static Path getPath(final String base, String... names) { + protected static Path getPath(final String base, final String... names) { try { final ServletContext servletContext = SingletonLaContainer.getComponent(ServletContext.class); - String webinfoPath = servletContext.getRealPath("/WEB-INF/" + base); + final String webinfoPath = servletContext.getRealPath("/WEB-INF/" + base); if (webinfoPath != null) { - Path path = Paths.get(webinfoPath, names); + final Path path = Paths.get(webinfoPath, names); if (Files.exists(path)) { return path; } diff --git a/src/main/resources/fess_api.xml b/src/main/resources/fess_api.xml index 6f46798dd..a2bf55dfe 100644 --- a/src/main/resources/fess_api.xml +++ b/src/main/resources/fess_api.xml @@ -5,9 +5,6 @@ - - xmlApiManager - jsonApiManager @@ -19,8 +16,6 @@ - - diff --git a/src/main/webapp/WEB-INF/view/searchResults.jsp b/src/main/webapp/WEB-INF/view/searchResults.jsp index ec251ad1e..a87d161b4 100644 --- a/src/main/webapp/WEB-INF/view/searchResults.jsp +++ b/src/main/webapp/WEB-INF/view/searchResults.jsp @@ -69,7 +69,7 @@ (${f:h(doc.favorite_count)}) (${f:h(doc.favoriteCount_l_x_dv)}) + key="labels.search_result_favorited"/> (${f:h(doc.favorite_count)})