diff --git a/client/rest-high-level/src/main/java/org/elasticsearch/client/MLRequestConverters.java b/client/rest-high-level/src/main/java/org/elasticsearch/client/MLRequestConverters.java index 35898a8a8e4d..dc4d550e7b9a 100644 --- a/client/rest-high-level/src/main/java/org/elasticsearch/client/MLRequestConverters.java +++ b/client/rest-high-level/src/main/java/org/elasticsearch/client/MLRequestConverters.java @@ -28,6 +28,7 @@ import org.elasticsearch.client.ml.CloseJobRequest; import org.elasticsearch.client.ml.DeleteJobRequest; import org.elasticsearch.client.ml.FlushJobRequest; import org.elasticsearch.client.ml.GetBucketsRequest; +import org.elasticsearch.client.ml.GetInfluencersRequest; import org.elasticsearch.client.ml.GetJobRequest; import org.elasticsearch.client.ml.GetJobStatsRequest; import org.elasticsearch.client.ml.GetOverallBucketsRequest; @@ -186,4 +187,18 @@ final class MLRequestConverters { request.setEntity(createEntity(getRecordsRequest, REQUEST_BODY_CONTENT_TYPE)); return request; } + + static Request getInfluencers(GetInfluencersRequest getInfluencersRequest) throws IOException { + String endpoint = new EndpointBuilder() + .addPathPartAsIs("_xpack") + .addPathPartAsIs("ml") + .addPathPartAsIs("anomaly_detectors") + .addPathPart(getInfluencersRequest.getJobId()) + .addPathPartAsIs("results") + .addPathPartAsIs("influencers") + .build(); + Request request = new Request(HttpGet.METHOD_NAME, endpoint); + request.setEntity(createEntity(getInfluencersRequest, REQUEST_BODY_CONTENT_TYPE)); + return request; + } } diff --git a/client/rest-high-level/src/main/java/org/elasticsearch/client/MachineLearningClient.java b/client/rest-high-level/src/main/java/org/elasticsearch/client/MachineLearningClient.java index 4757ec3182b4..be5f81076ae9 100644 --- a/client/rest-high-level/src/main/java/org/elasticsearch/client/MachineLearningClient.java +++ b/client/rest-high-level/src/main/java/org/elasticsearch/client/MachineLearningClient.java @@ -19,19 +19,20 @@ package org.elasticsearch.client; import org.elasticsearch.action.ActionListener; -import org.elasticsearch.client.ml.FlushJobRequest; -import org.elasticsearch.client.ml.FlushJobResponse; -import org.elasticsearch.client.ml.GetJobStatsRequest; -import org.elasticsearch.client.ml.GetJobStatsResponse; -import org.elasticsearch.client.ml.job.stats.JobStats; import org.elasticsearch.client.ml.CloseJobRequest; import org.elasticsearch.client.ml.CloseJobResponse; import org.elasticsearch.client.ml.DeleteJobRequest; import org.elasticsearch.client.ml.DeleteJobResponse; +import org.elasticsearch.client.ml.FlushJobRequest; +import org.elasticsearch.client.ml.FlushJobResponse; import org.elasticsearch.client.ml.GetBucketsRequest; import org.elasticsearch.client.ml.GetBucketsResponse; +import org.elasticsearch.client.ml.GetInfluencersRequest; +import org.elasticsearch.client.ml.GetInfluencersResponse; import org.elasticsearch.client.ml.GetJobRequest; import org.elasticsearch.client.ml.GetJobResponse; +import org.elasticsearch.client.ml.GetJobStatsRequest; +import org.elasticsearch.client.ml.GetJobStatsResponse; import org.elasticsearch.client.ml.GetOverallBucketsRequest; import org.elasticsearch.client.ml.GetOverallBucketsResponse; import org.elasticsearch.client.ml.GetRecordsRequest; @@ -40,6 +41,7 @@ import org.elasticsearch.client.ml.OpenJobRequest; import org.elasticsearch.client.ml.OpenJobResponse; import org.elasticsearch.client.ml.PutJobRequest; import org.elasticsearch.client.ml.PutJobResponse; +import org.elasticsearch.client.ml.job.stats.JobStats; import java.io.IOException; import java.util.Collections; @@ -464,4 +466,43 @@ public final class MachineLearningClient { listener, Collections.emptySet()); } + + /** + * Gets the influencers for a Machine Learning Job. + *

+ * For additional info + * see + * ML GET influencers documentation + * + * @param request the request + * @param options Additional request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized + */ + public GetInfluencersResponse getInfluencers(GetInfluencersRequest request, RequestOptions options) throws IOException { + return restHighLevelClient.performRequestAndParseEntity(request, + MLRequestConverters::getInfluencers, + options, + GetInfluencersResponse::fromXContent, + Collections.emptySet()); + } + + /** + * Gets the influencers for a Machine Learning Job, notifies listener once the requested influencers are retrieved. + *

+ * For additional info + * * see + * ML GET influencers documentation + * + * @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 getInfluencersAsync(GetInfluencersRequest request, RequestOptions options, + ActionListener listener) { + restHighLevelClient.performRequestAsyncAndParseEntity(request, + MLRequestConverters::getInfluencers, + options, + GetInfluencersResponse::fromXContent, + listener, + Collections.emptySet()); + } } diff --git a/client/rest-high-level/src/main/java/org/elasticsearch/client/ml/GetBucketsRequest.java b/client/rest-high-level/src/main/java/org/elasticsearch/client/ml/GetBucketsRequest.java index f50d92d58dda..927fd08c1ca7 100644 --- a/client/rest-high-level/src/main/java/org/elasticsearch/client/ml/GetBucketsRequest.java +++ b/client/rest-high-level/src/main/java/org/elasticsearch/client/ml/GetBucketsRequest.java @@ -41,7 +41,6 @@ public class GetBucketsRequest extends ActionRequest implements ToXContentObject public static final ParseField START = new ParseField("start"); public static final ParseField END = new ParseField("end"); public static final ParseField ANOMALY_SCORE = new ParseField("anomaly_score"); - public static final ParseField TIMESTAMP = new ParseField("timestamp"); public static final ParseField SORT = new ParseField("sort"); public static final ParseField DESCENDING = new ParseField("desc"); @@ -87,7 +86,7 @@ public class GetBucketsRequest extends ActionRequest implements ToXContentObject /** * Sets the timestamp of a specific bucket to be retrieved. - * @param timestamp the timestamp of a specific bucket to be retrieved + * @param timestamp String representation of a timestamp; may be an epoch seconds, epoch millis or an ISO string */ public void setTimestamp(String timestamp) { this.timestamp = timestamp; @@ -106,11 +105,11 @@ public class GetBucketsRequest extends ActionRequest implements ToXContentObject * When {@code true}, buckets will be expanded to include their records. * @param expand value of "expand" to be set */ - public void setExpand(boolean expand) { + public void setExpand(Boolean expand) { this.expand = expand; } - public boolean isExcludeInterim() { + public Boolean isExcludeInterim() { return excludeInterim; } @@ -119,7 +118,7 @@ public class GetBucketsRequest extends ActionRequest implements ToXContentObject * When {@code true}, interim buckets will be filtered out. * @param excludeInterim value of "exclude_interim" to be set */ - public void setExcludeInterim(boolean excludeInterim) { + public void setExcludeInterim(Boolean excludeInterim) { this.excludeInterim = excludeInterim; } @@ -130,7 +129,7 @@ public class GetBucketsRequest extends ActionRequest implements ToXContentObject /** * Sets the value of "start" which is a timestamp. * Only buckets whose timestamp is on or after the "start" value will be returned. - * @param start value of "start" to be set + * @param start String representation of a timestamp; may be an epoch seconds, epoch millis or an ISO string */ public void setStart(String start) { this.start = start; @@ -143,7 +142,7 @@ public class GetBucketsRequest extends ActionRequest implements ToXContentObject /** * Sets the value of "end" which is a timestamp. * Only buckets whose timestamp is before the "end" value will be returned. - * @param end value of "end" to be set + * @param end String representation of a timestamp; may be an epoch seconds, epoch millis or an ISO string */ public void setEnd(String end) { this.end = end; @@ -170,7 +169,7 @@ public class GetBucketsRequest extends ActionRequest implements ToXContentObject * Only buckets with "anomaly_score" equal or greater will be returned. * @param anomalyScore value of "anomaly_score". */ - public void setAnomalyScore(double anomalyScore) { + public void setAnomalyScore(Double anomalyScore) { this.anomalyScore = anomalyScore; } @@ -187,7 +186,7 @@ public class GetBucketsRequest extends ActionRequest implements ToXContentObject this.sort = sort; } - public boolean isDescending() { + public Boolean isDescending() { return descending; } diff --git a/client/rest-high-level/src/main/java/org/elasticsearch/client/ml/GetInfluencersRequest.java b/client/rest-high-level/src/main/java/org/elasticsearch/client/ml/GetInfluencersRequest.java new file mode 100644 index 000000000000..f57d327db3a3 --- /dev/null +++ b/client/rest-high-level/src/main/java/org/elasticsearch/client/ml/GetInfluencersRequest.java @@ -0,0 +1,227 @@ +/* + * 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.Job; +import org.elasticsearch.client.ml.job.util.PageParams; +import org.elasticsearch.common.ParseField; +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.Objects; + +/** + * A request to retrieve influencers of a given job + */ +public class GetInfluencersRequest extends ActionRequest implements ToXContentObject { + + public static final ParseField EXCLUDE_INTERIM = new ParseField("exclude_interim"); + public static final ParseField START = new ParseField("start"); + public static final ParseField END = new ParseField("end"); + public static final ParseField INFLUENCER_SCORE = new ParseField("influencer_score"); + public static final ParseField SORT = new ParseField("sort"); + public static final ParseField DESCENDING = new ParseField("desc"); + + public static final ConstructingObjectParser PARSER = new ConstructingObjectParser<>( + "get_influencers_request", a -> new GetInfluencersRequest((String) a[0])); + + static { + PARSER.declareString(ConstructingObjectParser.constructorArg(), Job.ID); + PARSER.declareBoolean(GetInfluencersRequest::setExcludeInterim, EXCLUDE_INTERIM); + PARSER.declareStringOrNull(GetInfluencersRequest::setStart, START); + PARSER.declareStringOrNull(GetInfluencersRequest::setEnd, END); + PARSER.declareObject(GetInfluencersRequest::setPageParams, PageParams.PARSER, PageParams.PAGE); + PARSER.declareDouble(GetInfluencersRequest::setInfluencerScore, INFLUENCER_SCORE); + PARSER.declareString(GetInfluencersRequest::setSort, SORT); + PARSER.declareBoolean(GetInfluencersRequest::setDescending, DESCENDING); + } + + private final String jobId; + private Boolean excludeInterim; + private String start; + private String end; + private Double influencerScore; + private PageParams pageParams; + private String sort; + private Boolean descending; + + /** + * Constructs a request to retrieve influencers of a given job + * @param jobId id of the job to retrieve influencers of + */ + public GetInfluencersRequest(String jobId) { + this.jobId = Objects.requireNonNull(jobId); + } + + public String getJobId() { + return jobId; + } + + public Boolean isExcludeInterim() { + return excludeInterim; + } + + /** + * Sets the value of "exclude_interim". + * When {@code true}, interim influencers will be filtered out. + * @param excludeInterim value of "exclude_interim" to be set + */ + public void setExcludeInterim(Boolean excludeInterim) { + this.excludeInterim = excludeInterim; + } + + public String getStart() { + return start; + } + + /** + * Sets the value of "start" which is a timestamp. + * Only influencers whose timestamp is on or after the "start" value will be returned. + * @param start String representation of a timestamp; may be an epoch seconds, epoch millis or an ISO string + */ + public void setStart(String start) { + this.start = start; + } + + public String getEnd() { + return end; + } + + /** + * Sets the value of "end" which is a timestamp. + * Only influencers whose timestamp is before the "end" value will be returned. + * @param end String representation of a timestamp; may be an epoch seconds, epoch millis or an ISO string + */ + public void setEnd(String end) { + this.end = end; + } + + public PageParams getPageParams() { + return pageParams; + } + + /** + * Sets the paging parameters + * @param pageParams The paging parameters + */ + public void setPageParams(PageParams pageParams) { + this.pageParams = pageParams; + } + + public Double getInfluencerScore() { + return influencerScore; + } + + /** + * Sets the value of "influencer_score". + * Only influencers with "influencer_score" equal or greater will be returned. + * @param influencerScore value of "influencer_score". + */ + public void setInfluencerScore(Double influencerScore) { + this.influencerScore = influencerScore; + } + + public String getSort() { + return sort; + } + + /** + * Sets the value of "sort". + * Specifies the influencer field to sort on. + * @param sort value of "sort". + */ + public void setSort(String sort) { + this.sort = sort; + } + + public Boolean isDescending() { + return descending; + } + + /** + * Sets the value of "desc". + * Specifies the sorting order. + * @param descending value of "desc" + */ + public void setDescending(Boolean descending) { + this.descending = descending; + } + + @Override + public ActionRequestValidationException validate() { + return null; + } + + @Override + public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException { + builder.startObject(); + builder.field(Job.ID.getPreferredName(), jobId); + if (excludeInterim != null) { + builder.field(EXCLUDE_INTERIM.getPreferredName(), excludeInterim); + } + if (start != null) { + builder.field(START.getPreferredName(), start); + } + if (end != null) { + builder.field(END.getPreferredName(), end); + } + if (pageParams != null) { + builder.field(PageParams.PAGE.getPreferredName(), pageParams); + } + if (influencerScore != null) { + builder.field(INFLUENCER_SCORE.getPreferredName(), influencerScore); + } + if (sort != null) { + builder.field(SORT.getPreferredName(), sort); + } + if (descending != null) { + builder.field(DESCENDING.getPreferredName(), descending); + } + builder.endObject(); + return builder; + } + + @Override + public int hashCode() { + return Objects.hash(jobId, excludeInterim, influencerScore, pageParams, start, end, sort, descending); + } + + @Override + public boolean equals(Object obj) { + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + GetInfluencersRequest other = (GetInfluencersRequest) obj; + return Objects.equals(jobId, other.jobId) && + Objects.equals(excludeInterim, other.excludeInterim) && + Objects.equals(influencerScore, other.influencerScore) && + Objects.equals(pageParams, other.pageParams) && + Objects.equals(start, other.start) && + Objects.equals(end, other.end) && + Objects.equals(sort, other.sort) && + Objects.equals(descending, other.descending); + } +} diff --git a/client/rest-high-level/src/main/java/org/elasticsearch/client/ml/GetInfluencersResponse.java b/client/rest-high-level/src/main/java/org/elasticsearch/client/ml/GetInfluencersResponse.java new file mode 100644 index 000000000000..113d960008c7 --- /dev/null +++ b/client/rest-high-level/src/main/java/org/elasticsearch/client/ml/GetInfluencersResponse.java @@ -0,0 +1,78 @@ +/* + * 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.results.Influencer; +import org.elasticsearch.common.ParseField; +import org.elasticsearch.common.xcontent.ConstructingObjectParser; +import org.elasticsearch.common.xcontent.XContentParser; + +import java.io.IOException; +import java.util.List; +import java.util.Objects; + +/** + * A response containing the requested influencers + */ +public class GetInfluencersResponse extends AbstractResultResponse { + + public static final ParseField INFLUENCERS = new ParseField("influencers"); + + @SuppressWarnings("unchecked") + public static final ConstructingObjectParser PARSER = new ConstructingObjectParser<>( + "get_influencers_response", true, a -> new GetInfluencersResponse((List) a[0], (long) a[1])); + + static { + PARSER.declareObjectArray(ConstructingObjectParser.constructorArg(), Influencer.PARSER, INFLUENCERS); + PARSER.declareLong(ConstructingObjectParser.constructorArg(), COUNT); + } + + public static GetInfluencersResponse fromXContent(XContentParser parser) throws IOException { + return PARSER.parse(parser, null); + } + + GetInfluencersResponse(List influencers, long count) { + super(INFLUENCERS, influencers, count); + } + + /** + * The retrieved influencers + * @return the retrieved influencers + */ + public List influencers() { + return results; + } + + @Override + public int hashCode() { + return Objects.hash(count, results); + } + + @Override + public boolean equals(Object obj) { + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + GetInfluencersResponse other = (GetInfluencersResponse) obj; + return count == other.count && Objects.equals(results, other.results); + } +} diff --git a/client/rest-high-level/src/main/java/org/elasticsearch/client/ml/GetOverallBucketsRequest.java b/client/rest-high-level/src/main/java/org/elasticsearch/client/ml/GetOverallBucketsRequest.java index e58b57605e6f..97bde11d8c6c 100644 --- a/client/rest-high-level/src/main/java/org/elasticsearch/client/ml/GetOverallBucketsRequest.java +++ b/client/rest-high-level/src/main/java/org/elasticsearch/client/ml/GetOverallBucketsRequest.java @@ -154,7 +154,7 @@ public class GetOverallBucketsRequest extends ActionRequest implements ToXConten /** * Sets the value of "start" which is a timestamp. * Only overall buckets whose timestamp is on or after the "start" value will be returned. - * @param start value of "start" to be set + * @param start String representation of a timestamp; may be an epoch seconds, epoch millis or an ISO string */ public void setStart(String start) { this.start = start; @@ -167,7 +167,7 @@ public class GetOverallBucketsRequest extends ActionRequest implements ToXConten /** * Sets the value of "end" which is a timestamp. * Only overall buckets whose timestamp is before the "end" value will be returned. - * @param end value of "end" to be set + * @param end String representation of a timestamp; may be an epoch seconds, epoch millis or an ISO string */ public void setEnd(String end) { this.end = end; diff --git a/client/rest-high-level/src/main/java/org/elasticsearch/client/ml/GetRecordsRequest.java b/client/rest-high-level/src/main/java/org/elasticsearch/client/ml/GetRecordsRequest.java index 0a701f5a1433..3c11cbd2c10f 100644 --- a/client/rest-high-level/src/main/java/org/elasticsearch/client/ml/GetRecordsRequest.java +++ b/client/rest-high-level/src/main/java/org/elasticsearch/client/ml/GetRecordsRequest.java @@ -41,7 +41,7 @@ public class GetRecordsRequest implements ToXContentObject, Validatable { public static final ParseField SORT = new ParseField("sort"); public static final ParseField DESCENDING = new ParseField("desc"); - public static final ObjectParser PARSER = new ObjectParser<>("get_buckets_request", GetRecordsRequest::new); + public static final ObjectParser PARSER = new ObjectParser<>("get_records_request", GetRecordsRequest::new); static { PARSER.declareString((request, jobId) -> request.jobId = jobId, Job.ID); @@ -77,7 +77,7 @@ public class GetRecordsRequest implements ToXContentObject, Validatable { return jobId; } - public boolean isExcludeInterim() { + public Boolean isExcludeInterim() { return excludeInterim; } @@ -86,7 +86,7 @@ public class GetRecordsRequest implements ToXContentObject, Validatable { * When {@code true}, interim records will be filtered out. * @param excludeInterim value of "exclude_interim" to be set */ - public void setExcludeInterim(boolean excludeInterim) { + public void setExcludeInterim(Boolean excludeInterim) { this.excludeInterim = excludeInterim; } @@ -97,7 +97,7 @@ public class GetRecordsRequest implements ToXContentObject, Validatable { /** * Sets the value of "start" which is a timestamp. * Only records whose timestamp is on or after the "start" value will be returned. - * @param start value of "start" to be set + * @param start String representation of a timestamp; may be an epoch seconds, epoch millis or an ISO string */ public void setStart(String start) { this.start = start; @@ -110,7 +110,7 @@ public class GetRecordsRequest implements ToXContentObject, Validatable { /** * Sets the value of "end" which is a timestamp. * Only records whose timestamp is before the "end" value will be returned. - * @param end value of "end" to be set + * @param end String representation of a timestamp; may be an epoch seconds, epoch millis or an ISO string */ public void setEnd(String end) { this.end = end; @@ -137,7 +137,7 @@ public class GetRecordsRequest implements ToXContentObject, Validatable { * Only records with "record_score" equal or greater will be returned. * @param recordScore value of "record_score". */ - public void setRecordScore(double recordScore) { + public void setRecordScore(Double recordScore) { this.recordScore = recordScore; } @@ -147,14 +147,14 @@ public class GetRecordsRequest implements ToXContentObject, Validatable { /** * Sets the value of "sort". - * Specifies the bucket field to sort on. + * Specifies the record field to sort on. * @param sort value of "sort". */ public void setSort(String sort) { this.sort = sort; } - public boolean isDescending() { + public Boolean isDescending() { return descending; } @@ -163,7 +163,7 @@ public class GetRecordsRequest implements ToXContentObject, Validatable { * Specifies the sorting order. * @param descending value of "desc" */ - public void setDescending(boolean descending) { + public void setDescending(Boolean descending) { this.descending = descending; } diff --git a/client/rest-high-level/src/main/java/org/elasticsearch/client/ml/GetRecordsResponse.java b/client/rest-high-level/src/main/java/org/elasticsearch/client/ml/GetRecordsResponse.java index 99e115242260..0d8efd5c6ea2 100644 --- a/client/rest-high-level/src/main/java/org/elasticsearch/client/ml/GetRecordsResponse.java +++ b/client/rest-high-level/src/main/java/org/elasticsearch/client/ml/GetRecordsResponse.java @@ -28,7 +28,7 @@ import java.util.List; import java.util.Objects; /** - * A response containing the requested buckets + * A response containing the requested records */ public class GetRecordsResponse extends AbstractResultResponse { @@ -47,8 +47,8 @@ public class GetRecordsResponse extends AbstractResultResponse { return PARSER.parse(parser, null); } - GetRecordsResponse(List buckets, long count) { - super(RECORDS, buckets, count); + GetRecordsResponse(List records, long count) { + super(RECORDS, records, count); } /** diff --git a/client/rest-high-level/src/test/java/org/elasticsearch/client/MLRequestConvertersTests.java b/client/rest-high-level/src/test/java/org/elasticsearch/client/MLRequestConvertersTests.java index bd997224bebd..0822db33505c 100644 --- a/client/rest-high-level/src/test/java/org/elasticsearch/client/MLRequestConvertersTests.java +++ b/client/rest-high-level/src/test/java/org/elasticsearch/client/MLRequestConvertersTests.java @@ -27,6 +27,7 @@ import org.elasticsearch.client.ml.CloseJobRequest; import org.elasticsearch.client.ml.DeleteJobRequest; import org.elasticsearch.client.ml.FlushJobRequest; import org.elasticsearch.client.ml.GetBucketsRequest; +import org.elasticsearch.client.ml.GetInfluencersRequest; import org.elasticsearch.client.ml.GetJobRequest; import org.elasticsearch.client.ml.GetJobStatsRequest; import org.elasticsearch.client.ml.GetOverallBucketsRequest; @@ -220,6 +221,26 @@ public class MLRequestConvertersTests extends ESTestCase { } } + public void testGetInfluencers() throws IOException { + String jobId = randomAlphaOfLength(10); + GetInfluencersRequest getInfluencersRequest = new GetInfluencersRequest(jobId); + getInfluencersRequest.setStart("2018-08-08T00:00:00Z"); + getInfluencersRequest.setEnd("2018-09-08T00:00:00Z"); + getInfluencersRequest.setPageParams(new PageParams(100, 300)); + getInfluencersRequest.setInfluencerScore(75.0); + getInfluencersRequest.setSort("anomaly_score"); + getInfluencersRequest.setDescending(true); + getInfluencersRequest.setExcludeInterim(true); + + Request request = MLRequestConverters.getInfluencers(getInfluencersRequest); + assertEquals(HttpGet.METHOD_NAME, request.getMethod()); + assertEquals("/_xpack/ml/anomaly_detectors/" + jobId + "/results/influencers", request.getEndpoint()); + try (XContentParser parser = createParser(JsonXContent.jsonXContent, request.getEntity().getContent())) { + GetInfluencersRequest parsedRequest = GetInfluencersRequest.PARSER.apply(parser, null); + assertThat(parsedRequest, equalTo(getInfluencersRequest)); + } + } + private static Job createValidJob(String jobId) { AnalysisConfig.Builder analysisConfig = AnalysisConfig.builder(Collections.singletonList( Detector.builder().setFunction("count").build())); diff --git a/client/rest-high-level/src/test/java/org/elasticsearch/client/MachineLearningGetResultsIT.java b/client/rest-high-level/src/test/java/org/elasticsearch/client/MachineLearningGetResultsIT.java index b1c743098db4..40d8596d1ba8 100644 --- a/client/rest-high-level/src/test/java/org/elasticsearch/client/MachineLearningGetResultsIT.java +++ b/client/rest-high-level/src/test/java/org/elasticsearch/client/MachineLearningGetResultsIT.java @@ -23,6 +23,8 @@ import org.elasticsearch.action.index.IndexRequest; import org.elasticsearch.action.support.WriteRequest; import org.elasticsearch.client.ml.GetBucketsRequest; import org.elasticsearch.client.ml.GetBucketsResponse; +import org.elasticsearch.client.ml.GetInfluencersRequest; +import org.elasticsearch.client.ml.GetInfluencersResponse; import org.elasticsearch.client.ml.GetOverallBucketsRequest; import org.elasticsearch.client.ml.GetOverallBucketsResponse; import org.elasticsearch.client.ml.GetRecordsRequest; @@ -34,6 +36,7 @@ import org.elasticsearch.client.ml.job.config.Detector; import org.elasticsearch.client.ml.job.config.Job; import org.elasticsearch.client.ml.job.results.AnomalyRecord; import org.elasticsearch.client.ml.job.results.Bucket; +import org.elasticsearch.client.ml.job.results.Influencer; import org.elasticsearch.client.ml.job.results.OverallBucket; import org.elasticsearch.client.ml.job.util.PageParams; import org.elasticsearch.common.unit.TimeValue; @@ -387,6 +390,106 @@ public class MachineLearningGetResultsIT extends ESRestHighLevelClientTestCase { } } + public void testGetInfluencers() throws IOException { + MachineLearningClient machineLearningClient = highLevelClient().machineLearning(); + + // Let us index a few influencer docs + BulkRequest bulkRequest = new BulkRequest(); + bulkRequest.setRefreshPolicy(WriteRequest.RefreshPolicy.IMMEDIATE); + long timestamp = START_TIME_EPOCH_MS; + long end = START_TIME_EPOCH_MS + 5 * 3600000L; + while (timestamp < end) { + boolean isLast = timestamp == end - 3600000L; + // Last one is interim + boolean isInterim = isLast; + // Last one score is higher + double score = isLast ? 90.0 : 42.0; + + IndexRequest indexRequest = new IndexRequest(RESULTS_INDEX, DOC); + indexRequest.source("{\"job_id\":\"" + JOB_ID + "\", \"result_type\":\"influencer\", \"timestamp\": " + + timestamp + "," + "\"bucket_span\": 3600,\"is_interim\": " + isInterim + ", \"influencer_score\": " + score + ", " + + "\"influencer_field_name\":\"my_influencer\", \"influencer_field_value\": \"inf_1\", \"probability\":" + + randomDouble() + "}", XContentType.JSON); + bulkRequest.add(indexRequest); + timestamp += 3600000L; + } + highLevelClient().bulk(bulkRequest, RequestOptions.DEFAULT); + + { + GetInfluencersRequest request = new GetInfluencersRequest(JOB_ID); + request.setDescending(false); + + GetInfluencersResponse response = execute(request, machineLearningClient::getInfluencers, + machineLearningClient::getInfluencersAsync); + + assertThat(response.count(), equalTo(5L)); + } + { + long requestStart = START_TIME_EPOCH_MS + 3600000L; + long requestEnd = end - 3600000L; + GetInfluencersRequest request = new GetInfluencersRequest(JOB_ID); + request.setStart(String.valueOf(requestStart)); + request.setEnd(String.valueOf(requestEnd)); + + GetInfluencersResponse response = execute(request, machineLearningClient::getInfluencers, + machineLearningClient::getInfluencersAsync); + + assertThat(response.count(), equalTo(3L)); + for (Influencer influencer : response.influencers()) { + assertThat(influencer.getTimestamp().getTime(), greaterThanOrEqualTo(START_TIME_EPOCH_MS)); + assertThat(influencer.getTimestamp().getTime(), lessThan(end)); + } + } + { + GetInfluencersRequest request = new GetInfluencersRequest(JOB_ID); + request.setSort("timestamp"); + request.setDescending(false); + request.setPageParams(new PageParams(1, 2)); + + GetInfluencersResponse response = execute(request, machineLearningClient::getInfluencers, + machineLearningClient::getInfluencersAsync); + + assertThat(response.influencers().size(), equalTo(2)); + assertThat(response.influencers().get(0).getTimestamp().getTime(), equalTo(START_TIME_EPOCH_MS + 3600000L)); + assertThat(response.influencers().get(1).getTimestamp().getTime(), equalTo(START_TIME_EPOCH_MS + 2 * 3600000L)); + } + { + GetInfluencersRequest request = new GetInfluencersRequest(JOB_ID); + request.setExcludeInterim(true); + + GetInfluencersResponse response = execute(request, machineLearningClient::getInfluencers, + machineLearningClient::getInfluencersAsync); + + assertThat(response.count(), equalTo(4L)); + assertThat(response.influencers().stream().anyMatch(Influencer::isInterim), is(false)); + } + { + GetInfluencersRequest request = new GetInfluencersRequest(JOB_ID); + request.setInfluencerScore(75.0); + + GetInfluencersResponse response = execute(request, machineLearningClient::getInfluencers, + machineLearningClient::getInfluencersAsync); + + assertThat(response.count(), equalTo(1L)); + assertThat(response.influencers().get(0).getInfluencerScore(), greaterThanOrEqualTo(75.0)); + } + { + GetInfluencersRequest request = new GetInfluencersRequest(JOB_ID); + request.setSort("probability"); + request.setDescending(true); + + GetInfluencersResponse response = execute(request, machineLearningClient::getInfluencers, + machineLearningClient::getInfluencersAsync); + + assertThat(response.influencers().size(), equalTo(5)); + double previousProb = 1.0; + for (Influencer influencer : response.influencers()) { + assertThat(influencer.getProbability(), lessThanOrEqualTo(previousProb)); + previousProb = influencer.getProbability(); + } + } + } + public static Job buildJob(String jobId) { Job.Builder builder = new Job.Builder(jobId); diff --git a/client/rest-high-level/src/test/java/org/elasticsearch/client/documentation/MlClientDocumentationIT.java b/client/rest-high-level/src/test/java/org/elasticsearch/client/documentation/MlClientDocumentationIT.java index 427f75a80d02..2e1fc6c2711d 100644 --- a/client/rest-high-level/src/test/java/org/elasticsearch/client/documentation/MlClientDocumentationIT.java +++ b/client/rest-high-level/src/test/java/org/elasticsearch/client/documentation/MlClientDocumentationIT.java @@ -37,6 +37,8 @@ import org.elasticsearch.client.ml.FlushJobRequest; import org.elasticsearch.client.ml.FlushJobResponse; import org.elasticsearch.client.ml.GetBucketsRequest; import org.elasticsearch.client.ml.GetBucketsResponse; +import org.elasticsearch.client.ml.GetInfluencersRequest; +import org.elasticsearch.client.ml.GetInfluencersResponse; import org.elasticsearch.client.ml.GetJobRequest; import org.elasticsearch.client.ml.GetJobResponse; import org.elasticsearch.client.ml.GetJobStatsRequest; @@ -55,6 +57,7 @@ import org.elasticsearch.client.ml.job.config.Detector; import org.elasticsearch.client.ml.job.config.Job; import org.elasticsearch.client.ml.job.results.AnomalyRecord; import org.elasticsearch.client.ml.job.results.Bucket; +import org.elasticsearch.client.ml.job.results.Influencer; import org.elasticsearch.client.ml.job.results.OverallBucket; import org.elasticsearch.client.ml.job.stats.JobStats; import org.elasticsearch.client.ml.job.util.PageParams; @@ -781,4 +784,95 @@ public class MlClientDocumentationIT extends ESRestHighLevelClientTestCase { assertTrue(latch.await(30L, TimeUnit.SECONDS)); } } + + public void testGetInfluencers() throws IOException, InterruptedException { + RestHighLevelClient client = highLevelClient(); + + String jobId = "test-get-influencers"; + Job job = MachineLearningIT.buildJob(jobId); + client.machineLearning().putJob(new PutJobRequest(job), RequestOptions.DEFAULT); + + // Let us index a record + IndexRequest indexRequest = new IndexRequest(".ml-anomalies-shared", "doc"); + indexRequest.setRefreshPolicy(WriteRequest.RefreshPolicy.IMMEDIATE); + indexRequest.source("{\"job_id\":\"test-get-influencers\", \"result_type\":\"influencer\", \"timestamp\": 1533081600000," + + "\"bucket_span\": 600,\"is_interim\": false, \"influencer_score\": 80.0, \"influencer_field_name\": \"my_influencer\"," + + "\"influencer_field_value\":\"foo\"}", XContentType.JSON); + client.index(indexRequest, RequestOptions.DEFAULT); + + { + // tag::x-pack-ml-get-influencers-request + GetInfluencersRequest request = new GetInfluencersRequest(jobId); // <1> + // end::x-pack-ml-get-influencers-request + + // tag::x-pack-ml-get-influencers-desc + request.setDescending(true); // <1> + // end::x-pack-ml-get-influencers-desc + + // tag::x-pack-ml-get-influencers-end + request.setEnd("2018-08-21T00:00:00Z"); // <1> + // end::x-pack-ml-get-influencers-end + + // tag::x-pack-ml-get-influencers-exclude-interim + request.setExcludeInterim(true); // <1> + // end::x-pack-ml-get-influencers-exclude-interim + + // tag::x-pack-ml-get-influencers-influencer-score + request.setInfluencerScore(75.0); // <1> + // end::x-pack-ml-get-influencers-influencer-score + + // tag::x-pack-ml-get-influencers-page + request.setPageParams(new PageParams(100, 200)); // <1> + // end::x-pack-ml-get-influencers-page + + // Set page params back to null so the response contains the influencer we indexed + request.setPageParams(null); + + // tag::x-pack-ml-get-influencers-sort + request.setSort("probability"); // <1> + // end::x-pack-ml-get-influencers-sort + + // tag::x-pack-ml-get-influencers-start + request.setStart("2018-08-01T00:00:00Z"); // <1> + // end::x-pack-ml-get-influencers-start + + // tag::x-pack-ml-get-influencers-execute + GetInfluencersResponse response = client.machineLearning().getInfluencers(request, RequestOptions.DEFAULT); + // end::x-pack-ml-get-influencers-execute + + // tag::x-pack-ml-get-influencers-response + long count = response.count(); // <1> + List influencers = response.influencers(); // <2> + // end::x-pack-ml-get-influencers-response + assertEquals(1, influencers.size()); + } + { + GetInfluencersRequest request = new GetInfluencersRequest(jobId); + + // tag::x-pack-ml-get-influencers-listener + ActionListener listener = + new ActionListener() { + @Override + public void onResponse(GetInfluencersResponse getInfluencersResponse) { + // <1> + } + + @Override + public void onFailure(Exception e) { + // <2> + } + }; + // end::x-pack-ml-get-influencers-listener + + // Replace the empty listener by a blocking listener in test + final CountDownLatch latch = new CountDownLatch(1); + listener = new LatchedActionListener<>(listener, latch); + + // tag::x-pack-ml-get-influencers-execute-async + client.machineLearning().getInfluencersAsync(request, RequestOptions.DEFAULT, listener); // <1> + // end::x-pack-ml-get-influencers-execute-async + + assertTrue(latch.await(30L, TimeUnit.SECONDS)); + } + } } diff --git a/client/rest-high-level/src/test/java/org/elasticsearch/client/ml/GetInfluencersRequestTests.java b/client/rest-high-level/src/test/java/org/elasticsearch/client/ml/GetInfluencersRequestTests.java new file mode 100644 index 000000000000..94937cd78155 --- /dev/null +++ b/client/rest-high-level/src/test/java/org/elasticsearch/client/ml/GetInfluencersRequestTests.java @@ -0,0 +1,71 @@ +/* + * 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.util.PageParams; +import org.elasticsearch.common.xcontent.XContentParser; +import org.elasticsearch.test.AbstractXContentTestCase; + +import java.io.IOException; + +public class GetInfluencersRequestTests extends AbstractXContentTestCase { + + @Override + protected GetInfluencersRequest createTestInstance() { + GetInfluencersRequest request = new GetInfluencersRequest(randomAlphaOfLengthBetween(1, 20)); + + if (randomBoolean()) { + request.setStart(String.valueOf(randomLong())); + } + if (randomBoolean()) { + request.setEnd(String.valueOf(randomLong())); + } + if (randomBoolean()) { + request.setExcludeInterim(randomBoolean()); + } + if (randomBoolean()) { + request.setInfluencerScore(randomDouble()); + } + if (randomBoolean()) { + int from = randomInt(10000); + int size = randomInt(10000); + request.setPageParams(new PageParams(from, size)); + } + if (randomBoolean()) { + request.setSort("influencer_score"); + } + if (randomBoolean()) { + request.setDescending(randomBoolean()); + } + if (randomBoolean()) { + request.setExcludeInterim(randomBoolean()); + } + return request; + } + + @Override + protected GetInfluencersRequest doParseInstance(XContentParser parser) throws IOException { + return GetInfluencersRequest.PARSER.apply(parser, null); + } + + @Override + protected boolean supportsUnknownFields() { + return false; + } +} diff --git a/client/rest-high-level/src/test/java/org/elasticsearch/client/ml/GetInfluencersResponseTests.java b/client/rest-high-level/src/test/java/org/elasticsearch/client/ml/GetInfluencersResponseTests.java new file mode 100644 index 000000000000..5f1fa3c1ab5e --- /dev/null +++ b/client/rest-high-level/src/test/java/org/elasticsearch/client/ml/GetInfluencersResponseTests.java @@ -0,0 +1,53 @@ +/* + * 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.results.Influencer; +import org.elasticsearch.client.ml.job.results.InfluencerTests; +import org.elasticsearch.common.xcontent.XContentParser; +import org.elasticsearch.test.AbstractXContentTestCase; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +public class GetInfluencersResponseTests extends AbstractXContentTestCase { + + @Override + protected GetInfluencersResponse createTestInstance() { + String jobId = randomAlphaOfLength(20); + int listSize = randomInt(10); + List influencers = new ArrayList<>(listSize); + for (int j = 0; j < listSize; j++) { + Influencer influencer = InfluencerTests.createTestInstance(jobId); + influencers.add(influencer); + } + return new GetInfluencersResponse(influencers, listSize); + } + + @Override + protected GetInfluencersResponse doParseInstance(XContentParser parser) throws IOException { + return GetInfluencersResponse.fromXContent(parser); + } + + @Override + protected boolean supportsUnknownFields() { + return true; + } +} diff --git a/client/rest-high-level/src/test/java/org/elasticsearch/client/ml/GetRecordsRequestTests.java b/client/rest-high-level/src/test/java/org/elasticsearch/client/ml/GetRecordsRequestTests.java index 226ffe75b01e..f6f4b49889a4 100644 --- a/client/rest-high-level/src/test/java/org/elasticsearch/client/ml/GetRecordsRequestTests.java +++ b/client/rest-high-level/src/test/java/org/elasticsearch/client/ml/GetRecordsRequestTests.java @@ -21,7 +21,6 @@ package org.elasticsearch.client.ml; import org.elasticsearch.client.ml.job.util.PageParams; import org.elasticsearch.common.xcontent.XContentParser; import org.elasticsearch.test.AbstractXContentTestCase; -import org.elasticsearch.test.ESTestCase; import java.io.IOException; @@ -29,33 +28,33 @@ public class GetRecordsRequestTests extends AbstractXContentTestCase records = new ArrayList<>(listSize); for (int j = 0; j < listSize; j++) { AnomalyRecord record = AnomalyRecordTests.createTestInstance(jobId); diff --git a/client/rest-high-level/src/test/java/org/elasticsearch/client/ml/job/results/InfluencerTests.java b/client/rest-high-level/src/test/java/org/elasticsearch/client/ml/job/results/InfluencerTests.java index ef83af39958d..33d1a33e9f15 100644 --- a/client/rest-high-level/src/test/java/org/elasticsearch/client/ml/job/results/InfluencerTests.java +++ b/client/rest-high-level/src/test/java/org/elasticsearch/client/ml/job/results/InfluencerTests.java @@ -29,7 +29,7 @@ import java.util.Date; public class InfluencerTests extends AbstractXContentTestCase { - public Influencer createTestInstance(String jobId) { + public static Influencer createTestInstance(String jobId) { Influencer influencer = new Influencer(jobId, randomAlphaOfLengthBetween(1, 20), randomAlphaOfLengthBetween(1, 20), new Date(randomNonNegativeLong()), randomNonNegativeLong()); influencer.setInterim(randomBoolean()); diff --git a/docs/java-rest/high-level/ml/get-influencers.asciidoc b/docs/java-rest/high-level/ml/get-influencers.asciidoc new file mode 100644 index 000000000000..e53e92ff1df0 --- /dev/null +++ b/docs/java-rest/high-level/ml/get-influencers.asciidoc @@ -0,0 +1,112 @@ +[[java-rest-high-x-pack-ml-get-influencers]] +=== Get Influencers API + +The Get Influencers API retrieves one or more influencer results. +It accepts a `GetInfluencersRequest` object and responds +with a `GetInfluencersResponse` object. + +[[java-rest-high-x-pack-ml-get-influencers-request]] +==== Get Influencers Request + +A `GetInfluencersRequest` object gets created with an existing non-null `jobId`. + +["source","java",subs="attributes,callouts,macros"] +-------------------------------------------------- +include-tagged::{doc-tests}/MlClientDocumentationIT.java[x-pack-ml-get-influencers-request] +-------------------------------------------------- +<1> Constructing a new request referencing an existing `jobId` + +==== Optional Arguments +The following arguments are optional: + +["source","java",subs="attributes,callouts,macros"] +-------------------------------------------------- +include-tagged::{doc-tests}/MlClientDocumentationIT.java[x-pack-ml-get-influencers-desc] +-------------------------------------------------- +<1> If `true`, the influencers are sorted in descending order. Defaults to `false`. + +["source","java",subs="attributes,callouts,macros"] +-------------------------------------------------- +include-tagged::{doc-tests}/MlClientDocumentationIT.java[x-pack-ml-get-influencers-end] +-------------------------------------------------- +<1> Influencers with timestamps earlier than this time will be returned. + +["source","java",subs="attributes,callouts,macros"] +-------------------------------------------------- +include-tagged::{doc-tests}/MlClientDocumentationIT.java[x-pack-ml-get-influencers-exclude-interim] +-------------------------------------------------- +<1> If `true`, interim results will be excluded. Defaults to `false`. + +["source","java",subs="attributes,callouts,macros"] +-------------------------------------------------- +include-tagged::{doc-tests}/MlClientDocumentationIT.java[x-pack-ml-get-influencers-influencer-score] +-------------------------------------------------- +<1> Influencers with influencer_score greater or equal than this value will be returned. + +["source","java",subs="attributes,callouts,macros"] +-------------------------------------------------- +include-tagged::{doc-tests}/MlClientDocumentationIT.java[x-pack-ml-get-influencers-page] +-------------------------------------------------- +<1> The page parameters `from` and `size`. `from` specifies the number of influencers to skip. +`size` specifies the maximum number of influencers to get. Defaults to `0` and `100` respectively. + +["source","java",subs="attributes,callouts,macros"] +-------------------------------------------------- +include-tagged::{doc-tests}/MlClientDocumentationIT.java[x-pack-ml-get-influencers-sort] +-------------------------------------------------- +<1> The field to sort influencers on. Defaults to `influencer_score`. + +["source","java",subs="attributes,callouts,macros"] +-------------------------------------------------- +include-tagged::{doc-tests}/MlClientDocumentationIT.java[x-pack-ml-get-influencers-start] +-------------------------------------------------- +<1> Influencers with timestamps on or after this time will be returned. + +[[java-rest-high-x-pack-ml-get-influencers-execution]] +==== Execution + +The request can be executed through the `MachineLearningClient` contained +in the `RestHighLevelClient` object, accessed via the `machineLearningClient()` method. + +["source","java",subs="attributes,callouts,macros"] +-------------------------------------------------- +include-tagged::{doc-tests}/MlClientDocumentationIT.java[x-pack-ml-get-influencers-execute] +-------------------------------------------------- + +[[java-rest-high-x-pack-ml-get-influencers-execution-async]] +==== Asynchronous Execution + +The request can also be executed asynchronously: + +["source","java",subs="attributes,callouts,macros"] +-------------------------------------------------- +include-tagged::{doc-tests}/MlClientDocumentationIT.java[x-pack-ml-get-influencers-execute-async] +-------------------------------------------------- +<1> The `GetInfluencersRequest` to execute and the `ActionListener` to use when +the execution completes + +The asynchronous method does not block and returns immediately. Once it is +completed the `ActionListener` is called back with the `onResponse` method +if the execution is successful or the `onFailure` method if the execution +failed. + +A typical listener for `GetInfluencersResponse` looks like: + +["source","java",subs="attributes,callouts,macros"] +-------------------------------------------------- +include-tagged::{doc-tests}/MlClientDocumentationIT.java[x-pack-ml-get-influencers-listener] +-------------------------------------------------- +<1> `onResponse` is called back when the action is completed successfully +<2> `onFailure` is called back when some unexpected error occurs + +[[java-rest-high-snapshot-ml-get-influencers-response]] +==== Get Influencers Response + +The returned `GetInfluencersResponse` contains the requested influencers: + +["source","java",subs="attributes,callouts,macros"] +-------------------------------------------------- +include-tagged::{doc-tests}/MlClientDocumentationIT.java[x-pack-ml-get-influencers-response] +-------------------------------------------------- +<1> The count of influencers that were matched +<2> The influencers retrieved \ No newline at end of file diff --git a/docs/java-rest/high-level/supported-apis.asciidoc b/docs/java-rest/high-level/supported-apis.asciidoc index 481a2470aa2d..f01e12c4c981 100644 --- a/docs/java-rest/high-level/supported-apis.asciidoc +++ b/docs/java-rest/high-level/supported-apis.asciidoc @@ -220,6 +220,7 @@ The Java High Level REST Client supports the following Machine Learning APIs: * <> * <> * <> +* <> include::ml/put-job.asciidoc[] include::ml/get-job.asciidoc[] @@ -231,6 +232,7 @@ include::ml/get-job-stats.asciidoc[] include::ml/get-buckets.asciidoc[] include::ml/get-overall-buckets.asciidoc[] include::ml/get-records.asciidoc[] +include::ml/get-influencers.asciidoc[] == Migration APIs