/*
 * 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.search.query;

import com.google.common.collect.ImmutableMap;
import org.elasticsearch.action.index.IndexRequestBuilder;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.common.xcontent.json.JsonXContent;
import org.elasticsearch.index.mapper.internal.FieldNamesFieldMapper;
import org.elasticsearch.index.query.FilterBuilders;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.test.ElasticsearchIntegrationTest;

import java.util.*;

import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertAcked;
import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertSearchResponse;


public class ExistsMissingTests extends ElasticsearchIntegrationTest {

    public void testExistsMissing() throws Exception {

        XContentBuilder mapping = XContentBuilder.builder(JsonXContent.jsonXContent)
            .startObject()
                .startObject("type")
                    .startObject(FieldNamesFieldMapper.NAME)
                        // by setting randomly index to no we also test the pre-1.3 behavior
                        .field("index", randomFrom("no", "not_analyzed"))
                        .field("store", randomFrom("no", "yes"))
                    .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>[] sources = new Map[] {
                    // simple property
                    ImmutableMap.of("foo", "bar"),
                    // object fields
                    ImmutableMap.of("bar", ImmutableMap.of("foo", "bar", "bar", ImmutableMap.of("bar", "foo"))),
                    ImmutableMap.of("bar", ImmutableMap.of("baz", 42)),
                    // empty doc
                    ImmutableMap.of()
            };
            List<IndexRequestBuilder> reqs = new ArrayList<IndexRequestBuilder>();
            for (Map<String, Object> source : sources) {
                reqs.add(client().prepareIndex("idx", "type").setSource(source));
            }
        indexRandom(true, reqs);

        final Map<String, Integer> expected = new LinkedHashMap<String, Integer>();
        expected.put("foo", 1);
        expected.put("f*", 2); // foo and bar.foo, that's how the expansion works
        expected.put("bar", 2);
        expected.put("bar.*", 2);
        expected.put("bar.foo", 1);
        expected.put("bar.bar", 1);
        expected.put("bar.bar.bar", 1);
        expected.put("baz", 1);
        expected.put("foobar", 0);

        final long numDocs = client().prepareSearch("idx").execute().actionGet().getHits().totalHits();
        ensureYellow("idx");
        for (Map.Entry<String, Integer> entry : expected.entrySet()) {
            final String fieldName = entry.getKey();
            final int count = entry.getValue();
            // exists
            SearchResponse resp = client().prepareSearch("idx").setQuery(QueryBuilders.filteredQuery(QueryBuilders.matchAllQuery(), FilterBuilders.existsFilter(fieldName))).execute().actionGet();
            assertSearchResponse(resp);
            assertEquals(String.format(Locale.ROOT, "exists(%s, %d) mapping: %s", fieldName, count, mapping.string()), count, resp.getHits().totalHits());

            // missing
            resp = client().prepareSearch("idx").setQuery(QueryBuilders.filteredQuery(QueryBuilders.matchAllQuery(), FilterBuilders.missingFilter(fieldName))).execute().actionGet();
            assertSearchResponse(resp);
            assertEquals(String.format(Locale.ROOT, "missing(%s, %d) mapping: %s", fieldName, count, mapping.string()), numDocs - count, resp.getHits().totalHits());
        }
    }

}

@@ -1434,111 +1434,9 @@
 ons.
-assertAcked;%0Aimport static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertSearchResponse
+*
 ;%0A%0A%0A
@@ -3410,20 +3410,16 @@
         
-    
 @Suppres
@@ -3449,19 +3449,21 @@
         
-   
+final
  Map%3CStr
@@ -3515,20 +3515,16 @@
         
-    
 // simpl
@@ -3542,36 +3542,32 @@
                 
- 
-   
 ImmutableMap.of(
@@ -3597,20 +3597,16 @@
         
-    
 // objec
@@ -3622,36 +3622,32 @@
                 
- 
-   
 ImmutableMap.of(
@@ -3731,36 +3731,32 @@
                 
-    
 ImmutableMap.of(
@@ -3803,28 +3803,24 @@
             
- 
-   
 // empty doc
@@ -3820,20 +3820,16 @@
 pty doc%0A
-    
         
@@ -3866,26 +3866,19 @@
     
-    %7D;%0A   
+%7D;%0A
         
  Lis
@@ -3873,17 +3873,16 @@
         
- 
 List%3CInd
@@ -3948,28 +3948,24 @@
 ();%0A        
-    
 for (Map%3CStr
@@ -4009,20 +4009,16 @@
         
-  
-  
 reqs.add
@@ -4071,28 +4071,24 @@
 e(source));%0A
-    
         %7D%0A  
@@ -4575,130 +4575,213 @@
     
-final long numDocs = client().prepareSearch(%22idx%22).execute().actionGet().getHits().totalHits();%0A        ensureYellow(%22idx%22
+ensureYellow(%22idx%22);%0A        final long numDocs = sources.length;%0A        SearchResponse resp = client().prepareSearch(%22idx%22).get();%0A        assertSearchResponse(resp);%0A        assertHitCount(resp, numDocs
 );%0A 
@@ -4981,39 +4981,24 @@
             
-SearchResponse 
 resp = clien
@@ -5262,32 +5262,45 @@
 %25s, %25d) mapping:
+ %25s response:
  %25s%22, fieldName,
@@ -5323,16 +5323,22 @@
 string()
+, resp
 ), count
@@ -5691,16 +5691,29 @@
 mapping:
+ %25s response:
  %25s%22, fi
@@ -5744,16 +5744,22 @@
 string()
+, resp
 ), numDo

Add more assertions to ExistsMissingTests.