14
14
import org .elasticsearch .action .search .SearchResponse ;
15
15
import org .elasticsearch .action .support .ThreadedActionListener ;
16
16
import org .elasticsearch .client .Client ;
17
- import org .elasticsearch .common .xcontent .LoggingDeprecationHandler ;
18
- import org .elasticsearch .common .xcontent .NamedXContentRegistry ;
19
- import org .elasticsearch .common .xcontent .XContentFactory ;
20
- import org .elasticsearch .common .xcontent .XContentParser ;
21
- import org .elasticsearch .common .xcontent .XContentType ;
17
+ import org .elasticsearch .common .document .DocumentField ;
22
18
import org .elasticsearch .index .query .BoolQueryBuilder ;
23
19
import org .elasticsearch .index .query .QueryBuilder ;
24
20
import org .elasticsearch .index .query .QueryBuilders ;
39
35
import org .joda .time .DateTime ;
40
36
import org .joda .time .chrono .ISOChronology ;
41
37
42
- import java .io .IOException ;
43
- import java .io .InputStream ;
44
38
import java .util .ArrayList ;
45
39
import java .util .List ;
46
40
import java .util .Objects ;
@@ -83,6 +77,10 @@ public void remove(ActionListener<Boolean> listener, Supplier<Boolean> isTimedOu
83
77
.filter (QueryBuilders .termQuery (Result .RESULT_TYPE .getPreferredName (), ForecastRequestStats .RESULT_TYPE_VALUE ))
84
78
.filter (QueryBuilders .existsQuery (ForecastRequestStats .EXPIRY_TIME .getPreferredName ())));
85
79
source .size (MAX_FORECASTS );
80
+ source .fetchSource (false );
81
+ source .docValueField (Job .ID .getPreferredName (), null );
82
+ source .docValueField (ForecastRequestStats .FORECAST_ID .getPreferredName (), null );
83
+ source .docValueField (ForecastRequestStats .EXPIRY_TIME .getPreferredName (), "epoch_millis" );
86
84
87
85
// _doc is the most efficient sort order and will also disable scoring
88
86
source .sort (ElasticsearchMappings .ES_DOC );
@@ -94,11 +92,9 @@ public void remove(ActionListener<Boolean> listener, Supplier<Boolean> isTimedOu
94
92
}
95
93
96
94
private void deleteForecasts (SearchResponse searchResponse , ActionListener <Boolean > listener , Supplier <Boolean > isTimedOutSupplier ) {
97
- List <ForecastRequestStats > forecastsToDelete ;
98
- try {
99
- forecastsToDelete = findForecastsToDelete (searchResponse );
100
- } catch (IOException e ) {
101
- listener .onFailure (e );
95
+ List <JobForecastId > forecastsToDelete = findForecastsToDelete (searchResponse );
96
+ if (forecastsToDelete .isEmpty ()) {
97
+ listener .onResponse (true );
102
98
return ;
103
99
}
104
100
@@ -129,39 +125,56 @@ public void onFailure(Exception e) {
129
125
});
130
126
}
131
127
132
- private List <ForecastRequestStats > findForecastsToDelete (SearchResponse searchResponse ) throws IOException {
133
- List <ForecastRequestStats > forecastsToDelete = new ArrayList <>();
128
+ private List <JobForecastId > findForecastsToDelete (SearchResponse searchResponse ) {
129
+ List <JobForecastId > forecastsToDelete = new ArrayList <>();
134
130
135
131
SearchHits hits = searchResponse .getHits ();
136
132
if (hits .getTotalHits () > MAX_FORECASTS ) {
137
133
LOGGER .info ("More than [{}] forecasts were found. This run will only delete [{}] of them" , MAX_FORECASTS , MAX_FORECASTS );
138
134
}
139
135
140
136
for (SearchHit hit : hits .getHits ()) {
141
- try (InputStream stream = hit .getSourceRef ().streamInput ();
142
- XContentParser parser = XContentFactory .xContent (XContentType .JSON ).createParser (
143
- NamedXContentRegistry .EMPTY , LoggingDeprecationHandler .INSTANCE , stream )) {
144
- ForecastRequestStats forecastRequestStats = ForecastRequestStats .LENIENT_PARSER .apply (parser , null );
145
- if (forecastRequestStats .getExpiryTime ().toEpochMilli () < cutoffEpochMs ) {
146
- forecastsToDelete .add (forecastRequestStats );
137
+ DocumentField docField = hit .field (ForecastRequestStats .EXPIRY_TIME .getPreferredName ());
138
+ if (docField == null ) {
139
+ LOGGER .warn ("Forecast request stats document [{}] has a null [{}] field" , hit .getId (),
140
+ ForecastRequestStats .EXPIRY_TIME .getPreferredName ());
141
+ continue ;
142
+ }
143
+
144
+ Long expiryMs = parseDateField (docField .getValue ());
145
+ if (expiryMs == null ) {
146
+ LOGGER .warn ("Forecast request stats document [{}] date field [{}] cannot be parsed" , hit .getId (),
147
+ ForecastRequestStats .EXPIRY_TIME .getPreferredName ());
148
+ continue ;
149
+ }
150
+
151
+ if (expiryMs < cutoffEpochMs ) {
152
+ JobForecastId idPair = new JobForecastId (
153
+ stringFieldValueOrNull (hit , Job .ID .getPreferredName ()),
154
+ stringFieldValueOrNull (hit , Forecast .FORECAST_ID .getPreferredName ()));
155
+
156
+ if (idPair .hasNullValue () == false ) {
157
+ forecastsToDelete .add (idPair );
147
158
}
148
159
}
149
160
}
150
161
return forecastsToDelete ;
151
162
}
152
163
153
- private DeleteByQueryRequest buildDeleteByQuery (List <ForecastRequestStats > forecastsToDelete ) {
164
+ private DeleteByQueryRequest buildDeleteByQuery (List <JobForecastId > ids ) {
154
165
DeleteByQueryRequest request = new DeleteByQueryRequest ();
155
166
request .setSlices (5 );
156
167
157
168
request .indices (RESULTS_INDEX_PATTERN );
158
169
BoolQueryBuilder boolQuery = QueryBuilders .boolQuery ().minimumShouldMatch (1 );
159
170
boolQuery .must (QueryBuilders .termsQuery (Result .RESULT_TYPE .getPreferredName (),
160
171
ForecastRequestStats .RESULT_TYPE_VALUE , Forecast .RESULT_TYPE_VALUE ));
161
- for (ForecastRequestStats forecastToDelete : forecastsToDelete ) {
162
- boolQuery .should (QueryBuilders .boolQuery ()
163
- .must (QueryBuilders .termQuery (Job .ID .getPreferredName (), forecastToDelete .getJobId ()))
164
- .must (QueryBuilders .termQuery (Forecast .FORECAST_ID .getPreferredName (), forecastToDelete .getForecastId ())));
172
+ for (JobForecastId jobForecastId : ids ) {
173
+ if (jobForecastId .hasNullValue () == false ) {
174
+ boolQuery .should (QueryBuilders .boolQuery ()
175
+ .must (QueryBuilders .termQuery (Job .ID .getPreferredName (), jobForecastId .jobId ))
176
+ .must (QueryBuilders .termQuery (Forecast .FORECAST_ID .getPreferredName (), jobForecastId .forecastId )));
177
+ }
165
178
}
166
179
QueryBuilder query = QueryBuilders .boolQuery ().filter (boolQuery );
167
180
request .setQuery (query );
@@ -171,4 +184,28 @@ private DeleteByQueryRequest buildDeleteByQuery(List<ForecastRequestStats> forec
171
184
172
185
return request ;
173
186
}
187
+
188
+ static Long parseDateField (Object value ) {
189
+ if (value instanceof String ) { // doc_value field with the epoch_millis format
190
+ return Long .parseLong ((String )value );
191
+ } else if (value instanceof Long ) { // pre-6.0 field
192
+ return (Long )value ;
193
+ } else {
194
+ return null ;
195
+ }
196
+ }
197
+
198
+ private static class JobForecastId {
199
+ private final String jobId ;
200
+ private final String forecastId ;
201
+
202
+ private JobForecastId (String jobId , String forecastId ) {
203
+ this .jobId = jobId ;
204
+ this .forecastId = forecastId ;
205
+ }
206
+
207
+ boolean hasNullValue () {
208
+ return jobId == null || forecastId == null ;
209
+ }
210
+ }
174
211
}
0 commit comments