1414import org .elasticsearch .action .search .SearchResponse ;
1515import org .elasticsearch .action .support .ThreadedActionListener ;
1616import org .elasticsearch .client .OriginSettingClient ;
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 ;
2217import org .elasticsearch .index .query .BoolQueryBuilder ;
2318import org .elasticsearch .index .query .QueryBuilder ;
2419import org .elasticsearch .index .query .QueryBuilders ;
3025import org .elasticsearch .search .SearchHits ;
3126import org .elasticsearch .search .builder .SearchSourceBuilder ;
3227import org .elasticsearch .threadpool .ThreadPool ;
28+ import org .elasticsearch .xpack .core .common .time .TimeUtils ;
3329import org .elasticsearch .xpack .core .ml .job .config .Job ;
3430import org .elasticsearch .xpack .core .ml .job .persistence .AnomalyDetectorsIndex ;
3531import org .elasticsearch .xpack .core .ml .job .persistence .ElasticsearchMappings ;
3834import org .elasticsearch .xpack .core .ml .job .results .Result ;
3935import org .elasticsearch .xpack .ml .MachineLearning ;
4036
41- import java .io .IOException ;
42- import java .io .InputStream ;
4337import java .time .Clock ;
4438import java .time .Instant ;
4539import java .util .ArrayList ;
@@ -84,7 +78,11 @@ public void remove(float requestsPerSec, ActionListener<Boolean> listener, Suppl
8478 .filter (QueryBuilders .termQuery (Result .RESULT_TYPE .getPreferredName (), ForecastRequestStats .RESULT_TYPE_VALUE ))
8579 .filter (QueryBuilders .existsQuery (ForecastRequestStats .EXPIRY_TIME .getPreferredName ())));
8680 source .size (MAX_FORECASTS );
87- source .trackTotalHits (true );
81+ source .fetchSource (false );
82+ source .docValueField (Job .ID .getPreferredName (), null );
83+ source .docValueField (ForecastRequestStats .FORECAST_ID .getPreferredName (), null );
84+ source .docValueField (ForecastRequestStats .EXPIRY_TIME .getPreferredName (), "epoch_millis" );
85+
8886
8987 // _doc is the most efficient sort order and will also disable scoring
9088 source .sort (ElasticsearchMappings .ES_DOC );
@@ -101,11 +99,9 @@ private void deleteForecasts(
10199 ActionListener <Boolean > listener ,
102100 Supplier <Boolean > isTimedOutSupplier
103101 ) {
104- List <ForecastRequestStats > forecastsToDelete ;
105- try {
106- forecastsToDelete = findForecastsToDelete (searchResponse );
107- } catch (IOException e ) {
108- listener .onFailure (e );
102+ List <JobForecastId > forecastsToDelete = findForecastsToDelete (searchResponse );
103+ if (forecastsToDelete .isEmpty ()) {
104+ listener .onResponse (true );
109105 return ;
110106 }
111107
@@ -117,7 +113,7 @@ private void deleteForecasts(
117113 DeleteByQueryRequest request = buildDeleteByQuery (forecastsToDelete )
118114 .setRequestsPerSecond (requestsPerSec )
119115 .setAbortOnVersionConflict (false );
120- client .execute (DeleteByQueryAction .INSTANCE , request , new ActionListener <BulkByScrollResponse >() {
116+ client .execute (DeleteByQueryAction .INSTANCE , request , new ActionListener <>() {
121117 @ Override
122118 public void onResponse (BulkByScrollResponse bulkByScrollResponse ) {
123119 try {
@@ -138,39 +134,51 @@ public void onFailure(Exception e) {
138134 });
139135 }
140136
141- private List <ForecastRequestStats > findForecastsToDelete (SearchResponse searchResponse ) throws IOException {
142- List <ForecastRequestStats > forecastsToDelete = new ArrayList <>();
137+ private List <JobForecastId > findForecastsToDelete (SearchResponse searchResponse ) {
138+ List <JobForecastId > forecastsToDelete = new ArrayList <>();
143139
144140 SearchHits hits = searchResponse .getHits ();
145141 if (hits .getTotalHits ().value > MAX_FORECASTS ) {
146142 LOGGER .info ("More than [{}] forecasts were found. This run will only delete [{}] of them" , MAX_FORECASTS , MAX_FORECASTS );
147143 }
148144
149145 for (SearchHit hit : hits .getHits ()) {
150- try (InputStream stream = hit .getSourceRef ().streamInput ();
151- XContentParser parser = XContentFactory .xContent (XContentType .JSON ).createParser (
152- NamedXContentRegistry .EMPTY , LoggingDeprecationHandler .INSTANCE , stream )) {
153- ForecastRequestStats forecastRequestStats = ForecastRequestStats .LENIENT_PARSER .apply (parser , null );
154- if (forecastRequestStats .getExpiryTime ().toEpochMilli () < cutoffEpochMs ) {
155- forecastsToDelete .add (forecastRequestStats );
146+ String expiryTime = stringFieldValueOrNull (hit , ForecastRequestStats .EXPIRY_TIME .getPreferredName ());
147+ if (expiryTime == null ) {
148+ LOGGER .warn ("Forecast request stats document [{}] has a null [{}] field" , hit .getId (),
149+ ForecastRequestStats .EXPIRY_TIME .getPreferredName ());
150+ continue ;
151+ }
152+ long expiryMs = TimeUtils .parseToEpochMs (expiryTime );
153+ if (expiryMs < cutoffEpochMs ) {
154+ JobForecastId idPair = new JobForecastId (
155+ stringFieldValueOrNull (hit , Job .ID .getPreferredName ()),
156+ stringFieldValueOrNull (hit , Forecast .FORECAST_ID .getPreferredName ()));
157+
158+ if (idPair .hasNullValue () == false ) {
159+ forecastsToDelete .add (idPair );
156160 }
161+
157162 }
163+
158164 }
159165 return forecastsToDelete ;
160166 }
161167
162- private DeleteByQueryRequest buildDeleteByQuery (List <ForecastRequestStats > forecastsToDelete ) {
168+ private DeleteByQueryRequest buildDeleteByQuery (List <JobForecastId > ids ) {
163169 DeleteByQueryRequest request = new DeleteByQueryRequest ();
164170 request .setSlices (AbstractBulkByScrollRequest .AUTO_SLICES );
165171
166172 request .indices (RESULTS_INDEX_PATTERN );
167173 BoolQueryBuilder boolQuery = QueryBuilders .boolQuery ().minimumShouldMatch (1 );
168174 boolQuery .must (QueryBuilders .termsQuery (Result .RESULT_TYPE .getPreferredName (),
169175 ForecastRequestStats .RESULT_TYPE_VALUE , Forecast .RESULT_TYPE_VALUE ));
170- for (ForecastRequestStats forecastToDelete : forecastsToDelete ) {
171- boolQuery .should (QueryBuilders .boolQuery ()
172- .must (QueryBuilders .termQuery (Job .ID .getPreferredName (), forecastToDelete .getJobId ()))
173- .must (QueryBuilders .termQuery (Forecast .FORECAST_ID .getPreferredName (), forecastToDelete .getForecastId ())));
176+ for (JobForecastId jobForecastId : ids ) {
177+ if (jobForecastId .hasNullValue () == false ) {
178+ boolQuery .should (QueryBuilders .boolQuery ()
179+ .must (QueryBuilders .termQuery (Job .ID .getPreferredName (), jobForecastId .jobId ))
180+ .must (QueryBuilders .termQuery (Forecast .FORECAST_ID .getPreferredName (), jobForecastId .forecastId )));
181+ }
174182 }
175183 QueryBuilder query = QueryBuilders .boolQuery ().filter (boolQuery );
176184 request .setQuery (query );
@@ -180,4 +188,18 @@ private DeleteByQueryRequest buildDeleteByQuery(List<ForecastRequestStats> forec
180188
181189 return request ;
182190 }
191+
192+ private static class JobForecastId {
193+ private final String jobId ;
194+ private final String forecastId ;
195+
196+ private JobForecastId (String jobId , String forecastId ) {
197+ this .jobId = jobId ;
198+ this .forecastId = forecastId ;
199+ }
200+
201+ boolean hasNullValue () {
202+ return jobId == null || forecastId == null ;
203+ }
204+ }
183205}
0 commit comments