Skip to content

Commit

Permalink
HLRC: Add ML API PUT filter (#35175)
Browse files Browse the repository at this point in the history
  • Loading branch information
benwtrent committed Nov 5, 2018
1 parent 8c18dc8 commit 7fa0bbd
Show file tree
Hide file tree
Showing 12 changed files with 469 additions and 0 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@
import org.elasticsearch.client.ml.PreviewDatafeedRequest;
import org.elasticsearch.client.ml.PutCalendarRequest;
import org.elasticsearch.client.ml.PutDatafeedRequest;
import org.elasticsearch.client.ml.PutFilterRequest;
import org.elasticsearch.client.ml.PutJobRequest;
import org.elasticsearch.client.ml.StartDatafeedRequest;
import org.elasticsearch.client.ml.StopDatafeedRequest;
Expand Down Expand Up @@ -463,4 +464,16 @@ static Request deleteCalendar(DeleteCalendarRequest deleteCalendarRequest) {
Request request = new Request(HttpDelete.METHOD_NAME, endpoint);
return request;
}

static Request putFilter(PutFilterRequest putFilterRequest) throws IOException {
String endpoint = new EndpointBuilder()
.addPathPartAsIs("_xpack")
.addPathPartAsIs("ml")
.addPathPartAsIs("filters")
.addPathPart(putFilterRequest.getMlFilter().getId())
.build();
Request request = new Request(HttpPut.METHOD_NAME, endpoint);
request.setEntity(createEntity(putFilterRequest, REQUEST_BODY_CONTENT_TYPE));
return request;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,8 @@
import org.elasticsearch.client.ml.PutCalendarResponse;
import org.elasticsearch.client.ml.PutDatafeedRequest;
import org.elasticsearch.client.ml.PutDatafeedResponse;
import org.elasticsearch.client.ml.PutFilterRequest;
import org.elasticsearch.client.ml.PutFilterResponse;
import org.elasticsearch.client.ml.PutJobRequest;
import org.elasticsearch.client.ml.PutJobResponse;
import org.elasticsearch.client.ml.StartDatafeedRequest;
Expand Down Expand Up @@ -1166,4 +1168,43 @@ public void deleteCalendarAsync(DeleteCalendarRequest request, RequestOptions op
listener,
Collections.emptySet());
}

/**
* Creates a new Machine Learning Filter
* <p>
* For additional info
* see <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/ml-put-filter.html">ML PUT Filter documentation</a>
*
* @param request The PutFilterRequest containing the {@link org.elasticsearch.client.ml.job.config.MlFilter} settings
* @param options Additional request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized
* @return PutFilterResponse with enclosed {@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 putFilter(PutFilterRequest request, RequestOptions options) throws IOException {
return restHighLevelClient.performRequestAndParseEntity(request,
MLRequestConverters::putFilter,
options,
PutFilterResponse::fromXContent,
Collections.emptySet());
}

/**
* Creates a new 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-put-filter.html">ML PUT Filter documentation</a>
*
* @param request The request containing the {@link org.elasticsearch.client.ml.job.config.MlFilter} settings
* @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 putFilterAsync(PutFilterRequest request, RequestOptions options, ActionListener<PutFilterResponse> listener) {
restHighLevelClient.performRequestAsyncAndParseEntity(request,
MLRequestConverters::putFilter,
options,
PutFilterResponse::fromXContent,
listener,
Collections.emptySet());
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
/*
* 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.Strings;
import org.elasticsearch.common.xcontent.ToXContentObject;
import org.elasticsearch.common.xcontent.XContentBuilder;

import java.io.IOException;
import java.util.Objects;

/**
* Request to create a new Machine Learning MlFilter given a {@link MlFilter} configuration
*/
public class PutFilterRequest extends ActionRequest implements ToXContentObject {

private final MlFilter filter;

/**
* Construct a new PutMlFilterRequest
*
* @param filter a {@link MlFilter} configuration to create
*/
public PutFilterRequest(MlFilter filter) {
this.filter = filter;
}

public MlFilter getMlFilter() {
return filter;
}

@Override
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
return filter.toXContent(builder, params);
}

@Override
public boolean equals(Object object) {
if (this == object) {
return true;
}

if (object == null || getClass() != object.getClass()) {
return false;
}

PutFilterRequest request = (PutFilterRequest) object;
return Objects.equals(filter, request.filter);
}

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

@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
@@ -0,0 +1,70 @@
/*
* 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.MlFilter;
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;

/**
* Response containing the newly created {@link MlFilter}
*/
public class PutFilterResponse implements ToXContentObject {

private MlFilter filter;

public static PutFilterResponse fromXContent(XContentParser parser) throws IOException {
return new PutFilterResponse(MlFilter.PARSER.parse(parser, null).build());
}

PutFilterResponse(MlFilter filter) {
this.filter = filter;
}

public MlFilter getResponse() {
return filter;
}

@Override
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
filter.toXContent(builder, params);
return builder;
}

@Override
public boolean equals(Object object) {
if (this == object) {
return true;
}
if (object == null || getClass() != object.getClass()) {
return false;
}
PutFilterResponse response = (PutFilterResponse) object;
return Objects.equals(filter, response.filter);
}

@Override
public int hashCode() {
return Objects.hash(filter);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,14 @@
import java.util.SortedSet;
import java.util.TreeSet;

/**
* An MlFilter Object
*
* A filter contains a list of strings.
* It can be used by one or more jobs.
*
* Specifically, filters are referenced in the custom_rules property of detector configuration objects.
*/
public class MlFilter implements ToXContentObject {

public static final ParseField TYPE = new ParseField("type");
Expand Down Expand Up @@ -105,6 +113,10 @@ public int hashCode() {
return Objects.hash(id, description, items);
}

/**
* Creates a new Builder object for creating an MlFilter object
* @param filterId The ID of the filter to create
*/
public static Builder builder(String filterId) {
return new Builder().setId(filterId);
}
Expand All @@ -118,6 +130,10 @@ public static class Builder {
private Builder() {
}

/**
* Set the ID of the filter
* @param id The id desired
*/
public Builder setId(String id) {
this.id = Objects.requireNonNull(id);
return this;
Expand All @@ -128,6 +144,10 @@ public String getId() {
return id;
}

/**
* Set the description of the filter
* @param description The description desired
*/
public Builder setDescription(String description) {
this.description = description;
return this;
Expand All @@ -143,6 +163,13 @@ public Builder setItems(List<String> items) {
return this;
}

/**
* The items of the filter.
*
* A wildcard * can be used at the beginning or the end of an item. Up to 10000 items are allowed in each filter.
*
* @param items String list of items to be applied in the filter
*/
public Builder setItems(String... items) {
setItems(Arrays.asList(items));
return this;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@
import org.elasticsearch.client.ml.PreviewDatafeedRequest;
import org.elasticsearch.client.ml.PutCalendarRequest;
import org.elasticsearch.client.ml.PutDatafeedRequest;
import org.elasticsearch.client.ml.PutFilterRequest;
import org.elasticsearch.client.ml.PutJobRequest;
import org.elasticsearch.client.ml.StartDatafeedRequest;
import org.elasticsearch.client.ml.StartDatafeedRequestTests;
Expand All @@ -59,6 +60,8 @@
import org.elasticsearch.client.ml.job.config.Job;
import org.elasticsearch.client.ml.job.config.JobUpdate;
import org.elasticsearch.client.ml.job.config.JobUpdateTests;
import org.elasticsearch.client.ml.job.config.MlFilter;
import org.elasticsearch.client.ml.job.config.MlFilterTests;
import org.elasticsearch.client.ml.job.util.PageParams;
import org.elasticsearch.common.Strings;
import org.elasticsearch.common.unit.TimeValue;
Expand Down Expand Up @@ -511,6 +514,20 @@ public void testDeleteCalendar() {
assertEquals("/_xpack/ml/calendars/" + deleteCalendarRequest.getCalendarId(), request.getEndpoint());
}

public void testPutFilter() throws IOException {
MlFilter filter = MlFilterTests.createRandom("foo");
PutFilterRequest putFilterRequest = new PutFilterRequest(filter);

Request request = MLRequestConverters.putFilter(putFilterRequest);

assertEquals(HttpPut.METHOD_NAME, request.getMethod());
assertThat(request.getEndpoint(), equalTo("/_xpack/ml/filters/foo"));
try (XContentParser parser = createParser(JsonXContent.jsonXContent, request.getEntity().getContent())) {
MlFilter parsedFilter = MlFilter.PARSER.apply(parser, null).build();
assertThat(parsedFilter, equalTo(filter));
}
}

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 @@ -58,6 +58,8 @@
import org.elasticsearch.client.ml.PutCalendarResponse;
import org.elasticsearch.client.ml.PutDatafeedRequest;
import org.elasticsearch.client.ml.PutDatafeedResponse;
import org.elasticsearch.client.ml.PutFilterRequest;
import org.elasticsearch.client.ml.PutFilterResponse;
import org.elasticsearch.client.ml.PutJobRequest;
import org.elasticsearch.client.ml.PutJobResponse;
import org.elasticsearch.client.ml.StartDatafeedRequest;
Expand All @@ -78,6 +80,7 @@
import org.elasticsearch.client.ml.job.config.Job;
import org.elasticsearch.client.ml.job.config.JobState;
import org.elasticsearch.client.ml.job.config.JobUpdate;
import org.elasticsearch.client.ml.job.config.MlFilter;
import org.elasticsearch.client.ml.job.stats.JobStats;
import org.elasticsearch.common.unit.TimeValue;
import org.elasticsearch.common.xcontent.XContentType;
Expand Down Expand Up @@ -859,6 +862,22 @@ public void testDeleteCalendar() throws IOException {
assertThat(exception.status().getStatus(), equalTo(404));
}

public void testFilterJob() throws Exception {
String filterId = "filter-job-test";
MlFilter mlFilter = MlFilter.builder(filterId)
.setDescription(randomAlphaOfLength(10))
.setItems(generateRandomStringArray(10, 10, false, false))
.build();
MachineLearningClient machineLearningClient = highLevelClient().machineLearning();

PutFilterResponse putFilterResponse = execute(new PutFilterRequest(mlFilter),
machineLearningClient::putFilter,
machineLearningClient::putFilterAsync);
MlFilter createdFilter = putFilterResponse.getResponse();

assertThat(createdFilter, equalTo(mlFilter));
}

public static String randomValidJobId() {
CodepointSetGenerator generator = new CodepointSetGenerator("abcdefghijklmnopqrstuvwxyz0123456789".toCharArray());
return generator.ofCodePointsLength(random(), 10, 10);
Expand Down
Loading

0 comments on commit 7fa0bbd

Please sign in to comment.