HLRC: ML Delete Forecast API (#33526)

* HLRC: ML Delete Forecast API
This commit is contained in:
Benjamin Trent 2018-09-11 16:32:52 -07:00 committed by GitHub
parent 9f8dff9281
commit 27e07ec859
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 582 additions and 0 deletions

View File

@ -28,6 +28,7 @@ import org.apache.http.entity.ByteArrayEntity;
import org.apache.lucene.util.BytesRef; import org.apache.lucene.util.BytesRef;
import org.elasticsearch.client.RequestConverters.EndpointBuilder; import org.elasticsearch.client.RequestConverters.EndpointBuilder;
import org.elasticsearch.client.ml.CloseJobRequest; import org.elasticsearch.client.ml.CloseJobRequest;
import org.elasticsearch.client.ml.DeleteForecastRequest;
import org.elasticsearch.client.ml.DeleteJobRequest; import org.elasticsearch.client.ml.DeleteJobRequest;
import org.elasticsearch.client.ml.FlushJobRequest; import org.elasticsearch.client.ml.FlushJobRequest;
import org.elasticsearch.client.ml.ForecastJobRequest; import org.elasticsearch.client.ml.ForecastJobRequest;
@ -181,6 +182,26 @@ final class MLRequestConverters {
return request; return request;
} }
static Request deleteForecast(DeleteForecastRequest deleteForecastRequest) throws IOException {
String endpoint = new EndpointBuilder()
.addPathPartAsIs("_xpack")
.addPathPartAsIs("ml")
.addPathPartAsIs("anomaly_detectors")
.addPathPart(deleteForecastRequest.getJobId())
.addPathPartAsIs("_forecast")
.addPathPart(Strings.collectionToCommaDelimitedString(deleteForecastRequest.getForecastIds()))
.build();
Request request = new Request(HttpDelete.METHOD_NAME, endpoint);
RequestConverters.Params params = new RequestConverters.Params(request);
if (deleteForecastRequest.isAllowNoForecasts() != null) {
params.putParam("allow_no_forecasts", Boolean.toString(deleteForecastRequest.isAllowNoForecasts()));
}
if (deleteForecastRequest.timeout() != null) {
params.putParam("timeout", deleteForecastRequest.timeout().getStringRep());
}
return request;
}
static Request getBuckets(GetBucketsRequest getBucketsRequest) throws IOException { static Request getBuckets(GetBucketsRequest getBucketsRequest) throws IOException {
String endpoint = new EndpointBuilder() String endpoint = new EndpointBuilder()
.addPathPartAsIs("_xpack") .addPathPartAsIs("_xpack")

View File

@ -19,6 +19,8 @@
package org.elasticsearch.client; package org.elasticsearch.client;
import org.elasticsearch.action.ActionListener; import org.elasticsearch.action.ActionListener;
import org.elasticsearch.action.support.master.AcknowledgedResponse;
import org.elasticsearch.client.ml.DeleteForecastRequest;
import org.elasticsearch.client.ml.ForecastJobRequest; import org.elasticsearch.client.ml.ForecastJobRequest;
import org.elasticsearch.client.ml.ForecastJobResponse; import org.elasticsearch.client.ml.ForecastJobResponse;
import org.elasticsearch.client.ml.PostDataRequest; import org.elasticsearch.client.ml.PostDataRequest;
@ -389,6 +391,11 @@ public final class MachineLearningClient {
/** /**
* Updates a Machine Learning {@link org.elasticsearch.client.ml.job.config.Job} * Updates a Machine Learning {@link org.elasticsearch.client.ml.job.config.Job}
* *
* <p>
* For additional info
* see <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/ml-update-job.html"></a>
* </p>
*
* @param request the {@link UpdateJobRequest} object enclosing the desired updates * @param request the {@link UpdateJobRequest} object enclosing the desired updates
* @param options Additional request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized * @param options Additional request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized
* @return a PutJobResponse object containing the updated job object * @return a PutJobResponse object containing the updated job object
@ -427,6 +434,10 @@ public final class MachineLearningClient {
/** /**
* Updates a Machine Learning {@link org.elasticsearch.client.ml.job.config.Job} asynchronously * Updates a Machine Learning {@link org.elasticsearch.client.ml.job.config.Job} asynchronously
* *
* <p>
* For additional info
* see <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/ml-update-job.html"></a>
* </p>
* @param request the {@link UpdateJobRequest} object enclosing the desired updates * @param request the {@link UpdateJobRequest} object enclosing the desired updates
* @param options Additional request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized * @param options Additional request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized
* @param listener Listener to be notified upon request completion * @param listener Listener to be notified upon request completion
@ -440,6 +451,48 @@ public final class MachineLearningClient {
Collections.emptySet()); Collections.emptySet());
} }
/**
* Deletes Machine Learning Job Forecasts
*
* <p>
* For additional info
* see <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/ml-delete-forecast.html"></a>
* </p>
*
* @param request the {@link DeleteForecastRequest} object enclosing the desired jobId, forecastIDs, and other options
* @param options Additional request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized
* @return a AcknowledgedResponse object indicating request success
* @throws IOException when there is a serialization issue sending the request or receiving the response
*/
public AcknowledgedResponse deleteForecast(DeleteForecastRequest request, RequestOptions options) throws IOException {
return restHighLevelClient.performRequestAndParseEntity(request,
MLRequestConverters::deleteForecast,
options,
AcknowledgedResponse::fromXContent,
Collections.emptySet());
}
/**
* Deletes Machine Learning Job Forecasts asynchronously
*
* <p>
* For additional info
* see <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/ml-delete-forecast.html"></a>
* </p>
*
* @param request the {@link DeleteForecastRequest} object enclosing the desired jobId, forecastIDs, and other options
* @param options Additional request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized
* @param listener Listener to be notified upon request completion
*/
public void deleteForecastAsync(DeleteForecastRequest request, RequestOptions options, ActionListener<AcknowledgedResponse> listener) {
restHighLevelClient.performRequestAsyncAndParseEntity(request,
MLRequestConverters::deleteForecast,
options,
AcknowledgedResponse::fromXContent,
listener,
Collections.emptySet());
}
/** /**
* Gets the buckets for a Machine Learning Job. * Gets the buckets for a Machine Learning Job.
* <p> * <p>

View File

@ -0,0 +1,183 @@
/*
* Licensed to Elasticsearch under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.elasticsearch.client.ml;
import org.elasticsearch.action.ActionRequest;
import org.elasticsearch.action.ActionRequestValidationException;
import org.elasticsearch.client.ml.job.config.Job;
import org.elasticsearch.common.ParseField;
import org.elasticsearch.common.Strings;
import org.elasticsearch.common.unit.TimeValue;
import org.elasticsearch.common.xcontent.ConstructingObjectParser;
import org.elasticsearch.common.xcontent.ToXContentObject;
import org.elasticsearch.common.xcontent.XContentBuilder;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
/**
* POJO for a delete forecast request
*/
public class DeleteForecastRequest extends ActionRequest implements ToXContentObject {
public static final ParseField FORECAST_ID = new ParseField("forecast_id");
public static final ParseField ALLOW_NO_FORECASTS = new ParseField("allow_no_forecasts");
public static final ParseField TIMEOUT = new ParseField("timeout");
public static final String ALL = "_all";
public static final ConstructingObjectParser<DeleteForecastRequest, Void> PARSER =
new ConstructingObjectParser<>("delete_forecast_request", (a) -> new DeleteForecastRequest((String) a[0]));
static {
PARSER.declareString(ConstructingObjectParser.constructorArg(), Job.ID);
PARSER.declareStringOrNull(
(c, p) -> c.setForecastIds(Strings.commaDelimitedListToStringArray(p)), FORECAST_ID);
PARSER.declareBoolean(DeleteForecastRequest::setAllowNoForecasts, ALLOW_NO_FORECASTS);
PARSER.declareString(DeleteForecastRequest::timeout, TIMEOUT);
}
/**
* Create a new {@link DeleteForecastRequest} that explicitly deletes all forecasts
*
* @param jobId the jobId of the Job whose forecasts to delete
*/
public static DeleteForecastRequest deleteAllForecasts(String jobId) {
DeleteForecastRequest request = new DeleteForecastRequest(jobId);
request.setForecastIds(ALL);
return request;
}
private final String jobId;
private List<String> forecastIds = new ArrayList<>();
private Boolean allowNoForecasts;
private TimeValue timeout;
/**
* Create a new DeleteForecastRequest for the given Job ID
*
* @param jobId the jobId of the Job whose forecast(s) to delete
*/
public DeleteForecastRequest(String jobId) {
this.jobId = Objects.requireNonNull(jobId, Job.ID.getPreferredName());
}
public String getJobId() {
return jobId;
}
public List<String> getForecastIds() {
return forecastIds;
}
/**
* The forecast IDs to delete. Can be also be {@link DeleteForecastRequest#ALL} to explicitly delete ALL forecasts
*
* @param forecastIds forecast IDs to delete
*/
public void setForecastIds(String... forecastIds) {
setForecastIds(Arrays.asList(forecastIds));
}
void setForecastIds(List<String> forecastIds) {
if (forecastIds.stream().anyMatch(Objects::isNull)) {
throw new NullPointerException("forecastIds must not contain null values");
}
this.forecastIds = new ArrayList<>(forecastIds);
}
public Boolean isAllowNoForecasts() {
return allowNoForecasts;
}
/**
* Sets the `allow_no_forecasts` field.
*
* @param allowNoForecasts when {@code true} no error is thrown when {@link DeleteForecastRequest#ALL} does not find any forecasts
*/
public void setAllowNoForecasts(boolean allowNoForecasts) {
this.allowNoForecasts = allowNoForecasts;
}
/**
* Allows to set the timeout
* @param timeout timeout as a string (e.g. 1s)
*/
public void timeout(String timeout) {
this.timeout = TimeValue.parseTimeValue(timeout, this.timeout, getClass().getSimpleName() + ".timeout");
}
/**
* Allows to set the timeout
* @param timeout timeout as a {@link TimeValue}
*/
public void timeout(TimeValue timeout) {
this.timeout = timeout;
}
public TimeValue timeout() {
return timeout;
}
@Override
public boolean equals(Object other) {
if (this == other) {
return true;
}
if (other == null || getClass() != other.getClass()) {
return false;
}
DeleteForecastRequest that = (DeleteForecastRequest) other;
return Objects.equals(jobId, that.jobId) &&
Objects.equals(forecastIds, that.forecastIds) &&
Objects.equals(allowNoForecasts, that.allowNoForecasts) &&
Objects.equals(timeout, that.timeout);
}
@Override
public int hashCode() {
return Objects.hash(jobId, forecastIds, allowNoForecasts, timeout);
}
@Override
public ActionRequestValidationException validate() {
return null;
}
@Override
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
builder.startObject();
builder.field(Job.ID.getPreferredName(), jobId);
if (forecastIds != null) {
builder.field(FORECAST_ID.getPreferredName(), Strings.collectionToCommaDelimitedString(forecastIds));
}
if (allowNoForecasts != null) {
builder.field(ALLOW_NO_FORECASTS.getPreferredName(), allowNoForecasts);
}
if (timeout != null) {
builder.field(TIMEOUT.getPreferredName(), timeout.getStringRep());
}
builder.endObject();
return builder;
}
}

View File

@ -24,6 +24,7 @@ import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost; import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpPut; import org.apache.http.client.methods.HttpPut;
import org.elasticsearch.client.ml.CloseJobRequest; import org.elasticsearch.client.ml.CloseJobRequest;
import org.elasticsearch.client.ml.DeleteForecastRequest;
import org.elasticsearch.client.ml.DeleteJobRequest; import org.elasticsearch.client.ml.DeleteJobRequest;
import org.elasticsearch.client.ml.FlushJobRequest; import org.elasticsearch.client.ml.FlushJobRequest;
import org.elasticsearch.client.ml.ForecastJobRequest; import org.elasticsearch.client.ml.ForecastJobRequest;
@ -44,6 +45,7 @@ import org.elasticsearch.client.ml.job.config.Job;
import org.elasticsearch.client.ml.job.config.JobUpdate; import org.elasticsearch.client.ml.job.config.JobUpdate;
import org.elasticsearch.client.ml.job.config.JobUpdateTests; import org.elasticsearch.client.ml.job.config.JobUpdateTests;
import org.elasticsearch.client.ml.job.util.PageParams; import org.elasticsearch.client.ml.job.util.PageParams;
import org.elasticsearch.common.Strings;
import org.elasticsearch.common.unit.TimeValue; import org.elasticsearch.common.unit.TimeValue;
import org.elasticsearch.common.xcontent.XContentParser; import org.elasticsearch.common.xcontent.XContentParser;
import org.elasticsearch.common.xcontent.XContentType; import org.elasticsearch.common.xcontent.XContentType;
@ -204,6 +206,33 @@ public class MLRequestConvertersTests extends ESTestCase {
} }
} }
public void testDeleteForecast() throws Exception {
String jobId = randomAlphaOfLength(10);
DeleteForecastRequest deleteForecastRequest = new DeleteForecastRequest(jobId);
Request request = MLRequestConverters.deleteForecast(deleteForecastRequest);
assertEquals(HttpDelete.METHOD_NAME, request.getMethod());
assertEquals("/_xpack/ml/anomaly_detectors/" + jobId + "/_forecast", request.getEndpoint());
assertFalse(request.getParameters().containsKey("timeout"));
assertFalse(request.getParameters().containsKey("allow_no_forecasts"));
deleteForecastRequest.setForecastIds(randomAlphaOfLength(10), randomAlphaOfLength(10));
deleteForecastRequest.timeout("10s");
deleteForecastRequest.setAllowNoForecasts(true);
request = MLRequestConverters.deleteForecast(deleteForecastRequest);
assertEquals(
"/_xpack/ml/anomaly_detectors/" +
jobId +
"/_forecast/" +
Strings.collectionToCommaDelimitedString(deleteForecastRequest.getForecastIds()),
request.getEndpoint());
assertEquals("10s",
request.getParameters().get(DeleteForecastRequest.TIMEOUT.getPreferredName()));
assertEquals(Boolean.toString(true),
request.getParameters().get(DeleteForecastRequest.ALLOW_NO_FORECASTS.getPreferredName()));
}
public void testGetBuckets() throws IOException { public void testGetBuckets() throws IOException {
String jobId = randomAlphaOfLength(10); String jobId = randomAlphaOfLength(10);
GetBucketsRequest getBucketsRequest = new GetBucketsRequest(jobId); GetBucketsRequest getBucketsRequest = new GetBucketsRequest(jobId);

View File

@ -20,6 +20,10 @@ package org.elasticsearch.client;
import com.carrotsearch.randomizedtesting.generators.CodepointSetGenerator; import com.carrotsearch.randomizedtesting.generators.CodepointSetGenerator;
import org.elasticsearch.ElasticsearchStatusException; import org.elasticsearch.ElasticsearchStatusException;
import org.elasticsearch.action.get.GetRequest;
import org.elasticsearch.action.get.GetResponse;
import org.elasticsearch.action.support.master.AcknowledgedResponse;
import org.elasticsearch.client.ml.DeleteForecastRequest;
import org.elasticsearch.client.ml.ForecastJobRequest; import org.elasticsearch.client.ml.ForecastJobRequest;
import org.elasticsearch.client.ml.ForecastJobResponse; import org.elasticsearch.client.ml.ForecastJobResponse;
import org.elasticsearch.client.ml.PostDataRequest; import org.elasticsearch.client.ml.PostDataRequest;
@ -288,6 +292,75 @@ public class MachineLearningIT extends ESRestHighLevelClientTestCase {
assertEquals("Updated description", getResponse.jobs().get(0).getDescription()); assertEquals("Updated description", getResponse.jobs().get(0).getDescription());
} }
public void testDeleteForecast() throws Exception {
String jobId = "test-delete-forecast";
Job job = buildJob(jobId);
MachineLearningClient machineLearningClient = highLevelClient().machineLearning();
machineLearningClient.putJob(new PutJobRequest(job), RequestOptions.DEFAULT);
machineLearningClient.openJob(new OpenJobRequest(jobId), RequestOptions.DEFAULT);
Job noForecastsJob = buildJob("test-delete-forecast-none");
machineLearningClient.putJob(new PutJobRequest(noForecastsJob), RequestOptions.DEFAULT);
PostDataRequest.JsonBuilder builder = new PostDataRequest.JsonBuilder();
for(int i = 0; i < 30; i++) {
Map<String, Object> hashMap = new HashMap<>();
hashMap.put("total", randomInt(1000));
hashMap.put("timestamp", (i+1)*1000);
builder.addDoc(hashMap);
}
PostDataRequest postDataRequest = new PostDataRequest(jobId, builder);
machineLearningClient.postData(postDataRequest, RequestOptions.DEFAULT);
machineLearningClient.flushJob(new FlushJobRequest(jobId), RequestOptions.DEFAULT);
ForecastJobResponse forecastJobResponse1 = machineLearningClient.forecastJob(new ForecastJobRequest(jobId), RequestOptions.DEFAULT);
ForecastJobResponse forecastJobResponse2 = machineLearningClient.forecastJob(new ForecastJobRequest(jobId), RequestOptions.DEFAULT);
waitForForecastToComplete(jobId, forecastJobResponse1.getForecastId());
waitForForecastToComplete(jobId, forecastJobResponse2.getForecastId());
{
DeleteForecastRequest request = new DeleteForecastRequest(jobId);
request.setForecastIds(forecastJobResponse1.getForecastId(), forecastJobResponse2.getForecastId());
AcknowledgedResponse response = execute(request, machineLearningClient::deleteForecast,
machineLearningClient::deleteForecastAsync);
assertTrue(response.isAcknowledged());
assertFalse(forecastExists(jobId, forecastJobResponse1.getForecastId()));
assertFalse(forecastExists(jobId, forecastJobResponse2.getForecastId()));
}
{
DeleteForecastRequest request = DeleteForecastRequest.deleteAllForecasts(noForecastsJob.getId());
request.setAllowNoForecasts(true);
AcknowledgedResponse response = execute(request, machineLearningClient::deleteForecast,
machineLearningClient::deleteForecastAsync);
assertTrue(response.isAcknowledged());
}
{
DeleteForecastRequest request = DeleteForecastRequest.deleteAllForecasts(noForecastsJob.getId());
request.setAllowNoForecasts(false);
ElasticsearchStatusException exception = expectThrows(ElasticsearchStatusException.class,
() -> execute(request, machineLearningClient::deleteForecast, machineLearningClient::deleteForecastAsync));
assertThat(exception.status().getStatus(), equalTo(404));
}
}
private void waitForForecastToComplete(String jobId, String forecastId) throws Exception {
GetRequest request = new GetRequest(".ml-anomalies-" + jobId);
request.id(jobId + "_model_forecast_request_stats_" + forecastId);
assertBusy(() -> {
GetResponse getResponse = highLevelClient().get(request, RequestOptions.DEFAULT);
assertTrue(getResponse.isExists());
assertTrue(getResponse.getSourceAsString().contains("finished"));
}, 30, TimeUnit.SECONDS);
}
private boolean forecastExists(String jobId, String forecastId) throws Exception {
GetRequest getRequest = new GetRequest(".ml-anomalies-" + jobId);
getRequest.id(jobId + "_model_forecast_request_stats_" + forecastId);
GetResponse getResponse = highLevelClient().get(getRequest, RequestOptions.DEFAULT);
return getResponse.isExists();
}
public static String randomValidJobId() { public static String randomValidJobId() {
CodepointSetGenerator generator = new CodepointSetGenerator("abcdefghijklmnopqrstuvwxyz0123456789".toCharArray()); CodepointSetGenerator generator = new CodepointSetGenerator("abcdefghijklmnopqrstuvwxyz0123456789".toCharArray());
return generator.ofCodePointsLength(random(), 10, 10); return generator.ofCodePointsLength(random(), 10, 10);

View File

@ -21,8 +21,11 @@ package org.elasticsearch.client.documentation;
import org.elasticsearch.action.ActionListener; import org.elasticsearch.action.ActionListener;
import org.elasticsearch.action.LatchedActionListener; import org.elasticsearch.action.LatchedActionListener;
import org.elasticsearch.action.bulk.BulkRequest; import org.elasticsearch.action.bulk.BulkRequest;
import org.elasticsearch.action.get.GetRequest;
import org.elasticsearch.action.get.GetResponse;
import org.elasticsearch.action.index.IndexRequest; import org.elasticsearch.action.index.IndexRequest;
import org.elasticsearch.action.support.WriteRequest; import org.elasticsearch.action.support.WriteRequest;
import org.elasticsearch.action.support.master.AcknowledgedResponse;
import org.elasticsearch.client.ESRestHighLevelClientTestCase; import org.elasticsearch.client.ESRestHighLevelClientTestCase;
import org.elasticsearch.client.MachineLearningGetResultsIT; import org.elasticsearch.client.MachineLearningGetResultsIT;
import org.elasticsearch.client.MachineLearningIT; import org.elasticsearch.client.MachineLearningIT;
@ -31,6 +34,7 @@ import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient; import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.client.ml.CloseJobRequest; import org.elasticsearch.client.ml.CloseJobRequest;
import org.elasticsearch.client.ml.CloseJobResponse; import org.elasticsearch.client.ml.CloseJobResponse;
import org.elasticsearch.client.ml.DeleteForecastRequest;
import org.elasticsearch.client.ml.DeleteJobRequest; import org.elasticsearch.client.ml.DeleteJobRequest;
import org.elasticsearch.client.ml.DeleteJobResponse; import org.elasticsearch.client.ml.DeleteJobResponse;
import org.elasticsearch.client.ml.FlushJobRequest; import org.elasticsearch.client.ml.FlushJobRequest;
@ -639,8 +643,85 @@ public class MlClientDocumentationIT extends ESRestHighLevelClientTestCase {
assertTrue(latch.await(30L, TimeUnit.SECONDS)); assertTrue(latch.await(30L, TimeUnit.SECONDS));
} }
} }
public void testDeleteForecast() throws Exception {
RestHighLevelClient client = highLevelClient();
Job job = MachineLearningIT.buildJob("deleting-forecast-for-job");
client.machineLearning().putJob(new PutJobRequest(job), RequestOptions.DEFAULT);
client.machineLearning().openJob(new OpenJobRequest(job.getId()), RequestOptions.DEFAULT);
PostDataRequest.JsonBuilder builder = new PostDataRequest.JsonBuilder();
for(int i = 0; i < 30; i++) {
Map<String, Object> hashMap = new HashMap<>();
hashMap.put("total", randomInt(1000));
hashMap.put("timestamp", (i+1)*1000);
builder.addDoc(hashMap);
}
PostDataRequest postDataRequest = new PostDataRequest(job.getId(), builder);
client.machineLearning().postData(postDataRequest, RequestOptions.DEFAULT);
client.machineLearning().flushJob(new FlushJobRequest(job.getId()), RequestOptions.DEFAULT);
ForecastJobResponse forecastJobResponse = client.machineLearning().
forecastJob(new ForecastJobRequest(job.getId()), RequestOptions.DEFAULT);
String forecastId = forecastJobResponse.getForecastId();
GetRequest request = new GetRequest(".ml-anomalies-" + job.getId());
request.id(job.getId() + "_model_forecast_request_stats_" + forecastId);
assertBusy(() -> {
GetResponse getResponse = highLevelClient().get(request, RequestOptions.DEFAULT);
assertTrue(getResponse.isExists());
assertTrue(getResponse.getSourceAsString().contains("finished"));
}, 30, TimeUnit.SECONDS);
{
//tag::x-pack-ml-delete-forecast-request
DeleteForecastRequest deleteForecastRequest = new DeleteForecastRequest("deleting-forecast-for-job"); //<1>
//end::x-pack-ml-delete-forecast-request
//tag::x-pack-ml-delete-forecast-request-options
deleteForecastRequest.setForecastIds(forecastId); //<1>
deleteForecastRequest.timeout("30s"); //<2>
deleteForecastRequest.setAllowNoForecasts(true); //<3>
//end::x-pack-ml-delete-forecast-request-options
//tag::x-pack-ml-delete-forecast-execute
AcknowledgedResponse deleteForecastResponse = client.machineLearning().deleteForecast(deleteForecastRequest,
RequestOptions.DEFAULT);
//end::x-pack-ml-delete-forecast-execute
//tag::x-pack-ml-delete-forecast-response
boolean isAcknowledged = deleteForecastResponse.isAcknowledged(); //<1>
//end::x-pack-ml-delete-forecast-response
}
{
//tag::x-pack-ml-delete-forecast-listener
ActionListener<AcknowledgedResponse> listener = new ActionListener<AcknowledgedResponse>() {
@Override
public void onResponse(AcknowledgedResponse DeleteForecastResponse) {
//<1>
}
@Override
public void onFailure(Exception e) {
// <2>
}
};
//end::x-pack-ml-delete-forecast-listener
DeleteForecastRequest deleteForecastRequest = DeleteForecastRequest.deleteAllForecasts(job.getId());
deleteForecastRequest.setAllowNoForecasts(true);
// Replace the empty listener by a blocking listener in test
final CountDownLatch latch = new CountDownLatch(1);
listener = new LatchedActionListener<>(listener, latch);
// tag::x-pack-ml-delete-forecast-execute-async
client.machineLearning().deleteForecastAsync(deleteForecastRequest, RequestOptions.DEFAULT, listener); //<1>
// end::x-pack-ml-delete-forecast-execute-async
assertTrue(latch.await(30L, TimeUnit.SECONDS));
}
}
public void testGetJobStats() throws Exception { public void testGetJobStats() throws Exception {
RestHighLevelClient client = highLevelClient(); RestHighLevelClient client = highLevelClient();

View File

@ -0,0 +1,62 @@
/*
* Licensed to Elasticsearch under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.elasticsearch.client.ml;
import org.elasticsearch.client.ml.job.config.JobTests;
import org.elasticsearch.common.xcontent.XContentParser;
import org.elasticsearch.test.AbstractXContentTestCase;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
public class DeleteForecastRequestTests extends AbstractXContentTestCase<DeleteForecastRequest> {
@Override
protected DeleteForecastRequest createTestInstance() {
DeleteForecastRequest deleteForecastRequest = new DeleteForecastRequest(JobTests.randomValidJobId());
if (randomBoolean()) {
int length = randomInt(10);
List<String> ids = new ArrayList<>(length);
for(int i = 0; i < length; i++) {
ids.add(randomAlphaOfLength(10));
}
deleteForecastRequest.setForecastIds(ids);
}
if (randomBoolean()) {
deleteForecastRequest.setAllowNoForecasts(randomBoolean());
}
if (randomBoolean()) {
deleteForecastRequest.timeout(randomTimeValue());
}
return deleteForecastRequest;
}
@Override
protected DeleteForecastRequest doParseInstance(XContentParser parser) throws IOException {
return DeleteForecastRequest.PARSER.apply(parser, null);
}
@Override
protected boolean supportsUnknownFields() {
return false;
}
}

View File

@ -0,0 +1,78 @@
[[java-rest-high-x-pack-ml-delete-forecast]]
=== Delete Forecast API
The Delete Forecast API provides the ability to delete a {ml} job's
forecast in the cluster.
It accepts a `DeleteForecastRequest` object and responds
with an `AcknowledgedResponse` object.
[[java-rest-high-x-pack-ml-delete-forecast-request]]
==== Delete Forecast Request
A `DeleteForecastRequest` object gets created with an existing non-null `jobId`.
All other fields are optional for the request.
["source","java",subs="attributes,callouts,macros"]
--------------------------------------------------
include-tagged::{doc-tests}/MlClientDocumentationIT.java[x-pack-ml-delete-forecast-request]
--------------------------------------------------
<1> Constructing a new request referencing an existing `jobId`
==== Optional Arguments
The following arguments are optional.
["source","java",subs="attributes,callouts,macros"]
--------------------------------------------------
include-tagged::{doc-tests}/MlClientDocumentationIT.java[x-pack-ml-delete-forecast-request-options]
--------------------------------------------------
<1> Sets the specific forecastIds to delete, can be set to `_all` to indicate ALL forecasts for the given
`jobId`
<2> Set the timeout for the request to respond, default is 30 seconds
<3> Set the `allow_no_forecasts` option. When `true` no error will be returned if an `_all`
request finds no forecasts. It defaults to `true`
[[java-rest-high-x-pack-ml-delete-forecast-execution]]
==== Execution
The request can be executed through the `MachineLearningClient` contained
in the `RestHighLevelClient` object, accessed via the `machineLearningClient()` method.
["source","java",subs="attributes,callouts,macros"]
--------------------------------------------------
include-tagged::{doc-tests}/MlClientDocumentationIT.java[x-pack-ml-delete-forecast-execute]
--------------------------------------------------
[[java-rest-high-x-pack-ml-delete-forecast-execution-async]]
==== Asynchronous Execution
The request can also be executed asynchronously:
["source","java",subs="attributes,callouts,macros"]
--------------------------------------------------
include-tagged::{doc-tests}/MlClientDocumentationIT.java[x-pack-ml-delete-forecast-execute-async]
--------------------------------------------------
<1> The `DeleteForecastRequest` to execute and the `ActionListener` to use when
the execution completes
The method does not block and returns immediately. The passed `ActionListener` is used
to notify the caller of completion. A typical `ActionListener` for `AcknowledgedResponse` may
look like
["source","java",subs="attributes,callouts,macros"]
--------------------------------------------------
include-tagged::{doc-tests}/MlClientDocumentationIT.java[x-pack-ml-delete-forecast-listener]
--------------------------------------------------
<1> `onResponse` is called back when the action is completed successfully
<2> `onFailure` is called back when some unexpected error occurs
[[java-rest-high-x-pack-ml-delete-forecast-response]]
==== Delete Forecast Response
An `AcknowledgedResponse` contains an acknowledgement of the forecast(s) deletion
["source","java",subs="attributes,callouts,macros"]
--------------------------------------------------
include-tagged::{doc-tests}/MlClientDocumentationIT.java[x-pack-ml-delete-forecast-response]
--------------------------------------------------
<1> `isAcknowledged()` indicates if the forecast was successfully deleted or not.

View File

@ -221,6 +221,7 @@ The Java High Level REST Client supports the following Machine Learning APIs:
* <<java-rest-high-x-pack-ml-update-job>> * <<java-rest-high-x-pack-ml-update-job>>
* <<java-rest-high-x-pack-ml-get-job-stats>> * <<java-rest-high-x-pack-ml-get-job-stats>>
* <<java-rest-high-x-pack-ml-forecast-job>> * <<java-rest-high-x-pack-ml-forecast-job>>
* <<java-rest-high-x-pack-ml-delete-forecast>>
* <<java-rest-high-x-pack-ml-get-buckets>> * <<java-rest-high-x-pack-ml-get-buckets>>
* <<java-rest-high-x-pack-ml-get-overall-buckets>> * <<java-rest-high-x-pack-ml-get-overall-buckets>>
* <<java-rest-high-x-pack-ml-get-records>> * <<java-rest-high-x-pack-ml-get-records>>
@ -237,6 +238,7 @@ include::ml/update-job.asciidoc[]
include::ml/flush-job.asciidoc[] include::ml/flush-job.asciidoc[]
include::ml/get-job-stats.asciidoc[] include::ml/get-job-stats.asciidoc[]
include::ml/forecast-job.asciidoc[] include::ml/forecast-job.asciidoc[]
include::ml/delete-forecast.asciidoc[]
include::ml/get-buckets.asciidoc[] include::ml/get-buckets.asciidoc[]
include::ml/get-overall-buckets.asciidoc[] include::ml/get-overall-buckets.asciidoc[]
include::ml/get-records.asciidoc[] include::ml/get-records.asciidoc[]