19
19
20
20
package org .elasticsearch .action .search ;
21
21
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 ;
24
33
25
34
import org .apache .lucene .index .Term ;
26
35
import org .apache .lucene .search .CollectionStatistics ;
58
67
import org .elasticsearch .search .suggest .Suggest .Suggestion ;
59
68
import org .elasticsearch .search .suggest .completion .CompletionSuggestion ;
60
69
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 ;
71
72
72
73
public final class SearchPhaseController {
73
74
private static final ScoreDoc [] EMPTY_DOCS = new ScoreDoc [0 ];
@@ -429,7 +430,7 @@ public ReducedQueryPhase reducedQueryPhase(Collection<? extends SearchPhaseResul
429
430
* @see QuerySearchResult#consumeProfileResult()
430
431
*/
431
432
private ReducedQueryPhase reducedQueryPhase (Collection <? extends SearchPhaseResult > queryResults ,
432
- List <InternalAggregations > bufferedAggs , List <TopDocs > bufferedTopDocs ,
433
+ List <Supplier < InternalAggregations > > bufferedAggs , List <TopDocs > bufferedTopDocs ,
433
434
TopDocsStats topDocsStats , int numReducePhases , boolean isScrollRequest ,
434
435
InternalAggregation .ReduceContextBuilder aggReduceContextBuilder ,
435
436
boolean performFinalReduce ) {
@@ -453,7 +454,7 @@ private ReducedQueryPhase reducedQueryPhase(Collection<? extends SearchPhaseResu
453
454
final boolean hasSuggest = firstResult .suggest () != null ;
454
455
final boolean hasProfileResults = firstResult .hasProfileResults ();
455
456
final boolean consumeAggs ;
456
- final List <InternalAggregations > aggregationsList ;
457
+ final List <Supplier < InternalAggregations > > aggregationsList ;
457
458
if (bufferedAggs != null ) {
458
459
consumeAggs = false ;
459
460
// 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
492
493
}
493
494
}
494
495
if (consumeAggs ) {
495
- aggregationsList .add (( InternalAggregations ) result .consumeAggs ());
496
+ aggregationsList .add (result .consumeAggs ());
496
497
}
497
498
if (hasProfileResults ) {
498
499
String key = result .getSearchShardTarget ().toString ();
@@ -508,8 +509,7 @@ private ReducedQueryPhase reducedQueryPhase(Collection<? extends SearchPhaseResu
508
509
reducedSuggest = new Suggest (Suggest .reduce (groupedSuggestions ));
509
510
reducedCompletionSuggestions = reducedSuggest .filter (CompletionSuggestion .class );
510
511
}
511
- final InternalAggregations aggregations = aggregationsList .isEmpty () ? null : InternalAggregations .topLevelReduce (aggregationsList ,
512
- performFinalReduce ? aggReduceContextBuilder .forFinalReduction () : aggReduceContextBuilder .forPartialReduction ());
512
+ final InternalAggregations aggregations = reduceAggs (aggReduceContextBuilder , performFinalReduce , aggregationsList );
513
513
final SearchProfileShardResults shardResults = profileResults .isEmpty () ? null : new SearchProfileShardResults (profileResults );
514
514
final SortedTopDocs sortedTopDocs = sortDocs (isScrollRequest , queryResults , bufferedTopDocs , topDocsStats , from , size ,
515
515
reducedCompletionSuggestions );
@@ -519,6 +519,24 @@ private ReducedQueryPhase reducedQueryPhase(Collection<? extends SearchPhaseResu
519
519
firstResult .sortValueFormats (), numReducePhases , size , from , false );
520
520
}
521
521
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
+
522
540
/*
523
541
* Returns the size of the requested top documents (from + size)
524
542
*/
@@ -600,7 +618,7 @@ public InternalSearchResponse buildResponse(SearchHits hits) {
600
618
*/
601
619
static final class QueryPhaseResultConsumer extends ArraySearchPhaseResults <SearchPhaseResult > {
602
620
private final SearchShardTarget [] processedShards ;
603
- private final InternalAggregations [] aggsBuffer ;
621
+ private final Supplier < InternalAggregations > [] aggsBuffer ;
604
622
private final TopDocs [] topDocsBuffer ;
605
623
private final boolean hasAggs ;
606
624
private final boolean hasTopDocs ;
@@ -642,7 +660,9 @@ private QueryPhaseResultConsumer(SearchProgressListener progressListener, Search
642
660
this .progressListener = progressListener ;
643
661
this .processedShards = new SearchShardTarget [expectedResultSize ];
644
662
// 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 ;
646
666
this .topDocsBuffer = new TopDocs [hasTopDocs ? bufferSize : 0 ];
647
667
this .hasTopDocs = hasTopDocs ;
648
668
this .hasAggs = hasAggs ;
@@ -665,10 +685,14 @@ private synchronized void consumeInternal(QuerySearchResult querySearchResult) {
665
685
if (querySearchResult .isNull () == false ) {
666
686
if (index == bufferSize ) {
667
687
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 ;
672
696
}
673
697
if (hasTopDocs ) {
674
698
TopDocs reducedTopDocs = mergeTopDocs (Arrays .asList (topDocsBuffer ),
@@ -681,12 +705,12 @@ private synchronized void consumeInternal(QuerySearchResult querySearchResult) {
681
705
index = 1 ;
682
706
if (hasAggs || hasTopDocs ) {
683
707
progressListener .notifyPartialReduce (SearchProgressListener .buildSearchShards (processedShards ),
684
- topDocsStats .getTotalHits (), hasAggs ? aggsBuffer [0 ] : null , numReducePhases );
708
+ topDocsStats .getTotalHits (), hasAggs ? aggsBuffer [0 ]. get () : null , numReducePhases );
685
709
}
686
710
}
687
711
final int i = index ++;
688
712
if (hasAggs ) {
689
- aggsBuffer [i ] = ( InternalAggregations ) querySearchResult .consumeAggs ();
713
+ aggsBuffer [i ] = querySearchResult .consumeAggs ();
690
714
}
691
715
if (hasTopDocs ) {
692
716
final TopDocsAndMaxScore topDocs = querySearchResult .consumeTopDocs (); // can't be null
@@ -698,7 +722,7 @@ private synchronized void consumeInternal(QuerySearchResult querySearchResult) {
698
722
processedShards [querySearchResult .getShardIndex ()] = querySearchResult .getSearchShardTarget ();
699
723
}
700
724
701
- private synchronized List <InternalAggregations > getRemainingAggs () {
725
+ private synchronized List <Supplier < InternalAggregations > > getRemainingAggs () {
702
726
return hasAggs ? Arrays .asList (aggsBuffer ).subList (0 , index ) : null ;
703
727
}
704
728
0 commit comments