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