@@ -211,18 +211,23 @@ static SortedTopDocs sortDocs(boolean ignoreFrom, Collection<? extends SearchPha
211
211
}
212
212
}
213
213
}
214
- final boolean isSortedByField ;
215
- final SortField [] sortFields ;
214
+ boolean isSortedByField = false ;
215
+ SortField [] sortFields = null ;
216
+ String collapseField = null ;
217
+ Object [] collapseValues = null ;
216
218
if (mergedTopDocs instanceof TopFieldDocs ) {
217
219
TopFieldDocs fieldDocs = (TopFieldDocs ) mergedTopDocs ;
218
- isSortedByField = (fieldDocs instanceof CollapseTopFieldDocs &&
219
- fieldDocs .fields .length == 1 && fieldDocs .fields [0 ].getType () == SortField .Type .SCORE ) == false ;
220
220
sortFields = fieldDocs .fields ;
221
- } else {
222
- isSortedByField = false ;
223
- sortFields = null ;
221
+ if (fieldDocs instanceof CollapseTopFieldDocs ) {
222
+ isSortedByField = (fieldDocs .fields .length == 1 && fieldDocs .fields [0 ].getType () == SortField .Type .SCORE ) == false ;
223
+ CollapseTopFieldDocs collapseTopFieldDocs = (CollapseTopFieldDocs ) fieldDocs ;
224
+ collapseField = collapseTopFieldDocs .field ;
225
+ collapseValues = collapseTopFieldDocs .collapseValues ;
226
+ } else {
227
+ isSortedByField = true ;
228
+ }
224
229
}
225
- return new SortedTopDocs (scoreDocs , isSortedByField , sortFields );
230
+ return new SortedTopDocs (scoreDocs , isSortedByField , sortFields , collapseField , collapseValues );
226
231
} else {
227
232
// no relevant docs
228
233
return SortedTopDocs .EMPTY ;
@@ -266,7 +271,7 @@ private static void setShardIndex(TopDocs topDocs, int shardIndex) {
266
271
public ScoreDoc [] getLastEmittedDocPerShard (ReducedQueryPhase reducedQueryPhase , int numShards ) {
267
272
final ScoreDoc [] lastEmittedDocPerShard = new ScoreDoc [numShards ];
268
273
if (reducedQueryPhase .isEmptyResult == false ) {
269
- final ScoreDoc [] sortedScoreDocs = reducedQueryPhase .scoreDocs ;
274
+ final ScoreDoc [] sortedScoreDocs = reducedQueryPhase .sortedTopDocs . scoreDocs ;
270
275
// from is always zero as when we use scroll, we ignore from
271
276
long size = Math .min (reducedQueryPhase .fetchHits , reducedQueryPhase .size );
272
277
// with collapsing we can have more hits than sorted docs
@@ -307,7 +312,7 @@ public InternalSearchResponse merge(boolean ignoreFrom, ReducedQueryPhase reduce
307
312
if (reducedQueryPhase .isEmptyResult ) {
308
313
return InternalSearchResponse .empty ();
309
314
}
310
- ScoreDoc [] sortedDocs = reducedQueryPhase .scoreDocs ;
315
+ ScoreDoc [] sortedDocs = reducedQueryPhase .sortedTopDocs . scoreDocs ;
311
316
SearchHits hits = getHits (reducedQueryPhase , ignoreFrom , fetchResults , resultsLookup );
312
317
if (reducedQueryPhase .suggest != null ) {
313
318
if (!fetchResults .isEmpty ()) {
@@ -345,12 +350,12 @@ public InternalSearchResponse merge(boolean ignoreFrom, ReducedQueryPhase reduce
345
350
346
351
private SearchHits getHits (ReducedQueryPhase reducedQueryPhase , boolean ignoreFrom ,
347
352
Collection <? extends SearchPhaseResult > fetchResults , IntFunction <SearchPhaseResult > resultsLookup ) {
348
- final boolean sorted = reducedQueryPhase .isSortedByField ;
349
- ScoreDoc [] sortedDocs = reducedQueryPhase .scoreDocs ;
353
+ SortedTopDocs sortedTopDocs = reducedQueryPhase .sortedTopDocs ;
350
354
int sortScoreIndex = -1 ;
351
- if (sorted ) {
352
- for (int i = 0 ; i < reducedQueryPhase .sortField .length ; i ++) {
353
- if (reducedQueryPhase .sortField [i ].getType () == SortField .Type .SCORE ) {
355
+ if (sortedTopDocs .isSortedByField ) {
356
+ SortField [] sortFields = sortedTopDocs .sortFields ;
357
+ for (int i = 0 ; i < sortFields .length ; i ++) {
358
+ if (sortFields [i ].getType () == SortField .Type .SCORE ) {
354
359
sortScoreIndex = i ;
355
360
}
356
361
}
@@ -362,12 +367,12 @@ private SearchHits getHits(ReducedQueryPhase reducedQueryPhase, boolean ignoreFr
362
367
int from = ignoreFrom ? 0 : reducedQueryPhase .from ;
363
368
int numSearchHits = (int ) Math .min (reducedQueryPhase .fetchHits - from , reducedQueryPhase .size );
364
369
// with collapsing we can have more fetch hits than sorted docs
365
- numSearchHits = Math .min (sortedDocs .length , numSearchHits );
370
+ numSearchHits = Math .min (sortedTopDocs . scoreDocs .length , numSearchHits );
366
371
// merge hits
367
372
List <SearchHit > hits = new ArrayList <>();
368
373
if (!fetchResults .isEmpty ()) {
369
374
for (int i = 0 ; i < numSearchHits ; i ++) {
370
- ScoreDoc shardDoc = sortedDocs [i ];
375
+ ScoreDoc shardDoc = sortedTopDocs . scoreDocs [i ];
371
376
SearchPhaseResult fetchResultProvider = resultsLookup .apply (shardDoc .shardIndex );
372
377
if (fetchResultProvider == null ) {
373
378
// this can happen if we are hitting a shard failure during the fetch phase
@@ -381,21 +386,21 @@ private SearchHits getHits(ReducedQueryPhase reducedQueryPhase, boolean ignoreFr
381
386
assert index < fetchResult .hits ().getHits ().length : "not enough hits fetched. index [" + index + "] length: "
382
387
+ fetchResult .hits ().getHits ().length ;
383
388
SearchHit searchHit = fetchResult .hits ().getHits ()[index ];
384
- if (sorted == false ) {
385
- searchHit .score (shardDoc .score );
386
- }
387
389
searchHit .shard (fetchResult .getSearchShardTarget ());
388
- if (sorted ) {
390
+ if (sortedTopDocs . isSortedByField ) {
389
391
FieldDoc fieldDoc = (FieldDoc ) shardDoc ;
390
392
searchHit .sortValues (fieldDoc .fields , reducedQueryPhase .sortValueFormats );
391
393
if (sortScoreIndex != -1 ) {
392
394
searchHit .score (((Number ) fieldDoc .fields [sortScoreIndex ]).floatValue ());
393
395
}
396
+ } else {
397
+ searchHit .score (shardDoc .score );
394
398
}
395
399
hits .add (searchHit );
396
400
}
397
401
}
398
- return new SearchHits (hits .toArray (new SearchHit [0 ]), reducedQueryPhase .totalHits , reducedQueryPhase .maxScore );
402
+ return new SearchHits (hits .toArray (new SearchHit [0 ]), reducedQueryPhase .totalHits ,
403
+ reducedQueryPhase .maxScore , sortedTopDocs .sortFields , sortedTopDocs .collapseField , sortedTopDocs .collapseValues );
399
404
}
400
405
401
406
/**
@@ -436,8 +441,7 @@ private ReducedQueryPhase reducedQueryPhase(Collection<? extends SearchPhaseResu
436
441
if (queryResults .isEmpty ()) { // early terminate we have nothing to reduce
437
442
final TotalHits totalHits = topDocsStats .getTotalHits ();
438
443
return new ReducedQueryPhase (totalHits , topDocsStats .fetchHits , topDocsStats .maxScore ,
439
- timedOut , terminatedEarly , null , null , null , EMPTY_DOCS , null ,
440
- null , numReducePhases , false , 0 , 0 , true );
444
+ timedOut , terminatedEarly , null , null , null , SortedTopDocs .EMPTY , null , numReducePhases , 0 , 0 , true );
441
445
}
442
446
final QuerySearchResult firstResult = queryResults .stream ().findFirst ().get ().queryResult ();
443
447
final boolean hasSuggest = firstResult .suggest () != null ;
@@ -499,11 +503,11 @@ private ReducedQueryPhase reducedQueryPhase(Collection<? extends SearchPhaseResu
499
503
final InternalAggregations aggregations = aggregationsList .isEmpty () ? null : reduceAggs (aggregationsList ,
500
504
firstResult .pipelineAggregators (), reduceContext );
501
505
final SearchProfileShardResults shardResults = profileResults .isEmpty () ? null : new SearchProfileShardResults (profileResults );
502
- final SortedTopDocs scoreDocs = sortDocs (isScrollRequest , queryResults , bufferedTopDocs , topDocsStats , from , size );
506
+ final SortedTopDocs sortedTopDocs = sortDocs (isScrollRequest , queryResults , bufferedTopDocs , topDocsStats , from , size );
503
507
final TotalHits totalHits = topDocsStats .getTotalHits ();
504
508
return new ReducedQueryPhase (totalHits , topDocsStats .fetchHits , topDocsStats .maxScore ,
505
- timedOut , terminatedEarly , suggest , aggregations , shardResults , scoreDocs . scoreDocs , scoreDocs . sortFields ,
506
- firstResult .sortValueFormats (), numReducePhases , scoreDocs . isSortedByField , size , from , false );
509
+ timedOut , terminatedEarly , suggest , aggregations , shardResults , sortedTopDocs ,
510
+ firstResult .sortValueFormats (), numReducePhases , size , from , firstResult == null );
507
511
}
508
512
509
513
/**
@@ -551,12 +555,8 @@ public static final class ReducedQueryPhase {
551
555
final SearchProfileShardResults shardResults ;
552
556
// the number of reduces phases
553
557
final int numReducePhases ;
554
- // the searches merged top docs
555
- final ScoreDoc [] scoreDocs ;
556
- // the top docs sort fields used to sort the score docs, <code>null</code> if the results are not sorted
557
- final SortField [] sortField ;
558
- // <code>true</code> iff the result score docs is sorted by a field (not score), this implies that <code>sortField</code> is set.
559
- final boolean isSortedByField ;
558
+ //encloses info about the merged top docs, the sort fields used to sort the score docs etc.
559
+ final SortedTopDocs sortedTopDocs ;
560
560
// the size of the top hits to return
561
561
final int size ;
562
562
// <code>true</code> iff the query phase had no results. Otherwise <code>false</code>
@@ -567,9 +567,8 @@ public static final class ReducedQueryPhase {
567
567
final DocValueFormat [] sortValueFormats ;
568
568
569
569
ReducedQueryPhase (TotalHits totalHits , long fetchHits , float maxScore , boolean timedOut , Boolean terminatedEarly , Suggest suggest ,
570
- InternalAggregations aggregations , SearchProfileShardResults shardResults , ScoreDoc [] scoreDocs ,
571
- SortField [] sortFields , DocValueFormat [] sortValueFormats , int numReducePhases , boolean isSortedByField , int size ,
572
- int from , boolean isEmptyResult ) {
570
+ InternalAggregations aggregations , SearchProfileShardResults shardResults , SortedTopDocs sortedTopDocs ,
571
+ DocValueFormat [] sortValueFormats , int numReducePhases , int size , int from , boolean isEmptyResult ) {
573
572
if (numReducePhases <= 0 ) {
574
573
throw new IllegalArgumentException ("at least one reduce phase must have been applied but was: " + numReducePhases );
575
574
}
@@ -586,9 +585,7 @@ public static final class ReducedQueryPhase {
586
585
this .aggregations = aggregations ;
587
586
this .shardResults = shardResults ;
588
587
this .numReducePhases = numReducePhases ;
589
- this .scoreDocs = scoreDocs ;
590
- this .sortField = sortFields ;
591
- this .isSortedByField = isSortedByField ;
588
+ this .sortedTopDocs = sortedTopDocs ;
592
589
this .size = size ;
593
590
this .from = from ;
594
591
this .isEmptyResult = isEmptyResult ;
@@ -728,7 +725,7 @@ InitialSearchPhase.ArraySearchPhaseResults<SearchPhaseResult> newSearchPhaseResu
728
725
}
729
726
return new InitialSearchPhase .ArraySearchPhaseResults <SearchPhaseResult >(numShards ) {
730
727
@ Override
731
- public ReducedQueryPhase reduce () {
728
+ ReducedQueryPhase reduce () {
732
729
return reducedQueryPhase (results .asList (), isScrollRequest , trackTotalHits );
733
730
}
734
731
};
@@ -770,15 +767,23 @@ void add(TopDocsAndMaxScore topDocs) {
770
767
}
771
768
772
769
static final class SortedTopDocs {
773
- static final SortedTopDocs EMPTY = new SortedTopDocs (EMPTY_DOCS , false , null );
770
+ static final SortedTopDocs EMPTY = new SortedTopDocs (EMPTY_DOCS , false , null , null , null );
771
+ // the searches merged top docs
774
772
final ScoreDoc [] scoreDocs ;
773
+ // <code>true</code> iff the result score docs is sorted by a field (not score), this implies that <code>sortField</code> is set.
775
774
final boolean isSortedByField ;
775
+ // the top docs sort fields used to sort the score docs, <code>null</code> if the results are not sorted
776
776
final SortField [] sortFields ;
777
+ final String collapseField ;
778
+ final Object [] collapseValues ;
777
779
778
- SortedTopDocs (ScoreDoc [] scoreDocs , boolean isSortedByField , SortField [] sortFields ) {
780
+ SortedTopDocs (ScoreDoc [] scoreDocs , boolean isSortedByField , SortField [] sortFields ,
781
+ String collapseField , Object [] collapseValues ) {
779
782
this .scoreDocs = scoreDocs ;
780
783
this .isSortedByField = isSortedByField ;
781
784
this .sortFields = sortFields ;
785
+ this .collapseField = collapseField ;
786
+ this .collapseValues = collapseValues ;
782
787
}
783
788
}
784
789
}
0 commit comments