1
0
Эх сурвалжийг харах

Refactor QueryStringQuery for 6.0 (#25646)

This change refactors the query_string query to analyze the query text around logical operators of the query string the same way than a match_query/multi_match_query.
It also adds a type parameter that can be used to change the way multi fields query are built the same way than a multi_match query does.

Now that these queries share the same behavior regarding text analysis, some parameters are obsolete and have been deprecated:

split_on_whitespace: This setting is now ignored with a deprecation notice
if it is used explicitely. With this PR The query_string always splits on logical operator.
It simplifies the understanding of the other parameters that can have different meanings
depending on the value of split_on_whitespace.

auto_generate_phrase_queries: This setting is now ignored with a deprecation notice
if it is used explicitely. This setting only makes sense when the parser splits on whitespace.

use_dismax: This setting is now ignored with a deprecation notice
if it is used explicitely. The tie_breaker parameter is sufficient to handle best_fields/most_fields.

Fixes #25574
Jim Ferenczi 8 жил өмнө
parent
commit
13da3eb53e
21 өөрчлөгдсөн 658 нэмэгдсэн , 912 устгасан
  1. 0 282
      core/src/main/java/org/apache/lucene/queryparser/classic/QueryParserSettings.java
  2. 40 0
      core/src/main/java/org/apache/lucene/queryparser/classic/XQueryParser.java
  3. 3 1
      core/src/main/java/org/elasticsearch/index/query/MatchPhrasePrefixQueryBuilder.java
  4. 3 1
      core/src/main/java/org/elasticsearch/index/query/MatchPhraseQueryBuilder.java
  5. 3 1
      core/src/main/java/org/elasticsearch/index/query/MatchQueryBuilder.java
  6. 1 1
      core/src/main/java/org/elasticsearch/index/query/QueryBuilders.java
  7. 0 8
      core/src/main/java/org/elasticsearch/index/query/QueryShardContext.java
  8. 145 140
      core/src/main/java/org/elasticsearch/index/query/QueryStringQueryBuilder.java
  9. 1 1
      core/src/main/java/org/elasticsearch/index/search/ExistsFieldQueryExtension.java
  10. 1 1
      core/src/main/java/org/elasticsearch/index/search/FieldQueryExtension.java
  11. 16 16
      core/src/main/java/org/elasticsearch/index/search/MatchQuery.java
  12. 1 2
      core/src/main/java/org/elasticsearch/index/search/MultiMatchQuery.java
  13. 321 283
      core/src/main/java/org/elasticsearch/index/search/QueryStringQueryParser.java
  14. 2 12
      core/src/test/java/org/elasticsearch/index/query/DisableGraphQueryTests.java
  15. 59 123
      core/src/test/java/org/elasticsearch/index/query/QueryStringQueryBuilderTests.java
  16. 2 2
      core/src/test/java/org/elasticsearch/search/fetch/subphase/highlight/HighlighterSearchIT.java
  17. 3 16
      core/src/test/java/org/elasticsearch/search/query/QueryStringIT.java
  18. 1 1
      core/src/test/java/org/elasticsearch/search/query/SearchQueryIT.java
  19. 14 2
      docs/reference/migration/migrate_6_0/search.asciidoc
  20. 41 18
      docs/reference/query-dsl/query-string-query.asciidoc
  21. 1 1
      modules/analysis-common/src/test/java/org/elasticsearch/analysis/common/QueryStringWithAnalyzersTests.java

+ 0 - 282
core/src/main/java/org/apache/lucene/queryparser/classic/QueryParserSettings.java

@@ -1,282 +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.apache.lucene.queryparser.classic;
-
-import org.apache.lucene.analysis.Analyzer;
-import org.apache.lucene.search.MultiTermQuery;
-import org.elasticsearch.common.unit.Fuzziness;
-import org.joda.time.DateTimeZone;
-
-import java.util.Map;
-
-/**
- * Encapsulates settings that affect query_string parsing via {@link MapperQueryParser}
- */
-public class QueryParserSettings {
-
-    private final String queryString;
-
-    private String defaultField;
-
-    private Map<String, Float> fieldsAndWeights;
-
-    private QueryParser.Operator defaultOperator;
-
-    private Analyzer analyzer;
-    private boolean forceAnalyzer;
-    private Analyzer quoteAnalyzer;
-    private boolean forceQuoteAnalyzer;
-
-    private String quoteFieldSuffix;
-
-    private boolean autoGeneratePhraseQueries;
-
-    private boolean allowLeadingWildcard;
-
-    private boolean analyzeWildcard;
-
-    private boolean enablePositionIncrements;
-
-    private Fuzziness fuzziness;
-    private int fuzzyPrefixLength;
-    private int fuzzyMaxExpansions;
-    private MultiTermQuery.RewriteMethod fuzzyRewriteMethod;
-
-    private int phraseSlop;
-
-    private boolean useDisMax;
-
-    private float tieBreaker;
-
-    private MultiTermQuery.RewriteMethod rewriteMethod;
-
-    private boolean lenient;
-
-    private DateTimeZone timeZone;
-
-    /** To limit effort spent determinizing regexp queries. */
-    private int maxDeterminizedStates;
-
-    private boolean splitOnWhitespace;
-
-    public QueryParserSettings(String queryString) {
-        this.queryString = queryString;
-    }
-
-    public String queryString() {
-        return queryString;
-    }
-
-    public String defaultField() {
-        return defaultField;
-    }
-
-    public void defaultField(String defaultField) {
-        this.defaultField = defaultField;
-    }
-
-    public Map<String, Float> fieldsAndWeights() {
-        return fieldsAndWeights;
-    }
-
-    public void fieldsAndWeights(Map<String, Float> fieldsAndWeights) {
-        this.fieldsAndWeights = fieldsAndWeights;
-    }
-
-    public QueryParser.Operator defaultOperator() {
-        return defaultOperator;
-    }
-
-    public void defaultOperator(QueryParser.Operator defaultOperator) {
-        this.defaultOperator = defaultOperator;
-    }
-
-    public boolean autoGeneratePhraseQueries() {
-        return autoGeneratePhraseQueries;
-    }
-
-    public void autoGeneratePhraseQueries(boolean autoGeneratePhraseQueries) {
-        this.autoGeneratePhraseQueries = autoGeneratePhraseQueries;
-    }
-
-    public int maxDeterminizedStates() {
-        return maxDeterminizedStates;
-    }
-
-    public void maxDeterminizedStates(int maxDeterminizedStates) {
-        this.maxDeterminizedStates = maxDeterminizedStates;
-    }
-
-    public boolean allowLeadingWildcard() {
-        return allowLeadingWildcard;
-    }
-
-    public void allowLeadingWildcard(boolean allowLeadingWildcard) {
-        this.allowLeadingWildcard = allowLeadingWildcard;
-    }
-
-    public boolean enablePositionIncrements() {
-        return enablePositionIncrements;
-    }
-
-    public void enablePositionIncrements(boolean enablePositionIncrements) {
-        this.enablePositionIncrements = enablePositionIncrements;
-    }
-
-    public int phraseSlop() {
-        return phraseSlop;
-    }
-
-    public void phraseSlop(int phraseSlop) {
-        this.phraseSlop = phraseSlop;
-    }
-
-    public int fuzzyPrefixLength() {
-        return fuzzyPrefixLength;
-    }
-
-    public void fuzzyPrefixLength(int fuzzyPrefixLength) {
-        this.fuzzyPrefixLength = fuzzyPrefixLength;
-    }
-
-    public int fuzzyMaxExpansions() {
-        return fuzzyMaxExpansions;
-    }
-
-    public void fuzzyMaxExpansions(int fuzzyMaxExpansions) {
-        this.fuzzyMaxExpansions = fuzzyMaxExpansions;
-    }
-
-    public MultiTermQuery.RewriteMethod fuzzyRewriteMethod() {
-        return fuzzyRewriteMethod;
-    }
-
-    public void fuzzyRewriteMethod(MultiTermQuery.RewriteMethod fuzzyRewriteMethod) {
-        this.fuzzyRewriteMethod = fuzzyRewriteMethod;
-    }
-
-    public void defaultAnalyzer(Analyzer analyzer) {
-        this.analyzer = analyzer;
-        this.forceAnalyzer = false;
-    }
-
-    public void forceAnalyzer(Analyzer analyzer) {
-        this.analyzer = analyzer;
-        this.forceAnalyzer = true;
-    }
-
-    public Analyzer analyzer() {
-        return analyzer;
-    }
-
-    public boolean forceAnalyzer() {
-        return forceAnalyzer;
-    }
-
-    public void defaultQuoteAnalyzer(Analyzer quoteAnalyzer) {
-        this.quoteAnalyzer = quoteAnalyzer;
-        this.forceQuoteAnalyzer = false;
-    }
-
-    public void forceQuoteAnalyzer(Analyzer quoteAnalyzer) {
-        this.quoteAnalyzer = quoteAnalyzer;
-        this.forceQuoteAnalyzer = true;
-    }
-
-    public Analyzer quoteAnalyzer() {
-        return quoteAnalyzer;
-    }
-
-    public boolean forceQuoteAnalyzer() {
-        return forceQuoteAnalyzer;
-    }
-
-    public boolean analyzeWildcard() {
-        return this.analyzeWildcard;
-    }
-
-    public void analyzeWildcard(boolean analyzeWildcard) {
-        this.analyzeWildcard = analyzeWildcard;
-    }
-
-    public MultiTermQuery.RewriteMethod rewriteMethod() {
-        return this.rewriteMethod;
-    }
-
-    public void rewriteMethod(MultiTermQuery.RewriteMethod rewriteMethod) {
-        this.rewriteMethod = rewriteMethod;
-    }
-
-    public void quoteFieldSuffix(String quoteFieldSuffix) {
-        this.quoteFieldSuffix = quoteFieldSuffix;
-    }
-
-    public String quoteFieldSuffix() {
-        return this.quoteFieldSuffix;
-    }
-
-    public void lenient(boolean lenient) {
-        this.lenient = lenient;
-    }
-
-    public boolean lenient() {
-        return this.lenient;
-    }
-
-    public float tieBreaker() {
-        return tieBreaker;
-    }
-
-    public void tieBreaker(float tieBreaker) {
-        this.tieBreaker = tieBreaker;
-    }
-
-    public boolean useDisMax() {
-        return useDisMax;
-    }
-
-    public void useDisMax(boolean useDisMax) {
-        this.useDisMax = useDisMax;
-    }
-
-    public void timeZone(DateTimeZone timeZone) {
-        this.timeZone = timeZone;
-    }
-
-    public DateTimeZone timeZone() {
-        return this.timeZone;
-    }
-
-    public void fuzziness(Fuzziness fuzziness) {
-        this.fuzziness = fuzziness;
-    }
-
-    public Fuzziness fuzziness() {
-        return fuzziness;
-    }
-
-    public void splitOnWhitespace(boolean value) {
-        this.splitOnWhitespace = value;
-    }
-
-    public boolean splitOnWhitespace() {
-        return splitOnWhitespace;
-    }
-}

+ 40 - 0
core/src/main/java/org/apache/lucene/queryparser/classic/XQueryParser.java

@@ -0,0 +1,40 @@
+/*
+ * 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.apache.lucene.queryparser.classic;
+
+import org.apache.lucene.analysis.Analyzer;
+import org.apache.lucene.search.Query;
+
+/**
+ * This class is just a workaround to make {@link QueryParser#handleBareFuzzy(String, Token, String)} accessible by sub-classes.
+ * It is needed for {@link QueryParser}s that need to override the parsing of the slop in a fuzzy query (e.g. word<b>~2</b>, word<b>~</b>).
+ *
+ * TODO: We should maybe rewrite this with the flexible query parser which matches the same syntax with more freedom.
+ */
+public class XQueryParser extends QueryParser {
+    public XQueryParser(String f, Analyzer a) {
+        super(f, a);
+    }
+
+    @Override
+    protected Query handleBareFuzzy(String field, Token fuzzySlop, String termImage) throws ParseException {
+        return super.handleBareFuzzy(field, fuzzySlop, termImage);
+    }
+}

+ 3 - 1
core/src/main/java/org/elasticsearch/index/query/MatchPhrasePrefixQueryBuilder.java

@@ -168,7 +168,9 @@ public class MatchPhrasePrefixQueryBuilder extends AbstractQueryBuilder<MatchPhr
         }
         }
 
 
         MatchQuery matchQuery = new MatchQuery(context);
         MatchQuery matchQuery = new MatchQuery(context);
-        matchQuery.setAnalyzer(analyzer);
+        if (analyzer != null) {
+            matchQuery.setAnalyzer(analyzer);
+        }
         matchQuery.setPhraseSlop(slop);
         matchQuery.setPhraseSlop(slop);
         matchQuery.setMaxExpansions(maxExpansions);
         matchQuery.setMaxExpansions(maxExpansions);
 
 

+ 3 - 1
core/src/main/java/org/elasticsearch/index/query/MatchPhraseQueryBuilder.java

@@ -144,7 +144,9 @@ public class MatchPhraseQueryBuilder extends AbstractQueryBuilder<MatchPhraseQue
         }
         }
 
 
         MatchQuery matchQuery = new MatchQuery(context);
         MatchQuery matchQuery = new MatchQuery(context);
-        matchQuery.setAnalyzer(analyzer);
+        if (analyzer != null) {
+            matchQuery.setAnalyzer(analyzer);
+        }
         matchQuery.setPhraseSlop(slop);
         matchQuery.setPhraseSlop(slop);
 
 
         return matchQuery.parse(MatchQuery.Type.PHRASE, fieldName, value);
         return matchQuery.parse(MatchQuery.Type.PHRASE, fieldName, value);

+ 3 - 1
core/src/main/java/org/elasticsearch/index/query/MatchQueryBuilder.java

@@ -445,7 +445,9 @@ public class MatchQueryBuilder extends AbstractQueryBuilder<MatchQueryBuilder> {
 
 
         MatchQuery matchQuery = new MatchQuery(context);
         MatchQuery matchQuery = new MatchQuery(context);
         matchQuery.setOccur(operator.toBooleanClauseOccur());
         matchQuery.setOccur(operator.toBooleanClauseOccur());
-        matchQuery.setAnalyzer(analyzer);
+        if (analyzer != null) {
+            matchQuery.setAnalyzer(analyzer);
+        }
         matchQuery.setPhraseSlop(slop);
         matchQuery.setPhraseSlop(slop);
         matchQuery.setFuzziness(fuzziness);
         matchQuery.setFuzziness(fuzziness);
         matchQuery.setFuzzyPrefixLength(prefixLength);
         matchQuery.setFuzzyPrefixLength(prefixLength);

+ 1 - 1
core/src/main/java/org/elasticsearch/index/query/QueryBuilders.java

@@ -267,7 +267,7 @@ public abstract class QueryBuilders {
      * when no field is added (using {@link QueryStringQueryBuilder#field(String)}, will run the query once and non prefixed fields
      * when no field is added (using {@link QueryStringQueryBuilder#field(String)}, will run the query once and non prefixed fields
      * will use the {@link QueryStringQueryBuilder#defaultField(String)} set. The second, when one or more fields are added
      * will use the {@link QueryStringQueryBuilder#defaultField(String)} set. The second, when one or more fields are added
      * (using {@link QueryStringQueryBuilder#field(String)}), will run the parsed query against the provided fields, and combine
      * (using {@link QueryStringQueryBuilder#field(String)}), will run the parsed query against the provided fields, and combine
-     * them either using DisMax or a plain boolean query (see {@link QueryStringQueryBuilder#useDisMax(boolean)}).
+     * them either using Dismax.
      *
      *
      * @param queryString The query string to run
      * @param queryString The query string to run
      */
      */

+ 0 - 8
core/src/main/java/org/elasticsearch/index/query/QueryShardContext.java

@@ -21,8 +21,6 @@ package org.elasticsearch.index.query;
 
 
 import org.apache.lucene.analysis.Analyzer;
 import org.apache.lucene.analysis.Analyzer;
 import org.apache.lucene.index.IndexReader;
 import org.apache.lucene.index.IndexReader;
-import org.apache.lucene.queryparser.classic.MapperQueryParser;
-import org.apache.lucene.queryparser.classic.QueryParserSettings;
 import org.apache.lucene.search.Query;
 import org.apache.lucene.search.Query;
 import org.apache.lucene.search.join.BitSetProducer;
 import org.apache.lucene.search.join.BitSetProducer;
 import org.apache.lucene.search.similarities.Similarity;
 import org.apache.lucene.search.similarities.Similarity;
@@ -87,7 +85,6 @@ public class QueryShardContext extends QueryRewriteContext {
     }
     }
 
 
     private final Map<String, Query> namedQueries = new HashMap<>();
     private final Map<String, Query> namedQueries = new HashMap<>();
-    private final MapperQueryParser queryParser = new MapperQueryParser(this);
     private boolean allowUnmappedFields;
     private boolean allowUnmappedFields;
     private boolean mapUnmappedFieldAsString;
     private boolean mapUnmappedFieldAsString;
     private NestedScope nestedScope;
     private NestedScope nestedScope;
@@ -148,11 +145,6 @@ public class QueryShardContext extends QueryRewriteContext {
         return indexSettings.isQueryStringAllowLeadingWildcard();
         return indexSettings.isQueryStringAllowLeadingWildcard();
     }
     }
 
 
-    public MapperQueryParser queryParser(QueryParserSettings settings) {
-        queryParser.reset(settings);
-        return queryParser;
-    }
-
     public BitSetProducer bitsetFilter(Query filter) {
     public BitSetProducer bitsetFilter(Query filter) {
         return bitsetFilterCache.getBitSetProducer(filter);
         return bitsetFilterCache.getBitSetProducer(filter);
     }
     }

+ 145 - 140
core/src/main/java/org/elasticsearch/index/query/QueryStringQueryBuilder.java

@@ -19,8 +19,6 @@
 
 
 package org.elasticsearch.index.query;
 package org.elasticsearch.index.query;
 
 
-import org.apache.lucene.queryparser.classic.MapperQueryParser;
-import org.apache.lucene.queryparser.classic.QueryParserSettings;
 import org.apache.lucene.search.BoostQuery;
 import org.apache.lucene.search.BoostQuery;
 import org.apache.lucene.search.FuzzyQuery;
 import org.apache.lucene.search.FuzzyQuery;
 import org.apache.lucene.search.Query;
 import org.apache.lucene.search.Query;
@@ -45,6 +43,7 @@ import org.elasticsearch.index.mapper.NumberFieldMapper;
 import org.elasticsearch.index.mapper.ScaledFloatFieldMapper;
 import org.elasticsearch.index.mapper.ScaledFloatFieldMapper;
 import org.elasticsearch.index.mapper.TextFieldMapper;
 import org.elasticsearch.index.mapper.TextFieldMapper;
 import org.elasticsearch.index.query.support.QueryParsers;
 import org.elasticsearch.index.query.support.QueryParsers;
+import org.elasticsearch.index.search.QueryStringQueryParser;
 import org.joda.time.DateTimeZone;
 import org.joda.time.DateTimeZone;
 
 
 import java.io.IOException;
 import java.io.IOException;
@@ -64,24 +63,21 @@ import java.util.TreeMap;
  * when no field is added (using {@link #field(String)}, will run the query once and non prefixed fields
  * when no field is added (using {@link #field(String)}, will run the query once and non prefixed fields
  * will use the {@link #defaultField(String)} set. The second, when one or more fields are added
  * will use the {@link #defaultField(String)} set. The second, when one or more fields are added
  * (using {@link #field(String)}), will run the parsed query against the provided fields, and combine
  * (using {@link #field(String)}), will run the parsed query against the provided fields, and combine
- * them either using DisMax or a plain boolean query (see {@link #useDisMax(boolean)}).
+ * them using Dismax.
  */
  */
 public class QueryStringQueryBuilder extends AbstractQueryBuilder<QueryStringQueryBuilder> {
 public class QueryStringQueryBuilder extends AbstractQueryBuilder<QueryStringQueryBuilder> {
 
 
     public static final String NAME = "query_string";
     public static final String NAME = "query_string";
 
 
-    public static final boolean DEFAULT_AUTO_GENERATE_PHRASE_QUERIES = false;
     public static final int DEFAULT_MAX_DETERMINED_STATES = Operations.DEFAULT_MAX_DETERMINIZED_STATES;
     public static final int DEFAULT_MAX_DETERMINED_STATES = Operations.DEFAULT_MAX_DETERMINIZED_STATES;
     public static final boolean DEFAULT_ENABLE_POSITION_INCREMENTS = true;
     public static final boolean DEFAULT_ENABLE_POSITION_INCREMENTS = true;
     public static final boolean DEFAULT_ESCAPE = false;
     public static final boolean DEFAULT_ESCAPE = false;
-    public static final boolean DEFAULT_USE_DIS_MAX = true;
     public static final int DEFAULT_FUZZY_PREFIX_LENGTH = FuzzyQuery.defaultPrefixLength;
     public static final int DEFAULT_FUZZY_PREFIX_LENGTH = FuzzyQuery.defaultPrefixLength;
     public static final int DEFAULT_FUZZY_MAX_EXPANSIONS = FuzzyQuery.defaultMaxExpansions;
     public static final int DEFAULT_FUZZY_MAX_EXPANSIONS = FuzzyQuery.defaultMaxExpansions;
     public static final int DEFAULT_PHRASE_SLOP = 0;
     public static final int DEFAULT_PHRASE_SLOP = 0;
-    public static final float DEFAULT_TIE_BREAKER = 0.0f;
     public static final Fuzziness DEFAULT_FUZZINESS = Fuzziness.AUTO;
     public static final Fuzziness DEFAULT_FUZZINESS = Fuzziness.AUTO;
     public static final Operator DEFAULT_OPERATOR = Operator.OR;
     public static final Operator DEFAULT_OPERATOR = Operator.OR;
-    public static final boolean DEFAULT_SPLIT_ON_WHITESPACE = true;
+    public static final MultiMatchQueryBuilder.Type DEFAULT_TYPE = MultiMatchQueryBuilder.Type.BEST_FIELDS;
 
 
     private static final ParseField QUERY_FIELD = new ParseField("query");
     private static final ParseField QUERY_FIELD = new ParseField("query");
     private static final ParseField FIELDS_FIELD = new ParseField("fields");
     private static final ParseField FIELDS_FIELD = new ParseField("fields");
@@ -90,13 +86,15 @@ public class QueryStringQueryBuilder extends AbstractQueryBuilder<QueryStringQue
     private static final ParseField ANALYZER_FIELD = new ParseField("analyzer");
     private static final ParseField ANALYZER_FIELD = new ParseField("analyzer");
     private static final ParseField QUOTE_ANALYZER_FIELD = new ParseField("quote_analyzer");
     private static final ParseField QUOTE_ANALYZER_FIELD = new ParseField("quote_analyzer");
     private static final ParseField ALLOW_LEADING_WILDCARD_FIELD = new ParseField("allow_leading_wildcard");
     private static final ParseField ALLOW_LEADING_WILDCARD_FIELD = new ParseField("allow_leading_wildcard");
-    private static final ParseField AUTO_GENERATE_PHRASE_QUERIES_FIELD = new ParseField("auto_generate_phrase_queries");
+    private static final ParseField AUTO_GENERATE_PHRASE_QUERIES_FIELD = new ParseField("auto_generate_phrase_queries")
+            .withAllDeprecated("This setting is ignored, use [type=phrase] instead");
     private static final ParseField MAX_DETERMINIZED_STATES_FIELD = new ParseField("max_determinized_states");
     private static final ParseField MAX_DETERMINIZED_STATES_FIELD = new ParseField("max_determinized_states");
     private static final ParseField LOWERCASE_EXPANDED_TERMS_FIELD = new ParseField("lowercase_expanded_terms")
     private static final ParseField LOWERCASE_EXPANDED_TERMS_FIELD = new ParseField("lowercase_expanded_terms")
             .withAllDeprecated("Decision is now made by the analyzer");
             .withAllDeprecated("Decision is now made by the analyzer");
     private static final ParseField ENABLE_POSITION_INCREMENTS_FIELD = new ParseField("enable_position_increments");
     private static final ParseField ENABLE_POSITION_INCREMENTS_FIELD = new ParseField("enable_position_increments");
     private static final ParseField ESCAPE_FIELD = new ParseField("escape");
     private static final ParseField ESCAPE_FIELD = new ParseField("escape");
-    private static final ParseField USE_DIS_MAX_FIELD = new ParseField("use_dis_max");
+    private static final ParseField USE_DIS_MAX_FIELD = new ParseField("use_dis_max")
+            .withAllDeprecated("Set [tie_breaker] to 1 instead");
     private static final ParseField FUZZY_PREFIX_LENGTH_FIELD = new ParseField("fuzzy_prefix_length");
     private static final ParseField FUZZY_PREFIX_LENGTH_FIELD = new ParseField("fuzzy_prefix_length");
     private static final ParseField FUZZY_MAX_EXPANSIONS_FIELD = new ParseField("fuzzy_max_expansions");
     private static final ParseField FUZZY_MAX_EXPANSIONS_FIELD = new ParseField("fuzzy_max_expansions");
     private static final ParseField FUZZY_REWRITE_FIELD = new ParseField("fuzzy_rewrite");
     private static final ParseField FUZZY_REWRITE_FIELD = new ParseField("fuzzy_rewrite");
@@ -110,8 +108,10 @@ public class QueryStringQueryBuilder extends AbstractQueryBuilder<QueryStringQue
     private static final ParseField LOCALE_FIELD = new ParseField("locale")
     private static final ParseField LOCALE_FIELD = new ParseField("locale")
             .withAllDeprecated("Decision is now made by the analyzer");
             .withAllDeprecated("Decision is now made by the analyzer");
     private static final ParseField TIME_ZONE_FIELD = new ParseField("time_zone");
     private static final ParseField TIME_ZONE_FIELD = new ParseField("time_zone");
-    private static final ParseField SPLIT_ON_WHITESPACE = new ParseField("split_on_whitespace");
+    private static final ParseField SPLIT_ON_WHITESPACE = new ParseField("split_on_whitespace")
+            .withAllDeprecated("This setting is ignored, the parser always splits on logical operator");
     private static final ParseField ALL_FIELDS_FIELD = new ParseField("all_fields");
     private static final ParseField ALL_FIELDS_FIELD = new ParseField("all_fields");
+    private static final ParseField TYPE_FIELD = new ParseField("type");
 
 
     // Mapping types the "all-ish" query can be executed against
     // Mapping types the "all-ish" query can be executed against
     public static final Set<String> ALLOWED_QUERY_MAPPER_TYPES;
     public static final Set<String> ALLOWED_QUERY_MAPPER_TYPES;
@@ -131,6 +131,7 @@ public class QueryStringQueryBuilder extends AbstractQueryBuilder<QueryStringQue
     private final String queryString;
     private final String queryString;
 
 
     private String defaultField;
     private String defaultField;
+
     /**
     /**
      * Fields to query against. If left empty will query default field,
      * Fields to query against. If left empty will query default field,
      * currently _ALL. Uses a TreeMap to hold the fields so boolean clauses are
      * currently _ALL. Uses a TreeMap to hold the fields so boolean clauses are
@@ -148,8 +149,6 @@ public class QueryStringQueryBuilder extends AbstractQueryBuilder<QueryStringQue
 
 
     private String quoteFieldSuffix;
     private String quoteFieldSuffix;
 
 
-    private boolean autoGeneratePhraseQueries = DEFAULT_AUTO_GENERATE_PHRASE_QUERIES;
-
     private Boolean allowLeadingWildcard;
     private Boolean allowLeadingWildcard;
 
 
     private Boolean analyzeWildcard;
     private Boolean analyzeWildcard;
@@ -170,9 +169,9 @@ public class QueryStringQueryBuilder extends AbstractQueryBuilder<QueryStringQue
 
 
     private int phraseSlop = DEFAULT_PHRASE_SLOP;
     private int phraseSlop = DEFAULT_PHRASE_SLOP;
 
 
-    private boolean useDisMax = DEFAULT_USE_DIS_MAX;
+    private MultiMatchQueryBuilder.Type type = DEFAULT_TYPE;
 
 
-    private float tieBreaker = DEFAULT_TIE_BREAKER;
+    private Float tieBreaker;
 
 
     private String minimumShouldMatch;
     private String minimumShouldMatch;
 
 
@@ -185,8 +184,6 @@ public class QueryStringQueryBuilder extends AbstractQueryBuilder<QueryStringQue
     /** To limit effort spent determinizing regexp queries. */
     /** To limit effort spent determinizing regexp queries. */
     private int maxDeterminizedStates = DEFAULT_MAX_DETERMINED_STATES;
     private int maxDeterminizedStates = DEFAULT_MAX_DETERMINED_STATES;
 
 
-    private boolean splitOnWhitespace = DEFAULT_SPLIT_ON_WHITESPACE;
-
     public QueryStringQueryBuilder(String queryString) {
     public QueryStringQueryBuilder(String queryString) {
         if (queryString == null) {
         if (queryString == null) {
             throw new IllegalArgumentException("query text missing");
             throw new IllegalArgumentException("query text missing");
@@ -209,7 +206,9 @@ public class QueryStringQueryBuilder extends AbstractQueryBuilder<QueryStringQue
         analyzer = in.readOptionalString();
         analyzer = in.readOptionalString();
         quoteAnalyzer = in.readOptionalString();
         quoteAnalyzer = in.readOptionalString();
         quoteFieldSuffix = in.readOptionalString();
         quoteFieldSuffix = in.readOptionalString();
-        autoGeneratePhraseQueries = in.readBoolean();
+        if (in.getVersion().before(Version.V_6_0_0_beta1)) {
+            in.readBoolean(); // auto_generate_phrase_query
+        }
         allowLeadingWildcard = in.readOptionalBoolean();
         allowLeadingWildcard = in.readOptionalBoolean();
         analyzeWildcard = in.readOptionalBoolean();
         analyzeWildcard = in.readOptionalBoolean();
         if (in.getVersion().before(Version.V_5_1_1)) {
         if (in.getVersion().before(Version.V_5_1_1)) {
@@ -224,8 +223,14 @@ public class QueryStringQueryBuilder extends AbstractQueryBuilder<QueryStringQue
         fuzzyMaxExpansions = in.readVInt();
         fuzzyMaxExpansions = in.readVInt();
         fuzzyRewrite = in.readOptionalString();
         fuzzyRewrite = in.readOptionalString();
         phraseSlop = in.readVInt();
         phraseSlop = in.readVInt();
-        useDisMax = in.readBoolean();
-        tieBreaker = in.readFloat();
+        if (in.getVersion().before(Version.V_6_0_0_beta1)) {
+            in.readBoolean(); // use_dismax
+            tieBreaker = in.readFloat();
+            type = DEFAULT_TYPE;
+        } else {
+            type = MultiMatchQueryBuilder.Type.readFromStream(in);
+            tieBreaker = in.readOptionalFloat();
+        }
         rewrite = in.readOptionalString();
         rewrite = in.readOptionalString();
         minimumShouldMatch = in.readOptionalString();
         minimumShouldMatch = in.readOptionalString();
         lenient = in.readOptionalBoolean();
         lenient = in.readOptionalBoolean();
@@ -233,10 +238,10 @@ public class QueryStringQueryBuilder extends AbstractQueryBuilder<QueryStringQue
         escape = in.readBoolean();
         escape = in.readBoolean();
         maxDeterminizedStates = in.readVInt();
         maxDeterminizedStates = in.readVInt();
         if (in.getVersion().onOrAfter(Version.V_5_1_1)) {
         if (in.getVersion().onOrAfter(Version.V_5_1_1)) {
-            splitOnWhitespace = in.readBoolean();
+            if (in.getVersion().before(Version.V_6_0_0_beta1)) {
+                in.readBoolean(); // split_on_whitespace
+            }
             useAllFields = in.readOptionalBoolean();
             useAllFields = in.readOptionalBoolean();
-        } else {
-            splitOnWhitespace = DEFAULT_SPLIT_ON_WHITESPACE;
         }
         }
     }
     }
 
 
@@ -253,7 +258,9 @@ public class QueryStringQueryBuilder extends AbstractQueryBuilder<QueryStringQue
         out.writeOptionalString(this.analyzer);
         out.writeOptionalString(this.analyzer);
         out.writeOptionalString(this.quoteAnalyzer);
         out.writeOptionalString(this.quoteAnalyzer);
         out.writeOptionalString(this.quoteFieldSuffix);
         out.writeOptionalString(this.quoteFieldSuffix);
-        out.writeBoolean(this.autoGeneratePhraseQueries);
+        if (out.getVersion().before(Version.V_6_0_0_beta1)) {
+            out.writeBoolean(false); // auto_generate_phrase_query
+        }
         out.writeOptionalBoolean(this.allowLeadingWildcard);
         out.writeOptionalBoolean(this.allowLeadingWildcard);
         out.writeOptionalBoolean(this.analyzeWildcard);
         out.writeOptionalBoolean(this.analyzeWildcard);
         if (out.getVersion().before(Version.V_5_1_1)) {
         if (out.getVersion().before(Version.V_5_1_1)) {
@@ -268,8 +275,13 @@ public class QueryStringQueryBuilder extends AbstractQueryBuilder<QueryStringQue
         out.writeVInt(this.fuzzyMaxExpansions);
         out.writeVInt(this.fuzzyMaxExpansions);
         out.writeOptionalString(this.fuzzyRewrite);
         out.writeOptionalString(this.fuzzyRewrite);
         out.writeVInt(this.phraseSlop);
         out.writeVInt(this.phraseSlop);
-        out.writeBoolean(this.useDisMax);
-        out.writeFloat(this.tieBreaker);
+        if (out.getVersion().before(Version.V_6_0_0_beta1)) {
+            out.writeBoolean(true); // use_dismax
+            out.writeFloat(tieBreaker != null ? tieBreaker : 0.0f);
+        } else {
+            type.writeTo(out);
+            out.writeOptionalFloat(tieBreaker);
+        }
         out.writeOptionalString(this.rewrite);
         out.writeOptionalString(this.rewrite);
         out.writeOptionalString(this.minimumShouldMatch);
         out.writeOptionalString(this.minimumShouldMatch);
         out.writeOptionalBoolean(this.lenient);
         out.writeOptionalBoolean(this.lenient);
@@ -277,7 +289,9 @@ public class QueryStringQueryBuilder extends AbstractQueryBuilder<QueryStringQue
         out.writeBoolean(this.escape);
         out.writeBoolean(this.escape);
         out.writeVInt(this.maxDeterminizedStates);
         out.writeVInt(this.maxDeterminizedStates);
         if (out.getVersion().onOrAfter(Version.V_5_1_1)) {
         if (out.getVersion().onOrAfter(Version.V_5_1_1)) {
-            out.writeBoolean(this.splitOnWhitespace);
+            if (out.getVersion().before(Version.V_6_0_0_beta1)) {
+                out.writeBoolean(false); // split_on_whitespace
+            }
             out.writeOptionalBoolean(this.useAllFields);
             out.writeOptionalBoolean(this.useAllFields);
         }
         }
     }
     }
@@ -345,16 +359,26 @@ public class QueryStringQueryBuilder extends AbstractQueryBuilder<QueryStringQue
     }
     }
 
 
     /**
     /**
-     * When more than one field is used with the query string, should queries be combined using
-     * dis max, or boolean query. Defaults to dis max (<tt>true</tt>).
+     * @param type Sets how multiple fields should be combined to build textual part queries.
      */
      */
+    public void type(MultiMatchQueryBuilder.Type type) {
+        this.type = type;
+    }
+
+    /**
+     * Use {@link QueryStringQueryBuilder#tieBreaker} instead.
+     */
+    @Deprecated
     public QueryStringQueryBuilder useDisMax(boolean useDisMax) {
     public QueryStringQueryBuilder useDisMax(boolean useDisMax) {
-        this.useDisMax = useDisMax;
         return this;
         return this;
     }
     }
 
 
+    /**
+     * Use {@link QueryStringQueryBuilder#tieBreaker} instead.
+     */
+    @Deprecated
     public boolean useDisMax() {
     public boolean useDisMax() {
-        return this.useDisMax;
+        return true;
     }
     }
 
 
     /**
     /**
@@ -408,21 +432,19 @@ public class QueryStringQueryBuilder extends AbstractQueryBuilder<QueryStringQue
     }
     }
 
 
     /**
     /**
-     * Set to true if phrase queries will be automatically generated
-     * when the analyzer returns more than one term from whitespace
-     * delimited text.
-     * NOTE: this behavior may not be suitable for all languages.
-     * <p>
-     * Set to false if phrase queries should only be generated when
-     * surrounded by double quotes.
+     * This setting is ignored
      */
      */
+    @Deprecated
     public QueryStringQueryBuilder autoGeneratePhraseQueries(boolean autoGeneratePhraseQueries) {
     public QueryStringQueryBuilder autoGeneratePhraseQueries(boolean autoGeneratePhraseQueries) {
-        this.autoGeneratePhraseQueries = autoGeneratePhraseQueries;
         return this;
         return this;
     }
     }
 
 
+    /**
+     * This setting is ignored
+     */
+    @Deprecated
     public boolean autoGeneratePhraseQueries() {
     public boolean autoGeneratePhraseQueries() {
-        return this.autoGeneratePhraseQueries;
+        return false;
     }
     }
 
 
     /**
     /**
@@ -609,16 +631,19 @@ public class QueryStringQueryBuilder extends AbstractQueryBuilder<QueryStringQue
     }
     }
 
 
     /**
     /**
-     * Whether query text should be split on whitespace prior to analysis.
-     * Default is <code>{@value #DEFAULT_SPLIT_ON_WHITESPACE}</code>.
+     * This setting is ignored, this query parser splits on operator only.
      */
      */
+    @Deprecated
     public QueryStringQueryBuilder splitOnWhitespace(boolean value) {
     public QueryStringQueryBuilder splitOnWhitespace(boolean value) {
-        this.splitOnWhitespace = value;
         return this;
         return this;
     }
     }
 
 
+    /**
+     * This setting is ignored, this query parser splits on operator only.
+     */
+    @Deprecated
     public boolean splitOnWhitespace() {
     public boolean splitOnWhitespace() {
-        return splitOnWhitespace;
+        return false;
     }
     }
 
 
     @Override
     @Override
@@ -633,8 +658,12 @@ public class QueryStringQueryBuilder extends AbstractQueryBuilder<QueryStringQue
             builder.value(fieldEntry.getKey() + "^" + fieldEntry.getValue());
             builder.value(fieldEntry.getKey() + "^" + fieldEntry.getValue());
         }
         }
         builder.endArray();
         builder.endArray();
-        builder.field(USE_DIS_MAX_FIELD.getPreferredName(), this.useDisMax);
-        builder.field(TIE_BREAKER_FIELD.getPreferredName(), this.tieBreaker);
+        if (this.type != null) {
+            builder.field(TYPE_FIELD.getPreferredName(), type.toString().toLowerCase(Locale.ENGLISH));
+        }
+        if (tieBreaker != null) {
+            builder.field(TIE_BREAKER_FIELD.getPreferredName(), this.tieBreaker);
+        }
         builder.field(DEFAULT_OPERATOR_FIELD.getPreferredName(),
         builder.field(DEFAULT_OPERATOR_FIELD.getPreferredName(),
                 this.defaultOperator.name().toLowerCase(Locale.ROOT));
                 this.defaultOperator.name().toLowerCase(Locale.ROOT));
         if (this.analyzer != null) {
         if (this.analyzer != null) {
@@ -643,7 +672,6 @@ public class QueryStringQueryBuilder extends AbstractQueryBuilder<QueryStringQue
         if (this.quoteAnalyzer != null) {
         if (this.quoteAnalyzer != null) {
             builder.field(QUOTE_ANALYZER_FIELD.getPreferredName(), this.quoteAnalyzer);
             builder.field(QUOTE_ANALYZER_FIELD.getPreferredName(), this.quoteAnalyzer);
         }
         }
-        builder.field(AUTO_GENERATE_PHRASE_QUERIES_FIELD.getPreferredName(), this.autoGeneratePhraseQueries);
         builder.field(MAX_DETERMINIZED_STATES_FIELD.getPreferredName(), this.maxDeterminizedStates);
         builder.field(MAX_DETERMINIZED_STATES_FIELD.getPreferredName(), this.maxDeterminizedStates);
         if (this.allowLeadingWildcard != null) {
         if (this.allowLeadingWildcard != null) {
             builder.field(ALLOW_LEADING_WILDCARD_FIELD.getPreferredName(), this.allowLeadingWildcard);
             builder.field(ALLOW_LEADING_WILDCARD_FIELD.getPreferredName(), this.allowLeadingWildcard);
@@ -675,7 +703,6 @@ public class QueryStringQueryBuilder extends AbstractQueryBuilder<QueryStringQue
             builder.field(TIME_ZONE_FIELD.getPreferredName(), this.timeZone.getID());
             builder.field(TIME_ZONE_FIELD.getPreferredName(), this.timeZone.getID());
         }
         }
         builder.field(ESCAPE_FIELD.getPreferredName(), this.escape);
         builder.field(ESCAPE_FIELD.getPreferredName(), this.escape);
-        builder.field(SPLIT_ON_WHITESPACE.getPreferredName(), this.splitOnWhitespace);
         if (this.useAllFields != null) {
         if (this.useAllFields != null) {
             builder.field(ALL_FIELDS_FIELD.getPreferredName(), this.useAllFields);
             builder.field(ALL_FIELDS_FIELD.getPreferredName(), this.useAllFields);
         }
         }
@@ -692,15 +719,14 @@ public class QueryStringQueryBuilder extends AbstractQueryBuilder<QueryStringQue
         String quoteAnalyzer = null;
         String quoteAnalyzer = null;
         String queryName = null;
         String queryName = null;
         float boost = AbstractQueryBuilder.DEFAULT_BOOST;
         float boost = AbstractQueryBuilder.DEFAULT_BOOST;
-        boolean autoGeneratePhraseQueries = QueryStringQueryBuilder.DEFAULT_AUTO_GENERATE_PHRASE_QUERIES;
         int maxDeterminizedStates = QueryStringQueryBuilder.DEFAULT_MAX_DETERMINED_STATES;
         int maxDeterminizedStates = QueryStringQueryBuilder.DEFAULT_MAX_DETERMINED_STATES;
         boolean enablePositionIncrements = QueryStringQueryBuilder.DEFAULT_ENABLE_POSITION_INCREMENTS;
         boolean enablePositionIncrements = QueryStringQueryBuilder.DEFAULT_ENABLE_POSITION_INCREMENTS;
         boolean escape = QueryStringQueryBuilder.DEFAULT_ESCAPE;
         boolean escape = QueryStringQueryBuilder.DEFAULT_ESCAPE;
-        boolean useDisMax = QueryStringQueryBuilder.DEFAULT_USE_DIS_MAX;
         int fuzzyPrefixLength = QueryStringQueryBuilder.DEFAULT_FUZZY_PREFIX_LENGTH;
         int fuzzyPrefixLength = QueryStringQueryBuilder.DEFAULT_FUZZY_PREFIX_LENGTH;
         int fuzzyMaxExpansions = QueryStringQueryBuilder.DEFAULT_FUZZY_MAX_EXPANSIONS;
         int fuzzyMaxExpansions = QueryStringQueryBuilder.DEFAULT_FUZZY_MAX_EXPANSIONS;
         int phraseSlop = QueryStringQueryBuilder.DEFAULT_PHRASE_SLOP;
         int phraseSlop = QueryStringQueryBuilder.DEFAULT_PHRASE_SLOP;
-        float tieBreaker = QueryStringQueryBuilder.DEFAULT_TIE_BREAKER;
+        MultiMatchQueryBuilder.Type type = DEFAULT_TYPE;
+        Float tieBreaker = null;
         Boolean analyzeWildcard = null;
         Boolean analyzeWildcard = null;
         Boolean allowLeadingWildcard = null;
         Boolean allowLeadingWildcard = null;
         String minimumShouldMatch = null;
         String minimumShouldMatch = null;
@@ -711,7 +737,6 @@ public class QueryStringQueryBuilder extends AbstractQueryBuilder<QueryStringQue
         Fuzziness fuzziness = QueryStringQueryBuilder.DEFAULT_FUZZINESS;
         Fuzziness fuzziness = QueryStringQueryBuilder.DEFAULT_FUZZINESS;
         String fuzzyRewrite = null;
         String fuzzyRewrite = null;
         String rewrite = null;
         String rewrite = null;
-        boolean splitOnWhitespace = DEFAULT_SPLIT_ON_WHITESPACE;
         Boolean useAllFields = null;
         Boolean useAllFields = null;
         Map<String, Float> fieldsAndWeights = new HashMap<>();
         Map<String, Float> fieldsAndWeights = new HashMap<>();
         while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) {
         while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) {
@@ -754,18 +779,12 @@ public class QueryStringQueryBuilder extends AbstractQueryBuilder<QueryStringQue
                     quoteAnalyzer = parser.text();
                     quoteAnalyzer = parser.text();
                 } else if (ALLOW_LEADING_WILDCARD_FIELD.match(currentFieldName)) {
                 } else if (ALLOW_LEADING_WILDCARD_FIELD.match(currentFieldName)) {
                     allowLeadingWildcard = parser.booleanValue();
                     allowLeadingWildcard = parser.booleanValue();
-                } else if (AUTO_GENERATE_PHRASE_QUERIES_FIELD.match(currentFieldName)) {
-                    autoGeneratePhraseQueries = parser.booleanValue();
                 } else if (MAX_DETERMINIZED_STATES_FIELD.match(currentFieldName)) {
                 } else if (MAX_DETERMINIZED_STATES_FIELD.match(currentFieldName)) {
                     maxDeterminizedStates = parser.intValue();
                     maxDeterminizedStates = parser.intValue();
-                } else if (LOWERCASE_EXPANDED_TERMS_FIELD.match(currentFieldName)) {
-                    // ignore, deprecated setting
                 } else if (ENABLE_POSITION_INCREMENTS_FIELD.match(currentFieldName)) {
                 } else if (ENABLE_POSITION_INCREMENTS_FIELD.match(currentFieldName)) {
                     enablePositionIncrements = parser.booleanValue();
                     enablePositionIncrements = parser.booleanValue();
                 } else if (ESCAPE_FIELD.match(currentFieldName)) {
                 } else if (ESCAPE_FIELD.match(currentFieldName)) {
                     escape = parser.booleanValue();
                     escape = parser.booleanValue();
-                } else if (USE_DIS_MAX_FIELD.match(currentFieldName)) {
-                    useDisMax = parser.booleanValue();
                 } else if (FUZZY_PREFIX_LENGTH_FIELD.match(currentFieldName)) {
                 } else if (FUZZY_PREFIX_LENGTH_FIELD.match(currentFieldName)) {
                     fuzzyPrefixLength = parser.intValue();
                     fuzzyPrefixLength = parser.intValue();
                 } else if (FUZZY_MAX_EXPANSIONS_FIELD.match(currentFieldName)) {
                 } else if (FUZZY_MAX_EXPANSIONS_FIELD.match(currentFieldName)) {
@@ -778,6 +797,8 @@ public class QueryStringQueryBuilder extends AbstractQueryBuilder<QueryStringQue
                     fuzziness = Fuzziness.parse(parser);
                     fuzziness = Fuzziness.parse(parser);
                 } else if (AbstractQueryBuilder.BOOST_FIELD.match(currentFieldName)) {
                 } else if (AbstractQueryBuilder.BOOST_FIELD.match(currentFieldName)) {
                     boost = parser.floatValue();
                     boost = parser.floatValue();
+                } else if (TYPE_FIELD.match(currentFieldName)) {
+                    type = MultiMatchQueryBuilder.Type.parse(parser.text());
                 } else if (TIE_BREAKER_FIELD.match(currentFieldName)) {
                 } else if (TIE_BREAKER_FIELD.match(currentFieldName)) {
                     tieBreaker = parser.floatValue();
                     tieBreaker = parser.floatValue();
                 } else if (ANALYZE_WILDCARD_FIELD.match(currentFieldName)) {
                 } else if (ANALYZE_WILDCARD_FIELD.match(currentFieldName)) {
@@ -790,8 +811,6 @@ public class QueryStringQueryBuilder extends AbstractQueryBuilder<QueryStringQue
                     quoteFieldSuffix = parser.textOrNull();
                     quoteFieldSuffix = parser.textOrNull();
                 } else if (LENIENT_FIELD.match(currentFieldName)) {
                 } else if (LENIENT_FIELD.match(currentFieldName)) {
                     lenient = parser.booleanValue();
                     lenient = parser.booleanValue();
-                } else if (LOCALE_FIELD.match(currentFieldName)) {
-                    // ignore, deprecated setting
                 } else if (ALL_FIELDS_FIELD.match(currentFieldName)) {
                 } else if (ALL_FIELDS_FIELD.match(currentFieldName)) {
                     useAllFields = parser.booleanValue();
                     useAllFields = parser.booleanValue();
                 } else if (MAX_DETERMINIZED_STATES_FIELD.match(currentFieldName)) {
                 } else if (MAX_DETERMINIZED_STATES_FIELD.match(currentFieldName)) {
@@ -805,8 +824,16 @@ public class QueryStringQueryBuilder extends AbstractQueryBuilder<QueryStringQue
                     }
                     }
                 } else if (AbstractQueryBuilder.NAME_FIELD.match(currentFieldName)) {
                 } else if (AbstractQueryBuilder.NAME_FIELD.match(currentFieldName)) {
                     queryName = parser.text();
                     queryName = parser.text();
+                } else if (AUTO_GENERATE_PHRASE_QUERIES_FIELD.match(currentFieldName)) {
+                    // ignore, deprecated setting
+                } else if (LOWERCASE_EXPANDED_TERMS_FIELD.match(currentFieldName)) {
+                    // ignore, deprecated setting
+                } else if (LOCALE_FIELD.match(currentFieldName)) {
+                    // ignore, deprecated setting
+                } else if (USE_DIS_MAX_FIELD.match(currentFieldName)) {
+                    // ignore, deprecated setting
                 } else if (SPLIT_ON_WHITESPACE.match(currentFieldName)) {
                 } else if (SPLIT_ON_WHITESPACE.match(currentFieldName)) {
-                    splitOnWhitespace = parser.booleanValue();
+                    // ignore, deprecated setting
                 } else {
                 } else {
                     throw new ParsingException(parser.getTokenLocation(), "[" + QueryStringQueryBuilder.NAME +
                     throw new ParsingException(parser.getTokenLocation(), "[" + QueryStringQueryBuilder.NAME +
                             "] query does not support [" + currentFieldName + "]");
                             "] query does not support [" + currentFieldName + "]");
@@ -833,17 +860,18 @@ public class QueryStringQueryBuilder extends AbstractQueryBuilder<QueryStringQue
         queryStringQuery.analyzer(analyzer);
         queryStringQuery.analyzer(analyzer);
         queryStringQuery.quoteAnalyzer(quoteAnalyzer);
         queryStringQuery.quoteAnalyzer(quoteAnalyzer);
         queryStringQuery.allowLeadingWildcard(allowLeadingWildcard);
         queryStringQuery.allowLeadingWildcard(allowLeadingWildcard);
-        queryStringQuery.autoGeneratePhraseQueries(autoGeneratePhraseQueries);
         queryStringQuery.maxDeterminizedStates(maxDeterminizedStates);
         queryStringQuery.maxDeterminizedStates(maxDeterminizedStates);
         queryStringQuery.enablePositionIncrements(enablePositionIncrements);
         queryStringQuery.enablePositionIncrements(enablePositionIncrements);
         queryStringQuery.escape(escape);
         queryStringQuery.escape(escape);
-        queryStringQuery.useDisMax(useDisMax);
         queryStringQuery.fuzzyPrefixLength(fuzzyPrefixLength);
         queryStringQuery.fuzzyPrefixLength(fuzzyPrefixLength);
         queryStringQuery.fuzzyMaxExpansions(fuzzyMaxExpansions);
         queryStringQuery.fuzzyMaxExpansions(fuzzyMaxExpansions);
         queryStringQuery.fuzzyRewrite(fuzzyRewrite);
         queryStringQuery.fuzzyRewrite(fuzzyRewrite);
         queryStringQuery.phraseSlop(phraseSlop);
         queryStringQuery.phraseSlop(phraseSlop);
         queryStringQuery.fuzziness(fuzziness);
         queryStringQuery.fuzziness(fuzziness);
-        queryStringQuery.tieBreaker(tieBreaker);
+        queryStringQuery.type(type);
+        if (tieBreaker != null) {
+            queryStringQuery.tieBreaker(tieBreaker);
+        }
         queryStringQuery.analyzeWildcard(analyzeWildcard);
         queryStringQuery.analyzeWildcard(analyzeWildcard);
         queryStringQuery.rewrite(rewrite);
         queryStringQuery.rewrite(rewrite);
         queryStringQuery.minimumShouldMatch(minimumShouldMatch);
         queryStringQuery.minimumShouldMatch(minimumShouldMatch);
@@ -852,7 +880,6 @@ public class QueryStringQueryBuilder extends AbstractQueryBuilder<QueryStringQue
         queryStringQuery.timeZone(timeZone);
         queryStringQuery.timeZone(timeZone);
         queryStringQuery.boost(boost);
         queryStringQuery.boost(boost);
         queryStringQuery.queryName(queryName);
         queryStringQuery.queryName(queryName);
-        queryStringQuery.splitOnWhitespace(splitOnWhitespace);
         queryStringQuery.useAllFields(useAllFields);
         queryStringQuery.useAllFields(useAllFields);
         return queryStringQuery;
         return queryStringQuery;
     }
     }
@@ -871,7 +898,6 @@ public class QueryStringQueryBuilder extends AbstractQueryBuilder<QueryStringQue
                 Objects.equals(analyzer, other.analyzer) &&
                 Objects.equals(analyzer, other.analyzer) &&
                 Objects.equals(quoteAnalyzer, other.quoteAnalyzer) &&
                 Objects.equals(quoteAnalyzer, other.quoteAnalyzer) &&
                 Objects.equals(quoteFieldSuffix, other.quoteFieldSuffix) &&
                 Objects.equals(quoteFieldSuffix, other.quoteFieldSuffix) &&
-                Objects.equals(autoGeneratePhraseQueries, other.autoGeneratePhraseQueries) &&
                 Objects.equals(allowLeadingWildcard, other.allowLeadingWildcard) &&
                 Objects.equals(allowLeadingWildcard, other.allowLeadingWildcard) &&
                 Objects.equals(enablePositionIncrements, other.enablePositionIncrements) &&
                 Objects.equals(enablePositionIncrements, other.enablePositionIncrements) &&
                 Objects.equals(analyzeWildcard, other.analyzeWildcard) &&
                 Objects.equals(analyzeWildcard, other.analyzeWildcard) &&
@@ -880,7 +906,7 @@ public class QueryStringQueryBuilder extends AbstractQueryBuilder<QueryStringQue
                 Objects.equals(fuzzyMaxExpansions, other.fuzzyMaxExpansions) &&
                 Objects.equals(fuzzyMaxExpansions, other.fuzzyMaxExpansions) &&
                 Objects.equals(fuzzyRewrite, other.fuzzyRewrite) &&
                 Objects.equals(fuzzyRewrite, other.fuzzyRewrite) &&
                 Objects.equals(phraseSlop, other.phraseSlop) &&
                 Objects.equals(phraseSlop, other.phraseSlop) &&
-                Objects.equals(useDisMax, other.useDisMax) &&
+                Objects.equals(type, other.type) &&
                 Objects.equals(tieBreaker, other.tieBreaker) &&
                 Objects.equals(tieBreaker, other.tieBreaker) &&
                 Objects.equals(rewrite, other.rewrite) &&
                 Objects.equals(rewrite, other.rewrite) &&
                 Objects.equals(minimumShouldMatch, other.minimumShouldMatch) &&
                 Objects.equals(minimumShouldMatch, other.minimumShouldMatch) &&
@@ -889,17 +915,16 @@ public class QueryStringQueryBuilder extends AbstractQueryBuilder<QueryStringQue
                 Objects.equals(timeZone.getID(), other.timeZone.getID()) &&
                 Objects.equals(timeZone.getID(), other.timeZone.getID()) &&
                 Objects.equals(escape, other.escape) &&
                 Objects.equals(escape, other.escape) &&
                 Objects.equals(maxDeterminizedStates, other.maxDeterminizedStates) &&
                 Objects.equals(maxDeterminizedStates, other.maxDeterminizedStates) &&
-                Objects.equals(splitOnWhitespace, other.splitOnWhitespace) &&
                 Objects.equals(useAllFields, other.useAllFields);
                 Objects.equals(useAllFields, other.useAllFields);
     }
     }
 
 
     @Override
     @Override
     protected int doHashCode() {
     protected int doHashCode() {
         return Objects.hash(queryString, defaultField, fieldsAndWeights, defaultOperator, analyzer, quoteAnalyzer,
         return Objects.hash(queryString, defaultField, fieldsAndWeights, defaultOperator, analyzer, quoteAnalyzer,
-                quoteFieldSuffix, autoGeneratePhraseQueries, allowLeadingWildcard, analyzeWildcard,
+                quoteFieldSuffix, allowLeadingWildcard, analyzeWildcard,
                 enablePositionIncrements, fuzziness, fuzzyPrefixLength,
                 enablePositionIncrements, fuzziness, fuzzyPrefixLength,
-                fuzzyMaxExpansions, fuzzyRewrite, phraseSlop, useDisMax, tieBreaker, rewrite, minimumShouldMatch, lenient,
-                timeZone == null ? 0 : timeZone.getID(), escape, maxDeterminizedStates, splitOnWhitespace, useAllFields);
+                fuzzyMaxExpansions, fuzzyRewrite, phraseSlop, type, tieBreaker, rewrite, minimumShouldMatch, lenient,
+                timeZone == null ? 0 : timeZone.getID(), escape, maxDeterminizedStates, useAllFields);
     }
     }
 
 
     /**
     /**
@@ -930,43 +955,17 @@ public class QueryStringQueryBuilder extends AbstractQueryBuilder<QueryStringQue
 
 
     @Override
     @Override
     protected Query doToQuery(QueryShardContext context) throws IOException {
     protected Query doToQuery(QueryShardContext context) throws IOException {
-        //TODO would be nice to have all the settings in one place: some change though at query execution time
-        //e.g. field names get expanded to concrete names, defaults get resolved sometimes to settings values etc.
-        if (splitOnWhitespace == false && autoGeneratePhraseQueries) {
-            throw new IllegalArgumentException("it is disallowed to disable [split_on_whitespace] " +
-                "if [auto_generate_phrase_queries] is activated");
-        }
-        QueryParserSettings qpSettings;
-        if (this.escape) {
-            qpSettings = new QueryParserSettings(org.apache.lucene.queryparser.classic.QueryParser.escape(this.queryString));
-        } else {
-            qpSettings = new QueryParserSettings(this.queryString);
-        }
-
-        Map<String, Float> resolvedFields = new TreeMap<>();
-
+        String rewrittenQueryString = escape ? org.apache.lucene.queryparser.classic.QueryParser.escape(this.queryString) : queryString;
         if ((useAllFields != null && useAllFields) && (fieldsAndWeights.size() != 0 || this.defaultField != null)) {
         if ((useAllFields != null && useAllFields) && (fieldsAndWeights.size() != 0 || this.defaultField != null)) {
             throw addValidationError("cannot use [all_fields] parameter in conjunction with [default_field] or [fields]", null);
             throw addValidationError("cannot use [all_fields] parameter in conjunction with [default_field] or [fields]", null);
         }
         }
 
 
-        // If explicitly required to use all fields, use all fields, OR:
-        // Automatically determine the fields (to replace the _all field) if all of the following are true:
-        // - The _all field is disabled,
-        // - and the default_field has not been changed in the settings
-        // - and default_field is not specified in the request
-        // - and no fields are specified in the request
-        if ((this.useAllFields != null && this.useAllFields) ||
-                (context.getMapperService().allEnabled() == false &&
-                        "_all".equals(context.defaultField()) &&
-                        this.defaultField == null &&
-                        this.fieldsAndWeights.size() == 0)) {
-            // Use the automatically determined expansion of all queryable fields
-            resolvedFields = allQueryableDefaultFields(context);
-            // Automatically set leniency to "true" if unset so mismatched fields don't cause exceptions
-            qpSettings.lenient(lenient == null ? true : lenient);
-        } else {
-            qpSettings.defaultField(this.defaultField == null ? context.defaultField() : this.defaultField);
-
+        QueryStringQueryParser queryParser;
+        boolean isLenient = lenient == null ? context.queryStringLenient() : lenient;
+        if (defaultField != null) {
+            queryParser = new QueryStringQueryParser(context, defaultField, isLenient);
+        } else if (fieldsAndWeights.size() > 0) {
+            final Map<String, Float> resolvedFields = new TreeMap<>();
             for (Map.Entry<String, Float> fieldsEntry : fieldsAndWeights.entrySet()) {
             for (Map.Entry<String, Float> fieldsEntry : fieldsAndWeights.entrySet()) {
                 String fieldName = fieldsEntry.getKey();
                 String fieldName = fieldsEntry.getKey();
                 Float weight = fieldsEntry.getValue();
                 Float weight = fieldsEntry.getValue();
@@ -978,57 +977,64 @@ public class QueryStringQueryBuilder extends AbstractQueryBuilder<QueryStringQue
                     resolvedFields.put(fieldName, weight);
                     resolvedFields.put(fieldName, weight);
                 }
                 }
             }
             }
-            qpSettings.lenient(lenient == null ? context.queryStringLenient() : lenient);
-        }
-        if (fieldsAndWeights.isEmpty() == false || resolvedFields.isEmpty() == false) {
-            // We set the fields and weight only if we have explicit fields to query
-            // Otherwise we set it to null and fallback to the default field.
-            qpSettings.fieldsAndWeights(resolvedFields);
+            queryParser = new QueryStringQueryParser(context, resolvedFields, isLenient);
+        } else {
+            // If explicitly required to use all fields, use all fields, OR:
+            // Automatically determine the fields (to replace the _all field) if all of the following are true:
+            // - The _all field is disabled,
+            // - and the default_field has not been changed in the settings
+            // - and default_field is not specified in the request
+            // - and no fields are specified in the request
+            if ((useAllFields != null && useAllFields) ||
+                    (context.getMapperService().allEnabled() == false && "_all".equals(context.defaultField()))) {
+                // Automatically determine the fields from the index mapping.
+                // Automatically set leniency to "true" if unset so mismatched fields don't cause exceptions;
+                queryParser = new QueryStringQueryParser(context, allQueryableDefaultFields(context), lenient == null ? true : lenient);
+            } else {
+                queryParser = new QueryStringQueryParser(context, context.defaultField(), isLenient);
+            }
         }
         }
-        qpSettings.defaultOperator(defaultOperator.toQueryParserOperator());
 
 
-        if (analyzer == null) {
-            qpSettings.defaultAnalyzer(context.getMapperService().searchAnalyzer());
-        } else {
+        if (analyzer != null) {
             NamedAnalyzer namedAnalyzer = context.getIndexAnalyzers().get(analyzer);
             NamedAnalyzer namedAnalyzer = context.getIndexAnalyzers().get(analyzer);
             if (namedAnalyzer == null) {
             if (namedAnalyzer == null) {
                 throw new QueryShardException(context, "[query_string] analyzer [" + analyzer + "] not found");
                 throw new QueryShardException(context, "[query_string] analyzer [" + analyzer + "] not found");
             }
             }
-            qpSettings.forceAnalyzer(namedAnalyzer);
+            queryParser.setForceAnalyzer(namedAnalyzer);
         }
         }
+
         if (quoteAnalyzer != null) {
         if (quoteAnalyzer != null) {
-            NamedAnalyzer namedAnalyzer = context.getIndexAnalyzers().get(quoteAnalyzer);
-            if (namedAnalyzer == null) {
+            NamedAnalyzer forceQuoteAnalyzer = context.getIndexAnalyzers().get(quoteAnalyzer);
+            if (forceQuoteAnalyzer == null) {
                 throw new QueryShardException(context, "[query_string] quote_analyzer [" + quoteAnalyzer + "] not found");
                 throw new QueryShardException(context, "[query_string] quote_analyzer [" + quoteAnalyzer + "] not found");
             }
             }
-            qpSettings.forceQuoteAnalyzer(namedAnalyzer);
-        } else if (analyzer != null) {
-            qpSettings.forceQuoteAnalyzer(qpSettings.analyzer());
+            queryParser.setForceQuoteAnalyzer(forceQuoteAnalyzer);
+        }
+
+        queryParser.setDefaultOperator(defaultOperator.toQueryParserOperator());
+        queryParser.setType(type);
+        if (tieBreaker != null) {
+            queryParser.setGroupTieBreaker(tieBreaker);
         } else {
         } else {
-            qpSettings.defaultQuoteAnalyzer(context.getMapperService().searchQuoteAnalyzer());
+            queryParser.setGroupTieBreaker(type.tieBreaker());
         }
         }
+        queryParser.setPhraseSlop(phraseSlop);
+        queryParser.setQuoteFieldSuffix(quoteFieldSuffix);
+        queryParser.setAllowLeadingWildcard(allowLeadingWildcard == null ?
+            context.queryStringAllowLeadingWildcard() : allowLeadingWildcard);
+        queryParser.setAnalyzeWildcard(analyzeWildcard == null ? context.queryStringAnalyzeWildcard() : analyzeWildcard);
+        queryParser.setEnablePositionIncrements(enablePositionIncrements);
+        queryParser.setFuzziness(fuzziness);
+        queryParser.setFuzzyPrefixLength(fuzzyPrefixLength);
+        queryParser.setFuzzyMaxExpansions(fuzzyMaxExpansions);
+        queryParser.setFuzzyRewriteMethod(QueryParsers.parseRewriteMethod(this.fuzzyRewrite));
+        queryParser.setMultiTermRewriteMethod(QueryParsers.parseRewriteMethod(this.rewrite));
+        queryParser.setTimeZone(timeZone);
+        queryParser.setMaxDeterminizedStates(maxDeterminizedStates);
 
 
-        qpSettings.quoteFieldSuffix(quoteFieldSuffix);
-        qpSettings.autoGeneratePhraseQueries(autoGeneratePhraseQueries);
-        qpSettings.allowLeadingWildcard(allowLeadingWildcard == null ? context.queryStringAllowLeadingWildcard() : allowLeadingWildcard);
-        qpSettings.analyzeWildcard(analyzeWildcard == null ? context.queryStringAnalyzeWildcard() : analyzeWildcard);
-        qpSettings.enablePositionIncrements(enablePositionIncrements);
-        qpSettings.fuzziness(fuzziness);
-        qpSettings.fuzzyPrefixLength(fuzzyPrefixLength);
-        qpSettings.fuzzyMaxExpansions(fuzzyMaxExpansions);
-        qpSettings.fuzzyRewriteMethod(QueryParsers.parseRewriteMethod(this.fuzzyRewrite));
-        qpSettings.phraseSlop(phraseSlop);
-        qpSettings.useDisMax(useDisMax);
-        qpSettings.tieBreaker(tieBreaker);
-        qpSettings.rewriteMethod(QueryParsers.parseRewriteMethod(this.rewrite));
-        qpSettings.timeZone(timeZone);
-        qpSettings.maxDeterminizedStates(maxDeterminizedStates);
-        qpSettings.splitOnWhitespace(splitOnWhitespace);
-
-        MapperQueryParser queryParser = context.queryParser(qpSettings);
         Query query;
         Query query;
         try {
         try {
-            query = queryParser.parse(queryString);
+            query = queryParser.parse(rewrittenQueryString);
         } catch (org.apache.lucene.queryparser.classic.ParseException e) {
         } catch (org.apache.lucene.queryparser.classic.ParseException e) {
             throw new QueryShardException(context, "Failed to parse query [" + this.queryString + "]", e);
             throw new QueryShardException(context, "Failed to parse query [" + this.queryString + "]", e);
         }
         }
@@ -1055,5 +1061,4 @@ public class QueryStringQueryBuilder extends AbstractQueryBuilder<QueryStringQue
 
 
         return query;
         return query;
     }
     }
-
 }
 }

+ 1 - 1
core/src/main/java/org/apache/lucene/queryparser/classic/ExistsFieldQueryExtension.java → core/src/main/java/org/elasticsearch/index/search/ExistsFieldQueryExtension.java

@@ -17,7 +17,7 @@
  * under the License.
  * under the License.
  */
  */
 
 
-package org.apache.lucene.queryparser.classic;
+package org.elasticsearch.index.search;
 
 
 import org.apache.lucene.index.Term;
 import org.apache.lucene.index.Term;
 import org.apache.lucene.search.Query;
 import org.apache.lucene.search.Query;

+ 1 - 1
core/src/main/java/org/apache/lucene/queryparser/classic/FieldQueryExtension.java → core/src/main/java/org/elasticsearch/index/search/FieldQueryExtension.java

@@ -17,7 +17,7 @@
  * under the License.
  * under the License.
  */
  */
 
 
-package org.apache.lucene.queryparser.classic;
+package org.elasticsearch.index.search;
 
 
 import org.apache.lucene.search.Query;
 import org.apache.lucene.search.Query;
 import org.elasticsearch.index.query.QueryShardContext;
 import org.elasticsearch.index.query.QueryShardContext;

+ 16 - 16
core/src/main/java/org/elasticsearch/index/search/MatchQuery.java

@@ -139,7 +139,7 @@ public class MatchQuery {
 
 
     protected final QueryShardContext context;
     protected final QueryShardContext context;
 
 
-    protected String analyzer;
+    protected Analyzer analyzer;
 
 
     protected BooleanClause.Occur occur = BooleanClause.Occur.SHOULD;
     protected BooleanClause.Occur occur = BooleanClause.Occur.SHOULD;
 
 
@@ -167,7 +167,14 @@ public class MatchQuery {
         this.context = context;
         this.context = context;
     }
     }
 
 
-    public void setAnalyzer(String analyzer) {
+    public void setAnalyzer(String analyzerName) {
+        this.analyzer = context.getMapperService().getIndexAnalyzers().get(analyzerName);
+        if (analyzer == null) {
+            throw new IllegalArgumentException("No analyzer found for [" + analyzerName + "]");
+        }
+    }
+
+    public void setAnalyzer(Analyzer analyzer) {
         this.analyzer = analyzer;
         this.analyzer = analyzer;
     }
     }
 
 
@@ -215,17 +222,13 @@ public class MatchQuery {
         this.zeroTermsQuery = zeroTermsQuery;
         this.zeroTermsQuery = zeroTermsQuery;
     }
     }
 
 
-    protected Analyzer getAnalyzer(MappedFieldType fieldType) {
-        if (this.analyzer == null) {
+    protected Analyzer getAnalyzer(MappedFieldType fieldType, boolean quoted) {
+        if (analyzer == null) {
             if (fieldType != null) {
             if (fieldType != null) {
-                return context.getSearchAnalyzer(fieldType);
+                return quoted ? context.getSearchQuoteAnalyzer(fieldType) : context.getSearchAnalyzer(fieldType);
             }
             }
-            return context.getMapperService().searchAnalyzer();
+            return quoted ? context.getMapperService().searchQuoteAnalyzer() : context.getMapperService().searchAnalyzer();
         } else {
         } else {
-            Analyzer analyzer = context.getMapperService().getIndexAnalyzers().get(this.analyzer);
-            if (analyzer == null) {
-                throw new IllegalArgumentException("No analyzer found for [" + this.analyzer + "]");
-            }
             return analyzer;
             return analyzer;
         }
         }
     }
     }
@@ -252,7 +255,7 @@ public class MatchQuery {
             return blendTermQuery(new Term(fieldName, value.toString()), fieldType);
             return blendTermQuery(new Term(fieldName, value.toString()), fieldType);
         }
         }
 
 
-        Analyzer analyzer = getAnalyzer(fieldType);
+        Analyzer analyzer = getAnalyzer(fieldType, type == Type.PHRASE);
         assert analyzer != null;
         assert analyzer != null;
         MatchQueryBuilder builder = new MatchQueryBuilder(analyzer, fieldType);
         MatchQueryBuilder builder = new MatchQueryBuilder(analyzer, fieldType);
         builder.setEnablePositionIncrements(this.enablePositionIncrements);
         builder.setEnablePositionIncrements(this.enablePositionIncrements);
@@ -460,7 +463,7 @@ public class MatchQuery {
                     return query;
                     return query;
                 } catch (RuntimeException e) {
                 } catch (RuntimeException e) {
                     if (lenient) {
                     if (lenient) {
-                        return new TermQuery(term);
+                        return null;
                     } else {
                     } else {
                         throw e;
                         throw e;
                     }
                     }
@@ -472,10 +475,7 @@ public class MatchQuery {
             return query;
             return query;
         }
         }
         if (fieldType != null) {
         if (fieldType != null) {
-            Query query = termQuery(fieldType, term.bytes(), lenient);
-            if (query != null) {
-                return query;
-            }
+            return termQuery(fieldType, term.bytes(), lenient);
         }
         }
         return new TermQuery(term);
         return new TermQuery(term);
     }
     }

+ 1 - 2
core/src/main/java/org/elasticsearch/index/search/MultiMatchQuery.java

@@ -89,7 +89,6 @@ public class MultiMatchQuery extends MatchQuery {
             final List<? extends Query> queries = queryBuilder.buildGroupedQueries(type, fieldNames, value, minimumShouldMatch);
             final List<? extends Query> queries = queryBuilder.buildGroupedQueries(type, fieldNames, value, minimumShouldMatch);
             result = queryBuilder.combineGrouped(queries);
             result = queryBuilder.combineGrouped(queries);
         }
         }
-        assert result != null;
         return result;
         return result;
     }
     }
 
 
@@ -160,7 +159,7 @@ public class MultiMatchQuery extends MatchQuery {
                 String name = entry.getKey();
                 String name = entry.getKey();
                 MappedFieldType fieldType = context.fieldMapper(name);
                 MappedFieldType fieldType = context.fieldMapper(name);
                 if (fieldType != null) {
                 if (fieldType != null) {
-                    Analyzer actualAnalyzer = getAnalyzer(fieldType);
+                    Analyzer actualAnalyzer = getAnalyzer(fieldType, type == MultiMatchQueryBuilder.Type.PHRASE);
                     name = fieldType.name();
                     name = fieldType.name();
                     if (!groups.containsKey(actualAnalyzer)) {
                     if (!groups.containsKey(actualAnalyzer)) {
                        groups.put(actualAnalyzer, new ArrayList<>());
                        groups.put(actualAnalyzer, new ArrayList<>());

+ 321 - 283
core/src/main/java/org/apache/lucene/queryparser/classic/MapperQueryParser.java → core/src/main/java/org/elasticsearch/index/search/QueryStringQueryParser.java

@@ -17,21 +17,24 @@
  * under the License.
  * under the License.
  */
  */
 
 
-package org.apache.lucene.queryparser.classic;
+package org.elasticsearch.index.search;
 
 
 import org.apache.lucene.analysis.Analyzer;
 import org.apache.lucene.analysis.Analyzer;
-import org.apache.lucene.analysis.miscellaneous.DisableGraphAttribute;
 import org.apache.lucene.analysis.TokenStream;
 import org.apache.lucene.analysis.TokenStream;
+import org.apache.lucene.analysis.miscellaneous.DisableGraphAttribute;
 import org.apache.lucene.analysis.tokenattributes.CharTermAttribute;
 import org.apache.lucene.analysis.tokenattributes.CharTermAttribute;
 import org.apache.lucene.analysis.tokenattributes.PositionIncrementAttribute;
 import org.apache.lucene.analysis.tokenattributes.PositionIncrementAttribute;
 import org.apache.lucene.index.Term;
 import org.apache.lucene.index.Term;
+import org.apache.lucene.queryparser.classic.ParseException;
+import org.apache.lucene.queryparser.classic.Token;
+import org.apache.lucene.queryparser.classic.XQueryParser;
 import org.apache.lucene.search.BooleanClause;
 import org.apache.lucene.search.BooleanClause;
-import org.apache.lucene.search.BooleanQuery;
 import org.apache.lucene.search.BoostQuery;
 import org.apache.lucene.search.BoostQuery;
 import org.apache.lucene.search.DisjunctionMaxQuery;
 import org.apache.lucene.search.DisjunctionMaxQuery;
 import org.apache.lucene.search.FuzzyQuery;
 import org.apache.lucene.search.FuzzyQuery;
 import org.apache.lucene.search.MatchNoDocsQuery;
 import org.apache.lucene.search.MatchNoDocsQuery;
 import org.apache.lucene.search.MultiPhraseQuery;
 import org.apache.lucene.search.MultiPhraseQuery;
+import org.apache.lucene.search.MultiTermQuery;
 import org.apache.lucene.search.PhraseQuery;
 import org.apache.lucene.search.PhraseQuery;
 import org.apache.lucene.search.Query;
 import org.apache.lucene.search.Query;
 import org.apache.lucene.search.SynonymQuery;
 import org.apache.lucene.search.SynonymQuery;
@@ -40,38 +43,38 @@ import org.apache.lucene.search.spans.SpanOrQuery;
 import org.apache.lucene.search.spans.SpanQuery;
 import org.apache.lucene.search.spans.SpanQuery;
 import org.apache.lucene.util.BytesRef;
 import org.apache.lucene.util.BytesRef;
 import org.apache.lucene.util.IOUtils;
 import org.apache.lucene.util.IOUtils;
-import org.apache.lucene.util.automaton.RegExp;
 import org.elasticsearch.common.lucene.search.Queries;
 import org.elasticsearch.common.lucene.search.Queries;
 import org.elasticsearch.common.unit.Fuzziness;
 import org.elasticsearch.common.unit.Fuzziness;
+import org.elasticsearch.index.analysis.ShingleTokenFilterFactory;
 import org.elasticsearch.index.mapper.AllFieldMapper;
 import org.elasticsearch.index.mapper.AllFieldMapper;
 import org.elasticsearch.index.mapper.DateFieldMapper;
 import org.elasticsearch.index.mapper.DateFieldMapper;
 import org.elasticsearch.index.mapper.MappedFieldType;
 import org.elasticsearch.index.mapper.MappedFieldType;
-import org.elasticsearch.index.mapper.MapperService;
 import org.elasticsearch.index.mapper.StringFieldType;
 import org.elasticsearch.index.mapper.StringFieldType;
+import org.elasticsearch.index.mapper.MapperService;
+import org.elasticsearch.index.query.MultiMatchQueryBuilder;
 import org.elasticsearch.index.query.QueryShardContext;
 import org.elasticsearch.index.query.QueryShardContext;
 import org.elasticsearch.index.query.support.QueryParsers;
 import org.elasticsearch.index.query.support.QueryParsers;
-import org.elasticsearch.index.analysis.ShingleTokenFilterFactory;
+import org.joda.time.DateTimeZone;
 
 
 import java.io.IOException;
 import java.io.IOException;
 import java.util.ArrayList;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Collection;
+import java.util.Collections;
 import java.util.HashMap;
 import java.util.HashMap;
 import java.util.List;
 import java.util.List;
 import java.util.Map;
 import java.util.Map;
-import java.util.Collections;
+
 import static java.util.Collections.unmodifiableMap;
 import static java.util.Collections.unmodifiableMap;
 import static org.elasticsearch.common.lucene.search.Queries.fixNegativeQueryIfNeeded;
 import static org.elasticsearch.common.lucene.search.Queries.fixNegativeQueryIfNeeded;
 
 
 /**
 /**
- * A query parser that uses the {@link MapperService} in order to build smarter
+ * A {@link XQueryParser} that uses the {@link MapperService} in order to build smarter
  * queries based on the mapping information.
  * queries based on the mapping information.
- * <p>
- * Also breaks fields with [type].[name] into a boolean query that must include the type
- * as well as the query on the name.
+ * This class uses {@link MultiMatchQuery} to build the text query around logical operators and {@link XQueryParser}
+ * to assemble the result logically.
  */
  */
-public class MapperQueryParser extends QueryParser {
-
-    public static final Map<String, FieldQueryExtension> FIELD_QUERY_EXTENSIONS;
+public class QueryStringQueryParser extends XQueryParser {
+    private static final Map<String, FieldQueryExtension> FIELD_QUERY_EXTENSIONS;
 
 
     static {
     static {
         Map<String, FieldQueryExtension> fieldQueryExtensions = new HashMap<>();
         Map<String, FieldQueryExtension> fieldQueryExtensions = new HashMap<>();
@@ -80,48 +83,172 @@ public class MapperQueryParser extends QueryParser {
     }
     }
 
 
     private final QueryShardContext context;
     private final QueryShardContext context;
+    private final Map<String, Float> fieldsAndWeights;
+    private final boolean lenient;
+
+    private final MultiMatchQuery queryBuilder;
+    private MultiMatchQueryBuilder.Type type = MultiMatchQueryBuilder.Type.BEST_FIELDS;
+    private Float groupTieBreaker;
+
+    private Analyzer forceAnalyzer;
+    private Analyzer forceQuoteAnalyzer;
+    private String quoteFieldSuffix;
+    private boolean analyzeWildcard;
+    private DateTimeZone timeZone;
+    private Fuzziness fuzziness = Fuzziness.AUTO;
+    private int fuzzyMaxExpansions = FuzzyQuery.defaultMaxExpansions;
+    private MappedFieldType currentFieldType;
+    private MultiTermQuery.RewriteMethod fuzzyRewriteMethod;
 
 
-    private QueryParserSettings settings;
+    /**
+     * @param context The query shard context.
+     * @param defaultField The default field for query terms.
+     */
+    public QueryStringQueryParser(QueryShardContext context, String defaultField) {
+        this(context, defaultField, Collections.emptyMap(), false, context.getMapperService().searchAnalyzer());
+    }
 
 
-    private MappedFieldType currentFieldType;
+    /**
+     * @param context The query shard context.
+     * @param defaultField The default field for query terms.
+     * @param lenient If set to `true` will cause format based failures (like providing text to a numeric field) to be ignored.
+     */
+    public QueryStringQueryParser(QueryShardContext context, String defaultField, boolean lenient) {
+        this(context, defaultField, Collections.emptyMap(), lenient, context.getMapperService().searchAnalyzer());
+    }
+
+    /**
+     * @param context The query shard context
+     * @param fieldsAndWeights The default fields and weights expansion for query terms
+     */
+    public QueryStringQueryParser(QueryShardContext context, Map<String, Float> fieldsAndWeights) {
+        this(context, null, fieldsAndWeights, false, context.getMapperService().searchAnalyzer());
+    }
 
 
-    public MapperQueryParser(QueryShardContext context) {
-        super(null, null);
+    /**
+     * @param context The query shard context.
+     * @param fieldsAndWeights The default fields and weights expansion for query terms.
+     * @param lenient If set to `true` will cause format based failures (like providing text to a numeric field) to be ignored.
+     */
+    public QueryStringQueryParser(QueryShardContext context, Map<String, Float> fieldsAndWeights, boolean lenient) {
+        this(context, null, fieldsAndWeights, lenient, context.getMapperService().searchAnalyzer());
+    }
+
+    private QueryStringQueryParser(QueryShardContext context, String defaultField, Map<String, Float> fieldsAndWeights,
+                                   boolean lenient, Analyzer analyzer) {
+        super(defaultField, analyzer);
         this.context = context;
         this.context = context;
+        this.fieldsAndWeights = Collections.unmodifiableMap(fieldsAndWeights);
+        this.queryBuilder = new MultiMatchQuery(context);
+        queryBuilder.setLenient(lenient);
+        this.lenient = lenient;
     }
     }
 
 
-    public void reset(QueryParserSettings settings) {
-        this.settings = settings;
-        if (settings.fieldsAndWeights() == null) {
-            // this query has no explicit fields to query so we fallback to the default field
-            this.field = settings.defaultField();
-        } else if (settings.fieldsAndWeights().size() == 1) {
-            this.field = settings.fieldsAndWeights().keySet().iterator().next();
-        } else {
-            this.field = null;
-        }
-        setAnalyzer(settings.analyzer());
-        setMultiTermRewriteMethod(settings.rewriteMethod());
-        setEnablePositionIncrements(settings.enablePositionIncrements());
-        setSplitOnWhitespace(settings.splitOnWhitespace());
-        setAutoGeneratePhraseQueries(settings.autoGeneratePhraseQueries());
-        setMaxDeterminizedStates(settings.maxDeterminizedStates());
-        setAllowLeadingWildcard(settings.allowLeadingWildcard());
-        setPhraseSlop(settings.phraseSlop());
-        setDefaultOperator(settings.defaultOperator());
-        setFuzzyPrefixLength(settings.fuzzyPrefixLength());
+    @Override
+    public void setDefaultOperator(Operator op) {
+        super.setDefaultOperator(op);
+        queryBuilder.setOccur(op == Operator.AND ? BooleanClause.Occur.MUST : BooleanClause.Occur.SHOULD);
     }
     }
 
 
     /**
     /**
-     * We override this one so we can get the fuzzy part to be treated as string,
-     * so people can do: "age:10~5" or "timestamp:2012-10-10~5d"
+     * @param type Sets how multiple fields should be combined to build textual part queries.
      */
      */
-    @Override
-    Query handleBareFuzzy(String qfield, Token fuzzySlop, String termImage) throws ParseException {
-        if (fuzzySlop.image.length() == 1) {
-            return getFuzzyQuery(qfield, termImage, Float.toString(settings.fuzziness().asDistance(termImage)));
+    public void setType(MultiMatchQueryBuilder.Type type) {
+        this.type = type;
+    }
+
+    /**
+     * @param fuzziness Sets the default {@link Fuzziness} for fuzzy query.
+     * Defaults to {@link Fuzziness#AUTO}.
+     */
+    public void setFuzziness(Fuzziness fuzziness) {
+        this.fuzziness = fuzziness;
+    }
+
+    /**
+     * @param fuzzyRewriteMethod Sets the default rewrite method for fuzzy query.
+     */
+    public void setFuzzyRewriteMethod(MultiTermQuery.RewriteMethod fuzzyRewriteMethod) {
+        this.fuzzyRewriteMethod = fuzzyRewriteMethod;
+    }
+
+    /**
+     * @param fuzzyMaxExpansions Sets the maximum number of expansions allowed in a fuzzy query.
+     * Defaults to {@link FuzzyQuery#defaultMaxExpansions}.
+     */
+    public void setFuzzyMaxExpansions(int fuzzyMaxExpansions) {
+        this.fuzzyMaxExpansions = fuzzyMaxExpansions;
+    }
+
+    /**
+     * @param analyzer Force the provided analyzer to be used for all query analysis regardless of the field.
+     */
+    public void setForceAnalyzer(Analyzer analyzer) {
+        this.forceAnalyzer = analyzer;
+    }
+
+    /**
+     * @param analyzer Force the provided analyzer to be used for all phrase query analysis regardless of the field.
+     */
+    public void setForceQuoteAnalyzer(Analyzer analyzer) {
+        this.forceQuoteAnalyzer = analyzer;
+    }
+
+    /**
+     * @param quoteFieldSuffix The suffix to append to fields for quoted parts of the query string.
+     */
+    public void setQuoteFieldSuffix(String quoteFieldSuffix) {
+        this.quoteFieldSuffix = quoteFieldSuffix;
+    }
+
+    /**
+     * @param analyzeWildcard If true, the wildcard operator analyzes the term to build a wildcard query.
+     *                        Otherwise the query terms are only normalized.
+     */
+    public void setAnalyzeWildcard(boolean analyzeWildcard) {
+        this.analyzeWildcard = analyzeWildcard;
+    }
+
+    /**
+     * @param timeZone Time Zone to be applied to any range query related to dates.
+     */
+    public void setTimeZone(DateTimeZone timeZone) {
+        this.timeZone = timeZone;
+    }
+
+    /**
+     * @param groupTieBreaker The tie breaker to apply when multiple fields are used.
+     */
+    public void setGroupTieBreaker(float groupTieBreaker) {
+        // Force the tie breaker in the query builder too
+        queryBuilder.setTieBreaker(groupTieBreaker);
+        this.groupTieBreaker = groupTieBreaker;
+    }
+
+    private Query applyBoost(Query q, Float boost) {
+        if (boost != null && boost != 1f) {
+            return new BoostQuery(q, boost);
+        }
+        return q;
+    }
+
+    private Map<String, Float> extractMultiFields(String field, boolean quoted) {
+        if (field != null) {
+            Collection<String> fields = queryBuilder.context.simpleMatchToIndexNames(field);
+            Map<String, Float> weights = new HashMap<>();
+            for (String fieldName : fields) {
+                Float weight = fieldsAndWeights.get(fieldName);
+                if (quoted && quoteFieldSuffix != null
+                        && queryBuilder.context.fieldMapper(fieldName + quoteFieldSuffix) != null) {
+                    fieldName = fieldName + quoteFieldSuffix;
+                    weight = fieldsAndWeights.get(fieldName);
+                }
+                weights.put(fieldName, weight == null ? 1.0f : weight);
+            }
+            return weights;
+        } else {
+            return fieldsAndWeights;
         }
         }
-        return getFuzzyQuery(qfield, termImage, fuzzySlop.image.substring(1));
     }
     }
 
 
     @Override
     @Override
@@ -144,124 +271,78 @@ public class MapperQueryParser extends QueryParser {
     public Query getFieldQuery(String field, String queryText, boolean quoted) throws ParseException {
     public Query getFieldQuery(String field, String queryText, boolean quoted) throws ParseException {
         FieldQueryExtension fieldQueryExtension = FIELD_QUERY_EXTENSIONS.get(field);
         FieldQueryExtension fieldQueryExtension = FIELD_QUERY_EXTENSIONS.get(field);
         if (fieldQueryExtension != null) {
         if (fieldQueryExtension != null) {
-            return fieldQueryExtension.query(context, queryText);
+            return fieldQueryExtension.query(queryBuilder.context, queryText);
         }
         }
-        Collection<String> fields = extractMultiFields(field);
-        if (fields != null) {
-            if (fields.size() == 1) {
-                return getFieldQuerySingle(fields.iterator().next(), queryText, quoted);
-            } else if (fields.isEmpty()) {
-                // the requested fields do not match any field in the mapping
-                // happens for wildcard fields only since we cannot expand to a valid field name
-                // if there is no match in the mappings.
-                return new MatchNoDocsQuery("empty fields");
-            }
-            float tiebreaker = settings.useDisMax() ? settings.tieBreaker() : 1.0f;
-            List<Query> queries = new ArrayList<>();
-            boolean added = false;
-            for (String mField : fields) {
-                Query q = getFieldQuerySingle(mField, queryText, quoted);
-                if (q != null) {
-                    added = true;
-                    queries.add(applyBoost(mField, q));
-                }
-            }
-            if (!added) {
-                return null;
-            }
-            return new DisjunctionMaxQuery(queries, tiebreaker);
-        } else {
-            return getFieldQuerySingle(field, queryText, quoted);
+        if (quoted) {
+            return getFieldQuery(field, queryText, getPhraseSlop());
         }
         }
-    }
 
 
-    private Query getFieldQuerySingle(String field, String queryText, boolean quoted) throws ParseException {
-        if (!quoted && queryText.length() > 1) {
-            if (queryText.charAt(0) == '>') {
-                if (queryText.length() > 2) {
-                    if (queryText.charAt(1) == '=') {
-                        return getRangeQuerySingle(field, queryText.substring(2), null, true, true, context);
+        // Detects additional operators '<', '<=', '>', '>=' to handle range query with one side unbounded.
+        // It is required to use a prefix field operator to enable the detection since they are not treated
+        // as logical operator by the query parser (e.g. age:>=10).
+        if (field != null) {
+            if (queryText.length() > 1) {
+                if (queryText.charAt(0) == '>') {
+                    if (queryText.length() > 2) {
+                        if (queryText.charAt(1) == '=') {
+                            return getRangeQuery(field, queryText.substring(2), null, true, true);
+                        }
                     }
                     }
-                }
-                return getRangeQuerySingle(field, queryText.substring(1), null, false, true, context);
-            } else if (queryText.charAt(0) == '<') {
-                if (queryText.length() > 2) {
-                    if (queryText.charAt(1) == '=') {
-                        return getRangeQuerySingle(field, null, queryText.substring(2), true, true, context);
+                    return getRangeQuery(field, queryText.substring(1), null, false, true);
+                } else if (queryText.charAt(0) == '<') {
+                    if (queryText.length() > 2) {
+                        if (queryText.charAt(1) == '=') {
+                            return getRangeQuery(field, null, queryText.substring(2), true, true);
+                        }
                     }
                     }
+                    return getRangeQuery(field, null, queryText.substring(1), true, false);
                 }
                 }
-                return getRangeQuerySingle(field, null, queryText.substring(1), true, false, context);
             }
             }
         }
         }
-        currentFieldType = null;
-        Analyzer oldAnalyzer = getAnalyzer();
+
+        Map<String, Float> fields = extractMultiFields(field, quoted);
+        if (fields.isEmpty()) {
+            // the requested fields do not match any field in the mapping
+            // happens for wildcard fields only since we cannot expand to a valid field name
+            // if there is no match in the mappings.
+            return new MatchNoDocsQuery("empty fields");
+        }
+        Analyzer oldAnalyzer = queryBuilder.analyzer;
         try {
         try {
-            if (quoted) {
-                setAnalyzer(settings.quoteAnalyzer());
-                if (settings.quoteFieldSuffix() != null) {
-                    currentFieldType = context.fieldMapper(field + settings.quoteFieldSuffix());
-                }
-            }
-            if (currentFieldType == null) {
-                currentFieldType = context.fieldMapper(field);
-            }
-            if (currentFieldType != null) {
-                if (quoted) {
-                    if (!settings.forceQuoteAnalyzer()) {
-                        setAnalyzer(context.getSearchQuoteAnalyzer(currentFieldType));
-                    }
-                } else {
-                    if (!settings.forceAnalyzer()) {
-                        setAnalyzer(context.getSearchAnalyzer(currentFieldType));
-                    }
-                }
-                if (currentFieldType != null) {
-                    Query query = null;
-                    if (currentFieldType.tokenized() == false) {
-                        // this might be a structured field like a numeric
-                        try {
-                            query = currentFieldType.termQuery(queryText, context);
-                        } catch (RuntimeException e) {
-                            if (settings.lenient()) {
-                                return null;
-                            } else {
-                                throw e;
-                            }
-                        }
-                    }
-                    if (query == null) {
-                        query = super.getFieldQuery(currentFieldType.name(), queryText, quoted);
-                    }
-                    return query;
-                }
+            if (forceAnalyzer != null) {
+                queryBuilder.setAnalyzer(forceAnalyzer);
             }
             }
-            return super.getFieldQuery(field, queryText, quoted);
+            return queryBuilder.parse(type, fields, queryText, null);
+        } catch (IOException e) {
+            throw new ParseException(e.getMessage());
         } finally {
         } finally {
-            setAnalyzer(oldAnalyzer);
+            queryBuilder.setAnalyzer(oldAnalyzer);
         }
         }
     }
     }
 
 
     @Override
     @Override
     protected Query getFieldQuery(String field, String queryText, int slop) throws ParseException {
     protected Query getFieldQuery(String field, String queryText, int slop) throws ParseException {
-        Collection<String> fields = extractMultiFields(field);
-        if (fields != null) {
-            float tiebreaker = settings.useDisMax() ? settings.tieBreaker() : 1.0f;
-            List<Query> queries = new ArrayList<>();
-            boolean added = false;
-            for (String mField : fields) {
-                Query q = super.getFieldQuery(mField, queryText, slop);
-                if (q != null) {
-                    added = true;
-                    q = applySlop(q, slop);
-                    queries.add(applyBoost(mField, q));
-                }
-            }
-            if (!added) {
-                return null;
-            }
-            return new DisjunctionMaxQuery(queries, tiebreaker);
-        } else {
-            return super.getFieldQuery(field, queryText, slop);
+        Map<String, Float> fields = extractMultiFields(field, true);
+        if (fields.isEmpty()) {
+            return new MatchNoDocsQuery("empty fields");
+        }
+        final Query query;
+        Analyzer oldAnalyzer = queryBuilder.analyzer;
+        int oldSlop = queryBuilder.phraseSlop;
+        try {
+            if (forceQuoteAnalyzer != null) {
+                queryBuilder.setAnalyzer(forceQuoteAnalyzer);
+            } else if (forceAnalyzer != null) {
+                queryBuilder.setAnalyzer(forceAnalyzer);
+            }
+            queryBuilder.setPhraseSlop(slop);
+            query = queryBuilder.parse(MultiMatchQueryBuilder.Type.PHRASE, fields, queryText, null);
+            return applySlop(query, slop);
+        } catch (IOException e) {
+            throw new ParseException(e.getMessage());
+        } finally {
+            queryBuilder.setAnalyzer(oldAnalyzer);
+            queryBuilder.setPhraseSlop(oldSlop);
         }
         }
     }
     }
 
 
@@ -275,51 +356,46 @@ public class MapperQueryParser extends QueryParser {
             part2 = null;
             part2 = null;
         }
         }
 
 
-        Collection<String> fields = extractMultiFields(field);
-
+        Map<String, Float> fields = extractMultiFields(field, false);
         if (fields == null) {
         if (fields == null) {
-            return getRangeQuerySingle(field, part1, part2, startInclusive, endInclusive, context);
-        }
-
-
-        if (fields.size() == 1) {
-            return getRangeQuerySingle(fields.iterator().next(), part1, part2, startInclusive, endInclusive, context);
+            return getRangeQuerySingle(field, part1, part2, startInclusive, endInclusive, queryBuilder.context);
         }
         }
 
 
-        float tiebreaker = settings.useDisMax() ? settings.tieBreaker() : 1.0f;
         List<Query> queries = new ArrayList<>();
         List<Query> queries = new ArrayList<>();
-        boolean added = false;
-        for (String mField : fields) {
-            Query q = getRangeQuerySingle(mField, part1, part2, startInclusive, endInclusive, context);
+        for (Map.Entry<String, Float> entry : fields.entrySet()) {
+            Query q = getRangeQuerySingle(entry.getKey(), part1, part2, startInclusive, endInclusive, context);
             if (q != null) {
             if (q != null) {
-                added = true;
-                queries.add(applyBoost(mField, q));
+                queries.add(applyBoost(q, entry.getValue()));
             }
             }
         }
         }
-        if (!added) {
+        if (queries.size() == 0) {
             return null;
             return null;
+        } else if (queries.size() == 1) {
+            return queries.get(0);
         }
         }
+        float tiebreaker = groupTieBreaker == null ? type.tieBreaker() : groupTieBreaker;
         return new DisjunctionMaxQuery(queries, tiebreaker);
         return new DisjunctionMaxQuery(queries, tiebreaker);
     }
     }
 
 
     private Query getRangeQuerySingle(String field, String part1, String part2,
     private Query getRangeQuerySingle(String field, String part1, String part2,
-            boolean startInclusive, boolean endInclusive, QueryShardContext context) {
+                                      boolean startInclusive, boolean endInclusive, QueryShardContext context) {
         currentFieldType = context.fieldMapper(field);
         currentFieldType = context.fieldMapper(field);
         if (currentFieldType != null) {
         if (currentFieldType != null) {
             try {
             try {
-                BytesRef part1Binary = part1 == null ? null : getAnalyzer().normalize(field, part1);
-                BytesRef part2Binary = part2 == null ? null : getAnalyzer().normalize(field, part2);
+                Analyzer normalizer = forceAnalyzer == null ? queryBuilder.context.getSearchAnalyzer(currentFieldType) : forceAnalyzer;
+                BytesRef part1Binary = part1 == null ? null : normalizer.normalize(field, part1);
+                BytesRef part2Binary = part2 == null ? null : normalizer.normalize(field, part2);
                 Query rangeQuery;
                 Query rangeQuery;
-                if (currentFieldType instanceof DateFieldMapper.DateFieldType && settings.timeZone() != null) {
+                if (currentFieldType instanceof DateFieldMapper.DateFieldType && timeZone != null) {
                     DateFieldMapper.DateFieldType dateFieldType = (DateFieldMapper.DateFieldType) this.currentFieldType;
                     DateFieldMapper.DateFieldType dateFieldType = (DateFieldMapper.DateFieldType) this.currentFieldType;
                     rangeQuery = dateFieldType.rangeQuery(part1Binary, part2Binary,
                     rangeQuery = dateFieldType.rangeQuery(part1Binary, part2Binary,
-                            startInclusive, endInclusive, settings.timeZone(), null, context);
+                        startInclusive, endInclusive, timeZone, null, context);
                 } else {
                 } else {
                     rangeQuery = currentFieldType.rangeQuery(part1Binary, part2Binary, startInclusive, endInclusive, context);
                     rangeQuery = currentFieldType.rangeQuery(part1Binary, part2Binary, startInclusive, endInclusive, context);
                 }
                 }
                 return rangeQuery;
                 return rangeQuery;
             } catch (RuntimeException e) {
             } catch (RuntimeException e) {
-                if (settings.lenient()) {
+                if (lenient) {
                     return null;
                     return null;
                 }
                 }
                 throw e;
                 throw e;
@@ -328,79 +404,80 @@ public class MapperQueryParser extends QueryParser {
         return newRangeQuery(field, part1, part2, startInclusive, endInclusive);
         return newRangeQuery(field, part1, part2, startInclusive, endInclusive);
     }
     }
 
 
-    protected Query getFuzzyQuery(String field, String termStr, String minSimilarity) throws ParseException {
-        Collection<String> fields = extractMultiFields(field);
-        if (fields != null) {
-            if (fields.size() == 1) {
-                return getFuzzyQuerySingle(fields.iterator().next(), termStr, minSimilarity);
-            }
-            float tiebreaker = settings.useDisMax() ? settings.tieBreaker() : 1.0f;
-            List<Query> queries = new ArrayList<>();
-            boolean added = false;
-            for (String mField : fields) {
-                Query q = getFuzzyQuerySingle(mField, termStr, minSimilarity);
-                if (q != null) {
-                    added = true;
-                    queries.add(applyBoost(mField, q));
-                }
-            }
-            if (!added) {
-                return null;
+    @Override
+    protected Query handleBareFuzzy(String field, Token fuzzySlop, String termImage) throws ParseException {
+        if (fuzzySlop.image.length() == 1) {
+            return getFuzzyQuery(field, termImage, fuzziness.asDistance(termImage));
+        }
+        return getFuzzyQuery(field, termImage, Fuzziness.build(fuzzySlop.image.substring(1)).asFloat());
+    }
+
+    @Override
+    protected Query getFuzzyQuery(String field, String termStr, float minSimilarity) throws ParseException {
+        Map<String, Float> fields = extractMultiFields(field, false);
+        float tiebreaker = groupTieBreaker == null ? type.tieBreaker() : groupTieBreaker;
+        List<Query> queries = new ArrayList<>();
+        for (Map.Entry<String, Float> entry : fields.entrySet()) {
+            Query q = getFuzzyQuerySingle(entry.getKey(), termStr, minSimilarity);
+            if (q != null) {
+                queries.add(applyBoost(q, entry.getValue()));
             }
             }
-            return new DisjunctionMaxQuery(queries, tiebreaker);
+        }
+        if (queries.size() == 0) {
+            return null;
+        } else if (queries.size() == 1) {
+            return queries.get(0);
         } else {
         } else {
-            return getFuzzyQuerySingle(field, termStr, minSimilarity);
+            return new DisjunctionMaxQuery(queries, tiebreaker);
         }
         }
     }
     }
 
 
-    private Query getFuzzyQuerySingle(String field, String termStr, String minSimilarity) throws ParseException {
+    private Query getFuzzyQuerySingle(String field, String termStr, float minSimilarity) throws ParseException {
         currentFieldType = context.fieldMapper(field);
         currentFieldType = context.fieldMapper(field);
         if (currentFieldType != null) {
         if (currentFieldType != null) {
             try {
             try {
-                BytesRef term = termStr == null ? null : getAnalyzer().normalize(field, termStr);
-                return currentFieldType.fuzzyQuery(term, Fuzziness.build(minSimilarity),
-                    getFuzzyPrefixLength(), settings.fuzzyMaxExpansions(), FuzzyQuery.defaultTranspositions);
+                Analyzer normalizer = forceAnalyzer == null ? queryBuilder.context.getSearchAnalyzer(currentFieldType) : forceAnalyzer;
+                BytesRef term = termStr == null ? null : normalizer.normalize(field, termStr);
+                return currentFieldType.fuzzyQuery(term, Fuzziness.fromEdits((int) minSimilarity),
+                    getFuzzyPrefixLength(), fuzzyMaxExpansions, FuzzyQuery.defaultTranspositions);
             } catch (RuntimeException e) {
             } catch (RuntimeException e) {
-                if (settings.lenient()) {
+                if (lenient) {
                     return null;
                     return null;
                 }
                 }
                 throw e;
                 throw e;
             }
             }
         }
         }
-        return super.getFuzzyQuery(field, termStr, Float.parseFloat(minSimilarity));
+        return super.getFuzzyQuery(field, termStr, minSimilarity);
     }
     }
 
 
     @Override
     @Override
     protected Query newFuzzyQuery(Term term, float minimumSimilarity, int prefixLength) {
     protected Query newFuzzyQuery(Term term, float minimumSimilarity, int prefixLength) {
-        String text = term.text();
-        int numEdits = FuzzyQuery.floatToEdits(minimumSimilarity, text.codePointCount(0, text.length()));
+        int numEdits = Fuzziness.build(minimumSimilarity).asDistance(term.text());
         FuzzyQuery query = new FuzzyQuery(term, numEdits, prefixLength,
         FuzzyQuery query = new FuzzyQuery(term, numEdits, prefixLength,
-            settings.fuzzyMaxExpansions(), FuzzyQuery.defaultTranspositions);
-        QueryParsers.setRewriteMethod(query, settings.fuzzyRewriteMethod());
+            fuzzyMaxExpansions, FuzzyQuery.defaultTranspositions);
+        QueryParsers.setRewriteMethod(query, fuzzyRewriteMethod);
         return query;
         return query;
     }
     }
 
 
     @Override
     @Override
     protected Query getPrefixQuery(String field, String termStr) throws ParseException {
     protected Query getPrefixQuery(String field, String termStr) throws ParseException {
-        Collection<String> fields = extractMultiFields(field);
+        Map<String, Float> fields = extractMultiFields(field, false);
         if (fields != null) {
         if (fields != null) {
-            if (fields.size() == 1) {
-                return getPrefixQuerySingle(fields.iterator().next(), termStr);
-            }
-            float tiebreaker = settings.useDisMax() ? settings.tieBreaker() : 1.0f;
+            float tiebreaker = groupTieBreaker == null ? type.tieBreaker() : groupTieBreaker;
             List<Query> queries = new ArrayList<>();
             List<Query> queries = new ArrayList<>();
-            boolean added = false;
-            for (String mField : fields) {
-                Query q = getPrefixQuerySingle(mField, termStr);
+            for (Map.Entry<String, Float> entry : fields.entrySet()) {
+                Query q = getPrefixQuerySingle(entry.getKey(), termStr);
                 if (q != null) {
                 if (q != null) {
-                    added = true;
-                    queries.add(applyBoost(mField, q));
+                    queries.add(applyBoost(q, entry.getValue()));
                 }
                 }
             }
             }
-            if (!added) {
+            if (queries.size() == 0) {
                 return null;
                 return null;
+            } else if (queries.size() == 1) {
+                return queries.get(0);
+            } else {
+                return new DisjunctionMaxQuery(queries, tiebreaker);
             }
             }
-            return new DisjunctionMaxQuery(queries, tiebreaker);
         } else {
         } else {
             return getPrefixQuerySingle(field, termStr);
             return getPrefixQuerySingle(field, termStr);
         }
         }
@@ -412,9 +489,7 @@ public class MapperQueryParser extends QueryParser {
         try {
         try {
             currentFieldType = context.fieldMapper(field);
             currentFieldType = context.fieldMapper(field);
             if (currentFieldType != null) {
             if (currentFieldType != null) {
-                if (!settings.forceAnalyzer()) {
-                    setAnalyzer(context.getSearchAnalyzer(currentFieldType));
-                }
+                setAnalyzer(forceAnalyzer == null ? queryBuilder.context.getSearchAnalyzer(currentFieldType) : forceAnalyzer);
                 Query query = null;
                 Query query = null;
                 if (currentFieldType instanceof StringFieldType == false) {
                 if (currentFieldType instanceof StringFieldType == false) {
                     query = currentFieldType.prefixQuery(termStr, getMultiTermRewriteMethod(), context);
                     query = currentFieldType.prefixQuery(termStr, getMultiTermRewriteMethod(), context);
@@ -426,7 +501,7 @@ public class MapperQueryParser extends QueryParser {
             }
             }
             return getPossiblyAnalyzedPrefixQuery(field, termStr);
             return getPossiblyAnalyzedPrefixQuery(field, termStr);
         } catch (RuntimeException e) {
         } catch (RuntimeException e) {
-            if (settings.lenient()) {
+            if (lenient) {
                 return null;
                 return null;
             }
             }
             throw e;
             throw e;
@@ -436,7 +511,7 @@ public class MapperQueryParser extends QueryParser {
     }
     }
 
 
     private Query getPossiblyAnalyzedPrefixQuery(String field, String termStr) throws ParseException {
     private Query getPossiblyAnalyzedPrefixQuery(String field, String termStr) throws ParseException {
-        if (!settings.analyzeWildcard()) {
+        if (analyzeWildcard == false) {
             return super.getPrefixQuery(field, termStr);
             return super.getPrefixQuery(field, termStr);
         }
         }
         List<List<String> > tlist;
         List<List<String> > tlist;
@@ -531,27 +606,26 @@ public class MapperQueryParser extends QueryParser {
                 actualField = this.field;
                 actualField = this.field;
             }
             }
             // effectively, we check if a field exists or not
             // effectively, we check if a field exists or not
-            return FIELD_QUERY_EXTENSIONS.get(ExistsFieldQueryExtension.NAME).query(context, actualField);
+            return FIELD_QUERY_EXTENSIONS.get(ExistsFieldQueryExtension.NAME).query(queryBuilder.context, actualField);
         }
         }
-        Collection<String> fields = extractMultiFields(field);
+
+        Map<String, Float> fields = extractMultiFields(field, false);
         if (fields != null) {
         if (fields != null) {
-            if (fields.size() == 1) {
-                return getWildcardQuerySingle(fields.iterator().next(), termStr);
-            }
-            float tiebreaker = settings.useDisMax() ? settings.tieBreaker() : 1.0f;
+            float tiebreaker = groupTieBreaker == null ? type.tieBreaker() : groupTieBreaker;
             List<Query> queries = new ArrayList<>();
             List<Query> queries = new ArrayList<>();
-            boolean added = false;
-            for (String mField : fields) {
-                Query q = getWildcardQuerySingle(mField, termStr);
+            for (Map.Entry<String, Float> entry : fields.entrySet()) {
+                Query q = getWildcardQuerySingle(entry.getKey(), termStr);
                 if (q != null) {
                 if (q != null) {
-                    added = true;
-                    queries.add(applyBoost(mField, q));
+                    queries.add(applyBoost(q, entry.getValue()));
                 }
                 }
             }
             }
-            if (!added) {
+            if (queries.size() == 0) {
                 return null;
                 return null;
+            } else if (queries.size() == 1) {
+                return queries.get(0);
+            } else {
+                return new DisjunctionMaxQuery(queries, tiebreaker);
             }
             }
-            return new DisjunctionMaxQuery(queries, tiebreaker);
         } else {
         } else {
             return getWildcardQuerySingle(field, termStr);
             return getWildcardQuerySingle(field, termStr);
         }
         }
@@ -560,22 +634,20 @@ public class MapperQueryParser extends QueryParser {
     private Query getWildcardQuerySingle(String field, String termStr) throws ParseException {
     private Query getWildcardQuerySingle(String field, String termStr) throws ParseException {
         if ("*".equals(termStr)) {
         if ("*".equals(termStr)) {
             // effectively, we check if a field exists or not
             // effectively, we check if a field exists or not
-            return FIELD_QUERY_EXTENSIONS.get(ExistsFieldQueryExtension.NAME).query(context, field);
+            return FIELD_QUERY_EXTENSIONS.get(ExistsFieldQueryExtension.NAME).query(queryBuilder.context, field);
         }
         }
         String indexedNameField = field;
         String indexedNameField = field;
         currentFieldType = null;
         currentFieldType = null;
         Analyzer oldAnalyzer = getAnalyzer();
         Analyzer oldAnalyzer = getAnalyzer();
         try {
         try {
-            currentFieldType = context.fieldMapper(field);
+            currentFieldType = queryBuilder.context.fieldMapper(field);
             if (currentFieldType != null) {
             if (currentFieldType != null) {
-                if (!settings.forceAnalyzer()) {
-                    setAnalyzer(context.getSearchAnalyzer(currentFieldType));
-                }
+                setAnalyzer(forceAnalyzer == null ? queryBuilder.context.getSearchAnalyzer(currentFieldType) : forceAnalyzer);
                 indexedNameField = currentFieldType.name();
                 indexedNameField = currentFieldType.name();
             }
             }
             return super.getWildcardQuery(indexedNameField, termStr);
             return super.getWildcardQuery(indexedNameField, termStr);
         } catch (RuntimeException e) {
         } catch (RuntimeException e) {
-            if (settings.lenient()) {
+            if (lenient) {
                 return null;
                 return null;
             }
             }
             throw e;
             throw e;
@@ -586,25 +658,23 @@ public class MapperQueryParser extends QueryParser {
 
 
     @Override
     @Override
     protected Query getRegexpQuery(String field, String termStr) throws ParseException {
     protected Query getRegexpQuery(String field, String termStr) throws ParseException {
-        Collection<String> fields = extractMultiFields(field);
+        Map<String, Float> fields = extractMultiFields(field, false);
         if (fields != null) {
         if (fields != null) {
-            if (fields.size() == 1) {
-                return getRegexpQuerySingle(fields.iterator().next(), termStr);
-            }
-            float tiebreaker = settings.useDisMax() ? settings.tieBreaker() : 1.0f;
+            float tiebreaker = groupTieBreaker == null ? type.tieBreaker() : groupTieBreaker;
             List<Query> queries = new ArrayList<>();
             List<Query> queries = new ArrayList<>();
-            boolean added = false;
-            for (String mField : fields) {
-                Query q = getRegexpQuerySingle(mField, termStr);
+            for (Map.Entry<String, Float> entry : fields.entrySet()) {
+                Query q = getRegexpQuerySingle(entry.getKey(), termStr);
                 if (q != null) {
                 if (q != null) {
-                    added = true;
-                    queries.add(applyBoost(mField, q));
+                    queries.add(applyBoost(q, entry.getValue()));
                 }
                 }
             }
             }
-            if (!added) {
+            if (queries.size() == 0) {
                 return null;
                 return null;
+            } else if (queries.size() == 1) {
+                return queries.get(0);
+            } else {
+                return new DisjunctionMaxQuery(queries, tiebreaker);
             }
             }
-            return new DisjunctionMaxQuery(queries, tiebreaker);
         } else {
         } else {
             return getRegexpQuerySingle(field, termStr);
             return getRegexpQuerySingle(field, termStr);
         }
         }
@@ -614,24 +684,15 @@ public class MapperQueryParser extends QueryParser {
         currentFieldType = null;
         currentFieldType = null;
         Analyzer oldAnalyzer = getAnalyzer();
         Analyzer oldAnalyzer = getAnalyzer();
         try {
         try {
-            currentFieldType = context.fieldMapper(field);
+            currentFieldType = queryBuilder.context.fieldMapper(field);
             if (currentFieldType != null) {
             if (currentFieldType != null) {
-                if (!settings.forceAnalyzer()) {
-                    setAnalyzer(context.getSearchAnalyzer(currentFieldType));
-                }
-                Query query = null;
-                if (currentFieldType.tokenized() == false) {
-                    query = currentFieldType.regexpQuery(termStr, RegExp.ALL,
-                        getMaxDeterminizedStates(), getMultiTermRewriteMethod(), context);
-                }
-                if (query == null) {
-                    query = super.getRegexpQuery(field, termStr);
-                }
+                setAnalyzer(forceAnalyzer == null ? queryBuilder.context.getSearchAnalyzer(currentFieldType) : forceAnalyzer);
+                Query query = super.getRegexpQuery(field, termStr);
                 return query;
                 return query;
             }
             }
             return super.getRegexpQuery(field, termStr);
             return super.getRegexpQuery(field, termStr);
         } catch (RuntimeException e) {
         } catch (RuntimeException e) {
-            if (settings.lenient()) {
+            if (lenient) {
                 return null;
                 return null;
             }
             }
             throw e;
             throw e;
@@ -640,7 +701,6 @@ public class MapperQueryParser extends QueryParser {
         }
         }
     }
     }
 
 
-
     @Override
     @Override
     protected Query getBooleanQuery(List<BooleanClause> clauses) throws ParseException {
     protected Query getBooleanQuery(List<BooleanClause> clauses) throws ParseException {
         Query q = super.getBooleanQuery(clauses);
         Query q = super.getBooleanQuery(clauses);
@@ -650,14 +710,6 @@ public class MapperQueryParser extends QueryParser {
         return fixNegativeQueryIfNeeded(q);
         return fixNegativeQueryIfNeeded(q);
     }
     }
 
 
-    private Query applyBoost(String field, Query q) {
-        Float fieldBoost = settings.fieldsAndWeights() == null ? null : settings.fieldsAndWeights().get(field);
-        if (fieldBoost != null && fieldBoost != 1f) {
-            return new BoostQuery(q, fieldBoost);
-        }
-        return q;
-    }
-
     private Query applySlop(Query q, int slop) {
     private Query applySlop(Query q, int slop) {
         if (q instanceof PhraseQuery) {
         if (q instanceof PhraseQuery) {
             //make sure that the boost hasn't been set beforehand, otherwise we'd lose it
             //make sure that the boost hasn't been set beforehand, otherwise we'd lose it
@@ -705,24 +757,10 @@ public class MapperQueryParser extends QueryParser {
         return builder.build();
         return builder.build();
     }
     }
 
 
-    private Collection<String> extractMultiFields(String field) {
-        Collection<String> fields;
-        if (field != null) {
-            fields = context.simpleMatchToIndexNames(field);
-        } else {
-            Map<String, Float> fieldsAndWeights = settings.fieldsAndWeights();
-            fields = fieldsAndWeights == null ? Collections.emptyList() : fieldsAndWeights.keySet();
-        }
-        return fields;
-    }
-
     @Override
     @Override
     public Query parse(String query) throws ParseException {
     public Query parse(String query) throws ParseException {
         if (query.trim().isEmpty()) {
         if (query.trim().isEmpty()) {
-            // if the query string is empty we return no docs / empty result
-            // the behavior is simple to change in the client if all docs is required
-            // or a default query
-            return new MatchNoDocsQuery();
+            return queryBuilder.zeroTermsQuery();
         }
         }
         return super.parse(query);
         return super.parse(query);
     }
     }

+ 2 - 12
core/src/test/java/org/elasticsearch/index/query/DisableGraphQueryTests.java

@@ -220,32 +220,22 @@ public class DisableGraphQueryTests extends ESSingleNodeTestCase {
     public void testQueryString() throws IOException {
     public void testQueryString() throws IOException {
         QueryStringQueryBuilder builder = new QueryStringQueryBuilder("foo bar baz");
         QueryStringQueryBuilder builder = new QueryStringQueryBuilder("foo bar baz");
         builder.field("text_shingle_unigram");
         builder.field("text_shingle_unigram");
-        builder.splitOnWhitespace(false);
         Query query = builder.doToQuery(shardContext);
         Query query = builder.doToQuery(shardContext);
         assertThat(expectedQueryWithUnigram, equalTo(query));
         assertThat(expectedQueryWithUnigram, equalTo(query));
 
 
         builder = new QueryStringQueryBuilder("\"foo bar baz\"");
         builder = new QueryStringQueryBuilder("\"foo bar baz\"");
         builder.field("text_shingle_unigram");
         builder.field("text_shingle_unigram");
-        builder.splitOnWhitespace(false);
         query = builder.doToQuery(shardContext);
         query = builder.doToQuery(shardContext);
-        assertThat(query, instanceOf(DisjunctionMaxQuery.class));
-        DisjunctionMaxQuery maxQuery = (DisjunctionMaxQuery) query;
-        assertThat(maxQuery.getDisjuncts().size(), equalTo(1));
-        assertThat(expectedPhraseQueryWithUnigram, equalTo(maxQuery.getDisjuncts().get(0)));
+        assertThat(expectedPhraseQueryWithUnigram, equalTo(query));
 
 
         builder = new QueryStringQueryBuilder("foo bar baz biz");
         builder = new QueryStringQueryBuilder("foo bar baz biz");
         builder.field("text_shingle");
         builder.field("text_shingle");
-        builder.splitOnWhitespace(false);
         query = builder.doToQuery(shardContext);
         query = builder.doToQuery(shardContext);
         assertThat(expectedQuery, equalTo(query));
         assertThat(expectedQuery, equalTo(query));
 
 
         builder = new QueryStringQueryBuilder("\"foo bar baz biz\"");
         builder = new QueryStringQueryBuilder("\"foo bar baz biz\"");
         builder.field("text_shingle");
         builder.field("text_shingle");
-        builder.splitOnWhitespace(false);
         query = builder.doToQuery(shardContext);
         query = builder.doToQuery(shardContext);
-        assertThat(query, instanceOf(DisjunctionMaxQuery.class));
-        maxQuery = (DisjunctionMaxQuery) query;
-        assertThat(maxQuery.getDisjuncts().size(), equalTo(1));
-        assertThat(expectedPhraseQuery, equalTo(maxQuery.getDisjuncts().get(0)));
+        assertThat(expectedPhraseQuery, equalTo(query));
     }
     }
 }
 }

+ 59 - 123
core/src/test/java/org/elasticsearch/index/query/QueryStringQueryBuilderTests.java

@@ -21,8 +21,6 @@ package org.elasticsearch.index.query;
 
 
 import org.apache.lucene.analysis.MockSynonymAnalyzer;
 import org.apache.lucene.analysis.MockSynonymAnalyzer;
 import org.apache.lucene.index.Term;
 import org.apache.lucene.index.Term;
-import org.apache.lucene.queryparser.classic.MapperQueryParser;
-import org.apache.lucene.queryparser.classic.QueryParserSettings;
 import org.apache.lucene.search.BooleanClause;
 import org.apache.lucene.search.BooleanClause;
 import org.apache.lucene.search.BooleanClause.Occur;
 import org.apache.lucene.search.BooleanClause.Occur;
 import org.apache.lucene.search.BooleanQuery;
 import org.apache.lucene.search.BooleanQuery;
@@ -43,6 +41,7 @@ import org.apache.lucene.search.TermRangeQuery;
 import org.apache.lucene.search.WildcardQuery;
 import org.apache.lucene.search.WildcardQuery;
 import org.apache.lucene.search.spans.SpanNearQuery;
 import org.apache.lucene.search.spans.SpanNearQuery;
 import org.apache.lucene.search.spans.SpanOrQuery;
 import org.apache.lucene.search.spans.SpanOrQuery;
+import org.apache.lucene.search.spans.SpanQuery;
 import org.apache.lucene.search.spans.SpanTermQuery;
 import org.apache.lucene.search.spans.SpanTermQuery;
 import org.apache.lucene.util.BytesRef;
 import org.apache.lucene.util.BytesRef;
 import org.apache.lucene.util.automaton.TooComplexToDeterminizeException;
 import org.apache.lucene.util.automaton.TooComplexToDeterminizeException;
@@ -51,10 +50,12 @@ import org.elasticsearch.action.admin.indices.mapping.put.PutMappingRequest;
 import org.elasticsearch.common.ParsingException;
 import org.elasticsearch.common.ParsingException;
 import org.elasticsearch.common.compress.CompressedXContent;
 import org.elasticsearch.common.compress.CompressedXContent;
 import org.elasticsearch.common.lucene.all.AllTermQuery;
 import org.elasticsearch.common.lucene.all.AllTermQuery;
+import org.elasticsearch.common.lucene.search.MultiPhrasePrefixQuery;
 import org.elasticsearch.common.unit.Fuzziness;
 import org.elasticsearch.common.unit.Fuzziness;
 import org.elasticsearch.common.xcontent.XContentBuilder;
 import org.elasticsearch.common.xcontent.XContentBuilder;
 import org.elasticsearch.common.xcontent.json.JsonXContent;
 import org.elasticsearch.common.xcontent.json.JsonXContent;
 import org.elasticsearch.index.mapper.MapperService;
 import org.elasticsearch.index.mapper.MapperService;
+import org.elasticsearch.index.search.QueryStringQueryParser;
 import org.elasticsearch.search.internal.SearchContext;
 import org.elasticsearch.search.internal.SearchContext;
 import org.elasticsearch.test.AbstractQueryTestCase;
 import org.elasticsearch.test.AbstractQueryTestCase;
 import org.hamcrest.Matchers;
 import org.hamcrest.Matchers;
@@ -62,7 +63,6 @@ import org.joda.time.DateTimeZone;
 
 
 import java.io.IOException;
 import java.io.IOException;
 import java.util.ArrayList;
 import java.util.ArrayList;
-import java.util.Collections;
 import java.util.List;
 import java.util.List;
 
 
 import static org.elasticsearch.index.query.AbstractQueryBuilder.parseInnerQueryBuilder;
 import static org.elasticsearch.index.query.AbstractQueryBuilder.parseInnerQueryBuilder;
@@ -160,10 +160,7 @@ public class QueryStringQueryBuilderTests extends AbstractQueryTestCase<QueryStr
         if (randomBoolean()) {
         if (randomBoolean()) {
             queryStringQueryBuilder.timeZone(randomDateTimeZone().getID());
             queryStringQueryBuilder.timeZone(randomDateTimeZone().getID());
         }
         }
-        if (queryStringQueryBuilder.autoGeneratePhraseQueries() == false) {
-            // setSplitOnWhitespace(false) is disallowed when getAutoGeneratePhraseQueries() == true
-            queryStringQueryBuilder.splitOnWhitespace(randomBoolean());
-        }
+        queryStringQueryBuilder.type(randomFrom(MultiMatchQueryBuilder.Type.values()));
         return queryStringQueryBuilder;
         return queryStringQueryBuilder;
     }
     }
 
 
@@ -175,7 +172,8 @@ public class QueryStringQueryBuilderTests extends AbstractQueryTestCase<QueryStr
         } else {
         } else {
             assertThat(query, either(instanceOf(TermQuery.class)).or(instanceOf(AllTermQuery.class))
             assertThat(query, either(instanceOf(TermQuery.class)).or(instanceOf(AllTermQuery.class))
                     .or(instanceOf(BooleanQuery.class)).or(instanceOf(DisjunctionMaxQuery.class))
                     .or(instanceOf(BooleanQuery.class)).or(instanceOf(DisjunctionMaxQuery.class))
-                    .or(instanceOf(PhraseQuery.class)));
+                    .or(instanceOf(PhraseQuery.class)).or(instanceOf(BoostQuery.class))
+                    .or(instanceOf(MultiPhrasePrefixQuery.class)).or(instanceOf(PrefixQuery.class)).or(instanceOf(SpanQuery.class)));
         }
         }
     }
     }
 
 
@@ -202,11 +200,8 @@ public class QueryStringQueryBuilderTests extends AbstractQueryTestCase<QueryStr
             .defaultField(STRING_FIELD_NAME)
             .defaultField(STRING_FIELD_NAME)
             .phraseSlop(3)
             .phraseSlop(3)
             .toQuery(createShardContext());
             .toQuery(createShardContext());
-        assertThat(query, instanceOf(DisjunctionMaxQuery.class));
-        DisjunctionMaxQuery disjunctionMaxQuery = (DisjunctionMaxQuery) query;
-        assertThat(disjunctionMaxQuery.getDisjuncts().size(), equalTo(1));
-        assertThat(disjunctionMaxQuery.getDisjuncts().get(0), instanceOf(PhraseQuery.class));
-        PhraseQuery phraseQuery = (PhraseQuery)disjunctionMaxQuery.getDisjuncts().get(0);
+        assertThat(query, instanceOf(PhraseQuery.class));
+        PhraseQuery phraseQuery = (PhraseQuery) query;
         assertThat(phraseQuery.getTerms().length, equalTo(2));
         assertThat(phraseQuery.getTerms().length, equalTo(2));
         assertThat(phraseQuery.getTerms()[0], equalTo(new Term(STRING_FIELD_NAME, "term1")));
         assertThat(phraseQuery.getTerms()[0], equalTo(new Term(STRING_FIELD_NAME, "term1")));
         assertThat(phraseQuery.getTerms()[1], equalTo(new Term(STRING_FIELD_NAME, "term2")));
         assertThat(phraseQuery.getTerms()[1], equalTo(new Term(STRING_FIELD_NAME, "term2")));
@@ -256,7 +251,6 @@ public class QueryStringQueryBuilderTests extends AbstractQueryTestCase<QueryStr
     public void testToQueryMultipleTermsBooleanQuery() throws Exception {
     public void testToQueryMultipleTermsBooleanQuery() throws Exception {
         assumeTrue("test runs only when at least a type is registered", getCurrentTypes().length > 0);
         assumeTrue("test runs only when at least a type is registered", getCurrentTypes().length > 0);
         Query query = queryStringQuery("test1 test2").field(STRING_FIELD_NAME)
         Query query = queryStringQuery("test1 test2").field(STRING_FIELD_NAME)
-            .useDisMax(false)
             .toQuery(createShardContext());
             .toQuery(createShardContext());
         assertThat(query, instanceOf(BooleanQuery.class));
         assertThat(query, instanceOf(BooleanQuery.class));
         BooleanQuery bQuery = (BooleanQuery) query;
         BooleanQuery bQuery = (BooleanQuery) query;
@@ -271,7 +265,6 @@ public class QueryStringQueryBuilderTests extends AbstractQueryTestCase<QueryStr
         assumeTrue("test runs only when at least a type is registered", getCurrentTypes().length > 0);
         assumeTrue("test runs only when at least a type is registered", getCurrentTypes().length > 0);
         Query query = queryStringQuery("test").field(STRING_FIELD_NAME)
         Query query = queryStringQuery("test").field(STRING_FIELD_NAME)
             .field(STRING_FIELD_NAME_2)
             .field(STRING_FIELD_NAME_2)
-            .useDisMax(false)
             .toQuery(createShardContext());
             .toQuery(createShardContext());
         assertThat(query, instanceOf(DisjunctionMaxQuery.class));
         assertThat(query, instanceOf(DisjunctionMaxQuery.class));
         DisjunctionMaxQuery bQuery = (DisjunctionMaxQuery) query;
         DisjunctionMaxQuery bQuery = (DisjunctionMaxQuery) query;
@@ -285,7 +278,6 @@ public class QueryStringQueryBuilderTests extends AbstractQueryTestCase<QueryStr
     public void testToQueryMultipleFieldsDisMaxQuery() throws Exception {
     public void testToQueryMultipleFieldsDisMaxQuery() throws Exception {
         assumeTrue("test runs only when at least a type is registered", getCurrentTypes().length > 0);
         assumeTrue("test runs only when at least a type is registered", getCurrentTypes().length > 0);
         Query query = queryStringQuery("test").field(STRING_FIELD_NAME).field(STRING_FIELD_NAME_2)
         Query query = queryStringQuery("test").field(STRING_FIELD_NAME).field(STRING_FIELD_NAME_2)
-            .useDisMax(true)
             .toQuery(createShardContext());
             .toQuery(createShardContext());
         assertThat(query, instanceOf(DisjunctionMaxQuery.class));
         assertThat(query, instanceOf(DisjunctionMaxQuery.class));
         DisjunctionMaxQuery disMaxQuery = (DisjunctionMaxQuery) query;
         DisjunctionMaxQuery disMaxQuery = (DisjunctionMaxQuery) query;
@@ -296,7 +288,7 @@ public class QueryStringQueryBuilderTests extends AbstractQueryTestCase<QueryStr
 
 
     public void testToQueryFieldsWildcard() throws Exception {
     public void testToQueryFieldsWildcard() throws Exception {
         assumeTrue("test runs only when at least a type is registered", getCurrentTypes().length > 0);
         assumeTrue("test runs only when at least a type is registered", getCurrentTypes().length > 0);
-        Query query = queryStringQuery("test").field("mapped_str*").useDisMax(false).toQuery(createShardContext());
+        Query query = queryStringQuery("test").field("mapped_str*").toQuery(createShardContext());
         assertThat(query, instanceOf(DisjunctionMaxQuery.class));
         assertThat(query, instanceOf(DisjunctionMaxQuery.class));
         DisjunctionMaxQuery dQuery = (DisjunctionMaxQuery) query;
         DisjunctionMaxQuery dQuery = (DisjunctionMaxQuery) query;
         assertThat(dQuery.getDisjuncts().size(), equalTo(2));
         assertThat(dQuery.getDisjuncts().size(), equalTo(2));
@@ -310,7 +302,6 @@ public class QueryStringQueryBuilderTests extends AbstractQueryTestCase<QueryStr
         assumeTrue("test runs only when at least a type is registered", getCurrentTypes().length > 0);
         assumeTrue("test runs only when at least a type is registered", getCurrentTypes().length > 0);
         Query query = queryStringQuery("test").field(STRING_FIELD_NAME, 2.2f)
         Query query = queryStringQuery("test").field(STRING_FIELD_NAME, 2.2f)
             .field(STRING_FIELD_NAME_2)
             .field(STRING_FIELD_NAME_2)
-            .useDisMax(true)
             .toQuery(createShardContext());
             .toQuery(createShardContext());
         assertThat(query, instanceOf(DisjunctionMaxQuery.class));
         assertThat(query, instanceOf(DisjunctionMaxQuery.class));
         DisjunctionMaxQuery disMaxQuery = (DisjunctionMaxQuery) query;
         DisjunctionMaxQuery disMaxQuery = (DisjunctionMaxQuery) query;
@@ -323,14 +314,10 @@ public class QueryStringQueryBuilderTests extends AbstractQueryTestCase<QueryStr
         assumeTrue("test runs only when at least a type is registered", getCurrentTypes().length > 0);
         assumeTrue("test runs only when at least a type is registered", getCurrentTypes().length > 0);
         for (Operator op : Operator.values()) {
         for (Operator op : Operator.values()) {
             BooleanClause.Occur defaultOp = op.toBooleanClauseOccur();
             BooleanClause.Occur defaultOp = op.toBooleanClauseOccur();
-            MapperQueryParser queryParser = new MapperQueryParser(createShardContext());
-            QueryParserSettings settings = new QueryParserSettings("first foo-bar-foobar* last");
-            settings.defaultField(STRING_FIELD_NAME);
-            settings.analyzeWildcard(true);
-            settings.fuzziness(Fuzziness.AUTO);
-            settings.rewriteMethod(MultiTermQuery.CONSTANT_SCORE_REWRITE);
-            settings.defaultOperator(op.toQueryParserOperator());
-            queryParser.reset(settings);
+            QueryStringQueryParser queryParser = new QueryStringQueryParser(createShardContext(), STRING_FIELD_NAME);
+            queryParser.setAnalyzeWildcard(true);
+            queryParser.setMultiTermRewriteMethod(MultiTermQuery.CONSTANT_SCORE_REWRITE);
+            queryParser.setDefaultOperator(op.toQueryParserOperator());
             Query query = queryParser.parse("first foo-bar-foobar* last");
             Query query = queryParser.parse("first foo-bar-foobar* last");
             Query expectedQuery =
             Query expectedQuery =
                 new BooleanQuery.Builder()
                 new BooleanQuery.Builder()
@@ -350,15 +337,11 @@ public class QueryStringQueryBuilderTests extends AbstractQueryTestCase<QueryStr
         assumeTrue("test runs only when at least a type is registered", getCurrentTypes().length > 0);
         assumeTrue("test runs only when at least a type is registered", getCurrentTypes().length > 0);
         for (Operator op : Operator.values()) {
         for (Operator op : Operator.values()) {
             BooleanClause.Occur defaultOp = op.toBooleanClauseOccur();
             BooleanClause.Occur defaultOp = op.toBooleanClauseOccur();
-            MapperQueryParser queryParser = new MapperQueryParser(createShardContext());
-            QueryParserSettings settings = new QueryParserSettings("first foo-bar-foobar* last");
-            settings.defaultField(STRING_FIELD_NAME);
-            settings.analyzeWildcard(true);
-            settings.fuzziness(Fuzziness.AUTO);
-            settings.rewriteMethod(MultiTermQuery.CONSTANT_SCORE_REWRITE);
-            settings.defaultOperator(op.toQueryParserOperator());
-            settings.forceAnalyzer(new MockRepeatAnalyzer());
-            queryParser.reset(settings);
+            QueryStringQueryParser queryParser = new QueryStringQueryParser(createShardContext(), STRING_FIELD_NAME);
+            queryParser.setAnalyzeWildcard(true);
+            queryParser.setMultiTermRewriteMethod(MultiTermQuery.CONSTANT_SCORE_REWRITE);
+            queryParser.setDefaultOperator(op.toQueryParserOperator());
+            queryParser.setForceAnalyzer(new MockRepeatAnalyzer());
             Query query = queryParser.parse("first foo-bar-foobar* last");
             Query query = queryParser.parse("first foo-bar-foobar* last");
 
 
             Query expectedQuery = new BooleanQuery.Builder()
             Query expectedQuery = new BooleanQuery.Builder()
@@ -387,16 +370,14 @@ public class QueryStringQueryBuilderTests extends AbstractQueryTestCase<QueryStr
         assumeTrue("test runs only when at least a type is registered", getCurrentTypes().length > 0);
         assumeTrue("test runs only when at least a type is registered", getCurrentTypes().length > 0);
         for (Operator op : Operator.values()) {
         for (Operator op : Operator.values()) {
             BooleanClause.Occur defaultOp = op.toBooleanClauseOccur();
             BooleanClause.Occur defaultOp = op.toBooleanClauseOccur();
-            MapperQueryParser queryParser = new MapperQueryParser(createShardContext());
-            QueryParserSettings settings = new QueryParserSettings("");
-            settings.defaultField(STRING_FIELD_NAME);
-            settings.fuzziness(Fuzziness.AUTO);
-            settings.analyzeWildcard(true);
-            settings.rewriteMethod(MultiTermQuery.CONSTANT_SCORE_REWRITE);
-            settings.defaultOperator(op.toQueryParserOperator());
-            settings.forceAnalyzer(new MockSynonymAnalyzer());
-            settings.forceQuoteAnalyzer(new MockSynonymAnalyzer());
-            queryParser.reset(settings);
+            QueryStringQueryParser queryParser = new QueryStringQueryParser(createShardContext(), STRING_FIELD_NAME);
+            queryParser.setAnalyzeWildcard(true);
+            queryParser.setMultiTermRewriteMethod(MultiTermQuery.CONSTANT_SCORE_REWRITE);
+            queryParser.setDefaultOperator(op.toQueryParserOperator());
+            queryParser.setAnalyzeWildcard(true);
+            queryParser.setMultiTermRewriteMethod(MultiTermQuery.CONSTANT_SCORE_REWRITE);
+            queryParser.setDefaultOperator(op.toQueryParserOperator());
+            queryParser.setForceAnalyzer(new MockSynonymAnalyzer());
 
 
             // simple multi-term
             // simple multi-term
             Query query = queryParser.parse("guinea pig");
             Query query = queryParser.parse("guinea pig");
@@ -455,7 +436,7 @@ public class QueryStringQueryBuilderTests extends AbstractQueryTestCase<QueryStr
             // span query
             // span query
             query = queryParser.parse("\"that guinea pig smells\"");
             query = queryParser.parse("\"that guinea pig smells\"");
 
 
-            SpanNearQuery nearQuery = new SpanNearQuery.Builder(STRING_FIELD_NAME, true)
+            expectedQuery = new SpanNearQuery.Builder(STRING_FIELD_NAME, true)
                 .addClause(new SpanTermQuery(new Term(STRING_FIELD_NAME, "that")))
                 .addClause(new SpanTermQuery(new Term(STRING_FIELD_NAME, "that")))
                 .addClause(
                 .addClause(
                     new SpanOrQuery(
                     new SpanOrQuery(
@@ -465,12 +446,11 @@ public class QueryStringQueryBuilderTests extends AbstractQueryTestCase<QueryStr
                         new SpanTermQuery(new Term(STRING_FIELD_NAME, "cavy"))))
                         new SpanTermQuery(new Term(STRING_FIELD_NAME, "cavy"))))
                     .addClause(new SpanTermQuery(new Term(STRING_FIELD_NAME, "smells")))
                     .addClause(new SpanTermQuery(new Term(STRING_FIELD_NAME, "smells")))
                     .build();
                     .build();
-            expectedQuery = new DisjunctionMaxQuery(Collections.singletonList(nearQuery), 1.0f);
             assertThat(query, Matchers.equalTo(expectedQuery));
             assertThat(query, Matchers.equalTo(expectedQuery));
 
 
             // span query with slop
             // span query with slop
             query = queryParser.parse("\"that guinea pig smells\"~2");
             query = queryParser.parse("\"that guinea pig smells\"~2");
-            nearQuery = new SpanNearQuery.Builder(STRING_FIELD_NAME, true)
+            expectedQuery = new SpanNearQuery.Builder(STRING_FIELD_NAME, true)
                 .addClause(new SpanTermQuery(new Term(STRING_FIELD_NAME, "that")))
                 .addClause(new SpanTermQuery(new Term(STRING_FIELD_NAME, "that")))
                 .addClause(
                 .addClause(
                     new SpanOrQuery(
                     new SpanOrQuery(
@@ -481,7 +461,6 @@ public class QueryStringQueryBuilderTests extends AbstractQueryTestCase<QueryStr
                 .addClause(new SpanTermQuery(new Term(STRING_FIELD_NAME, "smells")))
                 .addClause(new SpanTermQuery(new Term(STRING_FIELD_NAME, "smells")))
                 .setSlop(2)
                 .setSlop(2)
                 .build();
                 .build();
-            expectedQuery = new DisjunctionMaxQuery(Collections.singletonList(nearQuery), 1.0f);
             assertThat(query, Matchers.equalTo(expectedQuery));
             assertThat(query, Matchers.equalTo(expectedQuery));
         }
         }
     }
     }
@@ -678,11 +657,8 @@ public class QueryStringQueryBuilderTests extends AbstractQueryTestCase<QueryStr
         QueryStringQueryBuilder queryStringQueryBuilder =
         QueryStringQueryBuilder queryStringQueryBuilder =
             new QueryStringQueryBuilder("\"test phrase\"~2").field(STRING_FIELD_NAME, 5f);
             new QueryStringQueryBuilder("\"test phrase\"~2").field(STRING_FIELD_NAME, 5f);
         Query query = queryStringQueryBuilder.toQuery(createShardContext());
         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(query, instanceOf(BoostQuery.class));
+        BoostQuery boostQuery = (BoostQuery) query;
         assertThat(boostQuery.getBoost(), equalTo(5f));
         assertThat(boostQuery.getBoost(), equalTo(5f));
         assertThat(boostQuery.getQuery(), instanceOf(PhraseQuery.class));
         assertThat(boostQuery.getQuery(), instanceOf(PhraseQuery.class));
         PhraseQuery phraseQuery = (PhraseQuery) boostQuery.getQuery();
         PhraseQuery phraseQuery = (PhraseQuery) boostQuery.getQuery();
@@ -695,10 +671,8 @@ public class QueryStringQueryBuilderTests extends AbstractQueryTestCase<QueryStr
         QueryStringQueryBuilder queryStringQueryBuilder =
         QueryStringQueryBuilder queryStringQueryBuilder =
             new QueryStringQueryBuilder("foo bar").field("invalid*");
             new QueryStringQueryBuilder("foo bar").field("invalid*");
         Query query = queryStringQueryBuilder.toQuery(createShardContext());
         Query query = queryStringQueryBuilder.toQuery(createShardContext());
-        Query expectedQuery = new BooleanQuery.Builder()
-            .add(new MatchNoDocsQuery("empty fields"), Occur.SHOULD)
-            .add(new MatchNoDocsQuery("empty fields"), Occur.SHOULD)
-            .build();
+
+        Query expectedQuery = new MatchNoDocsQuery("empty fields");
         assertThat(expectedQuery, equalTo(query));
         assertThat(expectedQuery, equalTo(query));
 
 
         queryStringQueryBuilder =
         queryStringQueryBuilder =
@@ -709,18 +683,14 @@ public class QueryStringQueryBuilderTests extends AbstractQueryTestCase<QueryStr
             .add(new MatchNoDocsQuery("empty fields"), Occur.SHOULD)
             .add(new MatchNoDocsQuery("empty fields"), Occur.SHOULD)
             .build();
             .build();
         assertThat(expectedQuery, equalTo(query));
         assertThat(expectedQuery, equalTo(query));
-
-
     }
     }
 
 
-    public void testToQuerySplitOnWhitespace() throws IOException {
+    public void testToQueryTextParsing() throws IOException {
         assumeTrue("test runs only when at least a type is registered", getCurrentTypes().length > 0);
         assumeTrue("test runs only when at least a type is registered", getCurrentTypes().length > 0);
-        // splitOnWhitespace=false
         {
         {
             QueryStringQueryBuilder queryBuilder =
             QueryStringQueryBuilder queryBuilder =
                 new QueryStringQueryBuilder("foo bar")
                 new QueryStringQueryBuilder("foo bar")
-                    .field(STRING_FIELD_NAME).field(STRING_FIELD_NAME_2)
-                    .splitOnWhitespace(false);
+                    .field(STRING_FIELD_NAME).field(STRING_FIELD_NAME_2);
             Query query = queryBuilder.toQuery(createShardContext());
             Query query = queryBuilder.toQuery(createShardContext());
             BooleanQuery bq1 =
             BooleanQuery bq1 =
                 new BooleanQuery.Builder()
                 new BooleanQuery.Builder()
@@ -734,11 +704,29 @@ public class QueryStringQueryBuilderTests extends AbstractQueryTestCase<QueryStr
             assertThat(query, equalTo(expectedQuery));
             assertThat(query, equalTo(expectedQuery));
         }
         }
 
 
+        //  type=phrase
+        {
+            QueryStringQueryBuilder queryBuilder =
+                new QueryStringQueryBuilder("foo bar")
+                    .field(STRING_FIELD_NAME).field(STRING_FIELD_NAME_2);
+            queryBuilder.type(MultiMatchQueryBuilder.Type.PHRASE);
+            Query query = queryBuilder.toQuery(createShardContext());
+
+            List<Query> disjuncts = new ArrayList<>();
+            PhraseQuery pq = new PhraseQuery.Builder()
+                .add(new Term(STRING_FIELD_NAME, "foo"))
+                .add(new Term(STRING_FIELD_NAME, "bar"))
+                .build();
+            disjuncts.add(pq);
+            disjuncts.add(new TermQuery(new Term(STRING_FIELD_NAME_2, "foo bar")));
+            DisjunctionMaxQuery expectedQuery = new DisjunctionMaxQuery(disjuncts, 0.0f);
+            assertThat(query, equalTo(expectedQuery));
+        }
+
         {
         {
             QueryStringQueryBuilder queryBuilder =
             QueryStringQueryBuilder queryBuilder =
                 new QueryStringQueryBuilder("mapped_string:other foo bar")
                 new QueryStringQueryBuilder("mapped_string:other foo bar")
-                    .field(STRING_FIELD_NAME).field(STRING_FIELD_NAME_2)
-                    .splitOnWhitespace(false);
+                    .field(STRING_FIELD_NAME).field(STRING_FIELD_NAME_2);
             Query query = queryBuilder.toQuery(createShardContext());
             Query query = queryBuilder.toQuery(createShardContext());
             BooleanQuery bq1 =
             BooleanQuery bq1 =
                 new BooleanQuery.Builder()
                 new BooleanQuery.Builder()
@@ -760,8 +748,7 @@ public class QueryStringQueryBuilderTests extends AbstractQueryTestCase<QueryStr
         {
         {
             QueryStringQueryBuilder queryBuilder =
             QueryStringQueryBuilder queryBuilder =
                 new QueryStringQueryBuilder("foo OR bar")
                 new QueryStringQueryBuilder("foo OR bar")
-                    .field(STRING_FIELD_NAME).field(STRING_FIELD_NAME_2)
-                    .splitOnWhitespace(false);
+                    .field(STRING_FIELD_NAME).field(STRING_FIELD_NAME_2);
             Query query = queryBuilder.toQuery(createShardContext());
             Query query = queryBuilder.toQuery(createShardContext());
 
 
             List<Query> disjuncts1 = new ArrayList<>();
             List<Query> disjuncts1 = new ArrayList<>();
@@ -782,54 +769,15 @@ public class QueryStringQueryBuilderTests extends AbstractQueryTestCase<QueryStr
             assertThat(query, equalTo(expectedQuery));
             assertThat(query, equalTo(expectedQuery));
         }
         }
 
 
-        // split_on_whitespace=false breaks range query with simple syntax
+        // non-prefix queries do not work with range queries simple syntax
         {
         {
             // throws an exception when lenient is set to false
             // throws an exception when lenient is set to false
             QueryStringQueryBuilder queryBuilder =
             QueryStringQueryBuilder queryBuilder =
                 new QueryStringQueryBuilder(">10 foo")
                 new QueryStringQueryBuilder(">10 foo")
-                    .field(INT_FIELD_NAME)
-                    .splitOnWhitespace(false);
+                    .field(INT_FIELD_NAME);
             IllegalArgumentException exc =
             IllegalArgumentException exc =
                 expectThrows(IllegalArgumentException.class, () -> queryBuilder.toQuery(createShardContext()));
                 expectThrows(IllegalArgumentException.class, () -> queryBuilder.toQuery(createShardContext()));
-            assertThat(exc.getMessage(), equalTo("For input string: \"10 foo\""));
-        }
-
-        {
-            // returns an empty boolean query when lenient is set to true
-            QueryStringQueryBuilder queryBuilder =
-                new QueryStringQueryBuilder(">10 foo")
-                    .field(INT_FIELD_NAME)
-                    .splitOnWhitespace(false)
-                    .lenient(true);
-            Query query = queryBuilder.toQuery(createShardContext());
-            BooleanQuery bq = new BooleanQuery.Builder().build();
-            assertThat(bq, equalTo(query));
-        }
-
-        // splitOnWhitespace=true
-        {
-            QueryStringQueryBuilder queryBuilder =
-                new QueryStringQueryBuilder("foo bar")
-                    .field(STRING_FIELD_NAME).field(STRING_FIELD_NAME_2)
-                    .splitOnWhitespace(true);
-            Query query = queryBuilder.toQuery(createShardContext());
-
-            List<Query> disjuncts1 = new ArrayList<>();
-            disjuncts1.add(new TermQuery(new Term(STRING_FIELD_NAME, "foo")));
-            disjuncts1.add(new TermQuery(new Term(STRING_FIELD_NAME_2, "foo")));
-            DisjunctionMaxQuery maxQuery1 = new DisjunctionMaxQuery(disjuncts1, 0.0f);
-
-            List<Query> disjuncts2 = new ArrayList<>();
-            disjuncts2.add(new TermQuery(new Term(STRING_FIELD_NAME, "bar")));
-            disjuncts2.add(new TermQuery(new Term(STRING_FIELD_NAME_2, "bar")));
-            DisjunctionMaxQuery maxQuery2 = new DisjunctionMaxQuery(disjuncts2, 0.0f);
-
-            BooleanQuery expectedQuery =
-                new BooleanQuery.Builder()
-                    .add(new BooleanClause(maxQuery1, BooleanClause.Occur.SHOULD))
-                    .add(new BooleanClause(maxQuery2, BooleanClause.Occur.SHOULD))
-                    .build();
-            assertThat(query, equalTo(expectedQuery));
+            assertThat(exc.getMessage(), equalTo("For input string: \">10 foo\""));
         }
         }
     }
     }
 
 
@@ -893,10 +841,9 @@ public class QueryStringQueryBuilderTests extends AbstractQueryTestCase<QueryStr
                 "    \"query\" : \"this AND that OR thus\",\n" +
                 "    \"query\" : \"this AND that OR thus\",\n" +
                 "    \"default_field\" : \"content\",\n" +
                 "    \"default_field\" : \"content\",\n" +
                 "    \"fields\" : [ ],\n" +
                 "    \"fields\" : [ ],\n" +
-                "    \"use_dis_max\" : true,\n" +
+                "    \"type\" : \"best_fields\",\n" +
                 "    \"tie_breaker\" : 0.0,\n" +
                 "    \"tie_breaker\" : 0.0,\n" +
                 "    \"default_operator\" : \"or\",\n" +
                 "    \"default_operator\" : \"or\",\n" +
-                "    \"auto_generate_phrase_queries\" : false,\n" +
                 "    \"max_determinized_states\" : 10000,\n" +
                 "    \"max_determinized_states\" : 10000,\n" +
                 "    \"enable_position_increments\" : true,\n" +
                 "    \"enable_position_increments\" : true,\n" +
                 "    \"fuzziness\" : \"AUTO\",\n" +
                 "    \"fuzziness\" : \"AUTO\",\n" +
@@ -904,7 +851,6 @@ public class QueryStringQueryBuilderTests extends AbstractQueryTestCase<QueryStr
                 "    \"fuzzy_max_expansions\" : 50,\n" +
                 "    \"fuzzy_max_expansions\" : 50,\n" +
                 "    \"phrase_slop\" : 0,\n" +
                 "    \"phrase_slop\" : 0,\n" +
                 "    \"escape\" : false,\n" +
                 "    \"escape\" : false,\n" +
-                "    \"split_on_whitespace\" : true,\n" +
                 "    \"boost\" : 1.0\n" +
                 "    \"boost\" : 1.0\n" +
                 "  }\n" +
                 "  }\n" +
                 "}";
                 "}";
@@ -995,14 +941,4 @@ public class QueryStringQueryBuilderTests extends AbstractQueryTestCase<QueryStr
         assertThat(e.getMessage(),
         assertThat(e.getMessage(),
                 containsString("cannot use [all_fields] parameter in conjunction with [default_field] or [fields]"));
                 containsString("cannot use [all_fields] parameter in conjunction with [default_field] or [fields]"));
     }
     }
-
-    public void testInvalidCombo() throws IOException {
-        QueryStringQueryBuilder builder = new QueryStringQueryBuilder("foo bar");
-        builder.autoGeneratePhraseQueries(true);
-        builder.splitOnWhitespace(false);
-        IllegalArgumentException exc =
-            expectThrows(IllegalArgumentException.class, () -> builder.toQuery(createShardContext()));
-        assertEquals(exc.getMessage(),
-            "it is disallowed to disable [split_on_whitespace] if [auto_generate_phrase_queries] is activated");
-    }
 }
 }

+ 2 - 2
core/src/test/java/org/elasticsearch/search/fetch/subphase/highlight/HighlighterSearchIT.java

@@ -2558,8 +2558,8 @@ public class HighlighterSearchIT extends ESIntegTestCase {
 
 
         // Query string with a single field without dismax
         // Query string with a single field without dismax
         phraseBoostTestCaseForClauses(highlighterType, 100f,
         phraseBoostTestCaseForClauses(highlighterType, 100f,
-                queryStringQuery("highlight words together").field("field1").useDisMax(false),
-                queryStringQuery("\"highlight words together\"").field("field1").useDisMax(false).autoGeneratePhraseQueries(true));
+                queryStringQuery("highlight words together").field("field1"),
+                queryStringQuery("\"highlight words together\"").field("field1").autoGeneratePhraseQueries(true));
 
 
         // Query string with more than one field
         // Query string with more than one field
         phraseBoostTestCaseForClauses(highlighterType, 100f,
         phraseBoostTestCaseForClauses(highlighterType, 100f,

+ 3 - 16
core/src/test/java/org/elasticsearch/search/query/QueryStringIT.java

@@ -140,7 +140,7 @@ public class QueryStringIT extends ESIntegTestCase {
         assertHits(resp.getHits(), "1", "2");
         assertHits(resp.getHits(), "1", "2");
         assertHitCount(resp, 2L);
         assertHitCount(resp, 2L);
 
 
-        resp = client().prepareSearch("test").setQuery(queryStringQuery("127.0.0.1 1.8")).get();
+        resp = client().prepareSearch("test").setQuery(queryStringQuery("127.0.0.1 OR 1.8")).get();
         assertHits(resp.getHits(), "1", "2");
         assertHits(resp.getHits(), "1", "2");
         assertHitCount(resp, 2L);
         assertHitCount(resp, 2L);
     }
     }
@@ -201,7 +201,7 @@ public class QueryStringIT extends ESIntegTestCase {
         assertHitCount(resp, 2L);
         assertHitCount(resp, 2L);
 
 
         resp = client().prepareSearch("test")
         resp = client().prepareSearch("test")
-                .setQuery(queryStringQuery("Foo Bar").splitOnWhitespace(false))
+                .setQuery(queryStringQuery("Foo Bar"))
                 .get();
                 .get();
         assertHits(resp.getHits(), "1", "2", "3");
         assertHits(resp.getHits(), "1", "2", "3");
         assertHitCount(resp, 3L);
         assertHitCount(resp, 3L);
@@ -221,7 +221,7 @@ public class QueryStringIT extends ESIntegTestCase {
         assertHitCount(resp, 0L);
         assertHitCount(resp, 0L);
 
 
         resp = client().prepareSearch("test2").setQuery(
         resp = client().prepareSearch("test2").setQuery(
-                queryStringQuery("foo eggplant").defaultOperator(Operator.AND).useAllFields(true)).get();
+                queryStringQuery("foo eggplant").defaultOperator(Operator.OR).useAllFields(true)).get();
         assertHits(resp.getHits(), "1");
         assertHits(resp.getHits(), "1");
         assertHitCount(resp, 1L);
         assertHitCount(resp, 1L);
 
 
@@ -305,28 +305,16 @@ public class QueryStringIT extends ESIntegTestCase {
         searchResponse = client().prepareSearch(index).setQuery(
         searchResponse = client().prepareSearch(index).setQuery(
             QueryBuilders.queryStringQuery("say what the fudge")
             QueryBuilders.queryStringQuery("say what the fudge")
                 .defaultField("field")
                 .defaultField("field")
-                .splitOnWhitespace(false)
                 .defaultOperator(Operator.AND)
                 .defaultOperator(Operator.AND)
                 .analyzer("lower_graphsyns")).get();
                 .analyzer("lower_graphsyns")).get();
 
 
         assertHitCount(searchResponse, 1L);
         assertHitCount(searchResponse, 1L);
         assertSearchHits(searchResponse, "1");
         assertSearchHits(searchResponse, "1");
 
 
-        // and, split on whitespace means we should not recognize the multi-word synonym
-        searchResponse = client().prepareSearch(index).setQuery(
-            QueryBuilders.queryStringQuery("say what the fudge")
-                .defaultField("field")
-                .splitOnWhitespace(true)
-                .defaultOperator(Operator.AND)
-                .analyzer("lower_graphsyns")).get();
-
-        assertNoSearchHits(searchResponse);
-
         // or
         // or
         searchResponse = client().prepareSearch(index).setQuery(
         searchResponse = client().prepareSearch(index).setQuery(
             QueryBuilders.queryStringQuery("three what the fudge foo")
             QueryBuilders.queryStringQuery("three what the fudge foo")
                 .defaultField("field")
                 .defaultField("field")
-                .splitOnWhitespace(false)
                 .defaultOperator(Operator.OR)
                 .defaultOperator(Operator.OR)
                 .analyzer("lower_graphsyns")).get();
                 .analyzer("lower_graphsyns")).get();
 
 
@@ -337,7 +325,6 @@ public class QueryStringIT extends ESIntegTestCase {
         searchResponse = client().prepareSearch(index).setQuery(
         searchResponse = client().prepareSearch(index).setQuery(
             QueryBuilders.queryStringQuery("three what the fudge foo")
             QueryBuilders.queryStringQuery("three what the fudge foo")
                 .defaultField("field")
                 .defaultField("field")
-                .splitOnWhitespace(false)
                 .defaultOperator(Operator.OR)
                 .defaultOperator(Operator.OR)
                 .analyzer("lower_graphsyns")
                 .analyzer("lower_graphsyns")
                 .minimumShouldMatch("80%")).get();
                 .minimumShouldMatch("80%")).get();

+ 1 - 1
core/src/test/java/org/elasticsearch/search/query/SearchQueryIT.java

@@ -971,7 +971,7 @@ public class SearchQueryIT extends ESIntegTestCase {
         assertThat((double)searchResponse.getHits().getAt(0).getScore(), closeTo(boost * searchResponse.getHits().getAt(1).getScore(), .1));
         assertThat((double)searchResponse.getHits().getAt(0).getScore(), closeTo(boost * searchResponse.getHits().getAt(1).getScore(), .1));
 
 
         searchResponse = client().prepareSearch()
         searchResponse = client().prepareSearch()
-                .setQuery(queryStringQuery("\"phrase match\"").field("important", boost).field("less_important").useDisMax(false)).get();
+                .setQuery(queryStringQuery("\"phrase match\"").field("important", boost).field("less_important")).get();
         assertHitCount(searchResponse, 2L);
         assertHitCount(searchResponse, 2L);
         assertFirstHit(searchResponse, hasId("1"));
         assertFirstHit(searchResponse, hasId("1"));
         assertSecondHit(searchResponse, hasId("2"));
         assertSecondHit(searchResponse, hasId("2"));

+ 14 - 2
docs/reference/migration/migrate_6_0/search.asciidoc

@@ -56,6 +56,18 @@
 * The `percolate` query's `document_type` has been deprecated. From 6.0 and later
 * The `percolate` query's `document_type` has been deprecated. From 6.0 and later
   it is no longer required to specify the `document_type` parameter.
   it is no longer required to specify the `document_type` parameter.
 
 
+* The `split_on_whitespace` parameter for the `query_string` query has been removed.
+  If provided, it will be ignored and issue a deprecation warning.
+  The `query_string` query now splits on logical operator only.
+
+* The `use_dismax` parameter for the `query_string` query has been removed.
+  If provided, it will be ignored and issue a deprecation warning.
+  The `tie_breaker` parameter must be used instead.
+
+* The `auto_generate_phrase_queries` parameter for the `query_string` query has been removed,
+  use an explicit quoted query instead.
+  If provided, it will be ignored and issue a deprecation warning.
+
 ==== Search shards API
 ==== Search shards API
 
 
 The search shards API no longer accepts the `type` url parameter, which didn't
 The search shards API no longer accepts the `type` url parameter, which didn't
@@ -102,7 +114,7 @@ for the `random_score` function. If you really need access to the id of
 documents for sorting, aggregations or search scripts, the recommendation is
 documents for sorting, aggregations or search scripts, the recommendation is
 to duplicate the id as a field in the document.
 to duplicate the id as a field in the document.
 
 
-==== Highlighers
+==== Highlighters
 
 
 The `unified` highlighter is the new default choice for highlighter.
 The `unified` highlighter is the new default choice for highlighter.
 The offset strategy for each field is picked internally by this highlighter depending on the
 The offset strategy for each field is picked internally by this highlighter depending on the
@@ -115,4 +127,4 @@ The `unified` highlighter outputs the same highlighting when `index_options` is
 
 
 ==== `fielddata_fields`
 ==== `fielddata_fields`
 
 
-The deprecated `fielddata_fields` have now been removed. `docvalue_fields` should be used instead.
+The deprecated `fielddata_fields` have now been removed. `docvalue_fields` should be used instead.

+ 41 - 18
docs/reference/query-dsl/query-string-query.asciidoc

@@ -18,6 +18,29 @@ GET /_search
 --------------------------------------------------
 --------------------------------------------------
 // CONSOLE
 // CONSOLE
 
 
+The `query_string` query parses the input and splits text around logical operators.
+Each textual part is analyzed independently of each other. For instance the following query:
+
+[source,js]
+--------------------------------------------------
+GET /_search
+{
+    "query": {
+        "query_string" : {
+            "default_field" : "content",
+            "query" : "(new york) AND ny"
+        }
+    }
+}
+--------------------------------------------------
+// CONSOLE
+
+... will be splitted in `new york` and `ny` and each part is analyzed independently.
+When multiple fields are provided it is also possible to modify how the different
+field queries are combined inside each textual part using the `type` parameter.
+The possible modes are described <<multi-match-types, here>> and the default is `best_fields`.
+
+
 The `query_string` top level parameters include:
 The `query_string` top level parameters include:
 
 
 [cols="<,<",options="header",]
 [cols="<,<",options="header",]
@@ -84,10 +107,6 @@ the query string. This allows to use a field that has a different analysis chain
 for exact matching. Look <<mixing-exact-search-with-stemming,here>> for a
 for exact matching. Look <<mixing-exact-search-with-stemming,here>> for a
 comprehensive example.
 comprehensive example.
 
 
-|`split_on_whitespace` |Whether query text should be split on whitespace prior to analysis.
-Instead  the queryparser would parse around only real 'operators'. Default to `false`.
-It is not allowed to set this option to `false` if `auto_generate_phrase_queries` is already set to `true`.
-
 |`all_fields` | Perform the query on all fields detected in the mapping that can
 |`all_fields` | Perform the query on all fields detected in the mapping that can
 be queried. Will be used by default when the `_all` field is disabled and no
 be queried. Will be used by default when the `_all` field is disabled and no
 `default_field` is specified (either in the index settings or in the request
 `default_field` is specified (either in the index settings or in the request
@@ -156,9 +175,8 @@ GET /_search
 // CONSOLE
 // CONSOLE
 
 
 Since several queries are generated from the individual search terms,
 Since several queries are generated from the individual search terms,
-combining them can be automatically done using either a `dis_max` query or a
-simple `bool` query. For example (the `name` is boosted by 5 using `^5`
-notation):
+combining them is automatically done using a `dis_max` query with a tie_breaker.
+For example (the `name` is boosted by 5 using `^5` notation):
 
 
 [source,js]
 [source,js]
 --------------------------------------------------
 --------------------------------------------------
@@ -168,7 +186,7 @@ GET /_search
         "query_string" : {
         "query_string" : {
             "fields" : ["content", "name^5"],
             "fields" : ["content", "name^5"],
             "query" : "this AND that OR thus",
             "query" : "this AND that OR thus",
-            "use_dis_max" : true
+            "tie_breaker" : 0
         }
         }
     }
     }
 }
 }
@@ -187,8 +205,7 @@ GET /_search
     "query": {
     "query": {
         "query_string" : {
         "query_string" : {
             "fields" : ["city.*"],
             "fields" : ["city.*"],
-            "query" : "this AND that OR thus",
-            "use_dis_max" : true
+            "query" : "this AND that OR thus"
         }
         }
     }
     }
 }
 }
@@ -205,8 +222,7 @@ GET /_search
 {
 {
     "query": {
     "query": {
         "query_string" : {
         "query_string" : {
-            "query" : "city.\\*:(this AND that OR thus)",
-            "use_dis_max" : true
+            "query" : "city.\\*:(this AND that OR thus)"
         }
         }
     }
     }
 }
 }
@@ -222,11 +238,19 @@ following additional parameters are allowed:
 [cols="<,<",options="header",]
 [cols="<,<",options="header",]
 |=======================================================================
 |=======================================================================
 |Parameter |Description
 |Parameter |Description
-|`use_dis_max` |Should the queries be combined using `dis_max` (set it
-to `true`), or a `bool` query (set it to `false`). Defaults to `true`.
 
 
-|`tie_breaker` |When using `dis_max`, the disjunction max tie breaker.
-Defaults to `0`.
+|`type` |How the fields should be combined to build the text query.
+See <<multi-match-types, types>> for a complete example.
+Defaults to `best_fields`
+|=======================================================================
+
+
+[cols="<,<",options="header",]
+|=======================================================================
+|Parameter |Description
+
+|`tie_breaker` |The disjunction max tie breaker for multi fields.
+Defaults to `0`
 |=======================================================================
 |=======================================================================
 
 
 The fields parameter can also include pattern based field names,
 The fields parameter can also include pattern based field names,
@@ -240,8 +264,7 @@ GET /_search
     "query": {
     "query": {
         "query_string" : {
         "query_string" : {
             "fields" : ["content", "name.*^5"],
             "fields" : ["content", "name.*^5"],
-            "query" : "this AND that OR thus",
-            "use_dis_max" : true
+            "query" : "this AND that OR thus"
         }
         }
     }
     }
 }
 }

+ 1 - 1
modules/analysis-common/src/test/java/org/elasticsearch/analysis/common/QueryStringWithAnalyzersTests.java

@@ -65,7 +65,7 @@ public class QueryStringWithAnalyzersTests extends ESIntegTestCase {
         SearchResponse response = client()
         SearchResponse response = client()
                 .prepareSearch("test")
                 .prepareSearch("test")
                 .setQuery(
                 .setQuery(
-                        queryStringQuery("foo.baz").useDisMax(false).defaultOperator(Operator.AND)
+                        queryStringQuery("foo.baz").defaultOperator(Operator.AND)
                                 .field("field1").field("field2")).get();
                                 .field("field1").field("field2")).get();
         assertHitCount(response, 1L);
         assertHitCount(response, 1L);
     }
     }