Move optimization out of BoolQueryBuilder into tests

This commit is contained in:
Simon Willnauer 2016-02-12 15:14:28 +01:00
parent 387a55d5f9
commit 8d568ce3e1
8 changed files with 78 additions and 26 deletions

View File

@ -263,13 +263,6 @@ public class BoolQueryBuilder extends AbstractQueryBuilder<BoolQueryBuilder> {
@Override
protected Query doToQuery(QueryShardContext context) throws IOException {
final int numClauses = mustNotClauses.size() + filterClauses.size() + shouldClauses.size() + mustClauses.size();
if (numClauses == 1 && must().size() == 1 && boost() == DEFAULT_BOOST) {
// we really only optimize this for testing since we use this to rewrite
// named queries TemplateQueryBuilder and WrapperQueryBuilder.
// it's equivalent but will anyways happen later down the road in the BQ#rewrite method
return mustClauses.get(0).toQuery(context);
}
BooleanQuery.Builder booleanQueryBuilder = new BooleanQuery.Builder();
booleanQueryBuilder.setDisableCoord(disableCoord);
addBooleanClauses(context, booleanQueryBuilder, mustClauses, BooleanClause.Occur.MUST);

View File

@ -19,6 +19,7 @@
package org.elasticsearch.index.query;
import org.apache.lucene.search.MatchAllDocsQuery;
import org.apache.lucene.search.Query;
import org.elasticsearch.common.io.stream.StreamInput;
import org.elasticsearch.common.io.stream.StreamOutput;

View File

@ -143,4 +143,6 @@ public class WrapperQueryBuilder extends AbstractQueryBuilder<WrapperQueryBuilde
return queryBuilder;
}
}
}

View File

@ -23,6 +23,7 @@ import com.carrotsearch.randomizedtesting.generators.CodepointSetGenerator;
import com.fasterxml.jackson.core.JsonParseException;
import com.fasterxml.jackson.core.io.JsonStringEncoder;
import org.apache.lucene.index.memory.MemoryIndex;
import org.apache.lucene.search.BoostQuery;
import org.apache.lucene.search.Query;
import org.apache.lucene.search.TermQuery;
@ -538,7 +539,7 @@ public abstract class AbstractQueryTestCase<QB extends AbstractQueryBuilder<QB>>
assertLuceneQuery(secondQuery, secondLuceneQuery, context);
SearchContext.removeCurrent();
assertThat("two equivalent query builders lead to different lucene queries", secondLuceneQuery, equalTo(firstLuceneQuery));
assertEquals("two equivalent query builders lead to different lucene queries", rewrite(secondLuceneQuery), rewrite(firstLuceneQuery));
// if the initial lucene query is null, changing its boost won't have any effect, we shouldn't test that
if (firstLuceneQuery != null && supportsBoostAndQueryName()) {
@ -546,8 +547,8 @@ public abstract class AbstractQueryTestCase<QB extends AbstractQueryBuilder<QB>>
setSearchContext(randomTypes);
Query thirdLuceneQuery = rewriteQuery(secondQuery, context).toQuery(context);
SearchContext.removeCurrent();
assertThat("modifying the boost doesn't affect the corresponding lucene query", firstLuceneQuery,
not(equalTo(thirdLuceneQuery)));
assertNotEquals("modifying the boost doesn't affect the corresponding lucene query", rewrite(firstLuceneQuery),
rewrite(thirdLuceneQuery));
}
}
}
@ -988,4 +989,9 @@ public abstract class AbstractQueryTestCase<QB extends AbstractQueryBuilder<QB>>
setSearchContext(randomTypes); // only set search context for toQuery to be more realistic
queryBuilder.toQuery(context);
}
protected Query rewrite(Query query) throws IOException {
return query;
}
}

View File

@ -195,14 +195,14 @@ public class BoolQueryBuilderTests extends AbstractQueryTestCase<BoolQueryBuilde
public void testDefaultMinShouldMatch() throws Exception {
// Queries have a minShouldMatch of 0
BooleanQuery bq = (BooleanQuery) parseQuery(boolQuery().filter(termQuery("foo", "bar")).buildAsBytes()).toQuery(createShardContext());
BooleanQuery bq = (BooleanQuery) parseQuery(boolQuery().must(termQuery("foo", "bar")).buildAsBytes()).toQuery(createShardContext());
assertEquals(0, bq.getMinimumNumberShouldMatch());
bq = (BooleanQuery) parseQuery(boolQuery().should(termQuery("foo", "bar")).buildAsBytes()).toQuery(createShardContext());
assertEquals(0, bq.getMinimumNumberShouldMatch());
// Filters have a minShouldMatch of 0/1
ConstantScoreQuery csq = (ConstantScoreQuery) parseQuery(constantScoreQuery(boolQuery().filter(termQuery("foo", "bar"))).buildAsBytes()).toQuery(createShardContext());
ConstantScoreQuery csq = (ConstantScoreQuery) parseQuery(constantScoreQuery(boolQuery().must(termQuery("foo", "bar"))).buildAsBytes()).toQuery(createShardContext());
bq = (BooleanQuery) csq.getQuery();
assertEquals(0, bq.getMinimumNumberShouldMatch());
@ -213,7 +213,7 @@ public class BoolQueryBuilderTests extends AbstractQueryTestCase<BoolQueryBuilde
public void testMinShouldMatchFilterWithoutShouldClauses() throws Exception {
BoolQueryBuilder boolQueryBuilder = new BoolQueryBuilder();
boolQueryBuilder.filter(new BoolQueryBuilder().filter(new MatchAllQueryBuilder()));
boolQueryBuilder.filter(new BoolQueryBuilder().must(new MatchAllQueryBuilder()));
Query query = boolQueryBuilder.toQuery(createShardContext());
assertThat(query, instanceOf(BooleanQuery.class));
BooleanQuery booleanQuery = (BooleanQuery) query;
@ -227,7 +227,7 @@ public class BoolQueryBuilderTests extends AbstractQueryTestCase<BoolQueryBuilde
assertThat(innerBooleanQuery.getMinimumNumberShouldMatch(), equalTo(0));
assertThat(innerBooleanQuery.clauses().size(), equalTo(1));
BooleanClause innerBooleanClause = innerBooleanQuery.clauses().get(0);
assertThat(innerBooleanClause.getOccur(), equalTo(BooleanClause.Occur.FILTER));
assertThat(innerBooleanClause.getOccur(), equalTo(BooleanClause.Occur.MUST));
assertThat(innerBooleanClause.getQuery(), instanceOf(MatchAllDocsQuery.class));
}

View File

@ -19,6 +19,8 @@
package org.elasticsearch.index.query;
import org.apache.lucene.index.memory.MemoryIndex;
import org.apache.lucene.search.MatchAllDocsQuery;
import org.apache.lucene.search.Query;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.common.xcontent.XContentFactory;
@ -58,7 +60,7 @@ public class TemplateQueryBuilderTests extends AbstractQueryTestCase<TemplateQue
@Override
protected void doAssertLuceneQuery(TemplateQueryBuilder queryBuilder, Query query, QueryShardContext context) throws IOException {
assertEquals(QueryBuilder.rewriteQuery(templateBase, context).toQuery(context), query);
assertEquals(rewrite(QueryBuilder.rewriteQuery(templateBase, context).toQuery(context)), rewrite(query));
}
public void testIllegalArgument() {
@ -157,4 +159,16 @@ public class TemplateQueryBuilderTests extends AbstractQueryTestCase<TemplateQue
XContentType.JSON, Collections.emptyMap())).boost(3);
assertEquals(new BoolQueryBuilder().must(query).boost(3), builder.rewrite(queryShardContext()));
}
@Override
protected Query rewrite(Query query) throws IOException {
// TemplateQueryBuilder adds some optimization if the template and query builder have boosts / query names that wraps
// the actual QueryBuilder that comes from the template into a BooleanQueryBuilder to give it an outer boost / name
// this causes some queries to be not exactly equal but equivalent such that we need to rewrite them before comparing.
if (query != null) {
MemoryIndex idx = new MemoryIndex();
return idx.createSearcher().rewrite(query);
}
return new MatchAllDocsQuery(); // null == *:*
}
}

View File

@ -19,19 +19,17 @@
package org.elasticsearch.index.query;
import org.apache.lucene.index.memory.MemoryIndex;
import org.apache.lucene.search.MatchAllDocsQuery;
import org.apache.lucene.search.Query;
import org.elasticsearch.action.support.ToXContentToBytes;
import org.elasticsearch.common.ParsingException;
import org.elasticsearch.common.bytes.BytesArray;
import org.elasticsearch.common.bytes.BytesReference;
import org.elasticsearch.common.xcontent.XContentFactory;
import org.elasticsearch.common.xcontent.XContentParser;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import static org.hamcrest.Matchers.equalTo;
public class WrapperQueryBuilderTests extends AbstractQueryTestCase<WrapperQueryBuilder> {
@Override
@ -56,13 +54,9 @@ public class WrapperQueryBuilderTests extends AbstractQueryTestCase<WrapperQuery
@Override
protected void doAssertLuceneQuery(WrapperQueryBuilder queryBuilder, Query query, QueryShardContext context) throws IOException {
try (XContentParser qSourceParser = XContentFactory.xContent(queryBuilder.source()).createParser(queryBuilder.source())) {
final QueryShardContext contextCopy = new QueryShardContext(context);
contextCopy.reset(qSourceParser);
QueryBuilder<?> innerQuery = contextCopy.parseContext().parseInnerQueryBuilder();
Query expected = innerQuery.toQuery(context);
assertThat(query, equalTo(expected));
}
QueryBuilder<?> innerQuery = queryBuilder.rewrite(queryShardContext());
Query expected = rewrite(innerQuery.toQuery(context));
assertEquals(rewrite(query), expected);
}
public void testIllegalArgument() {
@ -164,4 +158,16 @@ public class WrapperQueryBuilderTests extends AbstractQueryTestCase<WrapperQuery
assertEquals(new BoolQueryBuilder().must(query).boost(3), builder.rewrite(queryShardContext()));
}
@Override
protected Query rewrite(Query query) throws IOException {
// WrapperQueryBuilder adds some optimization if the wrapper and query builder have boosts / query names that wraps
// the actual QueryBuilder that comes from the binary blob into a BooleanQueryBuilder to give it an outer boost / name
// this causes some queries to be not exactly equal but equivalent such that we need to rewrite them before comparing.
if (query != null) {
MemoryIndex idx = new MemoryIndex();
return idx.createSearcher().rewrite(query);
}
return new MatchAllDocsQuery(); // null == *:*
}
}

View File

@ -40,6 +40,7 @@ import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.index.query.QueryShardContext;
import org.elasticsearch.index.query.RandomQueryBuilder;
import org.elasticsearch.index.query.TermQueryBuilder;
import org.elasticsearch.index.query.WrapperQueryBuilder;
import org.elasticsearch.index.query.functionscore.exp.ExponentialDecayFunctionBuilder;
import org.elasticsearch.index.query.functionscore.fieldvaluefactor.FieldValueFactorFunctionBuilder;
import org.elasticsearch.index.query.functionscore.gauss.GaussDecayFunctionBuilder;
@ -630,4 +631,33 @@ public class FunctionScoreQueryBuilderTests extends AbstractQueryTestCase<Functi
assertEquals(json, 100, parsed.maxBoost(), 0.00001);
assertEquals(json, 1, parsed.getMinScore(), 0.0001);
}
@Override
public void testMustRewrite() throws IOException {
assumeTrue("test runs only when at least a type is registered", getCurrentTypes().length > 0);
super.testMustRewrite();
}
public void testRewrite() throws IOException {
FunctionScoreQueryBuilder functionScoreQueryBuilder = new FunctionScoreQueryBuilder(new WrapperQueryBuilder(new TermQueryBuilder("foo", "bar").toString()));
FunctionScoreQueryBuilder rewrite = (FunctionScoreQueryBuilder) functionScoreQueryBuilder.rewrite(queryShardContext());
assertNotSame(functionScoreQueryBuilder, rewrite);
assertEquals(rewrite.query(), new TermQueryBuilder("foo", "bar"));
}
public void testRewriteWithFunction() throws IOException {
TermQueryBuilder secondFunction = new TermQueryBuilder("tq", "2");
QueryBuilder queryBuilder = randomBoolean() ? new WrapperQueryBuilder(new TermQueryBuilder("foo", "bar").toString()) : new TermQueryBuilder("foo", "bar");
FunctionScoreQueryBuilder functionScoreQueryBuilder = new FunctionScoreQueryBuilder(queryBuilder,
new FunctionScoreQueryBuilder.FilterFunctionBuilder[]{
new FunctionScoreQueryBuilder.FilterFunctionBuilder(new WrapperQueryBuilder(new TermQueryBuilder("tq", "1").toString()), new RandomScoreFunctionBuilder()),
new FunctionScoreQueryBuilder.FilterFunctionBuilder(secondFunction, new RandomScoreFunctionBuilder())
});
FunctionScoreQueryBuilder rewrite = (FunctionScoreQueryBuilder) functionScoreQueryBuilder.rewrite(queryShardContext());
assertNotSame(functionScoreQueryBuilder, rewrite);
assertEquals(rewrite.query(), new TermQueryBuilder("foo", "bar"));
assertEquals(rewrite.filterFunctionBuilders()[0].getFilter(), new TermQueryBuilder("tq", "1"));
assertSame(rewrite.filterFunctionBuilders()[1].getFilter(), secondFunction);
}
}