HLRC: Add ML get overall buckets API (#33297)

Relates #29827
This commit is contained in:
Dimitris Athanasiou 2018-09-03 13:34:36 +01:00 committed by GitHub
parent 246a7df8c2
commit 54fe7fb5a1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 1051 additions and 142 deletions

View File

@ -26,14 +26,15 @@ import org.apache.http.client.methods.HttpPut;
import org.elasticsearch.client.RequestConverters.EndpointBuilder; import org.elasticsearch.client.RequestConverters.EndpointBuilder;
import org.elasticsearch.client.ml.CloseJobRequest; import org.elasticsearch.client.ml.CloseJobRequest;
import org.elasticsearch.client.ml.DeleteJobRequest; import org.elasticsearch.client.ml.DeleteJobRequest;
import org.elasticsearch.client.ml.FlushJobRequest;
import org.elasticsearch.client.ml.GetBucketsRequest; import org.elasticsearch.client.ml.GetBucketsRequest;
import org.elasticsearch.client.ml.GetJobRequest; import org.elasticsearch.client.ml.GetJobRequest;
import org.elasticsearch.client.ml.GetJobStatsRequest; import org.elasticsearch.client.ml.GetJobStatsRequest;
import org.elasticsearch.client.ml.GetOverallBucketsRequest;
import org.elasticsearch.client.ml.GetRecordsRequest; import org.elasticsearch.client.ml.GetRecordsRequest;
import org.elasticsearch.client.ml.OpenJobRequest; import org.elasticsearch.client.ml.OpenJobRequest;
import org.elasticsearch.client.ml.PutJobRequest; import org.elasticsearch.client.ml.PutJobRequest;
import org.elasticsearch.common.Strings; import org.elasticsearch.common.Strings;
import org.elasticsearch.client.ml.FlushJobRequest;
import java.io.IOException; import java.io.IOException;
@ -73,6 +74,23 @@ final class MLRequestConverters {
return request; return request;
} }
static Request getJobStats(GetJobStatsRequest getJobStatsRequest) {
String endpoint = new EndpointBuilder()
.addPathPartAsIs("_xpack")
.addPathPartAsIs("ml")
.addPathPartAsIs("anomaly_detectors")
.addPathPart(Strings.collectionToCommaDelimitedString(getJobStatsRequest.getJobIds()))
.addPathPartAsIs("_stats")
.build();
Request request = new Request(HttpGet.METHOD_NAME, endpoint);
RequestConverters.Params params = new RequestConverters.Params(request);
if (getJobStatsRequest.isAllowNoJobs() != null) {
params.putParam("allow_no_jobs", Boolean.toString(getJobStatsRequest.isAllowNoJobs()));
}
return request;
}
static Request openJob(OpenJobRequest openJobRequest) throws IOException { static Request openJob(OpenJobRequest openJobRequest) throws IOException {
String endpoint = new EndpointBuilder() String endpoint = new EndpointBuilder()
.addPathPartAsIs("_xpack") .addPathPartAsIs("_xpack")
@ -114,6 +132,19 @@ final class MLRequestConverters {
return request; return request;
} }
static Request flushJob(FlushJobRequest flushJobRequest) throws IOException {
String endpoint = new EndpointBuilder()
.addPathPartAsIs("_xpack")
.addPathPartAsIs("ml")
.addPathPartAsIs("anomaly_detectors")
.addPathPart(flushJobRequest.getJobId())
.addPathPartAsIs("_flush")
.build();
Request request = new Request(HttpPost.METHOD_NAME, endpoint);
request.setEntity(createEntity(flushJobRequest, REQUEST_BODY_CONTENT_TYPE));
return request;
}
static Request getBuckets(GetBucketsRequest getBucketsRequest) throws IOException { static Request getBuckets(GetBucketsRequest getBucketsRequest) throws IOException {
String endpoint = new EndpointBuilder() String endpoint = new EndpointBuilder()
.addPathPartAsIs("_xpack") .addPathPartAsIs("_xpack")
@ -128,33 +159,17 @@ final class MLRequestConverters {
return request; return request;
} }
static Request flushJob(FlushJobRequest flushJobRequest) throws IOException { static Request getOverallBuckets(GetOverallBucketsRequest getOverallBucketsRequest) throws IOException {
String endpoint = new EndpointBuilder() String endpoint = new EndpointBuilder()
.addPathPartAsIs("_xpack") .addPathPartAsIs("_xpack")
.addPathPartAsIs("ml") .addPathPartAsIs("ml")
.addPathPartAsIs("anomaly_detectors") .addPathPartAsIs("anomaly_detectors")
.addPathPart(flushJobRequest.getJobId()) .addPathPart(Strings.collectionToCommaDelimitedString(getOverallBucketsRequest.getJobIds()))
.addPathPartAsIs("_flush") .addPathPartAsIs("results")
.build(); .addPathPartAsIs("overall_buckets")
Request request = new Request(HttpPost.METHOD_NAME, endpoint); .build();
request.setEntity(createEntity(flushJobRequest, REQUEST_BODY_CONTENT_TYPE));
return request;
}
static Request getJobStats(GetJobStatsRequest getJobStatsRequest) {
String endpoint = new EndpointBuilder()
.addPathPartAsIs("_xpack")
.addPathPartAsIs("ml")
.addPathPartAsIs("anomaly_detectors")
.addPathPart(Strings.collectionToCommaDelimitedString(getJobStatsRequest.getJobIds()))
.addPathPartAsIs("_stats")
.build();
Request request = new Request(HttpGet.METHOD_NAME, endpoint); Request request = new Request(HttpGet.METHOD_NAME, endpoint);
request.setEntity(createEntity(getOverallBucketsRequest, REQUEST_BODY_CONTENT_TYPE));
RequestConverters.Params params = new RequestConverters.Params(request);
if (getJobStatsRequest.isAllowNoJobs() != null) {
params.putParam("allow_no_jobs", Boolean.toString(getJobStatsRequest.isAllowNoJobs()));
}
return request; return request;
} }

View File

@ -32,6 +32,8 @@ import org.elasticsearch.client.ml.GetBucketsRequest;
import org.elasticsearch.client.ml.GetBucketsResponse; import org.elasticsearch.client.ml.GetBucketsResponse;
import org.elasticsearch.client.ml.GetJobRequest; import org.elasticsearch.client.ml.GetJobRequest;
import org.elasticsearch.client.ml.GetJobResponse; import org.elasticsearch.client.ml.GetJobResponse;
import org.elasticsearch.client.ml.GetOverallBucketsRequest;
import org.elasticsearch.client.ml.GetOverallBucketsResponse;
import org.elasticsearch.client.ml.GetRecordsRequest; import org.elasticsearch.client.ml.GetRecordsRequest;
import org.elasticsearch.client.ml.GetRecordsResponse; import org.elasticsearch.client.ml.GetRecordsResponse;
import org.elasticsearch.client.ml.OpenJobRequest; import org.elasticsearch.client.ml.OpenJobRequest;
@ -136,6 +138,47 @@ public final class MachineLearningClient {
Collections.emptySet()); Collections.emptySet());
} }
/**
* Gets usage statistics for one or more Machine Learning jobs
*
* <p>
* For additional info
* see <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/ml-get-job-stats.html">Get Job stats docs</a>
* </p>
* @param request {@link GetJobStatsRequest} Request containing a list of jobId(s) and additional options
* @param options Additional request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized
* @return {@link GetJobStatsResponse} response object containing
* the {@link JobStats} objects and the number of jobs found
* @throws IOException when there is a serialization issue sending the request or receiving the response
*/
public GetJobStatsResponse getJobStats(GetJobStatsRequest request, RequestOptions options) throws IOException {
return restHighLevelClient.performRequestAndParseEntity(request,
MLRequestConverters::getJobStats,
options,
GetJobStatsResponse::fromXContent,
Collections.emptySet());
}
/**
* Gets one or more Machine Learning job configuration info, asynchronously.
*
* <p>
* For additional info
* see <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/ml-get-job-stats.html">Get Job stats docs</a>
* </p>
* @param request {@link GetJobStatsRequest} Request containing a list of jobId(s) and additional options
* @param options Additional request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized
* @param listener Listener to be notified with {@link GetJobStatsResponse} upon request completion
*/
public void getJobStatsAsync(GetJobStatsRequest request, RequestOptions options, ActionListener<GetJobStatsResponse> listener) {
restHighLevelClient.performRequestAsyncAndParseEntity(request,
MLRequestConverters::getJobStats,
options,
GetJobStatsResponse::fromXContent,
listener,
Collections.emptySet());
}
/** /**
* Deletes the given Machine Learning Job * Deletes the given Machine Learning Job
* <p> * <p>
@ -257,6 +300,60 @@ public final class MachineLearningClient {
Collections.emptySet()); Collections.emptySet());
} }
/**
* Flushes internally buffered data for the given Machine Learning Job ensuring all data sent to the has been processed.
* This may cause new results to be calculated depending on the contents of the buffer
*
* Both flush and close operations are similar,
* however the flush is more efficient if you are expecting to send more data for analysis.
*
* When flushing, the job remains open and is available to continue analyzing data.
* A close operation additionally prunes and persists the model state to disk and the
* job must be opened again before analyzing further data.
*
* <p>
* For additional info
* see <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/ml-flush-job.html">Flush ML job documentation</a>
*
* @param request The {@link FlushJobRequest} object enclosing the `jobId` and additional request options
* @param options Additional request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized
*/
public FlushJobResponse flushJob(FlushJobRequest request, RequestOptions options) throws IOException {
return restHighLevelClient.performRequestAndParseEntity(request,
MLRequestConverters::flushJob,
options,
FlushJobResponse::fromXContent,
Collections.emptySet());
}
/**
* Flushes internally buffered data for the given Machine Learning Job asynchronously ensuring all data sent to the has been processed.
* This may cause new results to be calculated depending on the contents of the buffer
*
* Both flush and close operations are similar,
* however the flush is more efficient if you are expecting to send more data for analysis.
*
* When flushing, the job remains open and is available to continue analyzing data.
* A close operation additionally prunes and persists the model state to disk and the
* job must be opened again before analyzing further data.
*
* <p>
* For additional info
* see <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/ml-flush-job.html">Flush ML job documentation</a>
*
* @param request The {@link FlushJobRequest} object enclosing the `jobId` and additional request options
* @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 flushJobAsync(FlushJobRequest request, RequestOptions options, ActionListener<FlushJobResponse> listener) {
restHighLevelClient.performRequestAsyncAndParseEntity(request,
MLRequestConverters::flushJob,
options,
FlushJobResponse::fromXContent,
listener,
Collections.emptySet());
}
/** /**
* Gets the buckets for a Machine Learning Job. * Gets the buckets for a Machine Learning Job.
* <p> * <p>
@ -294,98 +391,42 @@ public final class MachineLearningClient {
} }
/** /**
* Flushes internally buffered data for the given Machine Learning Job ensuring all data sent to the has been processed. * Gets overall buckets for a set of Machine Learning Jobs.
* This may cause new results to be calculated depending on the contents of the buffer
*
* Both flush and close operations are similar,
* however the flush is more efficient if you are expecting to send more data for analysis.
*
* When flushing, the job remains open and is available to continue analyzing data.
* A close operation additionally prunes and persists the model state to disk and the
* job must be opened again before analyzing further data.
*
* <p> * <p>
* For additional info * For additional info
* see <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/ml-flush-job.html">Flush ML job documentation</a> * see <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/ml-get-overall-buckets.html">
* ML GET overall buckets documentation</a>
* *
* @param request The {@link FlushJobRequest} object enclosing the `jobId` and additional request options * @param request The request
* @param options Additional request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized * @param options Additional request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized
*/ */
public FlushJobResponse flushJob(FlushJobRequest request, RequestOptions options) throws IOException { public GetOverallBucketsResponse getOverallBuckets(GetOverallBucketsRequest request, RequestOptions options) throws IOException {
return restHighLevelClient.performRequestAndParseEntity(request, return restHighLevelClient.performRequestAndParseEntity(request,
MLRequestConverters::flushJob, MLRequestConverters::getOverallBuckets,
options, options,
FlushJobResponse::fromXContent, GetOverallBucketsResponse::fromXContent,
Collections.emptySet()); Collections.emptySet());
}
/**
* Flushes internally buffered data for the given Machine Learning Job asynchronously ensuring all data sent to the has been processed.
* This may cause new results to be calculated depending on the contents of the buffer
*
* Both flush and close operations are similar,
* however the flush is more efficient if you are expecting to send more data for analysis.
*
* When flushing, the job remains open and is available to continue analyzing data.
* A close operation additionally prunes and persists the model state to disk and the
* job must be opened again before analyzing further data.
*
* <p>
* For additional info
* see <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/ml-flush-job.html">Flush ML job documentation</a>
*
* @param request The {@link FlushJobRequest} object enclosing the `jobId` and additional request options
* @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 flushJobAsync(FlushJobRequest request, RequestOptions options, ActionListener<FlushJobResponse> listener) {
restHighLevelClient.performRequestAsyncAndParseEntity(request,
MLRequestConverters::flushJob,
options,
FlushJobResponse::fromXContent,
listener,
Collections.emptySet());
}
/**
* Gets usage statistics for one or more Machine Learning jobs
*
* <p>
* For additional info
* see <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/ml-get-job-stats.html">Get Job stats docs</a>
* </p>
* @param request {@link GetJobStatsRequest} Request containing a list of jobId(s) and additional options
* @param options Additional request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized
* @return {@link GetJobStatsResponse} response object containing
* the {@link JobStats} objects and the number of jobs found
* @throws IOException when there is a serialization issue sending the request or receiving the response
*/
public GetJobStatsResponse getJobStats(GetJobStatsRequest request, RequestOptions options) throws IOException {
return restHighLevelClient.performRequestAndParseEntity(request,
MLRequestConverters::getJobStats,
options,
GetJobStatsResponse::fromXContent,
Collections.emptySet());
} }
/** /**
* Gets one or more Machine Learning job configuration info, asynchronously. * Gets overall buckets for a set of Machine Learning Jobs, notifies listener once the requested buckets are retrieved.
*
* <p> * <p>
* For additional info * For additional info
* see <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/ml-get-job-stats.html">Get Job stats docs</a> * see <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/ml-get-overall-buckets.html">
* </p> * ML GET overall buckets documentation</a>
* @param request {@link GetJobStatsRequest} Request containing a list of jobId(s) and additional options *
* @param request The request
* @param options Additional request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized * @param options Additional request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized
* @param listener Listener to be notified with {@link GetJobStatsResponse} upon request completion * @param listener Listener to be notified upon request completion
*/ */
public void getJobStatsAsync(GetJobStatsRequest request, RequestOptions options, ActionListener<GetJobStatsResponse> listener) { public void getOverallBucketsAsync(GetOverallBucketsRequest request, RequestOptions options,
ActionListener<GetOverallBucketsResponse> listener) {
restHighLevelClient.performRequestAsyncAndParseEntity(request, restHighLevelClient.performRequestAsyncAndParseEntity(request,
MLRequestConverters::getJobStats, MLRequestConverters::getOverallBuckets,
options, options,
GetJobStatsResponse::fromXContent, GetOverallBucketsResponse::fromXContent,
listener, listener,
Collections.emptySet()); Collections.emptySet());
} }
/** /**

View File

@ -0,0 +1,266 @@
/*
* 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.common.ParseField;
import org.elasticsearch.common.Strings;
import org.elasticsearch.common.unit.TimeValue;
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.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
/**
* A request to retrieve overall buckets of set of jobs
*/
public class GetOverallBucketsRequest extends ActionRequest implements ToXContentObject {
public static final ParseField TOP_N = new ParseField("top_n");
public static final ParseField BUCKET_SPAN = new ParseField("bucket_span");
public static final ParseField OVERALL_SCORE = new ParseField("overall_score");
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 ALLOW_NO_JOBS = new ParseField("allow_no_jobs");
private static final String ALL_JOBS = "_all";
@SuppressWarnings("unchecked")
public static final ConstructingObjectParser<GetOverallBucketsRequest, Void> PARSER = new ConstructingObjectParser<>(
"get_overall_buckets_request", a -> new GetOverallBucketsRequest((String) a[0]));
static {
PARSER.declareString(ConstructingObjectParser.constructorArg(), Job.ID);
PARSER.declareInt(GetOverallBucketsRequest::setTopN, TOP_N);
PARSER.declareString(GetOverallBucketsRequest::setBucketSpan, BUCKET_SPAN);
PARSER.declareBoolean(GetOverallBucketsRequest::setExcludeInterim, EXCLUDE_INTERIM);
PARSER.declareDouble(GetOverallBucketsRequest::setOverallScore, OVERALL_SCORE);
PARSER.declareStringOrNull(GetOverallBucketsRequest::setStart, START);
PARSER.declareStringOrNull(GetOverallBucketsRequest::setEnd, END);
PARSER.declareBoolean(GetOverallBucketsRequest::setAllowNoJobs, ALLOW_NO_JOBS);
}
private final List<String> jobIds;
private Integer topN;
private TimeValue bucketSpan;
private Boolean excludeInterim;
private Double overallScore;
private String start;
private String end;
private Boolean allowNoJobs;
private GetOverallBucketsRequest(String jobId) {
this(Strings.tokenizeToStringArray(jobId, ","));
}
/**
* Constructs a request to retrieve overall buckets for a set of jobs
* @param jobIds The job identifiers. Each can be a job identifier, a group name, or a wildcard expression.
*/
public GetOverallBucketsRequest(String... jobIds) {
this(Arrays.asList(jobIds));
}
/**
* Constructs a request to retrieve overall buckets for a set of jobs
* @param jobIds The job identifiers. Each can be a job identifier, a group name, or a wildcard expression.
*/
public GetOverallBucketsRequest(List<String> jobIds) {
if (jobIds.stream().anyMatch(Objects::isNull)) {
throw new NullPointerException("jobIds must not contain null values");
}
if (jobIds.isEmpty()) {
this.jobIds = Collections.singletonList(ALL_JOBS);
} else {
this.jobIds = Collections.unmodifiableList(jobIds);
}
}
public List<String> getJobIds() {
return jobIds;
}
public Integer getTopN() {
return topN;
}
/**
* Sets the value of `top_n`.
* @param topN The number of top job bucket scores to be used in the overall_score calculation. Defaults to 1.
*/
public void setTopN(Integer topN) {
this.topN = topN;
}
public TimeValue getBucketSpan() {
return bucketSpan;
}
/**
* Sets the value of `bucket_span`.
* @param bucketSpan The span of the overall buckets. Must be greater or equal to the largest jobs bucket_span.
* Defaults to the largest jobs bucket_span.
*/
public void setBucketSpan(TimeValue bucketSpan) {
this.bucketSpan = bucketSpan;
}
private void setBucketSpan(String bucketSpan) {
this.bucketSpan = TimeValue.parseTimeValue(bucketSpan, BUCKET_SPAN.getPreferredName());
}
public boolean isExcludeInterim() {
return excludeInterim;
}
/**
* Sets the value of "exclude_interim".
* When {@code true}, interim overall buckets will be filtered out.
* Overall buckets are interim if any of the job buckets within the overall bucket interval are interim.
* @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 overall buckets whose timestamp is on or after the "start" value will be returned.
* @param start value of "start" to be set
*/
public void setStart(String start) {
this.start = start;
}
public String getEnd() {
return end;
}
/**
* 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
*/
public void setEnd(String end) {
this.end = end;
}
public Double getOverallScore() {
return overallScore;
}
/**
* Sets the value of "overall_score".
* Only buckets with "overall_score" equal or greater will be returned.
* @param overallScore value of "anomaly_score".
*/
public void setOverallScore(double overallScore) {
this.overallScore = overallScore;
}
/**
* See {@link GetJobRequest#isAllowNoJobs()}
* @param allowNoJobs
*/
public void setAllowNoJobs(boolean allowNoJobs) {
this.allowNoJobs = allowNoJobs;
}
/**
* Whether to ignore if a wildcard expression matches no jobs.
*
* If this is `false`, then an error is returned when a wildcard (or `_all`) does not match any jobs
*/
public Boolean isAllowNoJobs() {
return allowNoJobs;
}
@Override
public ActionRequestValidationException validate() {
return null;
}
@Override
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
builder.startObject();
if (jobIds.isEmpty() == false) {
builder.field(Job.ID.getPreferredName(), Strings.collectionToCommaDelimitedString(jobIds));
}
if (topN != null) {
builder.field(TOP_N.getPreferredName(), topN);
}
if (bucketSpan != null) {
builder.field(BUCKET_SPAN.getPreferredName(), bucketSpan.getStringRep());
}
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 (overallScore != null) {
builder.field(OVERALL_SCORE.getPreferredName(), overallScore);
}
if (allowNoJobs != null) {
builder.field(ALLOW_NO_JOBS.getPreferredName(), allowNoJobs);
}
builder.endObject();
return builder;
}
@Override
public int hashCode() {
return Objects.hash(jobIds, topN, bucketSpan, excludeInterim, overallScore, start, end, allowNoJobs);
}
@Override
public boolean equals(Object obj) {
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
GetOverallBucketsRequest other = (GetOverallBucketsRequest) obj;
return Objects.equals(jobIds, other.jobIds) &&
Objects.equals(topN, other.topN) &&
Objects.equals(bucketSpan, other.bucketSpan) &&
Objects.equals(excludeInterim, other.excludeInterim) &&
Objects.equals(overallScore, other.overallScore) &&
Objects.equals(start, other.start) &&
Objects.equals(end, other.end) &&
Objects.equals(allowNoJobs, other.allowNoJobs);
}
}

View File

@ -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.OverallBucket;
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 overall buckets
*/
public class GetOverallBucketsResponse extends AbstractResultResponse<OverallBucket> {
public static final ParseField OVERALL_BUCKETS = new ParseField("overall_buckets");
@SuppressWarnings("unchecked")
public static final ConstructingObjectParser<GetOverallBucketsResponse, Void> PARSER = new ConstructingObjectParser<>(
"get_overall_buckets_response", true, a -> new GetOverallBucketsResponse((List<OverallBucket>) a[0], (long) a[1]));
static {
PARSER.declareObjectArray(ConstructingObjectParser.constructorArg(), OverallBucket.PARSER, OVERALL_BUCKETS);
PARSER.declareLong(ConstructingObjectParser.constructorArg(), COUNT);
}
public static GetOverallBucketsResponse fromXContent(XContentParser parser) throws IOException {
return PARSER.parse(parser, null);
}
GetOverallBucketsResponse(List<OverallBucket> overallBuckets, long count) {
super(OVERALL_BUCKETS, overallBuckets, count);
}
/**
* The retrieved overall buckets
* @return the retrieved overall buckets
*/
public List<OverallBucket> overallBuckets() {
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;
}
GetOverallBucketsResponse other = (GetOverallBucketsResponse) obj;
return count == other.count && Objects.equals(results, other.results);
}
}

View File

@ -25,8 +25,12 @@ import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpPut; import org.apache.http.client.methods.HttpPut;
import org.elasticsearch.client.ml.CloseJobRequest; import org.elasticsearch.client.ml.CloseJobRequest;
import org.elasticsearch.client.ml.DeleteJobRequest; import org.elasticsearch.client.ml.DeleteJobRequest;
import org.elasticsearch.client.ml.FlushJobRequest;
import org.elasticsearch.client.ml.GetBucketsRequest; import org.elasticsearch.client.ml.GetBucketsRequest;
import org.elasticsearch.client.ml.GetJobRequest; import org.elasticsearch.client.ml.GetJobRequest;
import org.elasticsearch.client.ml.GetJobStatsRequest;
import org.elasticsearch.client.ml.GetOverallBucketsRequest;
import org.elasticsearch.client.ml.GetRecordsRequest;
import org.elasticsearch.client.ml.OpenJobRequest; import org.elasticsearch.client.ml.OpenJobRequest;
import org.elasticsearch.client.ml.PutJobRequest; import org.elasticsearch.client.ml.PutJobRequest;
import org.elasticsearch.client.ml.job.config.AnalysisConfig; import org.elasticsearch.client.ml.job.config.AnalysisConfig;
@ -36,8 +40,6 @@ import org.elasticsearch.client.ml.job.util.PageParams;
import org.elasticsearch.common.unit.TimeValue; import org.elasticsearch.common.unit.TimeValue;
import org.elasticsearch.common.xcontent.XContentParser; import org.elasticsearch.common.xcontent.XContentParser;
import org.elasticsearch.common.xcontent.json.JsonXContent; import org.elasticsearch.common.xcontent.json.JsonXContent;
import org.elasticsearch.client.ml.FlushJobRequest;
import org.elasticsearch.client.ml.GetJobStatsRequest;
import org.elasticsearch.test.ESTestCase; import org.elasticsearch.test.ESTestCase;
import java.io.ByteArrayOutputStream; import java.io.ByteArrayOutputStream;
@ -79,6 +81,24 @@ public class MLRequestConvertersTests extends ESTestCase {
assertEquals(Boolean.toString(true), request.getParameters().get("allow_no_jobs")); assertEquals(Boolean.toString(true), request.getParameters().get("allow_no_jobs"));
} }
public void testGetJobStats() {
GetJobStatsRequest getJobStatsRequestRequest = new GetJobStatsRequest();
Request request = MLRequestConverters.getJobStats(getJobStatsRequestRequest);
assertEquals(HttpGet.METHOD_NAME, request.getMethod());
assertEquals("/_xpack/ml/anomaly_detectors/_stats", request.getEndpoint());
assertFalse(request.getParameters().containsKey("allow_no_jobs"));
getJobStatsRequestRequest = new GetJobStatsRequest("job1", "jobs*");
getJobStatsRequestRequest.setAllowNoJobs(true);
request = MLRequestConverters.getJobStats(getJobStatsRequestRequest);
assertEquals("/_xpack/ml/anomaly_detectors/job1,jobs*/_stats", request.getEndpoint());
assertEquals(Boolean.toString(true), request.getParameters().get("allow_no_jobs"));
}
public void testOpenJob() throws Exception { public void testOpenJob() throws Exception {
String jobId = "some-job-id"; String jobId = "some-job-id";
OpenJobRequest openJobRequest = new OpenJobRequest(jobId); OpenJobRequest openJobRequest = new OpenJobRequest(jobId);
@ -124,6 +144,27 @@ public class MLRequestConvertersTests extends ESTestCase {
assertEquals(Boolean.toString(true), request.getParameters().get("force")); assertEquals(Boolean.toString(true), request.getParameters().get("force"));
} }
public void testFlushJob() throws Exception {
String jobId = randomAlphaOfLength(10);
FlushJobRequest flushJobRequest = new FlushJobRequest(jobId);
Request request = MLRequestConverters.flushJob(flushJobRequest);
assertEquals(HttpPost.METHOD_NAME, request.getMethod());
assertEquals("/_xpack/ml/anomaly_detectors/" + jobId + "/_flush", request.getEndpoint());
assertEquals("{\"job_id\":\"" + jobId + "\"}", requestEntityToString(request));
flushJobRequest.setSkipTime("1000");
flushJobRequest.setStart("105");
flushJobRequest.setEnd("200");
flushJobRequest.setAdvanceTime("100");
flushJobRequest.setCalcInterim(true);
request = MLRequestConverters.flushJob(flushJobRequest);
assertEquals(
"{\"job_id\":\"" + jobId + "\",\"calc_interim\":true,\"start\":\"105\"," +
"\"end\":\"200\",\"advance_time\":\"100\",\"skip_time\":\"1000\"}",
requestEntityToString(request));
}
public void testGetBuckets() throws IOException { public void testGetBuckets() throws IOException {
String jobId = randomAlphaOfLength(10); String jobId = randomAlphaOfLength(10);
GetBucketsRequest getBucketsRequest = new GetBucketsRequest(jobId); GetBucketsRequest getBucketsRequest = new GetBucketsRequest(jobId);
@ -141,42 +182,42 @@ public class MLRequestConvertersTests extends ESTestCase {
} }
} }
public void testFlushJob() throws Exception { public void testGetOverallBuckets() throws IOException {
String jobId = randomAlphaOfLength(10); String jobId = randomAlphaOfLength(10);
FlushJobRequest flushJobRequest = new FlushJobRequest(jobId); GetOverallBucketsRequest getOverallBucketsRequest = new GetOverallBucketsRequest(jobId);
getOverallBucketsRequest.setBucketSpan(TimeValue.timeValueHours(3));
getOverallBucketsRequest.setTopN(3);
getOverallBucketsRequest.setStart("2018-08-08T00:00:00Z");
getOverallBucketsRequest.setEnd("2018-09-08T00:00:00Z");
getOverallBucketsRequest.setExcludeInterim(true);
Request request = MLRequestConverters.flushJob(flushJobRequest); Request request = MLRequestConverters.getOverallBuckets(getOverallBucketsRequest);
assertEquals(HttpPost.METHOD_NAME, request.getMethod()); assertEquals(HttpGet.METHOD_NAME, request.getMethod());
assertEquals("/_xpack/ml/anomaly_detectors/" + jobId + "/_flush", request.getEndpoint()); assertEquals("/_xpack/ml/anomaly_detectors/" + jobId + "/results/overall_buckets", request.getEndpoint());
assertEquals("{\"job_id\":\"" + jobId + "\"}", requestEntityToString(request)); try (XContentParser parser = createParser(JsonXContent.jsonXContent, request.getEntity().getContent())) {
GetOverallBucketsRequest parsedRequest = GetOverallBucketsRequest.PARSER.apply(parser, null);
flushJobRequest.setSkipTime("1000"); assertThat(parsedRequest, equalTo(getOverallBucketsRequest));
flushJobRequest.setStart("105"); }
flushJobRequest.setEnd("200");
flushJobRequest.setAdvanceTime("100");
flushJobRequest.setCalcInterim(true);
request = MLRequestConverters.flushJob(flushJobRequest);
assertEquals(
"{\"job_id\":\"" + jobId + "\",\"calc_interim\":true,\"start\":\"105\"," +
"\"end\":\"200\",\"advance_time\":\"100\",\"skip_time\":\"1000\"}",
requestEntityToString(request));
} }
public void testGetJobStats() { public void testGetRecords() throws IOException {
GetJobStatsRequest getJobStatsRequestRequest = new GetJobStatsRequest(); String jobId = randomAlphaOfLength(10);
GetRecordsRequest getRecordsRequest = new GetRecordsRequest(jobId);
Request request = MLRequestConverters.getJobStats(getJobStatsRequestRequest); getRecordsRequest.setStart("2018-08-08T00:00:00Z");
getRecordsRequest.setEnd("2018-09-08T00:00:00Z");
getRecordsRequest.setPageParams(new PageParams(100, 300));
getRecordsRequest.setRecordScore(75.0);
getRecordsRequest.setSort("anomaly_score");
getRecordsRequest.setDescending(true);
getRecordsRequest.setExcludeInterim(true);
Request request = MLRequestConverters.getRecords(getRecordsRequest);
assertEquals(HttpGet.METHOD_NAME, request.getMethod()); assertEquals(HttpGet.METHOD_NAME, request.getMethod());
assertEquals("/_xpack/ml/anomaly_detectors/_stats", request.getEndpoint()); assertEquals("/_xpack/ml/anomaly_detectors/" + jobId + "/results/records", request.getEndpoint());
assertFalse(request.getParameters().containsKey("allow_no_jobs")); try (XContentParser parser = createParser(JsonXContent.jsonXContent, request.getEntity().getContent())) {
GetRecordsRequest parsedRequest = GetRecordsRequest.PARSER.apply(parser, null);
getJobStatsRequestRequest = new GetJobStatsRequest("job1", "jobs*"); assertThat(parsedRequest, equalTo(getRecordsRequest));
getJobStatsRequestRequest.setAllowNoJobs(true); }
request = MLRequestConverters.getJobStats(getJobStatsRequestRequest);
assertEquals("/_xpack/ml/anomaly_detectors/job1,jobs*/_stats", request.getEndpoint());
assertEquals(Boolean.toString(true), request.getParameters().get("allow_no_jobs"));
} }
private static Job createValidJob(String jobId) { private static Job createValidJob(String jobId) {

View File

@ -23,19 +23,29 @@ import org.elasticsearch.action.index.IndexRequest;
import org.elasticsearch.action.support.WriteRequest; import org.elasticsearch.action.support.WriteRequest;
import org.elasticsearch.client.ml.GetBucketsRequest; import org.elasticsearch.client.ml.GetBucketsRequest;
import org.elasticsearch.client.ml.GetBucketsResponse; import org.elasticsearch.client.ml.GetBucketsResponse;
import org.elasticsearch.client.ml.GetOverallBucketsRequest;
import org.elasticsearch.client.ml.GetOverallBucketsResponse;
import org.elasticsearch.client.ml.GetRecordsRequest; import org.elasticsearch.client.ml.GetRecordsRequest;
import org.elasticsearch.client.ml.GetRecordsResponse; import org.elasticsearch.client.ml.GetRecordsResponse;
import org.elasticsearch.client.ml.PutJobRequest; import org.elasticsearch.client.ml.PutJobRequest;
import org.elasticsearch.client.ml.job.config.AnalysisConfig;
import org.elasticsearch.client.ml.job.config.DataDescription;
import org.elasticsearch.client.ml.job.config.Detector;
import org.elasticsearch.client.ml.job.config.Job; import org.elasticsearch.client.ml.job.config.Job;
import org.elasticsearch.client.ml.job.results.AnomalyRecord; import org.elasticsearch.client.ml.job.results.AnomalyRecord;
import org.elasticsearch.client.ml.job.results.Bucket; import org.elasticsearch.client.ml.job.results.Bucket;
import org.elasticsearch.client.ml.job.results.OverallBucket;
import org.elasticsearch.client.ml.job.util.PageParams; import org.elasticsearch.client.ml.job.util.PageParams;
import org.elasticsearch.common.unit.TimeValue;
import org.elasticsearch.common.xcontent.XContentType; import org.elasticsearch.common.xcontent.XContentType;
import org.junit.After; import org.junit.After;
import org.junit.Before; import org.junit.Before;
import java.io.IOException; import java.io.IOException;
import java.util.Arrays;
import java.util.List;
import static org.hamcrest.Matchers.closeTo;
import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.greaterThan; import static org.hamcrest.Matchers.greaterThan;
import static org.hamcrest.Matchers.greaterThanOrEqualTo; import static org.hamcrest.Matchers.greaterThanOrEqualTo;
@ -59,7 +69,7 @@ public class MachineLearningGetResultsIT extends ESRestHighLevelClientTestCase {
@Before @Before
public void createJobAndIndexResults() throws IOException { public void createJobAndIndexResults() throws IOException {
MachineLearningClient machineLearningClient = highLevelClient().machineLearning(); MachineLearningClient machineLearningClient = highLevelClient().machineLearning();
Job job = MachineLearningIT.buildJob(JOB_ID); Job job = buildJob(JOB_ID);
machineLearningClient.putJob(new PutJobRequest(job), RequestOptions.DEFAULT); machineLearningClient.putJob(new PutJobRequest(job), RequestOptions.DEFAULT);
BulkRequest bulkRequest = new BulkRequest(); BulkRequest bulkRequest = new BulkRequest();
@ -206,6 +216,111 @@ public class MachineLearningGetResultsIT extends ESRestHighLevelClientTestCase {
} }
} }
public void testGetOverallBuckets() throws IOException {
MachineLearningClient machineLearningClient = highLevelClient().machineLearning();
GetBucketsRequest getBucketsRequest = new GetBucketsRequest(JOB_ID);
getBucketsRequest.setPageParams(new PageParams(0, 3));
List<Bucket> firstBuckets = machineLearningClient.getBuckets(getBucketsRequest, RequestOptions.DEFAULT).buckets();
String anotherJobId = "test-get-overall-buckets-job";
Job anotherJob = buildJob(anotherJobId);
machineLearningClient.putJob(new PutJobRequest(anotherJob), RequestOptions.DEFAULT);
// Let's index matching buckets with the score being 10.0 higher
BulkRequest bulkRequest = new BulkRequest();
bulkRequest.setRefreshPolicy(WriteRequest.RefreshPolicy.IMMEDIATE);
for (Bucket bucket : firstBuckets) {
IndexRequest indexRequest = new IndexRequest(RESULTS_INDEX, DOC);
indexRequest.source("{\"job_id\":\"" + anotherJobId + "\", \"result_type\":\"bucket\", \"timestamp\": " +
bucket.getTimestamp().getTime() + "," + "\"bucket_span\": 3600,\"is_interim\": " + bucket.isInterim()
+ ", \"anomaly_score\": " + String.valueOf(bucket.getAnomalyScore() + 10.0) + "}", XContentType.JSON);
bulkRequest.add(indexRequest);
}
highLevelClient().bulk(bulkRequest, RequestOptions.DEFAULT);
{
GetOverallBucketsRequest request = new GetOverallBucketsRequest(JOB_ID, anotherJobId);
GetOverallBucketsResponse response = execute(request, machineLearningClient::getOverallBuckets,
machineLearningClient::getOverallBucketsAsync);
assertThat(response.count(), equalTo(241L));
List<OverallBucket> overallBuckets = response.overallBuckets();
assertThat(overallBuckets.size(), equalTo(241));
assertThat(overallBuckets.stream().allMatch(b -> b.getBucketSpan() == 3600L), is(true));
assertThat(overallBuckets.get(0).getTimestamp().getTime(), equalTo(START_TIME_EPOCH_MS));
assertThat(overallBuckets.get(240).isInterim(), is(true));
}
{
GetOverallBucketsRequest request = new GetOverallBucketsRequest(JOB_ID, anotherJobId);
request.setBucketSpan(TimeValue.timeValueHours(2));
GetOverallBucketsResponse response = execute(request, machineLearningClient::getOverallBuckets,
machineLearningClient::getOverallBucketsAsync);
assertThat(response.count(), equalTo(121L));
}
{
long end = START_TIME_EPOCH_MS + 10 * 3600000L;
GetOverallBucketsRequest request = new GetOverallBucketsRequest(JOB_ID, anotherJobId);
request.setEnd(String.valueOf(end));
GetOverallBucketsResponse response = execute(request, machineLearningClient::getOverallBuckets,
machineLearningClient::getOverallBucketsAsync);
assertThat(response.count(), equalTo(10L));
assertThat(response.overallBuckets().get(0).getTimestamp().getTime(), equalTo(START_TIME_EPOCH_MS));
assertThat(response.overallBuckets().get(9).getTimestamp().getTime(), equalTo(end - 3600000L));
}
{
GetOverallBucketsRequest request = new GetOverallBucketsRequest(JOB_ID, anotherJobId);
request.setExcludeInterim(true);
GetOverallBucketsResponse response = execute(request, machineLearningClient::getOverallBuckets,
machineLearningClient::getOverallBucketsAsync);
assertThat(response.count(), equalTo(240L));
assertThat(response.overallBuckets().stream().allMatch(b -> b.isInterim() == false), is(true));
}
{
GetOverallBucketsRequest request = new GetOverallBucketsRequest(JOB_ID);
request.setOverallScore(75.0);
GetOverallBucketsResponse response = execute(request, machineLearningClient::getOverallBuckets,
machineLearningClient::getOverallBucketsAsync);
assertThat(response.count(), equalTo(bucketStats.criticalCount));
assertThat(response.overallBuckets().stream().allMatch(b -> b.getOverallScore() >= 75.0), is(true));
}
{
long start = START_TIME_EPOCH_MS + 10 * 3600000L;
GetOverallBucketsRequest request = new GetOverallBucketsRequest(JOB_ID, anotherJobId);
request.setStart(String.valueOf(start));
GetOverallBucketsResponse response = execute(request, machineLearningClient::getOverallBuckets,
machineLearningClient::getOverallBucketsAsync);
assertThat(response.count(), equalTo(231L));
assertThat(response.overallBuckets().get(0).getTimestamp().getTime(), equalTo(start));
}
{
GetOverallBucketsRequest request = new GetOverallBucketsRequest(JOB_ID, anotherJobId);
request.setEnd(String.valueOf(START_TIME_EPOCH_MS + 3 * 3600000L));
request.setTopN(2);
GetOverallBucketsResponse response = execute(request, machineLearningClient::getOverallBuckets,
machineLearningClient::getOverallBucketsAsync);
assertThat(response.count(), equalTo(3L));
List<OverallBucket> overallBuckets = response.overallBuckets();
for (int i = 0; i < overallBuckets.size(); ++i) {
// As the second job has scores that are -10 from the first, the overall buckets should be +5 from the initial job
assertThat(overallBuckets.get(i).getOverallScore(), is(closeTo(firstBuckets.get(i).getAnomalyScore() + 5.0, 0.0001)));
}
}
}
public void testGetRecords() throws IOException { public void testGetRecords() throws IOException {
MachineLearningClient machineLearningClient = highLevelClient().machineLearning(); MachineLearningClient machineLearningClient = highLevelClient().machineLearning();
@ -272,6 +387,19 @@ public class MachineLearningGetResultsIT extends ESRestHighLevelClientTestCase {
} }
} }
public static Job buildJob(String jobId) {
Job.Builder builder = new Job.Builder(jobId);
Detector detector = new Detector.Builder("count", null).build();
AnalysisConfig.Builder configBuilder = new AnalysisConfig.Builder(Arrays.asList(detector));
configBuilder.setBucketSpan(TimeValue.timeValueHours(1));
builder.setAnalysisConfig(configBuilder);
DataDescription.Builder dataDescription = new DataDescription.Builder();
builder.setDataDescription(dataDescription);
return builder.build();
}
private static class Stats { private static class Stats {
// score < 50.0 // score < 50.0
private long minorCount; private long minorCount;

View File

@ -20,9 +20,11 @@ package org.elasticsearch.client.documentation;
import org.elasticsearch.action.ActionListener; import org.elasticsearch.action.ActionListener;
import org.elasticsearch.action.LatchedActionListener; import org.elasticsearch.action.LatchedActionListener;
import org.elasticsearch.action.bulk.BulkRequest;
import org.elasticsearch.action.index.IndexRequest; import org.elasticsearch.action.index.IndexRequest;
import org.elasticsearch.action.support.WriteRequest; import org.elasticsearch.action.support.WriteRequest;
import org.elasticsearch.client.ESRestHighLevelClientTestCase; import org.elasticsearch.client.ESRestHighLevelClientTestCase;
import org.elasticsearch.client.MachineLearningGetResultsIT;
import org.elasticsearch.client.MachineLearningIT; import org.elasticsearch.client.MachineLearningIT;
import org.elasticsearch.client.MlRestTestStateCleaner; import org.elasticsearch.client.MlRestTestStateCleaner;
import org.elasticsearch.client.RequestOptions; import org.elasticsearch.client.RequestOptions;
@ -31,12 +33,16 @@ import org.elasticsearch.client.ml.CloseJobRequest;
import org.elasticsearch.client.ml.CloseJobResponse; import org.elasticsearch.client.ml.CloseJobResponse;
import org.elasticsearch.client.ml.DeleteJobRequest; import org.elasticsearch.client.ml.DeleteJobRequest;
import org.elasticsearch.client.ml.DeleteJobResponse; 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.GetBucketsRequest;
import org.elasticsearch.client.ml.GetBucketsResponse; import org.elasticsearch.client.ml.GetBucketsResponse;
import org.elasticsearch.client.ml.GetJobRequest; import org.elasticsearch.client.ml.GetJobRequest;
import org.elasticsearch.client.ml.GetJobResponse; import org.elasticsearch.client.ml.GetJobResponse;
import org.elasticsearch.client.ml.GetJobStatsRequest; import org.elasticsearch.client.ml.GetJobStatsRequest;
import org.elasticsearch.client.ml.GetJobStatsResponse; import org.elasticsearch.client.ml.GetJobStatsResponse;
import org.elasticsearch.client.ml.GetOverallBucketsRequest;
import org.elasticsearch.client.ml.GetOverallBucketsResponse;
import org.elasticsearch.client.ml.GetRecordsRequest; import org.elasticsearch.client.ml.GetRecordsRequest;
import org.elasticsearch.client.ml.GetRecordsResponse; import org.elasticsearch.client.ml.GetRecordsResponse;
import org.elasticsearch.client.ml.OpenJobRequest; import org.elasticsearch.client.ml.OpenJobRequest;
@ -49,12 +55,11 @@ import org.elasticsearch.client.ml.job.config.Detector;
import org.elasticsearch.client.ml.job.config.Job; import org.elasticsearch.client.ml.job.config.Job;
import org.elasticsearch.client.ml.job.results.AnomalyRecord; import org.elasticsearch.client.ml.job.results.AnomalyRecord;
import org.elasticsearch.client.ml.job.results.Bucket; import org.elasticsearch.client.ml.job.results.Bucket;
import org.elasticsearch.client.ml.job.results.OverallBucket;
import org.elasticsearch.client.ml.job.stats.JobStats;
import org.elasticsearch.client.ml.job.util.PageParams; import org.elasticsearch.client.ml.job.util.PageParams;
import org.elasticsearch.common.unit.TimeValue; import org.elasticsearch.common.unit.TimeValue;
import org.elasticsearch.common.xcontent.XContentType; import org.elasticsearch.common.xcontent.XContentType;
import org.elasticsearch.client.ml.FlushJobRequest;
import org.elasticsearch.client.ml.FlushJobResponse;
import org.elasticsearch.client.ml.job.stats.JobStats;
import org.junit.After; import org.junit.After;
import java.io.IOException; import java.io.IOException;
@ -65,9 +70,11 @@ import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import static org.hamcrest.Matchers.closeTo;
import static org.hamcrest.Matchers.containsInAnyOrder; import static org.hamcrest.Matchers.containsInAnyOrder;
import static org.hamcrest.Matchers.greaterThan; import static org.hamcrest.Matchers.greaterThan;
import static org.hamcrest.Matchers.hasSize; import static org.hamcrest.Matchers.hasSize;
import static org.hamcrest.core.Is.is;
public class MlClientDocumentationIT extends ESRestHighLevelClientTestCase { public class MlClientDocumentationIT extends ESRestHighLevelClientTestCase {
@ -584,6 +591,107 @@ public class MlClientDocumentationIT extends ESRestHighLevelClientTestCase {
} }
} }
public void testGetOverallBuckets() throws IOException, InterruptedException {
RestHighLevelClient client = highLevelClient();
String jobId1 = "test-get-overall-buckets-1";
String jobId2 = "test-get-overall-buckets-2";
Job job1 = MachineLearningGetResultsIT.buildJob(jobId1);
Job job2 = MachineLearningGetResultsIT.buildJob(jobId2);
client.machineLearning().putJob(new PutJobRequest(job1), RequestOptions.DEFAULT);
client.machineLearning().putJob(new PutJobRequest(job2), RequestOptions.DEFAULT);
// Let us index some buckets
BulkRequest bulkRequest = new BulkRequest();
bulkRequest.setRefreshPolicy(WriteRequest.RefreshPolicy.IMMEDIATE);
{
IndexRequest indexRequest = new IndexRequest(".ml-anomalies-shared", "doc");
indexRequest.source("{\"job_id\":\"test-get-overall-buckets-1\", \"result_type\":\"bucket\", \"timestamp\": 1533081600000," +
"\"bucket_span\": 600,\"is_interim\": false, \"anomaly_score\": 60.0}", XContentType.JSON);
bulkRequest.add(indexRequest);
}
{
IndexRequest indexRequest = new IndexRequest(".ml-anomalies-shared", "doc");
indexRequest.source("{\"job_id\":\"test-get-overall-buckets-2\", \"result_type\":\"bucket\", \"timestamp\": 1533081600000," +
"\"bucket_span\": 3600,\"is_interim\": false, \"anomaly_score\": 100.0}", XContentType.JSON);
bulkRequest.add(indexRequest);
}
client.bulk(bulkRequest, RequestOptions.DEFAULT);
{
// tag::x-pack-ml-get-overall-buckets-request
GetOverallBucketsRequest request = new GetOverallBucketsRequest(jobId1, jobId2); // <1>
// end::x-pack-ml-get-overall-buckets-request
// tag::x-pack-ml-get-overall-buckets-bucket-span
request.setBucketSpan(TimeValue.timeValueHours(24)); // <1>
// end::x-pack-ml-get-overall-buckets-bucket-span
// tag::x-pack-ml-get-overall-buckets-end
request.setEnd("2018-08-21T00:00:00Z"); // <1>
// end::x-pack-ml-get-overall-buckets-end
// tag::x-pack-ml-get-overall-buckets-exclude-interim
request.setExcludeInterim(true); // <1>
// end::x-pack-ml-get-overall-buckets-exclude-interim
// tag::x-pack-ml-get-overall-buckets-overall-score
request.setOverallScore(75.0); // <1>
// end::x-pack-ml-get-overall-buckets-overall-score
// tag::x-pack-ml-get-overall-buckets-start
request.setStart("2018-08-01T00:00:00Z"); // <1>
// end::x-pack-ml-get-overall-buckets-start
// tag::x-pack-ml-get-overall-buckets-top-n
request.setTopN(2); // <1>
// end::x-pack-ml-get-overall-buckets-top-n
// tag::x-pack-ml-get-overall-buckets-execute
GetOverallBucketsResponse response = client.machineLearning().getOverallBuckets(request, RequestOptions.DEFAULT);
// end::x-pack-ml-get-overall-buckets-execute
// tag::x-pack-ml-get-overall-buckets-response
long count = response.count(); // <1>
List<OverallBucket> overallBuckets = response.overallBuckets(); // <2>
// end::x-pack-ml-get-overall-buckets-response
assertEquals(1, overallBuckets.size());
assertThat(overallBuckets.get(0).getOverallScore(), is(closeTo(80.0, 0.001)));
}
{
GetOverallBucketsRequest request = new GetOverallBucketsRequest(jobId1, jobId2);
// tag::x-pack-ml-get-overall-buckets-listener
ActionListener<GetOverallBucketsResponse> listener =
new ActionListener<GetOverallBucketsResponse>() {
@Override
public void onResponse(GetOverallBucketsResponse getOverallBucketsResponse) {
// <1>
}
@Override
public void onFailure(Exception e) {
// <2>
}
};
// end::x-pack-ml-get-overall-buckets-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-overall-buckets-execute-async
client.machineLearning().getOverallBucketsAsync(request, RequestOptions.DEFAULT, listener); // <1>
// end::x-pack-ml-get-overall-buckets-execute-async
assertTrue(latch.await(30L, TimeUnit.SECONDS));
}
}
public void testGetRecords() throws IOException, InterruptedException { public void testGetRecords() throws IOException, InterruptedException {
RestHighLevelClient client = highLevelClient(); RestHighLevelClient client = highLevelClient();

View File

@ -0,0 +1,67 @@
/*
* 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.common.unit.TimeValue;
import org.elasticsearch.common.xcontent.XContentParser;
import org.elasticsearch.test.AbstractXContentTestCase;
import java.io.IOException;
public class GetOverallBucketsRequestTests extends AbstractXContentTestCase<GetOverallBucketsRequest> {
@Override
protected GetOverallBucketsRequest createTestInstance() {
GetOverallBucketsRequest request = new GetOverallBucketsRequest(randomAlphaOfLengthBetween(1, 20));
if (randomBoolean()) {
request.setTopN(randomIntBetween(1, 10));
}
if (randomBoolean()) {
request.setBucketSpan(TimeValue.timeValueSeconds(randomIntBetween(1, 1_000_000)));
}
if (randomBoolean()) {
request.setStart(String.valueOf(randomLong()));
}
if (randomBoolean()) {
request.setEnd(String.valueOf(randomLong()));
}
if (randomBoolean()) {
request.setExcludeInterim(randomBoolean());
}
if (randomBoolean()) {
request.setOverallScore(randomDouble());
}
if (randomBoolean()) {
request.setExcludeInterim(randomBoolean());
}
return request;
}
@Override
protected GetOverallBucketsRequest doParseInstance(XContentParser parser) throws IOException {
return GetOverallBucketsRequest.PARSER.apply(parser, null);
}
@Override
protected boolean supportsUnknownFields() {
return false;
}
}

View File

@ -0,0 +1,52 @@
/*
* 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.OverallBucket;
import org.elasticsearch.client.ml.job.results.OverallBucketTests;
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 GetOverallBucketsResponseTests extends AbstractXContentTestCase<GetOverallBucketsResponse> {
@Override
protected GetOverallBucketsResponse createTestInstance() {
int listSize = randomInt(10);
List<OverallBucket> overallBuckets = new ArrayList<>(listSize);
for (int j = 0; j < listSize; j++) {
OverallBucket overallBucket = OverallBucketTests.createRandom();
overallBuckets.add(overallBucket);
}
return new GetOverallBucketsResponse(overallBuckets, listSize);
}
@Override
protected GetOverallBucketsResponse doParseInstance(XContentParser parser) throws IOException {
return GetOverallBucketsResponse.fromXContent(parser);
}
@Override
protected boolean supportsUnknownFields() {
return true;
}
}

View File

@ -32,6 +32,10 @@ public class OverallBucketTests extends AbstractXContentTestCase<OverallBucket>
@Override @Override
protected OverallBucket createTestInstance() { protected OverallBucket createTestInstance() {
return createRandom();
}
public static OverallBucket createRandom() {
int jobCount = randomIntBetween(0, 10); int jobCount = randomIntBetween(0, 10);
List<OverallBucket.JobInfo> jobs = new ArrayList<>(jobCount); List<OverallBucket.JobInfo> jobs = new ArrayList<>(jobCount);
for (int i = 0; i < jobCount; ++i) { for (int i = 0; i < jobCount; ++i) {

View File

@ -70,7 +70,7 @@ include-tagged::{doc-tests}/MlClientDocumentationIT.java[x-pack-ml-get-buckets-s
["source","java",subs="attributes,callouts,macros"] ["source","java",subs="attributes,callouts,macros"]
-------------------------------------------------- --------------------------------------------------
include-tagged::{doc-tests}/MlClientDocumentationIT.java[x-pack-ml-get-buckets-end] include-tagged::{doc-tests}/MlClientDocumentationIT.java[x-pack-ml-get-buckets-start]
-------------------------------------------------- --------------------------------------------------
<1> Buckets with timestamps on or after this time will be returned. <1> Buckets with timestamps on or after this time will be returned.

View File

@ -0,0 +1,107 @@
[[java-rest-high-x-pack-ml-get-overall-buckets]]
=== Get Overall Buckets API
The Get Overall Buckets API retrieves overall bucket results that
summarize the bucket results of multiple jobs.
It accepts a `GetOverallBucketsRequest` object and responds
with a `GetOverallBucketsResponse` object.
[[java-rest-high-x-pack-ml-get-overall-buckets-request]]
==== Get Overall Buckets Request
A `GetOverallBucketsRequest` object gets created with one or more `jobId`.
["source","java",subs="attributes,callouts,macros"]
--------------------------------------------------
include-tagged::{doc-tests}/MlClientDocumentationIT.java[x-pack-ml-get-overall-buckets-request]
--------------------------------------------------
<1> Constructing a new request referencing job IDs `jobId1` and `jobId2`.
==== Optional Arguments
The following arguments are optional:
["source","java",subs="attributes,callouts,macros"]
--------------------------------------------------
include-tagged::{doc-tests}/MlClientDocumentationIT.java[x-pack-ml-get-overall-buckets-bucket-span]
--------------------------------------------------
<1> The span of the overall buckets. Must be greater or equal to the jobs' largest `bucket_span`.
["source","java",subs="attributes,callouts,macros"]
--------------------------------------------------
include-tagged::{doc-tests}/MlClientDocumentationIT.java[x-pack-ml-get-overall-buckets-end]
--------------------------------------------------
<1> Overall buckets 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-overall-buckets-exclude-interim]
--------------------------------------------------
<1> If `true`, interim results will be excluded. Overall buckets are interim if any of the job buckets
within the overall bucket interval are interim. Defaults to `false`.
["source","java",subs="attributes,callouts,macros"]
--------------------------------------------------
include-tagged::{doc-tests}/MlClientDocumentationIT.java[x-pack-ml-get-overall-buckets-overall-score]
--------------------------------------------------
<1> Overall buckets with overall scores 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-overall-buckets-start]
--------------------------------------------------
<1> Overall buckets with timestamps on or after this time will be returned.
["source","java",subs="attributes,callouts,macros"]
--------------------------------------------------
include-tagged::{doc-tests}/MlClientDocumentationIT.java[x-pack-ml-get-overall-buckets-top-n]
--------------------------------------------------
<1> The number of top job bucket scores to be used in the `overall_score` calculation. Defaults to `1`.
[[java-rest-high-x-pack-ml-get-overall-buckets-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-overall-buckets-execute]
--------------------------------------------------
[[java-rest-high-x-pack-ml-get-overall-buckets-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-overall-buckets-execute-async]
--------------------------------------------------
<1> The `GetOverallBucketsRequest` 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 `GetBucketsResponse` looks like:
["source","java",subs="attributes,callouts,macros"]
--------------------------------------------------
include-tagged::{doc-tests}/MlClientDocumentationIT.java[x-pack-ml-get-overall-buckets-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-overall-buckets-response]]
==== Get Overall Buckets Response
The returned `GetOverallBucketsResponse` contains the requested buckets:
["source","java",subs="attributes,callouts,macros"]
--------------------------------------------------
include-tagged::{doc-tests}/MlClientDocumentationIT.java[x-pack-ml-get-overall-buckets-response]
--------------------------------------------------
<1> The count of overall buckets that were matched
<2> The overall buckets retrieved

View File

@ -216,6 +216,7 @@ The Java High Level REST Client supports the following Machine Learning APIs:
* <<java-rest-high-x-pack-ml-flush-job>> * <<java-rest-high-x-pack-ml-flush-job>>
* <<java-rest-high-x-pack-ml-get-job-stats>> * <<java-rest-high-x-pack-ml-get-job-stats>>
* <<java-rest-high-x-pack-ml-get-buckets>> * <<java-rest-high-x-pack-ml-get-buckets>>
* <<java-rest-high-x-pack-ml-get-overall-buckets>>
* <<java-rest-high-x-pack-ml-get-records>> * <<java-rest-high-x-pack-ml-get-records>>
include::ml/put-job.asciidoc[] include::ml/put-job.asciidoc[]
@ -226,6 +227,7 @@ include::ml/close-job.asciidoc[]
include::ml/flush-job.asciidoc[] include::ml/flush-job.asciidoc[]
include::ml/get-job-stats.asciidoc[] include::ml/get-job-stats.asciidoc[]
include::ml/get-buckets.asciidoc[] include::ml/get-buckets.asciidoc[]
include::ml/get-overall-buckets.asciidoc[]
include::ml/get-records.asciidoc[] include::ml/get-records.asciidoc[]
== Migration APIs == Migration APIs