Skip to content

Commit

Permalink
[ML] Add Detector config classes to protocol library (#32495)
Browse files Browse the repository at this point in the history
This commit adds the Detector class and its dependencies to the
X-Pack protocol library used by the high level REST client.

(Future commits will add the remaining config classes, plus results
and stats classes.)

These classes:

- Are immutable, with builders, but the builders do no validation
  beyond null checks
- Are convertible to and from X-Content, but NOT wire transportable
- Have lenient parsers to maximize compatibility across versions
- Have the same class names, member names and getter/setter names
  as the corresponding classes in X-Pack core to ease migration
  for transport client users
- Don't reproduce all the methods that do calculations or
  transformations that the the corresponding classes in X-Pack core
  have
  • Loading branch information
droberts195 committed Aug 3, 2018
1 parent 82e2385 commit 8f1d119
Show file tree
Hide file tree
Showing 17 changed files with 1,870 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
/*
* 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.protocol.xpack.ml.job.config;

import org.elasticsearch.common.Strings;

public final class DefaultDetectorDescription {
private static final String BY_TOKEN = " by ";
private static final String OVER_TOKEN = " over ";

private static final String USE_NULL_OPTION = " usenull=";
private static final String PARTITION_FIELD_OPTION = " partitionfield=";
private static final String EXCLUDE_FREQUENT_OPTION = " excludefrequent=";

private DefaultDetectorDescription() {
}

/**
* Returns the default description for the given {@code detector}
*
* @param detector the {@code Detector} for which a default description is requested
* @return the default description
*/
public static String of(Detector detector) {
StringBuilder sb = new StringBuilder();
appendOn(detector, sb);
return sb.toString();
}

/**
* Appends to the given {@code StringBuilder} the default description
* for the given {@code detector}
*
* @param detector the {@code Detector} for which a default description is requested
* @param sb the {@code StringBuilder} to append to
*/
public static void appendOn(Detector detector, StringBuilder sb) {
if (isNotNullOrEmpty(detector.getFunction().getFullName())) {
sb.append(detector.getFunction());
if (isNotNullOrEmpty(detector.getFieldName())) {
sb.append('(').append(quoteField(detector.getFieldName()))
.append(')');
}
} else if (isNotNullOrEmpty(detector.getFieldName())) {
sb.append(quoteField(detector.getFieldName()));
}

if (isNotNullOrEmpty(detector.getByFieldName())) {
sb.append(BY_TOKEN).append(quoteField(detector.getByFieldName()));
}

if (isNotNullOrEmpty(detector.getOverFieldName())) {
sb.append(OVER_TOKEN).append(quoteField(detector.getOverFieldName()));
}

if (detector.isUseNull()) {
sb.append(USE_NULL_OPTION).append(detector.isUseNull());
}

if (isNotNullOrEmpty(detector.getPartitionFieldName())) {
sb.append(PARTITION_FIELD_OPTION).append(quoteField(detector.getPartitionFieldName()));
}

if (detector.getExcludeFrequent() != null) {
sb.append(EXCLUDE_FREQUENT_OPTION).append(detector.getExcludeFrequent());
}
}

private static String quoteField(String field) {
if (field.matches("\\w*")) {
return field;
} else {
return "\"" + field.replace("\\", "\\\\").replace("\"", "\\\"") + "\"";
}
}

private static boolean isNotNullOrEmpty(String arg) {
return !Strings.isNullOrEmpty(arg);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
/*
* 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.protocol.xpack.ml.job.config;

import org.elasticsearch.common.ParseField;
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.Arrays;
import java.util.Collections;
import java.util.EnumSet;
import java.util.List;
import java.util.Objects;

public class DetectionRule implements ToXContentObject {

public static final ParseField DETECTION_RULE_FIELD = new ParseField("detection_rule");
public static final ParseField ACTIONS_FIELD = new ParseField("actions");
public static final ParseField SCOPE_FIELD = new ParseField("scope");
public static final ParseField CONDITIONS_FIELD = new ParseField("conditions");

public static final ObjectParser<Builder, Void> PARSER =
new ObjectParser<>(DETECTION_RULE_FIELD.getPreferredName(), true, Builder::new);;

static {
PARSER.declareStringArray(Builder::setActions, ACTIONS_FIELD);
PARSER.declareObject(Builder::setScope, RuleScope.parser(), SCOPE_FIELD);
PARSER.declareObjectArray(Builder::setConditions, RuleCondition.PARSER, CONDITIONS_FIELD);
}

private final EnumSet<RuleAction> actions;
private final RuleScope scope;
private final List<RuleCondition> conditions;

private DetectionRule(EnumSet<RuleAction> actions, RuleScope scope, List<RuleCondition> conditions) {
this.actions = Objects.requireNonNull(actions);
this.scope = Objects.requireNonNull(scope);
this.conditions = Collections.unmodifiableList(conditions);
}

@Override
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
builder.startObject();
builder.field(ACTIONS_FIELD.getPreferredName(), actions);
if (scope.isEmpty() == false) {
builder.field(SCOPE_FIELD.getPreferredName(), scope);
}
if (conditions.isEmpty() == false) {
builder.field(CONDITIONS_FIELD.getPreferredName(), conditions);
}
builder.endObject();
return builder;
}

public EnumSet<RuleAction> getActions() {
return actions;
}

public RuleScope getScope() {
return scope;
}

public List<RuleCondition> getConditions() {
return conditions;
}

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

if (obj instanceof DetectionRule == false) {
return false;
}

DetectionRule other = (DetectionRule) obj;
return Objects.equals(actions, other.actions)
&& Objects.equals(scope, other.scope)
&& Objects.equals(conditions, other.conditions);
}

@Override
public int hashCode() {
return Objects.hash(actions, scope, conditions);
}

public static class Builder {
private EnumSet<RuleAction> actions = EnumSet.of(RuleAction.SKIP_RESULT);
private RuleScope scope = new RuleScope();
private List<RuleCondition> conditions = Collections.emptyList();

public Builder(RuleScope.Builder scope) {
this.scope = scope.build();
}

public Builder(List<RuleCondition> conditions) {
this.conditions = Objects.requireNonNull(conditions);
}

Builder() {
}

public Builder setActions(List<String> actions) {
this.actions.clear();
actions.stream().map(RuleAction::fromString).forEach(this.actions::add);
return this;
}

public Builder setActions(EnumSet<RuleAction> actions) {
this.actions = Objects.requireNonNull(actions, ACTIONS_FIELD.getPreferredName());
return this;
}

public Builder setActions(RuleAction... actions) {
this.actions.clear();
Arrays.stream(actions).forEach(this.actions::add);
return this;
}

public Builder setScope(RuleScope scope) {
this.scope = Objects.requireNonNull(scope);
return this;
}

public Builder setConditions(List<RuleCondition> conditions) {
this.conditions = Objects.requireNonNull(conditions);
return this;
}

public DetectionRule build() {
return new DetectionRule(actions, scope, conditions);
}
}
}
Loading

0 comments on commit 8f1d119

Please sign in to comment.