diff --git a/src/main/java/org/codelibs/fess/mylasta/direction/FessConfig.java b/src/main/java/org/codelibs/fess/mylasta/direction/FessConfig.java
index 28c44a185..92b6452fd 100644
--- a/src/main/java/org/codelibs/fess/mylasta/direction/FessConfig.java
+++ b/src/main/java/org/codelibs/fess/mylasta/direction/FessConfig.java
@@ -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'.
+ * The value is, e.g. bool
+ * @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'.
+ * The value is, e.g. 0.1
+ * @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}.
+ * The value is, e.g. 0.1
+ * @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'.
+ * The value is, e.g.
+ * @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}.
+ * The value is, e.g.
+ * @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'.
* The value is, e.g. 50
@@ -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");
diff --git a/src/main/java/org/codelibs/fess/query/DefaultQueryBuilder.java b/src/main/java/org/codelibs/fess/query/DefaultQueryBuilder.java
new file mode 100644
index 000000000..b0682e2ef
--- /dev/null
+++ b/src/main/java/org/codelibs/fess/query/DefaultQueryBuilder.java
@@ -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();
+ }
+
+}
diff --git a/src/main/java/org/codelibs/fess/query/QueryCommand.java b/src/main/java/org/codelibs/fess/query/QueryCommand.java
index f7ff145a5..6b70450c8 100644
--- a/src/main/java/org/codelibs/fess/query/QueryCommand.java
+++ b/src/main/java/org/codelibs/fess/query/QueryCommand.java
@@ -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) {
diff --git a/src/main/java/org/codelibs/fess/query/TermQueryCommand.java b/src/main/java/org/codelibs/fess/query/TermQueryCommand.java
index 53641c461..6eb86f157 100644
--- a/src/main/java/org/codelibs/fess/query/TermQueryCommand.java
+++ b/src/main/java/org/codelibs/fess/query/TermQueryCommand.java
@@ -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,
diff --git a/src/main/resources/fess_config.properties b/src/main/resources/fess_config.properties
index 2060037d6..6afdce293 100644
--- a/src/main/resources/fess_config.properties
+++ b/src/main/resources/fess_config.properties
@@ -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
diff --git a/src/test/java/org/codelibs/fess/helper/QueryHelperTest.java b/src/test/java/org/codelibs/fess/helper/QueryHelperTest.java
index 2e586d6a0..0e1689e7f 100644
--- a/src/test/java/org/codelibs/fess/helper/QueryHelperTest.java
+++ b/src/test/java/org/codelibs/fess/helper/QueryHelperTest.java
@@ -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))//
diff --git a/src/test/java/org/codelibs/fess/query/DefaultQueryBuilderTest.java b/src/test/java/org/codelibs/fess/query/DefaultQueryBuilderTest.java
new file mode 100644
index 000000000..5662b16b8
--- /dev/null
+++ b/src/test/java/org/codelibs/fess/query/DefaultQueryBuilderTest.java
@@ -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
+ }
+ }
+}
diff --git a/src/test/java/org/codelibs/fess/query/TermQueryCommandTest.java b/src/test/java/org/codelibs/fess/query/TermQueryCommandTest.java
index a99cfc9c3..11d50be65 100644
--- a/src/test/java/org/codelibs/fess/query/TermQueryCommandTest.java
+++ b/src/test/java/org/codelibs/fess/query/TermQueryCommandTest.java
@@ -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> sortBuilders = queryContext.sortBuilders();