fix #2638 add query filter

This commit is contained in:
Shinsuke Sugaya 2022-03-27 16:12:55 +09:00
parent bf6751dc7c
commit f92fd3fa9d
10 changed files with 170 additions and 22 deletions

View file

@ -57,7 +57,7 @@ public class FuzzyQueryCommand extends QueryCommand {
// TODO fuzzy value
if (Constants.DEFAULT_FIELD.equals(field)) {
context.addFieldLog(field, term.text());
return buildDefaultQueryBuilder(
return buildDefaultQueryBuilder(fessConfig, context,
(f, b) -> QueryBuilders.fuzzyQuery(f, term.text()).fuzziness(Fuzziness.fromEdits(fuzzyQuery.getMaxEdits()))
.boost(b * boost).maxExpansions(fessConfig.getQueryFuzzyExpansionsAsInteger())
.prefixLength(fessConfig.getQueryFuzzyPrefixLengthAsInteger())
@ -73,9 +73,10 @@ public class FuzzyQueryCommand extends QueryCommand {
final String origQuery = fuzzyQuery.toString();
context.addFieldLog(Constants.DEFAULT_FIELD, origQuery);
context.addHighlightedQuery(origQuery);
return buildDefaultQueryBuilder((f, b) -> QueryBuilders.fuzzyQuery(f, origQuery)
.fuzziness(Fuzziness.fromEdits(fuzzyQuery.getMaxEdits())).boost(b * boost)
.maxExpansions(fessConfig.getQueryFuzzyExpansionsAsInteger()).prefixLength(fessConfig.getQueryFuzzyPrefixLengthAsInteger())
.transpositions(Constants.TRUE.equalsIgnoreCase(fessConfig.getQueryFuzzyTranspositions())));
return buildDefaultQueryBuilder(fessConfig, context,
(f, b) -> QueryBuilders.fuzzyQuery(f, origQuery).fuzziness(Fuzziness.fromEdits(fuzzyQuery.getMaxEdits())).boost(b * boost)
.maxExpansions(fessConfig.getQueryFuzzyExpansionsAsInteger())
.prefixLength(fessConfig.getQueryFuzzyPrefixLengthAsInteger())
.transpositions(Constants.TRUE.equalsIgnoreCase(fessConfig.getQueryFuzzyTranspositions())));
}
}

View file

@ -24,6 +24,8 @@ import org.apache.lucene.search.PhraseQuery;
import org.apache.lucene.search.Query;
import org.codelibs.fess.entity.QueryContext;
import org.codelibs.fess.exception.InvalidQueryException;
import org.codelibs.fess.mylasta.direction.FessConfig;
import org.codelibs.fess.util.ComponentUtil;
import org.lastaflute.core.message.UserMessages;
import org.opensearch.index.query.QueryBuilder;
@ -53,12 +55,13 @@ public class PhraseQueryCommand extends QueryCommand {
throw new InvalidQueryException(messages -> messages.addErrorsInvalidQueryUnknown(UserMessages.GLOBAL_PROPERTY_KEY),
"Unknown phrase query: " + phraseQuery);
}
final FessConfig fessConfig = ComponentUtil.getFessConfig();
final String field = terms[0].field();
final String[] texts = stream(terms).get(stream -> stream.map(Term::text).toArray(n -> new String[n]));
final String text = String.join(" ", texts);
context.addFieldLog(field, text);
stream(texts).of(stream -> stream.forEach(t -> context.addHighlightedQuery(t)));
return buildDefaultQueryBuilder((f, b) -> buildMatchPhraseQuery(f, text).boost(b * boost));
return buildDefaultQueryBuilder(fessConfig, context, (f, b) -> buildMatchPhraseQuery(f, text).boost(b * boost));
}
}

View file

@ -57,7 +57,7 @@ public class PrefixQueryCommand extends QueryCommand {
final String field = getSearchField(context.getDefaultField(), prefixQuery.getField());
if (Constants.DEFAULT_FIELD.equals(field)) {
context.addFieldLog(field, prefixQuery.getPrefix().text());
return buildDefaultQueryBuilder(
return buildDefaultQueryBuilder(fessConfig, context,
(f, b) -> QueryBuilders.matchPhrasePrefixQuery(f, toLowercaseWildcard(prefixQuery.getPrefix().text())).boost(b * boost)
.maxExpansions(fessConfig.getQueryPrefixExpansionsAsInteger()).slop(fessConfig.getQueryPrefixSlopAsInteger()));
}
@ -66,8 +66,9 @@ public class PrefixQueryCommand extends QueryCommand {
final String origQuery = toLowercaseWildcard(query);
context.addFieldLog(Constants.DEFAULT_FIELD, query + "*");
context.addHighlightedQuery(origQuery);
return buildDefaultQueryBuilder((f, b) -> QueryBuilders.matchPhrasePrefixQuery(f, origQuery).boost(b * boost)
.maxExpansions(fessConfig.getQueryPrefixExpansionsAsInteger()).slop(fessConfig.getQueryPrefixSlopAsInteger()));
return buildDefaultQueryBuilder(fessConfig, context,
(f, b) -> QueryBuilders.matchPhrasePrefixQuery(f, origQuery).boost(b * boost)
.maxExpansions(fessConfig.getQueryPrefixExpansionsAsInteger()).slop(fessConfig.getQueryPrefixSlopAsInteger()));
}
context.addFieldLog(field, prefixQuery.getPrefix().text() + "*");
if (getQueryFieldConfig().notAnalyzedFieldSet.contains(field)) {

View file

@ -72,9 +72,9 @@ public abstract class QueryCommand {
(String[]) request.getAttribute(Constants.REQUEST_LANGUAGES)));
}
protected BoolQueryBuilder buildDefaultQueryBuilder(final DefaultQueryBuilderFunction builder) {
protected BoolQueryBuilder buildDefaultQueryBuilder(final FessConfig fessConfig, final QueryContext context,
final DefaultQueryBuilderFunction builder) {
final BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();
final FessConfig fessConfig = ComponentUtil.getFessConfig();
boolQuery.should(builder.apply(fessConfig.getIndexFieldTitle(), fessConfig.getQueryBoostTitleAsDecimal().floatValue()));
boolQuery.should(builder.apply(fessConfig.getIndexFieldContent(), fessConfig.getQueryBoostContentAsDecimal().floatValue()));
final float importantContentBoost = fessConfig.getQueryBoostImportantContentAsDecimal().floatValue();

View file

@ -15,9 +15,13 @@
*/
package org.codelibs.fess.query;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.annotation.PostConstruct;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.apache.lucene.search.Query;
@ -31,13 +35,17 @@ public class QueryProcessor {
protected Map<String, QueryCommand> queryCommandMap = new HashMap<>();
protected List<Filter> filterList = new ArrayList<>();
protected FilterChain filterChain;
@PostConstruct
public void init() {
createFilterChain();
}
public QueryBuilder execute(final QueryContext context, final Query query, final float boost) {
final QueryCommand queryCommand = queryCommandMap.get(query.getClass().getSimpleName());
if (queryCommand != null) {
return queryCommand.execute(context, query, boost);
}
throw new InvalidQueryException(messages -> messages.addErrorsInvalidQueryUnknown(UserMessages.GLOBAL_PROPERTY_KEY),
"Unknown q: " + query.getClass() + " => " + query);
return filterChain.execute(context, query, boost);
}
public void add(final String name, final QueryCommand queryCommand) {
@ -49,4 +57,41 @@ public class QueryProcessor {
}
queryCommandMap.put(name, queryCommand);
}
protected void addFilter(final Filter filter) {
filterList.add(filter);
createFilterChain();
}
protected void createFilterChain() {
FilterChain chain = createDefaultFilterChain();
for (final Filter element : filterList) {
chain = appendFilterChain(element, chain);
}
filterChain = chain;
}
protected FilterChain appendFilterChain(final Filter filter, final FilterChain chain) {
return (context, query, boost) -> filter.execute(context, query, boost, chain);
}
protected FilterChain createDefaultFilterChain() {
return (context, query, boost) -> {
final QueryCommand queryCommand = queryCommandMap.get(query.getClass().getSimpleName());
if (queryCommand != null) {
return queryCommand.execute(context, query, boost);
}
throw new InvalidQueryException(messages -> messages.addErrorsInvalidQueryUnknown(UserMessages.GLOBAL_PROPERTY_KEY),
"Unknown q: " + query.getClass() + " => " + query);
};
}
protected interface Filter {
QueryBuilder execute(final QueryContext context, final Query query, final float boost, final FilterChain chain);
}
protected interface FilterChain {
QueryBuilder execute(final QueryContext context, final Query query, final float boost);
}
}

View file

@ -158,7 +158,8 @@ public class TermQueryCommand extends QueryCommand {
final float boost, final String field, final String text) {
context.addFieldLog(field, text);
context.addHighlightedQuery(text);
final BoolQueryBuilder boolQuery = buildDefaultQueryBuilder((f, b) -> buildMatchPhraseQuery(f, text).boost(b * boost));
final BoolQueryBuilder boolQuery =
buildDefaultQueryBuilder(fessConfig, context, (f, b) -> buildMatchPhraseQuery(f, text).boost(b * boost));
final Integer fuzzyMinLength = fessConfig.getQueryBoostFuzzyMinLengthAsInteger();
if (fuzzyMinLength >= 0 && text.length() >= fuzzyMinLength) {
boolQuery.should(QueryBuilders.fuzzyQuery(fessConfig.getIndexFieldTitle(), text)

View file

@ -23,6 +23,8 @@ import org.apache.lucene.util.BytesRef;
import org.codelibs.fess.Constants;
import org.codelibs.fess.entity.QueryContext;
import org.codelibs.fess.exception.InvalidQueryException;
import org.codelibs.fess.mylasta.direction.FessConfig;
import org.codelibs.fess.util.ComponentUtil;
import org.lastaflute.core.message.UserMessages;
import org.opensearch.index.query.QueryBuilder;
import org.opensearch.index.query.QueryBuilders;
@ -49,12 +51,13 @@ public class TermRangeQueryCommand extends QueryCommand {
}
protected QueryBuilder convertTermRangeQuery(final QueryContext context, final TermRangeQuery termRangeQuery, final float boost) {
final FessConfig fessConfig = ComponentUtil.getFessConfig();
final String field = getSearchField(context.getDefaultField(), termRangeQuery.getField());
if (!isSearchField(field)) {
final String origQuery = termRangeQuery.toString();
context.addFieldLog(Constants.DEFAULT_FIELD, origQuery);
context.addHighlightedQuery(origQuery);
return buildDefaultQueryBuilder((f, b) -> QueryBuilders.matchPhraseQuery(f, origQuery).boost(b));
return buildDefaultQueryBuilder(fessConfig, context, (f, b) -> QueryBuilders.matchPhraseQuery(f, origQuery).boost(b));
}
context.addFieldLog(field, termRangeQuery.toString(field));
final RangeQueryBuilder rangeQuery = QueryBuilders.rangeQuery(field);

View file

@ -24,6 +24,8 @@ import org.apache.lucene.search.WildcardQuery;
import org.codelibs.fess.Constants;
import org.codelibs.fess.entity.QueryContext;
import org.codelibs.fess.exception.InvalidQueryException;
import org.codelibs.fess.mylasta.direction.FessConfig;
import org.codelibs.fess.util.ComponentUtil;
import org.lastaflute.core.message.UserMessages;
import org.opensearch.index.query.QueryBuilder;
import org.opensearch.index.query.QueryBuilders;
@ -51,10 +53,11 @@ public class WildcardQueryCommand extends QueryCommand {
}
protected QueryBuilder convertWildcardQuery(final QueryContext context, final WildcardQuery wildcardQuery, final float boost) {
final FessConfig fessConfig = ComponentUtil.getFessConfig();
final String field = getSearchField(context.getDefaultField(), wildcardQuery.getField());
if (Constants.DEFAULT_FIELD.equals(field)) {
context.addFieldLog(field, wildcardQuery.getTerm().text());
return buildDefaultQueryBuilder(
return buildDefaultQueryBuilder(fessConfig, context,
(f, b) -> QueryBuilders.wildcardQuery(f, toLowercaseWildcard(wildcardQuery.getTerm().text())).boost(b * boost));
}
if (isSearchField(field)) {
@ -65,7 +68,7 @@ public class WildcardQueryCommand extends QueryCommand {
final String origQuery = "*" + toLowercaseWildcard(query) + "*";
context.addFieldLog(Constants.DEFAULT_FIELD, origQuery);
context.addHighlightedQuery(query);
return buildDefaultQueryBuilder((f, b) -> QueryBuilders.wildcardQuery(f, origQuery).boost(b * boost));
return buildDefaultQueryBuilder(fessConfig, context, (f, b) -> QueryBuilders.wildcardQuery(f, origQuery).boost(b * boost));
}
protected String toLowercaseWildcard(final String value) {

View file

@ -71,7 +71,9 @@ public class QueryHelperTest extends UnitFessTestCase {
queryFieldConfig = new QueryFieldConfig();
ComponentUtil.register(queryFieldConfig, "queryFieldConfig");
queryFieldConfig.init();
ComponentUtil.register(new QueryProcessor(), "queryProcessor");
QueryProcessor queryProcessor = new QueryProcessor();
ComponentUtil.register(queryProcessor, "queryProcessor");
queryProcessor.init();
new BooleanQueryCommand().register();
new BoostQueryCommand().register();
new FuzzyQueryCommand().register();

View file

@ -0,0 +1,89 @@
/*
* Copyright 2012-2022 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.query;
import java.util.concurrent.atomic.AtomicBoolean;
import org.apache.lucene.search.MatchAllDocsQuery;
import org.apache.lucene.search.Query;
import org.codelibs.fess.entity.QueryContext;
import org.codelibs.fess.query.QueryProcessor.FilterChain;
import org.codelibs.fess.unit.UnitFessTestCase;
import org.opensearch.index.query.BoolQueryBuilder;
import org.opensearch.index.query.QueryBuilder;
import org.opensearch.index.query.QueryBuilders;
public class QueryProcessorTest extends UnitFessTestCase {
public void test_executeWithFilter() {
final AtomicBoolean called = new AtomicBoolean(false);
QueryProcessor queryProcessor = new QueryProcessor() {
protected FilterChain createDefaultFilterChain() {
return (context, query, boost) -> {
called.set(true);
return QueryBuilders.boolQuery();
};
}
};
queryProcessor.init();
QueryContext context = new QueryContext(null, false);
MatchAllDocsQuery query = new MatchAllDocsQuery();
QueryBuilder queryBuilder = queryProcessor.execute(context, query, 1.0f);
assertTrue(called.get());
assertEquals(BoolQueryBuilder.class, queryBuilder.getClass());
called.set(false);
final AtomicBoolean calledFirst = new AtomicBoolean(false);
queryProcessor.addFilter(new QueryProcessor.Filter() {
@Override
public QueryBuilder execute(QueryContext context, Query query, float boost, FilterChain chain) {
calledFirst.set(true);
assertFalse(called.get());
QueryBuilder builder = chain.execute(context, query, boost);
assertTrue(called.get());
return builder;
}
});
queryBuilder = queryProcessor.execute(context, query, 1.0f);
assertTrue(called.get());
assertTrue(calledFirst.get());
assertEquals(BoolQueryBuilder.class, queryBuilder.getClass());
called.set(false);
calledFirst.set(false);
final AtomicBoolean calledSecond = new AtomicBoolean(false);
queryProcessor.addFilter(new QueryProcessor.Filter() {
@Override
public QueryBuilder execute(QueryContext context, Query query, float boost, FilterChain chain) {
calledSecond.set(true);
assertFalse(called.get());
assertFalse(calledFirst.get());
QueryBuilder builder = chain.execute(context, query, boost);
assertTrue(called.get());
assertTrue(calledFirst.get());
return builder;
}
});
queryBuilder = queryProcessor.execute(context, query, 1.0f);
assertTrue(called.get());
assertTrue(calledFirst.get());
assertTrue(calledSecond.get());
assertEquals(BoolQueryBuilder.class, queryBuilder.getClass());
}
}