From 10ddd691a36fa3a8199fac8da43da540af19a697 Mon Sep 17 00:00:00 2001 From: javanna Date: Fri, 23 Oct 2015 11:55:28 +0200 Subject: [PATCH] Internal: move to lucene BoostQuery Latest version of lucene deprecated Query#setBoost and Query#getBoost which made queries effectively immutable. Those methods need to be replaced with `BoostQuery` that wraps any query that needs boosting. This commit replaces usages of setBoost with BoostQuery and adds it to forbidden-apis for prod code. Usages of `getBoost` are only partially removed, as some will have to stay for backwards compatibility. Closes #14264 --- .../resources/forbidden/core-signatures.txt | 3 + .../lucene/queries/BlendedTermQuery.java | 67 ++-- .../apache/lucene/queries/MinDocQuery.java | 3 +- .../classic/MapperQueryParser.java | 70 ++--- .../vectorhighlight/CustomFieldQuery.java | 9 +- .../common/lucene/all/AllTermQuery.java | 13 +- .../lucene/search/MoreLikeThisQuery.java | 36 +-- .../lucene/search/MultiPhrasePrefixQuery.java | 18 +- .../common/lucene/search/Queries.java | 13 +- .../common/lucene/search/XMoreLikeThis.java | 4 +- .../function/FiltersFunctionScoreQuery.java | 5 +- .../search/function/FunctionScoreQuery.java | 5 +- .../index/mapper/core/DateFieldMapper.java | 15 +- .../index/query/AbstractQueryBuilder.java | 25 +- .../index/query/BoolQueryBuilder.java | 4 +- .../index/query/HasChildQueryBuilder.java | 18 +- .../index/query/HasParentQueryBuilder.java | 1 - .../index/query/IndicesQueryBuilder.java | 8 - .../index/query/MatchQueryBuilder.java | 3 +- .../index/query/MultiMatchQueryBuilder.java | 6 - .../index/query/QueryStringQueryBuilder.java | 27 +- .../index/query/ScriptQueryBuilder.java | 5 +- .../index/query/SimpleQueryParser.java | 28 +- .../index/query/SimpleQueryStringBuilder.java | 5 - .../query/SpanMultiTermQueryBuilder.java | 17 +- .../index/query/TemplateQueryBuilder.java | 5 - .../index/query/WrapperQueryBuilder.java | 5 - .../index/search/MultiMatchQuery.java | 14 +- .../geo/InMemoryGeoBoundingBoxQuery.java | 7 +- .../search/nested/IncludeNestedDocsQuery.java | 295 ------------------ .../search/internal/DefaultSearchContext.java | 7 +- .../search/MultiPhrasePrefixQueryTests.java | 6 +- .../index/query/AbstractQueryTestCase.java | 47 ++- .../index/query/DisMaxQueryBuilderTests.java | 19 +- .../index/query/FuzzyQueryBuilderTests.java | 13 +- .../index/query/IndicesQueryBuilderTests.java | 10 +- .../index/query/MatchQueryBuilderTests.java | 16 +- .../query/MultiMatchQueryBuilderTests.java | 33 +- .../query/QueryStringQueryBuilderTests.java | 82 +++-- .../index/query/RandomQueryBuilder.java | 23 +- .../query/SimpleQueryStringBuilderTests.java | 60 ++-- .../query/SpanMultiTermQueryBuilderTests.java | 20 ++ .../query/TemplateQueryBuilderTests.java | 5 - .../index/query/WrapperQueryBuilderTests.java | 5 - 44 files changed, 380 insertions(+), 700 deletions(-) delete mode 100644 core/src/main/java/org/elasticsearch/index/search/nested/IncludeNestedDocsQuery.java diff --git a/buildSrc/src/main/resources/forbidden/core-signatures.txt b/buildSrc/src/main/resources/forbidden/core-signatures.txt index 08c548f1dcc..0f54946ea37 100644 --- a/buildSrc/src/main/resources/forbidden/core-signatures.txt +++ b/buildSrc/src/main/resources/forbidden/core-signatures.txt @@ -87,3 +87,6 @@ java.util.concurrent.Future#cancel(boolean) @defaultMessage Don't try reading from paths that are not configured in Environment, resolve from Environment instead org.elasticsearch.common.io.PathUtils#get(java.lang.String, java.lang.String[]) org.elasticsearch.common.io.PathUtils#get(java.net.URI) + +@defaultMessage Don't use deprecated Query#setBoost, wrap the query into a BoostQuery instead +org.apache.lucene.search.Query#setBoost(float) diff --git a/core/src/main/java/org/apache/lucene/queries/BlendedTermQuery.java b/core/src/main/java/org/apache/lucene/queries/BlendedTermQuery.java index e411139bab0..81f49055223 100644 --- a/core/src/main/java/org/apache/lucene/queries/BlendedTermQuery.java +++ b/core/src/main/java/org/apache/lucene/queries/BlendedTermQuery.java @@ -18,18 +18,9 @@ */ package org.apache.lucene.queries; -import org.apache.lucene.index.IndexReader; -import org.apache.lucene.index.IndexReaderContext; -import org.apache.lucene.index.LeafReaderContext; -import org.apache.lucene.index.Term; -import org.apache.lucene.index.TermContext; -import org.apache.lucene.index.TermState; -import org.apache.lucene.search.BooleanClause; +import org.apache.lucene.index.*; +import org.apache.lucene.search.*; import org.apache.lucene.search.BooleanClause.Occur; -import org.apache.lucene.search.BooleanQuery; -import org.apache.lucene.search.DisjunctionMaxQuery; -import org.apache.lucene.search.Query; -import org.apache.lucene.search.TermQuery; import org.apache.lucene.util.ArrayUtil; import org.apache.lucene.util.InPlaceMergeSorter; import org.apache.lucene.util.ToStringUtils; @@ -37,6 +28,7 @@ import org.apache.lucene.util.ToStringUtils; import java.io.IOException; import java.util.Arrays; import java.util.List; +import java.util.Objects; /** * BlendedTermQuery can be used to unify term statistics across @@ -77,6 +69,10 @@ public abstract class BlendedTermQuery extends Query { @Override public Query rewrite(IndexReader reader) throws IOException { + Query rewritten = super.rewrite(reader); + if (rewritten != this) { + return rewritten; + } IndexReaderContext context = reader.getContext(); TermContext[] ctx = new TermContext[terms.length]; int[] docFreqs = new int[ctx.length]; @@ -87,9 +83,7 @@ public abstract class BlendedTermQuery extends Query { final int maxDoc = reader.maxDoc(); blend(ctx, maxDoc, reader); - Query query = topLevelQuery(terms, ctx, docFreqs, maxDoc); - query.setBoost(getBoost()); - return query; + return topLevelQuery(terms, ctx, docFreqs, maxDoc); } protected abstract Query topLevelQuery(Term[] terms, TermContext[] ctx, int[] docFreqs, int maxDoc); @@ -274,20 +268,15 @@ public abstract class BlendedTermQuery extends Query { @Override public boolean equals(Object o) { if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; if (!super.equals(o)) return false; BlendedTermQuery that = (BlendedTermQuery) o; - if (!Arrays.equals(equalsTerms(), that.equalsTerms())) return false; - - return true; + return Arrays.equals(equalsTerms(), that.equalsTerms()); } @Override public int hashCode() { - int result = super.hashCode(); - result = 31 * result + Arrays.hashCode(equalsTerms()); - return result; + return Objects.hash(super.hashCode(), Arrays.hashCode(equalsTerms())); } public static BlendedTermQuery booleanBlendedQuery(Term[] terms, final boolean disableCoord) { @@ -298,16 +287,16 @@ public abstract class BlendedTermQuery extends Query { return new BlendedTermQuery(terms, boosts) { @Override protected Query topLevelQuery(Term[] terms, TermContext[] ctx, int[] docFreqs, int maxDoc) { - BooleanQuery.Builder query = new BooleanQuery.Builder(); - query.setDisableCoord(disableCoord); + BooleanQuery.Builder booleanQueryBuilder = new BooleanQuery.Builder(); + booleanQueryBuilder.setDisableCoord(disableCoord); for (int i = 0; i < terms.length; i++) { - TermQuery termQuery = new TermQuery(terms[i], ctx[i]); - if (boosts != null) { - termQuery.setBoost(boosts[i]); + Query query = new TermQuery(terms[i], ctx[i]); + if (boosts != null && boosts[i] != 1f) { + query = new BoostQuery(query, boosts[i]); } - query.add(termQuery, BooleanClause.Occur.SHOULD); + booleanQueryBuilder.add(query, BooleanClause.Occur.SHOULD); } - return query.build(); + return booleanQueryBuilder.build(); } }; } @@ -321,16 +310,16 @@ public abstract class BlendedTermQuery extends Query { BooleanQuery.Builder lowBuilder = new BooleanQuery.Builder(); lowBuilder.setDisableCoord(disableCoord); for (int i = 0; i < terms.length; i++) { - TermQuery termQuery = new TermQuery(terms[i], ctx[i]); - if (boosts != null) { - termQuery.setBoost(boosts[i]); + Query query = new TermQuery(terms[i], ctx[i]); + if (boosts != null && boosts[i] != 1f) { + query = new BoostQuery(query, boosts[i]); } if ((maxTermFrequency >= 1f && docFreqs[i] > maxTermFrequency) || (docFreqs[i] > (int) Math.ceil(maxTermFrequency * (float) maxDoc))) { - highBuilder.add(termQuery, BooleanClause.Occur.SHOULD); + highBuilder.add(query, BooleanClause.Occur.SHOULD); } else { - lowBuilder.add(termQuery, BooleanClause.Occur.SHOULD); + lowBuilder.add(query, BooleanClause.Occur.SHOULD); } } BooleanQuery high = highBuilder.build(); @@ -363,15 +352,15 @@ public abstract class BlendedTermQuery extends Query { return new BlendedTermQuery(terms, boosts) { @Override protected Query topLevelQuery(Term[] terms, TermContext[] ctx, int[] docFreqs, int maxDoc) { - DisjunctionMaxQuery query = new DisjunctionMaxQuery(tieBreakerMultiplier); + DisjunctionMaxQuery disMaxQuery = new DisjunctionMaxQuery(tieBreakerMultiplier); for (int i = 0; i < terms.length; i++) { - TermQuery termQuery = new TermQuery(terms[i], ctx[i]); - if (boosts != null) { - termQuery.setBoost(boosts[i]); + Query query = new TermQuery(terms[i], ctx[i]); + if (boosts != null && boosts[i] != 1f) { + query = new BoostQuery(query, boosts[i]); } - query.add(termQuery); + disMaxQuery.add(query); } - return query; + return disMaxQuery; } }; } diff --git a/core/src/main/java/org/apache/lucene/queries/MinDocQuery.java b/core/src/main/java/org/apache/lucene/queries/MinDocQuery.java index 1e9ecf7ae6f..86982bfc949 100644 --- a/core/src/main/java/org/apache/lucene/queries/MinDocQuery.java +++ b/core/src/main/java/org/apache/lucene/queries/MinDocQuery.java @@ -29,6 +29,7 @@ import org.apache.lucene.search.Scorer; import org.apache.lucene.search.Weight; import java.io.IOException; +import java.util.Objects; /** A {@link Query} that only matches documents that are greater than or equal * to a configured doc ID. */ @@ -43,7 +44,7 @@ public final class MinDocQuery extends Query { @Override public int hashCode() { - return 31 * super.hashCode() + minDoc; + return Objects.hash(super.hashCode(), minDoc); } @Override diff --git a/core/src/main/java/org/apache/lucene/queryparser/classic/MapperQueryParser.java b/core/src/main/java/org/apache/lucene/queryparser/classic/MapperQueryParser.java index 3ef6e5a2c6d..fce58d2f88f 100644 --- a/core/src/main/java/org/apache/lucene/queryparser/classic/MapperQueryParser.java +++ b/core/src/main/java/org/apache/lucene/queryparser/classic/MapperQueryParser.java @@ -23,13 +23,7 @@ import org.apache.lucene.analysis.Analyzer; import org.apache.lucene.analysis.TokenStream; import org.apache.lucene.analysis.tokenattributes.CharTermAttribute; import org.apache.lucene.index.Term; -import org.apache.lucene.search.BooleanClause; -import org.apache.lucene.search.DisjunctionMaxQuery; -import org.apache.lucene.search.FuzzyQuery; -import org.apache.lucene.search.MatchNoDocsQuery; -import org.apache.lucene.search.MultiPhraseQuery; -import org.apache.lucene.search.PhraseQuery; -import org.apache.lucene.search.Query; +import org.apache.lucene.search.*; import org.apache.lucene.util.IOUtils; import org.apache.lucene.util.automaton.RegExp; import org.elasticsearch.common.lucene.search.Queries; @@ -41,12 +35,7 @@ import org.elasticsearch.index.query.QueryShardContext; import org.elasticsearch.index.query.support.QueryParsers; import java.io.IOException; -import java.util.ArrayList; -import java.util.Collection; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Objects; +import java.util.*; import static java.util.Collections.unmodifiableMap; import static org.elasticsearch.common.lucene.search.Queries.fixNegativeQueryIfNeeded; @@ -148,8 +137,7 @@ public class MapperQueryParser extends QueryParser { Query q = getFieldQuerySingle(mField, queryText, quoted); if (q != null) { added = true; - applyBoost(mField, q); - disMaxQuery.add(q); + disMaxQuery.add(applyBoost(mField, q)); } } if (!added) { @@ -161,8 +149,7 @@ public class MapperQueryParser extends QueryParser { for (String mField : fields) { Query q = getFieldQuerySingle(mField, queryText, quoted); if (q != null) { - applyBoost(mField, q); - clauses.add(new BooleanClause(q, BooleanClause.Occur.SHOULD)); + clauses.add(new BooleanClause(applyBoost(mField, q), BooleanClause.Occur.SHOULD)); } } if (clauses.size() == 0) // happens for stopwords @@ -250,9 +237,8 @@ public class MapperQueryParser extends QueryParser { Query q = super.getFieldQuery(mField, queryText, slop); if (q != null) { added = true; - applyBoost(mField, q); q = applySlop(q, slop); - disMaxQuery.add(q); + disMaxQuery.add(applyBoost(mField, q)); } } if (!added) { @@ -264,9 +250,8 @@ public class MapperQueryParser extends QueryParser { for (String mField : fields) { Query q = super.getFieldQuery(mField, queryText, slop); if (q != null) { - applyBoost(mField, q); q = applySlop(q, slop); - clauses.add(new BooleanClause(q, BooleanClause.Occur.SHOULD)); + clauses.add(new BooleanClause(applyBoost(mField, q), BooleanClause.Occur.SHOULD)); } } if (clauses.size() == 0) // happens for stopwords @@ -305,8 +290,7 @@ public class MapperQueryParser extends QueryParser { Query q = getRangeQuerySingle(mField, part1, part2, startInclusive, endInclusive); if (q != null) { added = true; - applyBoost(mField, q); - disMaxQuery.add(q); + disMaxQuery.add(applyBoost(mField, q)); } } if (!added) { @@ -318,8 +302,7 @@ public class MapperQueryParser extends QueryParser { for (String mField : fields) { Query q = getRangeQuerySingle(mField, part1, part2, startInclusive, endInclusive); if (q != null) { - applyBoost(mField, q); - clauses.add(new BooleanClause(q, BooleanClause.Occur.SHOULD)); + clauses.add(new BooleanClause(applyBoost(mField, q), BooleanClause.Occur.SHOULD)); } } if (clauses.size() == 0) // happens for stopwords @@ -371,8 +354,7 @@ public class MapperQueryParser extends QueryParser { Query q = getFuzzyQuerySingle(mField, termStr, minSimilarity); if (q != null) { added = true; - applyBoost(mField, q); - disMaxQuery.add(q); + disMaxQuery.add(applyBoost(mField, q)); } } if (!added) { @@ -383,8 +365,9 @@ public class MapperQueryParser extends QueryParser { List clauses = new ArrayList<>(); for (String mField : fields) { Query q = getFuzzyQuerySingle(mField, termStr, minSimilarity); - applyBoost(mField, q); - clauses.add(new BooleanClause(q, BooleanClause.Occur.SHOULD)); + if (q != null) { + clauses.add(new BooleanClause(applyBoost(mField, q), BooleanClause.Occur.SHOULD)); + } } return getBooleanQuery(clauses, true); } @@ -434,8 +417,7 @@ public class MapperQueryParser extends QueryParser { Query q = getPrefixQuerySingle(mField, termStr); if (q != null) { added = true; - applyBoost(mField, q); - disMaxQuery.add(q); + disMaxQuery.add(applyBoost(mField, q)); } } if (!added) { @@ -447,8 +429,7 @@ public class MapperQueryParser extends QueryParser { for (String mField : fields) { Query q = getPrefixQuerySingle(mField, termStr); if (q != null) { - applyBoost(mField, q); - clauses.add(new BooleanClause(q, BooleanClause.Occur.SHOULD)); + clauses.add(new BooleanClause(applyBoost(mField, q), BooleanClause.Occur.SHOULD)); } } if (clauses.size() == 0) // happens for stopwords @@ -566,8 +547,7 @@ public class MapperQueryParser extends QueryParser { Query q = getWildcardQuerySingle(mField, termStr); if (q != null) { added = true; - applyBoost(mField, q); - disMaxQuery.add(q); + disMaxQuery.add(applyBoost(mField, q)); } } if (!added) { @@ -579,8 +559,7 @@ public class MapperQueryParser extends QueryParser { for (String mField : fields) { Query q = getWildcardQuerySingle(mField, termStr); if (q != null) { - applyBoost(mField, q); - clauses.add(new BooleanClause(q, BooleanClause.Occur.SHOULD)); + clauses.add(new BooleanClause(applyBoost(mField, q), BooleanClause.Occur.SHOULD)); } } if (clauses.size() == 0) // happens for stopwords @@ -697,8 +676,7 @@ public class MapperQueryParser extends QueryParser { Query q = getRegexpQuerySingle(mField, termStr); if (q != null) { added = true; - applyBoost(mField, q); - disMaxQuery.add(q); + disMaxQuery.add(applyBoost(mField, q)); } } if (!added) { @@ -710,8 +688,7 @@ public class MapperQueryParser extends QueryParser { for (String mField : fields) { Query q = getRegexpQuerySingle(mField, termStr); if (q != null) { - applyBoost(mField, q); - clauses.add(new BooleanClause(q, BooleanClause.Occur.SHOULD)); + clauses.add(new BooleanClause(applyBoost(mField, q), BooleanClause.Occur.SHOULD)); } } if (clauses.size() == 0) // happens for stopwords @@ -761,11 +738,12 @@ public class MapperQueryParser extends QueryParser { return fixNegativeQueryIfNeeded(q); } - private void applyBoost(String field, Query q) { + private Query applyBoost(String field, Query q) { Float fieldBoost = settings.fieldsAndWeights().get(field); - if (fieldBoost != null) { - q.setBoost(fieldBoost); + if (fieldBoost != null && fieldBoost != 1f) { + return new BoostQuery(q, fieldBoost); } + return q; } private Query applySlop(Query q, int slop) { @@ -779,7 +757,9 @@ public class MapperQueryParser extends QueryParser { builder.add(terms[i], positions[i]); } pq = builder.build(); - pq.setBoost(q.getBoost()); + //make sure that the boost hasn't been set beforehand, otherwise we'd lose it + assert q.getBoost() == 1f; + assert q instanceof BoostQuery == false; return pq; } else if (q instanceof MultiPhraseQuery) { ((MultiPhraseQuery) q).setSlop(slop); diff --git a/core/src/main/java/org/apache/lucene/search/vectorhighlight/CustomFieldQuery.java b/core/src/main/java/org/apache/lucene/search/vectorhighlight/CustomFieldQuery.java index 97b5b716179..95657de5158 100644 --- a/core/src/main/java/org/apache/lucene/search/vectorhighlight/CustomFieldQuery.java +++ b/core/src/main/java/org/apache/lucene/search/vectorhighlight/CustomFieldQuery.java @@ -22,11 +22,7 @@ package org.apache.lucene.search.vectorhighlight; import org.apache.lucene.index.IndexReader; import org.apache.lucene.index.Term; import org.apache.lucene.queries.BlendedTermQuery; -import org.apache.lucene.search.ConstantScoreQuery; -import org.apache.lucene.search.MultiPhraseQuery; -import org.apache.lucene.search.PhraseQuery; -import org.apache.lucene.search.Query; -import org.apache.lucene.search.TermQuery; +import org.apache.lucene.search.*; import org.apache.lucene.search.spans.SpanTermQuery; import org.elasticsearch.common.lucene.search.MultiPhrasePrefixQuery; import org.elasticsearch.common.lucene.search.function.FiltersFunctionScoreQuery; @@ -103,8 +99,7 @@ public class CustomFieldQuery extends FieldQuery { for (int i = 0; i < termsIdx.length; i++) { queryBuilder.add(terms.get(i)[termsIdx[i]], pos[i]); } - PhraseQuery query = queryBuilder.build(); - query.setBoost(orig.getBoost()); + Query query = queryBuilder.build(); this.flatten(query, reader, flatQueries, orig.getBoost()); } else { Term[] t = terms.get(currentPos); diff --git a/core/src/main/java/org/elasticsearch/common/lucene/all/AllTermQuery.java b/core/src/main/java/org/elasticsearch/common/lucene/all/AllTermQuery.java index a59af2c7f51..7191c96e33e 100644 --- a/core/src/main/java/org/elasticsearch/common/lucene/all/AllTermQuery.java +++ b/core/src/main/java/org/elasticsearch/common/lucene/all/AllTermQuery.java @@ -64,8 +64,9 @@ public final class AllTermQuery extends Query { @Override public Query rewrite(IndexReader reader) throws IOException { - if (getBoost() != 1f) { - return super.rewrite(reader); + Query rewritten = super.rewrite(reader); + if (rewritten != this) { + return rewritten; } boolean fieldExists = false; boolean hasPayloads = false; @@ -80,14 +81,10 @@ public final class AllTermQuery extends Query { } } if (fieldExists == false) { - Query rewritten = new MatchNoDocsQuery(); - rewritten.setBoost(getBoost()); - return rewritten; + return new MatchNoDocsQuery(); } if (hasPayloads == false) { - TermQuery rewritten = new TermQuery(term); - rewritten.setBoost(getBoost()); - return rewritten; + return new TermQuery(term); } return this; } diff --git a/core/src/main/java/org/elasticsearch/common/lucene/search/MoreLikeThisQuery.java b/core/src/main/java/org/elasticsearch/common/lucene/search/MoreLikeThisQuery.java index 410796497d5..8b1dcd9dfcf 100644 --- a/core/src/main/java/org/elasticsearch/common/lucene/search/MoreLikeThisQuery.java +++ b/core/src/main/java/org/elasticsearch/common/lucene/search/MoreLikeThisQuery.java @@ -35,10 +35,7 @@ import org.elasticsearch.common.io.FastStringReader; import java.io.IOException; import java.io.Reader; -import java.util.Arrays; -import java.util.HashSet; -import java.util.List; -import java.util.Set; +import java.util.*; /** * @@ -79,29 +76,17 @@ public class MoreLikeThisQuery extends Query { @Override public int hashCode() { - int result = boostTerms ? 1 : 0; - result = 31 * result + Float.floatToIntBits(boostTermsFactor); - result = 31 * result + Arrays.hashCode(likeText); - result = 31 * result + maxDocFreq; - result = 31 * result + maxQueryTerms; - result = 31 * result + maxWordLen; - result = 31 * result + minDocFreq; - result = 31 * result + minTermFrequency; - result = 31 * result + minWordLen; - result = 31 * result + Arrays.hashCode(moreLikeFields); - result = 31 * result + minimumShouldMatch.hashCode(); - result = 31 * result + (stopWords == null ? 0 : stopWords.hashCode()); - result = 31 * result + Float.floatToIntBits(getBoost()); - return result; + return Objects.hash(super.hashCode(), boostTerms, boostTermsFactor, Arrays.hashCode(likeText), + maxDocFreq, maxQueryTerms, maxWordLen, minDocFreq, minTermFrequency, minWordLen, + Arrays.hashCode(moreLikeFields), minimumShouldMatch, stopWords); } @Override public boolean equals(Object obj) { - if (obj == null || getClass() != obj.getClass()) + if (super.equals(obj) == false) { return false; + } MoreLikeThisQuery other = (MoreLikeThisQuery) obj; - if (getBoost() != other.getBoost()) - return false; if (!analyzer.equals(other.analyzer)) return false; if (boostTerms != other.boostTerms) @@ -141,6 +126,10 @@ public class MoreLikeThisQuery extends Query { @Override public Query rewrite(IndexReader reader) throws IOException { + Query rewritten = super.rewrite(reader); + if (rewritten != this) { + return rewritten; + } XMoreLikeThis mlt = new XMoreLikeThis(reader, similarity == null ? new DefaultSimilarity() : similarity); mlt.setFieldNames(moreLikeFields); @@ -179,10 +168,7 @@ public class MoreLikeThisQuery extends Query { mltQuery = Queries.applyMinimumShouldMatch((BooleanQuery) mltQuery, minimumShouldMatch); bqBuilder.add(mltQuery, BooleanClause.Occur.SHOULD); } - - BooleanQuery bq = bqBuilder.build(); - bq.setBoost(getBoost()); - return bq; + return bqBuilder.build(); } private void handleUnlike(XMoreLikeThis mlt, String[] unlikeText, Fields[] unlikeFields) throws IOException { diff --git a/core/src/main/java/org/elasticsearch/common/lucene/search/MultiPhrasePrefixQuery.java b/core/src/main/java/org/elasticsearch/common/lucene/search/MultiPhrasePrefixQuery.java index 3d870bc0794..662c3294151 100644 --- a/core/src/main/java/org/elasticsearch/common/lucene/search/MultiPhrasePrefixQuery.java +++ b/core/src/main/java/org/elasticsearch/common/lucene/search/MultiPhrasePrefixQuery.java @@ -120,8 +120,9 @@ public class MultiPhrasePrefixQuery extends Query { @Override public Query rewrite(IndexReader reader) throws IOException { - if (getBoost() != 1.0F) { - return super.rewrite(reader); + Query rewritten = super.rewrite(reader); + if (rewritten != this) { + return rewritten; } if (termArrays.isEmpty()) { return new MatchNoDocsQuery(); @@ -145,7 +146,6 @@ public class MultiPhrasePrefixQuery extends Query { return Queries.newMatchNoDocsQuery(); } query.add(terms.toArray(Term.class), position); - query.setBoost(getBoost()); return query.rewrite(reader); } @@ -233,10 +233,11 @@ public class MultiPhrasePrefixQuery extends Query { */ @Override public boolean equals(Object o) { - if (!(o instanceof MultiPhrasePrefixQuery)) return false; + if (super.equals(o) == false) { + return false; + } MultiPhrasePrefixQuery other = (MultiPhrasePrefixQuery) o; - return this.getBoost() == other.getBoost() - && this.slop == other.slop + return this.slop == other.slop && termArraysEquals(this.termArrays, other.termArrays) && this.positions.equals(other.positions); } @@ -246,11 +247,10 @@ public class MultiPhrasePrefixQuery extends Query { */ @Override public int hashCode() { - return Float.floatToIntBits(getBoost()) + return super.hashCode() ^ slop ^ termArraysHashCode() - ^ positions.hashCode() - ^ 0x4AC65113; + ^ positions.hashCode(); } // Breakout calculation of the termArrays hashcode diff --git a/core/src/main/java/org/elasticsearch/common/lucene/search/Queries.java b/core/src/main/java/org/elasticsearch/common/lucene/search/Queries.java index ad1f97eb9cb..b7f534d2124 100644 --- a/core/src/main/java/org/elasticsearch/common/lucene/search/Queries.java +++ b/core/src/main/java/org/elasticsearch/common/lucene/search/Queries.java @@ -70,7 +70,7 @@ public class Queries { .build(); } - public static boolean isNegativeQuery(Query q) { + private static boolean isNegativeQuery(Query q) { if (!(q instanceof BooleanQuery)) { return false; } @@ -107,7 +107,7 @@ public class Queries { return false; } - public static BooleanQuery applyMinimumShouldMatch(BooleanQuery query, @Nullable String minimumShouldMatch) { + public static Query applyMinimumShouldMatch(BooleanQuery query, @Nullable String minimumShouldMatch) { if (minimumShouldMatch == null) { return query; } @@ -127,10 +127,13 @@ public class Queries { } builder.setMinimumNumberShouldMatch(msm); BooleanQuery bq = builder.build(); - bq.setBoost(query.getBoost()); - query = bq; + if (query.getBoost() != 1f) { + return new BoostQuery(bq, query.getBoost()); + } + return bq; + } else { + return query; } - return query; } private static Pattern spaceAroundLessThanPattern = Pattern.compile("(\\s+<\\s*)|(\\s*<\\s+)"); diff --git a/core/src/main/java/org/elasticsearch/common/lucene/search/XMoreLikeThis.java b/core/src/main/java/org/elasticsearch/common/lucene/search/XMoreLikeThis.java index 85e1899582c..53159660089 100644 --- a/core/src/main/java/org/elasticsearch/common/lucene/search/XMoreLikeThis.java +++ b/core/src/main/java/org/elasticsearch/common/lucene/search/XMoreLikeThis.java @@ -670,14 +670,14 @@ public final class XMoreLikeThis { float bestScore = -1; while ((scoreTerm = q.pop()) != null) { - TermQuery tq = new TermQuery(new Term(scoreTerm.topField, scoreTerm.word)); + Query tq = new TermQuery(new Term(scoreTerm.topField, scoreTerm.word)); if (boost) { if (bestScore == -1) { bestScore = (scoreTerm.score); } float myScore = (scoreTerm.score); - tq.setBoost(boostFactor * myScore / bestScore); + tq = new BoostQuery(tq, boostFactor * myScore / bestScore); } try { diff --git a/core/src/main/java/org/elasticsearch/common/lucene/search/function/FiltersFunctionScoreQuery.java b/core/src/main/java/org/elasticsearch/common/lucene/search/function/FiltersFunctionScoreQuery.java index 210b32d5e42..3da5ae0e4ab 100644 --- a/core/src/main/java/org/elasticsearch/common/lucene/search/function/FiltersFunctionScoreQuery.java +++ b/core/src/main/java/org/elasticsearch/common/lucene/search/function/FiltersFunctionScoreQuery.java @@ -123,8 +123,9 @@ public class FiltersFunctionScoreQuery extends Query { @Override public Query rewrite(IndexReader reader) throws IOException { - if (getBoost() != 1.0F) { - return super.rewrite(reader); + Query rewritten = super.rewrite(reader); + if (rewritten != this) { + return rewritten; } Query newQ = subQuery.rewrite(reader); if (newQ == subQuery) diff --git a/core/src/main/java/org/elasticsearch/common/lucene/search/function/FunctionScoreQuery.java b/core/src/main/java/org/elasticsearch/common/lucene/search/function/FunctionScoreQuery.java index 907d66957ac..972fb794fb5 100644 --- a/core/src/main/java/org/elasticsearch/common/lucene/search/function/FunctionScoreQuery.java +++ b/core/src/main/java/org/elasticsearch/common/lucene/search/function/FunctionScoreQuery.java @@ -71,8 +71,9 @@ public class FunctionScoreQuery extends Query { @Override public Query rewrite(IndexReader reader) throws IOException { - if (getBoost() != 1.0F) { - return super.rewrite(reader); + Query rewritten = super.rewrite(reader); + if (rewritten != this) { + return rewritten; } Query newQ = subQuery.rewrite(reader); if (newQ == subQuery) { diff --git a/core/src/main/java/org/elasticsearch/index/mapper/core/DateFieldMapper.java b/core/src/main/java/org/elasticsearch/index/mapper/core/DateFieldMapper.java index 686cfcfe6e2..27b96b27a44 100644 --- a/core/src/main/java/org/elasticsearch/index/mapper/core/DateFieldMapper.java +++ b/core/src/main/java/org/elasticsearch/index/mapper/core/DateFieldMapper.java @@ -219,8 +219,9 @@ public class DateFieldMapper extends NumberFieldMapper { @Override public Query rewrite(IndexReader reader) throws IOException { - if (getBoost() != 1.0F) { - return super.rewrite(reader); + Query rewritten = super.rewrite(reader); + if (rewritten != this) { + return rewritten; } return innerRangeQuery(lowerTerm, upperTerm, includeLower, includeUpper, timeZone, forcedDateParser); } @@ -229,11 +230,9 @@ public class DateFieldMapper extends NumberFieldMapper { @Override public boolean equals(Object o) { if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; if (!super.equals(o)) return false; LateParsingQuery that = (LateParsingQuery) o; - if (includeLower != that.includeLower) return false; if (includeUpper != that.includeUpper) return false; if (lowerTerm != null ? !lowerTerm.equals(that.lowerTerm) : that.lowerTerm != null) return false; @@ -245,13 +244,7 @@ public class DateFieldMapper extends NumberFieldMapper { @Override public int hashCode() { - int result = super.hashCode(); - result = 31 * result + (lowerTerm != null ? lowerTerm.hashCode() : 0); - result = 31 * result + (upperTerm != null ? upperTerm.hashCode() : 0); - result = 31 * result + (includeLower ? 1 : 0); - result = 31 * result + (includeUpper ? 1 : 0); - result = 31 * result + (timeZone != null ? timeZone.hashCode() : 0); - return result; + return Objects.hash(super.hashCode(), lowerTerm, upperTerm, includeLower, includeUpper, timeZone); } @Override diff --git a/core/src/main/java/org/elasticsearch/index/query/AbstractQueryBuilder.java b/core/src/main/java/org/elasticsearch/index/query/AbstractQueryBuilder.java index 560476a69d8..1f92f53ec07 100644 --- a/core/src/main/java/org/elasticsearch/index/query/AbstractQueryBuilder.java +++ b/core/src/main/java/org/elasticsearch/index/query/AbstractQueryBuilder.java @@ -19,7 +19,10 @@ package org.elasticsearch.index.query; +import org.apache.lucene.search.BoostQuery; import org.apache.lucene.search.Query; +import org.apache.lucene.search.spans.SpanBoostQuery; +import org.apache.lucene.search.spans.SpanQuery; import org.apache.lucene.util.BytesRef; import org.elasticsearch.action.support.ToXContentToBytes; import org.elasticsearch.common.ParseField; @@ -74,7 +77,13 @@ public abstract class AbstractQueryBuilder exte public final Query toQuery(QueryShardContext context) throws IOException { Query query = doToQuery(context); if (query != null) { - setFinalBoost(query); + if (boost != DEFAULT_BOOST) { + if (query instanceof SpanQuery) { + query = new SpanBoostQuery((SpanQuery) query, boost); + } else { + query = new BoostQuery(query, boost); + } + } if (queryName != null) { context.addNamedQuery(queryName, query); } @@ -82,20 +91,6 @@ public abstract class AbstractQueryBuilder exte return query; } - /** - * Sets the main boost to the query obtained by converting the current query into a lucene query. - * The default behaviour is to set the main boost, after verifying that we are not overriding any non default boost - * value that was previously set to the lucene query. That case would require some manual decision on how to combine - * the main boost with the boost coming from lucene by overriding this method. - * @throws IllegalStateException if the lucene query boost has already been set - */ - protected void setFinalBoost(Query query) { - if (query.getBoost() != AbstractQueryBuilder.DEFAULT_BOOST) { - throw new IllegalStateException("lucene query boost is already set, override setFinalBoost to define how to combine lucene boost with main boost"); - } - query.setBoost(boost); - } - @Override public final Query toFilter(QueryShardContext context) throws IOException { Query result = null; diff --git a/core/src/main/java/org/elasticsearch/index/query/BoolQueryBuilder.java b/core/src/main/java/org/elasticsearch/index/query/BoolQueryBuilder.java index 43b23778d56..9ed1623f66a 100644 --- a/core/src/main/java/org/elasticsearch/index/query/BoolQueryBuilder.java +++ b/core/src/main/java/org/elasticsearch/index/query/BoolQueryBuilder.java @@ -279,8 +279,8 @@ public class BoolQueryBuilder extends AbstractQueryBuilder { } else { minimumShouldMatch = this.minimumShouldMatch; } - booleanQuery = Queries.applyMinimumShouldMatch(booleanQuery, minimumShouldMatch); - return adjustPureNegative ? fixNegativeQueryIfNeeded(booleanQuery) : booleanQuery; + Query query = Queries.applyMinimumShouldMatch(booleanQuery, minimumShouldMatch); + return adjustPureNegative ? fixNegativeQueryIfNeeded(query) : query; } private static void addBooleanClauses(QueryShardContext context, BooleanQuery.Builder booleanQueryBuilder, List clauses, Occur occurs) throws IOException { diff --git a/core/src/main/java/org/elasticsearch/index/query/HasChildQueryBuilder.java b/core/src/main/java/org/elasticsearch/index/query/HasChildQueryBuilder.java index f07fba8234b..a78cb6c4fd7 100644 --- a/core/src/main/java/org/elasticsearch/index/query/HasChildQueryBuilder.java +++ b/core/src/main/java/org/elasticsearch/index/query/HasChildQueryBuilder.java @@ -215,8 +215,6 @@ public class HasChildQueryBuilder extends AbstractQueryBuilder { } /** - * Get the setting for handling zero terms queries. - * @see #zeroTermsQuery(ZeroTermsQuery) + * Returns the setting for handling zero terms queries. */ public MatchQuery.ZeroTermsQuery zeroTermsQuery() { return this.zeroTermsQuery; diff --git a/core/src/main/java/org/elasticsearch/index/query/MultiMatchQueryBuilder.java b/core/src/main/java/org/elasticsearch/index/query/MultiMatchQueryBuilder.java index 57e00a2dc2b..be05333a8eb 100644 --- a/core/src/main/java/org/elasticsearch/index/query/MultiMatchQueryBuilder.java +++ b/core/src/main/java/org/elasticsearch/index/query/MultiMatchQueryBuilder.java @@ -548,12 +548,6 @@ public class MultiMatchQueryBuilder extends AbstractQueryBuilder handleFieldsMatchPattern(MapperService mapperService, Map fieldsBoosts) { Map newFieldsBoosts = new TreeMap<>(); for (Map.Entry fieldBoost : fieldsBoosts.entrySet()) { diff --git a/core/src/main/java/org/elasticsearch/index/query/QueryStringQueryBuilder.java b/core/src/main/java/org/elasticsearch/index/query/QueryStringQueryBuilder.java index d0aa8dff9a0..393ff1e59a7 100644 --- a/core/src/main/java/org/elasticsearch/index/query/QueryStringQueryBuilder.java +++ b/core/src/main/java/org/elasticsearch/index/query/QueryStringQueryBuilder.java @@ -22,6 +22,7 @@ package org.elasticsearch.index.query; import org.apache.lucene.queryparser.classic.MapperQueryParser; import org.apache.lucene.queryparser.classic.QueryParserSettings; import org.apache.lucene.search.BooleanQuery; +import org.apache.lucene.search.BoostQuery; import org.apache.lucene.search.FuzzyQuery; import org.apache.lucene.search.Query; import org.apache.lucene.util.automaton.Operations; @@ -36,10 +37,7 @@ import org.elasticsearch.index.query.support.QueryParsers; import org.joda.time.DateTimeZone; import java.io.IOException; -import java.util.Locale; -import java.util.Map; -import java.util.Objects; -import java.util.TreeMap; +import java.util.*; /** * A query that parses a query string and runs it. There are two modes that this operates. The first, @@ -722,16 +720,25 @@ public class QueryStringQueryBuilder extends AbstractQueryBuilder boosts = new ArrayList<>(); + while(query instanceof BoostQuery) { + BoostQuery boostQuery = (BoostQuery) query; + boosts.add(boostQuery.getBoost()); + query = boostQuery.getQuery(); + } + query = Queries.fixNegativeQueryIfNeeded(query); if (query instanceof BooleanQuery) { query = Queries.applyMinimumShouldMatch((BooleanQuery) query, this.minimumShouldMatch()); } + + //restore the previous BoostQuery wrapping + for (int i = boosts.size() - 1; i >= 0; i--) { + query = new BoostQuery(query, boosts.get(i)); + } + return query; } - - @Override - protected void setFinalBoost(Query query) { - //we need to preserve the boost that came out of the parsing phase - query.setBoost(query.getBoost() * boost); - } } diff --git a/core/src/main/java/org/elasticsearch/index/query/ScriptQueryBuilder.java b/core/src/main/java/org/elasticsearch/index/query/ScriptQueryBuilder.java index 10407a2ba5f..f69ac8c0548 100644 --- a/core/src/main/java/org/elasticsearch/index/query/ScriptQueryBuilder.java +++ b/core/src/main/java/org/elasticsearch/index/query/ScriptQueryBuilder.java @@ -104,10 +104,7 @@ public class ScriptQueryBuilder extends AbstractQueryBuilder @Override public int hashCode() { - final int prime = 31; - int result = super.hashCode(); - result = prime * result + Objects.hashCode(script); - return result; + return Objects.hash(super.hashCode(), script); } @Override diff --git a/core/src/main/java/org/elasticsearch/index/query/SimpleQueryParser.java b/core/src/main/java/org/elasticsearch/index/query/SimpleQueryParser.java index f8b0deaf9be..7627644e750 100644 --- a/core/src/main/java/org/elasticsearch/index/query/SimpleQueryParser.java +++ b/core/src/main/java/org/elasticsearch/index/query/SimpleQueryParser.java @@ -63,8 +63,7 @@ public class SimpleQueryParser extends org.apache.lucene.queryparser.simple.Simp try { Query q = createBooleanQuery(entry.getKey(), text, super.getDefaultOperator()); if (q != null) { - q.setBoost(entry.getValue()); - bq.add(q, BooleanClause.Occur.SHOULD); + bq.add(wrapWithBoost(q, entry.getValue()), BooleanClause.Occur.SHOULD); } } catch (RuntimeException e) { rethrowUnlessLenient(e); @@ -86,9 +85,8 @@ public class SimpleQueryParser extends org.apache.lucene.queryparser.simple.Simp bq.setDisableCoord(true); for (Map.Entry entry : weights.entrySet()) { try { - Query q = new FuzzyQuery(new Term(entry.getKey(), text), fuzziness); - q.setBoost(entry.getValue()); - bq.add(q, BooleanClause.Occur.SHOULD); + Query query = new FuzzyQuery(new Term(entry.getKey(), text), fuzziness); + bq.add(wrapWithBoost(query, entry.getValue()), BooleanClause.Occur.SHOULD); } catch (RuntimeException e) { rethrowUnlessLenient(e); } @@ -104,8 +102,7 @@ public class SimpleQueryParser extends org.apache.lucene.queryparser.simple.Simp try { Query q = createPhraseQuery(entry.getKey(), text, slop); if (q != null) { - q.setBoost(entry.getValue()); - bq.add(q, BooleanClause.Occur.SHOULD); + bq.add(wrapWithBoost(q, entry.getValue()), BooleanClause.Occur.SHOULD); } } catch (RuntimeException e) { rethrowUnlessLenient(e); @@ -129,12 +126,12 @@ public class SimpleQueryParser extends org.apache.lucene.queryparser.simple.Simp try { if (settings.analyzeWildcard()) { Query analyzedQuery = newPossiblyAnalyzedQuery(entry.getKey(), text); - analyzedQuery.setBoost(entry.getValue()); - bq.add(analyzedQuery, BooleanClause.Occur.SHOULD); + if (analyzedQuery != null) { + bq.add(wrapWithBoost(analyzedQuery, entry.getValue()), BooleanClause.Occur.SHOULD); + } } else { - PrefixQuery prefix = new PrefixQuery(new Term(entry.getKey(), text)); - prefix.setBoost(entry.getValue()); - bq.add(prefix, BooleanClause.Occur.SHOULD); + Query query = new PrefixQuery(new Term(entry.getKey(), text)); + bq.add(wrapWithBoost(query, entry.getValue()), BooleanClause.Occur.SHOULD); } } catch (RuntimeException e) { return rethrowUnlessLenient(e); @@ -143,6 +140,13 @@ public class SimpleQueryParser extends org.apache.lucene.queryparser.simple.Simp return super.simplify(bq.build()); } + private static Query wrapWithBoost(Query query, float boost) { + if (boost != AbstractQueryBuilder.DEFAULT_BOOST) { + return new BoostQuery(query, boost); + } + return query; + } + /** * Analyze the given string using its analyzer, constructing either a * {@code PrefixQuery} or a {@code BooleanQuery} made up diff --git a/core/src/main/java/org/elasticsearch/index/query/SimpleQueryStringBuilder.java b/core/src/main/java/org/elasticsearch/index/query/SimpleQueryStringBuilder.java index 78d3bffd228..29720195c83 100644 --- a/core/src/main/java/org/elasticsearch/index/query/SimpleQueryStringBuilder.java +++ b/core/src/main/java/org/elasticsearch/index/query/SimpleQueryStringBuilder.java @@ -299,11 +299,6 @@ public class SimpleQueryStringBuilder extends AbstractQueryBuilder((MultiTermQuery) subQuery); + SpanQuery wrapper = new SpanMultiTermQueryWrapper<>((MultiTermQuery) subQuery); + if (boost != AbstractQueryBuilder.DEFAULT_BOOST) { + wrapper = new SpanBoostQuery(wrapper, boost); + } + return wrapper; } @Override diff --git a/core/src/main/java/org/elasticsearch/index/query/TemplateQueryBuilder.java b/core/src/main/java/org/elasticsearch/index/query/TemplateQueryBuilder.java index 2cecc4e2b0d..59ff19748af 100644 --- a/core/src/main/java/org/elasticsearch/index/query/TemplateQueryBuilder.java +++ b/core/src/main/java/org/elasticsearch/index/query/TemplateQueryBuilder.java @@ -110,11 +110,6 @@ public class TemplateQueryBuilder extends AbstractQueryBuilder()); + groups.put(actualAnalyzer, new ArrayList<>()); } Float boost = entry.getValue(); boost = boost == null ? Float.valueOf(1.0f) : boost; groups.get(actualAnalyzer).add(new FieldAndFieldType(name, fieldType, boost)); } else { - missing.add(new Tuple(name, entry.getValue())); + missing.add(new Tuple<>(name, entry.getValue())); } } diff --git a/core/src/main/java/org/elasticsearch/index/search/geo/InMemoryGeoBoundingBoxQuery.java b/core/src/main/java/org/elasticsearch/index/search/geo/InMemoryGeoBoundingBoxQuery.java index 8d7dba292f4..a2e9e1b689d 100644 --- a/core/src/main/java/org/elasticsearch/index/search/geo/InMemoryGeoBoundingBoxQuery.java +++ b/core/src/main/java/org/elasticsearch/index/search/geo/InMemoryGeoBoundingBoxQuery.java @@ -30,6 +30,7 @@ import org.elasticsearch.index.fielddata.IndexGeoPointFieldData; import org.elasticsearch.index.fielddata.MultiGeoPointValues; import java.io.IOException; +import java.util.Objects; /** * @@ -94,11 +95,7 @@ public class InMemoryGeoBoundingBoxQuery extends Query { @Override public int hashCode() { - int h = super.hashCode(); - h = 31 * h + fieldName().hashCode(); - h = 31 * h + topLeft.hashCode(); - h = 31 * h + bottomRight.hashCode(); - return h; + return Objects.hash(super.hashCode(), fieldName(), topLeft, bottomRight); } private static class Meridian180GeoBoundingBoxBits implements Bits { diff --git a/core/src/main/java/org/elasticsearch/index/search/nested/IncludeNestedDocsQuery.java b/core/src/main/java/org/elasticsearch/index/search/nested/IncludeNestedDocsQuery.java deleted file mode 100644 index e3631269fbe..00000000000 --- a/core/src/main/java/org/elasticsearch/index/search/nested/IncludeNestedDocsQuery.java +++ /dev/null @@ -1,295 +0,0 @@ -/* - * Licensed to Elasticsearch under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch licenses this file to you 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.elasticsearch.index.search.nested; - -import org.apache.lucene.index.IndexReader; -import org.apache.lucene.index.LeafReaderContext; -import org.apache.lucene.index.Term; -import org.apache.lucene.search.DocIdSetIterator; -import org.apache.lucene.search.Explanation; -import org.apache.lucene.search.IndexSearcher; -import org.apache.lucene.search.Query; -import org.apache.lucene.search.Scorer; -import org.apache.lucene.search.Weight; -import org.apache.lucene.search.join.BitSetProducer; -import org.apache.lucene.util.BitSet; - -import java.io.IOException; -import java.util.Collection; -import java.util.Set; - -/** - * A special query that accepts a top level parent matching query, and returns the nested docs of the matching parent - * doc as well. This is handy when deleting by query, don't use it for other purposes. - * - * @elasticsearch.internal - */ -public class IncludeNestedDocsQuery extends Query { - - private final BitSetProducer parentFilter; - private final Query parentQuery; - - // If we are rewritten, this is the original childQuery we - // were passed; we use this for .equals() and - // .hashCode(). This makes rewritten query equal the - // original, so that user does not have to .rewrite() their - // query before searching: - private final Query origParentQuery; - - - public IncludeNestedDocsQuery(Query parentQuery, BitSetProducer parentFilter) { - this.origParentQuery = parentQuery; - this.parentQuery = parentQuery; - this.parentFilter = parentFilter; - } - - // For rewriting - IncludeNestedDocsQuery(Query rewrite, Query originalQuery, IncludeNestedDocsQuery previousInstance) { - this.origParentQuery = originalQuery; - this.parentQuery = rewrite; - this.parentFilter = previousInstance.parentFilter; - setBoost(previousInstance.getBoost()); - } - - // For cloning - IncludeNestedDocsQuery(Query originalQuery, IncludeNestedDocsQuery previousInstance) { - this.origParentQuery = originalQuery; - this.parentQuery = originalQuery; - this.parentFilter = previousInstance.parentFilter; - } - - @Override - public Weight createWeight(IndexSearcher searcher, boolean needsScores) throws IOException { - return new IncludeNestedDocsWeight(this, parentQuery, parentQuery.createWeight(searcher, needsScores), parentFilter); - } - - static class IncludeNestedDocsWeight extends Weight { - - private final Query parentQuery; - private final Weight parentWeight; - private final BitSetProducer parentsFilter; - - IncludeNestedDocsWeight(Query query, Query parentQuery, Weight parentWeight, BitSetProducer parentsFilter) { - super(query); - this.parentQuery = parentQuery; - this.parentWeight = parentWeight; - this.parentsFilter = parentsFilter; - } - - @Override - public void extractTerms(Set terms) { - parentWeight.extractTerms(terms); - } - - @Override - public void normalize(float norm, float topLevelBoost) { - parentWeight.normalize(norm, topLevelBoost); - } - - @Override - public float getValueForNormalization() throws IOException { - return parentWeight.getValueForNormalization(); // this query is never boosted so just delegate... - } - - @Override - public Scorer scorer(LeafReaderContext context) throws IOException { - final Scorer parentScorer = parentWeight.scorer(context); - - // no matches - if (parentScorer == null) { - return null; - } - - BitSet parents = parentsFilter.getBitSet(context); - if (parents == null) { - // No matches - return null; - } - - int firstParentDoc = parentScorer.nextDoc(); - if (firstParentDoc == DocIdSetIterator.NO_MORE_DOCS) { - // No matches - return null; - } - return new IncludeNestedDocsScorer(this, parentScorer, parents, firstParentDoc); - } - - @Override - public Explanation explain(LeafReaderContext context, int doc) throws IOException { - return null; //Query is used internally and not by users, so explain can be empty - } - } - - static class IncludeNestedDocsScorer extends Scorer { - - final Scorer parentScorer; - final BitSet parentBits; - - int currentChildPointer = -1; - int currentParentPointer = -1; - int currentDoc = -1; - - IncludeNestedDocsScorer(Weight weight, Scorer parentScorer, BitSet parentBits, int currentParentPointer) { - super(weight); - this.parentScorer = parentScorer; - this.parentBits = parentBits; - this.currentParentPointer = currentParentPointer; - if (currentParentPointer == 0) { - currentChildPointer = 0; - } else { - this.currentChildPointer = this.parentBits.prevSetBit(currentParentPointer - 1); - if (currentChildPointer == -1) { - // no previous set parent, we delete from doc 0 - currentChildPointer = 0; - } else { - currentChildPointer++; // we only care about children - } - } - - currentDoc = currentChildPointer; - } - - @Override - public Collection getChildren() { - return parentScorer.getChildren(); - } - - @Override - public int nextDoc() throws IOException { - if (currentParentPointer == NO_MORE_DOCS) { - return (currentDoc = NO_MORE_DOCS); - } - - if (currentChildPointer == currentParentPointer) { - // we need to return the current parent as well, but prepare to return - // the next set of children - currentDoc = currentParentPointer; - currentParentPointer = parentScorer.nextDoc(); - if (currentParentPointer != NO_MORE_DOCS) { - currentChildPointer = parentBits.prevSetBit(currentParentPointer - 1); - if (currentChildPointer == -1) { - // no previous set parent, just set the child to the current parent - currentChildPointer = currentParentPointer; - } else { - currentChildPointer++; // we only care about children - } - } - } else { - currentDoc = currentChildPointer++; - } - - assert currentDoc != -1; - return currentDoc; - } - - @Override - public int advance(int target) throws IOException { - if (target == NO_MORE_DOCS) { - return (currentDoc = NO_MORE_DOCS); - } - - if (target == 0) { - return nextDoc(); - } - - if (target < currentParentPointer) { - currentDoc = currentParentPointer = parentScorer.advance(target); - if (currentParentPointer == NO_MORE_DOCS) { - return (currentDoc = NO_MORE_DOCS); - } - if (currentParentPointer == 0) { - currentChildPointer = 0; - } else { - currentChildPointer = parentBits.prevSetBit(currentParentPointer - 1); - if (currentChildPointer == -1) { - // no previous set parent, just set the child to 0 to delete all up to the parent - currentChildPointer = 0; - } else { - currentChildPointer++; // we only care about children - } - } - } else { - currentDoc = currentChildPointer++; - } - - return currentDoc; - } - - @Override - public float score() throws IOException { - return parentScorer.score(); - } - - @Override - public int freq() throws IOException { - return parentScorer.freq(); - } - - @Override - public int docID() { - return currentDoc; - } - - @Override - public long cost() { - return parentScorer.cost(); - } - } - - @Override - public Query rewrite(IndexReader reader) throws IOException { - final Query parentRewrite = parentQuery.rewrite(reader); - if (parentRewrite != parentQuery) { - return new IncludeNestedDocsQuery(parentRewrite, parentQuery, this); - } else { - return this; - } - } - - @Override - public String toString(String field) { - return "IncludeNestedDocsQuery (" + parentQuery.toString() + ")"; - } - - @Override - public boolean equals(Object _other) { - if (_other instanceof IncludeNestedDocsQuery) { - final IncludeNestedDocsQuery other = (IncludeNestedDocsQuery) _other; - return origParentQuery.equals(other.origParentQuery) && parentFilter.equals(other.parentFilter); - } else { - return false; - } - } - - @Override - public int hashCode() { - final int prime = 31; - int hash = 1; - hash = prime * hash + origParentQuery.hashCode(); - hash = prime * hash + parentFilter.hashCode(); - return hash; - } - - @Override - public Query clone() { - Query clonedQuery = origParentQuery.clone(); - return new IncludeNestedDocsQuery(clonedQuery, this); - } -} diff --git a/core/src/main/java/org/elasticsearch/search/internal/DefaultSearchContext.java b/core/src/main/java/org/elasticsearch/search/internal/DefaultSearchContext.java index e69ca5a3fce..1174fcdd8a9 100644 --- a/core/src/main/java/org/elasticsearch/search/internal/DefaultSearchContext.java +++ b/core/src/main/java/org/elasticsearch/search/internal/DefaultSearchContext.java @@ -41,6 +41,7 @@ import org.elasticsearch.index.fielddata.IndexFieldDataService; import org.elasticsearch.index.mapper.MappedFieldType; import org.elasticsearch.index.mapper.MapperService; import org.elasticsearch.index.mapper.object.ObjectMapper; +import org.elasticsearch.index.query.AbstractQueryBuilder; import org.elasticsearch.index.query.ParsedQuery; import org.elasticsearch.index.shard.IndexShard; import org.elasticsearch.index.similarity.SimilarityService; @@ -196,14 +197,16 @@ public class DefaultSearchContext extends SearchContext { if (query() == null) { parsedQuery(ParsedQuery.parsedMatchAllQuery()); } - if (queryBoost() != 1.0f) { + if (queryBoost() != AbstractQueryBuilder.DEFAULT_BOOST) { parsedQuery(new ParsedQuery(new FunctionScoreQuery(query(), new WeightFactorFunction(queryBoost)), parsedQuery())); } Query searchFilter = searchFilter(types()); if (searchFilter != null) { if (Queries.isConstantMatchAllQuery(query())) { Query q = new ConstantScoreQuery(searchFilter); - q.setBoost(query().getBoost()); + if (query().getBoost() != AbstractQueryBuilder.DEFAULT_BOOST) { + q = new BoostQuery(q, query().getBoost()); + } parsedQuery(new ParsedQuery(q, parsedQuery())); } else { BooleanQuery filtered = new BooleanQuery.Builder() diff --git a/core/src/test/java/org/elasticsearch/common/lucene/search/MultiPhrasePrefixQueryTests.java b/core/src/test/java/org/elasticsearch/common/lucene/search/MultiPhrasePrefixQueryTests.java index 5cb80607115..9098289847e 100644 --- a/core/src/test/java/org/elasticsearch/common/lucene/search/MultiPhrasePrefixQueryTests.java +++ b/core/src/test/java/org/elasticsearch/common/lucene/search/MultiPhrasePrefixQueryTests.java @@ -27,6 +27,7 @@ import org.apache.lucene.index.IndexReader; import org.apache.lucene.index.IndexWriter; import org.apache.lucene.index.IndexWriterConfig; import org.apache.lucene.index.Term; +import org.apache.lucene.search.BoostQuery; import org.apache.lucene.search.IndexSearcher; import org.apache.lucene.search.Query; import org.apache.lucene.store.RAMDirectory; @@ -34,6 +35,7 @@ import org.elasticsearch.common.lucene.Lucene; import org.elasticsearch.test.ESTestCase; import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.instanceOf; public class MultiPhrasePrefixQueryTests extends ESTestCase { public void testSimple() throws Exception { @@ -78,6 +80,8 @@ public class MultiPhrasePrefixQueryTests extends ESTestCase { multiPhrasePrefixQuery.add(new Term[]{new Term("field", "aaa"), new Term("field", "bb")}); multiPhrasePrefixQuery.setBoost(randomFloat()); Query query = multiPhrasePrefixQuery.rewrite(reader); - assertThat(query.getBoost(), equalTo(multiPhrasePrefixQuery.getBoost())); + assertThat(query, instanceOf(BoostQuery.class)); + BoostQuery boostQuery = (BoostQuery) query; + assertThat(boostQuery.getBoost(), equalTo(multiPhrasePrefixQuery.getBoost())); } } \ No newline at end of file diff --git a/core/src/test/java/org/elasticsearch/index/query/AbstractQueryTestCase.java b/core/src/test/java/org/elasticsearch/index/query/AbstractQueryTestCase.java index f3819964893..3df3b9c3b18 100644 --- a/core/src/test/java/org/elasticsearch/index/query/AbstractQueryTestCase.java +++ b/core/src/test/java/org/elasticsearch/index/query/AbstractQueryTestCase.java @@ -23,8 +23,12 @@ import com.carrotsearch.randomizedtesting.generators.CodepointSetGenerator; import com.fasterxml.jackson.core.JsonParseException; import com.fasterxml.jackson.core.io.JsonStringEncoder; +import org.apache.lucene.search.BoostQuery; import org.apache.lucene.search.Query; import org.apache.lucene.util.Accountable; +import org.apache.lucene.search.TermQuery; +import org.apache.lucene.search.spans.SpanBoostQuery; +import org.elasticsearch.ElasticsearchException; import org.elasticsearch.ElasticsearchParseException; import org.elasticsearch.Version; import org.elasticsearch.action.admin.indices.mapping.put.PutMappingRequest; @@ -104,9 +108,7 @@ import java.lang.reflect.Proxy; import java.util.*; import java.util.concurrent.ExecutionException; -import static org.hamcrest.Matchers.equalTo; -import static org.hamcrest.Matchers.not; -import static org.hamcrest.Matchers.containsString; +import static org.hamcrest.Matchers.*; public abstract class AbstractQueryTestCase> extends ESTestCase { @@ -501,26 +503,45 @@ public abstract class AbstractQueryTestCase> assertThat(namedQuery, equalTo(query)); } if (query != null) { - assertBoost(queryBuilder, query); + if (queryBuilder.boost() != AbstractQueryBuilder.DEFAULT_BOOST) { + assertThat(query, either(instanceOf(BoostQuery.class)).or(instanceOf(SpanBoostQuery.class))); + if (query instanceof SpanBoostQuery) { + SpanBoostQuery spanBoostQuery = (SpanBoostQuery) query; + assertThat(spanBoostQuery.getBoost(), equalTo(queryBuilder.boost())); + query = spanBoostQuery.getQuery(); + } else { + BoostQuery boostQuery = (BoostQuery) query; + assertThat(boostQuery.getBoost(), equalTo(queryBuilder.boost())); + query = boostQuery.getQuery(); + } + } } doAssertLuceneQuery(queryBuilder, query, context); } - /** - * Allows to override boost assertions for queries that don't have the default behaviour - */ - protected void assertBoost(QB queryBuilder, Query query) throws IOException { - // workaround https://bugs.openjdk.java.net/browse/JDK-8056984 - float boost = queryBuilder.boost(); - assertThat(query.getBoost(), equalTo(boost)); - } - /** * Checks the result of {@link QueryBuilder#toQuery(QueryShardContext)} given the original {@link QueryBuilder} and {@link QueryShardContext}. * Contains the query specific checks to be implemented by subclasses. */ protected abstract void doAssertLuceneQuery(QB queryBuilder, Query query, QueryShardContext context) throws IOException; + protected static void assertTermOrBoostQuery(Query query, String field, String value, float fieldBoost) { + if (fieldBoost != AbstractQueryBuilder.DEFAULT_BOOST) { + assertThat(query, instanceOf(BoostQuery.class)); + BoostQuery boostQuery = (BoostQuery) query; + assertThat(boostQuery.getBoost(), equalTo(fieldBoost)); + query = boostQuery.getQuery(); + } + assertTermQuery(query, field, value); + } + + protected static void assertTermQuery(Query query, String field, String value) { + assertThat(query, instanceOf(TermQuery.class)); + TermQuery termQuery = (TermQuery) query; + assertThat(termQuery.getTerm().field(), equalTo(field)); + assertThat(termQuery.getTerm().text().toLowerCase(Locale.ROOT), equalTo(value.toLowerCase(Locale.ROOT))); + } + /** * Test serialization and deserialization of the test query. */ diff --git a/core/src/test/java/org/elasticsearch/index/query/DisMaxQueryBuilderTests.java b/core/src/test/java/org/elasticsearch/index/query/DisMaxQueryBuilderTests.java index 3c6efd49358..4a523f6add7 100644 --- a/core/src/test/java/org/elasticsearch/index/query/DisMaxQueryBuilderTests.java +++ b/core/src/test/java/org/elasticsearch/index/query/DisMaxQueryBuilderTests.java @@ -20,21 +20,16 @@ package org.elasticsearch.index.query; import org.apache.lucene.index.Term; +import org.apache.lucene.search.BoostQuery; import org.apache.lucene.search.DisjunctionMaxQuery; import org.apache.lucene.search.PrefixQuery; import org.apache.lucene.search.Query; import java.io.IOException; -import java.util.Collection; -import java.util.HashMap; -import java.util.Iterator; -import java.util.List; -import java.util.Map; +import java.util.*; import static org.hamcrest.CoreMatchers.nullValue; -import static org.hamcrest.Matchers.closeTo; -import static org.hamcrest.Matchers.equalTo; -import static org.hamcrest.Matchers.instanceOf; +import static org.hamcrest.Matchers.*; public class DisMaxQueryBuilderTests extends AbstractQueryTestCase { /** @@ -138,9 +133,13 @@ public class DisMaxQueryBuilderTests extends AbstractQueryTestCase disjuncts = disjunctionMaxQuery.getDisjuncts(); assertThat(disjuncts.size(), equalTo(1)); - PrefixQuery firstQ = (PrefixQuery) disjuncts.get(0); + assertThat(disjuncts.get(0), instanceOf(BoostQuery.class)); + BoostQuery boostQuery = (BoostQuery) disjuncts.get(0); + assertThat((double) boostQuery.getBoost(), closeTo(1.2, 0.00001)); + assertThat(boostQuery.getQuery(), instanceOf(PrefixQuery.class)); + PrefixQuery firstQ = (PrefixQuery) boostQuery.getQuery(); // since age is automatically registered in data, we encode it as numeric assertThat(firstQ.getPrefix(), equalTo(new Term(STRING_FIELD_NAME, "sh"))); - assertThat((double) firstQ.getBoost(), closeTo(1.2, 0.00001)); + } } diff --git a/core/src/test/java/org/elasticsearch/index/query/FuzzyQueryBuilderTests.java b/core/src/test/java/org/elasticsearch/index/query/FuzzyQueryBuilderTests.java index c7787860c01..f1511bc3909 100644 --- a/core/src/test/java/org/elasticsearch/index/query/FuzzyQueryBuilderTests.java +++ b/core/src/test/java/org/elasticsearch/index/query/FuzzyQueryBuilderTests.java @@ -20,6 +20,7 @@ package org.elasticsearch.index.query; import org.apache.lucene.index.Term; +import org.apache.lucene.search.BoostQuery; import org.apache.lucene.search.FuzzyQuery; import org.apache.lucene.search.NumericRangeQuery; import org.apache.lucene.search.Query; @@ -116,12 +117,15 @@ public class FuzzyQueryBuilderTests extends AbstractQueryTestCase 0); } - Object value = ""; + Object value; if (fieldName.equals(STRING_FIELD_NAME)) { int terms = randomIntBetween(0, 3); StringBuilder builder = new StringBuilder(); for (int i = 0; i < terms; i++) { - builder.append(randomAsciiOfLengthBetween(1, 10) + " "); + builder.append(randomAsciiOfLengthBetween(1, 10)).append(" "); } value = builder.toString().trim(); } else { @@ -139,8 +133,6 @@ public class MatchQueryBuilderTests extends AbstractQueryTestCase 0); QueryShardContext shardContext = createShardContext(); MultiMatchQueryBuilder multiMatchQueryBuilder = new MultiMatchQueryBuilder("test"); - multiMatchQueryBuilder.field(STRING_FIELD_NAME, 5); + multiMatchQueryBuilder.field(STRING_FIELD_NAME, 5f); Query query = multiMatchQueryBuilder.toQuery(shardContext); - assertThat(query, instanceOf(TermQuery.class)); - assertThat(query.getBoost(), equalTo(5f)); + assertTermOrBoostQuery(query, STRING_FIELD_NAME, "test", 5f); multiMatchQueryBuilder = new MultiMatchQueryBuilder("test"); - multiMatchQueryBuilder.field(STRING_FIELD_NAME, 5); - multiMatchQueryBuilder.boost(2); + multiMatchQueryBuilder.field(STRING_FIELD_NAME, 5f); + multiMatchQueryBuilder.boost(2f); query = multiMatchQueryBuilder.toQuery(shardContext); - assertThat(query, instanceOf(TermQuery.class)); - assertThat(query.getBoost(), equalTo(10f)); + assertThat(query, instanceOf(BoostQuery.class)); + BoostQuery boostQuery = (BoostQuery) query; + assertThat(boostQuery.getBoost(), equalTo(2f)); + assertTermOrBoostQuery(boostQuery.getQuery(), STRING_FIELD_NAME, "test", 5f); } public void testToQueryMultipleTermsBooleanQuery() throws Exception { @@ -212,7 +201,9 @@ public class MultiMatchQueryBuilderTests extends AbstractQueryTestCase disjuncts = disMaxQuery.getDisjuncts(); + assertThat(disjuncts.get(0), instanceOf(TermQuery.class)); assertThat(((TermQuery) disjuncts.get(0)).getTerm(), equalTo(new Term(STRING_FIELD_NAME, "test"))); + assertThat(disjuncts.get(1), instanceOf(TermQuery.class)); assertThat(((TermQuery) disjuncts.get(1)).getTerm(), equalTo(new Term(STRING_FIELD_NAME_2, "test"))); } diff --git a/core/src/test/java/org/elasticsearch/index/query/QueryStringQueryBuilderTests.java b/core/src/test/java/org/elasticsearch/index/query/QueryStringQueryBuilderTests.java index 00b139039cf..2f2fdd406e1 100644 --- a/core/src/test/java/org/elasticsearch/index/query/QueryStringQueryBuilderTests.java +++ b/core/src/test/java/org/elasticsearch/index/query/QueryStringQueryBuilderTests.java @@ -20,16 +20,7 @@ package org.elasticsearch.index.query; import org.apache.lucene.index.Term; -import org.apache.lucene.search.BooleanQuery; -import org.apache.lucene.search.BoostQuery; -import org.apache.lucene.search.DisjunctionMaxQuery; -import org.apache.lucene.search.MatchAllDocsQuery; -import org.apache.lucene.search.MatchNoDocsQuery; -import org.apache.lucene.search.NumericRangeQuery; -import org.apache.lucene.search.PhraseQuery; -import org.apache.lucene.search.Query; -import org.apache.lucene.search.RegexpQuery; -import org.apache.lucene.search.TermQuery; +import org.apache.lucene.search.*; import org.apache.lucene.util.automaton.TooComplexToDeterminizeException; import org.elasticsearch.common.lucene.all.AllTermQuery; import org.hamcrest.Matchers; @@ -42,9 +33,8 @@ import static org.elasticsearch.index.query.QueryBuilders.queryStringQuery; import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertBooleanSubQuery; import static org.hamcrest.CoreMatchers.either; import static org.hamcrest.CoreMatchers.equalTo; -import static org.hamcrest.CoreMatchers.instanceOf; -import static org.hamcrest.Matchers.closeTo; import static org.hamcrest.Matchers.containsString; +import static org.hamcrest.Matchers.instanceOf; public class QueryStringQueryBuilderTests extends AbstractQueryTestCase { @@ -197,13 +187,17 @@ public class QueryStringQueryBuilderTests extends AbstractQueryTestCase disjuncts = disMaxQuery.getDisjuncts(); - assertThat(((TermQuery) disjuncts.get(0)).getTerm(), equalTo(new Term(STRING_FIELD_NAME, "test"))); - assertThat((double) disjuncts.get(0).getBoost(), closeTo(2.2, 0.01)); - assertThat(((TermQuery) disjuncts.get(1)).getTerm(), equalTo(new Term(STRING_FIELD_NAME_2, "test"))); - assertThat((double) disjuncts.get(1).getBoost(), closeTo(1, 0.01)); + assertTermOrBoostQuery(disjuncts.get(0), STRING_FIELD_NAME, "test", 2.2f); + assertTermOrBoostQuery(disjuncts.get(1), STRING_FIELD_NAME_2, "test", 1.0f); } public void testToQueryRegExpQuery() throws Exception { @@ -330,4 +322,56 @@ public class QueryStringQueryBuilderTests extends AbstractQueryTestCase 0); + int numBoosts = randomIntBetween(2, 10); + float[] boosts = new float[numBoosts + 1]; + String queryStringPrefix = ""; + String queryStringSuffix = ""; + for (int i = 0; i < boosts.length - 1; i++) { + float boost = 2.0f / randomIntBetween(3, 20); + boosts[i] = boost; + queryStringPrefix += "("; + queryStringSuffix += ")^" + boost; + } + String queryString = queryStringPrefix + "foo bar" + queryStringSuffix; + + float mainBoost = 2.0f / randomIntBetween(3, 20); + boosts[boosts.length - 1] = mainBoost; + QueryStringQueryBuilder queryStringQueryBuilder = new QueryStringQueryBuilder(queryString).field(STRING_FIELD_NAME) + .minimumShouldMatch("2").boost(mainBoost); + Query query = queryStringQueryBuilder.toQuery(createShardContext()); + + for (int i = boosts.length - 1; i >= 0; i--) { + assertThat(query, instanceOf(BoostQuery.class)); + BoostQuery boostQuery = (BoostQuery) query; + assertThat(boostQuery.getBoost(), equalTo(boosts[i])); + query = boostQuery.getQuery(); + } + + assertThat(query, instanceOf(BooleanQuery.class)); + BooleanQuery booleanQuery = (BooleanQuery) query; + assertThat(booleanQuery.getMinimumNumberShouldMatch(), equalTo(2)); + assertThat(booleanQuery.clauses().get(0).getOccur(), equalTo(BooleanClause.Occur.SHOULD)); + assertThat(booleanQuery.clauses().get(0).getQuery(), equalTo(new TermQuery(new Term(STRING_FIELD_NAME, "foo")))); + assertThat(booleanQuery.clauses().get(1).getOccur(), equalTo(BooleanClause.Occur.SHOULD)); + assertThat(booleanQuery.clauses().get(1).getQuery(), equalTo(new TermQuery(new Term(STRING_FIELD_NAME, "bar")))); + } + + public void testToQueryPhraseQueryBoostAndSlop() throws IOException { + assumeTrue("test runs only when at least a type is registered", getCurrentTypes().length > 0); + QueryStringQueryBuilder queryStringQueryBuilder = new QueryStringQueryBuilder("\"test phrase\"~2").field(STRING_FIELD_NAME, 5f); + Query query = queryStringQueryBuilder.toQuery(createShardContext()); + assertThat(query, instanceOf(DisjunctionMaxQuery.class)); + DisjunctionMaxQuery disjunctionMaxQuery = (DisjunctionMaxQuery) query; + assertThat(disjunctionMaxQuery.getDisjuncts().size(), equalTo(1)); + assertThat(disjunctionMaxQuery.getDisjuncts().get(0), instanceOf(BoostQuery.class)); + BoostQuery boostQuery = (BoostQuery) disjunctionMaxQuery.getDisjuncts().get(0); + assertThat(boostQuery.getBoost(), equalTo(5f)); + assertThat(boostQuery.getQuery(), instanceOf(PhraseQuery.class)); + PhraseQuery phraseQuery = (PhraseQuery) boostQuery.getQuery(); + assertThat(phraseQuery.getSlop(), Matchers.equalTo(2)); + assertThat(phraseQuery.getTerms().length, equalTo(2)); + } } diff --git a/core/src/test/java/org/elasticsearch/index/query/RandomQueryBuilder.java b/core/src/test/java/org/elasticsearch/index/query/RandomQueryBuilder.java index 2b173bdb1fe..147d21576c6 100644 --- a/core/src/test/java/org/elasticsearch/index/query/RandomQueryBuilder.java +++ b/core/src/test/java/org/elasticsearch/index/query/RandomQueryBuilder.java @@ -61,27 +61,38 @@ public class RandomQueryBuilder { public static MultiTermQueryBuilder createMultiTermQuery(Random r) { // for now, only use String Rangequeries for MultiTerm test, numeric and date makes little sense // see issue #12123 for discussion + MultiTermQueryBuilder multiTermQueryBuilder; switch(RandomInts.randomIntBetween(r, 0, 5)) { case 0: RangeQueryBuilder stringRangeQuery = new RangeQueryBuilder(AbstractQueryTestCase.STRING_FIELD_NAME); stringRangeQuery.from("a" + RandomStrings.randomAsciiOfLengthBetween(r, 1, 10)); stringRangeQuery.to("z" + RandomStrings.randomAsciiOfLengthBetween(r, 1, 10)); - return stringRangeQuery; + multiTermQueryBuilder = stringRangeQuery; + break; case 1: RangeQueryBuilder numericRangeQuery = new RangeQueryBuilder(AbstractQueryTestCase.INT_FIELD_NAME); numericRangeQuery.from(RandomInts.randomIntBetween(r, 1, 100)); numericRangeQuery.to(RandomInts.randomIntBetween(r, 101, 200)); - return numericRangeQuery; + multiTermQueryBuilder = numericRangeQuery; + break; case 2: - return new FuzzyQueryBuilder(AbstractQueryTestCase.INT_FIELD_NAME, RandomInts.randomInt(r, 1000)); + multiTermQueryBuilder = new FuzzyQueryBuilder(AbstractQueryTestCase.INT_FIELD_NAME, RandomInts.randomInt(r, 1000)); + break; case 3: - return new FuzzyQueryBuilder(AbstractQueryTestCase.STRING_FIELD_NAME, RandomStrings.randomAsciiOfLengthBetween(r, 1, 10)); + multiTermQueryBuilder = new FuzzyQueryBuilder(AbstractQueryTestCase.STRING_FIELD_NAME, RandomStrings.randomAsciiOfLengthBetween(r, 1, 10)); + break; case 4: - return new PrefixQueryBuilderTests().createTestQueryBuilder(); + multiTermQueryBuilder = new PrefixQueryBuilderTests().createTestQueryBuilder(); + break; case 5: - return new WildcardQueryBuilderTests().createTestQueryBuilder(); + multiTermQueryBuilder = new WildcardQueryBuilderTests().createTestQueryBuilder(); + break; default: throw new UnsupportedOperationException(); } + if (r.nextBoolean()) { + multiTermQueryBuilder.boost(2.0f / RandomInts.randomIntBetween(r, 1, 20)); + } + return multiTermQueryBuilder; } } diff --git a/core/src/test/java/org/elasticsearch/index/query/SimpleQueryStringBuilderTests.java b/core/src/test/java/org/elasticsearch/index/query/SimpleQueryStringBuilderTests.java index 85f1c1d5f0a..6cfcb7f2083 100644 --- a/core/src/test/java/org/elasticsearch/index/query/SimpleQueryStringBuilderTests.java +++ b/core/src/test/java/org/elasticsearch/index/query/SimpleQueryStringBuilderTests.java @@ -20,11 +20,7 @@ package org.elasticsearch.index.query; import org.apache.lucene.index.Term; -import org.apache.lucene.search.BooleanClause; -import org.apache.lucene.search.BooleanQuery; -import org.apache.lucene.search.MatchNoDocsQuery; -import org.apache.lucene.search.Query; -import org.apache.lucene.search.TermQuery; +import org.apache.lucene.search.*; import org.elasticsearch.Version; import org.elasticsearch.cluster.metadata.MetaData; import org.elasticsearch.common.ParseFieldMatcher; @@ -276,10 +272,9 @@ public class SimpleQueryStringBuilderTests extends AbstractQueryTestCase 1) { - assertTrue("Query should have been BooleanQuery but was " + query.getClass().getName(), query instanceof BooleanQuery); - + assertThat(query, instanceOf(BooleanQuery.class)); BooleanQuery boolQuery = (BooleanQuery) query; if (queryBuilder.lowercaseExpandedTerms()) { for (BooleanClause clause : boolQuery.clauses()) { @@ -289,43 +284,26 @@ public class SimpleQueryStringBuilderTests extends AbstractQueryTestCase fields = queryBuilder.fields().keySet().iterator(); + Iterator> fieldsIterator = queryBuilder.fields().entrySet().iterator(); for (BooleanClause booleanClause : boolQuery) { - assertThat(booleanClause.getQuery(), instanceOf(TermQuery.class)); - TermQuery termQuery = (TermQuery) booleanClause.getQuery(); - assertThat(termQuery.getTerm().field(), equalTo(fields.next())); - assertThat(termQuery.getTerm().text().toLowerCase(Locale.ROOT), equalTo(queryBuilder.value().toLowerCase(Locale.ROOT))); + Map.Entry field = fieldsIterator.next(); + assertTermOrBoostQuery(booleanClause.getQuery(), field.getKey(), queryBuilder.value(), field.getValue()); } - if (queryBuilder.minimumShouldMatch() != null) { assertThat(boolQuery.getMinimumNumberShouldMatch(), greaterThan(0)); } - } else if (queryBuilder.fields().size() <= 1) { - assertTrue("Query should have been TermQuery but was " + query.getClass().getName(), query instanceof TermQuery); - - TermQuery termQuery = (TermQuery) query; - String field; - if (queryBuilder.fields().size() == 0) { - field = MetaData.ALL; - } else { - field = queryBuilder.fields().keySet().iterator().next(); - } - assertThat(termQuery.getTerm().field(), equalTo(field)); - assertThat(termQuery.getTerm().text().toLowerCase(Locale.ROOT), equalTo(queryBuilder.value().toLowerCase(Locale.ROOT))); + } else if (queryBuilder.fields().size() == 1) { + Map.Entry field = queryBuilder.fields().entrySet().iterator().next(); + assertTermOrBoostQuery(query, field.getKey(), queryBuilder.value(), field.getValue()); + } else if (queryBuilder.fields().size() == 0) { + assertTermQuery(query, MetaData.ALL, queryBuilder.value()); } else { fail("Encountered lucene query type we do not have a validation implementation for in our " + SimpleQueryStringBuilderTests.class.getSimpleName()); } } - @Override - protected void assertBoost(SimpleQueryStringBuilder queryBuilder, Query query) throws IOException { - //boost may get parsed from the random query, we then combine the main boost with that one coming from lucene - //instead of trying to reparse the query and guess what the boost should be, we delegate boost checks to specific boost tests below - } - - private int shouldClauses(BooleanQuery query) { + private static int shouldClauses(BooleanQuery query) { int result = 0; for (BooleanClause c : query.clauses()) { if (c.getOccur() == BooleanClause.Occur.SHOULD) { @@ -341,15 +319,21 @@ public class SimpleQueryStringBuilderTests extends AbstractQueryTestCase((MultiTermQuery)multiTermQuery).getWrappedQuery())); } @@ -72,4 +86,10 @@ public class SpanMultiTermQueryBuilderTests extends AbstractQueryTestCase