20
20
package org .elasticsearch .action .search ;
21
21
22
22
import org .apache .logging .log4j .Logger ;
23
+ import org .apache .lucene .search .TopFieldDocs ;
23
24
import org .elasticsearch .action .ActionListener ;
24
25
import org .elasticsearch .cluster .ClusterState ;
25
26
import org .elasticsearch .cluster .routing .GroupShardsIterator ;
28
29
import org .elasticsearch .search .SearchShardTarget ;
29
30
import org .elasticsearch .search .builder .SearchSourceBuilder ;
30
31
import org .elasticsearch .search .internal .AliasFilter ;
32
+ import org .elasticsearch .search .internal .SearchContext ;
33
+ import org .elasticsearch .search .internal .ShardSearchRequest ;
34
+ import org .elasticsearch .search .query .QuerySearchResult ;
31
35
import org .elasticsearch .transport .Transport ;
32
36
33
37
import java .util .Map ;
34
38
import java .util .Set ;
35
39
import java .util .concurrent .Executor ;
36
40
import java .util .function .BiFunction ;
37
41
38
- final class SearchQueryThenFetchAsyncAction extends AbstractSearchAsyncAction <SearchPhaseResult > {
42
+ import static org .elasticsearch .action .search .SearchPhaseController .getTopDocsSize ;
43
+
44
+ class SearchQueryThenFetchAsyncAction extends AbstractSearchAsyncAction <SearchPhaseResult > {
39
45
40
46
private final SearchPhaseController searchPhaseController ;
41
47
private final SearchProgressListener progressListener ;
42
48
49
+ // informations to track the best bottom top doc globally.
50
+ private final int topDocsSize ;
51
+ private final int trackTotalHitsUpTo ;
52
+ private volatile BottomSortValuesCollector bottomSortCollector ;
53
+
43
54
SearchQueryThenFetchAsyncAction (final Logger logger , final SearchTransportService searchTransportService ,
44
55
final BiFunction <String , String , Transport .Connection > nodeIdToConnection ,
45
56
final Map <String , AliasFilter > aliasFilter ,
@@ -53,27 +64,64 @@ final class SearchQueryThenFetchAsyncAction extends AbstractSearchAsyncAction<Se
53
64
executor , request , listener , shardsIts , timeProvider , clusterState , task ,
54
65
searchPhaseController .newSearchPhaseResults (task .getProgressListener (), request , shardsIts .size ()),
55
66
request .getMaxConcurrentShardRequests (), clusters );
67
+ this .topDocsSize = getTopDocsSize (request );
68
+ this .trackTotalHitsUpTo = request .resolveTrackTotalHitsUpTo ();
56
69
this .searchPhaseController = searchPhaseController ;
57
70
this .progressListener = task .getProgressListener ();
58
- final SearchProgressListener progressListener = task .getProgressListener ();
59
71
final SearchSourceBuilder sourceBuilder = request .source ();
60
72
progressListener .notifyListShards (SearchProgressListener .buildSearchShards (this .shardsIts ),
61
73
SearchProgressListener .buildSearchShards (toSkipShardsIts ), clusters , sourceBuilder == null || sourceBuilder .size () != 0 );
62
74
}
63
75
64
76
protected void executePhaseOnShard (final SearchShardIterator shardIt , final ShardRouting shard ,
65
77
final SearchActionListener <SearchPhaseResult > listener ) {
78
+ ShardSearchRequest request = rewriteShardSearchRequest (super .buildShardSearchRequest (shardIt ));
66
79
getSearchTransport ().sendExecuteQuery (getConnection (shardIt .getClusterAlias (), shard .currentNodeId ()),
67
- buildShardSearchRequest ( shardIt ) , getTask (), listener );
80
+ request , getTask (), listener );
68
81
}
69
82
70
83
@ Override
71
84
protected void onShardGroupFailure (int shardIndex , SearchShardTarget shardTarget , Exception exc ) {
72
85
progressListener .notifyQueryFailure (shardIndex , shardTarget , exc );
73
86
}
74
87
88
+ @ Override
89
+ protected void onShardResult (SearchPhaseResult result , SearchShardIterator shardIt ) {
90
+ QuerySearchResult queryResult = result .queryResult ();
91
+ if (queryResult .isNull () == false && queryResult .topDocs ().topDocs instanceof TopFieldDocs ) {
92
+ TopFieldDocs topDocs = (TopFieldDocs ) queryResult .topDocs ().topDocs ;
93
+ if (bottomSortCollector == null ) {
94
+ synchronized (this ) {
95
+ if (bottomSortCollector == null ) {
96
+ bottomSortCollector = new BottomSortValuesCollector (topDocsSize , topDocs .fields );
97
+ }
98
+ }
99
+ }
100
+ bottomSortCollector .consumeTopDocs (topDocs , queryResult .sortValueFormats ());
101
+ }
102
+ super .onShardResult (result , shardIt );
103
+ }
104
+
75
105
@ Override
76
106
protected SearchPhase getNextPhase (final SearchPhaseResults <SearchPhaseResult > results , final SearchPhaseContext context ) {
77
107
return new FetchSearchPhase (results , searchPhaseController , context , clusterState ());
78
108
}
109
+
110
+ private ShardSearchRequest rewriteShardSearchRequest (ShardSearchRequest request ) {
111
+ if (bottomSortCollector == null ) {
112
+ return request ;
113
+ }
114
+
115
+ // disable tracking total hits if we already reached the required estimation.
116
+ if (trackTotalHitsUpTo != SearchContext .TRACK_TOTAL_HITS_ACCURATE
117
+ && bottomSortCollector .getTotalHits () > trackTotalHitsUpTo ) {
118
+ request .source (request .source ().shallowCopy ().trackTotalHits (false ));
119
+ }
120
+
121
+ // set the current best bottom field doc
122
+ if (bottomSortCollector .getBottomSortValues () != null ) {
123
+ request .setBottomSortValues (bottomSortCollector .getBottomSortValues ());
124
+ }
125
+ return request ;
126
+ }
79
127
}
0 commit comments