Skip to content

Commit 0ad15c8

Browse files
benwtrentkcm
authored andcommitted
HLRC: Adding Update datafeed API (#34882)
* HLRC: Adding Update datafeed API * Addressing unused import * Adjusting docs and fixing minor comments * fixing comment
1 parent b3478e2 commit 0ad15c8

File tree

10 files changed

+351
-2
lines changed

10 files changed

+351
-2
lines changed

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

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@
5252
import org.elasticsearch.client.ml.PutJobRequest;
5353
import org.elasticsearch.client.ml.StartDatafeedRequest;
5454
import org.elasticsearch.client.ml.StopDatafeedRequest;
55+
import org.elasticsearch.client.ml.UpdateDatafeedRequest;
5556
import org.elasticsearch.client.ml.UpdateJobRequest;
5657
import org.elasticsearch.common.Strings;
5758
import org.elasticsearch.common.bytes.BytesReference;
@@ -209,6 +210,19 @@ static Request putDatafeed(PutDatafeedRequest putDatafeedRequest) throws IOExcep
209210
return request;
210211
}
211212

213+
static Request updateDatafeed(UpdateDatafeedRequest updateDatafeedRequest) throws IOException {
214+
String endpoint = new EndpointBuilder()
215+
.addPathPartAsIs("_xpack")
216+
.addPathPartAsIs("ml")
217+
.addPathPartAsIs("datafeeds")
218+
.addPathPart(updateDatafeedRequest.getDatafeedUpdate().getId())
219+
.addPathPartAsIs("_update")
220+
.build();
221+
Request request = new Request(HttpPost.METHOD_NAME, endpoint);
222+
request.setEntity(createEntity(updateDatafeedRequest, REQUEST_BODY_CONTENT_TYPE));
223+
return request;
224+
}
225+
212226
static Request getDatafeed(GetDatafeedRequest getDatafeedRequest) {
213227
String endpoint = new EndpointBuilder()
214228
.addPathPartAsIs("_xpack")

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

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@
6767
import org.elasticsearch.client.ml.StartDatafeedResponse;
6868
import org.elasticsearch.client.ml.StopDatafeedRequest;
6969
import org.elasticsearch.client.ml.StopDatafeedResponse;
70+
import org.elasticsearch.client.ml.UpdateDatafeedRequest;
7071
import org.elasticsearch.client.ml.UpdateJobRequest;
7172
import org.elasticsearch.client.ml.job.stats.JobStats;
7273

@@ -494,6 +495,46 @@ public void putDatafeedAsync(PutDatafeedRequest request, RequestOptions options,
494495
Collections.emptySet());
495496
}
496497

498+
/**
499+
* Updates a Machine Learning Datafeed
500+
* <p>
501+
* For additional info
502+
* see <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/ml-update-datafeed.html">
503+
* ML Update datafeed documentation</a>
504+
*
505+
* @param request The UpdateDatafeedRequest containing the {@link org.elasticsearch.client.ml.datafeed.DatafeedUpdate} settings
506+
* @param options Additional request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized
507+
* @return PutDatafeedResponse with enclosed, updated {@link org.elasticsearch.client.ml.datafeed.DatafeedConfig} object
508+
* @throws IOException when there is a serialization issue sending the request or receiving the response
509+
*/
510+
public PutDatafeedResponse updateDatafeed(UpdateDatafeedRequest request, RequestOptions options) throws IOException {
511+
return restHighLevelClient.performRequestAndParseEntity(request,
512+
MLRequestConverters::updateDatafeed,
513+
options,
514+
PutDatafeedResponse::fromXContent,
515+
Collections.emptySet());
516+
}
517+
518+
/**
519+
* Updates a Machine Learning Datafeed asynchronously and notifies listener on completion
520+
* <p>
521+
* For additional info
522+
* see <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/ml-update-datafeed.html">
523+
* ML Update datafeed documentation</a>
524+
*
525+
* @param request The request containing the {@link org.elasticsearch.client.ml.datafeed.DatafeedUpdate} settings
526+
* @param options Additional request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized
527+
* @param listener Listener to be notified upon request completion
528+
*/
529+
public void updateDatafeedAsync(UpdateDatafeedRequest request, RequestOptions options, ActionListener<PutDatafeedResponse> listener) {
530+
restHighLevelClient.performRequestAsyncAndParseEntity(request,
531+
MLRequestConverters::updateDatafeed,
532+
options,
533+
PutDatafeedResponse::fromXContent,
534+
listener,
535+
Collections.emptySet());
536+
}
537+
497538
/**
498539
* Gets one or more Machine Learning datafeed configuration info.
499540
*
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
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.datafeed.DatafeedUpdate;
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+
* Requests an update to a {@link org.elasticsearch.client.ml.datafeed.DatafeedConfig} with the passed {@link DatafeedUpdate}
33+
* settings
34+
*/
35+
public class UpdateDatafeedRequest extends ActionRequest implements ToXContentObject {
36+
37+
private final DatafeedUpdate update;
38+
39+
public UpdateDatafeedRequest(DatafeedUpdate update) {
40+
this.update = update;
41+
}
42+
43+
public DatafeedUpdate getDatafeedUpdate() {
44+
return update;
45+
}
46+
47+
@Override
48+
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
49+
return update.toXContent(builder, params);
50+
}
51+
52+
@Override
53+
public boolean equals(Object o) {
54+
if (this == o) {
55+
return true;
56+
}
57+
58+
if (o == null || getClass() != o.getClass()) {
59+
return false;
60+
}
61+
62+
UpdateDatafeedRequest that = (UpdateDatafeedRequest) o;
63+
return Objects.equals(update, that.update);
64+
}
65+
66+
@Override
67+
public int hashCode() {
68+
return Objects.hash(update);
69+
}
70+
71+
@Override
72+
public final String toString() {
73+
return Strings.toString(this);
74+
}
75+
76+
@Override
77+
public ActionRequestValidationException validate() {
78+
return null;
79+
}
80+
}

client/rest-high-level/src/main/java/org/elasticsearch/client/ml/datafeed/DatafeedUpdate.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737

3838
import java.io.IOException;
3939
import java.util.ArrayList;
40+
import java.util.Arrays;
4041
import java.util.Collections;
4142
import java.util.Comparator;
4243
import java.util.List;
@@ -292,6 +293,10 @@ public Builder setIndices(List<String> indices) {
292293
return this;
293294
}
294295

296+
public Builder setIndices(String... indices) {
297+
return setIndices(Arrays.asList(indices));
298+
}
299+
295300
public Builder setTypes(List<String> types) {
296301
this.types = types;
297302
return this;

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

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,12 +64,14 @@
6464
import org.elasticsearch.client.ml.StartDatafeedResponse;
6565
import org.elasticsearch.client.ml.StopDatafeedRequest;
6666
import org.elasticsearch.client.ml.StopDatafeedResponse;
67+
import org.elasticsearch.client.ml.UpdateDatafeedRequest;
6768
import org.elasticsearch.client.ml.UpdateJobRequest;
6869
import org.elasticsearch.client.ml.calendars.Calendar;
6970
import org.elasticsearch.client.ml.calendars.CalendarTests;
7071
import org.elasticsearch.client.ml.datafeed.DatafeedConfig;
7172
import org.elasticsearch.client.ml.datafeed.DatafeedState;
7273
import org.elasticsearch.client.ml.datafeed.DatafeedStats;
74+
import org.elasticsearch.client.ml.datafeed.DatafeedUpdate;
7375
import org.elasticsearch.client.ml.job.config.AnalysisConfig;
7476
import org.elasticsearch.client.ml.job.config.DataDescription;
7577
import org.elasticsearch.client.ml.job.config.Detector;
@@ -357,6 +359,33 @@ public void testPutDatafeed() throws Exception {
357359
assertThat(createdDatafeed.getIndices(), equalTo(datafeedConfig.getIndices()));
358360
}
359361

362+
public void testUpdateDatafeed() throws Exception {
363+
String jobId = randomValidJobId();
364+
Job job = buildJob(jobId);
365+
MachineLearningClient machineLearningClient = highLevelClient().machineLearning();
366+
execute(new PutJobRequest(job), machineLearningClient::putJob, machineLearningClient::putJobAsync);
367+
368+
String datafeedId = "datafeed-" + jobId;
369+
DatafeedConfig datafeedConfig = DatafeedConfig.builder(datafeedId, jobId).setIndices("some_data_index").build();
370+
371+
PutDatafeedResponse response = machineLearningClient.putDatafeed(new PutDatafeedRequest(datafeedConfig), RequestOptions.DEFAULT);
372+
373+
DatafeedConfig createdDatafeed = response.getResponse();
374+
assertThat(createdDatafeed.getId(), equalTo(datafeedId));
375+
assertThat(createdDatafeed.getIndices(), equalTo(datafeedConfig.getIndices()));
376+
377+
DatafeedUpdate datafeedUpdate = DatafeedUpdate.builder(datafeedId).setIndices("some_other_data_index").setScrollSize(10).build();
378+
379+
response = execute(new UpdateDatafeedRequest(datafeedUpdate),
380+
machineLearningClient::updateDatafeed,
381+
machineLearningClient::updateDatafeedAsync);
382+
383+
DatafeedConfig updatedDatafeed = response.getResponse();
384+
assertThat(datafeedUpdate.getId(), equalTo(updatedDatafeed.getId()));
385+
assertThat(datafeedUpdate.getIndices(), equalTo(updatedDatafeed.getIndices()));
386+
assertThat(datafeedUpdate.getScrollSize(), equalTo(updatedDatafeed.getScrollSize()));
387+
}
388+
360389
public void testGetDatafeed() throws Exception {
361390
String jobId1 = "test-get-datafeed-job-1";
362391
String jobId2 = "test-get-datafeed-job-2";

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

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,11 +80,13 @@
8080
import org.elasticsearch.client.ml.StartDatafeedResponse;
8181
import org.elasticsearch.client.ml.StopDatafeedRequest;
8282
import org.elasticsearch.client.ml.StopDatafeedResponse;
83+
import org.elasticsearch.client.ml.UpdateDatafeedRequest;
8384
import org.elasticsearch.client.ml.UpdateJobRequest;
8485
import org.elasticsearch.client.ml.calendars.Calendar;
8586
import org.elasticsearch.client.ml.datafeed.ChunkingConfig;
8687
import org.elasticsearch.client.ml.datafeed.DatafeedConfig;
8788
import org.elasticsearch.client.ml.datafeed.DatafeedStats;
89+
import org.elasticsearch.client.ml.datafeed.DatafeedUpdate;
8890
import org.elasticsearch.client.ml.job.config.AnalysisConfig;
8991
import org.elasticsearch.client.ml.job.config.AnalysisLimits;
9092
import org.elasticsearch.client.ml.job.config.DataDescription;
@@ -630,6 +632,77 @@ public void onFailure(Exception e) {
630632
}
631633
}
632634

635+
public void testUpdateDatafeed() throws Exception {
636+
RestHighLevelClient client = highLevelClient();
637+
638+
Job job = MachineLearningIT.buildJob("update-datafeed-job");
639+
client.machineLearning().putJob(new PutJobRequest(job), RequestOptions.DEFAULT);
640+
String datafeedId = job.getId() + "-feed";
641+
DatafeedConfig datafeed = DatafeedConfig.builder(datafeedId, job.getId()).setIndices("foo").build();
642+
client.machineLearning().putDatafeed(new PutDatafeedRequest(datafeed), RequestOptions.DEFAULT);
643+
644+
{
645+
AggregatorFactories.Builder aggs = AggregatorFactories.builder();
646+
List<SearchSourceBuilder.ScriptField> scriptFields = Collections.emptyList();
647+
// tag::update-datafeed-config
648+
DatafeedUpdate.Builder datafeedUpdateBuilder = new DatafeedUpdate.Builder(datafeedId) // <1>
649+
.setAggregations(aggs) // <2>
650+
.setIndices("index_1", "index_2") // <3>
651+
.setChunkingConfig(ChunkingConfig.newAuto()) // <4>
652+
.setFrequency(TimeValue.timeValueSeconds(30)) // <5>
653+
.setQuery(QueryBuilders.matchAllQuery()) // <6>
654+
.setQueryDelay(TimeValue.timeValueMinutes(1)) // <7>
655+
.setScriptFields(scriptFields) // <8>
656+
.setScrollSize(1000) // <9>
657+
.setJobId("update-datafeed-job"); // <10>
658+
// end::update-datafeed-config
659+
660+
// Clearing aggregation to avoid complex validation rules
661+
datafeedUpdateBuilder.setAggregations((String) null);
662+
663+
// tag::update-datafeed-request
664+
UpdateDatafeedRequest request = new UpdateDatafeedRequest(datafeedUpdateBuilder.build()); // <1>
665+
// end::update-datafeed-request
666+
667+
// tag::update-datafeed-execute
668+
PutDatafeedResponse response = client.machineLearning().updateDatafeed(request, RequestOptions.DEFAULT);
669+
// end::update-datafeed-execute
670+
671+
// tag::update-datafeed-response
672+
DatafeedConfig updatedDatafeed = response.getResponse(); // <1>
673+
// end::update-datafeed-response
674+
assertThat(updatedDatafeed.getId(), equalTo(datafeedId));
675+
}
676+
{
677+
DatafeedUpdate datafeedUpdate = new DatafeedUpdate.Builder(datafeedId).setIndices("index_1", "index_2").build();
678+
679+
UpdateDatafeedRequest request = new UpdateDatafeedRequest(datafeedUpdate);
680+
// tag::update-datafeed-execute-listener
681+
ActionListener<PutDatafeedResponse> listener = new ActionListener<PutDatafeedResponse>() {
682+
@Override
683+
public void onResponse(PutDatafeedResponse response) {
684+
// <1>
685+
}
686+
687+
@Override
688+
public void onFailure(Exception e) {
689+
// <2>
690+
}
691+
};
692+
// end::update-datafeed-execute-listener
693+
694+
// Replace the empty listener by a blocking listener in test
695+
final CountDownLatch latch = new CountDownLatch(1);
696+
listener = new LatchedActionListener<>(listener, latch);
697+
698+
// tag::update-datafeed-execute-async
699+
client.machineLearning().updateDatafeedAsync(request, RequestOptions.DEFAULT, listener); // <1>
700+
// end::update-datafeed-execute-async
701+
702+
assertTrue(latch.await(30L, TimeUnit.SECONDS));
703+
}
704+
}
705+
633706
public void testGetDatafeed() throws Exception {
634707
RestHighLevelClient client = highLevelClient();
635708

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
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.datafeed.DatafeedUpdate;
22+
import org.elasticsearch.client.ml.datafeed.DatafeedUpdateTests;
23+
import org.elasticsearch.common.xcontent.XContentParser;
24+
import org.elasticsearch.test.AbstractXContentTestCase;
25+
26+
27+
public class UpdateDatafeedRequestTests extends AbstractXContentTestCase<UpdateDatafeedRequest> {
28+
29+
@Override
30+
protected UpdateDatafeedRequest createTestInstance() {
31+
return new UpdateDatafeedRequest(DatafeedUpdateTests.createRandom());
32+
}
33+
34+
@Override
35+
protected UpdateDatafeedRequest doParseInstance(XContentParser parser) {
36+
return new UpdateDatafeedRequest(DatafeedUpdate.PARSER.apply(parser, null).build());
37+
}
38+
39+
@Override
40+
protected boolean supportsUnknownFields() {
41+
return false;
42+
}
43+
}

client/rest-high-level/src/test/java/org/elasticsearch/client/ml/datafeed/DatafeedUpdateTests.java

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,8 +32,7 @@
3232

3333
public class DatafeedUpdateTests extends AbstractXContentTestCase<DatafeedUpdate> {
3434

35-
@Override
36-
protected DatafeedUpdate createTestInstance() {
35+
public static DatafeedUpdate createRandom() {
3736
DatafeedUpdate.Builder builder = new DatafeedUpdate.Builder(DatafeedConfigTests.randomValidDatafeedId());
3837
if (randomBoolean()) {
3938
builder.setJobId(randomAlphaOfLength(10));
@@ -87,6 +86,11 @@ protected DatafeedUpdate createTestInstance() {
8786
return builder.build();
8887
}
8988

89+
@Override
90+
protected DatafeedUpdate createTestInstance() {
91+
return createRandom();
92+
}
93+
9094
@Override
9195
protected DatafeedUpdate doParseInstance(XContentParser parser) {
9296
return DatafeedUpdate.PARSER.apply(parser, null).build();

0 commit comments

Comments
 (0)