Skip to content

Commit

Permalink
HLRC: Adding ML Update Filter API (#35522)
Browse files Browse the repository at this point in the history
* HLRC: Adding ml get filters api

* HLRC: Adding ML Update Filter API
  • Loading branch information
benwtrent authored Nov 14, 2018
1 parent c8c8ce2 commit 803ecce
Show file tree
Hide file tree
Showing 9 changed files with 440 additions and 0 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@
import org.elasticsearch.client.ml.StartDatafeedRequest;
import org.elasticsearch.client.ml.StopDatafeedRequest;
import org.elasticsearch.client.ml.UpdateDatafeedRequest;
import org.elasticsearch.client.ml.UpdateFilterRequest;
import org.elasticsearch.client.ml.UpdateJobRequest;
import org.elasticsearch.client.ml.job.util.PageParams;
import org.elasticsearch.common.Strings;
Expand Down Expand Up @@ -510,4 +511,17 @@ static Request getFilter(GetFiltersRequest getFiltersRequest) {
}
return request;
}

static Request updateFilter(UpdateFilterRequest updateFilterRequest) throws IOException {
String endpoint = new EndpointBuilder()
.addPathPartAsIs("_xpack")
.addPathPartAsIs("ml")
.addPathPartAsIs("filters")
.addPathPart(updateFilterRequest.getFilterId())
.addPathPartAsIs("_update")
.build();
Request request = new Request(HttpPost.METHOD_NAME, endpoint);
request.setEntity(createEntity(updateFilterRequest, REQUEST_BODY_CONTENT_TYPE));
return request;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@
import org.elasticsearch.client.ml.StopDatafeedRequest;
import org.elasticsearch.client.ml.StopDatafeedResponse;
import org.elasticsearch.client.ml.UpdateDatafeedRequest;
import org.elasticsearch.client.ml.UpdateFilterRequest;
import org.elasticsearch.client.ml.UpdateJobRequest;
import org.elasticsearch.client.ml.job.stats.JobStats;

Expand Down Expand Up @@ -1288,4 +1289,44 @@ public void getFilterAsync(GetFiltersRequest request, RequestOptions options, Ac
listener,
Collections.emptySet());
}

/**
* Updates a Machine Learning Filter
* <p>
* For additional info
* see <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/ml-update-filter.html">
* ML Update Filter documentation</a>
*
* @param request The request
* @param options Additional request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized
* @return PutFilterResponse with the updated {@link org.elasticsearch.client.ml.job.config.MlFilter} object
* @throws IOException when there is a serialization issue sending the request or receiving the response
*/
public PutFilterResponse updateFilter(UpdateFilterRequest request, RequestOptions options) throws IOException {
return restHighLevelClient.performRequestAndParseEntity(request,
MLRequestConverters::updateFilter,
options,
PutFilterResponse::fromXContent,
Collections.emptySet());
}

/**
* Updates a Machine Learning Filter asynchronously and notifies listener on completion
* <p>
* For additional info
* see <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/ml-update-filter.html">
* ML Update Filter documentation</a>
*
* @param request The request
* @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 updateFilterAsync(UpdateFilterRequest request, RequestOptions options, ActionListener<PutFilterResponse> listener) {
restHighLevelClient.performRequestAsyncAndParseEntity(request,
MLRequestConverters::updateFilter,
options,
PutFilterResponse::fromXContent,
listener,
Collections.emptySet());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,156 @@
/*
* 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.MlFilter;
import org.elasticsearch.common.ParseField;
import org.elasticsearch.common.Strings;
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.Collection;
import java.util.Objects;
import java.util.SortedSet;
import java.util.TreeSet;

/**
* Updates an existing {@link MlFilter} configuration
*/
public class UpdateFilterRequest extends ActionRequest implements ToXContentObject {

public static final ParseField ADD_ITEMS = new ParseField("add_items");
public static final ParseField REMOVE_ITEMS = new ParseField("remove_items");

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

static {
PARSER.declareString(ConstructingObjectParser.constructorArg(), MlFilter.ID);
PARSER.declareStringOrNull(UpdateFilterRequest::setDescription, MlFilter.DESCRIPTION);
PARSER.declareStringArray(UpdateFilterRequest::setAddItems, ADD_ITEMS);
PARSER.declareStringArray(UpdateFilterRequest::setRemoveItems, REMOVE_ITEMS);
}

private String filterId;
private String description;
private SortedSet<String> addItems;
private SortedSet<String> removeItems;

/**
* Construct a new request referencing a non-null, existing filter_id
* @param filterId Id referencing the filter to update
*/
public UpdateFilterRequest(String filterId) {
this.filterId = Objects.requireNonNull(filterId, "[" + MlFilter.ID.getPreferredName() + "] must not be null");
}

public String getFilterId() {
return filterId;
}

public String getDescription() {
return description;
}

/**
* The new description of the filter
* @param description the updated filter description
*/
public void setDescription(String description) {
this.description = description;
}

public SortedSet<String> getAddItems() {
return addItems;
}

/**
* The collection of items to add to the filter
* @param addItems non-null items to add to the filter, defaults to empty array
*/
public void setAddItems(Collection<String> addItems) {
this.addItems = new TreeSet<>(Objects.requireNonNull(addItems,
"[" + ADD_ITEMS.getPreferredName()+"] must not be null"));
}

public SortedSet<String> getRemoveItems() {
return removeItems;
}

/**
* The collection of items to remove from the filter
* @param removeItems non-null items to remove from the filter, defaults to empty array
*/
public void setRemoveItems(Collection<String> removeItems) {
this.removeItems = new TreeSet<>(Objects.requireNonNull(removeItems,
"[" + REMOVE_ITEMS.getPreferredName()+"] must not be null"));
}

@Override
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
builder.startObject();
builder.field(MlFilter.ID.getPreferredName(), filterId);
if (description != null) {
builder.field(MlFilter.DESCRIPTION.getPreferredName(), description);
}
if (addItems != null) {
builder.field(ADD_ITEMS.getPreferredName(), addItems);
}
if (removeItems != null) {
builder.field(REMOVE_ITEMS.getPreferredName(), removeItems);
}
builder.endObject();
return builder;
}

@Override
public int hashCode() {
return Objects.hash(filterId, description, addItems, removeItems);
}

@Override
public boolean equals(Object obj) {
if (obj == this) {
return true;
}
if (obj == null || getClass() != obj.getClass()) {
return false;
}

UpdateFilterRequest other = (UpdateFilterRequest) obj;
return Objects.equals(filterId, other.filterId)
&& Objects.equals(description, other.description)
&& Objects.equals(addItems, other.addItems)
&& Objects.equals(removeItems, other.removeItems);
}

@Override
public final String toString() {
return Strings.toString(this);
}

@Override
public ActionRequestValidationException validate() {
return null;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@
import org.elasticsearch.client.ml.StartDatafeedRequest;
import org.elasticsearch.client.ml.StartDatafeedRequestTests;
import org.elasticsearch.client.ml.StopDatafeedRequest;
import org.elasticsearch.client.ml.UpdateFilterRequest;
import org.elasticsearch.client.ml.UpdateJobRequest;
import org.elasticsearch.client.ml.calendars.Calendar;
import org.elasticsearch.client.ml.calendars.CalendarTests;
Expand All @@ -74,6 +75,7 @@

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
Expand Down Expand Up @@ -566,6 +568,23 @@ public void testGetFilter() throws IOException {
assertThat(request.getParameters().get(PageParams.SIZE.getPreferredName()), equalTo("10"));
}

public void testUpdateFilter() throws IOException {
String filterId = randomAlphaOfLength(10);
UpdateFilterRequest updateFilterRequest = new UpdateFilterRequest(filterId);
updateFilterRequest.setDescription(randomAlphaOfLength(10));
updateFilterRequest.setRemoveItems(Arrays.asList("item1", "item2"));
updateFilterRequest.setAddItems(Arrays.asList("item3", "item5"));

Request request = MLRequestConverters.updateFilter(updateFilterRequest);

assertEquals(HttpPost.METHOD_NAME, request.getMethod());
assertThat(request.getEndpoint(), equalTo("/_xpack/ml/filters/"+filterId+"/_update"));
try (XContentParser parser = createParser(JsonXContent.jsonXContent, request.getEntity().getContent())) {
UpdateFilterRequest parsedFilterRequest = UpdateFilterRequest.PARSER.apply(parser, null);
assertThat(parsedFilterRequest, equalTo(updateFilterRequest));
}
}

private static Job createValidJob(String jobId) {
AnalysisConfig.Builder analysisConfig = AnalysisConfig.builder(Collections.singletonList(
Detector.builder().setFunction("count").build()));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@
import org.elasticsearch.client.ml.StopDatafeedRequest;
import org.elasticsearch.client.ml.StopDatafeedResponse;
import org.elasticsearch.client.ml.UpdateDatafeedRequest;
import org.elasticsearch.client.ml.UpdateFilterRequest;
import org.elasticsearch.client.ml.UpdateJobRequest;
import org.elasticsearch.client.ml.calendars.Calendar;
import org.elasticsearch.client.ml.calendars.CalendarTests;
Expand Down Expand Up @@ -101,6 +102,7 @@

import static org.hamcrest.CoreMatchers.equalTo;
import static org.hamcrest.CoreMatchers.hasItems;
import static org.hamcrest.Matchers.contains;
import static org.hamcrest.Matchers.containsInAnyOrder;
import static org.hamcrest.Matchers.hasSize;
import static org.hamcrest.Matchers.is;
Expand Down Expand Up @@ -927,6 +929,28 @@ public void testGetFilters() throws Exception {
}
}

public void testUpdateFilter() throws Exception {
String filterId = "update-filter-test";
MlFilter mlFilter = MlFilter.builder(filterId)
.setDescription("old description")
.setItems(Arrays.asList("olditem1", "olditem2"))
.build();
MachineLearningClient machineLearningClient = highLevelClient().machineLearning();
machineLearningClient.putFilter(new PutFilterRequest(mlFilter), RequestOptions.DEFAULT);

UpdateFilterRequest updateFilterRequest = new UpdateFilterRequest(filterId);

updateFilterRequest.setAddItems(Arrays.asList("newItem1", "newItem2"));
updateFilterRequest.setRemoveItems(Collections.singletonList("olditem1"));
updateFilterRequest.setDescription("new description");
MlFilter filter = execute(updateFilterRequest,
machineLearningClient::updateFilter,
machineLearningClient::updateFilterAsync).getResponse();

assertThat(filter.getDescription(), equalTo(updateFilterRequest.getDescription()));
assertThat(filter.getItems(), contains("newItem1", "newItem2", "olditem2"));
}

public static String randomValidJobId() {
CodepointSetGenerator generator = new CodepointSetGenerator("abcdefghijklmnopqrstuvwxyz0123456789".toCharArray());
return generator.ofCodePointsLength(random(), 10, 10);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@
import org.elasticsearch.client.ml.StopDatafeedRequest;
import org.elasticsearch.client.ml.StopDatafeedResponse;
import org.elasticsearch.client.ml.UpdateDatafeedRequest;
import org.elasticsearch.client.ml.UpdateFilterRequest;
import org.elasticsearch.client.ml.UpdateJobRequest;
import org.elasticsearch.client.ml.calendars.Calendar;
import org.elasticsearch.client.ml.datafeed.ChunkingConfig;
Expand Down Expand Up @@ -2229,4 +2230,66 @@ public void onFailure(Exception e) {
assertTrue(latch.await(30L, TimeUnit.SECONDS));
}
}

public void testUpdateFilter() throws IOException, InterruptedException {
RestHighLevelClient client = highLevelClient();
String filterId = "update-filter-doc-test";
MlFilter.Builder filterBuilder = MlFilter.builder(filterId).setDescription("test").setItems("*.google.com", "wikipedia.org");

client.machineLearning().putFilter(new PutFilterRequest(filterBuilder.build()), RequestOptions.DEFAULT);

{
// tag::update-filter-request
UpdateFilterRequest request = new UpdateFilterRequest(filterId); // <1>
// end::update-filter-request

// tag::update-filter-description
request.setDescription("my new description"); // <1>
// end::update-filter-description

// tag::update-filter-add-items
request.setAddItems(Arrays.asList("*.bing.com", "*.elastic.co")); // <1>
// end::update-filter-add-items

// tag::update-filter-remove-items
request.setRemoveItems(Arrays.asList("*.google.com")); // <1>
// end::update-filter-remove-items

// tag::update-filter-execute
PutFilterResponse response = client.machineLearning().updateFilter(request, RequestOptions.DEFAULT);
// end::update-filter-execute

// tag::update-filter-response
MlFilter updatedFilter = response.getResponse(); // <1>
// end::update-filter-response
assertEquals(request.getDescription(), updatedFilter.getDescription());
}
{
UpdateFilterRequest request = new UpdateFilterRequest(filterId);

// tag::update-filter-execute-listener
ActionListener<PutFilterResponse> listener = new ActionListener<PutFilterResponse>() {
@Override
public void onResponse(PutFilterResponse putFilterResponse) {
// <1>
}

@Override
public void onFailure(Exception e) {
// <2>
}
};
// end::update-filter-execute-listener

// Replace the empty listener by a blocking listener in test
final CountDownLatch latch = new CountDownLatch(1);
listener = new LatchedActionListener<>(listener, latch);

// tag::update-filter-execute-async
client.machineLearning().updateFilterAsync(request, RequestOptions.DEFAULT, listener); // <1>
// end::update-filter-execute-async

assertTrue(latch.await(30L, TimeUnit.SECONDS));
}
}
}
Loading

0 comments on commit 803ecce

Please sign in to comment.