HLRC: Add ML API PUT filter (#35175)

This commit is contained in:
Benjamin Trent 2018-11-05 08:56:53 -06:00 committed by GitHub
parent 9d28a104e1
commit a4442dacd7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 469 additions and 0 deletions

View File

@ -49,6 +49,7 @@ import org.elasticsearch.client.ml.PostDataRequest;
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;
@ -463,4 +464,16 @@ final class MLRequestConverters {
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;
}
}

View File

@ -61,6 +61,8 @@ import org.elasticsearch.client.ml.PutCalendarRequest;
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;
@ -1166,4 +1168,43 @@ public final class MachineLearningClient {
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());
}
}

View File

@ -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;
}
}

View File

@ -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);
}
}

View File

@ -32,6 +32,14 @@ import java.util.Objects;
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");
@ -105,6 +113,10 @@ public class MlFilter implements ToXContentObject {
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);
}
@ -118,6 +130,10 @@ public class MlFilter implements ToXContentObject {
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;
@ -128,6 +144,10 @@ public class MlFilter implements ToXContentObject {
return id;
}
/**
* Set the description of the filter
* @param description The description desired
*/
public Builder setDescription(String description) {
this.description = description;
return this;
@ -143,6 +163,13 @@ public class MlFilter implements ToXContentObject {
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;

View File

@ -45,6 +45,7 @@ import org.elasticsearch.client.ml.PostDataRequest;
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;
@ -59,6 +60,8 @@ import org.elasticsearch.client.ml.job.config.Detector;
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;
@ -511,6 +514,20 @@ public class MLRequestConvertersTests extends ESTestCase {
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()));

View File

@ -58,6 +58,8 @@ import org.elasticsearch.client.ml.PutCalendarRequest;
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;
@ -78,6 +80,7 @@ import org.elasticsearch.client.ml.job.config.Detector;
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;
@ -859,6 +862,22 @@ public class MachineLearningIT extends ESRestHighLevelClientTestCase {
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);

View File

@ -74,6 +74,8 @@ import org.elasticsearch.client.ml.PutCalendarRequest;
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;
@ -94,6 +96,7 @@ import org.elasticsearch.client.ml.job.config.DetectionRule;
import org.elasticsearch.client.ml.job.config.Detector;
import org.elasticsearch.client.ml.job.config.Job;
import org.elasticsearch.client.ml.job.config.JobUpdate;
import org.elasticsearch.client.ml.job.config.MlFilter;
import org.elasticsearch.client.ml.job.config.ModelPlotConfig;
import org.elasticsearch.client.ml.job.config.Operator;
import org.elasticsearch.client.ml.job.config.RuleCondition;
@ -2007,4 +2010,58 @@ public class MlClientDocumentationIT extends ESRestHighLevelClientTestCase {
assertTrue(latch.await(30L, TimeUnit.SECONDS));
}
public void testCreateFilter() throws Exception {
RestHighLevelClient client = highLevelClient();
{
// tag::put-filter-config
MlFilter.Builder filterBuilder = MlFilter.builder("my_safe_domains") // <1>
.setDescription("A list of safe domains") // <2>
.setItems("*.google.com", "wikipedia.org"); // <3>
// end::put-filter-config
// tag::put-filter-request
PutFilterRequest request = new PutFilterRequest(filterBuilder.build()); // <1>
// end::put-filter-request
// tag::put-filter-execute
PutFilterResponse response = client.machineLearning().putFilter(request, RequestOptions.DEFAULT);
// end::put-filter-execute
// tag::put-filter-response
MlFilter createdFilter = response.getResponse(); // <1>
// end::put-filter-response
assertThat(createdFilter.getId(), equalTo("my_safe_domains"));
}
{
MlFilter.Builder filterBuilder = MlFilter.builder("safe_domains_async")
.setDescription("A list of safe domains")
.setItems("*.google.com", "wikipedia.org");
PutFilterRequest request = new PutFilterRequest(filterBuilder.build());
// tag::put-filter-execute-listener
ActionListener<PutFilterResponse> listener = new ActionListener<PutFilterResponse>() {
@Override
public void onResponse(PutFilterResponse response) {
// <1>
}
@Override
public void onFailure(Exception e) {
// <2>
}
};
// end::put-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::put-filter-execute-async
client.machineLearning().putFilterAsync(request, RequestOptions.DEFAULT, listener); // <1>
// end::put-filter-execute-async
assertTrue(latch.await(30L, TimeUnit.SECONDS));
}
}
}

View File

@ -0,0 +1,43 @@
/*
* 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.client.ml.job.config.MlFilterTests;
import org.elasticsearch.common.xcontent.XContentParser;
import org.elasticsearch.test.AbstractXContentTestCase;
public class PutFilterRequestTests extends AbstractXContentTestCase<PutFilterRequest> {
@Override
protected PutFilterRequest createTestInstance() {
return new PutFilterRequest(MlFilterTests.createRandom());
}
@Override
protected PutFilterRequest doParseInstance(XContentParser parser) {
return new PutFilterRequest(MlFilter.PARSER.apply(parser, null).build());
}
@Override
protected boolean supportsUnknownFields() {
return false;
}
}

View File

@ -0,0 +1,43 @@
/*
* 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.MlFilterTests;
import org.elasticsearch.common.xcontent.XContentParser;
import org.elasticsearch.test.AbstractXContentTestCase;
import java.io.IOException;
public class PutFilterResponseTests extends AbstractXContentTestCase<PutFilterResponse> {
@Override
protected PutFilterResponse createTestInstance() {
return new PutFilterResponse(MlFilterTests.createRandom());
}
@Override
protected PutFilterResponse doParseInstance(XContentParser parser) throws IOException {
return PutFilterResponse.fromXContent(parser);
}
@Override
protected boolean supportsUnknownFields() {
return false;
}
}

View File

@ -0,0 +1,53 @@
--
:api: put-filter
:request: PutFilterRequest
:response: PutFilterResponse
--
[id="{upid}-{api}"]
=== Put Filter API
The Put Filter API can be used to create a new {ml} filter
in the cluster. The API accepts a +{request}+ object
as a request and returns a +{response}+.
[id="{upid}-{api}-request"]
==== Put Filter Request
A +{request}+ requires the following argument:
["source","java",subs="attributes,callouts,macros"]
--------------------------------------------------
include-tagged::{doc-tests-file}[{api}-request]
--------------------------------------------------
<1> The configuration of the {ml} filter to create as a `MlFilter`
[id="{upid}-{api}-config"]
==== Filter Configuration
The `MlFilter` object contains all the details about the {ml} filter
configuration.
A `MlFilter` contains the following arguments:
["source","java",subs="attributes,callouts,macros"]
--------------------------------------------------
include-tagged::{doc-tests-file}[{api}-config]
--------------------------------------------------
<1> Required, the filter ID
<2> Optional, the filter description
<3> Optional, 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.
include::../execution.asciidoc[]
[id="{upid}-{api}-response"]
==== Response
The returned +{response}+ returns the full representation of
the new {ml} filter if it has been successfully created.
["source","java",subs="attributes,callouts,macros"]
--------------------------------------------------
include-tagged::{doc-tests-file}[{api}-response]
--------------------------------------------------
<1> The newly created `MlFilter`

View File

@ -260,6 +260,7 @@ The Java High Level REST Client supports the following Machine Learning APIs:
* <<{upid}-get-calendars>>
* <<{upid}-put-calendar>>
* <<{upid}-delete-calendar>>
* <<{upid}-put-filter>>
include::ml/put-job.asciidoc[]
include::ml/get-job.asciidoc[]
@ -288,6 +289,7 @@ include::ml/get-categories.asciidoc[]
include::ml/get-calendars.asciidoc[]
include::ml/put-calendar.asciidoc[]
include::ml/delete-calendar.asciidoc[]
include::ml/put-filter.asciidoc[]
== Migration APIs