1414import org .elasticsearch .action .search .SearchResponse ;
1515import org .elasticsearch .action .support .ThreadedActionListener ;
1616import 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 ;
2218import org .elasticsearch .index .query .BoolQueryBuilder ;
2319import org .elasticsearch .index .query .QueryBuilder ;
2420import org .elasticsearch .index .query .QueryBuilders ;
3935import org .joda .time .DateTime ;
4036import org .joda .time .chrono .ISOChronology ;
4137
42- import java .io .IOException ;
43- import java .io .InputStream ;
4438import java .util .ArrayList ;
4539import java .util .List ;
4640import java .util .Objects ;
@@ -83,6 +77,10 @@ public void remove(ActionListener<Boolean> listener, Supplier<Boolean> isTimedOu
8377 .filter (QueryBuilders .termQuery (Result .RESULT_TYPE .getPreferredName (), ForecastRequestStats .RESULT_TYPE_VALUE ))
8478 .filter (QueryBuilders .existsQuery (ForecastRequestStats .EXPIRY_TIME .getPreferredName ())));
8579 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" );
8684
8785 // _doc is the most efficient sort order and will also disable scoring
8886 source .sort (ElasticsearchMappings .ES_DOC );
@@ -94,11 +92,9 @@ public void remove(ActionListener<Boolean> listener, Supplier<Boolean> isTimedOu
9492 }
9593
9694 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 );
10298 return ;
10399 }
104100
@@ -129,39 +125,56 @@ public void onFailure(Exception e) {
129125 });
130126 }
131127
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 <>();
134130
135131 SearchHits hits = searchResponse .getHits ();
136132 if (hits .getTotalHits () > MAX_FORECASTS ) {
137133 LOGGER .info ("More than [{}] forecasts were found. This run will only delete [{}] of them" , MAX_FORECASTS , MAX_FORECASTS );
138134 }
139135
140136 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 );
147158 }
148159 }
149160 }
150161 return forecastsToDelete ;
151162 }
152163
153- private DeleteByQueryRequest buildDeleteByQuery (List <ForecastRequestStats > forecastsToDelete ) {
164+ private DeleteByQueryRequest buildDeleteByQuery (List <JobForecastId > ids ) {
154165 DeleteByQueryRequest request = new DeleteByQueryRequest ();
155166 request .setSlices (5 );
156167
157168 request .indices (RESULTS_INDEX_PATTERN );
158169 BoolQueryBuilder boolQuery = QueryBuilders .boolQuery ().minimumShouldMatch (1 );
159170 boolQuery .must (QueryBuilders .termsQuery (Result .RESULT_TYPE .getPreferredName (),
160171 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+ }
165178 }
166179 QueryBuilder query = QueryBuilders .boolQuery ().filter (boolQuery );
167180 request .setQuery (query );
@@ -171,4 +184,28 @@ private DeleteByQueryRequest buildDeleteByQuery(List<ForecastRequestStats> forec
171184
172185 return request ;
173186 }
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+ }
174211}
0 commit comments