apply fixes after review

This commit is contained in:
Simon Willnauer 2016-02-12 12:53:12 +01:00
parent 685bee3081
commit b906c8a389
32 changed files with 351 additions and 96 deletions

View File

@ -33,7 +33,6 @@ import org.elasticsearch.common.inject.Inject;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.index.IndexNotFoundException;
import org.elasticsearch.indices.IndexClosedException;
import org.elasticsearch.search.SearchService;
import org.elasticsearch.threadpool.ThreadPool;
import org.elasticsearch.transport.TransportService;

View File

@ -258,4 +258,23 @@ public abstract class AbstractQueryBuilder<QB extends AbstractQueryBuilder<QB>>
}
return queries;
}
@Override
public final QueryBuilder<?> rewrite(QueryRewriteContext queryShardContext) throws IOException {
QueryBuilder rewritten = doRewrite(queryShardContext);
if (rewritten == this) {
return rewritten;
}
if (queryName() != null && rewritten.queryName() == null) { // we inherit the name
rewritten.queryName(queryName());
}
if (boost() != DEFAULT_BOOST && rewritten.boost() == DEFAULT_BOOST) {
rewritten.boost(boost());
}
return rewritten;
}
protected QueryBuilder<?> doRewrite(QueryRewriteContext queryShardContext) throws IOException {
return this;
}
}

View File

@ -263,6 +263,13 @@ 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);
@ -273,6 +280,7 @@ public class BoolQueryBuilder extends AbstractQueryBuilder<BoolQueryBuilder> {
if (booleanQuery.clauses().isEmpty()) {
return new MatchAllDocsQuery();
}
final String minimumShouldMatch;
if (context.isFilter() && this.minimumShouldMatch == null && shouldClauses.size() > 0) {
minimumShouldMatch = "1";
@ -349,7 +357,7 @@ public class BoolQueryBuilder extends AbstractQueryBuilder<BoolQueryBuilder> {
}
@Override
public QueryBuilder<?> rewrite(QueryRewriteContext queryRewriteContext) throws IOException {
protected QueryBuilder<?> doRewrite(QueryRewriteContext queryRewriteContext) throws IOException {
BoolQueryBuilder newBuilder = new BoolQueryBuilder();
boolean changed = false;
final int clauses = mustClauses.size() + mustNotClauses.size() + filterClauses.size() + shouldClauses.size();
@ -372,14 +380,14 @@ public class BoolQueryBuilder extends AbstractQueryBuilder<BoolQueryBuilder> {
return this;
}
private static boolean rewriteClauses(QueryRewriteContext queryRewriteContext, List<QueryBuilder<?>> builders, Consumer<QueryBuilder<?>> conusmer) throws IOException {
private static boolean rewriteClauses(QueryRewriteContext queryRewriteContext, List<QueryBuilder<?>> builders, Consumer<QueryBuilder<?>> consumer) throws IOException {
boolean changed = false;
for (QueryBuilder builder : builders) {
QueryBuilder result = builder.rewrite(queryRewriteContext);
if (result != builder) {
changed = true;
}
conusmer.accept(result);
consumer.accept(result);
}
return changed;
}

View File

@ -160,12 +160,11 @@ public class BoostingQueryBuilder extends AbstractQueryBuilder<BoostingQueryBuil
}
@Override
public QueryBuilder<?> rewrite(QueryRewriteContext queryShardContext) throws IOException {
QueryBuilder positiveQuery = this.positiveQuery.rewrite(queryShardContext);
QueryBuilder negativeQuery = this.negativeQuery.rewrite(queryShardContext);
protected QueryBuilder<?> doRewrite(QueryRewriteContext queryRewriteContext) throws IOException {
QueryBuilder positiveQuery = this.positiveQuery.rewrite(queryRewriteContext);
QueryBuilder negativeQuery = this.negativeQuery.rewrite(queryRewriteContext);
if (positiveQuery != this.positiveQuery || negativeQuery != this.negativeQuery) {
BoostingQueryBuilder newQueryBuilder = new BoostingQueryBuilder(positiveQuery, negativeQuery)
.boost(boost()).queryName(queryName());
BoostingQueryBuilder newQueryBuilder = new BoostingQueryBuilder(positiveQuery, negativeQuery);
newQueryBuilder.negativeBoost = negativeBoost;
return newQueryBuilder;
}

View File

@ -106,10 +106,10 @@ public class ConstantScoreQueryBuilder extends AbstractQueryBuilder<ConstantScor
}
@Override
public QueryBuilder<?> rewrite(QueryRewriteContext queryShardContext) throws IOException {
QueryBuilder rewrite = filterBuilder.rewrite(queryShardContext);
protected QueryBuilder<?> doRewrite(QueryRewriteContext queryRewriteContext) throws IOException {
QueryBuilder rewrite = filterBuilder.rewrite(queryRewriteContext);
if (rewrite != filterBuilder) {
return new ConstantScoreQueryBuilder(rewrite).boost(boost()).queryName(queryName());
return new ConstantScoreQueryBuilder(rewrite);
}
return this;
}

View File

@ -20,24 +20,16 @@
package org.elasticsearch.index.query;
import org.apache.lucene.search.Query;
import org.elasticsearch.action.support.ToXContentToBytes;
import org.elasticsearch.common.io.stream.StreamInput;
import org.elasticsearch.common.io.stream.StreamOutput;
import org.elasticsearch.common.xcontent.ToXContent;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.common.xcontent.XContentType;
import java.io.IOException;
import java.util.Objects;
/**
* A {@link QueryBuilder} that is a stand in replacement for an empty query clause in the DSL.
* The current DSL allows parsing inner queries / filters like "{ }", in order to have a
* valid non-null representation of these clauses that actually do nothing we can use this class.
*
* This builder has no corresponding parser and it is not registered under the query name. It is
* intended to be used internally as a stand-in for nested queries that are left empty and should
* be ignored upstream.
*/
public final class EmptyQueryBuilder extends AbstractQueryBuilder<EmptyQueryBuilder> {

View File

@ -235,7 +235,7 @@ public class GeoShapeQueryBuilder extends AbstractQueryBuilder<GeoShapeQueryBuil
}
@Override
protected Query doToQuery(QueryShardContext context) throws IOException {
protected Query doToQuery(QueryShardContext context) {
if (shape == null) {
throw new UnsupportedOperationException("query must be rewritten first");
}
@ -449,12 +449,11 @@ public class GeoShapeQueryBuilder extends AbstractQueryBuilder<GeoShapeQueryBuil
}
@Override
public QueryBuilder<GeoShapeQueryBuilder> rewrite(QueryRewriteContext queryShardContext) throws IOException {
protected QueryBuilder<GeoShapeQueryBuilder> doRewrite(QueryRewriteContext queryShardContext) throws IOException {
if (this.shape == null) {
GetRequest getRequest = new GetRequest(indexedShapeIndex, indexedShapeType, indexedShapeId);
ShapeBuilder shape = fetch(queryShardContext.getClient(), getRequest, indexedShapePath);
return new GeoShapeQueryBuilder(this.fieldName, shape).relation(relation).strategy(strategy)
.boost(boost()).queryName(queryName());
return new GeoShapeQueryBuilder(this.fieldName, shape).relation(relation).strategy(strategy);
}
return this;
}

View File

@ -398,15 +398,15 @@ public class HasChildQueryBuilder extends AbstractQueryBuilder<HasChildQueryBuil
}
@Override
public QueryBuilder<?> rewrite(QueryRewriteContext queryShardContext) throws IOException {
QueryBuilder rewrite = query.rewrite(queryShardContext);
protected QueryBuilder<?> doRewrite(QueryRewriteContext queryRewriteContext) throws IOException {
QueryBuilder rewrite = query.rewrite(queryRewriteContext);
if (rewrite != query) {
HasChildQueryBuilder hasChildQueryBuilder = new HasChildQueryBuilder(type, rewrite);
hasChildQueryBuilder.minChildren = minChildren;
hasChildQueryBuilder.maxChildren = maxChildren;
hasChildQueryBuilder.scoreMode = scoreMode;
hasChildQueryBuilder.queryInnerHits = queryInnerHits;
return hasChildQueryBuilder.queryName(queryName()).boost(boost());
return hasChildQueryBuilder;
}
return this;
}

View File

@ -257,13 +257,13 @@ public class HasParentQueryBuilder extends AbstractQueryBuilder<HasParentQueryBu
}
@Override
public QueryBuilder<?> rewrite(QueryRewriteContext queryShardContext) throws IOException {
protected QueryBuilder<?> doRewrite(QueryRewriteContext queryShardContext) throws IOException {
QueryBuilder rewrite = query.rewrite(queryShardContext);
if (rewrite != query) {
HasParentQueryBuilder hasParentQueryBuilder = new HasParentQueryBuilder(type, rewrite);
hasParentQueryBuilder.score = score;
hasParentQueryBuilder.innerHit = innerHit;
return hasParentQueryBuilder.queryName(queryName()).boost(boost());
return hasParentQueryBuilder;
}
return this;
}

View File

@ -142,11 +142,11 @@ public class IndicesQueryBuilder extends AbstractQueryBuilder<IndicesQueryBuilde
}
@Override
public QueryBuilder<?> rewrite(QueryRewriteContext queryShardContext) throws IOException {
protected QueryBuilder<?> doRewrite(QueryRewriteContext queryShardContext) throws IOException {
QueryBuilder<?> newInnnerQuery = innerQuery.rewrite(queryShardContext);
QueryBuilder<?> newNoMatchQuery = noMatchQuery.rewrite(queryShardContext);
if (newInnnerQuery != innerQuery || newNoMatchQuery != noMatchQuery) {
return new IndicesQueryBuilder(innerQuery, indices).noMatchQuery(noMatchQuery).boost(boost()).queryName(queryName());
return new IndicesQueryBuilder(innerQuery, indices).noMatchQuery(noMatchQuery);
}
return this;
}

View File

@ -1052,7 +1052,7 @@ public class MoreLikeThisQueryBuilder extends AbstractQueryBuilder<MoreLikeThisQ
}
@Override
public QueryBuilder<?> rewrite(QueryRewriteContext queryShardContext) throws IOException {
protected QueryBuilder<?> doRewrite(QueryRewriteContext queryRewriteContext) throws IOException {
// TODO this needs heavy cleanups before we can rewrite it
return this;
}

View File

@ -226,10 +226,10 @@ public class NestedQueryBuilder extends AbstractQueryBuilder<NestedQueryBuilder>
}
@Override
public QueryBuilder<?> rewrite(QueryRewriteContext queryShardContext) throws IOException {
QueryBuilder rewrite = query.rewrite(queryShardContext);
protected QueryBuilder<?> doRewrite(QueryRewriteContext queryRewriteContext) throws IOException {
QueryBuilder rewrite = query.rewrite(queryRewriteContext);
if (rewrite != query) {
return new NestedQueryBuilder(path, rewrite).queryName(queryName()).boost(boost()).scoreMode(scoreMode);
return new NestedQueryBuilder(path, rewrite).scoreMode(scoreMode);
}
return this;
}

View File

@ -74,16 +74,16 @@ public interface QueryBuilder<QB extends QueryBuilder<QB>> extends NamedWriteabl
String getName();
/**
* Rewrites this query builder into it's primitive form. By default this method return theb builder itself. If the builder
* did not change the identity reference must be returend otherwise the builder will be rewritten infinitely.
* Rewrites this query builder into its primitive form. By default this method return the builder itself. If the builder
* did not change the identity reference must be returned otherwise the builder will be rewritten infinitely.
*/
default QueryBuilder<?> rewrite(QueryRewriteContext queryShardContext) throws IOException {
return this;
}
/**
* Rewrites the given query into it's primitive form. Queries that for instance fetch resources from remote hosts or
* can simplify / optimize itself should do their heavy lifting duringt {@link #rewrite(QueryRewriteContext)}. This method
* Rewrites the given query into its primitive form. Queries that for instance fetch resources from remote hosts or
* can simplify / optimize itself should do their heavy lifting during {@link #rewrite(QueryRewriteContext)}. This method
* rewrites the query until it doesn't change anymore.
* @throws IOException if an {@link IOException} occurs
*/

View File

@ -24,6 +24,7 @@ import org.elasticsearch.indices.query.IndicesQueriesRegistry;
import org.elasticsearch.script.ScriptService;
/**
* Context object used to rewrite {@link QueryBuilder} instances into simplified version.
*/
public class QueryRewriteContext {
protected final ScriptService scriptService;
@ -38,26 +39,34 @@ public class QueryRewriteContext {
this.parseContext = new QueryParseContext(indicesQueriesRegistry);
}
/**
* Returns a clients to fetch resources from local or remove nodes.
*/
public final Client getClient() {
return scriptService.getClient();
}
/**
* Returns the index settings for this context. This might return null if the
* context has not index scope.
*/
public final IndexSettings getIndexSettings() {
return indexSettings;
}
/**
* Returns a script service to fetch scripts.
*/
public final ScriptService getScriptService() {
return scriptService;
}
/**
* Returns a new {@link QueryParseContext} to parse template or wrapped queries.
*/
public QueryParseContext newParseContext() {
QueryParseContext queryParseContext = new QueryParseContext(indicesQueriesRegistry);
queryParseContext.parseFieldMatcher(parseContext.parseFieldMatcher());
return queryParseContext;
}
public boolean hasIndex() {
return indexSettings != null;
}
}

View File

@ -191,10 +191,6 @@ public class QueryShardContext extends QueryRewriteContext {
return unmodifiableMap(new HashMap<>(namedQueries));
}
public void combineNamedQueries(QueryShardContext context) {
namedQueries.putAll(context.namedQueries);
}
/**
* Return whether we are currently parsing a filter or a query.
*/

View File

@ -25,7 +25,6 @@ import org.apache.lucene.search.Query;
import org.apache.lucene.search.RandomAccessWeight;
import org.apache.lucene.search.Weight;
import org.apache.lucene.util.Bits;
import org.elasticsearch.client.Client;
import org.elasticsearch.common.io.stream.StreamInput;
import org.elasticsearch.common.io.stream.StreamOutput;
import org.elasticsearch.common.xcontent.XContentBuilder;
@ -33,7 +32,6 @@ import org.elasticsearch.script.LeafSearchScript;
import org.elasticsearch.script.Script;
import org.elasticsearch.script.Script.ScriptField;
import org.elasticsearch.script.ScriptContext;
import org.elasticsearch.script.ScriptEngineService;
import org.elasticsearch.script.ScriptService;
import org.elasticsearch.script.SearchScript;
import org.elasticsearch.search.lookup.SearchLookup;

View File

@ -127,7 +127,7 @@ public class TemplateQueryBuilder extends AbstractQueryBuilder<TemplateQueryBuil
}
@Override
public QueryBuilder<?> rewrite(QueryRewriteContext queryRewriteContext) throws IOException {
protected QueryBuilder<?> doRewrite(QueryRewriteContext queryRewriteContext) throws IOException {
ExecutableScript executable = queryRewriteContext.getScriptService().executable(template,
ScriptContext.Standard.SEARCH, Collections.emptyMap());
BytesReference querySource = (BytesReference) executable.run();
@ -135,11 +135,10 @@ public class TemplateQueryBuilder extends AbstractQueryBuilder<TemplateQueryBuil
try (XContentParser qSourceParser = XContentFactory.xContent(querySource).createParser(querySource)) {
queryParseContext.reset(qSourceParser);
final QueryBuilder<?> queryBuilder = queryParseContext.parseInnerQueryBuilder();
if (queryBuilder.boost() == DEFAULT_BOOST) {
queryBuilder.boost(boost()); // only pass down the boost if it has it's own boost
}
if (queryName() != null) {
queryBuilder.queryName(queryName());
if (boost() != DEFAULT_BOOST || queryName() != null) {
final BoolQueryBuilder boolQueryBuilder = new BoolQueryBuilder();
boolQueryBuilder.must(queryBuilder);
return boolQueryBuilder;
}
return queryBuilder;
}

View File

@ -316,18 +316,18 @@ public class TermsQueryBuilder extends AbstractQueryBuilder<TermsQueryBuilder> {
}
@Override
public QueryBuilder<TermsQueryBuilder> rewrite(QueryRewriteContext queryRewriteContext) throws IOException {
protected QueryBuilder<?> doRewrite(QueryRewriteContext queryRewriteContext) throws IOException {
if (this.termsLookup != null) {
TermsLookup termsLookup = new TermsLookup(this.termsLookup);
if (termsLookup.index() == null) { // TODO this should go away?
if (queryRewriteContext.hasIndex()) {
if (queryRewriteContext.getIndexSettings() != null) {
termsLookup.index(queryRewriteContext.getIndexSettings().getIndex().getName());
} else {
return this; // can't rewrite until we have index scope on the shard
}
}
List<Object> values = fetch(termsLookup, queryRewriteContext.getClient());
return new TermsQueryBuilder(this.fieldName, values).boost(boost()).queryName(queryName());
return new TermsQueryBuilder(this.fieldName, values);
}
return this;
}

View File

@ -105,7 +105,7 @@ public class WrapperQueryBuilder extends AbstractQueryBuilder<WrapperQueryBuilde
@Override
protected Query doToQuery(QueryShardContext context) throws IOException {
throw new UnsupportedOperationException("this query must be rewritten first");
throw new UnsupportedOperationException("this query must be rewritten first");
}
@Override
@ -129,17 +129,16 @@ public class WrapperQueryBuilder extends AbstractQueryBuilder<WrapperQueryBuilde
}
@Override
public QueryBuilder<?> rewrite(QueryRewriteContext context) throws IOException {
protected QueryBuilder<?> doRewrite(QueryRewriteContext context) throws IOException {
try (XContentParser qSourceParser = XContentFactory.xContent(source).createParser(source)) {
QueryParseContext parseContext = context.newParseContext();
parseContext.reset(qSourceParser);
final QueryBuilder<?> queryBuilder = parseContext.parseInnerQueryBuilder();
if (queryBuilder.boost() == DEFAULT_BOOST) {
queryBuilder.boost(boost());
}
if (queryName() != null) { // we inherit the name
queryBuilder.queryName(queryName());
if (boost() != DEFAULT_BOOST || queryName() != null) {
final BoolQueryBuilder boolQueryBuilder = new BoolQueryBuilder();
boolQueryBuilder.must(queryBuilder);
return boolQueryBuilder;
}
return queryBuilder;
}

View File

@ -394,17 +394,32 @@ public class FunctionScoreQueryBuilder extends AbstractQueryBuilder<FunctionScor
public FilterFunctionBuilder readFrom(StreamInput in) throws IOException {
return new FilterFunctionBuilder(in.readQuery(), in.readScoreFunction());
}
public FilterFunctionBuilder rewrite(QueryRewriteContext context) throws IOException {
QueryBuilder<?> rewrite = filter.rewrite(context);
if (rewrite != filter) {
return new FilterFunctionBuilder(rewrite, scoreFunction);
}
return this;
}
}
@Override
public QueryBuilder<?> rewrite(QueryRewriteContext queryShardContext) throws IOException {
QueryBuilder<?> queryBuilder = this.query.rewrite(queryShardContext);
if (queryBuilder != query) {
FunctionScoreQueryBuilder newQueryBuilder = new FunctionScoreQueryBuilder(queryBuilder, filterFunctionBuilders);
protected QueryBuilder<?> doRewrite(QueryRewriteContext queryRewriteContext) throws IOException {
QueryBuilder<?> queryBuilder = this.query.rewrite(queryRewriteContext);
FilterFunctionBuilder[] rewrittenBuilders = new FilterFunctionBuilder[this.filterFunctionBuilders.length];
boolean rewritten = false;
for (int i = 0; i < rewrittenBuilders.length; i++) {
FilterFunctionBuilder rewrite = filterFunctionBuilders[i].rewrite(queryRewriteContext);
rewritten |= rewrite != filterFunctionBuilders[i];
rewrittenBuilders[i] = rewrite;
}
if (queryBuilder != query || rewritten) {
FunctionScoreQueryBuilder newQueryBuilder = new FunctionScoreQueryBuilder(queryBuilder, rewrittenBuilders);
newQueryBuilder.scoreMode = scoreMode;
newQueryBuilder.minScore = minScore;
newQueryBuilder.maxBoost = maxBoost;
return newQueryBuilder.queryName(queryName()).boost(boost());
return newQueryBuilder;
}
return this;
}

View File

@ -554,6 +554,7 @@ public abstract class AbstractQueryTestCase<QB extends AbstractQueryBuilder<QB>>
private QueryBuilder<?> rewriteQuery(QB queryBuilder, QueryRewriteContext rewriteContext) throws IOException {
QueryBuilder<?> rewritten = QueryBuilder.rewriteQuery(queryBuilder, rewriteContext);
// extra safety to fail fast - serialize the rewritten version to ensure it's serializable.
assertSerialization(rewritten);
return rewritten;
}
@ -975,4 +976,16 @@ public abstract class AbstractQueryTestCase<QB extends AbstractQueryBuilder<QB>>
}
return "";
}
/**
* This test ensures that queries that need to be rewritten have dedicated tests.
* These queries must override this method accordingly.
*/
public void testMustRewrite() throws IOException {
QueryShardContext context = createShardContext();
context.setAllowUnmappedFields(true);
QB queryBuilder = createTestQueryBuilder();
setSearchContext(randomTypes); // only set search context for toQuery to be more realistic
queryBuilder.toQuery(context);
}
}

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().must(termQuery("foo", "bar")).buildAsBytes()).toQuery(createShardContext());
BooleanQuery bq = (BooleanQuery) parseQuery(boolQuery().filter(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().must(termQuery("foo", "bar"))).buildAsBytes()).toQuery(createShardContext());
ConstantScoreQuery csq = (ConstantScoreQuery) parseQuery(constantScoreQuery(boolQuery().filter(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().must(new MatchAllQueryBuilder()));
boolQueryBuilder.filter(new BoolQueryBuilder().filter(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.MUST));
assertThat(innerBooleanClause.getOccur(), equalTo(BooleanClause.Occur.FILTER));
assertThat(innerBooleanClause.getQuery(), instanceOf(MatchAllDocsQuery.class));
}
@ -343,4 +343,70 @@ public class BoolQueryBuilderTests extends AbstractQueryTestCase<BoolQueryBuilde
assertEquals(query, "23", queryBuilder.minimumShouldMatch());
assertEquals(query, "kimchy", ((TermQueryBuilder)queryBuilder.must().get(0)).value());
}
public void testRewrite() throws IOException {
BoolQueryBuilder boolQueryBuilder = new BoolQueryBuilder();
boolean mustRewrite = false;
if (randomBoolean()) {
mustRewrite = true;
boolQueryBuilder.must(new WrapperQueryBuilder(new TermsQueryBuilder("foo", "must").toString()));
}
if (randomBoolean()) {
mustRewrite = true;
boolQueryBuilder.should(new WrapperQueryBuilder(new TermsQueryBuilder("foo", "should").toString()));
}
if (randomBoolean()) {
mustRewrite = true;
boolQueryBuilder.filter(new WrapperQueryBuilder(new TermsQueryBuilder("foo", "filter").toString()));
}
if (randomBoolean()) {
mustRewrite = true;
boolQueryBuilder.mustNot(new WrapperQueryBuilder(new TermsQueryBuilder("foo", "must_not").toString()));
}
if (mustRewrite == false && randomBoolean()) {
boolQueryBuilder.must(new TermsQueryBuilder("foo", "no_rewrite"));
}
QueryBuilder<?> rewritten = boolQueryBuilder.rewrite(queryShardContext());
if (mustRewrite == false && boolQueryBuilder.must().isEmpty()) {
// if it's empty we rewrite to match all
assertEquals(rewritten, new MatchAllQueryBuilder());
} else {
BoolQueryBuilder rewrite = (BoolQueryBuilder) rewritten;
if (mustRewrite) {
assertNotSame(rewrite, boolQueryBuilder);
if (boolQueryBuilder.must().isEmpty() == false) {
assertEquals(new TermsQueryBuilder("foo", "must"), rewrite.must().get(0));
}
if (boolQueryBuilder.should().isEmpty() == false) {
assertEquals(new TermsQueryBuilder("foo", "should"), rewrite.should().get(0));
}
if (boolQueryBuilder.mustNot().isEmpty() == false) {
assertEquals(new TermsQueryBuilder("foo", "must_not"), rewrite.mustNot().get(0));
}
if (boolQueryBuilder.filter().isEmpty() == false) {
assertEquals(new TermsQueryBuilder("foo", "filter"), rewrite.filter().get(0));
}
} else {
assertSame(rewrite, boolQueryBuilder);
if (boolQueryBuilder.must().isEmpty() == false) {
assertSame(boolQueryBuilder.must().get(0), rewrite.must().get(0));
}
}
}
}
public void testRewriteMultipleTimes() throws IOException {
BoolQueryBuilder boolQueryBuilder = new BoolQueryBuilder();
boolQueryBuilder.must(new WrapperQueryBuilder(new WrapperQueryBuilder(new MatchAllQueryBuilder().toString()).toString()));
QueryBuilder<?> rewritten = boolQueryBuilder.rewrite(queryShardContext());
BoolQueryBuilder expected = new BoolQueryBuilder();
expected.must(new WrapperQueryBuilder(new MatchAllQueryBuilder().toString()));
assertEquals(expected, rewritten);
expected = new BoolQueryBuilder();
expected.must(new MatchAllQueryBuilder());
QueryBuilder<?> rewrittenAgain = rewritten.rewrite(queryShardContext());
assertEquals(rewrittenAgain, expected);
assertEquals(QueryBuilder.rewriteQuery(boolQueryBuilder, queryShardContext()), expected);
}
}

View File

@ -20,6 +20,7 @@
package org.elasticsearch.index.query;
import org.apache.lucene.queries.BoostingQuery;
import org.apache.lucene.search.MatchAllDocsQuery;
import org.apache.lucene.search.Query;
import java.io.IOException;
@ -72,27 +73,27 @@ public class BoostingQueryBuilderTests extends AbstractQueryTestCase<BoostingQue
public void testFromJson() throws IOException {
String query =
"{\n" +
" \"boosting\" : {\n" +
" \"positive\" : {\n" +
" \"term\" : {\n" +
" \"field1\" : {\n" +
" \"value\" : \"value1\",\n" +
" \"boost\" : 5.0\n" +
" }\n" +
" }\n" +
" },\n" +
" \"negative\" : {\n" +
" \"term\" : {\n" +
" \"field2\" : {\n" +
" \"value\" : \"value2\",\n" +
" \"boost\" : 8.0\n" +
" }\n" +
" }\n" +
" },\n" +
" \"negative_boost\" : 23.0,\n" +
" \"boost\" : 42.0\n" +
" }\n" +
"{\n" +
" \"boosting\" : {\n" +
" \"positive\" : {\n" +
" \"term\" : {\n" +
" \"field1\" : {\n" +
" \"value\" : \"value1\",\n" +
" \"boost\" : 5.0\n" +
" }\n" +
" }\n" +
" },\n" +
" \"negative\" : {\n" +
" \"term\" : {\n" +
" \"field2\" : {\n" +
" \"value\" : \"value2\",\n" +
" \"boost\" : 8.0\n" +
" }\n" +
" }\n" +
" },\n" +
" \"negative_boost\" : 23.0,\n" +
" \"boost\" : 42.0\n" +
" }\n" +
"}";
BoostingQueryBuilder queryBuilder = (BoostingQueryBuilder) parseQuery(query);
@ -103,4 +104,17 @@ public class BoostingQueryBuilderTests extends AbstractQueryTestCase<BoostingQue
assertEquals(query, 8, queryBuilder.negativeQuery().boost(), 0.00001);
assertEquals(query, 5, queryBuilder.positiveQuery().boost(), 0.00001);
}
public void testRewrite() throws IOException {
QueryBuilder positive = randomBoolean() ? new MatchAllQueryBuilder() : new WrapperQueryBuilder(new TermQueryBuilder("pos", "bar").toString());
QueryBuilder negative = randomBoolean() ? new MatchAllQueryBuilder() : new WrapperQueryBuilder(new TermQueryBuilder("neg", "bar").toString());
BoostingQueryBuilder qb = new BoostingQueryBuilder(positive, negative);
QueryBuilder<?> rewrite = qb.rewrite(queryShardContext());
if (positive instanceof MatchAllQueryBuilder && negative instanceof MatchAllQueryBuilder) {
assertSame(rewrite, qb);
} else {
assertNotSame(rewrite, qb);
assertEquals(new BoostingQueryBuilder(positive.rewrite(queryShardContext()), negative.rewrite(queryShardContext())), rewrite);
}
}
}

View File

@ -468,4 +468,10 @@ public class GeoBoundingBoxQueryBuilderTests extends AbstractQueryTestCase<GeoBo
assertEquals(json, 1.0, parsed.boost(), 0.0001);
assertEquals(json, GeoExecType.MEMORY, parsed.type());
}
@Override
public void testMustRewrite() throws IOException {
assumeTrue("test runs only when at least a type is registered", getCurrentTypes().length > 0);
super.testMustRewrite();
}
}

View File

@ -411,4 +411,10 @@ public class GeoDistanceQueryBuilderTests extends AbstractQueryTestCase<GeoDista
assertEquals(json, 40.0, parsed.point().getLat(), 0.0001);
assertEquals(json, 12000.0, parsed.distance(), 0.0001);
}
@Override
public void testMustRewrite() throws IOException {
assumeTrue("test runs only when at least a type is registered", getCurrentTypes().length > 0);
super.testMustRewrite();
}
}

View File

@ -316,4 +316,10 @@ public class GeoDistanceRangeQueryTests extends AbstractQueryTestCase<GeoDistanc
checkGeneratedJson(json, parsed);
assertEquals(json, -70.0, parsed.point().lon(), 0.0001);
}
@Override
public void testMustRewrite() throws IOException {
assumeTrue("test runs only when at least a type is registered", getCurrentTypes().length > 0);
super.testMustRewrite();
}
}

View File

@ -343,4 +343,10 @@ public class GeoPolygonQueryBuilderTests extends AbstractQueryTestCase<GeoPolygo
checkGeneratedJson(json, parsed);
assertEquals(json, 4, parsed.points().size());
}
@Override
public void testMustRewrite() throws IOException {
assumeTrue("test runs only when at least a type is registered", getCurrentTypes().length > 0);
super.testMustRewrite();
}
}

View File

@ -240,4 +240,24 @@ public class GeoShapeQueryBuilderTests extends AbstractQueryTestCase<GeoShapeQue
checkGeneratedJson(json, parsed);
assertEquals(json, 42.0, parsed.boost(), 0.0001);
}
@Override
public void testMustRewrite() throws IOException {
GeoShapeQueryBuilder sqb;
do {
sqb = doCreateTestQueryBuilder();
// do this until we get one without a shape
} while (sqb.shape() != null);
try {
sqb.toQuery(queryShardContext());
fail();
} catch (UnsupportedOperationException e) {
assertEquals("query must be rewritten first", e.getMessage());
}
QueryBuilder<?> rewrite = sqb.rewrite(queryShardContext());
GeoShapeQueryBuilder geoShapeQueryBuilder = new GeoShapeQueryBuilder(GEO_SHAPE_FIELD_NAME, indexedShapeToReturn);
geoShapeQueryBuilder.strategy(sqb.strategy());
geoShapeQueryBuilder.relation(sqb.relation());
assertEquals(geoShapeQueryBuilder, rewrite);
}
}

View File

@ -145,4 +145,10 @@ public class GeohashCellQueryBuilderTests extends AbstractQueryTestCase<Builder>
checkGeneratedJson(json, parsed);
assertEquals(json, 3, parsed.precision().intValue());
}
@Override
public void testMustRewrite() throws IOException {
assumeTrue("test runs only when at least a type is registered", getCurrentTypes().length > 0);
super.testMustRewrite();
}
}

View File

@ -22,6 +22,7 @@ package org.elasticsearch.index.query;
import org.apache.lucene.search.Query;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.common.xcontent.XContentFactory;
import org.elasticsearch.common.xcontent.XContentParser;
import org.elasticsearch.common.xcontent.XContentType;
import org.elasticsearch.script.Script.ScriptParseException;
import org.elasticsearch.script.ScriptService.ScriptType;
@ -29,6 +30,7 @@ import org.elasticsearch.script.Template;
import org.junit.BeforeClass;
import java.io.IOException;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
@ -118,4 +120,40 @@ public class TemplateQueryBuilderTests extends AbstractQueryTestCase<TemplateQue
XContentType.JSON, params));
assertParsedQuery(query, expectedBuilder);
}
@Override
public void testMustRewrite() throws IOException {
String query = "{ \"match_all\" : {}}";
QueryBuilder<?> builder = new TemplateQueryBuilder(new Template(query, ScriptType.INLINE, "mockscript",
XContentType.JSON, Collections.emptyMap()));
try {
builder.toQuery(queryShardContext());
fail();
} catch (UnsupportedOperationException ex) {
assertEquals("this query must be rewritten first", ex.getMessage());
}
assertEquals(new MatchAllQueryBuilder(), builder.rewrite(queryShardContext()));
}
public void testRewriteWithInnerName() throws IOException {
final String query = "{ \"match_all\" : {\"_name\" : \"foobar\"}}";
QueryBuilder<?> builder = new TemplateQueryBuilder(new Template(query, ScriptType.INLINE, "mockscript",
XContentType.JSON, Collections.emptyMap()));
assertEquals(new MatchAllQueryBuilder().queryName("foobar"), builder.rewrite(queryShardContext()));
builder = new TemplateQueryBuilder(new Template(query, ScriptType.INLINE, "mockscript",
XContentType.JSON, Collections.emptyMap())).queryName("outer");
assertEquals(new BoolQueryBuilder().must(new MatchAllQueryBuilder().queryName("foobar")).queryName("outer"), builder.rewrite(queryShardContext()));
}
public void testRewriteWithInnerBoost() throws IOException {
final TermQueryBuilder query = new TermQueryBuilder("foo", "bar").boost(2);
QueryBuilder<?> builder = new TemplateQueryBuilder(new Template(query.toString(), ScriptType.INLINE, "mockscript",
XContentType.JSON, Collections.emptyMap()));
assertEquals(query, builder.rewrite(queryShardContext()));
builder = new TemplateQueryBuilder(new Template(query.toString(), ScriptType.INLINE, "mockscript",
XContentType.JSON, Collections.emptyMap())).boost(3);
assertEquals(new BoolQueryBuilder().must(query).boost(3), builder.rewrite(queryShardContext()));
}
}

View File

@ -272,5 +272,17 @@ public class TermsQueryBuilderTests extends AbstractQueryTestCase<TermsQueryBuil
assertEquals(json, 2, parsed.values().size());
}
@Override
public void testMustRewrite() throws IOException {
TermsQueryBuilder termsQueryBuilder = new TermsQueryBuilder(STRING_FIELD_NAME, randomTermsLookup());
try {
termsQueryBuilder.toQuery(queryShardContext());
fail();
} catch (UnsupportedOperationException ex) {
assertEquals("query must be rewritten first", ex.getMessage());
}
assertEquals(termsQueryBuilder.rewrite(queryShardContext()), new TermsQueryBuilder(STRING_FIELD_NAME, randomTerms));
}
}

View File

@ -133,4 +133,34 @@ public class WrapperQueryBuilderTests extends AbstractQueryTestCase<WrapperQuery
throw new RuntimeException(e);
}
}
@Override
public void testMustRewrite() throws IOException {
TermQueryBuilder tqb = new TermQueryBuilder("foo", "bar");
WrapperQueryBuilder qb = new WrapperQueryBuilder(tqb.toString());
try {
qb.toQuery(queryShardContext());
fail();
} catch (UnsupportedOperationException e) {
assertEquals("this query must be rewritten first", e.getMessage());
}
QueryBuilder<?> rewrite = qb.rewrite(queryShardContext());
assertEquals(tqb, rewrite);
}
public void testRewriteWithInnerName() throws IOException {
QueryBuilder<?> builder = new WrapperQueryBuilder("{ \"match_all\" : {\"_name\" : \"foobar\"}}");
assertEquals(new MatchAllQueryBuilder().queryName("foobar"), builder.rewrite(queryShardContext()));
builder = new WrapperQueryBuilder("{ \"match_all\" : {\"_name\" : \"foobar\"}}").queryName("outer");
assertEquals(new BoolQueryBuilder().must(new MatchAllQueryBuilder().queryName("foobar")).queryName("outer"), builder.rewrite(queryShardContext()));
}
public void testRewriteWithInnerBoost() throws IOException {
final TermQueryBuilder query = new TermQueryBuilder("foo", "bar").boost(2);
QueryBuilder<?> builder = new WrapperQueryBuilder(query.toString());
assertEquals(query, builder.rewrite(queryShardContext()));
builder = new WrapperQueryBuilder(query.toString()).boost(3);
assertEquals(new BoolQueryBuilder().must(query).boost(3), builder.rewrite(queryShardContext()));
}
}