@@ -207,18 +207,23 @@ static SortedTopDocs sortDocs(boolean ignoreFrom, Collection<? extends SearchPha
207
207
}
208
208
}
209
209
}
210
- final boolean isSortedByField ;
211
- final SortField [] sortFields ;
210
+ boolean isSortedByField = false ;
211
+ SortField [] sortFields = null ;
212
+ String collapseField = null ;
213
+ Object [] collapseValues = null ;
212
214
if (mergedTopDocs instanceof TopFieldDocs ) {
213
215
TopFieldDocs fieldDocs = (TopFieldDocs ) mergedTopDocs ;
214
- isSortedByField = (fieldDocs instanceof CollapseTopFieldDocs &&
215
- fieldDocs .fields .length == 1 && fieldDocs .fields [0 ].getType () == SortField .Type .SCORE ) == false ;
216
216
sortFields = fieldDocs .fields ;
217
- } else {
218
- isSortedByField = false ;
219
- sortFields = null ;
217
+ if (fieldDocs instanceof CollapseTopFieldDocs ) {
218
+ isSortedByField = (fieldDocs .fields .length == 1 && fieldDocs .fields [0 ].getType () == SortField .Type .SCORE ) == false ;
219
+ CollapseTopFieldDocs collapseTopFieldDocs = (CollapseTopFieldDocs ) fieldDocs ;
220
+ collapseField = collapseTopFieldDocs .field ;
221
+ collapseValues = collapseTopFieldDocs .collapseValues ;
222
+ } else {
223
+ isSortedByField = true ;
224
+ }
220
225
}
221
- return new SortedTopDocs (scoreDocs , isSortedByField , sortFields );
226
+ return new SortedTopDocs (scoreDocs , isSortedByField , sortFields , collapseField , collapseValues );
222
227
} else {
223
228
// no relevant docs
224
229
return SortedTopDocs .EMPTY ;
@@ -262,7 +267,7 @@ private static void setShardIndex(TopDocs topDocs, int shardIndex) {
262
267
public ScoreDoc [] getLastEmittedDocPerShard (ReducedQueryPhase reducedQueryPhase , int numShards ) {
263
268
final ScoreDoc [] lastEmittedDocPerShard = new ScoreDoc [numShards ];
264
269
if (reducedQueryPhase .isEmptyResult == false ) {
265
- final ScoreDoc [] sortedScoreDocs = reducedQueryPhase .scoreDocs ;
270
+ final ScoreDoc [] sortedScoreDocs = reducedQueryPhase .sortedTopDocs . scoreDocs ;
266
271
// from is always zero as when we use scroll, we ignore from
267
272
long size = Math .min (reducedQueryPhase .fetchHits , reducedQueryPhase .size );
268
273
// with collapsing we can have more hits than sorted docs
@@ -303,7 +308,7 @@ public InternalSearchResponse merge(boolean ignoreFrom, ReducedQueryPhase reduce
303
308
if (reducedQueryPhase .isEmptyResult ) {
304
309
return InternalSearchResponse .empty ();
305
310
}
306
- ScoreDoc [] sortedDocs = reducedQueryPhase .scoreDocs ;
311
+ ScoreDoc [] sortedDocs = reducedQueryPhase .sortedTopDocs . scoreDocs ;
307
312
SearchHits hits = getHits (reducedQueryPhase , ignoreFrom , fetchResults , resultsLookup );
308
313
if (reducedQueryPhase .suggest != null ) {
309
314
if (!fetchResults .isEmpty ()) {
@@ -341,12 +346,12 @@ public InternalSearchResponse merge(boolean ignoreFrom, ReducedQueryPhase reduce
341
346
342
347
private SearchHits getHits (ReducedQueryPhase reducedQueryPhase , boolean ignoreFrom ,
343
348
Collection <? extends SearchPhaseResult > fetchResults , IntFunction <SearchPhaseResult > resultsLookup ) {
344
- final boolean sorted = reducedQueryPhase .isSortedByField ;
345
- ScoreDoc [] sortedDocs = reducedQueryPhase .scoreDocs ;
349
+ SortedTopDocs sortedTopDocs = reducedQueryPhase .sortedTopDocs ;
346
350
int sortScoreIndex = -1 ;
347
- if (sorted ) {
348
- for (int i = 0 ; i < reducedQueryPhase .sortField .length ; i ++) {
349
- if (reducedQueryPhase .sortField [i ].getType () == SortField .Type .SCORE ) {
351
+ if (sortedTopDocs .isSortedByField ) {
352
+ SortField [] sortFields = sortedTopDocs .sortFields ;
353
+ for (int i = 0 ; i < sortFields .length ; i ++) {
354
+ if (sortFields [i ].getType () == SortField .Type .SCORE ) {
350
355
sortScoreIndex = i ;
351
356
}
352
357
}
@@ -358,12 +363,12 @@ private SearchHits getHits(ReducedQueryPhase reducedQueryPhase, boolean ignoreFr
358
363
int from = ignoreFrom ? 0 : reducedQueryPhase .from ;
359
364
int numSearchHits = (int ) Math .min (reducedQueryPhase .fetchHits - from , reducedQueryPhase .size );
360
365
// with collapsing we can have more fetch hits than sorted docs
361
- numSearchHits = Math .min (sortedDocs .length , numSearchHits );
366
+ numSearchHits = Math .min (sortedTopDocs . scoreDocs .length , numSearchHits );
362
367
// merge hits
363
368
List <SearchHit > hits = new ArrayList <>();
364
369
if (!fetchResults .isEmpty ()) {
365
370
for (int i = 0 ; i < numSearchHits ; i ++) {
366
- ScoreDoc shardDoc = sortedDocs [i ];
371
+ ScoreDoc shardDoc = sortedTopDocs . scoreDocs [i ];
367
372
SearchPhaseResult fetchResultProvider = resultsLookup .apply (shardDoc .shardIndex );
368
373
if (fetchResultProvider == null ) {
369
374
// this can happen if we are hitting a shard failure during the fetch phase
@@ -379,7 +384,7 @@ private SearchHits getHits(ReducedQueryPhase reducedQueryPhase, boolean ignoreFr
379
384
SearchHit searchHit = fetchResult .hits ().getHits ()[index ];
380
385
searchHit .score (shardDoc .score );
381
386
searchHit .shard (fetchResult .getSearchShardTarget ());
382
- if (sorted ) {
387
+ if (sortedTopDocs . isSortedByField ) {
383
388
FieldDoc fieldDoc = (FieldDoc ) shardDoc ;
384
389
searchHit .sortValues (fieldDoc .fields , reducedQueryPhase .sortValueFormats );
385
390
if (sortScoreIndex != -1 ) {
@@ -389,7 +394,8 @@ private SearchHits getHits(ReducedQueryPhase reducedQueryPhase, boolean ignoreFr
389
394
hits .add (searchHit );
390
395
}
391
396
}
392
- return new SearchHits (hits .toArray (new SearchHit [0 ]), reducedQueryPhase .totalHits , reducedQueryPhase .maxScore );
397
+ return new SearchHits (hits .toArray (new SearchHit [0 ]), reducedQueryPhase .totalHits ,
398
+ reducedQueryPhase .maxScore , sortedTopDocs .sortFields , sortedTopDocs .collapseField , sortedTopDocs .collapseValues );
393
399
}
394
400
395
401
/**
@@ -429,7 +435,7 @@ private ReducedQueryPhase reducedQueryPhase(Collection<? extends SearchPhaseResu
429
435
Boolean terminatedEarly = null ;
430
436
if (queryResults .isEmpty ()) { // early terminate we have nothing to reduce
431
437
return new ReducedQueryPhase (topDocsStats .totalHits , topDocsStats .fetchHits , topDocsStats .maxScore ,
432
- timedOut , terminatedEarly , null , null , null , EMPTY_DOCS , null , null , numReducePhases , false , 0 , 0 , true );
438
+ timedOut , terminatedEarly , null , null , null , SortedTopDocs . EMPTY , null , numReducePhases , 0 , 0 , true );
433
439
}
434
440
final QuerySearchResult firstResult = queryResults .stream ().findFirst ().get ().queryResult ();
435
441
final boolean hasSuggest = firstResult .suggest () != null ;
@@ -491,10 +497,10 @@ private ReducedQueryPhase reducedQueryPhase(Collection<? extends SearchPhaseResu
491
497
final InternalAggregations aggregations = aggregationsList .isEmpty () ? null : reduceAggs (aggregationsList ,
492
498
firstResult .pipelineAggregators (), reduceContext );
493
499
final SearchProfileShardResults shardResults = profileResults .isEmpty () ? null : new SearchProfileShardResults (profileResults );
494
- final SortedTopDocs scoreDocs = sortDocs (isScrollRequest , queryResults , bufferedTopDocs , topDocsStats , from , size );
500
+ final SortedTopDocs sortedTopDocs = sortDocs (isScrollRequest , queryResults , bufferedTopDocs , topDocsStats , from , size );
495
501
return new ReducedQueryPhase (topDocsStats .totalHits , topDocsStats .fetchHits , topDocsStats .maxScore ,
496
- timedOut , terminatedEarly , suggest , aggregations , shardResults , scoreDocs . scoreDocs , scoreDocs . sortFields ,
497
- firstResult .sortValueFormats (), numReducePhases , scoreDocs . isSortedByField , size , from , false );
502
+ timedOut , terminatedEarly , suggest , aggregations , shardResults , sortedTopDocs ,
503
+ firstResult .sortValueFormats (), numReducePhases , size , from , firstResult == null );
498
504
}
499
505
500
506
/**
@@ -542,12 +548,8 @@ public static final class ReducedQueryPhase {
542
548
final SearchProfileShardResults shardResults ;
543
549
// the number of reduces phases
544
550
final int numReducePhases ;
545
- // the searches merged top docs
546
- final ScoreDoc [] scoreDocs ;
547
- // the top docs sort fields used to sort the score docs, <code>null</code> if the results are not sorted
548
- final SortField [] sortField ;
549
- // <code>true</code> iff the result score docs is sorted by a field (not score), this implies that <code>sortField</code> is set.
550
- final boolean isSortedByField ;
551
+ //encloses info about the merged top docs, the sort fields used to sort the score docs etc.
552
+ final SortedTopDocs sortedTopDocs ;
551
553
// the size of the top hits to return
552
554
final int size ;
553
555
// <code>true</code> iff the query phase had no results. Otherwise <code>false</code>
@@ -558,9 +560,8 @@ public static final class ReducedQueryPhase {
558
560
final DocValueFormat [] sortValueFormats ;
559
561
560
562
ReducedQueryPhase (long totalHits , long fetchHits , float maxScore , boolean timedOut , Boolean terminatedEarly , Suggest suggest ,
561
- InternalAggregations aggregations , SearchProfileShardResults shardResults , ScoreDoc [] scoreDocs ,
562
- SortField [] sortFields , DocValueFormat [] sortValueFormats , int numReducePhases , boolean isSortedByField , int size ,
563
- int from , boolean isEmptyResult ) {
563
+ InternalAggregations aggregations , SearchProfileShardResults shardResults , SortedTopDocs sortedTopDocs ,
564
+ DocValueFormat [] sortValueFormats , int numReducePhases , int size , int from , boolean isEmptyResult ) {
564
565
if (numReducePhases <= 0 ) {
565
566
throw new IllegalArgumentException ("at least one reduce phase must have been applied but was: " + numReducePhases );
566
567
}
@@ -577,9 +578,7 @@ public static final class ReducedQueryPhase {
577
578
this .aggregations = aggregations ;
578
579
this .shardResults = shardResults ;
579
580
this .numReducePhases = numReducePhases ;
580
- this .scoreDocs = scoreDocs ;
581
- this .sortField = sortFields ;
582
- this .isSortedByField = isSortedByField ;
581
+ this .sortedTopDocs = sortedTopDocs ;
583
582
this .size = size ;
584
583
this .from = from ;
585
584
this .isEmptyResult = isEmptyResult ;
@@ -719,7 +718,7 @@ InitialSearchPhase.ArraySearchPhaseResults<SearchPhaseResult> newSearchPhaseResu
719
718
}
720
719
return new InitialSearchPhase .ArraySearchPhaseResults <SearchPhaseResult >(numShards ) {
721
720
@ Override
722
- public ReducedQueryPhase reduce () {
721
+ ReducedQueryPhase reduce () {
723
722
return reducedQueryPhase (results .asList (), isScrollRequest , trackTotalHits );
724
723
}
725
724
};
@@ -752,15 +751,23 @@ void add(TopDocs topDocs) {
752
751
}
753
752
754
753
static final class SortedTopDocs {
755
- static final SortedTopDocs EMPTY = new SortedTopDocs (EMPTY_DOCS , false , null );
754
+ static final SortedTopDocs EMPTY = new SortedTopDocs (EMPTY_DOCS , false , null , null , null );
755
+ // the searches merged top docs
756
756
final ScoreDoc [] scoreDocs ;
757
+ // <code>true</code> iff the result score docs is sorted by a field (not score), this implies that <code>sortField</code> is set.
757
758
final boolean isSortedByField ;
759
+ // the top docs sort fields used to sort the score docs, <code>null</code> if the results are not sorted
758
760
final SortField [] sortFields ;
761
+ final String collapseField ;
762
+ final Object [] collapseValues ;
759
763
760
- SortedTopDocs (ScoreDoc [] scoreDocs , boolean isSortedByField , SortField [] sortFields ) {
764
+ SortedTopDocs (ScoreDoc [] scoreDocs , boolean isSortedByField , SortField [] sortFields ,
765
+ String collapseField , Object [] collapseValues ) {
761
766
this .scoreDocs = scoreDocs ;
762
767
this .isSortedByField = isSortedByField ;
763
768
this .sortFields = sortFields ;
769
+ this .collapseField = collapseField ;
770
+ this .collapseValues = collapseValues ;
764
771
}
765
772
}
766
773
}
0 commit comments