Browse Source

Remove the MissingQueryBuilder which was deprecated in 2.2.0.
As a replacement use ExistsQueryBuilder inside a mustNot() clause.
So instead of using `new ExistsQueryBuilder(name)` now use:
`new BoolQueryBuilder().mustNot(new ExistsQueryBuilder(name))`.

Closes #14112

Jim Ferenczi 9 years ago
parent
commit
437488ae64
19 changed files with 63 additions and 794 deletions
  1. 0 1
      core/src/main/java/org/apache/lucene/queryparser/classic/MapperQueryParser.java
  2. 0 42
      core/src/main/java/org/apache/lucene/queryparser/classic/MissingFieldQueryExtension.java
  3. 0 234
      core/src/main/java/org/elasticsearch/index/query/MissingQueryBuilder.java
  4. 0 88
      core/src/main/java/org/elasticsearch/index/query/MissingQueryParser.java
  5. 0 21
      core/src/main/java/org/elasticsearch/index/query/QueryBuilders.java
  6. 0 1
      core/src/main/java/org/elasticsearch/indices/IndicesModule.java
  7. 1 1
      core/src/test/java/org/elasticsearch/aliases/IndexAliasesIT.java
  8. 0 17
      core/src/test/java/org/elasticsearch/bwcompat/BasicBackwardsCompatibilityIT.java
  9. 0 7
      core/src/test/java/org/elasticsearch/bwcompat/OldIndexBackwardsCompatibilityIT.java
  10. 0 107
      core/src/test/java/org/elasticsearch/index/query/MissingQueryBuilderTests.java
  11. 0 5
      core/src/test/java/org/elasticsearch/index/query/QueryDSLDocumentationTests.java
  12. 1 1
      core/src/test/java/org/elasticsearch/indices/template/SimpleIndexTemplateIT.java
  13. 5 3
      core/src/test/java/org/elasticsearch/search/highlight/HighlighterSearchIT.java
  14. 33 89
      core/src/test/java/org/elasticsearch/search/query/ExistsIT.java
  15. 0 27
      core/src/test/java/org/elasticsearch/search/query/SearchQueryIT.java
  16. 0 14
      docs/java-api/query-dsl/missing-query.asciidoc
  17. 4 3
      docs/reference/migration/migrate_3_0.asciidoc
  18. 19 1
      docs/reference/query-dsl/exists-query.asciidoc
  19. 0 132
      docs/reference/query-dsl/missing-query.asciidoc

+ 0 - 1
core/src/main/java/org/apache/lucene/queryparser/classic/MapperQueryParser.java

@@ -54,7 +54,6 @@ public class MapperQueryParser extends QueryParser {
     static {
         Map<String, FieldQueryExtension> fieldQueryExtensions = new HashMap<>();
         fieldQueryExtensions.put(ExistsFieldQueryExtension.NAME, new ExistsFieldQueryExtension());
-        fieldQueryExtensions.put(MissingFieldQueryExtension.NAME, new MissingFieldQueryExtension());
         FIELD_QUERY_EXTENSIONS = unmodifiableMap(fieldQueryExtensions);
     }
 

+ 0 - 42
core/src/main/java/org/apache/lucene/queryparser/classic/MissingFieldQueryExtension.java

@@ -1,42 +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.search.ConstantScoreQuery;
-import org.apache.lucene.search.Query;
-import org.elasticsearch.index.query.MissingQueryBuilder;
-import org.elasticsearch.index.query.QueryShardContext;
-
-/**
- *
- */
-public class MissingFieldQueryExtension implements FieldQueryExtension {
-
-    public static final String NAME = "_missing_";
-
-    @Override
-    public Query query(QueryShardContext context, String queryText) {
-        Query query = MissingQueryBuilder.newFilter(context, queryText, MissingQueryBuilder.DEFAULT_EXISTENCE_VALUE, MissingQueryBuilder.DEFAULT_NULL_VALUE);
-        if (query != null) {
-            return new ConstantScoreQuery(query);
-        }
-        return null;
-    }
-}

+ 0 - 234
core/src/main/java/org/elasticsearch/index/query/MissingQueryBuilder.java

@@ -1,234 +0,0 @@
-/*
- * Licensed to Elasticsearch under one or more contributor
- * license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright
- * ownership. Elasticsearch licenses this file to you under
- * the Apache License, Version 2.0 (the "License"); you may
- * not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *    http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-package org.elasticsearch.index.query;
-
-import org.apache.lucene.search.BooleanClause;
-import org.apache.lucene.search.BooleanQuery;
-import org.apache.lucene.search.ConstantScoreQuery;
-import org.apache.lucene.search.Query;
-import org.apache.lucene.search.TermRangeQuery;
-import org.elasticsearch.common.Strings;
-import org.elasticsearch.common.io.stream.StreamInput;
-import org.elasticsearch.common.io.stream.StreamOutput;
-import org.elasticsearch.common.lucene.search.Queries;
-import org.elasticsearch.common.xcontent.XContentBuilder;
-import org.elasticsearch.index.mapper.MappedFieldType;
-import org.elasticsearch.index.mapper.internal.FieldNamesFieldMapper;
-import org.elasticsearch.index.mapper.object.ObjectMapper;
-
-import java.io.IOException;
-import java.util.Collection;
-import java.util.Objects;
-
-/**
- * Constructs a filter that have only null values or no value in the original field.
- */
-public class MissingQueryBuilder extends AbstractQueryBuilder<MissingQueryBuilder> {
-
-    public static final String NAME = "missing";
-
-    public static final boolean DEFAULT_NULL_VALUE = false;
-
-    public static final boolean DEFAULT_EXISTENCE_VALUE = true;
-
-    private final String fieldPattern;
-
-    private final boolean nullValue;
-
-    private final boolean existence;
-
-    static final MissingQueryBuilder PROTOTYPE = new MissingQueryBuilder("field", DEFAULT_NULL_VALUE, DEFAULT_EXISTENCE_VALUE);
-
-    /**
-     * Constructs a filter that returns documents with only null values or no value in the original field.
-     * @param fieldPattern the field to query
-     * @param nullValue should the missing filter automatically include fields with null value configured in the
-     * mappings. Defaults to <tt>false</tt>.
-     * @param existence should the missing filter include documents where the field doesn't exist in the docs.
-     * Defaults to <tt>true</tt>.
-     * @throws IllegalArgumentException when both <tt>existence</tt> and <tt>nullValue</tt> are set to false
-     */
-    public MissingQueryBuilder(String fieldPattern, boolean nullValue, boolean existence) {
-        if (Strings.isEmpty(fieldPattern)) {
-            throw new IllegalArgumentException("missing query must be provided with a [field]");
-        }
-        if (nullValue == false && existence == false) {
-            throw new IllegalArgumentException("missing query must have either 'existence', or 'null_value', or both set to true");
-        }
-        this.fieldPattern = fieldPattern;
-        this.nullValue = nullValue;
-        this.existence = existence;
-    }
-
-    public MissingQueryBuilder(String fieldPattern) {
-        this(fieldPattern, DEFAULT_NULL_VALUE, DEFAULT_EXISTENCE_VALUE);
-    }
-
-    public String fieldPattern() {
-        return this.fieldPattern;
-    }
-
-    /**
-     * Returns true if the missing filter will include documents where the field contains a null value, otherwise
-     * these documents will not be included.
-     */
-    public boolean nullValue() {
-        return this.nullValue;
-    }
-
-    /**
-     * Returns true if the missing filter will include documents where the field has no values, otherwise
-     * these documents will not be included.
-     */
-    public boolean existence() {
-        return this.existence;
-    }
-
-    @Override
-    protected void doXContent(XContentBuilder builder, Params params) throws IOException {
-        builder.startObject(NAME);
-        builder.field(MissingQueryParser.FIELD_FIELD.getPreferredName(), fieldPattern);
-        builder.field(MissingQueryParser.NULL_VALUE_FIELD.getPreferredName(), nullValue);
-        builder.field(MissingQueryParser.EXISTENCE_FIELD.getPreferredName(), existence);
-        printBoostAndQueryName(builder);
-        builder.endObject();
-    }
-
-    @Override
-    public String getWriteableName() {
-        return NAME;
-    }
-
-    @Override
-    protected Query doToQuery(QueryShardContext context) throws IOException {
-        return newFilter(context, fieldPattern, existence, nullValue);
-    }
-
-    public static Query newFilter(QueryShardContext context, String fieldPattern, boolean existence, boolean nullValue) {
-        if (!existence && !nullValue) {
-            throw new QueryShardException(context, "missing must have either existence, or null_value, or both set to true");
-        }
-
-        final FieldNamesFieldMapper.FieldNamesFieldType fieldNamesFieldType = (FieldNamesFieldMapper.FieldNamesFieldType) context.getMapperService().fullName(FieldNamesFieldMapper.NAME);
-        if (fieldNamesFieldType == null) {
-            // can only happen when no types exist, so no docs exist either
-            return Queries.newMatchNoDocsQuery();
-        }
-
-        ObjectMapper objectMapper = context.getObjectMapper(fieldPattern);
-        if (objectMapper != null) {
-            // automatic make the object mapper pattern
-            fieldPattern = fieldPattern + ".*";
-        }
-
-        Collection<String> fields = context.simpleMatchToIndexNames(fieldPattern);
-        if (fields.isEmpty()) {
-            if (existence) {
-                // if we ask for existence of fields, and we found none, then we should match on all
-                return Queries.newMatchAllQuery();
-            }
-            return null;
-        }
-
-        Query existenceFilter = null;
-        Query nullFilter = null;
-
-        if (existence) {
-            BooleanQuery.Builder boolFilter = new BooleanQuery.Builder();
-            for (String field : fields) {
-                MappedFieldType fieldType = context.fieldMapper(field);
-                Query filter = null;
-                if (fieldNamesFieldType.isEnabled()) {
-                    final String f;
-                    if (fieldType != null) {
-                        f = fieldType.names().indexName();
-                    } else {
-                        f = field;
-                    }
-                    filter = fieldNamesFieldType.termQuery(f, context);
-                }
-                // if _field_names are not indexed, we need to go the slow way
-                if (filter == null && fieldType != null) {
-                    filter = fieldType.rangeQuery(null, null, true, true);
-                }
-                if (filter == null) {
-                    filter = new TermRangeQuery(field, null, null, true, true);
-                }
-                boolFilter.add(filter, BooleanClause.Occur.SHOULD);
-            }
-
-            existenceFilter = boolFilter.build();
-            existenceFilter = Queries.not(existenceFilter);;
-        }
-
-        if (nullValue) {
-            for (String field : fields) {
-                MappedFieldType fieldType = context.fieldMapper(field);
-                if (fieldType != null) {
-                    nullFilter = fieldType.nullValueQuery();
-                }
-            }
-        }
-
-        Query filter;
-        if (nullFilter != null) {
-            if (existenceFilter != null) {
-                filter = new BooleanQuery.Builder()
-                        .add(existenceFilter, BooleanClause.Occur.SHOULD)
-                        .add(nullFilter, BooleanClause.Occur.SHOULD)
-                        .build();
-            } else {
-                filter = nullFilter;
-            }
-        } else {
-            filter = existenceFilter;
-        }
-
-        if (filter == null) {
-            return null;
-        }
-
-        return new ConstantScoreQuery(filter);
-    }
-
-    @Override
-    protected MissingQueryBuilder doReadFrom(StreamInput in) throws IOException {
-        return new MissingQueryBuilder(in.readString(), in.readBoolean(), in.readBoolean());
-    }
-
-    @Override
-    protected void doWriteTo(StreamOutput out) throws IOException {
-        out.writeString(fieldPattern);
-        out.writeBoolean(nullValue);
-        out.writeBoolean(existence);
-    }
-
-    @Override
-    protected int doHashCode() {
-        return Objects.hash(fieldPattern, nullValue, existence);
-    }
-
-    @Override
-    protected boolean doEquals(MissingQueryBuilder other) {
-        return Objects.equals(fieldPattern, other.fieldPattern) &&
-                Objects.equals(nullValue, other.nullValue) &&
-                Objects.equals(existence, other.existence);
-    }
-}

+ 0 - 88
core/src/main/java/org/elasticsearch/index/query/MissingQueryParser.java

@@ -1,88 +0,0 @@
-/*
- * Licensed to Elasticsearch under one or more contributor
- * license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright
- * ownership. Elasticsearch licenses this file to you under
- * the Apache License, Version 2.0 (the "License"); you may
- * not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *    http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-package org.elasticsearch.index.query;
-
-import org.elasticsearch.common.ParseField;
-import org.elasticsearch.common.ParsingException;
-import org.elasticsearch.common.xcontent.XContentParser;
-
-import java.io.IOException;
-
-/**
- * Parser for missing query
- */
-public class MissingQueryParser implements QueryParser<MissingQueryBuilder> {
-
-    public static final ParseField FIELD_FIELD = new ParseField("field");
-    public static final ParseField NULL_VALUE_FIELD = new ParseField("null_value");
-    public static final ParseField EXISTENCE_FIELD = new ParseField("existence");
-
-    @Override
-    public String[] names() {
-        return new String[]{MissingQueryBuilder.NAME};
-    }
-
-    @Override
-    public MissingQueryBuilder fromXContent(QueryParseContext parseContext) throws IOException {
-        XContentParser parser = parseContext.parser();
-
-        String fieldPattern = null;
-        String queryName = null;
-        float boost = AbstractQueryBuilder.DEFAULT_BOOST;
-        boolean nullValue = MissingQueryBuilder.DEFAULT_NULL_VALUE;
-        boolean existence = MissingQueryBuilder.DEFAULT_EXISTENCE_VALUE;
-
-        XContentParser.Token token;
-        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, FIELD_FIELD)) {
-                    fieldPattern = parser.text();
-                } else if (parseContext.parseFieldMatcher().match(currentFieldName, NULL_VALUE_FIELD)) {
-                    nullValue = parser.booleanValue();
-                } else if (parseContext.parseFieldMatcher().match(currentFieldName, EXISTENCE_FIELD)) {
-                    existence = parser.booleanValue();
-                } else if (parseContext.parseFieldMatcher().match(currentFieldName, AbstractQueryBuilder.NAME_FIELD)) {
-                    queryName = parser.text();
-                } else if (parseContext.parseFieldMatcher().match(currentFieldName, AbstractQueryBuilder.BOOST_FIELD)) {
-                    boost = parser.floatValue();
-                } else {
-                    throw new ParsingException(parser.getTokenLocation(), "[" + MissingQueryBuilder.NAME + "] query does not support [" + currentFieldName + "]");
-                }
-            } else {
-                throw new ParsingException(parser.getTokenLocation(), "[" + MissingQueryBuilder.NAME + "] unknown token [" + token + "] after [" + currentFieldName + "]");
-            }
-        }
-
-        if (fieldPattern == null) {
-            throw new ParsingException(parser.getTokenLocation(), "missing must be provided with a [field]");
-        }
-        return new MissingQueryBuilder(fieldPattern, nullValue, existence)
-                .boost(boost)
-                .queryName(queryName);
-    }
-
-    @Override
-    public MissingQueryBuilder getBuilderPrototype() {
-        return MissingQueryBuilder.PROTOTYPE;
-    }
-}

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

@@ -810,27 +810,6 @@ public abstract class QueryBuilders {
         return new ExistsQueryBuilder(name);
     }
 
-    /**
-     * A filter to filter only documents where a field does not exists in them.
-     * @param name the field to query
-     */
-    public static MissingQueryBuilder missingQuery(String name) {
-        return missingQuery(name, MissingQueryBuilder.DEFAULT_NULL_VALUE, MissingQueryBuilder.DEFAULT_EXISTENCE_VALUE);
-    }
-
-    /**
-     * A filter to filter only documents where a field does not exists in them.
-     * @param name the field to query
-     * @param nullValue should the missing filter automatically include fields with null value configured in the
-     * mappings. Defaults to <tt>false</tt>.
-     * @param existence should the missing filter include documents where the field doesn't exist in the docs.
-     * Defaults to <tt>true</tt>.
-     * @throws IllegalArgumentException when both <tt>existence</tt> and <tt>nullValue</tt> are set to false
-     */
-    public static MissingQueryBuilder missingQuery(String name, boolean nullValue, boolean existence) {
-        return new MissingQueryBuilder(name, nullValue, existence);
-    }
-
     private QueryBuilders() {
 
     }

+ 0 - 1
core/src/main/java/org/elasticsearch/indices/IndicesModule.java

@@ -120,7 +120,6 @@ public class IndicesModule extends AbstractModule {
         registerQueryParser(GeohashCellQuery.Parser.class);
         registerQueryParser(GeoPolygonQueryParser.class);
         registerQueryParser(ExistsQueryParser.class);
-        registerQueryParser(MissingQueryParser.class);
         registerQueryParser(MatchNoneQueryParser.class);
 
         if (ShapesAvailability.JTS_AVAILABLE) {

+ 1 - 1
core/src/test/java/org/elasticsearch/aliases/IndexAliasesIT.java

@@ -865,7 +865,7 @@ public class IndexAliasesIT extends ESIntegTestCase {
         assertAcked(prepareCreate("test")
                 .addMapping("type", "field", "type=string")
                 .addAlias(new Alias("alias1"))
-                .addAlias(new Alias("alias2").filter(QueryBuilders.missingQuery("field")))
+                .addAlias(new Alias("alias2").filter(QueryBuilders.boolQuery().mustNot(QueryBuilders.existsQuery("field"))))
                 .addAlias(new Alias("alias3").indexRouting("index").searchRouting("search")));
 
         checkAliases();

+ 0 - 17
core/src/test/java/org/elasticsearch/bwcompat/BasicBackwardsCompatibilityIT.java

@@ -71,7 +71,6 @@ import java.util.concurrent.ExecutionException;
 import static org.elasticsearch.common.xcontent.XContentFactory.jsonBuilder;
 import static org.elasticsearch.index.query.QueryBuilders.constantScoreQuery;
 import static org.elasticsearch.index.query.QueryBuilders.existsQuery;
-import static org.elasticsearch.index.query.QueryBuilders.missingQuery;
 import static org.elasticsearch.index.query.QueryBuilders.queryStringQuery;
 import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertAcked;
 import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertHitCount;
@@ -440,25 +439,9 @@ public class BasicBackwardsCompatibilityIT extends ESBackcompatTestCase {
             countResponse = client().prepareSearch().setSize(0).setQuery(existsQuery("obj1")).get();
             assertHitCount(countResponse, 2l);
 
-            countResponse = client().prepareSearch().setSize(0).setQuery(missingQuery("field1")).get();
-            assertHitCount(countResponse, 2l);
-
-            countResponse = client().prepareSearch().setSize(0).setQuery(missingQuery("field1")).get();
-            assertHitCount(countResponse, 2l);
-
-            countResponse = client().prepareSearch().setSize(0).setQuery(constantScoreQuery(missingQuery("field1"))).get();
-            assertHitCount(countResponse, 2l);
-
             countResponse = client().prepareSearch().setSize(0).setQuery(queryStringQuery("_missing_:field1")).get();
             assertHitCount(countResponse, 2l);
 
-            // wildcard check
-            countResponse = client().prepareSearch().setSize(0).setQuery(missingQuery("x*")).get();
-            assertHitCount(countResponse, 2l);
-
-            // object check
-            countResponse = client().prepareSearch().setSize(0).setQuery(missingQuery("obj1")).get();
-            assertHitCount(countResponse, 2l);
             if (!backwardsCluster().upgradeOneNode()) {
                 break;
             }

+ 0 - 7
core/src/test/java/org/elasticsearch/bwcompat/OldIndexBackwardsCompatibilityIT.java

@@ -341,13 +341,6 @@ public class OldIndexBackwardsCompatibilityIT extends ESIntegTestCase {
         searchRsp = searchReq.get();
         ElasticsearchAssertions.assertNoFailures(searchRsp);
         assertEquals(numDocs, searchRsp.getHits().getTotalHits());
-
-        logger.info("--> testing missing filter");
-        // the field for the missing filter here needs to be different than the exists filter above, to avoid being found in the cache
-        searchReq = client().prepareSearch(indexName).setQuery(QueryBuilders.missingQuery("long_sort"));
-        searchRsp = searchReq.get();
-        ElasticsearchAssertions.assertNoFailures(searchRsp);
-        assertEquals(0, searchRsp.getHits().getTotalHits());
     }
 
     void assertBasicAggregationWorks(String indexName) {

+ 0 - 107
core/src/test/java/org/elasticsearch/index/query/MissingQueryBuilderTests.java

@@ -1,107 +0,0 @@
-/*
- * Licensed to Elasticsearch under one or more contributor
- * license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright
- * ownership. Elasticsearch licenses this file to you under
- * the Apache License, Version 2.0 (the "License"); you may
- * not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *    http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-package org.elasticsearch.index.query;
-
-import org.apache.lucene.search.Query;
-
-import java.io.IOException;
-
-import static org.hamcrest.Matchers.containsString;
-
-public class MissingQueryBuilderTests extends AbstractQueryTestCase<MissingQueryBuilder> {
-
-    @Override
-    protected MissingQueryBuilder doCreateTestQueryBuilder() {
-        String fieldName = randomBoolean() ? randomFrom(MAPPED_FIELD_NAMES) : randomAsciiOfLengthBetween(1, 10);
-        Boolean existence = randomBoolean();
-        Boolean nullValue = randomBoolean();
-        if (existence == false && nullValue == false) {
-            if (randomBoolean()) {
-                existence = true;
-            } else {
-                nullValue = true;
-            }
-        }
-        return new MissingQueryBuilder(fieldName, nullValue, existence);
-    }
-
-    @Override
-    protected void doAssertLuceneQuery(MissingQueryBuilder queryBuilder, Query query, QueryShardContext context) throws IOException {
-        // too many mapping dependent cases to test, we don't want to end up
-        // duplication the toQuery method
-    }
-
-    public void testIllegalArguments() {
-        try {
-            if (randomBoolean()) {
-                new MissingQueryBuilder("", true, true);
-            } else {
-                new MissingQueryBuilder(null, true, true);
-            }
-            fail("must not be null or empty");
-        } catch (IllegalArgumentException e) {
-            // expected
-        }
-
-        try {
-            new MissingQueryBuilder("fieldname", false, false);
-            fail("existence and nullValue cannot both be false");
-        } catch (IllegalArgumentException e) {
-            // expected
-        }
-
-        try {
-            new MissingQueryBuilder("fieldname", MissingQueryBuilder.DEFAULT_NULL_VALUE, false);
-            fail("existence and nullValue cannot both be false");
-        } catch (IllegalArgumentException e) {
-            // expected
-        }
-    }
-
-    public void testBothNullValueAndExistenceFalse() throws IOException {
-        QueryShardContext context = createShardContext();
-        context.setAllowUnmappedFields(true);
-        try {
-            MissingQueryBuilder.newFilter(context, "field", false, false);
-            fail("Expected QueryShardException");
-        } catch (QueryShardException e) {
-            assertThat(e.getMessage(), containsString("missing must have either existence, or null_value"));
-        }
-    }
-
-    public void testFromJson() throws IOException {
-        String json =
-            "{\n" + 
-                "  \"missing\" : {\n" + 
-                "    \"field\" : \"user\",\n" + 
-                "    \"null_value\" : false,\n" + 
-                "    \"existence\" : true,\n" + 
-                "    \"boost\" : 1.0\n" + 
-                "  }\n" + 
-                "}";
-
-        MissingQueryBuilder parsed = (MissingQueryBuilder) parseQuery(json);
-        checkGeneratedJson(json, parsed);
-
-        assertEquals(json, false, parsed.nullValue());
-        assertEquals(json, true, parsed.existence());
-        assertEquals(json, "user", parsed.fieldPattern());
-    }
-}

+ 0 - 5
core/src/test/java/org/elasticsearch/index/query/QueryDSLDocumentationTests.java

@@ -58,7 +58,6 @@ import static org.elasticsearch.index.query.QueryBuilders.idsQuery;
 import static org.elasticsearch.index.query.QueryBuilders.indicesQuery;
 import static org.elasticsearch.index.query.QueryBuilders.matchAllQuery;
 import static org.elasticsearch.index.query.QueryBuilders.matchQuery;
-import static org.elasticsearch.index.query.QueryBuilders.missingQuery;
 import static org.elasticsearch.index.query.QueryBuilders.moreLikeThisQuery;
 import static org.elasticsearch.index.query.QueryBuilders.multiMatchQuery;
 import static org.elasticsearch.index.query.QueryBuilders.nestedQuery;
@@ -240,10 +239,6 @@ public class QueryDSLDocumentationTests extends ESTestCase {
         matchQuery("name", "kimchy elasticsearch");
     }
 
-    public void testMissing() {
-        missingQuery("user", true, true);
-    }
-
     public void testMLT() {
         String[] fields = {"name.first", "name.last"};
         String[] texts = {"text like this one"};

+ 1 - 1
core/src/test/java/org/elasticsearch/indices/template/SimpleIndexTemplateIT.java

@@ -560,7 +560,7 @@ public class SimpleIndexTemplateIT extends ESIntegTestCase {
                 .setOrder(0)
                 .addAlias(new Alias("alias1"))
                 .addAlias(new Alias("{index}-alias"))
-                .addAlias(new Alias("alias3").filter(QueryBuilders.missingQuery("test")))
+                .addAlias(new Alias("alias3").filter(QueryBuilders.boolQuery().mustNot(QueryBuilders.existsQuery("test"))))
                 .addAlias(new Alias("alias4")).get();
 
         client().admin().indices().preparePutTemplate("template2")

+ 5 - 3
core/src/test/java/org/elasticsearch/search/highlight/HighlighterSearchIT.java

@@ -58,7 +58,6 @@ import static org.elasticsearch.index.query.QueryBuilders.fuzzyQuery;
 import static org.elasticsearch.index.query.QueryBuilders.matchPhrasePrefixQuery;
 import static org.elasticsearch.index.query.QueryBuilders.matchPhraseQuery;
 import static org.elasticsearch.index.query.QueryBuilders.matchQuery;
-import static org.elasticsearch.index.query.QueryBuilders.missingQuery;
 import static org.elasticsearch.index.query.QueryBuilders.multiMatchQuery;
 import static org.elasticsearch.index.query.QueryBuilders.prefixQuery;
 import static org.elasticsearch.index.query.QueryBuilders.queryStringQuery;
@@ -67,6 +66,7 @@ import static org.elasticsearch.index.query.QueryBuilders.regexpQuery;
 import static org.elasticsearch.index.query.QueryBuilders.termQuery;
 import static org.elasticsearch.index.query.QueryBuilders.typeQuery;
 import static org.elasticsearch.index.query.QueryBuilders.wildcardQuery;
+import static org.elasticsearch.index.query.QueryBuilders.existsQuery;
 import static org.elasticsearch.search.builder.SearchSourceBuilder.highlight;
 import static org.elasticsearch.search.builder.SearchSourceBuilder.searchSource;
 import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertAcked;
@@ -2471,7 +2471,7 @@ public class HighlighterSearchIT extends ESIntegTestCase {
 
         logger.info("--> highlighting and searching on field1");
         SearchSourceBuilder source = searchSource().query(boolQuery()
-                .should(constantScoreQuery(QueryBuilders.missingQuery("field1")))
+                .should(boolQuery().mustNot(constantScoreQuery(QueryBuilders.existsQuery("field1"))))
                 .should(matchQuery("field1", "test"))
                 .should(constantScoreQuery(queryStringQuery("field1:photo*"))))
                 .highlighter(highlight().field("field1"));
@@ -2501,7 +2501,9 @@ public class HighlighterSearchIT extends ESIntegTestCase {
         refresh();
 
         logger.info("--> highlighting and searching on field1");
-        SearchSourceBuilder source = searchSource().query(boolQuery().must(queryStringQuery("field1:photo*")).filter(missingQuery("field_null")))
+        SearchSourceBuilder source = searchSource().query(boolQuery()
+                .must(queryStringQuery("field1:photo*"))
+                .mustNot(existsQuery("field_null")))
                 .highlighter(highlight().field("field1"));
         SearchResponse searchResponse = client().prepareSearch("test").setSource(source).get();
         assertHighlight(searchResponse, 0, "field1", 0, 1, equalTo("The <em>photography</em> word will get highlighted"));

+ 33 - 89
core/src/test/java/org/elasticsearch/search/query/ExistsMissingIT.java → core/src/test/java/org/elasticsearch/search/query/ExistsIT.java

@@ -43,7 +43,7 @@ import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertHitC
 import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertSearchHits;
 import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertSearchResponse;
 
-public class ExistsMissingIT extends ESIntegTestCase {
+public class ExistsIT extends ESIntegTestCase {
 
     // TODO: move this to a unit test somewhere...
     public void testEmptyIndex() throws Exception {
@@ -51,50 +51,50 @@ public class ExistsMissingIT extends ESIntegTestCase {
         ensureYellow("test");
         SearchResponse resp = client().prepareSearch("test").setQuery(QueryBuilders.existsQuery("foo")).execute().actionGet();
         assertSearchResponse(resp);
-        resp = client().prepareSearch("test").setQuery(QueryBuilders.missingQuery("foo")).execute().actionGet();
+        resp = client().prepareSearch("test").setQuery(QueryBuilders.boolQuery().mustNot(QueryBuilders.existsQuery("foo"))).execute().actionGet();
         assertSearchResponse(resp);
     }
 
-    public void testExistsMissing() throws Exception {
+    public void testExists() throws Exception {
         XContentBuilder mapping = XContentBuilder.builder(JsonXContent.jsonXContent)
-            .startObject()
+                .startObject()
                 .startObject("type")
-                    .startObject(FieldNamesFieldMapper.NAME)
-                        .field("enabled", randomBoolean())
-                    .endObject()
-                    .startObject("properties")
-                        .startObject("foo")
-                            .field("type", "string")
-                        .endObject()
-                        .startObject("bar")
-                            .field("type", "object")
-                            .startObject("properties")
-                                .startObject("foo")
-                                    .field("type", "string")
-                                .endObject()
-                                .startObject("bar")
-                                    .field("type", "object")
-                                    .startObject("properties")
-                                        .startObject("bar")
-                                            .field("type", "string")
-                                        .endObject()
-                                    .endObject()
-                                .endObject()
-                                .startObject("baz")
-                                    .field("type", "long")
-                                .endObject()
-                            .endObject()
-                        .endObject()
-                    .endObject()
+                .startObject(FieldNamesFieldMapper.NAME)
+                .field("enabled", randomBoolean())
                 .endObject()
-            .endObject();
+                .startObject("properties")
+                .startObject("foo")
+                .field("type", "string")
+                .endObject()
+                .startObject("bar")
+                .field("type", "object")
+                .startObject("properties")
+                .startObject("foo")
+                .field("type", "string")
+                .endObject()
+                .startObject("bar")
+                .field("type", "object")
+                .startObject("properties")
+                .startObject("bar")
+                .field("type", "string")
+                .endObject()
+                .endObject()
+                .endObject()
+                .startObject("baz")
+                .field("type", "long")
+                .endObject()
+                .endObject()
+                .endObject()
+                .endObject()
+                .endObject()
+                .endObject();
 
         assertAcked(client().admin().indices().prepareCreate("idx").addMapping("type", mapping));
         @SuppressWarnings("unchecked")
         Map<String, Object> barObject = new HashMap<>();
         barObject.put("foo", "bar");
         barObject.put("bar", singletonMap("bar", "foo"));
-        final Map<String, Object>[] sources = new Map[] {
+        final Map<String, Object>[] sources = new Map[]{
                 // simple property
                 singletonMap("foo", "bar"),
                 // object fields
@@ -145,62 +145,6 @@ public class ExistsMissingIT extends ESIntegTestCase {
                 }
                 throw e;
             }
-
-            // missing
-            resp = client().prepareSearch("idx").setQuery(QueryBuilders.missingQuery(fieldName)).execute().actionGet();
-            assertSearchResponse(resp);
-            assertEquals(String.format(Locale.ROOT, "missing(%s, %d) mapping: %s response: %s", fieldName, count, mapping.string(), resp), numDocs - count, resp.getHits().totalHits());
         }
     }
-
-    public void testNullValueUnset() throws Exception {
-        assertAcked(client().admin().indices().prepareCreate("idx").addMapping("type", "f", "type=string,index=not_analyzed"));
-        indexRandom(true,
-                client().prepareIndex("idx", "type", "1").setSource("f", "foo"),
-                client().prepareIndex("idx", "type", "2").setSource("f", null),
-                client().prepareIndex("idx", "type", "3").setSource("g", "bar"),
-                client().prepareIndex("idx", "type", "4").setSource("f", "bar"));
-
-        SearchResponse resp = client().prepareSearch("idx").setQuery(QueryBuilders.missingQuery("f", true, true)).get();
-        assertSearchHits(resp, "2", "3");
-
-        resp = client().prepareSearch("idx").setQuery(QueryBuilders.missingQuery("f", false, true)).get();
-        assertSearchHits(resp, "2", "3");
-
-        resp = client().prepareSearch("idx").setQuery(QueryBuilders.missingQuery("f", true, false)).get();
-        assertSearchHits(resp);
-
-        try {
-            client().prepareSearch("idx").setQuery(QueryBuilders.missingQuery("f", false, false)).get();
-            fail("both existence and null_value can't be false");
-        } catch (IllegalArgumentException e) {
-            // expected
-        }
-    }
-
-    public void testNullValueSet() throws Exception {
-        assertAcked(client().admin().indices().prepareCreate("idx").addMapping("type", "f", "type=string,index=not_analyzed,null_value=bar"));
-        indexRandom(true,
-                client().prepareIndex("idx", "type", "1").setSource("f", "foo"),
-                client().prepareIndex("idx", "type", "2").setSource("f", null),
-                client().prepareIndex("idx", "type", "3").setSource("g", "bar"),
-                client().prepareIndex("idx", "type", "4").setSource("f", "bar"));
-
-        SearchResponse resp = client().prepareSearch("idx").setQuery(QueryBuilders.missingQuery("f", true, true)).get();
-        assertSearchHits(resp, "2", "3", "4");
-
-        resp = client().prepareSearch("idx").setQuery(QueryBuilders.missingQuery("f", false, true)).get();
-        assertSearchHits(resp, "3");
-
-        resp = client().prepareSearch("idx").setQuery(QueryBuilders.missingQuery("f", true, false)).get();
-        assertSearchHits(resp, "2", "4");
-
-        try {
-            client().prepareSearch("idx").setQuery(QueryBuilders.missingQuery("f", false, false)).get();
-            fail("both existence and null_value can't be false");
-        } catch (IllegalArgumentException e) {
-            // expected
-        }
-    }
-
 }

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

@@ -70,7 +70,6 @@ import static org.elasticsearch.index.query.QueryBuilders.idsQuery;
 import static org.elasticsearch.index.query.QueryBuilders.indicesQuery;
 import static org.elasticsearch.index.query.QueryBuilders.matchAllQuery;
 import static org.elasticsearch.index.query.QueryBuilders.matchQuery;
-import static org.elasticsearch.index.query.QueryBuilders.missingQuery;
 import static org.elasticsearch.index.query.QueryBuilders.multiMatchQuery;
 import static org.elasticsearch.index.query.QueryBuilders.prefixQuery;
 import static org.elasticsearch.index.query.QueryBuilders.queryStringQuery;
@@ -805,32 +804,6 @@ public class SearchQueryIT extends ESIntegTestCase {
         searchResponse = client().prepareSearch().setQuery(existsQuery("obj1")).get();
         assertHitCount(searchResponse, 2l);
         assertSearchHits(searchResponse, "1", "2");
-
-        searchResponse = client().prepareSearch().setQuery(missingQuery("field1")).get();
-        assertHitCount(searchResponse, 2l);
-        assertSearchHits(searchResponse, "3", "4");
-
-        searchResponse = client().prepareSearch().setQuery(missingQuery("field1")).get();
-        assertHitCount(searchResponse, 2l);
-        assertSearchHits(searchResponse, "3", "4");
-
-        searchResponse = client().prepareSearch().setQuery(constantScoreQuery(missingQuery("field1"))).get();
-        assertHitCount(searchResponse, 2l);
-        assertSearchHits(searchResponse, "3", "4");
-
-        searchResponse = client().prepareSearch().setQuery(queryStringQuery("_missing_:field1")).get();
-        assertHitCount(searchResponse, 2l);
-        assertSearchHits(searchResponse, "3", "4");
-
-        // wildcard check
-        searchResponse = client().prepareSearch().setQuery(missingQuery("x*")).get();
-        assertHitCount(searchResponse, 2l);
-        assertSearchHits(searchResponse, "3", "4");
-
-        // object check
-        searchResponse = client().prepareSearch().setQuery(missingQuery("obj1")).get();
-        assertHitCount(searchResponse, 2l);
-        assertSearchHits(searchResponse, "3", "4");
     }
 
     public void testPassQueryOrFilterAsJSONString() throws Exception {

+ 0 - 14
docs/java-api/query-dsl/missing-query.asciidoc

@@ -1,14 +0,0 @@
-[[java-query-dsl-missing-query]]
-==== Missing Query
-
-See {ref}/query-dsl-missing-query.html[Missing Query]
-
-[source,java]
---------------------------------------------------
-QueryBuilder qb = missingQuery("user",          <1>
-    true,                                       <2>
-    true);                                      <3>
---------------------------------------------------
-<1> field
-<2> find missing field with an explicit `null` value
-<3> find missing field that doesn’t exist

+ 4 - 3
docs/reference/migration/migrate_3_0.asciidoc

@@ -411,9 +411,9 @@ Use the `field(String, float)` method instead.
 
 ==== MissingQueryBuilder
 
-The two individual setters for existence() and nullValue() were removed in favour of
-optional constructor settings in order to better capture and validate their interdependent
-settings at construction time.
+The MissingQueryBuilder which was deprecated in 2.2.0 is removed. As a replacement use ExistsQueryBuilder
+inside a mustNot() clause. So instead of using `new ExistsQueryBuilder(name)` now use
+`new BoolQueryBuilder().mustNot(new ExistsQueryBuilder(name))`.
 
 ==== NotQueryBuilder
 
@@ -491,3 +491,4 @@ from `OsStats.Cpu#getPercent`.
 === Fields option
 Only stored fields are retrievable with this option.
 The fields option won't be able to load non stored fields from _source anymore.
+

+ 19 - 1
docs/reference/query-dsl/exists-query.asciidoc

@@ -38,7 +38,7 @@ These documents would *not* match the above query:
 <3> The `user` field is missing completely.
 
 [float]
-===== `null_value` mapping
+==== `null_value` mapping
 
 If the field mapping includes the <<null-value,`null_value`>> setting
 then explicit `null` values are replaced with the specified `null_value`.  For
@@ -70,3 +70,21 @@ no values in the `user` field and thus would not match the `exists` filter:
 { "foo": "bar" }
 --------------------------------------------------
 
+==== `missing` query
+
+'missing' query has been removed because it can be advantageously replaced by an `exists` query inside a must_not
+clause as follows:
+
+[source,js]
+--------------------------------------------------
+"bool": {
+    "must_not": {
+        "exists": {
+            "field": "user"
+        }
+    }
+}
+--------------------------------------------------
+
+This query returns documents that have no value in the user field.
+

+ 0 - 132
docs/reference/query-dsl/missing-query.asciidoc

@@ -1,132 +0,0 @@
-[[query-dsl-missing-query]]
-=== Missing Query
-
-Returns documents that have only `null` values or no value in the original field:
-
-[source,js]
---------------------------------------------------
-{
-    "constant_score" : {
-        "filter" : {
-            "missing" : { "field" : "user" }
-        }
-    }
-}
---------------------------------------------------
-
-For instance, the following docs would match the above filter:
-
-[source,js]
---------------------------------------------------
-{ "user": null }
-{ "user": [] } <1>
-{ "user": [null] } <2>
-{ "foo":  "bar" } <3>
---------------------------------------------------
-<1> This field has no values.
-<2> This field has no non-`null` values.
-<3> The `user` field is missing completely.
-
-These documents would *not* match the above filter:
-
-[source,js]
---------------------------------------------------
-{ "user": "jane" }
-{ "user": "" } <1>
-{ "user": "-" } <2>
-{ "user": ["jane"] }
-{ "user": ["jane", null ] } <3>
---------------------------------------------------
-<1> An empty string is a non-`null` value.
-<2> Even though the `standard` analyzer would emit zero tokens, the original field is non-`null`.
-<3> This field has one non-`null` value.
-
-[float]
-==== `null_value` mapping
-
-If the field mapping includes a <<null-value,`null_value`>> then explicit `null` values
-are replaced with the specified `null_value`.  For instance, if the `user` field were mapped
-as follows:
-
-[source,js]
---------------------------------------------------
-  "user": {
-    "type": "string",
-    "null_value": "_null_"
-  }
---------------------------------------------------
-
-then explicit `null` values would be indexed as the string `_null_`, and the
-the following docs would *not* match the `missing` filter:
-
-[source,js]
---------------------------------------------------
-{ "user": null }
-{ "user": [null] }
---------------------------------------------------
-
-However, these docs--without explicit `null` values--would still have
-no values in the `user` field and thus would match the `missing` filter:
-
-[source,js]
---------------------------------------------------
-{ "user": [] }
-{ "foo": "bar" }
---------------------------------------------------
-
-[float]
-===== `existence` and `null_value` parameters
-
-When the field being queried has a `null_value` mapping, then the behaviour of
-the `missing` filter can be altered with the `existence` and `null_value`
-parameters:
-
-[source,js]
---------------------------------------------------
-{
-    "constant_score" : {
-        "filter" : {
-            "missing" : {
-                "field" : "user",
-                "existence" : true,
-                "null_value" : false
-            }
-        }
-    }
-}
---------------------------------------------------
-
-
-`existence`::
-+
---
-When the `existence` parameter is set to `true` (the default), the missing
-filter will include documents where the field has *no* values, ie:
-
-[source,js]
---------------------------------------------------
-{ "user": [] }
-{ "foo": "bar" }
---------------------------------------------------
-
-When set to `false`, these documents will not be included.
---
-
-`null_value`::
-+
---
-When the `null_value` parameter is set to `true`, the missing
-filter will include documents where the field contains a `null` value, ie:
-
-[source,js]
---------------------------------------------------
-{ "user": null }
-{ "user": [null] }
-{ "user": ["jane",null] } <1>
---------------------------------------------------
-<1> Matches because the field contains a `null` value, even though it also contains a non-`null` value.
-
-When set to `false` (the default), these documents will not be included.
---
-
-NOTE: Either `existence` or `null_value` or both must be set to `true`.