fix #2751 add rank fusion
This commit is contained in:
parent
bc337f84b2
commit
09c47c206a
21 changed files with 1299 additions and 222 deletions
|
@ -848,6 +848,8 @@ public class SearchApiManager extends BaseApiManager {
|
|||
|
||||
private int startPosition = -1;
|
||||
|
||||
private int offset = -1;
|
||||
|
||||
private int pageSize = -1;
|
||||
|
||||
protected JsonRequestParams(final HttpServletRequest request, final FessConfig fessConfig) {
|
||||
|
@ -935,6 +937,25 @@ public class SearchApiManager extends BaseApiManager {
|
|||
return startPosition;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getOffset() {
|
||||
if (offset != -1) {
|
||||
return offset;
|
||||
}
|
||||
|
||||
final String value = request.getParameter("offset");
|
||||
if (StringUtil.isBlank(value)) {
|
||||
offset = 0;
|
||||
} else {
|
||||
try {
|
||||
offset = Integer.parseInt(value);
|
||||
} catch (final NumberFormatException e) {
|
||||
offset = 0;
|
||||
}
|
||||
}
|
||||
return offset;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getPageSize() {
|
||||
if (pageSize != -1) {
|
||||
|
@ -1071,6 +1092,11 @@ public class SearchApiManager extends BaseApiManager {
|
|||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getOffset() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getPageSize() {
|
||||
throw new UnsupportedOperationException();
|
||||
|
|
|
@ -47,6 +47,9 @@ public class ListForm extends SearchRequestParams {
|
|||
@ValidateTypeFailure
|
||||
public Integer start;
|
||||
|
||||
@ValidateTypeFailure
|
||||
public Integer offset;
|
||||
|
||||
@ValidateTypeFailure
|
||||
public Integer pn;
|
||||
|
||||
|
@ -91,6 +94,14 @@ public class ListForm extends SearchRequestParams {
|
|||
return start;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getOffset() {
|
||||
if (offset == null) {
|
||||
offset = 0;
|
||||
}
|
||||
return offset;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getPageSize() {
|
||||
final FessConfig fessConfig = ComponentUtil.getFessConfig();
|
||||
|
|
|
@ -55,6 +55,9 @@ public class SearchForm extends SearchRequestParams {
|
|||
@ValidateTypeFailure
|
||||
public Integer start;
|
||||
|
||||
@ValidateTypeFailure
|
||||
public Integer offset;
|
||||
|
||||
@ValidateTypeFailure
|
||||
public Integer pn;
|
||||
|
||||
|
@ -68,13 +71,20 @@ public class SearchForm extends SearchRequestParams {
|
|||
|
||||
@Override
|
||||
public int getStartPosition() {
|
||||
final FessConfig fessConfig = ComponentUtil.getFessConfig();
|
||||
if (start == null) {
|
||||
start = fessConfig.getPagingSearchPageStartAsInteger();
|
||||
start = ComponentUtil.getFessConfig().getPagingSearchPageStartAsInteger();
|
||||
}
|
||||
return start;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getOffset() {
|
||||
if (offset == null) {
|
||||
offset = 0;
|
||||
}
|
||||
return offset;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getPageSize() {
|
||||
final FessConfig fessConfig = ComponentUtil.getFessConfig();
|
||||
|
|
|
@ -15,8 +15,6 @@
|
|||
*/
|
||||
package org.codelibs.fess.entity;
|
||||
|
||||
import static org.codelibs.core.stream.StreamUtil.stream;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
|
@ -25,6 +23,7 @@ import java.util.Map;
|
|||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
import org.codelibs.core.lang.StringUtil;
|
||||
import org.codelibs.core.stream.StreamUtil;
|
||||
import org.codelibs.fess.exception.InvalidQueryException;
|
||||
import org.codelibs.fess.mylasta.direction.FessConfig;
|
||||
import org.codelibs.fess.util.ComponentUtil;
|
||||
|
@ -43,7 +42,7 @@ public class GeoInfo {
|
|||
final String[] geoFields = fessConfig.getQueryGeoFieldsAsArray();
|
||||
final Map<String, List<QueryBuilder>> geoMap = new HashMap<>();
|
||||
|
||||
stream(request.getParameterMap())
|
||||
StreamUtil.stream(request.getParameterMap())
|
||||
.of(stream -> stream.filter(e -> e.getKey().startsWith("geo.") && e.getKey().endsWith(".point")).forEach(e -> {
|
||||
final String key = e.getKey();
|
||||
for (final String geoField : geoFields) {
|
||||
|
@ -51,7 +50,7 @@ public class GeoInfo {
|
|||
final String distanceKey = key.replaceFirst(".point$", ".distance");
|
||||
final String distance = request.getParameter(distanceKey);
|
||||
if (StringUtil.isNotBlank(distance)) {
|
||||
stream(e.getValue()).of(s -> s.forEach(pt -> {
|
||||
StreamUtil.stream(e.getValue()).of(s -> s.forEach(pt -> {
|
||||
List<QueryBuilder> list = geoMap.get(geoField);
|
||||
if (list == null) {
|
||||
list = new ArrayList<>();
|
||||
|
@ -95,7 +94,7 @@ public class GeoInfo {
|
|||
builder = queryBuilders[0];
|
||||
} else if (queryBuilders.length > 1) {
|
||||
final BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();
|
||||
stream(queryBuilders).of(stream -> stream.forEach(boolQuery::must));
|
||||
StreamUtil.stream(queryBuilders).of(stream -> stream.forEach(boolQuery::must));
|
||||
builder = boolQuery;
|
||||
}
|
||||
|
||||
|
|
|
@ -63,6 +63,8 @@ public abstract class SearchRequestParams {
|
|||
|
||||
public abstract int getPageSize();
|
||||
|
||||
public abstract int getOffset();
|
||||
|
||||
public abstract String[] getExtraQueries();
|
||||
|
||||
public abstract Object getAttribute(String name);
|
||||
|
|
|
@ -18,6 +18,7 @@ package org.codelibs.fess.es.client;
|
|||
import static org.codelibs.core.stream.StreamUtil.split;
|
||||
import static org.codelibs.core.stream.StreamUtil.stream;
|
||||
import static org.codelibs.opensearch.runner.OpenSearchRunner.newConfigs;
|
||||
import static org.opensearch.action.ActionListener.wrap;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
|
@ -918,7 +919,7 @@ public class SearchEngineClient implements Client {
|
|||
protected void deleteScrollContext(final String scrollId) {
|
||||
if (scrollId != null) {
|
||||
client.prepareClearScroll().addScrollId(scrollId)
|
||||
.execute(ActionListener.wrap(res -> {}, e -> logger.warn("Failed to clear the scroll context.", e)));
|
||||
.execute(wrap(res -> {}, e -> logger.warn("Failed to clear the scroll context.", e)));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -159,33 +159,7 @@ public class SearchHelper {
|
|||
|
||||
protected List<Map<String, Object>> searchInternal(final String query, final SearchRequestParams params,
|
||||
final OptionalThing<FessUserBean> userBean) {
|
||||
final FessConfig fessConfig = ComponentUtil.getFessConfig();
|
||||
final QueryHelper queryHelper = ComponentUtil.getQueryHelper();
|
||||
final int pageSize = params.getPageSize();
|
||||
LaRequestUtil.getOptionalRequest().ifPresent(request -> {
|
||||
request.setAttribute(Constants.REQUEST_PAGE_SIZE, pageSize);
|
||||
});
|
||||
return ComponentUtil.getSearchEngineClient().search(fessConfig.getIndexDocumentSearchIndex(), searchRequestBuilder -> {
|
||||
queryHelper.processSearchPreference(searchRequestBuilder, userBean, query);
|
||||
return SearchConditionBuilder.builder(searchRequestBuilder).query(query).offset(params.getStartPosition()).size(pageSize)
|
||||
.facetInfo(params.getFacetInfo()).geoInfo(params.getGeoInfo()).highlightInfo(params.getHighlightInfo())
|
||||
.similarDocHash(params.getSimilarDocHash()).responseFields(params.getResponseFields())
|
||||
.searchRequestType(params.getType()).trackTotalHits(params.getTrackTotalHits()).build();
|
||||
}, (searchRequestBuilder, execTime, searchResponse) -> {
|
||||
searchResponse.ifPresent(r -> {
|
||||
if (r.getTotalShards() != r.getSuccessfulShards() && fessConfig.isQueryTimeoutLogging()) {
|
||||
// partial results
|
||||
final StringBuilder buf = new StringBuilder(1000);
|
||||
buf.append("[SEARCH TIMEOUT] {\"exec_time\":").append(execTime)//
|
||||
.append(",\"request\":").append(searchRequestBuilder.toString())//
|
||||
.append(",\"response\":").append(r.toString()).append('}');
|
||||
logger.warn(buf.toString());
|
||||
}
|
||||
});
|
||||
final QueryResponseList queryResponseList = ComponentUtil.getQueryResponseList();
|
||||
queryResponseList.init(searchResponse, params.getStartPosition(), params.getPageSize());
|
||||
return queryResponseList;
|
||||
});
|
||||
return ComponentUtil.getRankFusionProcessor().search(query, params, userBean);
|
||||
}
|
||||
|
||||
public long scrollSearch(final SearchRequestParams params, final BooleanFunction<Map<String, Object>> cursor,
|
||||
|
|
|
@ -1140,6 +1140,18 @@ public interface FessConfig extends FessEnv, org.codelibs.fess.mylasta.direction
|
|||
* */
|
||||
String QUERY_FACET_QUERIES = "query.facet.queries";
|
||||
|
||||
/** The key of the configuration. e.g. 200 */
|
||||
String RANK_FUSION_window_size = "rank.fusion.window_size";
|
||||
|
||||
/** The key of the configuration. e.g. 20 */
|
||||
String RANK_FUSION_rank_constant = "rank.fusion.rank_constant";
|
||||
|
||||
/** The key of the configuration. e.g. -1 */
|
||||
String RANK_FUSION_THREADS = "rank.fusion.threads";
|
||||
|
||||
/** The key of the configuration. e.g. rf_score */
|
||||
String RANK_FUSION_score_field = "rank.fusion.score_field";
|
||||
|
||||
/** The key of the configuration. e.g. true */
|
||||
String SMB_ROLE_FROM_FILE = "smb.role.from.file";
|
||||
|
||||
|
@ -5345,6 +5357,60 @@ public interface FessConfig extends FessEnv, org.codelibs.fess.mylasta.direction
|
|||
*/
|
||||
String getQueryFacetQueries();
|
||||
|
||||
/**
|
||||
* Get the value for the key 'rank.fusion.window_size'. <br>
|
||||
* The value is, e.g. 200 <br>
|
||||
* comment: ranking
|
||||
* @return The value of found property. (NotNull: if not found, exception but basically no way)
|
||||
*/
|
||||
String getRankFusionWindowSize();
|
||||
|
||||
/**
|
||||
* Get the value for the key 'rank.fusion.window_size' as {@link Integer}. <br>
|
||||
* The value is, e.g. 200 <br>
|
||||
* comment: ranking
|
||||
* @return The value of found property. (NotNull: if not found, exception but basically no way)
|
||||
* @throws NumberFormatException When the property is not integer.
|
||||
*/
|
||||
Integer getRankFusionWindowSizeAsInteger();
|
||||
|
||||
/**
|
||||
* Get the value for the key 'rank.fusion.rank_constant'. <br>
|
||||
* The value is, e.g. 20 <br>
|
||||
* @return The value of found property. (NotNull: if not found, exception but basically no way)
|
||||
*/
|
||||
String getRankFusionRankConstant();
|
||||
|
||||
/**
|
||||
* Get the value for the key 'rank.fusion.rank_constant' as {@link Integer}. <br>
|
||||
* The value is, e.g. 20 <br>
|
||||
* @return The value of found property. (NotNull: if not found, exception but basically no way)
|
||||
* @throws NumberFormatException When the property is not integer.
|
||||
*/
|
||||
Integer getRankFusionRankConstantAsInteger();
|
||||
|
||||
/**
|
||||
* Get the value for the key 'rank.fusion.threads'. <br>
|
||||
* The value is, e.g. -1 <br>
|
||||
* @return The value of found property. (NotNull: if not found, exception but basically no way)
|
||||
*/
|
||||
String getRankFusionThreads();
|
||||
|
||||
/**
|
||||
* Get the value for the key 'rank.fusion.threads' as {@link Integer}. <br>
|
||||
* The value is, e.g. -1 <br>
|
||||
* @return The value of found property. (NotNull: if not found, exception but basically no way)
|
||||
* @throws NumberFormatException When the property is not integer.
|
||||
*/
|
||||
Integer getRankFusionThreadsAsInteger();
|
||||
|
||||
/**
|
||||
* Get the value for the key 'rank.fusion.score_field'. <br>
|
||||
* The value is, e.g. rf_score <br>
|
||||
* @return The value of found property. (NotNull: if not found, exception but basically no way)
|
||||
*/
|
||||
String getRankFusionScoreField();
|
||||
|
||||
/**
|
||||
* Get the value for the key 'smb.role.from.file'. <br>
|
||||
* The value is, e.g. true <br>
|
||||
|
@ -9448,6 +9514,34 @@ public interface FessConfig extends FessEnv, org.codelibs.fess.mylasta.direction
|
|||
return get(FessConfig.QUERY_FACET_QUERIES);
|
||||
}
|
||||
|
||||
public String getRankFusionWindowSize() {
|
||||
return get(FessConfig.RANK_FUSION_window_size);
|
||||
}
|
||||
|
||||
public Integer getRankFusionWindowSizeAsInteger() {
|
||||
return getAsInteger(FessConfig.RANK_FUSION_window_size);
|
||||
}
|
||||
|
||||
public String getRankFusionRankConstant() {
|
||||
return get(FessConfig.RANK_FUSION_rank_constant);
|
||||
}
|
||||
|
||||
public Integer getRankFusionRankConstantAsInteger() {
|
||||
return getAsInteger(FessConfig.RANK_FUSION_rank_constant);
|
||||
}
|
||||
|
||||
public String getRankFusionThreads() {
|
||||
return get(FessConfig.RANK_FUSION_THREADS);
|
||||
}
|
||||
|
||||
public Integer getRankFusionThreadsAsInteger() {
|
||||
return getAsInteger(FessConfig.RANK_FUSION_THREADS);
|
||||
}
|
||||
|
||||
public String getRankFusionScoreField() {
|
||||
return get(FessConfig.RANK_FUSION_score_field);
|
||||
}
|
||||
|
||||
public String getSmbRoleFromFile() {
|
||||
return get(FessConfig.SMB_ROLE_FROM_FILE);
|
||||
}
|
||||
|
@ -11019,6 +11113,10 @@ public interface FessConfig extends FessEnv, org.codelibs.fess.mylasta.direction
|
|||
defaultMap.put(FessConfig.QUERY_FACET_FIELDS_MISSING, "");
|
||||
defaultMap.put(FessConfig.QUERY_FACET_QUERIES,
|
||||
"labels.facet_timestamp_title:labels.facet_timestamp_1day=timestamp:[now/d-1d TO *]\tlabels.facet_timestamp_1week=timestamp:[now/d-7d TO *]\tlabels.facet_timestamp_1month=timestamp:[now/d-1M TO *]\tlabels.facet_timestamp_1year=timestamp:[now/d-1y TO *]\nlabels.facet_contentLength_title:labels.facet_contentLength_10k=content_length:[0 TO 9999]\tlabels.facet_contentLength_10kto100k=content_length:[10000 TO 99999]\tlabels.facet_contentLength_100kto500k=content_length:[100000 TO 499999]\tlabels.facet_contentLength_500kto1m=content_length:[500000 TO 999999]\tlabels.facet_contentLength_1m=content_length:[1000000 TO *]\nlabels.facet_filetype_title:labels.facet_filetype_html=filetype:html\tlabels.facet_filetype_word=filetype:word\tlabels.facet_filetype_excel=filetype:excel\tlabels.facet_filetype_powerpoint=filetype:powerpoint\tlabels.facet_filetype_odt=filetype:odt\tlabels.facet_filetype_ods=filetype:ods\tlabels.facet_filetype_odp=filetype:odp\tlabels.facet_filetype_pdf=filetype:pdf\tlabels.facet_filetype_txt=filetype:txt\tlabels.facet_filetype_others=filetype:others\n");
|
||||
defaultMap.put(FessConfig.RANK_FUSION_window_size, "200");
|
||||
defaultMap.put(FessConfig.RANK_FUSION_rank_constant, "20");
|
||||
defaultMap.put(FessConfig.RANK_FUSION_THREADS, "-1");
|
||||
defaultMap.put(FessConfig.RANK_FUSION_score_field, "rf_score");
|
||||
defaultMap.put(FessConfig.SMB_ROLE_FROM_FILE, "true");
|
||||
defaultMap.put(FessConfig.SMB_AVAILABLE_SID_TYPES, "1,2,4:2,5:1");
|
||||
defaultMap.put(FessConfig.FILE_ROLE_FROM_FILE, "true");
|
||||
|
|
186
src/main/java/org/codelibs/fess/rank/fusion/DefaultSearcher.java
Normal file
186
src/main/java/org/codelibs/fess/rank/fusion/DefaultSearcher.java
Normal file
|
@ -0,0 +1,186 @@
|
|||
/*
|
||||
* Copyright 2012-2023 CodeLibs Project and the Others.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
|
||||
* either express or implied. See the License for the specific language
|
||||
* governing permissions and limitations under the License.
|
||||
*/
|
||||
package org.codelibs.fess.rank.fusion;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.codelibs.core.stream.StreamUtil;
|
||||
import org.codelibs.fess.Constants;
|
||||
import org.codelibs.fess.entity.SearchRequestParams;
|
||||
import org.codelibs.fess.es.client.SearchEngineClient.SearchConditionBuilder;
|
||||
import org.codelibs.fess.helper.ViewHelper;
|
||||
import org.codelibs.fess.mylasta.action.FessUserBean;
|
||||
import org.codelibs.fess.mylasta.direction.FessConfig;
|
||||
import org.codelibs.fess.rank.fusion.SearchResult.SearchResultBuilder;
|
||||
import org.codelibs.fess.util.ComponentUtil;
|
||||
import org.codelibs.fess.util.FacetResponse;
|
||||
import org.dbflute.optional.OptionalEntity;
|
||||
import org.dbflute.optional.OptionalThing;
|
||||
import org.lastaflute.web.util.LaRequestUtil;
|
||||
import org.opensearch.action.search.SearchResponse;
|
||||
import org.opensearch.common.document.DocumentField;
|
||||
import org.opensearch.search.SearchHit;
|
||||
import org.opensearch.search.SearchHits;
|
||||
import org.opensearch.search.aggregations.Aggregations;
|
||||
import org.opensearch.search.fetch.subphase.highlight.HighlightField;
|
||||
|
||||
public class DefaultSearcher extends RankFusionSearcher {
|
||||
|
||||
private static final Logger logger = LogManager.getLogger(DefaultSearcher.class);
|
||||
|
||||
@Override
|
||||
protected SearchResult search(final String query, final SearchRequestParams params, final OptionalThing<FessUserBean> userBean) {
|
||||
final int pageSize = params.getPageSize();
|
||||
LaRequestUtil.getOptionalRequest().ifPresent(request -> {
|
||||
request.setAttribute(Constants.REQUEST_PAGE_SIZE, pageSize);
|
||||
});
|
||||
final OptionalEntity<SearchResponse> searchResponseOpt = sendRequest(query, params, userBean);
|
||||
return processResponse(searchResponseOpt);
|
||||
}
|
||||
|
||||
protected SearchResult processResponse(final OptionalEntity<SearchResponse> searchResponseOpt) {
|
||||
final FessConfig fessConfig = ComponentUtil.getFessConfig();
|
||||
final SearchResultBuilder builder = SearchResult.create();
|
||||
searchResponseOpt.ifPresent(searchResponse -> {
|
||||
final SearchHits searchHits = searchResponse.getHits();
|
||||
builder.allRecordCount(searchHits.getTotalHits().value);
|
||||
builder.allRecordCountRelation(searchHits.getTotalHits().relation.toString());
|
||||
builder.queryTime(searchResponse.getTook().millis());
|
||||
|
||||
if (searchResponse.getTotalShards() != searchResponse.getSuccessfulShards()) {
|
||||
builder.partialResults(true);
|
||||
}
|
||||
|
||||
// build highlighting fields
|
||||
final String hlPrefix = ComponentUtil.getQueryHelper().getHighlightPrefix();
|
||||
for (final SearchHit searchHit : searchHits.getHits()) {
|
||||
final Map<String, Object> docMap = parseSearchHit(fessConfig, hlPrefix, searchHit);
|
||||
|
||||
if (fessConfig.isResultCollapsed()) {
|
||||
final Map<String, SearchHits> innerHits = searchHit.getInnerHits();
|
||||
if (innerHits != null) {
|
||||
final SearchHits innerSearchHits = innerHits.get(fessConfig.getQueryCollapseInnerHitsName());
|
||||
if (innerSearchHits != null) {
|
||||
final long totalHits = innerSearchHits.getTotalHits().value;
|
||||
if (totalHits > 1) {
|
||||
docMap.put(fessConfig.getQueryCollapseInnerHitsName() + "_count", totalHits);
|
||||
final DocumentField bitsField = searchHit.getFields().get(fessConfig.getIndexFieldContentMinhashBits());
|
||||
if (bitsField != null && !bitsField.getValues().isEmpty()) {
|
||||
docMap.put(fessConfig.getQueryCollapseInnerHitsName() + "_hash", bitsField.getValues().get(0));
|
||||
}
|
||||
docMap.put(fessConfig.getQueryCollapseInnerHitsName(), StreamUtil.stream(innerSearchHits.getHits())
|
||||
.get(stream -> stream.map(v -> parseSearchHit(fessConfig, hlPrefix, v)).toArray(n -> new Map[n])));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
builder.addDocument(docMap);
|
||||
}
|
||||
|
||||
// facet
|
||||
final Aggregations aggregations = searchResponse.getAggregations();
|
||||
if (aggregations != null) {
|
||||
builder.facetResponse(new FacetResponse(aggregations));
|
||||
}
|
||||
|
||||
});
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
protected OptionalEntity<SearchResponse> sendRequest(final String query, final SearchRequestParams params,
|
||||
final OptionalThing<FessUserBean> userBean) {
|
||||
final FessConfig fessConfig = ComponentUtil.getFessConfig();
|
||||
final int pageSize = params.getPageSize();
|
||||
return ComponentUtil.getSearchEngineClient().search(fessConfig.getIndexDocumentSearchIndex(), searchRequestBuilder -> {
|
||||
ComponentUtil.getQueryHelper().processSearchPreference(searchRequestBuilder, userBean, query);
|
||||
return SearchConditionBuilder.builder(searchRequestBuilder).query(query).offset(params.getStartPosition()).size(pageSize)
|
||||
.facetInfo(params.getFacetInfo()).geoInfo(params.getGeoInfo()).highlightInfo(params.getHighlightInfo())
|
||||
.similarDocHash(params.getSimilarDocHash()).responseFields(params.getResponseFields())
|
||||
.searchRequestType(params.getType()).trackTotalHits(params.getTrackTotalHits()).build();
|
||||
}, (searchRequestBuilder, execTime, searchResponse) -> {
|
||||
searchResponse.ifPresent(r -> {
|
||||
if (r.getTotalShards() != r.getSuccessfulShards() && fessConfig.isQueryTimeoutLogging()) {
|
||||
// partial results
|
||||
final StringBuilder buf = new StringBuilder(1000);
|
||||
buf.append("[SEARCH TIMEOUT] {\"exec_time\":").append(execTime)//
|
||||
.append(",\"request\":").append(searchRequestBuilder.toString())//
|
||||
.append(",\"response\":").append(r.toString()).append('}');
|
||||
logger.warn(buf.toString());
|
||||
}
|
||||
});
|
||||
return searchResponse;
|
||||
});
|
||||
}
|
||||
|
||||
protected Map<String, Object> parseSearchHit(final FessConfig fessConfig, final String hlPrefix, final SearchHit searchHit) {
|
||||
final Map<String, Object> docMap = new HashMap<>(32);
|
||||
if (searchHit.getSourceAsMap() == null) {
|
||||
searchHit.getFields().forEach((key, value) -> {
|
||||
docMap.put(key, value.getValue());
|
||||
});
|
||||
} else {
|
||||
docMap.putAll(searchHit.getSourceAsMap());
|
||||
}
|
||||
|
||||
final ViewHelper viewHelper = ComponentUtil.getViewHelper();
|
||||
|
||||
final Map<String, HighlightField> highlightFields = searchHit.getHighlightFields();
|
||||
try {
|
||||
if (highlightFields != null) {
|
||||
highlightFields.values().stream().forEach(highlightField -> {
|
||||
final String text = viewHelper.createHighlightText(highlightField);
|
||||
if (text != null) {
|
||||
docMap.put(hlPrefix + highlightField.getName(), text);
|
||||
}
|
||||
});
|
||||
if (Constants.TEXT_FRAGMENT_TYPE_HIGHLIGHT.equals(fessConfig.getQueryHighlightTextFragmentType())) {
|
||||
docMap.put(Constants.TEXT_FRAGMENTS,
|
||||
viewHelper.createTextFragmentsByHighlight(highlightFields.values().toArray(n -> new HighlightField[n])));
|
||||
}
|
||||
}
|
||||
} catch (final Exception e) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Could not create a highlighting value: {}", docMap, e);
|
||||
}
|
||||
}
|
||||
|
||||
if (Constants.TEXT_FRAGMENT_TYPE_QUERY.equals(fessConfig.getQueryHighlightTextFragmentType())) {
|
||||
docMap.put(Constants.TEXT_FRAGMENTS, viewHelper.createTextFragmentsByQuery());
|
||||
}
|
||||
|
||||
// ContentTitle
|
||||
if (viewHelper != null) {
|
||||
docMap.put(fessConfig.getResponseFieldContentTitle(), viewHelper.getContentTitle(docMap));
|
||||
docMap.put(fessConfig.getResponseFieldContentDescription(), viewHelper.getContentDescription(docMap));
|
||||
docMap.put(fessConfig.getResponseFieldUrlLink(), viewHelper.getUrlLink(docMap));
|
||||
docMap.put(fessConfig.getResponseFieldSitePath(), viewHelper.getSitePath(docMap));
|
||||
}
|
||||
|
||||
if (!docMap.containsKey(Constants.SCORE)) {
|
||||
docMap.put(Constants.SCORE, searchHit.getScore());
|
||||
}
|
||||
|
||||
if (!docMap.containsKey(fessConfig.getIndexFieldId())) {
|
||||
docMap.put(fessConfig.getIndexFieldId(), searchHit.getId());
|
||||
}
|
||||
return docMap;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,311 @@
|
|||
/*
|
||||
* Copyright 2012-2023 CodeLibs Project and the Others.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
|
||||
* either express or implied. See the License for the specific language
|
||||
* governing permissions and limitations under the License.
|
||||
*/
|
||||
package org.codelibs.fess.rank.fusion;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.Future;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import javax.annotation.PostConstruct;
|
||||
import javax.annotation.PreDestroy;
|
||||
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.apache.lucene.search.TotalHits.Relation;
|
||||
import org.codelibs.fess.entity.FacetInfo;
|
||||
import org.codelibs.fess.entity.GeoInfo;
|
||||
import org.codelibs.fess.entity.HighlightInfo;
|
||||
import org.codelibs.fess.entity.SearchRequestParams;
|
||||
import org.codelibs.fess.mylasta.action.FessUserBean;
|
||||
import org.codelibs.fess.mylasta.direction.FessConfig;
|
||||
import org.codelibs.fess.util.ComponentUtil;
|
||||
import org.codelibs.fess.util.QueryResponseList;
|
||||
import org.dbflute.optional.OptionalThing;
|
||||
|
||||
public class RankFusionProcessor implements AutoCloseable {
|
||||
|
||||
private static final Logger logger = LogManager.getLogger(RankFusionProcessor.class);
|
||||
|
||||
protected RankFusionSearcher[] searchers = new RankFusionSearcher[1];
|
||||
|
||||
protected ExecutorService executorService;
|
||||
|
||||
protected int windowSize;
|
||||
|
||||
@PostConstruct
|
||||
public void init() {
|
||||
final FessConfig fessConfig = ComponentUtil.getFessConfig();
|
||||
final int maxPageSize = fessConfig.getPagingSearchPageMaxSizeAsInteger();
|
||||
final int windowSize = fessConfig.getRankFusionWindowSizeAsInteger();
|
||||
if (maxPageSize * 2 < windowSize) {
|
||||
logger.warn("rank.fusion.window_size is lower than paging.search.page.max.size. "
|
||||
+ "The window size should be 2x more than the page size. ({} * 2 <= {})", maxPageSize, windowSize);
|
||||
this.windowSize = 2 * maxPageSize;
|
||||
} else {
|
||||
this.windowSize = windowSize;
|
||||
}
|
||||
}
|
||||
|
||||
@PreDestroy
|
||||
public void close() throws Exception {
|
||||
if (executorService != null) {
|
||||
try {
|
||||
executorService.shutdown();
|
||||
executorService.awaitTermination(60, TimeUnit.SECONDS);
|
||||
} catch (final InterruptedException e) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Interrupted.", e);
|
||||
}
|
||||
} finally {
|
||||
executorService.shutdownNow();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public List<Map<String, Object>> search(final String query, final SearchRequestParams params,
|
||||
final OptionalThing<FessUserBean> userBean) {
|
||||
final int pageSize = params.getPageSize();
|
||||
if (searchers.length == 1) {
|
||||
final SearchResult searchResult = searchers[0].search(query, params, userBean);
|
||||
return new QueryResponseList(searchResult.getDocumentList(), searchResult.getAllRecordCount(),
|
||||
searchResult.getAllRecordCountRelation(), searchResult.getQueryTime(), searchResult.isPartialResults(),
|
||||
searchResult.getFacetResponse(), params.getStartPosition(), pageSize, 0);
|
||||
}
|
||||
|
||||
final int startPosition = params.getStartPosition();
|
||||
if (startPosition * 2 >= windowSize) {
|
||||
int offset = params.getOffset();
|
||||
if (offset < 0) {
|
||||
offset = 0;
|
||||
} else if (offset > windowSize / 2) {
|
||||
offset = windowSize / 2;
|
||||
}
|
||||
int start = startPosition - offset;
|
||||
if (start < 0) {
|
||||
start = 0;
|
||||
}
|
||||
final SearchRequestParams reqParams = new SearchRequestParamsWrapper(params, start, pageSize);
|
||||
final SearchResult searchResult = searchers[0].search(query, reqParams, userBean);
|
||||
long allRecordCount = searchResult.getAllRecordCount();
|
||||
if (Relation.EQUAL_TO.toString().equals(searchResult.getAllRecordCountRelation())) {
|
||||
allRecordCount += offset;
|
||||
}
|
||||
return new QueryResponseList(searchResult.getDocumentList(), allRecordCount, searchResult.getAllRecordCountRelation(),
|
||||
searchResult.getQueryTime(), searchResult.isPartialResults(), searchResult.getFacetResponse(),
|
||||
params.getStartPosition(), pageSize, offset);
|
||||
}
|
||||
|
||||
final FessConfig fessConfig = ComponentUtil.getFessConfig();
|
||||
final int rankConstant = fessConfig.getRankFusionRankConstantAsInteger();
|
||||
final int size = windowSize / searchers.length;
|
||||
final List<Future<SearchResult>> resultList = new ArrayList<>();
|
||||
for (int i = 0; i < searchers.length; i++) {
|
||||
final SearchRequestParams reqParams = new SearchRequestParamsWrapper(params, 0, i == 0 ? windowSize : size);
|
||||
final RankFusionSearcher searcher = searchers[i];
|
||||
resultList.add(executorService.submit(() -> searcher.search(query, reqParams, userBean)));
|
||||
}
|
||||
final SearchResult[] results = resultList.stream().map(f -> {
|
||||
try {
|
||||
return f.get();
|
||||
} catch (InterruptedException | ExecutionException e) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Failed to process a search result.", e);
|
||||
}
|
||||
return SearchResult.create().build();
|
||||
}
|
||||
}).toArray(n -> new SearchResult[n]);
|
||||
|
||||
final String scoreField = fessConfig.getRankFusionScoreField();
|
||||
final Map<String, Map<String, Object>> scoreDocMap = new HashMap<>();
|
||||
final String idField = fessConfig.getIndexFieldId();
|
||||
final Set<Object> mainIdSet = new HashSet<>();
|
||||
for (int i = 0; i < results.length; i++) {
|
||||
final List<Map<String, Object>> docList = results[i].getDocumentList();
|
||||
for (int j = 0; j < docList.size(); j++) {
|
||||
final Map<String, Object> doc = docList.get(j);
|
||||
if (doc.get(idField) instanceof final String id) {
|
||||
final float score = 1.0f / (rankConstant + j);
|
||||
if (scoreDocMap.containsKey(id)) {
|
||||
Map<String, Object> baseDoc = scoreDocMap.get(id);
|
||||
float oldScore = toFloat(baseDoc.get(scoreField));
|
||||
baseDoc.put(scoreField, oldScore + score);
|
||||
} else {
|
||||
doc.put(scoreField, Float.valueOf(score));
|
||||
scoreDocMap.put(id, doc);
|
||||
}
|
||||
if (i == 0 && j < windowSize / 2) {
|
||||
mainIdSet.add(id);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
final var docs = scoreDocMap.values().stream()
|
||||
.sorted((e1, e2) -> Float.compare(toFloat(e2.get(scoreField)), toFloat(e1.get(scoreField)))).toList();
|
||||
int offset = 0;
|
||||
for (int i = 0; i < windowSize / 2; i++) {
|
||||
if (!mainIdSet.contains(docs.get(i).get(idField))) {
|
||||
offset++;
|
||||
}
|
||||
}
|
||||
final SearchResult mainResult = results[0];
|
||||
long allRecordCount = mainResult.getAllRecordCount();
|
||||
if (Relation.EQUAL_TO.toString().equals(mainResult.getAllRecordCountRelation())) {
|
||||
allRecordCount += offset;
|
||||
}
|
||||
return new QueryResponseList(docs.subList(startPosition, startPosition + pageSize), allRecordCount,
|
||||
mainResult.getAllRecordCountRelation(), mainResult.getQueryTime(), mainResult.isPartialResults(),
|
||||
mainResult.getFacetResponse(), startPosition, pageSize, offset);
|
||||
}
|
||||
|
||||
protected float toFloat(final Object value) {
|
||||
if (value instanceof final Float f) {
|
||||
return f;
|
||||
}
|
||||
if (value instanceof final String s) {
|
||||
return Float.parseFloat(s);
|
||||
}
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
protected static class SearchRequestParamsWrapper extends SearchRequestParams {
|
||||
private final SearchRequestParams parent;
|
||||
private final int startPosition;
|
||||
private final int pageSize;
|
||||
|
||||
SearchRequestParamsWrapper(final SearchRequestParams parent, final int startPosition, final int pageSize) {
|
||||
this.parent = parent;
|
||||
this.startPosition = startPosition;
|
||||
this.pageSize = pageSize;
|
||||
}
|
||||
|
||||
public SearchRequestParams getParent() {
|
||||
return parent;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getQuery() {
|
||||
return parent.getQuery();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, String[]> getFields() {
|
||||
return parent.getFields();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, String[]> getConditions() {
|
||||
return parent.getConditions();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] getLanguages() {
|
||||
return parent.getLanguages();
|
||||
}
|
||||
|
||||
@Override
|
||||
public GeoInfo getGeoInfo() {
|
||||
return parent.getGeoInfo();
|
||||
}
|
||||
|
||||
@Override
|
||||
public FacetInfo getFacetInfo() {
|
||||
return parent.getFacetInfo();
|
||||
}
|
||||
|
||||
@Override
|
||||
public HighlightInfo getHighlightInfo() {
|
||||
return parent.getHighlightInfo();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getSort() {
|
||||
return parent.getSort();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getStartPosition() {
|
||||
return startPosition;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getOffset() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getPageSize() {
|
||||
return pageSize;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] getExtraQueries() {
|
||||
return parent.getExtraQueries();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getAttribute(final String name) {
|
||||
return parent.getAttribute(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Locale getLocale() {
|
||||
return parent.getLocale();
|
||||
}
|
||||
|
||||
@Override
|
||||
public SearchRequestType getType() {
|
||||
return parent.getType();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getSimilarDocHash() {
|
||||
return parent.getSimilarDocHash();
|
||||
}
|
||||
}
|
||||
|
||||
public void setSeacher(final RankFusionSearcher searcher) {
|
||||
this.searchers[0] = searcher;
|
||||
}
|
||||
|
||||
public void register(final RankFusionSearcher searcher) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Load {}", searcher.getClass().getSimpleName());
|
||||
}
|
||||
final RankFusionSearcher[] newSearchers = Arrays.copyOf(searchers, searchers.length + 1);
|
||||
newSearchers[newSearchers.length - 1] = searcher;
|
||||
searchers = newSearchers;
|
||||
synchronized (this) {
|
||||
if (executorService == null) {
|
||||
int numThreads = ComponentUtil.getFessConfig().getRankFusionThreadsAsInteger();
|
||||
if (numThreads <= 0) {
|
||||
numThreads = (Runtime.getRuntime().availableProcessors() * 3) / 2 + 1;
|
||||
}
|
||||
executorService = Executors.newFixedThreadPool(numThreads);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
/*
|
||||
* Copyright 2012-2023 CodeLibs Project and the Others.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
|
||||
* either express or implied. See the License for the specific language
|
||||
* governing permissions and limitations under the License.
|
||||
*/
|
||||
package org.codelibs.fess.rank.fusion;
|
||||
|
||||
import org.codelibs.fess.entity.SearchRequestParams;
|
||||
import org.codelibs.fess.mylasta.action.FessUserBean;
|
||||
import org.dbflute.optional.OptionalThing;
|
||||
|
||||
public abstract class RankFusionSearcher {
|
||||
|
||||
protected abstract SearchResult search(String query, SearchRequestParams params, OptionalThing<FessUserBean> userBean);
|
||||
|
||||
}
|
120
src/main/java/org/codelibs/fess/rank/fusion/SearchResult.java
Normal file
120
src/main/java/org/codelibs/fess/rank/fusion/SearchResult.java
Normal file
|
@ -0,0 +1,120 @@
|
|||
/*
|
||||
* Copyright 2012-2023 CodeLibs Project and the Others.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
|
||||
* either express or implied. See the License for the specific language
|
||||
* governing permissions and limitations under the License.
|
||||
*/
|
||||
package org.codelibs.fess.rank.fusion;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.apache.lucene.search.TotalHits.Relation;
|
||||
import org.codelibs.fess.util.FacetResponse;
|
||||
|
||||
public class SearchResult {
|
||||
|
||||
protected final List<Map<String, Object>> documentList;
|
||||
protected final long allRecordCount;
|
||||
protected final String allRecordCountRelation;
|
||||
protected final long queryTime;
|
||||
protected final boolean partialResults;
|
||||
protected final FacetResponse facetResponse;
|
||||
|
||||
SearchResult(final List<Map<String, Object>> documentList, final long allRecordCount, final String allRecordCountRelation,
|
||||
final long queryTime, final boolean partialResults, final FacetResponse facetResponse) {
|
||||
this.documentList = documentList;
|
||||
this.allRecordCount = allRecordCount;
|
||||
this.allRecordCountRelation = allRecordCountRelation;
|
||||
this.queryTime = queryTime;
|
||||
this.partialResults = partialResults;
|
||||
this.facetResponse = facetResponse;
|
||||
}
|
||||
|
||||
public List<Map<String, Object>> getDocumentList() {
|
||||
return documentList;
|
||||
}
|
||||
|
||||
public long getAllRecordCount() {
|
||||
return allRecordCount;
|
||||
}
|
||||
|
||||
public String getAllRecordCountRelation() {
|
||||
return allRecordCountRelation;
|
||||
}
|
||||
|
||||
public long getQueryTime() {
|
||||
return queryTime;
|
||||
}
|
||||
|
||||
public boolean isPartialResults() {
|
||||
return partialResults;
|
||||
}
|
||||
|
||||
public FacetResponse getFacetResponse() {
|
||||
return facetResponse;
|
||||
}
|
||||
|
||||
public static SearchResultBuilder create() {
|
||||
return new SearchResultBuilder();
|
||||
}
|
||||
|
||||
static class SearchResultBuilder {
|
||||
|
||||
private long allRecordCount;
|
||||
private String allRecordCountRelation = Relation.GREATER_THAN_OR_EQUAL_TO.toString();
|
||||
private long queryTime;
|
||||
private boolean partialResults;
|
||||
private FacetResponse facetResponse;
|
||||
private final List<Map<String, Object>> documentList = new ArrayList<>();
|
||||
|
||||
public SearchResultBuilder allRecordCount(final long allRecordCount) {
|
||||
this.allRecordCount = allRecordCount;
|
||||
return this;
|
||||
}
|
||||
|
||||
public SearchResultBuilder allRecordCountRelation(final String allRecordCountRelation) {
|
||||
this.allRecordCountRelation = allRecordCountRelation;
|
||||
return this;
|
||||
}
|
||||
|
||||
public SearchResultBuilder queryTime(final long queryTime) {
|
||||
this.queryTime = queryTime;
|
||||
return this;
|
||||
}
|
||||
|
||||
public SearchResultBuilder partialResults(final boolean partialResults) {
|
||||
this.partialResults = partialResults;
|
||||
return this;
|
||||
}
|
||||
|
||||
public SearchResultBuilder addDocument(final Map<String, Object> doc) {
|
||||
documentList.add(doc);
|
||||
return this;
|
||||
}
|
||||
|
||||
public SearchResultBuilder facetResponse(final FacetResponse facetResponse) {
|
||||
this.facetResponse = facetResponse;
|
||||
return this;
|
||||
}
|
||||
|
||||
public SearchResult build() {
|
||||
return new SearchResult(documentList, //
|
||||
allRecordCount, //
|
||||
allRecordCountRelation, //
|
||||
queryTime, //
|
||||
partialResults, //
|
||||
facetResponse);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -81,6 +81,7 @@ import org.codelibs.fess.mylasta.direction.FessProp;
|
|||
import org.codelibs.fess.query.QueryFieldConfig;
|
||||
import org.codelibs.fess.query.QueryProcessor;
|
||||
import org.codelibs.fess.query.parser.QueryParser;
|
||||
import org.codelibs.fess.rank.fusion.RankFusionProcessor;
|
||||
import org.codelibs.fess.script.ScriptEngineFactory;
|
||||
import org.codelibs.fess.sso.SsoManager;
|
||||
import org.codelibs.fess.thumbnail.ThumbnailManager;
|
||||
|
@ -196,8 +197,6 @@ public final class ComponentUtil {
|
|||
|
||||
private static final String CRAWLER_PROPERTIES = "systemProperties";
|
||||
|
||||
private static final String QUERY_RESPONSE_LIST = "queryResponseList";
|
||||
|
||||
private static final String JOB_EXECUTOR_SUFFIX = "JobExecutor";
|
||||
|
||||
private static final String KEY_MATCH_HELPER = "keyMatchHelper";
|
||||
|
@ -214,6 +213,8 @@ public final class ComponentUtil {
|
|||
|
||||
private static final String CORS_HANDLER_FACTORY = "corsHandlerFactory";
|
||||
|
||||
private static final String RANK_FUSION_PROCESSOR = "rankFusionProcessor";
|
||||
|
||||
private static IndexingHelper indexingHelper;
|
||||
|
||||
private static CrawlingConfigHelper crawlingConfigHelper;
|
||||
|
@ -247,10 +248,6 @@ public final class ComponentUtil {
|
|||
return getComponent(cipherName);
|
||||
}
|
||||
|
||||
public static QueryResponseList getQueryResponseList() {
|
||||
return getComponent(QUERY_RESPONSE_LIST);
|
||||
}
|
||||
|
||||
public static DynamicProperties getSystemProperties() {
|
||||
return getComponent(CRAWLER_PROPERTIES);
|
||||
}
|
||||
|
@ -519,6 +516,10 @@ public final class ComponentUtil {
|
|||
return getComponent(CORS_HANDLER_FACTORY);
|
||||
}
|
||||
|
||||
public static RankFusionProcessor getRankFusionProcessor() {
|
||||
return getComponent(RANK_FUSION_PROCESSOR);
|
||||
}
|
||||
|
||||
public static <T> T getComponent(final Class<T> clazz) {
|
||||
try {
|
||||
return SingletonLaContainer.getComponent(clazz);
|
||||
|
|
|
@ -94,4 +94,9 @@ public class FacetResponse {
|
|||
return fieldList;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "FacetResponse [queryCountMap=" + queryCountMap + ", fieldList=" + fieldList + "]";
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -17,35 +17,21 @@ package org.codelibs.fess.util;
|
|||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.ListIterator;
|
||||
import java.util.Map;
|
||||
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.codelibs.core.stream.StreamUtil;
|
||||
import org.codelibs.fess.Constants;
|
||||
import org.codelibs.fess.helper.QueryHelper;
|
||||
import org.codelibs.fess.helper.ViewHelper;
|
||||
import org.codelibs.fess.mylasta.direction.FessConfig;
|
||||
import org.dbflute.optional.OptionalEntity;
|
||||
import org.opensearch.action.search.SearchResponse;
|
||||
import org.opensearch.common.document.DocumentField;
|
||||
import org.opensearch.search.SearchHit;
|
||||
import org.opensearch.search.SearchHits;
|
||||
import org.opensearch.search.aggregations.Aggregations;
|
||||
import org.opensearch.search.fetch.subphase.highlight.HighlightField;
|
||||
|
||||
public class QueryResponseList implements List<Map<String, Object>> {
|
||||
|
||||
private static final Logger logger = LogManager.getLogger(QueryResponseList.class);
|
||||
|
||||
protected final List<Map<String, Object>> parent;
|
||||
|
||||
protected final int start;
|
||||
|
||||
protected final int offset;
|
||||
|
||||
/** The value of current page number. */
|
||||
protected int pageSize;
|
||||
protected final int pageSize;
|
||||
|
||||
/** The value of current page number. */
|
||||
protected int currentPageNumber;
|
||||
|
@ -76,127 +62,36 @@ public class QueryResponseList implements List<Map<String, Object>> {
|
|||
|
||||
protected long queryTime;
|
||||
|
||||
public QueryResponseList() {
|
||||
parent = new ArrayList<>();
|
||||
}
|
||||
|
||||
// for testing
|
||||
protected QueryResponseList(final List<Map<String, Object>> parent) {
|
||||
this.parent = parent;
|
||||
protected QueryResponseList(final List<Map<String, Object>> documentList, final int start, final int pageSize, final int offset) {
|
||||
this.parent = documentList;
|
||||
this.offset = offset;
|
||||
this.start = start;
|
||||
this.pageSize = pageSize;
|
||||
}
|
||||
|
||||
public void init(final OptionalEntity<SearchResponse> searchResponseOpt, final int start, final int pageSize) {
|
||||
searchResponseOpt.ifPresent(searchResponse -> {
|
||||
final FessConfig fessConfig = ComponentUtil.getFessConfig();
|
||||
final SearchHits searchHits = searchResponse.getHits();
|
||||
allRecordCount = searchHits.getTotalHits().value;
|
||||
allRecordCountRelation = searchHits.getTotalHits().relation.toString();
|
||||
queryTime = searchResponse.getTook().millis();
|
||||
|
||||
if (searchResponse.getTotalShards() != searchResponse.getSuccessfulShards()) {
|
||||
partialResults = true;
|
||||
}
|
||||
|
||||
// build highlighting fields
|
||||
final QueryHelper queryHelper = ComponentUtil.getQueryHelper();
|
||||
final String hlPrefix = queryHelper.getHighlightPrefix();
|
||||
for (final SearchHit searchHit : searchHits.getHits()) {
|
||||
final Map<String, Object> docMap = parseSearchHit(fessConfig, hlPrefix, searchHit);
|
||||
|
||||
if (fessConfig.isResultCollapsed()) {
|
||||
final Map<String, SearchHits> innerHits = searchHit.getInnerHits();
|
||||
if (innerHits != null) {
|
||||
final SearchHits innerSearchHits = innerHits.get(fessConfig.getQueryCollapseInnerHitsName());
|
||||
if (innerSearchHits != null) {
|
||||
final long totalHits = innerSearchHits.getTotalHits().value;
|
||||
if (totalHits > 1) {
|
||||
docMap.put(fessConfig.getQueryCollapseInnerHitsName() + "_count", totalHits);
|
||||
final DocumentField bitsField = searchHit.getFields().get(fessConfig.getIndexFieldContentMinhashBits());
|
||||
if (bitsField != null && !bitsField.getValues().isEmpty()) {
|
||||
docMap.put(fessConfig.getQueryCollapseInnerHitsName() + "_hash", bitsField.getValues().get(0));
|
||||
}
|
||||
docMap.put(fessConfig.getQueryCollapseInnerHitsName(), StreamUtil.stream(innerSearchHits.getHits())
|
||||
.get(stream -> stream.map(v -> parseSearchHit(fessConfig, hlPrefix, v)).toArray(n -> new Map[n])));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
parent.add(docMap);
|
||||
}
|
||||
|
||||
// facet
|
||||
final Aggregations aggregations = searchResponse.getAggregations();
|
||||
if (aggregations != null) {
|
||||
facetResponse = new FacetResponse(aggregations);
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
public QueryResponseList(final List<Map<String, Object>> documentList, final long allRecordCount, final String allRecordCountRelation,
|
||||
final long queryTime, final boolean partialResults, final FacetResponse facetResponse, final int start, final int pageSize,
|
||||
final int offset) {
|
||||
this(documentList, start, pageSize, offset);
|
||||
this.allRecordCount = allRecordCount;
|
||||
this.allRecordCountRelation = allRecordCountRelation;
|
||||
this.queryTime = queryTime;
|
||||
this.partialResults = partialResults;
|
||||
this.facetResponse = facetResponse;
|
||||
if (pageSize > 0) {
|
||||
calculatePageInfo(start, pageSize);
|
||||
calculatePageInfo();
|
||||
}
|
||||
}
|
||||
|
||||
protected Map<String, Object> parseSearchHit(final FessConfig fessConfig, final String hlPrefix, final SearchHit searchHit) {
|
||||
final Map<String, Object> docMap = new HashMap<>(32);
|
||||
if (searchHit.getSourceAsMap() == null) {
|
||||
searchHit.getFields().forEach((key, value) -> {
|
||||
docMap.put(key, value.getValue());
|
||||
});
|
||||
} else {
|
||||
docMap.putAll(searchHit.getSourceAsMap());
|
||||
protected void calculatePageInfo() {
|
||||
int startWithOffset = start - offset;
|
||||
if (startWithOffset < 0) {
|
||||
startWithOffset = 0;
|
||||
}
|
||||
|
||||
final ViewHelper viewHelper = ComponentUtil.getViewHelper();
|
||||
|
||||
final Map<String, HighlightField> highlightFields = searchHit.getHighlightFields();
|
||||
try {
|
||||
if (highlightFields != null) {
|
||||
highlightFields.values().stream().forEach(highlightField -> {
|
||||
final String text = viewHelper.createHighlightText(highlightField);
|
||||
if (text != null) {
|
||||
docMap.put(hlPrefix + highlightField.getName(), text);
|
||||
}
|
||||
});
|
||||
if (Constants.TEXT_FRAGMENT_TYPE_HIGHLIGHT.equals(fessConfig.getQueryHighlightTextFragmentType())) {
|
||||
docMap.put(Constants.TEXT_FRAGMENTS,
|
||||
viewHelper.createTextFragmentsByHighlight(highlightFields.values().toArray(n -> new HighlightField[n])));
|
||||
}
|
||||
}
|
||||
} catch (final Exception e) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Could not create a highlighting value: {}", docMap, e);
|
||||
}
|
||||
}
|
||||
|
||||
if (Constants.TEXT_FRAGMENT_TYPE_QUERY.equals(fessConfig.getQueryHighlightTextFragmentType())) {
|
||||
docMap.put(Constants.TEXT_FRAGMENTS, viewHelper.createTextFragmentsByQuery());
|
||||
}
|
||||
|
||||
// ContentTitle
|
||||
if (viewHelper != null) {
|
||||
docMap.put(fessConfig.getResponseFieldContentTitle(), viewHelper.getContentTitle(docMap));
|
||||
docMap.put(fessConfig.getResponseFieldContentDescription(), viewHelper.getContentDescription(docMap));
|
||||
docMap.put(fessConfig.getResponseFieldUrlLink(), viewHelper.getUrlLink(docMap));
|
||||
docMap.put(fessConfig.getResponseFieldSitePath(), viewHelper.getSitePath(docMap));
|
||||
}
|
||||
|
||||
if (!docMap.containsKey(Constants.SCORE)) {
|
||||
docMap.put(Constants.SCORE, searchHit.getScore());
|
||||
}
|
||||
|
||||
if (!docMap.containsKey(fessConfig.getIndexFieldId())) {
|
||||
docMap.put(fessConfig.getIndexFieldId(), searchHit.getId());
|
||||
}
|
||||
return docMap;
|
||||
}
|
||||
|
||||
protected void calculatePageInfo(final int start, final int size) {
|
||||
pageSize = size;
|
||||
allPageCount = (int) ((allRecordCount - 1) / pageSize) + 1;
|
||||
existPrevPage = start > 0;
|
||||
existNextPage = start < (long) (allPageCount - 1) * (long) pageSize;
|
||||
existPrevPage = startWithOffset > 0;
|
||||
existNextPage = startWithOffset < (long) (allPageCount - 1) * (long) pageSize;
|
||||
currentPageNumber = start / pageSize + 1;
|
||||
if (existNextPage && size() < pageSize) {
|
||||
// collapsing
|
||||
|
@ -347,6 +242,14 @@ public class QueryResponseList implements List<Map<String, Object>> {
|
|||
return parent.toArray(a);
|
||||
}
|
||||
|
||||
public int getStart() {
|
||||
return start;
|
||||
}
|
||||
|
||||
public int getOffset() {
|
||||
return offset;
|
||||
}
|
||||
|
||||
public int getPageSize() {
|
||||
return pageSize;
|
||||
}
|
||||
|
@ -415,4 +318,14 @@ public class QueryResponseList implements List<Map<String, Object>> {
|
|||
return queryTime;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "QueryResponseList [parent=" + parent + ", start=" + start + ", offset=" + offset + ", pageSize=" + pageSize
|
||||
+ ", currentPageNumber=" + currentPageNumber + ", allRecordCount=" + allRecordCount + ", allRecordCountRelation="
|
||||
+ allRecordCountRelation + ", allPageCount=" + allPageCount + ", existNextPage=" + existNextPage + ", existPrevPage="
|
||||
+ existPrevPage + ", currentStartRecordNumber=" + currentStartRecordNumber + ", currentEndRecordNumber="
|
||||
+ currentEndRecordNumber + ", pageNumberList=" + pageNumberList + ", searchQuery=" + searchQuery + ", execTime=" + execTime
|
||||
+ ", facetResponse=" + facetResponse + ", partialResults=" + partialResults + ", queryTime=" + queryTime + "]";
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -7,15 +7,16 @@
|
|||
<include path="lasta_job.xml"/>
|
||||
|
||||
<include path="fess.xml"/>
|
||||
<include path="fess_ldap.xml"/>
|
||||
<include path="fess_api.xml"/>
|
||||
<include path="fess_cors.xml"/>
|
||||
<include path="fess_dict.xml"/>
|
||||
<include path="fess_job.xml"/>
|
||||
<include path="fess_thumbnail.xml"/>
|
||||
<include path="fess_sso.xml"/>
|
||||
<include path="fess_score.xml"/>
|
||||
<include path="fess_ldap.xml"/>
|
||||
<include path="fess_query.xml"/>
|
||||
<include path="fess_rankfusion.xml"/>
|
||||
<include path="fess_score.xml"/>
|
||||
<include path="fess_sso.xml"/>
|
||||
<include path="fess_thumbnail.xml"/>
|
||||
|
||||
<include path="crawler/client.xml" />
|
||||
<include path="crawler/mimetype.xml" />
|
||||
|
@ -103,8 +104,6 @@
|
|||
</component>
|
||||
<component name="suggestHelper" class="org.codelibs.fess.helper.SuggestHelper">
|
||||
</component>
|
||||
<component name="queryResponseList" class="org.codelibs.fess.util.QueryResponseList" instance="prototype">
|
||||
</component>
|
||||
<component name="gsaConfigParser" class="org.codelibs.fess.util.GsaConfigParser" instance="prototype">
|
||||
</component>
|
||||
|
||||
|
|
|
@ -604,6 +604,12 @@ labels.facet_filetype_pdf=filetype:pdf\t\
|
|||
labels.facet_filetype_txt=filetype:txt\t\
|
||||
labels.facet_filetype_others=filetype:others\n\
|
||||
|
||||
# ranking
|
||||
rank.fusion.window_size=200
|
||||
rank.fusion.rank_constant=20
|
||||
rank.fusion.threads=-1
|
||||
rank.fusion.score_field=rf_score
|
||||
|
||||
# acl
|
||||
smb.role.from.file=true
|
||||
smb.available.sid.types=1,2,4:2,5:1
|
||||
|
|
15
src/main/resources/fess_rankfusion.xml
Normal file
15
src/main/resources/fess_rankfusion.xml
Normal file
|
@ -0,0 +1,15 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE components PUBLIC "-//DBFLUTE//DTD LastaDi 1.0//EN"
|
||||
"http://dbflute.org/meta/lastadi10.dtd">
|
||||
<components>
|
||||
<component name="rankFusionProcessor"
|
||||
class="org.codelibs.fess.rank.fusion.RankFusionProcessor">
|
||||
<postConstruct name="setSeacher">
|
||||
<arg>
|
||||
<component
|
||||
class="org.codelibs.fess.rank.fusion.DefaultSearcher">
|
||||
</component>
|
||||
</arg>
|
||||
</postConstruct>
|
||||
</component>
|
||||
</components>
|
|
@ -0,0 +1,371 @@
|
|||
/*
|
||||
* Copyright 2012-2023 CodeLibs Project and the Others.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
|
||||
* either express or implied. See the License for the specific language
|
||||
* governing permissions and limitations under the License.
|
||||
*/
|
||||
package org.codelibs.fess.rank.fusion;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
|
||||
import org.apache.lucene.search.TotalHits.Relation;
|
||||
import org.codelibs.fess.entity.FacetInfo;
|
||||
import org.codelibs.fess.entity.GeoInfo;
|
||||
import org.codelibs.fess.entity.HighlightInfo;
|
||||
import org.codelibs.fess.entity.SearchRequestParams;
|
||||
import org.codelibs.fess.mylasta.action.FessUserBean;
|
||||
import org.codelibs.fess.rank.fusion.SearchResult.SearchResultBuilder;
|
||||
import org.codelibs.fess.unit.UnitFessTestCase;
|
||||
import org.codelibs.fess.util.QueryResponseList;
|
||||
import org.dbflute.optional.OptionalThing;
|
||||
|
||||
public class RankFusionProcessorTest extends UnitFessTestCase {
|
||||
|
||||
private static final String ID_FIELD = "_id";
|
||||
|
||||
public void test_default_1000docs_10size() throws Exception {
|
||||
String query = "*";
|
||||
int allRecordCount = 1000;
|
||||
int pageSize = 10;
|
||||
try (RankFusionProcessor rankFusionProcessor = new RankFusionProcessor()) {
|
||||
rankFusionProcessor.setSeacher(new TestMainSearcher(allRecordCount));
|
||||
rankFusionProcessor.init();
|
||||
|
||||
if (rankFusionProcessor.search(query, new TestSearchRequestParams(0, pageSize, 0),
|
||||
OptionalThing.empty()) instanceof QueryResponseList list) {
|
||||
assertEquals(pageSize, list.size());
|
||||
assertEquals(allRecordCount, list.getAllRecordCount());
|
||||
assertEquals(100, list.getAllPageCount());
|
||||
assertEquals(10, list.getCurrentEndRecordNumber());
|
||||
assertEquals(1, list.getCurrentPageNumber());
|
||||
assertEquals(1, list.getCurrentStartRecordNumber());
|
||||
assertEquals(0, list.getOffset());
|
||||
assertEquals(10, list.getPageSize());
|
||||
assertEquals(0, list.getStart());
|
||||
assertEquals("0", list.get(0).get(ID_FIELD));
|
||||
assertEquals("9", list.get(9).get(ID_FIELD));
|
||||
} else {
|
||||
fail();
|
||||
}
|
||||
|
||||
if (rankFusionProcessor.search(query, new TestSearchRequestParams(10, pageSize, 0),
|
||||
OptionalThing.empty()) instanceof QueryResponseList list) {
|
||||
assertEquals(pageSize, list.size());
|
||||
assertEquals(allRecordCount, list.getAllRecordCount());
|
||||
assertEquals(100, list.getAllPageCount());
|
||||
assertEquals(20, list.getCurrentEndRecordNumber());
|
||||
assertEquals(2, list.getCurrentPageNumber());
|
||||
assertEquals(11, list.getCurrentStartRecordNumber());
|
||||
assertEquals(0, list.getOffset());
|
||||
assertEquals(10, list.getPageSize());
|
||||
assertEquals(10, list.getStart());
|
||||
assertEquals("10", list.get(0).get(ID_FIELD));
|
||||
assertEquals("19", list.get(9).get(ID_FIELD));
|
||||
} else {
|
||||
fail();
|
||||
}
|
||||
|
||||
if (rankFusionProcessor.search(query, new TestSearchRequestParams(990, pageSize, 0),
|
||||
OptionalThing.empty()) instanceof QueryResponseList list) {
|
||||
assertEquals(pageSize, list.size());
|
||||
assertEquals(allRecordCount, list.getAllRecordCount());
|
||||
assertEquals(100, list.getAllPageCount());
|
||||
assertEquals(1000, list.getCurrentEndRecordNumber());
|
||||
assertEquals(100, list.getCurrentPageNumber());
|
||||
assertEquals(991, list.getCurrentStartRecordNumber());
|
||||
assertEquals(0, list.getOffset());
|
||||
assertEquals(10, list.getPageSize());
|
||||
assertEquals(990, list.getStart());
|
||||
assertEquals("990", list.get(0).get(ID_FIELD));
|
||||
assertEquals("999", list.get(9).get(ID_FIELD));
|
||||
} else {
|
||||
fail();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void test_1searcher10_1000docs_100size() throws Exception {
|
||||
String query = "*";
|
||||
int allRecordCount = 1000;
|
||||
int pageSize = 100;
|
||||
int offset = 45;
|
||||
try (RankFusionProcessor rankFusionProcessor = new RankFusionProcessor()) {
|
||||
rankFusionProcessor.setSeacher(new TestMainSearcher(allRecordCount));
|
||||
rankFusionProcessor.register(new TestSubSearcher(10, 45, 45));
|
||||
rankFusionProcessor.init();
|
||||
|
||||
if (rankFusionProcessor.search(query, new TestSearchRequestParams(0, pageSize, 0),
|
||||
OptionalThing.empty()) instanceof QueryResponseList list) {
|
||||
assertEquals(pageSize, list.size());
|
||||
assertEquals(allRecordCount + offset, list.getAllRecordCount());
|
||||
assertEquals(11, list.getAllPageCount());
|
||||
assertEquals(100, list.getCurrentEndRecordNumber());
|
||||
assertEquals(1, list.getCurrentPageNumber());
|
||||
assertEquals(1, list.getCurrentStartRecordNumber());
|
||||
assertEquals(offset, list.getOffset());
|
||||
assertEquals(100, list.getPageSize());
|
||||
assertEquals(0, list.getStart());
|
||||
assertEquals("0", list.get(0).get(ID_FIELD));
|
||||
assertEquals("9", list.get(1).get(ID_FIELD));
|
||||
assertEquals("1", list.get(2).get(ID_FIELD));
|
||||
assertEquals("8", list.get(3).get(ID_FIELD));
|
||||
assertEquals("2", list.get(4).get(ID_FIELD));
|
||||
assertEquals("7", list.get(5).get(ID_FIELD));
|
||||
assertEquals("3", list.get(6).get(ID_FIELD));
|
||||
assertEquals("6", list.get(7).get(ID_FIELD));
|
||||
assertEquals("4", list.get(8).get(ID_FIELD));
|
||||
assertEquals("5", list.get(9).get(ID_FIELD));
|
||||
} else {
|
||||
fail();
|
||||
}
|
||||
|
||||
if (rankFusionProcessor.search(query, new TestSearchRequestParams(0, pageSize, offset),
|
||||
OptionalThing.empty()) instanceof QueryResponseList list) {
|
||||
assertEquals(pageSize, list.size());
|
||||
assertEquals(allRecordCount + offset, list.getAllRecordCount());
|
||||
assertEquals(11, list.getAllPageCount());
|
||||
assertEquals(100, list.getCurrentEndRecordNumber());
|
||||
assertEquals(1, list.getCurrentPageNumber());
|
||||
assertEquals(1, list.getCurrentStartRecordNumber());
|
||||
assertEquals(offset, list.getOffset());
|
||||
assertEquals(100, list.getPageSize());
|
||||
assertEquals(0, list.getStart());
|
||||
assertEquals("0", list.get(0).get(ID_FIELD));
|
||||
assertEquals("9", list.get(1).get(ID_FIELD));
|
||||
assertEquals("1", list.get(2).get(ID_FIELD));
|
||||
assertEquals("8", list.get(3).get(ID_FIELD));
|
||||
assertEquals("2", list.get(4).get(ID_FIELD));
|
||||
assertEquals("7", list.get(5).get(ID_FIELD));
|
||||
assertEquals("3", list.get(6).get(ID_FIELD));
|
||||
assertEquals("6", list.get(7).get(ID_FIELD));
|
||||
assertEquals("4", list.get(8).get(ID_FIELD));
|
||||
assertEquals("5", list.get(9).get(ID_FIELD));
|
||||
} else {
|
||||
fail();
|
||||
}
|
||||
|
||||
if (rankFusionProcessor.search(query, new TestSearchRequestParams(100, pageSize, offset),
|
||||
OptionalThing.empty()) instanceof QueryResponseList list) {
|
||||
assertEquals(pageSize, list.size());
|
||||
assertEquals(allRecordCount + offset, list.getAllRecordCount());
|
||||
assertEquals(11, list.getAllPageCount());
|
||||
assertEquals(200, list.getCurrentEndRecordNumber());
|
||||
assertEquals(2, list.getCurrentPageNumber());
|
||||
assertEquals(101, list.getCurrentStartRecordNumber());
|
||||
assertEquals(offset, list.getOffset());
|
||||
assertEquals(100, list.getPageSize());
|
||||
assertEquals(100, list.getStart());
|
||||
assertEquals("55", list.get(0).get(ID_FIELD));
|
||||
assertEquals("154", list.get(99).get(ID_FIELD));
|
||||
} else {
|
||||
fail();
|
||||
}
|
||||
|
||||
if (rankFusionProcessor.search(query, new TestSearchRequestParams(900, pageSize, offset),
|
||||
OptionalThing.empty()) instanceof QueryResponseList list) {
|
||||
assertEquals(pageSize, list.size());
|
||||
assertEquals(allRecordCount + offset, list.getAllRecordCount());
|
||||
assertEquals(11, list.getAllPageCount());
|
||||
assertEquals(1000, list.getCurrentEndRecordNumber());
|
||||
assertEquals(10, list.getCurrentPageNumber());
|
||||
assertEquals(901, list.getCurrentStartRecordNumber());
|
||||
assertEquals(offset, list.getOffset());
|
||||
assertEquals(100, list.getPageSize());
|
||||
assertEquals(900, list.getStart());
|
||||
assertEquals("855", list.get(0).get(ID_FIELD));
|
||||
assertEquals("954", list.get(99).get(ID_FIELD));
|
||||
} else {
|
||||
fail();
|
||||
}
|
||||
|
||||
if (rankFusionProcessor.search(query, new TestSearchRequestParams(1000, pageSize, offset),
|
||||
OptionalThing.empty()) instanceof QueryResponseList list) {
|
||||
assertEquals(pageSize, list.size());
|
||||
assertEquals(allRecordCount + offset, list.getAllRecordCount());
|
||||
assertEquals(11, list.getAllPageCount());
|
||||
assertEquals(1045, list.getCurrentEndRecordNumber());
|
||||
assertEquals(11, list.getCurrentPageNumber());
|
||||
assertEquals(1001, list.getCurrentStartRecordNumber());
|
||||
assertEquals(offset, list.getOffset());
|
||||
assertEquals(100, list.getPageSize());
|
||||
assertEquals(1000, list.getStart());
|
||||
assertEquals("955", list.get(0).get(ID_FIELD));
|
||||
assertEquals("999", list.get(44).get(ID_FIELD));
|
||||
} else {
|
||||
fail();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static class TestMainSearcher extends RankFusionSearcher {
|
||||
|
||||
private long allRecordCount;
|
||||
|
||||
TestMainSearcher(int allRecordCount) {
|
||||
this.allRecordCount = allRecordCount;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected SearchResult search(String query, SearchRequestParams params, OptionalThing<FessUserBean> userBean) {
|
||||
int start = params.getStartPosition();
|
||||
int size = params.getPageSize();
|
||||
SearchResultBuilder builder = SearchResult.create();
|
||||
for (int i = start; i < start + size; i++) {
|
||||
Map<String, Object> doc = new HashMap<>();
|
||||
doc.put(ID_FIELD, Integer.toString(i));
|
||||
doc.put("score", 1.0f / (i + 1));
|
||||
builder.addDocument(doc);
|
||||
}
|
||||
builder.allRecordCount(allRecordCount);
|
||||
if (allRecordCount < 10000) {
|
||||
builder.allRecordCountRelation(Relation.EQUAL_TO.toString());
|
||||
}
|
||||
return builder.build();
|
||||
}
|
||||
}
|
||||
|
||||
static class TestSubSearcher extends RankFusionSearcher {
|
||||
|
||||
private int mainSize;
|
||||
private int inSize;
|
||||
private int outSize;
|
||||
|
||||
TestSubSearcher(int mainSize, int inSize, int outSize) {
|
||||
this.mainSize = mainSize;
|
||||
this.inSize = inSize;
|
||||
this.outSize = outSize;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected SearchResult search(String query, SearchRequestParams params, OptionalThing<FessUserBean> userBean) {
|
||||
SearchResultBuilder builder = SearchResult.create();
|
||||
for (int i = 0; i < mainSize; i++) {
|
||||
Map<String, Object> doc = new HashMap<>();
|
||||
doc.put(ID_FIELD, Integer.toString(mainSize - i - 1));
|
||||
doc.put("score", 1.0f / (i + 2));
|
||||
builder.addDocument(doc);
|
||||
}
|
||||
for (int i = 100; i < inSize + 100; i++) {
|
||||
Map<String, Object> doc = new HashMap<>();
|
||||
doc.put(ID_FIELD, Integer.toString(i));
|
||||
doc.put("score", 1.0f / (mainSize + i + 2));
|
||||
builder.addDocument(doc);
|
||||
}
|
||||
for (int i = 200; i < outSize + 200; i++) {
|
||||
Map<String, Object> doc = new HashMap<>();
|
||||
doc.put(ID_FIELD, Integer.toString(i));
|
||||
doc.put("score", 1.0f / (mainSize + i + 3));
|
||||
builder.addDocument(doc);
|
||||
}
|
||||
builder.allRecordCount(mainSize + inSize + outSize);
|
||||
return builder.build();
|
||||
}
|
||||
}
|
||||
|
||||
static class TestSearchRequestParams extends SearchRequestParams {
|
||||
|
||||
private int startPosition;
|
||||
|
||||
private int pageSize;
|
||||
|
||||
private int offset;
|
||||
|
||||
TestSearchRequestParams(int startPosition, int pageSize, int offset) {
|
||||
this.startPosition = startPosition;
|
||||
this.pageSize = pageSize;
|
||||
this.offset = offset;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getQuery() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, String[]> getFields() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, String[]> getConditions() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] getLanguages() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public GeoInfo getGeoInfo() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FacetInfo getFacetInfo() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public HighlightInfo getHighlightInfo() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getSort() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getStartPosition() {
|
||||
return startPosition;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getOffset() {
|
||||
return offset;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getPageSize() {
|
||||
return pageSize;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] getExtraQueries() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getAttribute(String name) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Locale getLocale() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SearchRequestType getType() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getSimilarDocHash() {
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -15,9 +15,7 @@
|
|||
*/
|
||||
package org.codelibs.fess.util;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.codelibs.fess.unit.UnitFessTestCase;
|
||||
|
||||
|
@ -25,13 +23,13 @@ public class QueryResponseListTest extends UnitFessTestCase {
|
|||
public void test_calculatePageInfo_page0() {
|
||||
QueryResponseList qrList;
|
||||
|
||||
qrList = new QueryResponseList(new ArrayList<Map<String, Object>>()) {
|
||||
qrList = new QueryResponseList(null, 0, 20, 0) {
|
||||
@Override
|
||||
public int size() {
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
qrList.calculatePageInfo(0, 20);
|
||||
qrList.calculatePageInfo();
|
||||
assertEquals(20, qrList.getPageSize());
|
||||
assertEquals(1, qrList.getCurrentPageNumber());
|
||||
assertEquals(0, qrList.getAllRecordCount());
|
||||
|
@ -45,14 +43,14 @@ public class QueryResponseListTest extends UnitFessTestCase {
|
|||
public void test_calculatePageInfo_page1() {
|
||||
QueryResponseList qrList;
|
||||
|
||||
qrList = new QueryResponseList(new ArrayList<Map<String, Object>>()) {
|
||||
qrList = new QueryResponseList(null, 0, 20, 0) {
|
||||
@Override
|
||||
public int size() {
|
||||
return 10;
|
||||
}
|
||||
};
|
||||
qrList.allRecordCount = 10;
|
||||
qrList.calculatePageInfo(0, 20);
|
||||
qrList.calculatePageInfo();
|
||||
assertEquals(20, qrList.getPageSize());
|
||||
assertEquals(1, qrList.getCurrentPageNumber());
|
||||
assertEquals(10, qrList.getAllRecordCount());
|
||||
|
@ -62,14 +60,14 @@ public class QueryResponseListTest extends UnitFessTestCase {
|
|||
assertEquals(1, qrList.getCurrentStartRecordNumber());
|
||||
assertEquals(10, qrList.getCurrentEndRecordNumber());
|
||||
|
||||
qrList = new QueryResponseList(new ArrayList<Map<String, Object>>()) {
|
||||
qrList = new QueryResponseList(null, 0, 20, 0) {
|
||||
@Override
|
||||
public int size() {
|
||||
return 20;
|
||||
}
|
||||
};
|
||||
qrList.allRecordCount = 20;
|
||||
qrList.calculatePageInfo(0, 20);
|
||||
qrList.calculatePageInfo();
|
||||
assertEquals(20, qrList.getPageSize());
|
||||
assertEquals(1, qrList.getCurrentPageNumber());
|
||||
assertEquals(20, qrList.getAllRecordCount());
|
||||
|
@ -79,14 +77,14 @@ public class QueryResponseListTest extends UnitFessTestCase {
|
|||
assertEquals(1, qrList.getCurrentStartRecordNumber());
|
||||
assertEquals(20, qrList.getCurrentEndRecordNumber());
|
||||
|
||||
qrList = new QueryResponseList(new ArrayList<Map<String, Object>>()) {
|
||||
qrList = new QueryResponseList(null, 0, 20, 0) {
|
||||
@Override
|
||||
public int size() {
|
||||
return 20;
|
||||
}
|
||||
};
|
||||
qrList.allRecordCount = 21;
|
||||
qrList.calculatePageInfo(0, 20);
|
||||
qrList.calculatePageInfo();
|
||||
assertEquals(20, qrList.getPageSize());
|
||||
assertEquals(1, qrList.getCurrentPageNumber());
|
||||
assertEquals(21, qrList.getAllRecordCount());
|
||||
|
@ -96,14 +94,14 @@ public class QueryResponseListTest extends UnitFessTestCase {
|
|||
assertEquals(1, qrList.getCurrentStartRecordNumber());
|
||||
assertEquals(20, qrList.getCurrentEndRecordNumber());
|
||||
|
||||
qrList = new QueryResponseList(new ArrayList<Map<String, Object>>()) {
|
||||
qrList = new QueryResponseList(null, 0, 20, 0) {
|
||||
@Override
|
||||
public int size() {
|
||||
return 20;
|
||||
}
|
||||
};
|
||||
qrList.allRecordCount = 40;
|
||||
qrList.calculatePageInfo(0, 20);
|
||||
qrList.calculatePageInfo();
|
||||
assertEquals(20, qrList.getPageSize());
|
||||
assertEquals(1, qrList.getCurrentPageNumber());
|
||||
assertEquals(40, qrList.getAllRecordCount());
|
||||
|
@ -113,14 +111,14 @@ public class QueryResponseListTest extends UnitFessTestCase {
|
|||
assertEquals(1, qrList.getCurrentStartRecordNumber());
|
||||
assertEquals(20, qrList.getCurrentEndRecordNumber());
|
||||
|
||||
qrList = new QueryResponseList(new ArrayList<Map<String, Object>>()) {
|
||||
qrList = new QueryResponseList(null, 0, 20, 0) {
|
||||
@Override
|
||||
public int size() {
|
||||
return 20;
|
||||
}
|
||||
};
|
||||
qrList.allRecordCount = 41;
|
||||
qrList.calculatePageInfo(0, 20);
|
||||
qrList.calculatePageInfo();
|
||||
assertEquals(20, qrList.getPageSize());
|
||||
assertEquals(1, qrList.getCurrentPageNumber());
|
||||
assertEquals(41, qrList.getAllRecordCount());
|
||||
|
@ -134,14 +132,14 @@ public class QueryResponseListTest extends UnitFessTestCase {
|
|||
public void test_calculatePageInfo_page2() {
|
||||
QueryResponseList qrList;
|
||||
|
||||
qrList = new QueryResponseList(new ArrayList<Map<String, Object>>()) {
|
||||
qrList = new QueryResponseList(null, 20, 20, 0) {
|
||||
@Override
|
||||
public int size() {
|
||||
return 1;
|
||||
}
|
||||
};
|
||||
qrList.allRecordCount = 21;
|
||||
qrList.calculatePageInfo(20, 20);
|
||||
qrList.calculatePageInfo();
|
||||
assertEquals(20, qrList.getPageSize());
|
||||
assertEquals(2, qrList.getCurrentPageNumber());
|
||||
assertEquals(21, qrList.getAllRecordCount());
|
||||
|
@ -151,14 +149,14 @@ public class QueryResponseListTest extends UnitFessTestCase {
|
|||
assertEquals(21, qrList.getCurrentStartRecordNumber());
|
||||
assertEquals(21, qrList.getCurrentEndRecordNumber());
|
||||
|
||||
qrList = new QueryResponseList(new ArrayList<Map<String, Object>>()) {
|
||||
qrList = new QueryResponseList(null, 20, 20, 0) {
|
||||
@Override
|
||||
public int size() {
|
||||
return 20;
|
||||
}
|
||||
};
|
||||
qrList.allRecordCount = 40;
|
||||
qrList.calculatePageInfo(20, 20);
|
||||
qrList.calculatePageInfo();
|
||||
assertEquals(20, qrList.getPageSize());
|
||||
assertEquals(2, qrList.getCurrentPageNumber());
|
||||
assertEquals(40, qrList.getAllRecordCount());
|
||||
|
@ -168,14 +166,14 @@ public class QueryResponseListTest extends UnitFessTestCase {
|
|||
assertEquals(21, qrList.getCurrentStartRecordNumber());
|
||||
assertEquals(40, qrList.getCurrentEndRecordNumber());
|
||||
|
||||
qrList = new QueryResponseList(new ArrayList<Map<String, Object>>()) {
|
||||
qrList = new QueryResponseList(null, 20, 20, 0) {
|
||||
@Override
|
||||
public int size() {
|
||||
return 20;
|
||||
}
|
||||
};
|
||||
qrList.allRecordCount = 41;
|
||||
qrList.calculatePageInfo(20, 20);
|
||||
qrList.calculatePageInfo();
|
||||
assertEquals(20, qrList.getPageSize());
|
||||
assertEquals(2, qrList.getCurrentPageNumber());
|
||||
assertEquals(41, qrList.getAllRecordCount());
|
||||
|
@ -185,14 +183,14 @@ public class QueryResponseListTest extends UnitFessTestCase {
|
|||
assertEquals(21, qrList.getCurrentStartRecordNumber());
|
||||
assertEquals(40, qrList.getCurrentEndRecordNumber());
|
||||
|
||||
qrList = new QueryResponseList(new ArrayList<Map<String, Object>>()) {
|
||||
qrList = new QueryResponseList(null, 20, 20, 0) {
|
||||
@Override
|
||||
public int size() {
|
||||
return 20;
|
||||
}
|
||||
};
|
||||
qrList.allRecordCount = 61;
|
||||
qrList.calculatePageInfo(20, 20);
|
||||
qrList.calculatePageInfo();
|
||||
assertEquals(20, qrList.getPageSize());
|
||||
assertEquals(2, qrList.getCurrentPageNumber());
|
||||
assertEquals(61, qrList.getAllRecordCount());
|
||||
|
@ -207,26 +205,26 @@ public class QueryResponseListTest extends UnitFessTestCase {
|
|||
QueryResponseList qrList;
|
||||
List<String> pnList;
|
||||
|
||||
qrList = new QueryResponseList(new ArrayList<Map<String, Object>>()) {
|
||||
qrList = new QueryResponseList(null, 0, 20, 0) {
|
||||
@Override
|
||||
public int size() {
|
||||
return 20;
|
||||
}
|
||||
};
|
||||
qrList.allRecordCount = 20;
|
||||
qrList.calculatePageInfo(0, 20);
|
||||
qrList.calculatePageInfo();
|
||||
pnList = qrList.getPageNumberList();
|
||||
assertEquals(1, pnList.size());
|
||||
assertEquals("1", pnList.get(0));
|
||||
|
||||
qrList = new QueryResponseList(new ArrayList<Map<String, Object>>()) {
|
||||
qrList = new QueryResponseList(null, 0, 20, 0) {
|
||||
@Override
|
||||
public int size() {
|
||||
return 20;
|
||||
}
|
||||
};
|
||||
qrList.allRecordCount = 61;
|
||||
qrList.calculatePageInfo(0, 20);
|
||||
qrList.calculatePageInfo();
|
||||
pnList = qrList.getPageNumberList();
|
||||
assertEquals(4, pnList.size());
|
||||
assertEquals("1", pnList.get(0));
|
||||
|
@ -234,14 +232,14 @@ public class QueryResponseListTest extends UnitFessTestCase {
|
|||
assertEquals("3", pnList.get(2));
|
||||
assertEquals("4", pnList.get(3));
|
||||
|
||||
qrList = new QueryResponseList(new ArrayList<Map<String, Object>>()) {
|
||||
qrList = new QueryResponseList(null, 0, 20, 0) {
|
||||
@Override
|
||||
public int size() {
|
||||
return 20;
|
||||
}
|
||||
};
|
||||
qrList.allRecordCount = 200;
|
||||
qrList.calculatePageInfo(0, 20);
|
||||
qrList.calculatePageInfo();
|
||||
pnList = qrList.getPageNumberList();
|
||||
assertEquals(6, pnList.size());
|
||||
assertEquals("1", pnList.get(0));
|
||||
|
@ -251,27 +249,27 @@ public class QueryResponseListTest extends UnitFessTestCase {
|
|||
assertEquals("5", pnList.get(4));
|
||||
assertEquals("6", pnList.get(5));
|
||||
|
||||
qrList = new QueryResponseList(new ArrayList<Map<String, Object>>()) {
|
||||
qrList = new QueryResponseList(null, 20, 20, 0) {
|
||||
@Override
|
||||
public int size() {
|
||||
return 1;
|
||||
}
|
||||
};
|
||||
qrList.allRecordCount = 21;
|
||||
qrList.calculatePageInfo(20, 20);
|
||||
qrList.calculatePageInfo();
|
||||
pnList = qrList.getPageNumberList();
|
||||
assertEquals(2, pnList.size());
|
||||
assertEquals("1", pnList.get(0));
|
||||
assertEquals("2", pnList.get(1));
|
||||
|
||||
qrList = new QueryResponseList(new ArrayList<Map<String, Object>>()) {
|
||||
qrList = new QueryResponseList(null, 20, 20, 0) {
|
||||
@Override
|
||||
public int size() {
|
||||
return 20;
|
||||
}
|
||||
};
|
||||
qrList.allRecordCount = 61;
|
||||
qrList.calculatePageInfo(20, 20);
|
||||
qrList.calculatePageInfo();
|
||||
pnList = qrList.getPageNumberList();
|
||||
assertEquals(4, pnList.size());
|
||||
assertEquals("1", pnList.get(0));
|
||||
|
@ -279,14 +277,14 @@ public class QueryResponseListTest extends UnitFessTestCase {
|
|||
assertEquals("3", pnList.get(2));
|
||||
assertEquals("4", pnList.get(3));
|
||||
|
||||
qrList = new QueryResponseList(new ArrayList<Map<String, Object>>()) {
|
||||
qrList = new QueryResponseList(null, 20, 20, 0) {
|
||||
@Override
|
||||
public int size() {
|
||||
return 20;
|
||||
}
|
||||
};
|
||||
qrList.allRecordCount = 200;
|
||||
qrList.calculatePageInfo(20, 20);
|
||||
qrList.calculatePageInfo();
|
||||
pnList = qrList.getPageNumberList();
|
||||
assertEquals(7, pnList.size());
|
||||
assertEquals("1", pnList.get(0));
|
||||
|
@ -302,13 +300,13 @@ public class QueryResponseListTest extends UnitFessTestCase {
|
|||
public void test_calculatePageInfo_collapse() {
|
||||
QueryResponseList qrList;
|
||||
|
||||
qrList = new QueryResponseList(new ArrayList<Map<String, Object>>()) {
|
||||
qrList = new QueryResponseList(null, 0, 20, 0) {
|
||||
@Override
|
||||
public int size() {
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
qrList.calculatePageInfo(0, 20);
|
||||
qrList.calculatePageInfo();
|
||||
assertEquals(20, qrList.getPageSize());
|
||||
assertEquals(1, qrList.getCurrentPageNumber());
|
||||
assertEquals(0, qrList.getAllRecordCount());
|
||||
|
@ -318,14 +316,14 @@ public class QueryResponseListTest extends UnitFessTestCase {
|
|||
assertEquals(0, qrList.getCurrentStartRecordNumber());
|
||||
assertEquals(0, qrList.getCurrentEndRecordNumber());
|
||||
|
||||
qrList = new QueryResponseList(new ArrayList<Map<String, Object>>()) {
|
||||
qrList = new QueryResponseList(null, 0, 20, 0) {
|
||||
@Override
|
||||
public int size() {
|
||||
return 10;
|
||||
}
|
||||
};
|
||||
qrList.allRecordCount = 20;
|
||||
qrList.calculatePageInfo(0, 20);
|
||||
qrList.calculatePageInfo();
|
||||
assertEquals(20, qrList.getPageSize());
|
||||
assertEquals(1, qrList.getCurrentPageNumber());
|
||||
assertEquals(20, qrList.getAllRecordCount());
|
||||
|
@ -335,14 +333,14 @@ public class QueryResponseListTest extends UnitFessTestCase {
|
|||
assertEquals(1, qrList.getCurrentStartRecordNumber());
|
||||
assertEquals(20, qrList.getCurrentEndRecordNumber());
|
||||
|
||||
qrList = new QueryResponseList(new ArrayList<Map<String, Object>>()) {
|
||||
qrList = new QueryResponseList(null, 0, 20, 0) {
|
||||
@Override
|
||||
public int size() {
|
||||
return 10;
|
||||
}
|
||||
};
|
||||
qrList.allRecordCount = 21;
|
||||
qrList.calculatePageInfo(0, 20);
|
||||
qrList.calculatePageInfo();
|
||||
assertEquals(20, qrList.getPageSize());
|
||||
assertEquals(1, qrList.getCurrentPageNumber());
|
||||
assertEquals(21, qrList.getAllRecordCount());
|
||||
|
@ -352,14 +350,14 @@ public class QueryResponseListTest extends UnitFessTestCase {
|
|||
assertEquals(1, qrList.getCurrentStartRecordNumber());
|
||||
assertEquals(20, qrList.getCurrentEndRecordNumber());
|
||||
|
||||
qrList = new QueryResponseList(new ArrayList<Map<String, Object>>()) {
|
||||
qrList = new QueryResponseList(null, 0, 20, 0) {
|
||||
@Override
|
||||
public int size() {
|
||||
return 21;
|
||||
}
|
||||
};
|
||||
qrList.allRecordCount = 41;
|
||||
qrList.calculatePageInfo(0, 20);
|
||||
qrList.calculatePageInfo();
|
||||
assertEquals(20, qrList.getPageSize());
|
||||
assertEquals(1, qrList.getCurrentPageNumber());
|
||||
assertEquals(41, qrList.getAllRecordCount());
|
||||
|
@ -369,14 +367,14 @@ public class QueryResponseListTest extends UnitFessTestCase {
|
|||
assertEquals(1, qrList.getCurrentStartRecordNumber());
|
||||
assertEquals(20, qrList.getCurrentEndRecordNumber());
|
||||
|
||||
qrList = new QueryResponseList(new ArrayList<Map<String, Object>>()) {
|
||||
qrList = new QueryResponseList(null, 20, 20, 0) {
|
||||
@Override
|
||||
public int size() {
|
||||
return 1;
|
||||
}
|
||||
};
|
||||
qrList.allRecordCount = 41;
|
||||
qrList.calculatePageInfo(20, 20);
|
||||
qrList.calculatePageInfo();
|
||||
assertEquals(20, qrList.getPageSize());
|
||||
assertEquals(2, qrList.getCurrentPageNumber());
|
||||
assertEquals(41, qrList.getAllRecordCount());
|
||||
|
|
|
@ -131,6 +131,11 @@ public class QueryStringBuilderTest extends UnitFessTestCase {
|
|||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getOffset() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getPageSize() {
|
||||
return 0;
|
||||
|
|
Loading…
Add table
Reference in a new issue