Browse Source

Splits `phrase` and phrase_prefix` in match query into `MatchPhraseQueryBuilder` and `MatchPhrasePrefixQueryBuilder`

The `phrase` and `phrase_prefix` options in the `MatchQueryBuilder` have been deprecated in favour of using the new `MatchPhraseQueryBuilder` and `MatchPhrasePrefixQueryBuilder`. This is not a breaking change since `MatchQueryBuilder` still supports `phrase` and `phrase_prefix` but this option will be removed from the `MatchQueryBuilder` in the future (probably in 6.0)

Relates to https://github.com/elastic/elasticsearch/pull/17458#discussion_r58351998
Colin Goodheart-Smithe 9 years ago
parent
commit
84eacadd51

+ 261 - 0
core/src/main/java/org/elasticsearch/index/query/MatchPhrasePrefixQueryBuilder.java

@@ -0,0 +1,261 @@
+/*
+ * Licensed to Elasticsearch under one or more contributor
+ * license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright
+ * ownership. Elasticsearch licenses this file to you under
+ * the Apache License, Version 2.0 (the "License"); you may
+ * not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.elasticsearch.index.query;
+
+import org.apache.lucene.search.FuzzyQuery;
+import org.apache.lucene.search.Query;
+import org.elasticsearch.common.ParseField;
+import org.elasticsearch.common.ParsingException;
+import org.elasticsearch.common.io.stream.StreamInput;
+import org.elasticsearch.common.io.stream.StreamOutput;
+import org.elasticsearch.common.xcontent.XContentBuilder;
+import org.elasticsearch.common.xcontent.XContentParser;
+import org.elasticsearch.index.search.MatchQuery;
+
+import java.io.IOException;
+import java.util.Objects;
+
+/**
+ * Match query is a query that analyzes the text and constructs a phrase prefix
+ * query as the result of the analysis.
+ */
+public class MatchPhrasePrefixQueryBuilder extends AbstractQueryBuilder<MatchPhrasePrefixQueryBuilder> {
+
+    public static final ParseField MAX_EXPANSIONS_FIELD = new ParseField("max_expansions");
+
+    public static final String NAME = "match_phrase_prefix";
+
+    public static final MatchPhrasePrefixQueryBuilder PROTOTYPE = new MatchPhrasePrefixQueryBuilder("", "");
+
+    private final String fieldName;
+
+    private final Object value;
+
+    private String analyzer;
+
+    private int slop = MatchQuery.DEFAULT_PHRASE_SLOP;
+
+    private int maxExpansions = FuzzyQuery.defaultMaxExpansions;
+
+    public MatchPhrasePrefixQueryBuilder(String fieldName, Object value) {
+        if (fieldName == null) {
+            throw new IllegalArgumentException("[" + NAME + "] requires fieldName");
+        }
+        if (value == null) {
+            throw new IllegalArgumentException("[" + NAME + "] requires query value");
+        }
+        this.fieldName = fieldName;
+        this.value = value;
+    }
+
+    /** Returns the field name used in this query. */
+    public String fieldName() {
+        return this.fieldName;
+    }
+
+    /** Returns the value used in this query. */
+    public Object value() {
+        return this.value;
+    }
+
+    /**
+     * Explicitly set the analyzer to use. Defaults to use explicit mapping
+     * config for the field, or, if not set, the default search analyzer.
+     */
+    public MatchPhrasePrefixQueryBuilder analyzer(String analyzer) {
+        this.analyzer = analyzer;
+        return this;
+    }
+
+    /** Get the analyzer to use, if previously set, otherwise <tt>null</tt> */
+    public String analyzer() {
+        return this.analyzer;
+    }
+
+    /** Sets a slop factor for phrase queries */
+    public MatchPhrasePrefixQueryBuilder slop(int slop) {
+        if (slop < 0) {
+            throw new IllegalArgumentException("No negative slop allowed.");
+        }
+        this.slop = slop;
+        return this;
+    }
+
+    /** Get the slop factor for phrase queries. */
+    public int slop() {
+        return this.slop;
+    }
+
+    /**
+     * The number of term expansions to use.
+     */
+    public MatchPhrasePrefixQueryBuilder maxExpansions(int maxExpansions) {
+        if (maxExpansions < 0) {
+            throw new IllegalArgumentException("No negative maxExpansions allowed.");
+        }
+        this.maxExpansions = maxExpansions;
+        return this;
+    }
+
+    /**
+     * Get the (optional) number of term expansions when using fuzzy or prefix
+     * type query.
+     */
+    public int maxExpansions() {
+        return this.maxExpansions;
+    }
+
+    @Override
+    public String getWriteableName() {
+        return NAME;
+    }
+
+    @Override
+    protected void doXContent(XContentBuilder builder, Params params) throws IOException {
+        builder.startObject(NAME);
+        builder.startObject(fieldName);
+
+        builder.field(MatchQueryBuilder.QUERY_FIELD.getPreferredName(), value);
+        if (analyzer != null) {
+            builder.field(MatchQueryBuilder.ANALYZER_FIELD.getPreferredName(), analyzer);
+        }
+        builder.field(MatchPhraseQueryBuilder.SLOP_FIELD.getPreferredName(), slop);
+        builder.field(MAX_EXPANSIONS_FIELD.getPreferredName(), maxExpansions);
+        printBoostAndQueryName(builder);
+        builder.endObject();
+        builder.endObject();
+    }
+
+    @Override
+    protected Query doToQuery(QueryShardContext context) throws IOException {
+        // validate context specific fields
+        if (analyzer != null && context.getAnalysisService().analyzer(analyzer) == null) {
+            throw new QueryShardException(context, "[" + NAME + "] analyzer [" + analyzer + "] not found");
+        }
+
+        MatchQuery matchQuery = new MatchQuery(context);
+        matchQuery.setAnalyzer(analyzer);
+        matchQuery.setPhraseSlop(slop);
+        matchQuery.setMaxExpansions(maxExpansions);
+
+        return matchQuery.parse(MatchQuery.Type.PHRASE_PREFIX, fieldName, value);
+    }
+
+    @Override
+    protected MatchPhrasePrefixQueryBuilder doReadFrom(StreamInput in) throws IOException {
+        MatchPhrasePrefixQueryBuilder matchQuery = new MatchPhrasePrefixQueryBuilder(in.readString(), in.readGenericValue());
+        matchQuery.slop = in.readVInt();
+        matchQuery.maxExpansions = in.readVInt();
+        matchQuery.analyzer = in.readOptionalString();
+        return matchQuery;
+    }
+
+    @Override
+    protected void doWriteTo(StreamOutput out) throws IOException {
+        out.writeString(fieldName);
+        out.writeGenericValue(value);
+        out.writeVInt(slop);
+        out.writeVInt(maxExpansions);
+        out.writeOptionalString(analyzer);
+    }
+
+    @Override
+    protected boolean doEquals(MatchPhrasePrefixQueryBuilder other) {
+        return Objects.equals(fieldName, other.fieldName) &&
+                Objects.equals(value, other.value) &&
+                Objects.equals(analyzer, other.analyzer)&&
+                Objects.equals(slop, other.slop) &&
+                Objects.equals(maxExpansions, other.maxExpansions);
+    }
+
+    @Override
+    protected int doHashCode() {
+        return Objects.hash(fieldName, value, analyzer, slop, maxExpansions);
+    }
+
+    public static MatchPhrasePrefixQueryBuilder fromXContent(QueryParseContext parseContext) throws IOException {
+        XContentParser parser = parseContext.parser();
+
+        XContentParser.Token token = parser.nextToken();
+        if (token != XContentParser.Token.FIELD_NAME) {
+            throw new ParsingException(parser.getTokenLocation(), "[" + NAME + "] query malformed, no field");
+        }
+        String fieldName = parser.currentName();
+
+        Object value = null;
+        float boost = AbstractQueryBuilder.DEFAULT_BOOST;
+        String analyzer = null;
+        int slop = MatchQuery.DEFAULT_PHRASE_SLOP;
+        int maxExpansion = FuzzyQuery.defaultMaxExpansions;
+        String queryName = null;
+
+        token = parser.nextToken();
+        if (token == XContentParser.Token.START_OBJECT) {
+            String currentFieldName = null;
+            while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) {
+                if (token == XContentParser.Token.FIELD_NAME) {
+                    currentFieldName = parser.currentName();
+                } else if (token.isValue()) {
+                    if (parseContext.parseFieldMatcher().match(currentFieldName, MatchQueryBuilder.QUERY_FIELD)) {
+                        value = parser.objectText();
+                    } else if (parseContext.parseFieldMatcher().match(currentFieldName, MatchQueryBuilder.ANALYZER_FIELD)) {
+                        analyzer = parser.text();
+                    } else if (parseContext.parseFieldMatcher().match(currentFieldName, AbstractQueryBuilder.BOOST_FIELD)) {
+                        boost = parser.floatValue();
+                    } else if (parseContext.parseFieldMatcher().match(currentFieldName, MatchPhraseQueryBuilder.SLOP_FIELD)) {
+                        slop = parser.intValue();
+                    } else if (parseContext.parseFieldMatcher().match(currentFieldName, MAX_EXPANSIONS_FIELD)) {
+                        maxExpansion = parser.intValue();
+                    } else if (parseContext.parseFieldMatcher().match(currentFieldName, AbstractQueryBuilder.NAME_FIELD)) {
+                        queryName = parser.text();
+                    } else {
+                        throw new ParsingException(parser.getTokenLocation(),
+                                "[" + NAME + "] query does not support [" + currentFieldName + "]");
+                    }
+                } else {
+                    throw new ParsingException(parser.getTokenLocation(),
+                            "[" + NAME + "] unknown token [" + token + "] after [" + currentFieldName + "]");
+                }
+            }
+            parser.nextToken();
+        } else {
+            value = parser.objectText();
+            // move to the next token
+            token = parser.nextToken();
+            if (token != XContentParser.Token.END_OBJECT) {
+                throw new ParsingException(parser.getTokenLocation(), "[" + NAME
+                        + "] query parsed in simplified form, with direct field name, "
+                        + "but included more options than just the field name, possibly use its 'options' form, with 'query' element?");
+            }
+        }
+
+        if (value == null) {
+            throw new ParsingException(parser.getTokenLocation(), "No text specified for text query");
+        }
+
+        MatchPhrasePrefixQueryBuilder matchQuery = new MatchPhrasePrefixQueryBuilder(fieldName, value);
+        matchQuery.analyzer(analyzer);
+        matchQuery.slop(slop);
+        matchQuery.maxExpansions(maxExpansion);
+        matchQuery.queryName(queryName);
+        matchQuery.boost(boost);
+        return matchQuery;
+    }
+}

+ 227 - 0
core/src/main/java/org/elasticsearch/index/query/MatchPhraseQueryBuilder.java

@@ -0,0 +1,227 @@
+/*
+ * Licensed to Elasticsearch under one or more contributor
+ * license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright
+ * ownership. Elasticsearch licenses this file to you under
+ * the Apache License, Version 2.0 (the "License"); you may
+ * not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.elasticsearch.index.query;
+
+import org.apache.lucene.search.Query;
+import org.elasticsearch.common.ParseField;
+import org.elasticsearch.common.ParsingException;
+import org.elasticsearch.common.io.stream.StreamInput;
+import org.elasticsearch.common.io.stream.StreamOutput;
+import org.elasticsearch.common.xcontent.XContentBuilder;
+import org.elasticsearch.common.xcontent.XContentParser;
+import org.elasticsearch.index.search.MatchQuery;
+import java.io.IOException;
+import java.util.Objects;
+
+/**
+ * Match query is a query that analyzes the text and constructs a phrase query
+ * as the result of the analysis.
+ */
+public class MatchPhraseQueryBuilder extends AbstractQueryBuilder<MatchPhraseQueryBuilder> {
+
+    public static final ParseField SLOP_FIELD = new ParseField("slop", "phrase_slop");
+
+    public static final String NAME = "match_phrase";
+
+    public static final MatchPhraseQueryBuilder PROTOTYPE = new MatchPhraseQueryBuilder("", "");
+
+    private final String fieldName;
+
+    private final Object value;
+
+    private String analyzer;
+
+    private int slop = MatchQuery.DEFAULT_PHRASE_SLOP;
+
+    public MatchPhraseQueryBuilder(String fieldName, Object value) {
+        if (fieldName == null) {
+            throw new IllegalArgumentException("[" + NAME + "] requires fieldName");
+        }
+        if (value == null) {
+            throw new IllegalArgumentException("[" + NAME + "] requires query value");
+        }
+        this.fieldName = fieldName;
+        this.value = value;
+    }
+
+    /** Returns the field name used in this query. */
+    public String fieldName() {
+        return this.fieldName;
+    }
+
+    /** Returns the value used in this query. */
+    public Object value() {
+        return this.value;
+    }
+
+    /**
+     * Explicitly set the analyzer to use. Defaults to use explicit mapping
+     * config for the field, or, if not set, the default search analyzer.
+     */
+    public MatchPhraseQueryBuilder analyzer(String analyzer) {
+        this.analyzer = analyzer;
+        return this;
+    }
+
+    /** Get the analyzer to use, if previously set, otherwise <tt>null</tt> */
+    public String analyzer() {
+        return this.analyzer;
+    }
+
+    /** Sets a slop factor for phrase queries */
+    public MatchPhraseQueryBuilder slop(int slop) {
+        if (slop < 0) {
+            throw new IllegalArgumentException("No negative slop allowed.");
+        }
+        this.slop = slop;
+        return this;
+    }
+
+    /** Get the slop factor for phrase queries. */
+    public int slop() {
+        return this.slop;
+    }
+
+    @Override
+    public String getWriteableName() {
+        return NAME;
+    }
+
+    @Override
+    protected void doXContent(XContentBuilder builder, Params params) throws IOException {
+        builder.startObject(NAME);
+        builder.startObject(fieldName);
+
+        builder.field(MatchQueryBuilder.QUERY_FIELD.getPreferredName(), value);
+        if (analyzer != null) {
+            builder.field(MatchQueryBuilder.ANALYZER_FIELD.getPreferredName(), analyzer);
+        }
+        builder.field(SLOP_FIELD.getPreferredName(), slop);
+        printBoostAndQueryName(builder);
+        builder.endObject();
+        builder.endObject();
+    }
+
+    @Override
+    protected Query doToQuery(QueryShardContext context) throws IOException {
+        // validate context specific fields
+        if (analyzer != null && context.getAnalysisService().analyzer(analyzer) == null) {
+            throw new QueryShardException(context, "[" + NAME + "] analyzer [" + analyzer + "] not found");
+        }
+
+        MatchQuery matchQuery = new MatchQuery(context);
+        matchQuery.setAnalyzer(analyzer);
+        matchQuery.setPhraseSlop(slop);
+
+        return matchQuery.parse(MatchQuery.Type.PHRASE, fieldName, value);
+    }
+
+    @Override
+    protected MatchPhraseQueryBuilder doReadFrom(StreamInput in) throws IOException {
+        MatchPhraseQueryBuilder matchQuery = new MatchPhraseQueryBuilder(in.readString(), in.readGenericValue());
+        matchQuery.slop = in.readVInt();
+        matchQuery.analyzer = in.readOptionalString();
+        return matchQuery;
+    }
+
+    @Override
+    protected void doWriteTo(StreamOutput out) throws IOException {
+        out.writeString(fieldName);
+        out.writeGenericValue(value);
+        out.writeVInt(slop);
+        out.writeOptionalString(analyzer);
+    }
+
+    @Override
+    protected boolean doEquals(MatchPhraseQueryBuilder other) {
+        return Objects.equals(fieldName, other.fieldName) && Objects.equals(value, other.value) && Objects.equals(analyzer, other.analyzer)
+                && Objects.equals(slop, other.slop);
+    }
+
+    @Override
+    protected int doHashCode() {
+        return Objects.hash(fieldName, value, analyzer, slop);
+    }
+
+    public static MatchPhraseQueryBuilder fromXContent(QueryParseContext parseContext) throws IOException {
+        XContentParser parser = parseContext.parser();
+
+        XContentParser.Token token = parser.nextToken();
+        if (token != XContentParser.Token.FIELD_NAME) {
+            throw new ParsingException(parser.getTokenLocation(), "[" + NAME + "] query malformed, no field");
+        }
+        String fieldName = parser.currentName();
+
+        Object value = null;
+        float boost = AbstractQueryBuilder.DEFAULT_BOOST;
+        String analyzer = null;
+        int slop = MatchQuery.DEFAULT_PHRASE_SLOP;
+        String queryName = null;
+
+        token = parser.nextToken();
+        if (token == XContentParser.Token.START_OBJECT) {
+            String currentFieldName = null;
+            while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) {
+                if (token == XContentParser.Token.FIELD_NAME) {
+                    currentFieldName = parser.currentName();
+                } else if (token.isValue()) {
+                    if (parseContext.parseFieldMatcher().match(currentFieldName, MatchQueryBuilder.QUERY_FIELD)) {
+                        value = parser.objectText();
+                    } else if (parseContext.parseFieldMatcher().match(currentFieldName, MatchQueryBuilder.ANALYZER_FIELD)) {
+                        analyzer = parser.text();
+                    } else if (parseContext.parseFieldMatcher().match(currentFieldName, AbstractQueryBuilder.BOOST_FIELD)) {
+                        boost = parser.floatValue();
+                    } else if (parseContext.parseFieldMatcher().match(currentFieldName, SLOP_FIELD)) {
+                        slop = parser.intValue();
+                    } else if (parseContext.parseFieldMatcher().match(currentFieldName, AbstractQueryBuilder.NAME_FIELD)) {
+                        queryName = parser.text();
+                    } else {
+                        throw new ParsingException(parser.getTokenLocation(),
+                                "[" + NAME + "] query does not support [" + currentFieldName + "]");
+                    }
+                } else {
+                    throw new ParsingException(parser.getTokenLocation(),
+                            "[" + NAME + "] unknown token [" + token + "] after [" + currentFieldName + "]");
+                }
+            }
+            parser.nextToken();
+        } else {
+            value = parser.objectText();
+            // move to the next token
+            token = parser.nextToken();
+            if (token != XContentParser.Token.END_OBJECT) {
+                throw new ParsingException(parser.getTokenLocation(), "[" + NAME
+                        + "] query parsed in simplified form, with direct field name, "
+                        + "but included more options than just the field name, possibly use its 'options' form, with 'query' element?");
+            }
+        }
+
+        if (value == null) {
+            throw new ParsingException(parser.getTokenLocation(), "No text specified for text query");
+        }
+
+        MatchPhraseQueryBuilder matchQuery = new MatchPhraseQueryBuilder(fieldName, value);
+        matchQuery.analyzer(analyzer);
+        matchQuery.slop(slop);
+        matchQuery.queryName(queryName);
+        matchQuery.boost(boost);
+        return matchQuery;
+    }
+}

+ 46 - 21
core/src/main/java/org/elasticsearch/index/query/MatchQueryBuilder.java

@@ -40,13 +40,11 @@ import java.util.Locale;
 import java.util.Objects;
 
 /**
- * Match query is a query that analyzes the text and constructs a query as the result of the analysis. It
- * can construct different queries based on the type provided.
+ * Match query is a query that analyzes the text and constructs a query as the
+ * result of the analysis.
  */
 public class MatchQueryBuilder extends AbstractQueryBuilder<MatchQueryBuilder> {
-    public static final ParseField MATCH_PHRASE_FIELD = new ParseField("match_phrase", "text_phrase");
-    public static final ParseField MATCH_PHRASE_PREFIX_FIELD = new ParseField("match_phrase_prefix", "text_phrase_prefix");
-    public static final ParseField SLOP_FIELD = new ParseField("slop", "phrase_slop");
+    public static final ParseField SLOP_FIELD = new ParseField("slop", "phrase_slop").withAllDeprecated("match_phrase query");
     public static final ParseField ZERO_TERMS_QUERY_FIELD = new ParseField("zero_terms_query");
     public static final ParseField CUTOFF_FREQUENCY_FIELD = new ParseField("cutoff_frequency");
     public static final ParseField LENIENT_FIELD = new ParseField("lenient");
@@ -57,28 +55,31 @@ public class MatchQueryBuilder extends AbstractQueryBuilder<MatchQueryBuilder> {
     public static final ParseField MAX_EXPANSIONS_FIELD = new ParseField("max_expansions");
     public static final ParseField PREFIX_LENGTH_FIELD = new ParseField("prefix_length");
     public static final ParseField ANALYZER_FIELD = new ParseField("analyzer");
-    public static final ParseField TYPE_FIELD = new ParseField("type");
+    public static final ParseField TYPE_FIELD = new ParseField("type").withAllDeprecated("match_phrase and match_phrase_prefix query");
     public static final ParseField QUERY_FIELD = new ParseField("query");
 
-    /** The default name for the match query */
+    /** The name for the match query */
     public static final String NAME = "match";
 
     /** The default mode terms are combined in a match query */
     public static final Operator DEFAULT_OPERATOR = Operator.OR;
 
     /** The default mode match query type */
+    @Deprecated
     public static final MatchQuery.Type DEFAULT_TYPE = MatchQuery.Type.BOOLEAN;
 
     private final String fieldName;
 
     private final Object value;
 
+    @Deprecated
     private MatchQuery.Type type = DEFAULT_TYPE;
 
     private Operator operator = DEFAULT_OPERATOR;
 
     private String analyzer;
 
+    @Deprecated
     private int slop = MatchQuery.DEFAULT_PHRASE_SLOP;
 
     private Fuzziness fuzziness = null;
@@ -125,7 +126,14 @@ public class MatchQueryBuilder extends AbstractQueryBuilder<MatchQueryBuilder> {
         return this.value;
     }
 
-    /** Sets the type of the text query. */
+    /**
+     * Sets the type of the text query.
+     *
+     * @deprecated Use {@link MatchPhraseQueryBuilder} for <code>phrase</code>
+     *             queries and {@link MatchPhrasePrefixQueryBuilder} for
+     *             <code>phrase_prefix</code> queries
+     */
+    @Deprecated
     public MatchQueryBuilder type(MatchQuery.Type type) {
         if (type == null) {
             throw new IllegalArgumentException("[" + NAME + "] requires type to be non-null");
@@ -134,7 +142,14 @@ public class MatchQueryBuilder extends AbstractQueryBuilder<MatchQueryBuilder> {
         return this;
     }
 
-    /** Get the type of the query. */
+    /**
+     * Get the type of the query.
+     *
+     * @deprecated Use {@link MatchPhraseQueryBuilder} for <code>phrase</code>
+     *             queries and {@link MatchPhrasePrefixQueryBuilder} for
+     *             <code>phrase_prefix</code> queries
+     */
+    @Deprecated
     public MatchQuery.Type type() {
         return this.type;
     }
@@ -167,7 +182,12 @@ public class MatchQueryBuilder extends AbstractQueryBuilder<MatchQueryBuilder> {
         return this.analyzer;
     }
 
-    /** Sets a slop factor for phrase queries */
+    /**
+     * Sets a slop factor for phrase queries
+     *
+     * @deprecated for phrase queries use {@link MatchPhraseQueryBuilder}
+     */
+    @Deprecated
     public MatchQueryBuilder slop(int slop) {
         if (slop < 0 ) {
             throw new IllegalArgumentException("No negative slop allowed.");
@@ -176,7 +196,12 @@ public class MatchQueryBuilder extends AbstractQueryBuilder<MatchQueryBuilder> {
         return this;
     }
 
-    /** Get the slop factor for phrase queries. */
+    /**
+     * Get the slop factor for phrase queries.
+     *
+     * @deprecated for phrase queries use {@link MatchPhraseQueryBuilder}
+     */
+    @Deprecated
     public int slop() {
         return this.slop;
     }
@@ -337,12 +362,18 @@ public class MatchQueryBuilder extends AbstractQueryBuilder<MatchQueryBuilder> {
         builder.startObject(fieldName);
 
         builder.field(QUERY_FIELD.getPreferredName(), value);
-        builder.field(TYPE_FIELD.getPreferredName(), type.toString().toLowerCase(Locale.ENGLISH));
+        // this is deprecated so only output the value if its not the default value (for bwc)
+        if (type != MatchQuery.Type.BOOLEAN) {
+            builder.field(TYPE_FIELD.getPreferredName(), type.toString().toLowerCase(Locale.ENGLISH));
+        }
         builder.field(OPERATOR_FIELD.getPreferredName(), operator.toString());
         if (analyzer != null) {
             builder.field(ANALYZER_FIELD.getPreferredName(), analyzer);
         }
-        builder.field(SLOP_FIELD.getPreferredName(), slop);
+        // this is deprecated so only output the value if its not the default value (for bwc)
+        if (slop != MatchQuery.DEFAULT_PHRASE_SLOP) {
+            builder.field(SLOP_FIELD.getPreferredName(), slop);
+        }
         if (fuzziness != null) {
             fuzziness.toXContent(builder, params);
         }
@@ -370,7 +401,7 @@ public class MatchQueryBuilder extends AbstractQueryBuilder<MatchQueryBuilder> {
     protected Query doToQuery(QueryShardContext context) throws IOException {
         // validate context specific fields
         if (analyzer != null && context.getAnalysisService().analyzer(analyzer) == null) {
-            throw new QueryShardException(context, "[match] analyzer [" + analyzer + "] not found");
+            throw new QueryShardException(context, "[" + NAME + "] analyzer [" + analyzer + "] not found");
         }
 
         MatchQuery matchQuery = new MatchQuery(context);
@@ -490,19 +521,13 @@ public class MatchQueryBuilder extends AbstractQueryBuilder<MatchQueryBuilder> {
     public static MatchQueryBuilder fromXContent(QueryParseContext parseContext) throws IOException {
         XContentParser parser = parseContext.parser();
 
-        MatchQuery.Type type = MatchQuery.Type.BOOLEAN;
-        if (parseContext.parseFieldMatcher().match(parser.currentName(), MATCH_PHRASE_FIELD)) {
-            type = MatchQuery.Type.PHRASE;
-        } else if (parseContext.parseFieldMatcher().match(parser.currentName(), MATCH_PHRASE_PREFIX_FIELD)) {
-            type = MatchQuery.Type.PHRASE_PREFIX;
-        }
-
         XContentParser.Token token = parser.nextToken();
         if (token != XContentParser.Token.FIELD_NAME) {
             throw new ParsingException(parser.getTokenLocation(), "[" + MatchQueryBuilder.NAME + "] query malformed, no field");
         }
         String fieldName = parser.currentName();
 
+        MatchQuery.Type type = MatchQuery.Type.BOOLEAN;
         Object value = null;
         float boost = AbstractQueryBuilder.DEFAULT_BOOST;
         String minimumShouldMatch = null;

+ 8 - 2
core/src/main/java/org/elasticsearch/search/SearchModule.java

@@ -52,6 +52,8 @@ import org.elasticsearch.index.query.IdsQueryParser;
 import org.elasticsearch.index.query.IndicesQueryParser;
 import org.elasticsearch.index.query.MatchAllQueryParser;
 import org.elasticsearch.index.query.MatchNoneQueryParser;
+import org.elasticsearch.index.query.MatchPhrasePrefixQueryBuilder;
+import org.elasticsearch.index.query.MatchPhraseQueryBuilder;
 import org.elasticsearch.index.query.MatchQueryBuilder;
 import org.elasticsearch.index.query.MoreLikeThisQueryParser;
 import org.elasticsearch.index.query.MultiMatchQueryParser;
@@ -513,8 +515,12 @@ public class SearchModule extends AbstractModule {
     }
 
     private void registerBuiltinQueryParsers() {
-        registerQuery(MatchQueryBuilder.PROTOTYPE::readFrom, MatchQueryBuilder::fromXContent, MatchQueryBuilder.NAME,
-                "match_phrase", "matchPhrase", "match_phrase_prefix", "matchPhrasePrefix", "matchFuzzy", "match_fuzzy", "fuzzy_match");
+        registerQuery(MatchQueryBuilder.PROTOTYPE::readFrom, MatchQueryBuilder::fromXContent, MatchQueryBuilder.NAME, "matchFuzzy",
+                "match_fuzzy", "fuzzy_match");
+        registerQuery(MatchPhraseQueryBuilder.PROTOTYPE::readFrom, MatchPhraseQueryBuilder::fromXContent, MatchPhraseQueryBuilder.NAME,
+                "matchPhrase");
+        registerQuery(MatchPhrasePrefixQueryBuilder.PROTOTYPE::readFrom, MatchPhrasePrefixQueryBuilder::fromXContent,
+                MatchPhrasePrefixQueryBuilder.NAME, "matchPhrasePrefix");
         registerQueryParser(MultiMatchQueryParser::new);
         registerQueryParser(NestedQueryParser::new);
         registerQueryParser(HasChildQueryParser::new);

+ 153 - 0
core/src/test/java/org/elasticsearch/index/query/MatchPhrasePrefixQueryBuilderTests.java

@@ -0,0 +1,153 @@
+/*
+ * Licensed to Elasticsearch under one or more contributor
+ * license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright
+ * ownership. Elasticsearch licenses this file to you under
+ * the Apache License, Version 2.0 (the "License"); you may
+ * not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.elasticsearch.index.query;
+
+import org.apache.lucene.search.BooleanQuery;
+import org.apache.lucene.search.Query;
+import org.apache.lucene.search.TermQuery;
+import org.elasticsearch.common.lucene.search.MultiPhrasePrefixQuery;
+import java.io.IOException;
+import static org.hamcrest.CoreMatchers.either;
+import static org.hamcrest.CoreMatchers.instanceOf;
+import static org.hamcrest.Matchers.containsString;
+import static org.hamcrest.Matchers.notNullValue;
+
+public class MatchPhrasePrefixQueryBuilderTests extends AbstractQueryTestCase<MatchPhrasePrefixQueryBuilder> {
+    @Override
+    protected MatchPhrasePrefixQueryBuilder doCreateTestQueryBuilder() {
+        String fieldName = randomFrom(STRING_FIELD_NAME, BOOLEAN_FIELD_NAME, INT_FIELD_NAME,
+                DOUBLE_FIELD_NAME, DATE_FIELD_NAME);
+        if (fieldName.equals(DATE_FIELD_NAME)) {
+            assumeTrue("test runs only when at least a type is registered", getCurrentTypes().length > 0);
+        }
+        Object value;
+        if (fieldName.equals(STRING_FIELD_NAME)) {
+            int terms = randomIntBetween(0, 3);
+            StringBuilder builder = new StringBuilder();
+            for (int i = 0; i < terms; i++) {
+                builder.append(randomAsciiOfLengthBetween(1, 10)).append(" ");
+            }
+            value = builder.toString().trim();
+        } else {
+            value = getRandomValueForFieldName(fieldName);
+        }
+
+        MatchPhrasePrefixQueryBuilder matchQuery = new MatchPhrasePrefixQueryBuilder(fieldName, value);
+
+        if (randomBoolean()) {
+            matchQuery.analyzer(randomFrom("simple", "keyword", "whitespace"));
+        }
+
+        if (randomBoolean()) {
+            matchQuery.slop(randomIntBetween(0, 10));
+        }
+
+        if (randomBoolean()) {
+            matchQuery.maxExpansions(randomIntBetween(1, 10000));
+        }
+        return matchQuery;
+    }
+
+    @Override
+    protected void doAssertLuceneQuery(MatchPhrasePrefixQueryBuilder queryBuilder, Query query, QueryShardContext context)
+            throws IOException {
+        assertThat(query, notNullValue());
+        assertThat(query,
+                either(instanceOf(BooleanQuery.class)).or(instanceOf(MultiPhrasePrefixQuery.class)).or(instanceOf(TermQuery.class)));
+    }
+
+    public void testIllegalValues() {
+        try {
+            new MatchPhrasePrefixQueryBuilder(null, "value");
+            fail("value must not be non-null");
+        } catch (IllegalArgumentException ex) {
+            // expected
+        }
+
+        try {
+            new MatchPhrasePrefixQueryBuilder("fieldName", null);
+            fail("value must not be non-null");
+        } catch (IllegalArgumentException ex) {
+            // expected
+        }
+
+        MatchPhrasePrefixQueryBuilder matchQuery = new MatchPhrasePrefixQueryBuilder("fieldName", "text");
+
+        try {
+            matchQuery.maxExpansions(-1);
+            fail("must not be positive");
+        } catch (IllegalArgumentException ex) {
+            // expected
+        }
+    }
+
+    public void testBadAnalyzer() throws IOException {
+        MatchPhrasePrefixQueryBuilder matchQuery = new MatchPhrasePrefixQueryBuilder("fieldName", "text");
+        matchQuery.analyzer("bogusAnalyzer");
+        try {
+            matchQuery.toQuery(createShardContext());
+            fail("Expected QueryShardException");
+        } catch (QueryShardException e) {
+            assertThat(e.getMessage(), containsString("analyzer [bogusAnalyzer] not found"));
+        }
+    }
+
+    public void testPhrasePrefixMatchQuery() throws IOException {
+        String json1 = "{\n" +
+                "    \"match_phrase_prefix\" : {\n" +
+                "        \"message\" : \"this is a test\"\n" +
+                "    }\n" +
+                "}";
+
+        String expected = "{\n" +
+                "  \"match_phrase_prefix\" : {\n" +
+                "    \"message\" : {\n" +
+                "      \"query\" : \"this is a test\",\n" +
+                "      \"slop\" : 0,\n" +
+                "      \"max_expansions\" : 50,\n" +
+                "      \"boost\" : 1.0\n" +
+                "    }\n" +
+                "  }\n" +
+                "}";
+        MatchPhrasePrefixQueryBuilder qb = (MatchPhrasePrefixQueryBuilder) parseQuery(json1);
+        checkGeneratedJson(expected, qb);
+
+        String json3 = "{\n" +
+                "    \"match_phrase_prefix\" : {\n" +
+                "        \"message\" : {\n" +
+                "            \"query\" : \"this is a test\",\n" +
+                "            \"max_expansions\" : 10\n" +
+                "        }\n" +
+                "    }\n" +
+                "}";
+        expected = "{\n" +
+                "  \"match_phrase_prefix\" : {\n" +
+                "    \"message\" : {\n" +
+                "      \"query\" : \"this is a test\",\n" +
+                "      \"slop\" : 0,\n" +
+                "      \"max_expansions\" : 10,\n" +
+                "      \"boost\" : 1.0\n" +
+                "    }\n" +
+                "  }\n" +
+                "}";
+        qb = (MatchPhrasePrefixQueryBuilder) parseQuery(json3);
+        checkGeneratedJson(expected, qb);
+    }
+}

+ 118 - 0
core/src/test/java/org/elasticsearch/index/query/MatchPhraseQueryBuilderTests.java

@@ -0,0 +1,118 @@
+/*
+ * Licensed to Elasticsearch under one or more contributor
+ * license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright
+ * ownership. Elasticsearch licenses this file to you under
+ * the Apache License, Version 2.0 (the "License"); you may
+ * not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.elasticsearch.index.query;
+
+import org.apache.lucene.search.BooleanQuery;
+import org.apache.lucene.search.PhraseQuery;
+import org.apache.lucene.search.Query;
+import org.apache.lucene.search.TermQuery;
+
+import java.io.IOException;
+
+import static org.hamcrest.CoreMatchers.either;
+import static org.hamcrest.CoreMatchers.instanceOf;
+import static org.hamcrest.Matchers.containsString;
+import static org.hamcrest.Matchers.notNullValue;
+
+public class MatchPhraseQueryBuilderTests extends AbstractQueryTestCase<MatchPhraseQueryBuilder> {
+    @Override
+    protected MatchPhraseQueryBuilder doCreateTestQueryBuilder() {
+        String fieldName = randomFrom(STRING_FIELD_NAME, BOOLEAN_FIELD_NAME, INT_FIELD_NAME,
+                DOUBLE_FIELD_NAME, DATE_FIELD_NAME);
+        if (fieldName.equals(DATE_FIELD_NAME)) {
+            assumeTrue("test runs only when at least a type is registered", getCurrentTypes().length > 0);
+        }
+        Object value;
+        if (fieldName.equals(STRING_FIELD_NAME)) {
+            int terms = randomIntBetween(0, 3);
+            StringBuilder builder = new StringBuilder();
+            for (int i = 0; i < terms; i++) {
+                builder.append(randomAsciiOfLengthBetween(1, 10)).append(" ");
+            }
+            value = builder.toString().trim();
+        } else {
+            value = getRandomValueForFieldName(fieldName);
+        }
+
+        MatchPhraseQueryBuilder matchQuery = new MatchPhraseQueryBuilder(fieldName, value);
+
+        if (randomBoolean()) {
+            matchQuery.analyzer(randomFrom("simple", "keyword", "whitespace"));
+        }
+
+        if (randomBoolean()) {
+            matchQuery.slop(randomIntBetween(0, 10));
+        }
+        return matchQuery;
+    }
+
+    @Override
+    protected void doAssertLuceneQuery(MatchPhraseQueryBuilder queryBuilder, Query query, QueryShardContext context) throws IOException {
+        assertThat(query, notNullValue());
+        assertThat(query, either(instanceOf(BooleanQuery.class)).or(instanceOf(PhraseQuery.class)).or(instanceOf(TermQuery.class)));
+    }
+
+    public void testIllegalValues() {
+        try {
+            new MatchPhraseQueryBuilder(null, "value");
+            fail("value must not be non-null");
+        } catch (IllegalArgumentException ex) {
+            // expected
+        }
+
+        try {
+            new MatchPhraseQueryBuilder("fieldName", null);
+            fail("value must not be non-null");
+        } catch (IllegalArgumentException ex) {
+            // expected
+        }
+    }
+
+    public void testBadAnalyzer() throws IOException {
+        MatchPhraseQueryBuilder matchQuery = new MatchPhraseQueryBuilder("fieldName", "text");
+        matchQuery.analyzer("bogusAnalyzer");
+        try {
+            matchQuery.toQuery(createShardContext());
+            fail("Expected QueryShardException");
+        } catch (QueryShardException e) {
+            assertThat(e.getMessage(), containsString("analyzer [bogusAnalyzer] not found"));
+        }
+    }
+
+    public void testPhraseMatchQuery() throws IOException {
+        String json1 = "{\n" +
+                "    \"match_phrase\" : {\n" +
+                "        \"message\" : \"this is a test\"\n" +
+                "    }\n" +
+                "}";
+
+        String expected = "{\n" +
+                "  \"match_phrase\" : {\n" +
+                "    \"message\" : {\n" +
+                "      \"query\" : \"this is a test\",\n" +
+                "      \"slop\" : 0,\n" +
+                "      \"boost\" : 1.0\n" +
+                "    }\n" +
+                "  }\n" +
+                "}";
+        MatchPhraseQueryBuilder qb = (MatchPhraseQueryBuilder) parseQuery(json1);
+        checkGeneratedJson(expected, qb);
+    }
+}

+ 62 - 53
core/src/test/java/org/elasticsearch/index/query/MatchQueryBuilderTests.java

@@ -28,11 +28,13 @@ import org.apache.lucene.search.MatchAllDocsQuery;
 import org.apache.lucene.search.PhraseQuery;
 import org.apache.lucene.search.Query;
 import org.apache.lucene.search.TermQuery;
+import org.elasticsearch.common.ParseFieldMatcher;
 import org.elasticsearch.common.lucene.search.MultiPhrasePrefixQuery;
 import org.elasticsearch.common.lucene.search.Queries;
 import org.elasticsearch.common.unit.Fuzziness;
 import org.elasticsearch.index.mapper.MappedFieldType;
 import org.elasticsearch.index.search.MatchQuery;
+import org.elasticsearch.index.search.MatchQuery.Type;
 import org.elasticsearch.index.search.MatchQuery.ZeroTermsQuery;
 import org.hamcrest.Matcher;
 import org.joda.time.format.ISODateTimeFormat;
@@ -67,7 +69,6 @@ public class MatchQueryBuilderTests extends AbstractQueryTestCase<MatchQueryBuil
         }
 
         MatchQueryBuilder matchQuery = new MatchQueryBuilder(fieldName, value);
-        matchQuery.type(randomFrom(MatchQuery.Type.values()));
         matchQuery.operator(randomFrom(Operator.values()));
 
         if (randomBoolean()) {
@@ -75,15 +76,15 @@ public class MatchQueryBuilderTests extends AbstractQueryTestCase<MatchQueryBuil
         }
 
         if (randomBoolean()) {
-            matchQuery.slop(randomIntBetween(0, 10));
+            matchQuery.fuzziness(randomFuzziness(fieldName));
         }
 
         if (randomBoolean()) {
-            matchQuery.fuzziness(randomFuzziness(fieldName));
+            matchQuery.prefixLength(randomIntBetween(0, 10));
         }
 
         if (randomBoolean()) {
-            matchQuery.prefixLength(randomIntBetween(0, 10));
+            matchQuery.maxExpansions(randomIntBetween(0, 1000));
         }
 
         if (randomBoolean()) {
@@ -277,60 +278,42 @@ public class MatchQueryBuilderTests extends AbstractQueryTestCase<MatchQueryBuil
         }
     }
 
-    public void testPhrasePrefixMatchQuery() throws IOException {
-        String json1 = "{\n" +
-                "    \"match_phrase_prefix\" : {\n" +
-                "        \"message\" : \"this is a test\"\n" +
-                "    }\n" +
-                "}";
-
-        String expected = "{\n" +
+    public void testSimpleMatchQuery() throws IOException {
+        String json = "{\n" +
                 "  \"match\" : {\n" +
                 "    \"message\" : {\n" +
-                "      \"query\" : \"this is a test\",\n" +
-                "      \"type\" : \"phrase_prefix\",\n" +
-                "      \"operator\" : \"OR\",\n" +
-                "      \"slop\" : 0,\n" +
+                "      \"query\" : \"to be or not to be\",\n" +
+                "      \"operator\" : \"AND\",\n" +
                 "      \"prefix_length\" : 0,\n" +
                 "      \"max_expansions\" : 50,\n" +
                 "      \"fuzzy_transpositions\" : true,\n" +
                 "      \"lenient\" : false,\n" +
-                "      \"zero_terms_query\" : \"NONE\",\n" +
+                "      \"zero_terms_query\" : \"ALL\",\n" +
                 "      \"boost\" : 1.0\n" +
                 "    }\n" +
                 "  }\n" +
                 "}";
-        MatchQueryBuilder qb = (MatchQueryBuilder) parseQuery(json1);
-        checkGeneratedJson(expected, qb);
-
-        String json2 = "{\n" +
-                "    \"match\" : {\n" +
-                "        \"message\" : {\n" +
-                "            \"query\" : \"this is a test\",\n" +
-                "            \"type\" : \"phrase_prefix\"\n" +
-                "        }\n" +
-                "    }\n" +
-                "}";
-        qb = (MatchQueryBuilder) parseQuery(json2);
-        checkGeneratedJson(expected, qb);
-
-        String json3 = "{\n" +
-                "    \"match_phrase_prefix\" : {\n" +
-                "        \"message\" : {\n" +
-                "            \"query\" : \"this is a test\",\n" +
-                "            \"max_expansions\" : 10\n" +
-                "        }\n" +
-                "    }\n" +
-                "}";
-        expected = "{\n" +
+        MatchQueryBuilder qb = (MatchQueryBuilder) parseQuery(json);
+        checkGeneratedJson(json, qb);
+
+        assertEquals(json, "to be or not to be", qb.value());
+        assertEquals(json, Operator.AND, qb.operator());
+    }
+
+    public void testLegacyMatchPhrasePrefixQuery() throws IOException {
+        MatchQueryBuilder expectedQB = new MatchQueryBuilder("message", "to be or not to be");
+        expectedQB.type(Type.PHRASE_PREFIX);
+        expectedQB.slop(2);
+        expectedQB.maxExpansions(30);
+        String json = "{\n" +
                 "  \"match\" : {\n" +
                 "    \"message\" : {\n" +
-                "      \"query\" : \"this is a test\",\n" +
+                "      \"query\" : \"to be or not to be\",\n" +
                 "      \"type\" : \"phrase_prefix\",\n" +
                 "      \"operator\" : \"OR\",\n" +
-                "      \"slop\" : 0,\n" +
+                "      \"slop\" : 2,\n" +
                 "      \"prefix_length\" : 0,\n" +
-                "      \"max_expansions\" : 10,\n" +
+                "      \"max_expansions\" : 30,\n" +
                 "      \"fuzzy_transpositions\" : true,\n" +
                 "      \"lenient\" : false,\n" +
                 "      \"zero_terms_query\" : \"NONE\",\n" +
@@ -338,31 +321,57 @@ public class MatchQueryBuilderTests extends AbstractQueryTestCase<MatchQueryBuil
                 "    }\n" +
                 "  }\n" +
                 "}";
-        qb = (MatchQueryBuilder) parseQuery(json3);
-        checkGeneratedJson(expected, qb);
+        MatchQueryBuilder qb = (MatchQueryBuilder) parseQuery(json, ParseFieldMatcher.EMPTY);
+        checkGeneratedJson(json, qb);
+
+        assertEquals(json, expectedQB, qb);
+
+        assertSerialization(qb);
+
+        // Now check with strict parsing an exception is thrown
+        try {
+            parseQuery(json, ParseFieldMatcher.STRICT);
+            fail("Expected query to fail with strict parsing");
+        } catch (IllegalArgumentException e) {
+            assertThat(e.getMessage(),
+                    containsString("Deprecated field [type] used, replaced by [match_phrase and match_phrase_prefix query]"));
+        }
     }
 
-    public void testSimpleMatchQuery() throws IOException {
+    public void testLegacyMatchPhraseQuery() throws IOException {
+        MatchQueryBuilder expectedQB = new MatchQueryBuilder("message", "to be or not to be");
+        expectedQB.type(Type.PHRASE);
+        expectedQB.slop(2);
         String json = "{\n" +
                 "  \"match\" : {\n" +
                 "    \"message\" : {\n" +
                 "      \"query\" : \"to be or not to be\",\n" +
-                "      \"type\" : \"boolean\",\n" +
-                "      \"operator\" : \"AND\",\n" +
-                "      \"slop\" : 0,\n" +
+                "      \"type\" : \"phrase\",\n" +
+                "      \"operator\" : \"OR\",\n" +
+                "      \"slop\" : 2,\n" +
                 "      \"prefix_length\" : 0,\n" +
                 "      \"max_expansions\" : 50,\n" +
                 "      \"fuzzy_transpositions\" : true,\n" +
                 "      \"lenient\" : false,\n" +
-                "      \"zero_terms_query\" : \"ALL\",\n" +
+                "      \"zero_terms_query\" : \"NONE\",\n" +
                 "      \"boost\" : 1.0\n" +
                 "    }\n" +
                 "  }\n" +
                 "}";
-        MatchQueryBuilder qb = (MatchQueryBuilder) parseQuery(json);
+        MatchQueryBuilder qb = (MatchQueryBuilder) parseQuery(json, ParseFieldMatcher.EMPTY);
         checkGeneratedJson(json, qb);
 
-        assertEquals(json, "to be or not to be", qb.value());
-        assertEquals(json, Operator.AND, qb.operator());
+        assertEquals(json, expectedQB, qb);
+
+        assertSerialization(qb);
+
+        // Now check with strict parsing an exception is thrown
+        try {
+            parseQuery(json, ParseFieldMatcher.STRICT);
+            fail("Expected query to fail with strict parsing");
+        } catch (IllegalArgumentException e) {
+            assertThat(e.getMessage(),
+                    containsString("Deprecated field [type] used, replaced by [match_phrase and match_phrase_prefix query]"));
+        }
     }
 }

+ 0 - 2
core/src/test/java/org/elasticsearch/index/query/NestedQueryBuilderTests.java

@@ -150,9 +150,7 @@ public class NestedQueryBuilderTests extends AbstractQueryTestCase<NestedQueryBu
                 "          \"match\" : {\n" +
                 "            \"obj1.name\" : {\n" +
                 "              \"query\" : \"blue\",\n" +
-                "              \"type\" : \"boolean\",\n" +
                 "              \"operator\" : \"OR\",\n" +
-                "              \"slop\" : 0,\n" +
                 "              \"prefix_length\" : 0,\n" +
                 "              \"max_expansions\" : 50,\n" +
                 "              \"fuzzy_transpositions\" : true,\n" +