Skip to content

Commit 7fa0bbd

Browse files
committed
HLRC: Add ML API PUT filter (#35175)
1 parent 8c18dc8 commit 7fa0bbd

File tree

12 files changed

+469
-0
lines changed

12 files changed

+469
-0
lines changed

client/rest-high-level/src/main/java/org/elasticsearch/client/MLRequestConverters.java

+13
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@
4949
import org.elasticsearch.client.ml.PreviewDatafeedRequest;
5050
import org.elasticsearch.client.ml.PutCalendarRequest;
5151
import org.elasticsearch.client.ml.PutDatafeedRequest;
52+
import org.elasticsearch.client.ml.PutFilterRequest;
5253
import org.elasticsearch.client.ml.PutJobRequest;
5354
import org.elasticsearch.client.ml.StartDatafeedRequest;
5455
import org.elasticsearch.client.ml.StopDatafeedRequest;
@@ -463,4 +464,16 @@ static Request deleteCalendar(DeleteCalendarRequest deleteCalendarRequest) {
463464
Request request = new Request(HttpDelete.METHOD_NAME, endpoint);
464465
return request;
465466
}
467+
468+
static Request putFilter(PutFilterRequest putFilterRequest) throws IOException {
469+
String endpoint = new EndpointBuilder()
470+
.addPathPartAsIs("_xpack")
471+
.addPathPartAsIs("ml")
472+
.addPathPartAsIs("filters")
473+
.addPathPart(putFilterRequest.getMlFilter().getId())
474+
.build();
475+
Request request = new Request(HttpPut.METHOD_NAME, endpoint);
476+
request.setEntity(createEntity(putFilterRequest, REQUEST_BODY_CONTENT_TYPE));
477+
return request;
478+
}
466479
}

client/rest-high-level/src/main/java/org/elasticsearch/client/MachineLearningClient.java

+41
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,8 @@
6161
import org.elasticsearch.client.ml.PutCalendarResponse;
6262
import org.elasticsearch.client.ml.PutDatafeedRequest;
6363
import org.elasticsearch.client.ml.PutDatafeedResponse;
64+
import org.elasticsearch.client.ml.PutFilterRequest;
65+
import org.elasticsearch.client.ml.PutFilterResponse;
6466
import org.elasticsearch.client.ml.PutJobRequest;
6567
import org.elasticsearch.client.ml.PutJobResponse;
6668
import org.elasticsearch.client.ml.StartDatafeedRequest;
@@ -1166,4 +1168,43 @@ public void deleteCalendarAsync(DeleteCalendarRequest request, RequestOptions op
11661168
listener,
11671169
Collections.emptySet());
11681170
}
1171+
1172+
/**
1173+
* Creates a new Machine Learning Filter
1174+
* <p>
1175+
* For additional info
1176+
* see <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/ml-put-filter.html">ML PUT Filter documentation</a>
1177+
*
1178+
* @param request The PutFilterRequest containing the {@link org.elasticsearch.client.ml.job.config.MlFilter} settings
1179+
* @param options Additional request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized
1180+
* @return PutFilterResponse with enclosed {@link org.elasticsearch.client.ml.job.config.MlFilter} object
1181+
* @throws IOException when there is a serialization issue sending the request or receiving the response
1182+
*/
1183+
public PutFilterResponse putFilter(PutFilterRequest request, RequestOptions options) throws IOException {
1184+
return restHighLevelClient.performRequestAndParseEntity(request,
1185+
MLRequestConverters::putFilter,
1186+
options,
1187+
PutFilterResponse::fromXContent,
1188+
Collections.emptySet());
1189+
}
1190+
1191+
/**
1192+
* Creates a new Machine Learning Filter asynchronously and notifies listener on completion
1193+
* <p>
1194+
* For additional info
1195+
* see <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/ml-put-filter.html">ML PUT Filter documentation</a>
1196+
*
1197+
* @param request The request containing the {@link org.elasticsearch.client.ml.job.config.MlFilter} settings
1198+
* @param options Additional request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized
1199+
* @param listener Listener to be notified upon request completion
1200+
*/
1201+
public void putFilterAsync(PutFilterRequest request, RequestOptions options, ActionListener<PutFilterResponse> listener) {
1202+
restHighLevelClient.performRequestAsyncAndParseEntity(request,
1203+
MLRequestConverters::putFilter,
1204+
options,
1205+
PutFilterResponse::fromXContent,
1206+
listener,
1207+
Collections.emptySet());
1208+
}
1209+
11691210
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
/*
2+
* Licensed to Elasticsearch under one or more contributor
3+
* license agreements. See the NOTICE file distributed with
4+
* this work for additional information regarding copyright
5+
* ownership. Elasticsearch licenses this file to you under
6+
* the Apache License, Version 2.0 (the "License"); you may
7+
* not use this file except in compliance with the License.
8+
* You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*/
19+
package org.elasticsearch.client.ml;
20+
21+
import org.elasticsearch.action.ActionRequest;
22+
import org.elasticsearch.action.ActionRequestValidationException;
23+
import org.elasticsearch.client.ml.job.config.MlFilter;
24+
import org.elasticsearch.common.Strings;
25+
import org.elasticsearch.common.xcontent.ToXContentObject;
26+
import org.elasticsearch.common.xcontent.XContentBuilder;
27+
28+
import java.io.IOException;
29+
import java.util.Objects;
30+
31+
/**
32+
* Request to create a new Machine Learning MlFilter given a {@link MlFilter} configuration
33+
*/
34+
public class PutFilterRequest extends ActionRequest implements ToXContentObject {
35+
36+
private final MlFilter filter;
37+
38+
/**
39+
* Construct a new PutMlFilterRequest
40+
*
41+
* @param filter a {@link MlFilter} configuration to create
42+
*/
43+
public PutFilterRequest(MlFilter filter) {
44+
this.filter = filter;
45+
}
46+
47+
public MlFilter getMlFilter() {
48+
return filter;
49+
}
50+
51+
@Override
52+
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
53+
return filter.toXContent(builder, params);
54+
}
55+
56+
@Override
57+
public boolean equals(Object object) {
58+
if (this == object) {
59+
return true;
60+
}
61+
62+
if (object == null || getClass() != object.getClass()) {
63+
return false;
64+
}
65+
66+
PutFilterRequest request = (PutFilterRequest) object;
67+
return Objects.equals(filter, request.filter);
68+
}
69+
70+
@Override
71+
public int hashCode() {
72+
return Objects.hash(filter);
73+
}
74+
75+
@Override
76+
public final String toString() {
77+
return Strings.toString(this);
78+
}
79+
80+
@Override
81+
public ActionRequestValidationException validate() {
82+
return null;
83+
}
84+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
/*
2+
* Licensed to Elasticsearch under one or more contributor
3+
* license agreements. See the NOTICE file distributed with
4+
* this work for additional information regarding copyright
5+
* ownership. Elasticsearch licenses this file to you under
6+
* the Apache License, Version 2.0 (the "License"); you may
7+
* not use this file except in compliance with the License.
8+
* You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*/
19+
package org.elasticsearch.client.ml;
20+
21+
import org.elasticsearch.client.ml.job.config.MlFilter;
22+
import org.elasticsearch.common.xcontent.ToXContentObject;
23+
import org.elasticsearch.common.xcontent.XContentBuilder;
24+
import org.elasticsearch.common.xcontent.XContentParser;
25+
26+
import java.io.IOException;
27+
import java.util.Objects;
28+
29+
/**
30+
* Response containing the newly created {@link MlFilter}
31+
*/
32+
public class PutFilterResponse implements ToXContentObject {
33+
34+
private MlFilter filter;
35+
36+
public static PutFilterResponse fromXContent(XContentParser parser) throws IOException {
37+
return new PutFilterResponse(MlFilter.PARSER.parse(parser, null).build());
38+
}
39+
40+
PutFilterResponse(MlFilter filter) {
41+
this.filter = filter;
42+
}
43+
44+
public MlFilter getResponse() {
45+
return filter;
46+
}
47+
48+
@Override
49+
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
50+
filter.toXContent(builder, params);
51+
return builder;
52+
}
53+
54+
@Override
55+
public boolean equals(Object object) {
56+
if (this == object) {
57+
return true;
58+
}
59+
if (object == null || getClass() != object.getClass()) {
60+
return false;
61+
}
62+
PutFilterResponse response = (PutFilterResponse) object;
63+
return Objects.equals(filter, response.filter);
64+
}
65+
66+
@Override
67+
public int hashCode() {
68+
return Objects.hash(filter);
69+
}
70+
}

client/rest-high-level/src/main/java/org/elasticsearch/client/ml/job/config/MlFilter.java

+27
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,14 @@
3232
import java.util.SortedSet;
3333
import java.util.TreeSet;
3434

35+
/**
36+
* An MlFilter Object
37+
*
38+
* A filter contains a list of strings.
39+
* It can be used by one or more jobs.
40+
*
41+
* Specifically, filters are referenced in the custom_rules property of detector configuration objects.
42+
*/
3543
public class MlFilter implements ToXContentObject {
3644

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

116+
/**
117+
* Creates a new Builder object for creating an MlFilter object
118+
* @param filterId The ID of the filter to create
119+
*/
108120
public static Builder builder(String filterId) {
109121
return new Builder().setId(filterId);
110122
}
@@ -118,6 +130,10 @@ public static class Builder {
118130
private Builder() {
119131
}
120132

133+
/**
134+
* Set the ID of the filter
135+
* @param id The id desired
136+
*/
121137
public Builder setId(String id) {
122138
this.id = Objects.requireNonNull(id);
123139
return this;
@@ -128,6 +144,10 @@ public String getId() {
128144
return id;
129145
}
130146

147+
/**
148+
* Set the description of the filter
149+
* @param description The description desired
150+
*/
131151
public Builder setDescription(String description) {
132152
this.description = description;
133153
return this;
@@ -143,6 +163,13 @@ public Builder setItems(List<String> items) {
143163
return this;
144164
}
145165

166+
/**
167+
* The items of the filter.
168+
*
169+
* A wildcard * can be used at the beginning or the end of an item. Up to 10000 items are allowed in each filter.
170+
*
171+
* @param items String list of items to be applied in the filter
172+
*/
146173
public Builder setItems(String... items) {
147174
setItems(Arrays.asList(items));
148175
return this;

client/rest-high-level/src/test/java/org/elasticsearch/client/MLRequestConvertersTests.java

+17
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@
4545
import org.elasticsearch.client.ml.PreviewDatafeedRequest;
4646
import org.elasticsearch.client.ml.PutCalendarRequest;
4747
import org.elasticsearch.client.ml.PutDatafeedRequest;
48+
import org.elasticsearch.client.ml.PutFilterRequest;
4849
import org.elasticsearch.client.ml.PutJobRequest;
4950
import org.elasticsearch.client.ml.StartDatafeedRequest;
5051
import org.elasticsearch.client.ml.StartDatafeedRequestTests;
@@ -59,6 +60,8 @@
5960
import org.elasticsearch.client.ml.job.config.Job;
6061
import org.elasticsearch.client.ml.job.config.JobUpdate;
6162
import org.elasticsearch.client.ml.job.config.JobUpdateTests;
63+
import org.elasticsearch.client.ml.job.config.MlFilter;
64+
import org.elasticsearch.client.ml.job.config.MlFilterTests;
6265
import org.elasticsearch.client.ml.job.util.PageParams;
6366
import org.elasticsearch.common.Strings;
6467
import org.elasticsearch.common.unit.TimeValue;
@@ -511,6 +514,20 @@ public void testDeleteCalendar() {
511514
assertEquals("/_xpack/ml/calendars/" + deleteCalendarRequest.getCalendarId(), request.getEndpoint());
512515
}
513516

517+
public void testPutFilter() throws IOException {
518+
MlFilter filter = MlFilterTests.createRandom("foo");
519+
PutFilterRequest putFilterRequest = new PutFilterRequest(filter);
520+
521+
Request request = MLRequestConverters.putFilter(putFilterRequest);
522+
523+
assertEquals(HttpPut.METHOD_NAME, request.getMethod());
524+
assertThat(request.getEndpoint(), equalTo("/_xpack/ml/filters/foo"));
525+
try (XContentParser parser = createParser(JsonXContent.jsonXContent, request.getEntity().getContent())) {
526+
MlFilter parsedFilter = MlFilter.PARSER.apply(parser, null).build();
527+
assertThat(parsedFilter, equalTo(filter));
528+
}
529+
}
530+
514531
private static Job createValidJob(String jobId) {
515532
AnalysisConfig.Builder analysisConfig = AnalysisConfig.builder(Collections.singletonList(
516533
Detector.builder().setFunction("count").build()));

client/rest-high-level/src/test/java/org/elasticsearch/client/MachineLearningIT.java

+19
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,8 @@
5858
import org.elasticsearch.client.ml.PutCalendarResponse;
5959
import org.elasticsearch.client.ml.PutDatafeedRequest;
6060
import org.elasticsearch.client.ml.PutDatafeedResponse;
61+
import org.elasticsearch.client.ml.PutFilterRequest;
62+
import org.elasticsearch.client.ml.PutFilterResponse;
6163
import org.elasticsearch.client.ml.PutJobRequest;
6264
import org.elasticsearch.client.ml.PutJobResponse;
6365
import org.elasticsearch.client.ml.StartDatafeedRequest;
@@ -78,6 +80,7 @@
7880
import org.elasticsearch.client.ml.job.config.Job;
7981
import org.elasticsearch.client.ml.job.config.JobState;
8082
import org.elasticsearch.client.ml.job.config.JobUpdate;
83+
import org.elasticsearch.client.ml.job.config.MlFilter;
8184
import org.elasticsearch.client.ml.job.stats.JobStats;
8285
import org.elasticsearch.common.unit.TimeValue;
8386
import org.elasticsearch.common.xcontent.XContentType;
@@ -859,6 +862,22 @@ public void testDeleteCalendar() throws IOException {
859862
assertThat(exception.status().getStatus(), equalTo(404));
860863
}
861864

865+
public void testFilterJob() throws Exception {
866+
String filterId = "filter-job-test";
867+
MlFilter mlFilter = MlFilter.builder(filterId)
868+
.setDescription(randomAlphaOfLength(10))
869+
.setItems(generateRandomStringArray(10, 10, false, false))
870+
.build();
871+
MachineLearningClient machineLearningClient = highLevelClient().machineLearning();
872+
873+
PutFilterResponse putFilterResponse = execute(new PutFilterRequest(mlFilter),
874+
machineLearningClient::putFilter,
875+
machineLearningClient::putFilterAsync);
876+
MlFilter createdFilter = putFilterResponse.getResponse();
877+
878+
assertThat(createdFilter, equalTo(mlFilter));
879+
}
880+
862881
public static String randomValidJobId() {
863882
CodepointSetGenerator generator = new CodepointSetGenerator("abcdefghijklmnopqrstuvwxyz0123456789".toCharArray());
864883
return generator.ofCodePointsLength(random(), 10, 10);

0 commit comments

Comments
 (0)