fix #2828 Add configurable DisMax query support for default field searches

This commit is contained in:
Shinsuke Sugaya 2024-07-11 17:26:36 +09:00
parent a21cea6ffe
commit 7ee6b77a60
8 changed files with 425 additions and 17 deletions

View file

@ -1110,6 +1110,15 @@ public interface FessConfig extends FessEnv, org.codelibs.fess.mylasta.direction
/** The key of the configuration. e.g. true */
String QUERY_BOOST_FUZZY_CONTENT_TRANSPOSITIONS = "query.boost.fuzzy.content.transpositions";
/** The key of the configuration. e.g. bool */
String QUERY_DEFAULT_query_type = "query.default.query_type";
/** The key of the configuration. e.g. 0.1 */
String QUERY_DISMAX_tie_breaker = "query.dismax.tie_breaker";
/** The key of the configuration. e.g. */
String QUERY_BOOL_minimum_should_match = "query.bool.minimum_should_match";
/** The key of the configuration. e.g. 50 */
String QUERY_PREFIX_EXPANSIONS = "query.prefix.expansions";
@ -5244,6 +5253,43 @@ public interface FessConfig extends FessEnv, org.codelibs.fess.mylasta.direction
*/
boolean isQueryBoostFuzzyContentTranspositions();
/**
* Get the value for the key 'query.default.query_type'. <br>
* The value is, e.g. bool <br>
* @return The value of found property. (NotNull: if not found, exception but basically no way)
*/
String getQueryDefaultQueryType();
/**
* Get the value for the key 'query.dismax.tie_breaker'. <br>
* The value is, e.g. 0.1 <br>
* @return The value of found property. (NotNull: if not found, exception but basically no way)
*/
String getQueryDismaxTieBreaker();
/**
* Get the value for the key 'query.dismax.tie_breaker' as {@link java.math.BigDecimal}. <br>
* The value is, e.g. 0.1 <br>
* @return The value of found property. (NotNull: if not found, exception but basically no way)
* @throws NumberFormatException When the property is not decimal.
*/
java.math.BigDecimal getQueryDismaxTieBreakerAsDecimal();
/**
* Get the value for the key 'query.bool.minimum_should_match'. <br>
* The value is, e.g. <br>
* @return The value of found property. (NotNull: if not found, exception but basically no way)
*/
String getQueryBoolMinimumShouldMatch();
/**
* Get the value for the key 'query.bool.minimum_should_match' as {@link Integer}. <br>
* The value is, e.g. <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 getQueryBoolMinimumShouldMatchAsInteger();
/**
* Get the value for the key 'query.prefix.expansions'. <br>
* The value is, e.g. 50 <br>
@ -9489,6 +9535,26 @@ public interface FessConfig extends FessEnv, org.codelibs.fess.mylasta.direction
return is(FessConfig.QUERY_BOOST_FUZZY_CONTENT_TRANSPOSITIONS);
}
public String getQueryDefaultQueryType() {
return get(FessConfig.QUERY_DEFAULT_query_type);
}
public String getQueryDismaxTieBreaker() {
return get(FessConfig.QUERY_DISMAX_tie_breaker);
}
public java.math.BigDecimal getQueryDismaxTieBreakerAsDecimal() {
return getAsDecimal(FessConfig.QUERY_DISMAX_tie_breaker);
}
public String getQueryBoolMinimumShouldMatch() {
return get(FessConfig.QUERY_BOOL_minimum_should_match);
}
public Integer getQueryBoolMinimumShouldMatchAsInteger() {
return getAsInteger(FessConfig.QUERY_BOOL_minimum_should_match);
}
public String getQueryPrefixExpansions() {
return get(FessConfig.QUERY_PREFIX_EXPANSIONS);
}
@ -11158,6 +11224,9 @@ public interface FessConfig extends FessEnv, org.codelibs.fess.mylasta.direction
defaultMap.put(FessConfig.QUERY_BOOST_FUZZY_CONTENT_EXPANSIONS, "10");
defaultMap.put(FessConfig.QUERY_BOOST_FUZZY_CONTENT_prefix_length, "0");
defaultMap.put(FessConfig.QUERY_BOOST_FUZZY_CONTENT_TRANSPOSITIONS, "true");
defaultMap.put(FessConfig.QUERY_DEFAULT_query_type, "bool");
defaultMap.put(FessConfig.QUERY_DISMAX_tie_breaker, "0.1");
defaultMap.put(FessConfig.QUERY_BOOL_minimum_should_match, "");
defaultMap.put(FessConfig.QUERY_PREFIX_EXPANSIONS, "50");
defaultMap.put(FessConfig.QUERY_PREFIX_SLOP, "0");
defaultMap.put(FessConfig.QUERY_FUZZY_prefix_length, "0");

View file

@ -0,0 +1,148 @@
/*
* Copyright 2012-2024 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.io.IOException;
import java.util.Objects;
import org.apache.lucene.search.Query;
import org.opensearch.core.common.io.stream.StreamOutput;
import org.opensearch.core.xcontent.XContentBuilder;
import org.opensearch.index.query.BoolQueryBuilder;
import org.opensearch.index.query.DisMaxQueryBuilder;
import org.opensearch.index.query.QueryBuilder;
import org.opensearch.index.query.QueryBuilderVisitor;
import org.opensearch.index.query.QueryRewriteContext;
import org.opensearch.index.query.QueryShardContext;
public class DefaultQueryBuilder implements QueryBuilder {
private final QueryBuilder queryBuilder;
private final QueryType queryType;
public DefaultQueryBuilder(final QueryBuilder queryBuilder) {
this.queryBuilder = queryBuilder;
if (queryBuilder instanceof BoolQueryBuilder) {
queryType = QueryType.BOOL;
} else if (queryBuilder instanceof DisMaxQueryBuilder) {
queryType = QueryType.DISMAX;
} else {
throw new IllegalArgumentException("Unknown query builder: " + queryBuilder);
}
}
public DefaultQueryBuilder add(final QueryBuilder innerQueryBuilder) {
switch (queryType) {
case BOOL:
((BoolQueryBuilder) queryBuilder).should(innerQueryBuilder);
break;
case DISMAX:
((DisMaxQueryBuilder) queryBuilder).add(innerQueryBuilder);
break;
default:
break;
}
return this;
}
enum QueryType {
BOOL, DISMAX;
}
@Override
public String getWriteableName() {
return queryBuilder.getWriteableName();
}
@Override
public Query toQuery(final QueryShardContext context) throws IOException {
return queryBuilder.toQuery(context);
}
@Override
public boolean isFragment() {
return queryBuilder.isFragment();
}
@Override
public QueryBuilder queryName(final String queryName) {
return queryBuilder.queryName(queryName);
}
@Override
public String queryName() {
return queryBuilder.queryName();
}
@Override
public float boost() {
return queryBuilder.boost();
}
@Override
public QueryBuilder boost(final float boost) {
return queryBuilder.boost(boost);
}
@Override
public String getName() {
return queryBuilder.getName();
}
@Override
public QueryBuilder rewrite(final QueryRewriteContext queryShardContext) throws IOException {
return queryBuilder.rewrite(queryShardContext);
}
@Override
public void visit(final QueryBuilderVisitor visitor) {
queryBuilder.visit(visitor);
}
@Override
public XContentBuilder toXContent(final XContentBuilder builder, final Params params) throws IOException {
return queryBuilder.toXContent(builder, params);
}
@Override
public void writeTo(final StreamOutput out) throws IOException {
queryBuilder.writeTo(out);
}
@Override
public int hashCode() {
return queryBuilder.hashCode();
}
@Override
public boolean equals(final Object obj) {
if (this == obj) {
return true;
}
if ((obj == null) || (getClass() != obj.getClass())) {
return false;
}
final DefaultQueryBuilder other = (DefaultQueryBuilder) obj;
return Objects.equals(queryBuilder, other.queryBuilder);
}
@Override
public String toString() {
return queryBuilder.toString();
}
}

View file

@ -20,6 +20,7 @@ import static org.codelibs.core.stream.StreamUtil.stream;
import java.lang.Character.UnicodeBlock;
import org.apache.lucene.search.Query;
import org.codelibs.core.lang.StringUtil;
import org.codelibs.fess.Constants;
import org.codelibs.fess.entity.QueryContext;
import org.codelibs.fess.mylasta.direction.FessConfig;
@ -27,6 +28,7 @@ import org.codelibs.fess.util.ComponentUtil;
import org.dbflute.optional.OptionalThing;
import org.lastaflute.web.util.LaRequestUtil;
import org.opensearch.index.query.BoolQueryBuilder;
import org.opensearch.index.query.DisMaxQueryBuilder;
import org.opensearch.index.query.QueryBuilder;
import org.opensearch.index.query.QueryBuilders;
import org.opensearch.search.sort.SortBuilder;
@ -72,30 +74,47 @@ public abstract class QueryCommand {
(String[]) request.getAttribute(Constants.REQUEST_LANGUAGES)));
}
protected BoolQueryBuilder buildDefaultQueryBuilder(final FessConfig fessConfig, final QueryContext context,
protected DefaultQueryBuilder buildDefaultQueryBuilder(final FessConfig fessConfig, final QueryContext context,
final DefaultQueryBuilderFunction builder) {
final BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();
boolQuery.should(builder.apply(fessConfig.getIndexFieldTitle(), fessConfig.getQueryBoostTitleAsDecimal().floatValue()));
boolQuery.should(builder.apply(fessConfig.getIndexFieldContent(), fessConfig.getQueryBoostContentAsDecimal().floatValue()));
final DefaultQueryBuilder defaultQuery = createDefaultQueryBuilder();
defaultQuery.add(builder.apply(fessConfig.getIndexFieldTitle(), fessConfig.getQueryBoostTitleAsDecimal().floatValue()));
defaultQuery.add(builder.apply(fessConfig.getIndexFieldContent(), fessConfig.getQueryBoostContentAsDecimal().floatValue()));
final float importantContentBoost = fessConfig.getQueryBoostImportantContentAsDecimal().floatValue();
if (importantContentBoost >= 0.0f) {
boolQuery.should(builder.apply(fessConfig.getIndexFieldImportantContent(), importantContentBoost));
defaultQuery.add(builder.apply(fessConfig.getIndexFieldImportantContent(), importantContentBoost));
}
final float importantContantLangBoost = fessConfig.getQueryBoostImportantContentLangAsDecimal().floatValue();
getQueryLanguages().ifPresent(langs -> stream(langs).of(stream -> stream.forEach(lang -> {
boolQuery.should(
defaultQuery.add(
builder.apply(fessConfig.getIndexFieldTitle() + "_" + lang, fessConfig.getQueryBoostTitleLangAsDecimal().floatValue()));
boolQuery.should(builder.apply(fessConfig.getIndexFieldContent() + "_" + lang,
defaultQuery.add(builder.apply(fessConfig.getIndexFieldContent() + "_" + lang,
fessConfig.getQueryBoostContentLangAsDecimal().floatValue()));
if (importantContantLangBoost >= 0.0f) {
boolQuery.should(builder.apply(fessConfig.getIndexFieldImportantContent() + "_" + lang, importantContantLangBoost));
defaultQuery.add(builder.apply(fessConfig.getIndexFieldImportantContent() + "_" + lang, importantContantLangBoost));
}
})));
getQueryFieldConfig().additionalDefaultList.stream().forEach(f -> {
final QueryBuilder query = builder.apply(f.getFirst(), f.getSecond());
boolQuery.should(query);
defaultQuery.add(query);
});
return boolQuery;
return defaultQuery;
}
protected DefaultQueryBuilder createDefaultQueryBuilder() {
final FessConfig fessConfig = ComponentUtil.getFessConfig();
if ("dismax".equals(fessConfig.getQueryDefaultQueryType())) {
final DisMaxQueryBuilder disMaxQuery = QueryBuilders.disMaxQuery();
disMaxQuery.tieBreaker(fessConfig.getQueryDismaxTieBreakerAsDecimal().floatValue());
return new DefaultQueryBuilder(disMaxQuery);
}
final BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();
final String minimumShouldMatch = fessConfig.getQueryBoolMinimumShouldMatch();
if (StringUtil.isNotBlank(minimumShouldMatch)) {
boolQuery.minimumShouldMatch(minimumShouldMatch);
}
return new DefaultQueryBuilder(boolQuery);
}
protected QueryBuilder buildMatchPhraseQuery(final String f, final String text) {

View file

@ -34,7 +34,6 @@ import org.codelibs.fess.mylasta.direction.FessConfig;
import org.codelibs.fess.util.ComponentUtil;
import org.lastaflute.core.message.UserMessages;
import org.opensearch.common.unit.Fuzziness;
import org.opensearch.index.query.BoolQueryBuilder;
import org.opensearch.index.query.QueryBuilder;
import org.opensearch.index.query.QueryBuilders;
import org.opensearch.search.sort.SortOrder;
@ -161,24 +160,24 @@ public class TermQueryCommand extends QueryCommand {
final float boost, final String field, final String text) {
context.addFieldLog(field, text);
context.addHighlightedQuery(text);
final BoolQueryBuilder boolQuery =
final DefaultQueryBuilder defaultQuery =
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)
defaultQuery.add(QueryBuilders.fuzzyQuery(fessConfig.getIndexFieldTitle(), text)
.boost(fessConfig.getQueryBoostFuzzyTitleAsDecimal().floatValue())
.prefixLength(fessConfig.getQueryBoostFuzzyTitlePrefixLengthAsInteger())
.transpositions(Constants.TRUE.equalsIgnoreCase(fessConfig.getQueryBoostFuzzyTitleTranspositions()))
.fuzziness(Fuzziness.build(fessConfig.getQueryBoostFuzzyTitleFuzziness()))
.maxExpansions(fessConfig.getQueryBoostFuzzyTitleExpansionsAsInteger()));
boolQuery.should(QueryBuilders.fuzzyQuery(fessConfig.getIndexFieldContent(), text)
defaultQuery.add(QueryBuilders.fuzzyQuery(fessConfig.getIndexFieldContent(), text)
.prefixLength(fessConfig.getQueryBoostFuzzyContentPrefixLengthAsInteger())
.transpositions(Constants.TRUE.equalsIgnoreCase(fessConfig.getQueryBoostFuzzyContentTranspositions()))
.boost(fessConfig.getQueryBoostFuzzyContentAsDecimal().floatValue())
.fuzziness(Fuzziness.build(fessConfig.getQueryBoostFuzzyContentFuzziness()))
.maxExpansions(fessConfig.getQueryBoostFuzzyContentExpansionsAsInteger()));
}
return boolQuery;
return defaultQuery;
}
protected QueryBuilder convertSiteQuery(final FessConfig fessConfig, final QueryContext context, final TermQuery termQuery,

View file

@ -570,6 +570,10 @@ query.boost.fuzzy.content.expansions=10
query.boost.fuzzy.content.prefix_length=0
query.boost.fuzzy.content.transpositions=true
query.default.query_type=bool
query.dismax.tie_breaker=0.1
query.bool.minimum_should_match=
query.prefix.expansions=50
query.prefix.slop=0
query.fuzzy.prefix_length=0

View file

@ -16,6 +16,7 @@
package org.codelibs.fess.helper;
import java.io.File;
import java.math.BigDecimal;
import java.util.List;
import java.util.Map;
import java.util.Set;
@ -27,6 +28,7 @@ import org.codelibs.fess.Constants;
import org.codelibs.fess.entity.QueryContext;
import org.codelibs.fess.entity.SearchRequestParams.SearchRequestType;
import org.codelibs.fess.exception.InvalidQueryException;
import org.codelibs.fess.mylasta.direction.FessConfig;
import org.codelibs.fess.query.BooleanQueryCommand;
import org.codelibs.fess.query.BoostQueryCommand;
import org.codelibs.fess.query.FuzzyQueryCommand;
@ -41,6 +43,7 @@ import org.codelibs.fess.query.WildcardQueryCommand;
import org.codelibs.fess.query.parser.QueryParser;
import org.codelibs.fess.unit.UnitFessTestCase;
import org.codelibs.fess.util.ComponentUtil;
import org.dbflute.util.DfTypeUtil;
import org.opensearch.index.query.BoolQueryBuilder;
import org.opensearch.index.query.QueryBuilder;
import org.opensearch.index.query.QueryBuilders;
@ -88,7 +91,54 @@ public class QueryHelperTest extends UnitFessTestCase {
new WildcardQueryCommand().register();
}
private void setQueryType(final String queryType) {
final FessConfig fessConfig = ComponentUtil.getFessConfig();
ComponentUtil.setFessConfig(new FessConfig.SimpleImpl() {
@Override
public String get(String propertyKey) {
return fessConfig.get(propertyKey);
}
@Override
public BigDecimal getAsDecimal(String propertyKey) {
return DfTypeUtil.toBigDecimal(get(propertyKey));
}
@Override
public Integer getAsInteger(String propertyKey) {
return DfTypeUtil.toInteger(get(propertyKey));
}
@Override
public String getQueryDefaultQueryType() {
return queryType;
}
});
}
public void test_build_simple() {
setQueryType("bool");
float titleBoost = 0.5f;
float contentBoost = 0.05f;
assertQuery(functionScoreQuery(simpleQuery("QUERY", titleBoost, contentBoost)), //
Map.of("_default", List.of("QUERY")), //
Set.of("QUERY"), //
buildQuery("QUERY"));
assertQuery(functionScoreQuery(simpleQuery("QUERY", titleBoost, contentBoost)), //
Map.of("_default", List.of("QUERY")), //
Set.of("QUERY"), //
buildQuery(" QUERY"));
assertQuery(functionScoreQuery(simpleQuery("QUERY", titleBoost, contentBoost)), //
Map.of("_default", List.of("QUERY")), //
Set.of("QUERY"), //
buildQuery("QUERY "));
}
public void test_build_simple_dismax() {
setQueryType("dismax");
float titleBoost = 0.5f;
float contentBoost = 0.05f;
@ -133,6 +183,7 @@ public class QueryHelperTest extends UnitFessTestCase {
}
public void test_build_boost() {
setQueryType("bool");
assertQueryContext(
"{\"function_score\":{\"query\":{\"bool\":{\"should\":[{\"match_phrase\":{\"title\":{\"query\":\"QUERY1\",\"slop\":0,\"zero_terms_query\":\"NONE\",\"boost\":5.0}}},{\"match_phrase\":{\"content\":{\"query\":\"QUERY1\",\"slop\":0,\"zero_terms_query\":\"NONE\",\"boost\":0.5}}},{\"fuzzy\":{\"title\":{\"value\":\"QUERY1\",\"fuzziness\":\"AUTO\",\"prefix_length\":0,\"max_expansions\":10,\"transpositions\":true,\"boost\":0.01}}},{\"fuzzy\":{\"content\":{\"value\":\"QUERY1\",\"fuzziness\":\"AUTO\",\"prefix_length\":0,\"max_expansions\":10,\"transpositions\":true,\"boost\":0.005}}}],\"adjust_pure_negative\":true,\"boost\":1.0}},\"functions\":[{\"filter\":{\"match_all\":{\"boost\":1.0}},\"field_value_factor\":{\"field\":\"boost\",\"factor\":1.0,\"modifier\":\"none\"}}],\"score_mode\":\"multiply\",\"max_boost\":3.4028235E38,\"boost\":1.0}}",
Map.of("_default", List.of("QUERY1")), //
@ -153,6 +204,7 @@ public class QueryHelperTest extends UnitFessTestCase {
}
public void test_build_wildcard() {
setQueryType("bool");
assertQueryContext(
"{\"function_score\":{\"query\":{\"bool\":{\"should\":[{\"wildcard\":{\"title\":{\"wildcard\":\"*\",\"boost\":0.5}}},{\"wildcard\":{\"content\":{\"wildcard\":\"*\",\"boost\":0.05}}}],\"adjust_pure_negative\":true,\"boost\":1.0}},\"functions\":[{\"filter\":{\"match_all\":{\"boost\":1.0}},\"field_value_factor\":{\"field\":\"boost\",\"factor\":1.0,\"modifier\":\"none\"}}],\"score_mode\":\"multiply\",\"max_boost\":3.4028235E38,\"boost\":1.0}}",
Map.of("_default", List.of("*")), //
@ -191,6 +243,7 @@ public class QueryHelperTest extends UnitFessTestCase {
}
public void test_build_phrase() {
setQueryType("bool");
assertQueryContext(
"{\"function_score\":{\"query\":{\"bool\":{\"should\":[{\"match_phrase\":{\"title\":{\"query\":\"QUERY1QUERY2\",\"slop\":0,\"zero_terms_query\":\"NONE\",\"boost\":0.5}}},{\"match_phrase\":{\"content\":{\"query\":\"QUERY1QUERY2\",\"slop\":0,\"zero_terms_query\":\"NONE\",\"boost\":0.05}}}],\"adjust_pure_negative\":true,\"boost\":1.0}},\"functions\":[{\"filter\":{\"match_all\":{\"boost\":1.0}},\"field_value_factor\":{\"field\":\"boost\",\"factor\":1.0,\"modifier\":\"none\"}}],\"score_mode\":\"multiply\",\"max_boost\":3.4028235E38,\"boost\":1.0}}",
Map.of("_default", List.of("QUERY1 QUERY2")), //
@ -207,6 +260,7 @@ public class QueryHelperTest extends UnitFessTestCase {
}
public void test_build_prefix() {
setQueryType("bool");
assertQueryContext(
"{\"function_score\":{\"query\":{\"bool\":{\"should\":[{\"match_phrase_prefix\":{\"title\":{\"query\":\"query\",\"slop\":0,\"max_expansions\":50,\"zero_terms_query\":\"NONE\",\"boost\":0.5}}},{\"match_phrase_prefix\":{\"content\":{\"query\":\"query\",\"slop\":0,\"max_expansions\":50,\"zero_terms_query\":\"NONE\",\"boost\":0.05}}}],\"adjust_pure_negative\":true,\"boost\":1.0}},\"functions\":[{\"filter\":{\"match_all\":{\"boost\":1.0}},\"field_value_factor\":{\"field\":\"boost\",\"factor\":1.0,\"modifier\":\"none\"}}],\"score_mode\":\"multiply\",\"max_boost\":3.4028235E38,\"boost\":1.0}}",
Map.of("_default", List.of("QUERY*")), //
@ -229,6 +283,7 @@ public class QueryHelperTest extends UnitFessTestCase {
}
public void test_build_escape() {
setQueryType("bool");
assertQueryContext(
"{\"function_score\":{\"query\":{\"bool\":{\"should\":[{\"match_phrase\":{\"title\":{\"query\":\"aaa:bbb\",\"slop\":0,\"zero_terms_query\":\"NONE\",\"boost\":0.5}}},{\"match_phrase\":{\"content\":{\"query\":\"aaa:bbb\",\"slop\":0,\"zero_terms_query\":\"NONE\",\"boost\":0.05}}},{\"fuzzy\":{\"title\":{\"value\":\"aaa:bbb\",\"fuzziness\":\"AUTO\",\"prefix_length\":0,\"max_expansions\":10,\"transpositions\":true,\"boost\":0.01}}},{\"fuzzy\":{\"content\":{\"value\":\"aaa:bbb\",\"fuzziness\":\"AUTO\",\"prefix_length\":0,\"max_expansions\":10,\"transpositions\":true,\"boost\":0.005}}}],\"adjust_pure_negative\":true,\"boost\":1.0}},\"functions\":[{\"filter\":{\"match_all\":{\"boost\":1.0}},\"field_value_factor\":{\"field\":\"boost\",\"factor\":1.0,\"modifier\":\"none\"}}],\"score_mode\":\"multiply\",\"max_boost\":3.4028235E38,\"boost\":1.0}}",
Map.of("_default", List.of("aaa:bbb")), //
@ -335,6 +390,7 @@ public class QueryHelperTest extends UnitFessTestCase {
}
public void test_build_fuzzy() {
setQueryType("bool");
assertQueryContext(
"{\"function_score\":{\"query\":{\"bool\":{\"should\":[{\"fuzzy\":{\"title\":{\"value\":\"QUERY1\",\"fuzziness\":\"2\",\"prefix_length\":0,\"max_expansions\":50,\"transpositions\":true,\"boost\":0.5}}},{\"fuzzy\":{\"content\":{\"value\":\"QUERY1\",\"fuzziness\":\"2\",\"prefix_length\":0,\"max_expansions\":50,\"transpositions\":true,\"boost\":0.05}}}],\"adjust_pure_negative\":true,\"boost\":1.0}},\"functions\":[{\"filter\":{\"match_all\":{\"boost\":1.0}},\"field_value_factor\":{\"field\":\"boost\",\"factor\":1.0,\"modifier\":\"none\"}}],\"score_mode\":\"multiply\",\"max_boost\":3.4028235E38,\"boost\":1.0}}",
Map.of("_default", List.of("QUERY1")), //
@ -357,6 +413,7 @@ public class QueryHelperTest extends UnitFessTestCase {
}
public void test_build_range() {
setQueryType("bool");
assertQueryContext(
"{\"function_score\":{\"query\":{\"bool\":{\"should\":[{\"match_phrase\":{\"title\":{\"query\":\"[aaaTObbb]\",\"slop\":0,\"zero_terms_query\":\"NONE\",\"boost\":0.5}}},{\"match_phrase\":{\"content\":{\"query\":\"[aaaTObbb]\",\"slop\":0,\"zero_terms_query\":\"NONE\",\"boost\":0.05}}}],\"adjust_pure_negative\":true,\"boost\":1.0}},\"functions\":[{\"filter\":{\"match_all\":{\"boost\":1.0}},\"field_value_factor\":{\"field\":\"boost\",\"factor\":1.0,\"modifier\":\"none\"}}],\"score_mode\":\"multiply\",\"max_boost\":3.4028235E38,\"boost\":1.0}}",
Map.of("_default", List.of("[aaa TO bbb]")), //
@ -439,6 +496,14 @@ public class QueryHelperTest extends UnitFessTestCase {
}
private QueryBuilder simpleQuery(String query, float titleBoost, float contentBoost) {
if ("dismax".equals(ComponentUtil.getFessConfig().getQueryDefaultQueryType())) {
return QueryBuilders.disMaxQuery().tieBreaker(0.1f)//
.add(QueryBuilders.matchPhraseQuery("title", query).boost(titleBoost))//
.add(QueryBuilders.matchPhraseQuery("content", query).boost(contentBoost))//
.add(QueryBuilders.fuzzyQuery("title", query).boost(0.01f).maxExpansions(10))//
.add(QueryBuilders.fuzzyQuery("content", query).boost(0.005f).maxExpansions(10))//
;
}
return QueryBuilders.boolQuery()//
.should(QueryBuilders.matchPhraseQuery("title", query).boost(titleBoost))//
.should(QueryBuilders.matchPhraseQuery("content", query).boost(contentBoost))//

View file

@ -0,0 +1,39 @@
/*
* Copyright 2012-2024 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 org.codelibs.fess.unit.UnitFessTestCase;
import org.opensearch.index.query.QueryBuilders;
public class DefaultQueryBuilderTest extends UnitFessTestCase {
public void test_invalid_null() {
try {
new DefaultQueryBuilder(null);
fail();
} catch (IllegalArgumentException e) {
// nothing
}
}
public void test_invalid_query() {
try {
new DefaultQueryBuilder(QueryBuilders.matchAllQuery());
fail();
} catch (IllegalArgumentException e) {
// nothing
}
}
}

View file

@ -15,6 +15,7 @@
*/
package org.codelibs.fess.query;
import java.math.BigDecimal;
import java.util.List;
import org.apache.logging.log4j.LogManager;
@ -23,9 +24,11 @@ import org.apache.lucene.search.Query;
import org.apache.lucene.search.TermQuery;
import org.codelibs.fess.entity.QueryContext;
import org.codelibs.fess.exception.InvalidQueryException;
import org.codelibs.fess.mylasta.direction.FessConfig;
import org.codelibs.fess.query.parser.QueryParser;
import org.codelibs.fess.unit.UnitFessTestCase;
import org.codelibs.fess.util.ComponentUtil;
import org.dbflute.util.DfTypeUtil;
import org.opensearch.index.query.BoolQueryBuilder;
import org.opensearch.index.query.MatchPhraseQueryBuilder;
import org.opensearch.index.query.PrefixQueryBuilder;
@ -60,8 +63,34 @@ public class TermQueryCommandTest extends UnitFessTestCase {
super.tearDown();
}
private void setQueryType(final String queryType) {
final FessConfig fessConfig = ComponentUtil.getFessConfig();
ComponentUtil.setFessConfig(new FessConfig.SimpleImpl() {
@Override
public String get(String propertyKey) {
return fessConfig.get(propertyKey);
}
@Override
public BigDecimal getAsDecimal(String propertyKey) {
return DfTypeUtil.toBigDecimal(get(propertyKey));
}
@Override
public Integer getAsInteger(String propertyKey) {
return DfTypeUtil.toInteger(get(propertyKey));
}
@Override
public String getQueryDefaultQueryType() {
return queryType;
}
});
}
public void test_convertTermQuery() throws Exception {
assertQueryBuilder(BoolQueryBuilder.class,
setQueryType("bool");
assertQueryBuilder(DefaultQueryBuilder.class,
"{\"bool\":{\"should\":[{\"match_phrase\":{\"title\":{\"query\":\"aaa\",\"slop\":0,\"zero_terms_query\":\"NONE\",\"boost\":0.5}}},{\"match_phrase\":{\"content\":{\"query\":\"aaa\",\"slop\":0,\"zero_terms_query\":\"NONE\",\"boost\":0.05}}}],\"adjust_pure_negative\":true,\"boost\":1.0}}",
"aaa");
assertQueryBuilder(MatchPhraseQueryBuilder.class,
@ -70,7 +99,7 @@ public class TermQueryCommandTest extends UnitFessTestCase {
assertQueryBuilder(MatchPhraseQueryBuilder.class,
"{\"match_phrase\":{\"content\":{\"query\":\"aaa\",\"slop\":0,\"zero_terms_query\":\"NONE\",\"boost\":1.0}}}", //
"content:aaa");
assertQueryBuilder(BoolQueryBuilder.class,
assertQueryBuilder(DefaultQueryBuilder.class,
"{\"bool\":{\"should\":[{\"match_phrase\":{\"title\":{\"query\":\"xxx:aaa\",\"slop\":0,\"zero_terms_query\":\"NONE\",\"boost\":0.5}}},{\"match_phrase\":{\"content\":{\"query\":\"xxx:aaa\",\"slop\":0,\"zero_terms_query\":\"NONE\",\"boost\":0.05}}},{\"fuzzy\":{\"title\":{\"value\":\"xxx:aaa\",\"fuzziness\":\"AUTO\",\"prefix_length\":0,\"max_expansions\":10,\"transpositions\":true,\"boost\":0.01}}},{\"fuzzy\":{\"content\":{\"value\":\"xxx:aaa\",\"fuzziness\":\"AUTO\",\"prefix_length\":0,\"max_expansions\":10,\"transpositions\":true,\"boost\":0.005}}}],\"adjust_pure_negative\":true,\"boost\":1.0}}",
"xxx:aaa");
assertQueryBuilder(WildcardQueryBuilder.class, //
@ -95,6 +124,42 @@ public class TermQueryCommandTest extends UnitFessTestCase {
}
}
public void test_convertTermQuery_dismax() throws Exception {
setQueryType("dismax");
assertQueryBuilder(DefaultQueryBuilder.class,
"{\"dis_max\":{\"tie_breaker\":0.1,\"queries\":[{\"match_phrase\":{\"title\":{\"query\":\"aaa\",\"slop\":0,\"zero_terms_query\":\"NONE\",\"boost\":0.5}}},{\"match_phrase\":{\"content\":{\"query\":\"aaa\",\"slop\":0,\"zero_terms_query\":\"NONE\",\"boost\":0.05}}}],\"boost\":1.0}}",
"aaa");
assertQueryBuilder(MatchPhraseQueryBuilder.class,
"{\"match_phrase\":{\"title\":{\"query\":\"aaa\",\"slop\":0,\"zero_terms_query\":\"NONE\",\"boost\":1.0}}}", //
"title:aaa");
assertQueryBuilder(MatchPhraseQueryBuilder.class,
"{\"match_phrase\":{\"content\":{\"query\":\"aaa\",\"slop\":0,\"zero_terms_query\":\"NONE\",\"boost\":1.0}}}", //
"content:aaa");
assertQueryBuilder(DefaultQueryBuilder.class,
"{\"dis_max\":{\"tie_breaker\":0.1,\"queries\":[{\"match_phrase\":{\"title\":{\"query\":\"xxx:aaa\",\"slop\":0,\"zero_terms_query\":\"NONE\",\"boost\":0.5}}},{\"match_phrase\":{\"content\":{\"query\":\"xxx:aaa\",\"slop\":0,\"zero_terms_query\":\"NONE\",\"boost\":0.05}}},{\"fuzzy\":{\"title\":{\"value\":\"xxx:aaa\",\"fuzziness\":\"AUTO\",\"prefix_length\":0,\"max_expansions\":10,\"transpositions\":true,\"boost\":0.01}}},{\"fuzzy\":{\"content\":{\"value\":\"xxx:aaa\",\"fuzziness\":\"AUTO\",\"prefix_length\":0,\"max_expansions\":10,\"transpositions\":true,\"boost\":0.005}}}],\"boost\":1.0}}",
"xxx:aaa");
assertQueryBuilder(WildcardQueryBuilder.class, //
"{\"wildcard\":{\"url\":{\"wildcard\":\"*aaa*\",\"boost\":1.0}}}", //
"inurl:aaa");
assertQueryBuilder(TermQueryBuilder.class, //
"{\"term\":{\"url\":{\"value\":\"aaa\",\"boost\":1.0}}}", //
"url:aaa");
assertQueryBuilder(PrefixQueryBuilder.class, //
"{\"prefix\":{\"site\":{\"value\":\"aaa\",\"boost\":1.0}}}", //
"site:aaa");
assertQueryBuilder("{\"timestamp\":{\"order\":\"asc\"}}", "sort:timestamp");
assertQueryBuilder("{\"timestamp\":{\"order\":\"asc\"}}", "sort:timestamp.asc");
assertQueryBuilder("{\"timestamp\":{\"order\":\"desc\"}}", "sort:timestamp.desc");
try {
assertQueryBuilder("", "sort:xxx");
fail();
} catch (InvalidQueryException e) {
// nothing
}
}
private void assertQueryBuilder(final String expect, final String text) throws Exception {
QueryContext queryContext = assertQueryBuilder(null, null, text);
List<SortBuilder<?>> sortBuilders = queryContext.sortBuilders();