Skip to content

Commit

Permalink
HLRC: Adding ML Job stats (#33183)
Browse files Browse the repository at this point in the history
* HLRC: Adding pojos for get job stats

HLRC: Adding pojos for job stats request

* HLRC: Adding job stats pojos

* HLRC: ML job stats

* Minor syntax changes and adding license headers

* minor comment change

* Moving to client package, minor changes

* Addressing PR comments

* removing bad sleep

* addressing minor comment around test methods

* adding toplevel random fields for tests

* addressing minor review comments
  • Loading branch information
benwtrent authored Sep 1, 2018
1 parent f28cddf commit 19b14fa
Show file tree
Hide file tree
Showing 21 changed files with 1,596 additions and 2 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
import org.elasticsearch.client.ml.DeleteJobRequest;
import org.elasticsearch.client.ml.GetBucketsRequest;
import org.elasticsearch.client.ml.GetJobRequest;
import org.elasticsearch.client.ml.GetJobStatsRequest;
import org.elasticsearch.client.ml.GetRecordsRequest;
import org.elasticsearch.client.ml.OpenJobRequest;
import org.elasticsearch.client.ml.PutJobRequest;
Expand Down Expand Up @@ -126,6 +127,23 @@ static Request getBuckets(GetBucketsRequest getBucketsRequest) throws IOExceptio
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 getRecords(GetRecordsRequest getRecordsRequest) throws IOException {
String endpoint = new EndpointBuilder()
.addPathPartAsIs("_xpack")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@
package org.elasticsearch.client;

import org.elasticsearch.action.ActionListener;
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;
Expand Down Expand Up @@ -288,6 +291,47 @@ public void getBucketsAsync(GetBucketsRequest request, RequestOptions options, A
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());
}

/**
* Gets the records for a Machine Learning Job.
* <p>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
/*
* 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.xcontent.ConstructingObjectParser;
import org.elasticsearch.common.xcontent.ObjectParser;
import org.elasticsearch.common.xcontent.ToXContentObject;
import org.elasticsearch.common.xcontent.XContentBuilder;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;


/**
* Request object to get {@link org.elasticsearch.client.ml.job.stats.JobStats} by their respective jobIds
*
* `_all` explicitly gets all the jobs' statistics in the cluster
* An empty request (no `jobId`s) implicitly gets all the jobs' statistics in the cluster
*/
public class GetJobStatsRequest extends ActionRequest implements ToXContentObject {

public static final ParseField ALLOW_NO_JOBS = new ParseField("allow_no_jobs");

@SuppressWarnings("unchecked")
public static final ConstructingObjectParser<GetJobStatsRequest, Void> PARSER = new ConstructingObjectParser<>(
"get_jobs_stats_request", a -> new GetJobStatsRequest((List<String>) a[0]));

static {
PARSER.declareField(ConstructingObjectParser.constructorArg(),
p -> Arrays.asList(Strings.commaDelimitedListToStringArray(p.text())),
Job.ID, ObjectParser.ValueType.STRING_ARRAY);
PARSER.declareBoolean(GetJobStatsRequest::setAllowNoJobs, ALLOW_NO_JOBS);
}

private static final String ALL_JOBS = "_all";

private final List<String> jobIds;
private Boolean allowNoJobs;

/**
* Explicitly gets all jobs statistics
*
* @return a {@link GetJobStatsRequest} for all existing jobs
*/
public static GetJobStatsRequest getAllJobStatsRequest(){
return new GetJobStatsRequest(ALL_JOBS);
}

GetJobStatsRequest(List<String> jobIds) {
if (jobIds.stream().anyMatch(Objects::isNull)) {
throw new NullPointerException("jobIds must not contain null values");
}
this.jobIds = new ArrayList<>(jobIds);
}

/**
* Get the specified Job's statistics via their unique jobIds
*
* @param jobIds must be non-null and each jobId must be non-null
*/
public GetJobStatsRequest(String... jobIds) {
this(Arrays.asList(jobIds));
}

/**
* All the jobIds for which to get statistics
*/
public List<String> getJobIds() {
return jobIds;
}

public Boolean isAllowNoJobs() {
return this.allowNoJobs;
}

/**
* Whether to ignore if a wildcard expression matches no jobs.
*
* This includes `_all` string or when no jobs have been specified
*
* @param allowNoJobs When {@code true} ignore if wildcard or `_all` matches no jobs. Defaults to {@code true}
*/
public void setAllowNoJobs(boolean allowNoJobs) {
this.allowNoJobs = allowNoJobs;
}

@Override
public int hashCode() {
return Objects.hash(jobIds, allowNoJobs);
}

@Override
public boolean equals(Object other) {
if (this == other) {
return true;
}

if (other == null || getClass() != other.getClass()) {
return false;
}

GetJobStatsRequest that = (GetJobStatsRequest) other;
return Objects.equals(jobIds, that.jobIds) &&
Objects.equals(allowNoJobs, that.allowNoJobs);
}

@Override
public ActionRequestValidationException validate() {
return null;
}

@Override
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
builder.startObject();
builder.field(Job.ID.getPreferredName(), Strings.collectionToCommaDelimitedString(jobIds));
if (allowNoJobs != null) {
builder.field(ALLOW_NO_JOBS.getPreferredName(), allowNoJobs);
}
builder.endObject();
return builder;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
/*
* 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.ParseField;
import org.elasticsearch.common.Strings;
import org.elasticsearch.common.xcontent.ConstructingObjectParser;
import org.elasticsearch.common.xcontent.XContentParser;
import org.elasticsearch.client.ml.job.stats.JobStats;

import java.io.IOException;
import java.util.List;
import java.util.Objects;

import static org.elasticsearch.common.xcontent.ConstructingObjectParser.constructorArg;

/**
* Contains a {@link List} of the found {@link JobStats} objects and the total count found
*/
public class GetJobStatsResponse extends AbstractResultResponse<JobStats> {

public static final ParseField RESULTS_FIELD = new ParseField("jobs");

@SuppressWarnings("unchecked")
public static final ConstructingObjectParser<GetJobStatsResponse, Void> PARSER =
new ConstructingObjectParser<>("jobs_stats_response", true,
a -> new GetJobStatsResponse((List<JobStats>) a[0], (long) a[1]));

static {
PARSER.declareObjectArray(constructorArg(), JobStats.PARSER, RESULTS_FIELD);
PARSER.declareLong(constructorArg(), COUNT);
}

GetJobStatsResponse(List<JobStats> jobStats, long count) {
super(RESULTS_FIELD, jobStats, count);
}

/**
* The collection of {@link JobStats} objects found in the query
*/
public List<JobStats> jobStats() {
return results;
}

public static GetJobStatsResponse fromXContent(XContentParser parser) throws IOException {
return PARSER.parse(parser, null);
}

@Override
public int hashCode() {
return Objects.hash(results, count);
}

@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}

if (obj == null || getClass() != obj.getClass()) {
return false;
}

GetJobStatsResponse other = (GetJobStatsResponse) obj;
return Objects.equals(results, other.results) && count == other.count;
}

@Override
public final String toString() {
return Strings.toString(this);
}
}
Loading

0 comments on commit 19b14fa

Please sign in to comment.