Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
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;
Expand Down Expand Up @@ -153,6 +154,19 @@ static Request flushJob(FlushJobRequest flushJobRequest) throws IOException {
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")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -360,6 +362,28 @@ public void flushJobAsync(FlushJobRequest request, RequestOptions options, Actio
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.
*
* <p>
* For additional info
* see <a href="https://www.elastic.co/guide/en/elasticsearch/reference/master/ml-forecast.html">Forecast ML Job Documentation</a>
* </p>
* @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}
*
Expand All @@ -376,6 +400,28 @@ public PutJobResponse updateJob(UpdateJobRequest request, RequestOptions options
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.
*
* <p>
* For additional info
* see <a href="https://www.elastic.co/guide/en/elasticsearch/reference/master/ml-forecast.html">Forecast ML Job Documentation</a>
* </p>
* @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<ForecastJobResponse> 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
*
Expand Down
Original file line number Diff line number Diff line change
@@ -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<ForecastJobRequest, Void> 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;
}
}
Original file line number Diff line number Diff line change
@@ -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<ForecastJobResponse, Void> 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;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
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;
Expand Down Expand Up @@ -173,6 +174,21 @@ public void testFlushJob() throws Exception {
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);
Expand Down
Loading