diff --git a/client/rest-high-level/src/main/java/org/elasticsearch/client/MLRequestConverters.java b/client/rest-high-level/src/main/java/org/elasticsearch/client/MLRequestConverters.java index cbf653a713d..ecbe7f2d3a5 100644 --- a/client/rest-high-level/src/main/java/org/elasticsearch/client/MLRequestConverters.java +++ b/client/rest-high-level/src/main/java/org/elasticsearch/client/MLRequestConverters.java @@ -30,6 +30,7 @@ import org.elasticsearch.client.RequestConverters.EndpointBuilder; import org.elasticsearch.client.ml.CloseJobRequest; import org.elasticsearch.client.ml.DeleteJobRequest; import org.elasticsearch.client.ml.FlushJobRequest; +import org.elasticsearch.client.ml.ForecastJobRequest; import org.elasticsearch.client.ml.GetBucketsRequest; import org.elasticsearch.client.ml.GetInfluencersRequest; import org.elasticsearch.client.ml.GetJobRequest; @@ -153,6 +154,19 @@ final class MLRequestConverters { return request; } + static Request forecastJob(ForecastJobRequest forecastJobRequest) throws IOException { + String endpoint = new EndpointBuilder() + .addPathPartAsIs("_xpack") + .addPathPartAsIs("ml") + .addPathPartAsIs("anomaly_detectors") + .addPathPart(forecastJobRequest.getJobId()) + .addPathPartAsIs("_forecast") + .build(); + Request request = new Request(HttpPost.METHOD_NAME, endpoint); + request.setEntity(createEntity(forecastJobRequest, REQUEST_BODY_CONTENT_TYPE)); + return request; + } + static Request updateJob(UpdateJobRequest updateJobRequest) throws IOException { String endpoint = new EndpointBuilder() .addPathPartAsIs("_xpack") diff --git a/client/rest-high-level/src/main/java/org/elasticsearch/client/MachineLearningClient.java b/client/rest-high-level/src/main/java/org/elasticsearch/client/MachineLearningClient.java index 6e54b925986..85c5771f345 100644 --- a/client/rest-high-level/src/main/java/org/elasticsearch/client/MachineLearningClient.java +++ b/client/rest-high-level/src/main/java/org/elasticsearch/client/MachineLearningClient.java @@ -19,6 +19,8 @@ package org.elasticsearch.client; import org.elasticsearch.action.ActionListener; +import org.elasticsearch.client.ml.ForecastJobRequest; +import org.elasticsearch.client.ml.ForecastJobResponse; import org.elasticsearch.client.ml.PostDataRequest; import org.elasticsearch.client.ml.PostDataResponse; import org.elasticsearch.client.ml.UpdateJobRequest; @@ -360,6 +362,28 @@ public final class MachineLearningClient { Collections.emptySet()); } + /** + * Creates a forecast of an existing, opened Machine Learning Job + * + * This predicts the future behavior of a time series by using its historical behavior. + * + *

+ * For additional info + * see Forecast ML Job Documentation + *

+ * @param request ForecastJobRequest with forecasting options + * @param options Additional request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized + * @return response containing forecast acknowledgement and new forecast's ID + * @throws IOException when there is a serialization issue sending the request or receiving the response + */ + public ForecastJobResponse forecastJob(ForecastJobRequest request, RequestOptions options) throws IOException { + return restHighLevelClient.performRequestAndParseEntity(request, + MLRequestConverters::forecastJob, + options, + ForecastJobResponse::fromXContent, + Collections.emptySet()); + } + /** * Updates a Machine Learning {@link org.elasticsearch.client.ml.job.config.Job} * @@ -376,6 +400,28 @@ public final class MachineLearningClient { Collections.emptySet()); } + /** + * Creates a forecast of an existing, opened Machine Learning Job asynchronously + * + * This predicts the future behavior of a time series by using its historical behavior. + * + *

+ * For additional info + * see Forecast ML Job Documentation + *

+ * @param request ForecastJobRequest with forecasting 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 forecastJobAsync(ForecastJobRequest request, RequestOptions options, ActionListener listener) { + restHighLevelClient.performRequestAsyncAndParseEntity(request, + MLRequestConverters::forecastJob, + options, + ForecastJobResponse::fromXContent, + listener, + Collections.emptySet()); + } + /** * Updates a Machine Learning {@link org.elasticsearch.client.ml.job.config.Job} asynchronously * diff --git a/client/rest-high-level/src/main/java/org/elasticsearch/client/ml/ForecastJobRequest.java b/client/rest-high-level/src/main/java/org/elasticsearch/client/ml/ForecastJobRequest.java new file mode 100644 index 00000000000..67d290c37f0 --- /dev/null +++ b/client/rest-high-level/src/main/java/org/elasticsearch/client/ml/ForecastJobRequest.java @@ -0,0 +1,140 @@ +/* + * 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.unit.TimeValue; +import org.elasticsearch.common.xcontent.ConstructingObjectParser; +import org.elasticsearch.common.xcontent.ToXContent; +import org.elasticsearch.common.xcontent.ToXContentObject; +import org.elasticsearch.common.xcontent.XContentBuilder; + +import java.io.IOException; +import java.util.Objects; + +/** + * Pojo for forecasting an existing and open Machine Learning Job + */ +public class ForecastJobRequest extends ActionRequest implements ToXContentObject { + + public static final ParseField DURATION = new ParseField("duration"); + public static final ParseField EXPIRES_IN = new ParseField("expires_in"); + + public static final ConstructingObjectParser PARSER = + new ConstructingObjectParser<>("forecast_job_request", (a) -> new ForecastJobRequest((String)a[0])); + + static { + PARSER.declareString(ConstructingObjectParser.constructorArg(), Job.ID); + PARSER.declareString( + (request, val) -> request.setDuration(TimeValue.parseTimeValue(val, DURATION.getPreferredName())), DURATION); + PARSER.declareString( + (request, val) -> request.setExpiresIn(TimeValue.parseTimeValue(val, EXPIRES_IN.getPreferredName())), EXPIRES_IN); + } + + private final String jobId; + private TimeValue duration; + private TimeValue expiresIn; + + /** + * A new forecast request + * + * @param jobId the non-null, existing, and opened jobId to forecast + */ + public ForecastJobRequest(String jobId) { + this.jobId = jobId; + } + + public String getJobId() { + return jobId; + } + + public TimeValue getDuration() { + return duration; + } + + /** + * Set the forecast duration + * + * A period of time that indicates how far into the future to forecast. + * The default value is 1 day. The forecast starts at the last record that was processed. + * + * @param duration TimeValue for the duration of the forecast + */ + public void setDuration(TimeValue duration) { + this.duration = duration; + } + + public TimeValue getExpiresIn() { + return expiresIn; + } + + /** + * Set the forecast expiration + * + * The period of time that forecast results are retained. + * After a forecast expires, the results are deleted. The default value is 14 days. + * If set to a value of 0, the forecast is never automatically deleted. + * + * @param expiresIn TimeValue for the forecast expiration + */ + public void setExpiresIn(TimeValue expiresIn) { + this.expiresIn = expiresIn; + } + + @Override + public int hashCode() { + return Objects.hash(jobId, duration, expiresIn); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null || getClass() != obj.getClass()) { + return false; + } + ForecastJobRequest other = (ForecastJobRequest) obj; + return Objects.equals(jobId, other.jobId) + && Objects.equals(duration, other.duration) + && Objects.equals(expiresIn, other.expiresIn); + } + + @Override + public XContentBuilder toXContent(XContentBuilder builder, ToXContent.Params params) throws IOException { + builder.startObject(); + builder.field(Job.ID.getPreferredName(), jobId); + if (duration != null) { + builder.field(DURATION.getPreferredName(), duration.getStringRep()); + } + if (expiresIn != null) { + builder.field(EXPIRES_IN.getPreferredName(), expiresIn.getStringRep()); + } + builder.endObject(); + return builder; + } + + @Override + public ActionRequestValidationException validate() { + return null; + } +} diff --git a/client/rest-high-level/src/main/java/org/elasticsearch/client/ml/ForecastJobResponse.java b/client/rest-high-level/src/main/java/org/elasticsearch/client/ml/ForecastJobResponse.java new file mode 100644 index 00000000000..b45275c5e59 --- /dev/null +++ b/client/rest-high-level/src/main/java/org/elasticsearch/client/ml/ForecastJobResponse.java @@ -0,0 +1,102 @@ +/* + * 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.ActionResponse; +import org.elasticsearch.common.ParseField; +import org.elasticsearch.common.xcontent.ConstructingObjectParser; +import org.elasticsearch.common.xcontent.ToXContentObject; +import org.elasticsearch.common.xcontent.XContentBuilder; +import org.elasticsearch.common.xcontent.XContentParser; + +import java.io.IOException; +import java.util.Objects; + +/** + * Forecast response object + */ +public class ForecastJobResponse extends ActionResponse implements ToXContentObject { + + public static final ParseField ACKNOWLEDGED = new ParseField("acknowledged"); + public static final ParseField FORECAST_ID = new ParseField("forecast_id"); + + public static final ConstructingObjectParser PARSER = + new ConstructingObjectParser<>("forecast_job_response", + true, + (a) -> new ForecastJobResponse((Boolean)a[0], (String)a[1])); + + static { + PARSER.declareBoolean(ConstructingObjectParser.constructorArg(), ACKNOWLEDGED); + PARSER.declareString(ConstructingObjectParser.constructorArg(), FORECAST_ID); + } + + public static ForecastJobResponse fromXContent(XContentParser parser) throws IOException { + return PARSER.parse(parser, null); + } + + private final boolean acknowledged; + private final String forecastId; + + public ForecastJobResponse(boolean acknowledged, String forecastId) { + this.acknowledged = acknowledged; + this.forecastId = forecastId; + } + + /** + * Forecast creating acknowledgement + * @return {@code true} indicates success, {@code false} otherwise + */ + public boolean isAcknowledged() { + return acknowledged; + } + + /** + * The created forecast ID + */ + public String getForecastId() { + return forecastId; + } + + @Override + public int hashCode() { + return Objects.hash(acknowledged, forecastId); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null || getClass() != obj.getClass()) { + return false; + } + ForecastJobResponse other = (ForecastJobResponse) obj; + return Objects.equals(acknowledged, other.acknowledged) + && Objects.equals(forecastId, other.forecastId); + } + + @Override + public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException { + builder.startObject(); + builder.field(ACKNOWLEDGED.getPreferredName(), acknowledged); + builder.field(FORECAST_ID.getPreferredName(), forecastId); + builder.endObject(); + return builder; + } +} diff --git a/client/rest-high-level/src/test/java/org/elasticsearch/client/MLRequestConvertersTests.java b/client/rest-high-level/src/test/java/org/elasticsearch/client/MLRequestConvertersTests.java index e0f20e2f23c..26e6251af48 100644 --- a/client/rest-high-level/src/test/java/org/elasticsearch/client/MLRequestConvertersTests.java +++ b/client/rest-high-level/src/test/java/org/elasticsearch/client/MLRequestConvertersTests.java @@ -26,6 +26,7 @@ import org.apache.http.client.methods.HttpPut; import org.elasticsearch.client.ml.CloseJobRequest; import org.elasticsearch.client.ml.DeleteJobRequest; import org.elasticsearch.client.ml.FlushJobRequest; +import org.elasticsearch.client.ml.ForecastJobRequest; import org.elasticsearch.client.ml.GetBucketsRequest; import org.elasticsearch.client.ml.GetInfluencersRequest; import org.elasticsearch.client.ml.GetJobRequest; @@ -173,6 +174,21 @@ public class MLRequestConvertersTests extends ESTestCase { requestEntityToString(request)); } + public void testForecastJob() throws Exception { + String jobId = randomAlphaOfLength(10); + ForecastJobRequest forecastJobRequest = new ForecastJobRequest(jobId); + + forecastJobRequest.setDuration(TimeValue.timeValueHours(10)); + forecastJobRequest.setExpiresIn(TimeValue.timeValueHours(12)); + Request request = MLRequestConverters.forecastJob(forecastJobRequest); + assertEquals(HttpPost.METHOD_NAME, request.getMethod()); + assertEquals("/_xpack/ml/anomaly_detectors/" + jobId + "/_forecast", request.getEndpoint()); + try (XContentParser parser = createParser(JsonXContent.jsonXContent, request.getEntity().getContent())) { + ForecastJobRequest parsedRequest = ForecastJobRequest.PARSER.apply(parser, null); + assertThat(parsedRequest, equalTo(forecastJobRequest)); + } + } + public void testUpdateJob() throws Exception { String jobId = randomAlphaOfLength(10); JobUpdate updates = JobUpdateTests.createRandom(jobId); diff --git a/client/rest-high-level/src/test/java/org/elasticsearch/client/MachineLearningIT.java b/client/rest-high-level/src/test/java/org/elasticsearch/client/MachineLearningIT.java index 93019ba0d43..fb715683b27 100644 --- a/client/rest-high-level/src/test/java/org/elasticsearch/client/MachineLearningIT.java +++ b/client/rest-high-level/src/test/java/org/elasticsearch/client/MachineLearningIT.java @@ -20,6 +20,8 @@ package org.elasticsearch.client; import com.carrotsearch.randomizedtesting.generators.CodepointSetGenerator; import org.elasticsearch.ElasticsearchStatusException; +import org.elasticsearch.client.ml.ForecastJobRequest; +import org.elasticsearch.client.ml.ForecastJobResponse; import org.elasticsearch.client.ml.PostDataRequest; import org.elasticsearch.client.ml.PostDataResponse; import org.elasticsearch.client.ml.UpdateJobRequest; @@ -223,6 +225,31 @@ public class MachineLearningIT extends ESRestHighLevelClientTestCase { assertThat(exception.status().getStatus(), equalTo(404)); } + public void testForecastJob() throws Exception { + String jobId = "ml-forecast-job-test"; + Job job = buildJob(jobId); + MachineLearningClient machineLearningClient = highLevelClient().machineLearning(); + machineLearningClient.putJob(new PutJobRequest(job), RequestOptions.DEFAULT); + machineLearningClient.openJob(new OpenJobRequest(jobId), RequestOptions.DEFAULT); + + PostDataRequest.JsonBuilder builder = new PostDataRequest.JsonBuilder(); + for(int i = 0; i < 30; i++) { + Map 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); + + ForecastJobRequest request = new ForecastJobRequest(jobId); + ForecastJobResponse response = execute(request, machineLearningClient::forecastJob, machineLearningClient::forecastJobAsync); + + assertTrue(response.isAcknowledged()); + assertNotNull(response.getForecastId()); + } + public void testPostData() throws Exception { String jobId = randomValidJobId(); Job job = buildJob(jobId); diff --git a/client/rest-high-level/src/test/java/org/elasticsearch/client/documentation/MlClientDocumentationIT.java b/client/rest-high-level/src/test/java/org/elasticsearch/client/documentation/MlClientDocumentationIT.java index bc452ad8503..9abef54d0d2 100644 --- a/client/rest-high-level/src/test/java/org/elasticsearch/client/documentation/MlClientDocumentationIT.java +++ b/client/rest-high-level/src/test/java/org/elasticsearch/client/documentation/MlClientDocumentationIT.java @@ -35,6 +35,8 @@ import org.elasticsearch.client.ml.DeleteJobRequest; import org.elasticsearch.client.ml.DeleteJobResponse; import org.elasticsearch.client.ml.FlushJobRequest; import org.elasticsearch.client.ml.FlushJobResponse; +import org.elasticsearch.client.ml.ForecastJobRequest; +import org.elasticsearch.client.ml.ForecastJobResponse; import org.elasticsearch.client.ml.GetBucketsRequest; import org.elasticsearch.client.ml.GetBucketsResponse; import org.elasticsearch.client.ml.GetInfluencersRequest; @@ -694,6 +696,73 @@ public class MlClientDocumentationIT extends ESRestHighLevelClientTestCase { } } + public void testForecastJob() throws Exception { + RestHighLevelClient client = highLevelClient(); + + Job job = MachineLearningIT.buildJob("forecasting-my-first-machine-learning-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 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); + + { + //tag::x-pack-ml-forecast-job-request + ForecastJobRequest forecastJobRequest = new ForecastJobRequest("forecasting-my-first-machine-learning-job"); //<1> + //end::x-pack-ml-forecast-job-request + + //tag::x-pack-ml-forecast-job-request-options + forecastJobRequest.setExpiresIn(TimeValue.timeValueHours(48)); //<1> + forecastJobRequest.setDuration(TimeValue.timeValueHours(24)); //<2> + //end::x-pack-ml-forecast-job-request-options + + //tag::x-pack-ml-forecast-job-execute + ForecastJobResponse forecastJobResponse = client.machineLearning().forecastJob(forecastJobRequest, RequestOptions.DEFAULT); + //end::x-pack-ml-forecast-job-execute + + //tag::x-pack-ml-forecast-job-response + boolean isAcknowledged = forecastJobResponse.isAcknowledged(); //<1> + String forecastId = forecastJobResponse.getForecastId(); //<2> + //end::x-pack-ml-forecast-job-response + assertTrue(isAcknowledged); + assertNotNull(forecastId); + } + { + //tag::x-pack-ml-forecast-job-listener + ActionListener listener = new ActionListener() { + @Override + public void onResponse(ForecastJobResponse forecastJobResponse) { + //<1> + } + + @Override + public void onFailure(Exception e) { + // <2> + } + }; + //end::x-pack-ml-forecast-job-listener + ForecastJobRequest forecastJobRequest = new ForecastJobRequest("forecasting-my-first-machine-learning-job"); + + // 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-forecast-job-execute-async + client.machineLearning().forecastJobAsync(forecastJobRequest, RequestOptions.DEFAULT, listener); //<1> + // end::x-pack-ml-forecast-job-execute-async + + assertTrue(latch.await(30L, TimeUnit.SECONDS)); + } + } + public void testGetOverallBuckets() throws IOException, InterruptedException { RestHighLevelClient client = highLevelClient(); diff --git a/client/rest-high-level/src/test/java/org/elasticsearch/client/ml/ForecastJobRequestTests.java b/client/rest-high-level/src/test/java/org/elasticsearch/client/ml/ForecastJobRequestTests.java new file mode 100644 index 00000000000..c6a33dad609 --- /dev/null +++ b/client/rest-high-level/src/test/java/org/elasticsearch/client/ml/ForecastJobRequestTests.java @@ -0,0 +1,51 @@ +/* + * 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.common.unit.TimeValue; +import org.elasticsearch.common.xcontent.XContentParser; +import org.elasticsearch.test.AbstractXContentTestCase; + +import java.io.IOException; + +public class ForecastJobRequestTests extends AbstractXContentTestCase { + + @Override + protected ForecastJobRequest createTestInstance() { + ForecastJobRequest request = new ForecastJobRequest(randomAlphaOfLengthBetween(1, 20)); + + if (randomBoolean()) { + request.setExpiresIn(TimeValue.timeValueHours(randomInt(10))); + } + if (randomBoolean()) { + request.setDuration(TimeValue.timeValueHours(randomIntBetween(24, 72))); + } + return request; + } + + @Override + protected ForecastJobRequest doParseInstance(XContentParser parser) throws IOException { + return ForecastJobRequest.PARSER.apply(parser, null); + } + + @Override + protected boolean supportsUnknownFields() { + return false; + } +} diff --git a/client/rest-high-level/src/test/java/org/elasticsearch/client/ml/ForecastJobResponseTests.java b/client/rest-high-level/src/test/java/org/elasticsearch/client/ml/ForecastJobResponseTests.java new file mode 100644 index 00000000000..c7833a79cba --- /dev/null +++ b/client/rest-high-level/src/test/java/org/elasticsearch/client/ml/ForecastJobResponseTests.java @@ -0,0 +1,42 @@ +/* + * 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.common.xcontent.XContentParser; +import org.elasticsearch.test.AbstractXContentTestCase; + +import java.io.IOException; + +public class ForecastJobResponseTests extends AbstractXContentTestCase { + + @Override + protected ForecastJobResponse createTestInstance() { + return new ForecastJobResponse(randomBoolean(),randomAlphaOfLength(10)); + } + + @Override + protected ForecastJobResponse doParseInstance(XContentParser parser) throws IOException { + return ForecastJobResponse.PARSER.apply(parser, null); + } + + @Override + protected boolean supportsUnknownFields() { + return true; + } +} diff --git a/docs/java-rest/high-level/ml/forecast-job.asciidoc b/docs/java-rest/high-level/ml/forecast-job.asciidoc new file mode 100644 index 00000000000..88bd5fdb532 --- /dev/null +++ b/docs/java-rest/high-level/ml/forecast-job.asciidoc @@ -0,0 +1,76 @@ +[[java-rest-high-x-pack-ml-forecast-job]] +=== Forecast Job API + +The Forecast Job API provides the ability to forecast a {ml} job's behavior based +on historical data. +It accepts a `ForecastJobRequest` object and responds +with a `ForecastJobResponse` object. + +[[java-rest-high-x-pack-ml-forecast-job-request]] +==== Forecast Job Request + +A `ForecastJobRequest` 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-forecast-job-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-forecast-job-request-options] +-------------------------------------------------- +<1> Set when the forecast for the job should expire +<2> Set how far into the future should the forecast predict + +[[java-rest-high-x-pack-ml-forecast-job-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-forecast-job-execute] +-------------------------------------------------- + +[[java-rest-high-x-pack-ml-forecast-job-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-forecast-job-execute-async] +-------------------------------------------------- +<1> The `ForecastJobRequest` 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 `ForecastJobResponse` may +look like + +["source","java",subs="attributes,callouts,macros"] +-------------------------------------------------- +include-tagged::{doc-tests}/MlClientDocumentationIT.java[x-pack-ml-forecast-job-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-forecast-job-response]] +==== Forecast Job Response + +A `ForecastJobResponse` contains an acknowledgement and the forecast ID + +["source","java",subs="attributes,callouts,macros"] +-------------------------------------------------- +include-tagged::{doc-tests}/MlClientDocumentationIT.java[x-pack-ml-forecast-job-response] +-------------------------------------------------- +<1> `isAcknowledged()` indicates if the forecast was successful +<2> `getForecastId()` provides the ID of the forecast that was created \ No newline at end of file diff --git a/docs/java-rest/high-level/supported-apis.asciidoc b/docs/java-rest/high-level/supported-apis.asciidoc index e1335b0effc..eb03d8ee4c6 100644 --- a/docs/java-rest/high-level/supported-apis.asciidoc +++ b/docs/java-rest/high-level/supported-apis.asciidoc @@ -218,6 +218,7 @@ The Java High Level REST Client supports the following Machine Learning APIs: * <> * <> * <> +* <> * <> * <> * <> @@ -232,6 +233,7 @@ include::ml/close-job.asciidoc[] include::ml/update-job.asciidoc[] include::ml/flush-job.asciidoc[] include::ml/get-job-stats.asciidoc[] +include::ml/forecast-job.asciidoc[] include::ml/get-buckets.asciidoc[] include::ml/get-overall-buckets.asciidoc[] include::ml/get-records.asciidoc[]