Skip to content

Commit

Permalink
[HLRC][ML] Add ML revert model snapshot API (#35750)
Browse files Browse the repository at this point in the history
Relates to #29827
  • Loading branch information
edsavage committed Nov 21, 2018
1 parent f6a43b5 commit 4f857c4
Show file tree
Hide file tree
Showing 11 changed files with 572 additions and 3 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@
import org.elasticsearch.client.ml.PutDatafeedRequest;
import org.elasticsearch.client.ml.PutFilterRequest;
import org.elasticsearch.client.ml.PutJobRequest;
import org.elasticsearch.client.ml.RevertModelSnapshotRequest;
import org.elasticsearch.client.ml.StartDatafeedRequest;
import org.elasticsearch.client.ml.StopDatafeedRequest;
import org.elasticsearch.client.ml.UpdateDatafeedRequest;
Expand Down Expand Up @@ -410,6 +411,21 @@ static Request updateModelSnapshot(UpdateModelSnapshotRequest updateModelSnapsho
return request;
}

static Request revertModelSnapshot(RevertModelSnapshotRequest revertModelSnapshotsRequest) throws IOException {
String endpoint = new EndpointBuilder()
.addPathPartAsIs("_xpack")
.addPathPartAsIs("ml")
.addPathPartAsIs("anomaly_detectors")
.addPathPart(revertModelSnapshotsRequest.getJobId())
.addPathPartAsIs("model_snapshots")
.addPathPart(revertModelSnapshotsRequest.getSnapshotId())
.addPathPart("_revert")
.build();
Request request = new Request(HttpPost.METHOD_NAME, endpoint);
request.setEntity(createEntity(revertModelSnapshotsRequest, REQUEST_BODY_CONTENT_TYPE));
return request;
}

static Request getOverallBuckets(GetOverallBucketsRequest getOverallBucketsRequest) throws IOException {
String endpoint = new EndpointBuilder()
.addPathPartAsIs("_xpack")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,8 @@
import org.elasticsearch.client.ml.PutFilterResponse;
import org.elasticsearch.client.ml.PutJobRequest;
import org.elasticsearch.client.ml.PutJobResponse;
import org.elasticsearch.client.ml.RevertModelSnapshotRequest;
import org.elasticsearch.client.ml.RevertModelSnapshotResponse;
import org.elasticsearch.client.ml.StartDatafeedRequest;
import org.elasticsearch.client.ml.StartDatafeedResponse;
import org.elasticsearch.client.ml.StopDatafeedRequest;
Expand Down Expand Up @@ -515,6 +517,47 @@ public void deleteModelSnapshotAsync(DeleteModelSnapshotRequest request, Request
Collections.emptySet());
}

/**
* Reverts to a particular Machine Learning Model Snapshot
* <p>
* For additional info
* see <a href="http://www.elastic.co/guide/en/elasticsearch/reference/current/ml-revert-snapshot.html">
* ML Revert Model Snapshot documentation</a>
*
* @param request The request to revert to a previous model snapshot
* @param options Additional request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized
* @return action acknowledgement
* @throws IOException when there is a serialization issue sending the request or receiving the response
*/
public RevertModelSnapshotResponse revertModelSnapshot(RevertModelSnapshotRequest request, RequestOptions options) throws IOException {
return restHighLevelClient.performRequestAndParseEntity(request,
MLRequestConverters::revertModelSnapshot,
options,
RevertModelSnapshotResponse::fromXContent,
Collections.emptySet());
}

/**
* Reverts to a particular Machine Learning Model Snapshot asynchronously and notifies the listener on completion
* <p>
* For additional info
* see <a href="http://www.elastic.co/guide/en/elasticsearch/reference/current/ml-revert-snapshot.html">
* ML Revert Model Snapshot documentation</a>
*
* @param request The request to revert to a previous model snapshot
* @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 revertModelSnapshotAsync(RevertModelSnapshotRequest request, RequestOptions options,
ActionListener<RevertModelSnapshotResponse> listener) {
restHighLevelClient.performRequestAsyncAndParseEntity(request,
MLRequestConverters::revertModelSnapshot,
options,
RevertModelSnapshotResponse::fromXContent,
listener,
Collections.emptySet());
}

/**
* Creates a new Machine Learning Datafeed
* <p>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
/*
* 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.client.ml.job.process.ModelSnapshot;
import org.elasticsearch.common.ParseField;
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.Objects;

/**
* A request to revert to a specific model snapshot for a given job
*/
public class RevertModelSnapshotRequest extends ActionRequest implements ToXContentObject {


public static final ParseField DELETE_INTERVENING = new ParseField("delete_intervening_results");

public static final ConstructingObjectParser<RevertModelSnapshotRequest, Void> PARSER = new ConstructingObjectParser<>(
"revert_model_snapshots_request", a -> new RevertModelSnapshotRequest((String) a[0], (String) a[1]));


static {
PARSER.declareString(ConstructingObjectParser.constructorArg(), Job.ID);
PARSER.declareString(ConstructingObjectParser.constructorArg(), ModelSnapshot.SNAPSHOT_ID);
PARSER.declareBoolean(RevertModelSnapshotRequest::setDeleteInterveningResults, DELETE_INTERVENING);
}

private final String jobId;
private final String snapshotId;
private Boolean deleteInterveningResults;

/**
* Constructs a request to revert to a given model snapshot
* @param jobId id of the job for which to revert the model snapshot
* @param snapshotId id of the snapshot to which to revert
*/
public RevertModelSnapshotRequest(String jobId, String snapshotId) {
this.jobId = Objects.requireNonNull(jobId, "[" + Job.ID + "] must not be null");
this.snapshotId = Objects.requireNonNull(snapshotId, "[" + ModelSnapshot.SNAPSHOT_ID + "] must not be null");
}

public String getJobId() {
return jobId;
}

public String getSnapshotId() {
return snapshotId;
}

public Boolean getDeleteInterveningResults() {
return deleteInterveningResults;
}

/**
* Sets the request flag that indicates whether or not intervening results should be deleted.
* @param deleteInterveningResults Flag that indicates whether or not intervening results should be deleted.
*/
public void setDeleteInterveningResults(Boolean deleteInterveningResults) {
this.deleteInterveningResults = deleteInterveningResults;
}

@Override
public ActionRequestValidationException validate() {
return null;
}

@Override
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
builder.startObject();
builder.field(Job.ID.getPreferredName(), jobId);
builder.field(ModelSnapshot.SNAPSHOT_ID.getPreferredName(), snapshotId);
if (deleteInterveningResults != null) {
builder.field(DELETE_INTERVENING.getPreferredName(), deleteInterveningResults);
}
builder.endObject();
return builder;
}

@Override
public boolean equals(Object obj) {
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
RevertModelSnapshotRequest request = (RevertModelSnapshotRequest) obj;
return Objects.equals(jobId, request.jobId)
&& Objects.equals(snapshotId, request.snapshotId)
&& Objects.equals(deleteInterveningResults, request.deleteInterveningResults);
}

@Override
public int hashCode() {
return Objects.hash(jobId, snapshotId, deleteInterveningResults);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
/*
* 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.client.ml.job.process.ModelSnapshot;
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;

/**
* A response containing the reverted model snapshot
*/
public class RevertModelSnapshotResponse extends ActionResponse implements ToXContentObject {

private static final ParseField MODEL = new ParseField("model");

public static final ConstructingObjectParser<RevertModelSnapshotResponse, Void> PARSER =
new ConstructingObjectParser<>("revert_model_snapshot_response", true,
a -> new RevertModelSnapshotResponse((ModelSnapshot.Builder) a[0]));

static {
PARSER.declareObject(ConstructingObjectParser.constructorArg(), ModelSnapshot.PARSER, MODEL);
}

public static RevertModelSnapshotResponse fromXContent(XContentParser parser) throws IOException {
return PARSER.parse(parser, null);
}

public RevertModelSnapshotResponse(ModelSnapshot.Builder modelSnapshot) {
this.model = modelSnapshot.build();
}

private final ModelSnapshot model;

/**
* Get full information about the reverted model snapshot
* @return the reverted model snapshot.
*/
public ModelSnapshot getModel() {
return model;
}

@Override
public int hashCode() {
return Objects.hash(model);
}

@Override
public boolean equals(Object obj) {
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
RevertModelSnapshotResponse other = (RevertModelSnapshotResponse) obj;
return Objects.equals(model, other.model);
}

@Override
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
builder.startObject();
if (model != null) {
builder.field(MODEL.getPreferredName(), model);
}
builder.endObject();
return builder;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@
import org.elasticsearch.client.ml.PutDatafeedRequest;
import org.elasticsearch.client.ml.PutFilterRequest;
import org.elasticsearch.client.ml.PutJobRequest;
import org.elasticsearch.client.ml.RevertModelSnapshotRequest;
import org.elasticsearch.client.ml.StartDatafeedRequest;
import org.elasticsearch.client.ml.StartDatafeedRequestTests;
import org.elasticsearch.client.ml.StopDatafeedRequest;
Expand Down Expand Up @@ -446,6 +447,24 @@ public void testUpdateModelSnapshot() throws IOException {
}
}

public void testRevertModelSnapshot() throws IOException {
String jobId = randomAlphaOfLength(10);
String snapshotId = randomAlphaOfLength(10);
RevertModelSnapshotRequest revertModelSnapshotRequest = new RevertModelSnapshotRequest(jobId, snapshotId);
if (randomBoolean()) {
revertModelSnapshotRequest.setDeleteInterveningResults(randomBoolean());
}

Request request = MLRequestConverters.revertModelSnapshot(revertModelSnapshotRequest);
assertEquals(HttpPost.METHOD_NAME, request.getMethod());
assertEquals("/_xpack/ml/anomaly_detectors/" + jobId + "/model_snapshots/" + snapshotId + "/_revert",
request.getEndpoint());
try (XContentParser parser = createParser(JsonXContent.jsonXContent, request.getEntity().getContent())) {
RevertModelSnapshotRequest parsedRequest = RevertModelSnapshotRequest.PARSER.apply(parser, null);
assertThat(parsedRequest, equalTo(revertModelSnapshotRequest));
}
}

public void testGetOverallBuckets() throws IOException {
String jobId = randomAlphaOfLength(10);
GetOverallBucketsRequest getOverallBucketsRequest = new GetOverallBucketsRequest(jobId);
Expand Down

0 comments on commit 4f857c4

Please sign in to comment.