1919
2020package org .elasticsearch .action .search ;
2121
22- import com .carrotsearch .hppc .IntArrayList ;
23- import com .carrotsearch .hppc .ObjectObjectHashMap ;
22+ import java .util .ArrayList ;
23+ import java .util .Arrays ;
24+ import java .util .Collection ;
25+ import java .util .Collections ;
26+ import java .util .HashMap ;
27+ import java .util .List ;
28+ import java .util .Map ;
29+ import java .util .function .Function ;
30+ import java .util .function .IntFunction ;
31+ import java .util .function .Supplier ;
32+ import java .util .stream .Collectors ;
2433
2534import org .apache .lucene .index .Term ;
2635import org .apache .lucene .search .CollectionStatistics ;
5867import org .elasticsearch .search .suggest .Suggest .Suggestion ;
5968import org .elasticsearch .search .suggest .completion .CompletionSuggestion ;
6069
61- import java .util .ArrayList ;
62- import java .util .Arrays ;
63- import java .util .Collection ;
64- import java .util .Collections ;
65- import java .util .HashMap ;
66- import java .util .List ;
67- import java .util .Map ;
68- import java .util .function .Function ;
69- import java .util .function .IntFunction ;
70- import java .util .stream .Collectors ;
70+ import com .carrotsearch .hppc .IntArrayList ;
71+ import com .carrotsearch .hppc .ObjectObjectHashMap ;
7172
7273public final class SearchPhaseController {
7374 private static final ScoreDoc [] EMPTY_DOCS = new ScoreDoc [0 ];
@@ -429,7 +430,7 @@ public ReducedQueryPhase reducedQueryPhase(Collection<? extends SearchPhaseResul
429430 * @see QuerySearchResult#consumeProfileResult()
430431 */
431432 private ReducedQueryPhase reducedQueryPhase (Collection <? extends SearchPhaseResult > queryResults ,
432- List <InternalAggregations > bufferedAggs , List <TopDocs > bufferedTopDocs ,
433+ List <Supplier < InternalAggregations > > bufferedAggs , List <TopDocs > bufferedTopDocs ,
433434 TopDocsStats topDocsStats , int numReducePhases , boolean isScrollRequest ,
434435 InternalAggregation .ReduceContextBuilder aggReduceContextBuilder ,
435436 boolean performFinalReduce ) {
@@ -453,7 +454,7 @@ private ReducedQueryPhase reducedQueryPhase(Collection<? extends SearchPhaseResu
453454 final boolean hasSuggest = firstResult .suggest () != null ;
454455 final boolean hasProfileResults = firstResult .hasProfileResults ();
455456 final boolean consumeAggs ;
456- final List <InternalAggregations > aggregationsList ;
457+ final List <Supplier < InternalAggregations > > aggregationsList ;
457458 if (bufferedAggs != null ) {
458459 consumeAggs = false ;
459460 // we already have results from intermediate reduces and just need to perform the final reduce
@@ -492,7 +493,7 @@ private ReducedQueryPhase reducedQueryPhase(Collection<? extends SearchPhaseResu
492493 }
493494 }
494495 if (consumeAggs ) {
495- aggregationsList .add (( InternalAggregations ) result .consumeAggs ());
496+ aggregationsList .add (result .consumeAggs ());
496497 }
497498 if (hasProfileResults ) {
498499 String key = result .getSearchShardTarget ().toString ();
@@ -508,8 +509,7 @@ private ReducedQueryPhase reducedQueryPhase(Collection<? extends SearchPhaseResu
508509 reducedSuggest = new Suggest (Suggest .reduce (groupedSuggestions ));
509510 reducedCompletionSuggestions = reducedSuggest .filter (CompletionSuggestion .class );
510511 }
511- final InternalAggregations aggregations = aggregationsList .isEmpty () ? null : InternalAggregations .topLevelReduce (aggregationsList ,
512- performFinalReduce ? aggReduceContextBuilder .forFinalReduction () : aggReduceContextBuilder .forPartialReduction ());
512+ final InternalAggregations aggregations = reduceAggs (aggReduceContextBuilder , performFinalReduce , aggregationsList );
513513 final SearchProfileShardResults shardResults = profileResults .isEmpty () ? null : new SearchProfileShardResults (profileResults );
514514 final SortedTopDocs sortedTopDocs = sortDocs (isScrollRequest , queryResults , bufferedTopDocs , topDocsStats , from , size ,
515515 reducedCompletionSuggestions );
@@ -519,6 +519,24 @@ private ReducedQueryPhase reducedQueryPhase(Collection<? extends SearchPhaseResu
519519 firstResult .sortValueFormats (), numReducePhases , size , from , false );
520520 }
521521
522+ private InternalAggregations reduceAggs (
523+ InternalAggregation .ReduceContextBuilder aggReduceContextBuilder ,
524+ boolean performFinalReduce ,
525+ List <Supplier <InternalAggregations >> aggregationsList
526+ ) {
527+ /*
528+ * Parse the aggregations, clearing the list as we go so bits backing
529+ * the DelayedWriteable can be collected immediately.
530+ */
531+ List <InternalAggregations > toReduce = new ArrayList <>(aggregationsList .size ());
532+ for (int i = 0 ; i < aggregationsList .size (); i ++) {
533+ toReduce .add (aggregationsList .get (i ).get ());
534+ aggregationsList .set (i , null );
535+ }
536+ return aggregationsList .isEmpty () ? null : InternalAggregations .topLevelReduce (toReduce ,
537+ performFinalReduce ? aggReduceContextBuilder .forFinalReduction () : aggReduceContextBuilder .forPartialReduction ());
538+ }
539+
522540 /*
523541 * Returns the size of the requested top documents (from + size)
524542 */
@@ -600,7 +618,7 @@ public InternalSearchResponse buildResponse(SearchHits hits) {
600618 */
601619 static final class QueryPhaseResultConsumer extends ArraySearchPhaseResults <SearchPhaseResult > {
602620 private final SearchShardTarget [] processedShards ;
603- private final InternalAggregations [] aggsBuffer ;
621+ private final Supplier < InternalAggregations > [] aggsBuffer ;
604622 private final TopDocs [] topDocsBuffer ;
605623 private final boolean hasAggs ;
606624 private final boolean hasTopDocs ;
@@ -642,7 +660,9 @@ private QueryPhaseResultConsumer(SearchProgressListener progressListener, Search
642660 this .progressListener = progressListener ;
643661 this .processedShards = new SearchShardTarget [expectedResultSize ];
644662 // no need to buffer anything if we have less expected results. in this case we don't consume any results ahead of time.
645- this .aggsBuffer = new InternalAggregations [hasAggs ? bufferSize : 0 ];
663+ @ SuppressWarnings ("unchecked" )
664+ Supplier <InternalAggregations >[] aggsBuffer = new Supplier [hasAggs ? bufferSize : 0 ];
665+ this .aggsBuffer = aggsBuffer ;
646666 this .topDocsBuffer = new TopDocs [hasTopDocs ? bufferSize : 0 ];
647667 this .hasTopDocs = hasTopDocs ;
648668 this .hasAggs = hasAggs ;
@@ -665,10 +685,14 @@ private synchronized void consumeInternal(QuerySearchResult querySearchResult) {
665685 if (querySearchResult .isNull () == false ) {
666686 if (index == bufferSize ) {
667687 if (hasAggs ) {
668- ReduceContext reduceContext = aggReduceContextBuilder .forPartialReduction ();
669- InternalAggregations reducedAggs = InternalAggregations .topLevelReduce (Arrays .asList (aggsBuffer ), reduceContext );
670- Arrays .fill (aggsBuffer , null );
671- aggsBuffer [0 ] = reducedAggs ;
688+ List <InternalAggregations > aggs = new ArrayList <>(aggsBuffer .length );
689+ for (int i = 0 ; i < aggsBuffer .length ; i ++) {
690+ aggs .add (aggsBuffer [i ].get ());
691+ aggsBuffer [i ] = null ; // null the buffer so it can be GCed now.
692+ }
693+ InternalAggregations reducedAggs = InternalAggregations .topLevelReduce (
694+ aggs , aggReduceContextBuilder .forPartialReduction ());
695+ aggsBuffer [0 ] = () -> reducedAggs ;
672696 }
673697 if (hasTopDocs ) {
674698 TopDocs reducedTopDocs = mergeTopDocs (Arrays .asList (topDocsBuffer ),
@@ -681,12 +705,12 @@ private synchronized void consumeInternal(QuerySearchResult querySearchResult) {
681705 index = 1 ;
682706 if (hasAggs || hasTopDocs ) {
683707 progressListener .notifyPartialReduce (SearchProgressListener .buildSearchShards (processedShards ),
684- topDocsStats .getTotalHits (), hasAggs ? aggsBuffer [0 ] : null , numReducePhases );
708+ topDocsStats .getTotalHits (), hasAggs ? aggsBuffer [0 ]. get () : null , numReducePhases );
685709 }
686710 }
687711 final int i = index ++;
688712 if (hasAggs ) {
689- aggsBuffer [i ] = ( InternalAggregations ) querySearchResult .consumeAggs ();
713+ aggsBuffer [i ] = querySearchResult .consumeAggs ();
690714 }
691715 if (hasTopDocs ) {
692716 final TopDocsAndMaxScore topDocs = querySearchResult .consumeTopDocs (); // can't be null
@@ -698,7 +722,7 @@ private synchronized void consumeInternal(QuerySearchResult querySearchResult) {
698722 processedShards [querySearchResult .getShardIndex ()] = querySearchResult .getSearchShardTarget ();
699723 }
700724
701- private synchronized List <InternalAggregations > getRemainingAggs () {
725+ private synchronized List <Supplier < InternalAggregations > > getRemainingAggs () {
702726 return hasAggs ? Arrays .asList (aggsBuffer ).subList (0 , index ) : null ;
703727 }
704728
0 commit comments