141141import  static  org .elasticsearch .search .aggregations .PipelineAggregatorBuilders .bucketScript ;
142142import  static  org .elasticsearch .test .MapMatcher .assertMap ;
143143import  static  org .elasticsearch .test .MapMatcher .matchesMap ;
144+ import  static  org .hamcrest .Matchers .anyOf ;
144145import  static  org .hamcrest .Matchers .closeTo ;
145146import  static  org .hamcrest .Matchers .either ;
146147import  static  org .hamcrest .Matchers .equalTo ;
@@ -286,8 +287,18 @@ public void testSimple() throws Exception {
286287    }
287288
288289    public  void  testMatchNoDocsQuery () throws  Exception  {
290+         for  (TermsAggregatorFactory .ExecutionMode  executionMode  : TermsAggregatorFactory .ExecutionMode .values ()) {
291+             testMatchNoDocsQuery (executionMode );
292+         }
293+         testMatchNoDocsQuery (null );
294+     }
295+ 
296+     private  void  testMatchNoDocsQuery (TermsAggregatorFactory .ExecutionMode  hint ) throws  Exception  {
289297        MappedFieldType  fieldType  = new  KeywordFieldMapper .KeywordFieldType ("string" , randomBoolean (), true , Collections .emptyMap ());
290298        TermsAggregationBuilder  aggregationBuilder  = new  TermsAggregationBuilder ("_name" ).field ("string" );
299+         if  (hint  != null ) {
300+             aggregationBuilder .executionHint (hint .toString ());
301+         }
291302        CheckedConsumer <RandomIndexWriter , IOException > createIndex  = iw  -> {
292303            iw .addDocument (doc (fieldType , "a" , "b" ));
293304            iw .addDocument (doc (fieldType , "" , "c" , "a" ));
@@ -298,11 +309,8 @@ public void testMatchNoDocsQuery() throws Exception {
298309            createIndex ,
299310            (InternalTerms <?, ?> result ) -> { assertEquals (0 , result .getBuckets ().size ()); },
300311            new  AggTestConfig (aggregationBuilder , fieldType ).withQuery (new  MatchNoDocsQuery ())
312+                 .withCheckAggregator (checkTopLevelAggregator (hint  == null  ? TermsAggregatorFactory .ExecutionMode .MAP  : hint ))
301313        );
302- 
303-         debugTestCase (aggregationBuilder , new  MatchNoDocsQuery (), createIndex , (result , impl , debug ) -> {
304-             assertEquals (impl , MapStringTermsAggregator .class );
305-         }, fieldType );
306314    }
307315
308316    public  void  testStringShardMinDocCount () throws  IOException  {
@@ -337,7 +345,6 @@ public void testStringShardZeroMinDocCount() throws IOException {
337345                .executionHint (executionMode .toString ())
338346                .size (2 )
339347                .minDocCount (0 )
340-                 .executionHint ("map" )
341348                .excludeDeletedDocs (true )
342349                .order (BucketOrder .key (true ));
343350
@@ -360,7 +367,80 @@ public void testStringShardZeroMinDocCount() throws IOException {
360367                        assertEquals ("b" , result .getBuckets ().get (1 ).getKeyAsString ());
361368                    }
362369                    assertEquals (0L , result .getBuckets ().get (1 ).getDocCount ());
363-                 }, new  AggTestConfig (aggregationBuilder , fieldType ).withQuery (new  TermQuery (new  Term ("string" , "e" ))));
370+                 },
371+                     new  AggTestConfig (aggregationBuilder , fieldType ).withQuery (new  TermQuery (new  Term ("string" , "e" )))
372+                         .withCheckAggregator (checkTopLevelAggregator (executionMode ))
373+                 );
374+             }
375+ 
376+             {
377+                 boolean  delete  = randomBoolean ();
378+                 // force single shard/segment 
379+                 testCase (iw  -> {
380+                     // force single shard/segment 
381+                     iw .addDocuments (
382+                         Arrays .asList (doc (fieldType , "a" ), doc (fieldType , "c" , "d" ), doc (fieldType , "b" , "d" ), doc (fieldType , "b" ))
383+                     );
384+                     if  (delete ) {
385+                         iw .deleteDocuments (new  TermQuery (new  Term ("string" , "b" )));
386+                     }
387+                 }, (InternalTerms <?, ?> result ) -> {
388+                     assertEquals (2 , result .getBuckets ().size ());
389+                     assertEquals ("a" , result .getBuckets ().get (0 ).getKeyAsString ());
390+                     assertEquals (0L , result .getBuckets ().get (0 ).getDocCount ());
391+                     if  (delete ) {
392+                         assertEquals ("c" , result .getBuckets ().get (1 ).getKeyAsString ());
393+                     } else  {
394+                         assertEquals ("b" , result .getBuckets ().get (1 ).getKeyAsString ());
395+                     }
396+                     assertEquals (0L , result .getBuckets ().get (1 ).getDocCount ());
397+                 },
398+                     new  AggTestConfig (aggregationBuilder , fieldType ).withQuery (new  TermQuery (new  Term ("string" , "e" )))
399+                         .withCheckAggregator (checkTopLevelAggregator (executionMode ))
400+                 );
401+             }
402+         }
403+     }
404+ 
405+     /** 
406+      * Asserts that sane output for {@code min_doc_count: 0} when the top level 
407+      * query is {@code match_none}. Especially important is that we respect the 
408+      * execution hint in this case. It's a lot faster to collect {@code min_doc_count: 0} 
409+      * via ordinals. 
410+      */ 
411+     public  void  testStringZeroMinDocCountMatchNone () throws  IOException  {
412+         MappedFieldType  fieldType  = new  KeywordFieldMapper .KeywordFieldType ("string" , true , true , Collections .emptyMap ());
413+         for  (TermsAggregatorFactory .ExecutionMode  executionMode  : TermsAggregatorFactory .ExecutionMode .values ()) {
414+             TermsAggregationBuilder  aggregationBuilder  = new  TermsAggregationBuilder ("_name" ).field ("string" )
415+                 .executionHint (executionMode .toString ())
416+                 .size (2 )
417+                 .minDocCount (0 )
418+                 .excludeDeletedDocs (true )
419+                 .order (BucketOrder .key (true ));
420+ 
421+             {
422+                 boolean  delete  = randomBoolean ();
423+                 // force single shard/segment 
424+                 testCase (iw  -> {
425+                     // force single shard/segment 
426+                     iw .addDocuments (Arrays .asList (doc (fieldType , "a" ), doc (fieldType , "b" ), doc (fieldType , "c" ), doc (fieldType , "d" )));
427+                     if  (delete ) {
428+                         iw .deleteDocuments (new  TermQuery (new  Term ("string" , "b" )));
429+                     }
430+                 }, (InternalTerms <?, ?> result ) -> {
431+                     assertEquals (2 , result .getBuckets ().size ());
432+                     assertEquals ("a" , result .getBuckets ().get (0 ).getKeyAsString ());
433+                     assertEquals (0L , result .getBuckets ().get (0 ).getDocCount ());
434+                     if  (delete ) {
435+                         assertEquals ("c" , result .getBuckets ().get (1 ).getKeyAsString ());
436+                     } else  {
437+                         assertEquals ("b" , result .getBuckets ().get (1 ).getKeyAsString ());
438+                     }
439+                     assertEquals (0L , result .getBuckets ().get (1 ).getDocCount ());
440+                 },
441+                     new  AggTestConfig (aggregationBuilder , fieldType ).withQuery (new  MatchNoDocsQuery ())
442+                         .withCheckAggregator (checkTopLevelAggregator (executionMode ))
443+                 );
364444            }
365445
366446            {
@@ -384,7 +464,10 @@ public void testStringShardZeroMinDocCount() throws IOException {
384464                        assertEquals ("b" , result .getBuckets ().get (1 ).getKeyAsString ());
385465                    }
386466                    assertEquals (0L , result .getBuckets ().get (1 ).getDocCount ());
387-                 }, new  AggTestConfig (aggregationBuilder , fieldType ).withQuery (new  TermQuery (new  Term ("string" , "e" ))));
467+                 },
468+                     new  AggTestConfig (aggregationBuilder , fieldType ).withQuery (new  MatchNoDocsQuery ())
469+                         .withCheckAggregator (checkTopLevelAggregator (executionMode ))
470+                 );
388471            }
389472        }
390473    }
@@ -2279,4 +2362,14 @@ protected List<ObjectMapper> objectMappers() {
22792362    private  String  randomHint () {
22802363        return  randomFrom (TermsAggregatorFactory .ExecutionMode .values ()).toString ();
22812364    }
2365+ 
2366+     private  Consumer <Aggregator > checkTopLevelAggregator (TermsAggregatorFactory .ExecutionMode  executionMode ) {
2367+         return  switch  (executionMode ) {
2368+             case  MAP  -> a  -> assertThat (a , instanceOf (MapStringTermsAggregator .class ));
2369+             case  GLOBAL_ORDINALS  -> a  -> assertThat (
2370+                 a ,
2371+                 anyOf (instanceOf (GlobalOrdinalsStringTermsAggregator .class ), instanceOf (StringTermsAggregatorFromFilters .class ))
2372+             );
2373+         };
2374+     }
22822375}
0 commit comments