[ML] Add Detector config classes to protocol library (#32495)
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
This commit is contained in:
parent
937dcfd716
commit
eb17128b9c
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,362 @@
|
|||
/*
|
||||
* 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 org.elasticsearch.common.xcontent.XContentParser;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* Defines the fields and functions used in the analysis. A combination of <code>field_name</code>,
|
||||
* <code>by_field_name</code> and <code>over_field_name</code> can be used depending on the specific
|
||||
* function chosen. For more information see
|
||||
* <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/ml-job-resource.html#ml-detectorconfig">configuring
|
||||
* detectors</a> and <a href="https://www.elastic.co/guide/en/elastic-stack-overview/current/ml-functions.html">detector functions</a>.
|
||||
*/
|
||||
public class Detector implements ToXContentObject {
|
||||
|
||||
public enum ExcludeFrequent {
|
||||
ALL,
|
||||
NONE,
|
||||
BY,
|
||||
OVER;
|
||||
|
||||
/**
|
||||
* Case-insensitive from string method.
|
||||
* Works with either ALL, All, etc.
|
||||
*
|
||||
* @param value String representation
|
||||
* @return The data format
|
||||
*/
|
||||
public static ExcludeFrequent forString(String value) {
|
||||
return valueOf(value.toUpperCase(Locale.ROOT));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return name().toLowerCase(Locale.ROOT);
|
||||
}
|
||||
}
|
||||
|
||||
public static final ParseField DETECTOR_DESCRIPTION_FIELD = new ParseField("detector_description");
|
||||
public static final ParseField FUNCTION_FIELD = new ParseField("function");
|
||||
public static final ParseField FIELD_NAME_FIELD = new ParseField("field_name");
|
||||
public static final ParseField BY_FIELD_NAME_FIELD = new ParseField("by_field_name");
|
||||
public static final ParseField OVER_FIELD_NAME_FIELD = new ParseField("over_field_name");
|
||||
public static final ParseField PARTITION_FIELD_NAME_FIELD = new ParseField("partition_field_name");
|
||||
public static final ParseField USE_NULL_FIELD = new ParseField("use_null");
|
||||
public static final ParseField EXCLUDE_FREQUENT_FIELD = new ParseField("exclude_frequent");
|
||||
public static final ParseField CUSTOM_RULES_FIELD = new ParseField("custom_rules");
|
||||
public static final ParseField DETECTOR_INDEX = new ParseField("detector_index");
|
||||
|
||||
public static final ObjectParser<Builder, Void> PARSER = new ObjectParser<>("detector", true, Builder::new);
|
||||
|
||||
static {
|
||||
PARSER.declareString(Builder::setDetectorDescription, DETECTOR_DESCRIPTION_FIELD);
|
||||
PARSER.declareString(Builder::setFunction, FUNCTION_FIELD);
|
||||
PARSER.declareString(Builder::setFieldName, FIELD_NAME_FIELD);
|
||||
PARSER.declareString(Builder::setByFieldName, BY_FIELD_NAME_FIELD);
|
||||
PARSER.declareString(Builder::setOverFieldName, OVER_FIELD_NAME_FIELD);
|
||||
PARSER.declareString(Builder::setPartitionFieldName, PARTITION_FIELD_NAME_FIELD);
|
||||
PARSER.declareBoolean(Builder::setUseNull, USE_NULL_FIELD);
|
||||
PARSER.declareField(Builder::setExcludeFrequent, p -> {
|
||||
if (p.currentToken() == XContentParser.Token.VALUE_STRING) {
|
||||
return ExcludeFrequent.forString(p.text());
|
||||
}
|
||||
throw new IllegalArgumentException("Unsupported token [" + p.currentToken() + "]");
|
||||
}, EXCLUDE_FREQUENT_FIELD, ObjectParser.ValueType.STRING);
|
||||
PARSER.declareObjectArray(Builder::setRules, (p, c) -> DetectionRule.PARSER.apply(p, c).build(), CUSTOM_RULES_FIELD);
|
||||
PARSER.declareInt(Builder::setDetectorIndex, DETECTOR_INDEX);
|
||||
}
|
||||
|
||||
private final String detectorDescription;
|
||||
private final DetectorFunction function;
|
||||
private final String fieldName;
|
||||
private final String byFieldName;
|
||||
private final String overFieldName;
|
||||
private final String partitionFieldName;
|
||||
private final boolean useNull;
|
||||
private final ExcludeFrequent excludeFrequent;
|
||||
private final List<DetectionRule> rules;
|
||||
private final int detectorIndex;
|
||||
|
||||
@Override
|
||||
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
|
||||
builder.startObject();
|
||||
builder.field(DETECTOR_DESCRIPTION_FIELD.getPreferredName(), detectorDescription);
|
||||
builder.field(FUNCTION_FIELD.getPreferredName(), function);
|
||||
if (fieldName != null) {
|
||||
builder.field(FIELD_NAME_FIELD.getPreferredName(), fieldName);
|
||||
}
|
||||
if (byFieldName != null) {
|
||||
builder.field(BY_FIELD_NAME_FIELD.getPreferredName(), byFieldName);
|
||||
}
|
||||
if (overFieldName != null) {
|
||||
builder.field(OVER_FIELD_NAME_FIELD.getPreferredName(), overFieldName);
|
||||
}
|
||||
if (partitionFieldName != null) {
|
||||
builder.field(PARTITION_FIELD_NAME_FIELD.getPreferredName(), partitionFieldName);
|
||||
}
|
||||
if (useNull) {
|
||||
builder.field(USE_NULL_FIELD.getPreferredName(), useNull);
|
||||
}
|
||||
if (excludeFrequent != null) {
|
||||
builder.field(EXCLUDE_FREQUENT_FIELD.getPreferredName(), excludeFrequent);
|
||||
}
|
||||
if (rules.isEmpty() == false) {
|
||||
builder.field(CUSTOM_RULES_FIELD.getPreferredName(), rules);
|
||||
}
|
||||
// negative means unknown
|
||||
if (detectorIndex >= 0) {
|
||||
builder.field(DETECTOR_INDEX.getPreferredName(), detectorIndex);
|
||||
}
|
||||
builder.endObject();
|
||||
return builder;
|
||||
}
|
||||
|
||||
private Detector(String detectorDescription, DetectorFunction function, String fieldName, String byFieldName, String overFieldName,
|
||||
String partitionFieldName, boolean useNull, ExcludeFrequent excludeFrequent, List<DetectionRule> rules,
|
||||
int detectorIndex) {
|
||||
this.function = function;
|
||||
this.fieldName = fieldName;
|
||||
this.byFieldName = byFieldName;
|
||||
this.overFieldName = overFieldName;
|
||||
this.partitionFieldName = partitionFieldName;
|
||||
this.useNull = useNull;
|
||||
this.excludeFrequent = excludeFrequent;
|
||||
this.rules = Collections.unmodifiableList(rules);
|
||||
this.detectorDescription = detectorDescription != null ? detectorDescription : DefaultDetectorDescription.of(this);
|
||||
this.detectorIndex = detectorIndex;
|
||||
}
|
||||
|
||||
public String getDetectorDescription() {
|
||||
return detectorDescription;
|
||||
}
|
||||
|
||||
/**
|
||||
* The analysis function used e.g. count, rare, min etc.
|
||||
*
|
||||
* @return The function or <code>null</code> if not set
|
||||
*/
|
||||
public DetectorFunction getFunction() {
|
||||
return function;
|
||||
}
|
||||
|
||||
/**
|
||||
* The Analysis field
|
||||
*
|
||||
* @return The field to analyse
|
||||
*/
|
||||
public String getFieldName() {
|
||||
return fieldName;
|
||||
}
|
||||
|
||||
/**
|
||||
* The 'by' field or <code>null</code> if not set.
|
||||
*
|
||||
* @return The 'by' field
|
||||
*/
|
||||
public String getByFieldName() {
|
||||
return byFieldName;
|
||||
}
|
||||
|
||||
/**
|
||||
* The 'over' field or <code>null</code> if not set.
|
||||
*
|
||||
* @return The 'over' field
|
||||
*/
|
||||
public String getOverFieldName() {
|
||||
return overFieldName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Segments the analysis along another field to have completely
|
||||
* independent baselines for each instance of partitionfield
|
||||
*
|
||||
* @return The Partition Field
|
||||
*/
|
||||
public String getPartitionFieldName() {
|
||||
return partitionFieldName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Where there isn't a value for the 'by' or 'over' field should a new
|
||||
* series be used as the 'null' series.
|
||||
*
|
||||
* @return true if the 'null' series should be created
|
||||
*/
|
||||
public boolean isUseNull() {
|
||||
return useNull;
|
||||
}
|
||||
|
||||
/**
|
||||
* Excludes frequently-occuring metrics from the analysis;
|
||||
* can apply to 'by' field, 'over' field, or both
|
||||
*
|
||||
* @return the value that the user set
|
||||
*/
|
||||
public ExcludeFrequent getExcludeFrequent() {
|
||||
return excludeFrequent;
|
||||
}
|
||||
|
||||
public List<DetectionRule> getRules() {
|
||||
return rules;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the detector index or a negative number if unknown
|
||||
*/
|
||||
public int getDetectorIndex() {
|
||||
return detectorIndex;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object other) {
|
||||
if (this == other) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (other instanceof Detector == false) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Detector that = (Detector) other;
|
||||
|
||||
return Objects.equals(this.detectorDescription, that.detectorDescription) &&
|
||||
Objects.equals(this.function, that.function) &&
|
||||
Objects.equals(this.fieldName, that.fieldName) &&
|
||||
Objects.equals(this.byFieldName, that.byFieldName) &&
|
||||
Objects.equals(this.overFieldName, that.overFieldName) &&
|
||||
Objects.equals(this.partitionFieldName, that.partitionFieldName) &&
|
||||
Objects.equals(this.useNull, that.useNull) &&
|
||||
Objects.equals(this.excludeFrequent, that.excludeFrequent) &&
|
||||
Objects.equals(this.rules, that.rules) &&
|
||||
this.detectorIndex == that.detectorIndex;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(detectorDescription, function, fieldName, byFieldName, overFieldName, partitionFieldName, useNull,
|
||||
excludeFrequent, rules, detectorIndex);
|
||||
}
|
||||
|
||||
public static class Builder {
|
||||
|
||||
private String detectorDescription;
|
||||
private DetectorFunction function;
|
||||
private String fieldName;
|
||||
private String byFieldName;
|
||||
private String overFieldName;
|
||||
private String partitionFieldName;
|
||||
private boolean useNull = false;
|
||||
private ExcludeFrequent excludeFrequent;
|
||||
private List<DetectionRule> rules = Collections.emptyList();
|
||||
// negative means unknown
|
||||
private int detectorIndex = -1;
|
||||
|
||||
public Builder() {
|
||||
}
|
||||
|
||||
public Builder(Detector detector) {
|
||||
detectorDescription = detector.detectorDescription;
|
||||
function = detector.function;
|
||||
fieldName = detector.fieldName;
|
||||
byFieldName = detector.byFieldName;
|
||||
overFieldName = detector.overFieldName;
|
||||
partitionFieldName = detector.partitionFieldName;
|
||||
useNull = detector.useNull;
|
||||
excludeFrequent = detector.excludeFrequent;
|
||||
rules = new ArrayList<>(detector.rules);
|
||||
detectorIndex = detector.detectorIndex;
|
||||
}
|
||||
|
||||
public Builder(String function, String fieldName) {
|
||||
this(DetectorFunction.fromString(function), fieldName);
|
||||
}
|
||||
|
||||
public Builder(DetectorFunction function, String fieldName) {
|
||||
this.function = function;
|
||||
this.fieldName = fieldName;
|
||||
}
|
||||
|
||||
public Builder setDetectorDescription(String detectorDescription) {
|
||||
this.detectorDescription = detectorDescription;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setFunction(String function) {
|
||||
this.function = DetectorFunction.fromString(function);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setFieldName(String fieldName) {
|
||||
this.fieldName = fieldName;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setByFieldName(String byFieldName) {
|
||||
this.byFieldName = byFieldName;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setOverFieldName(String overFieldName) {
|
||||
this.overFieldName = overFieldName;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setPartitionFieldName(String partitionFieldName) {
|
||||
this.partitionFieldName = partitionFieldName;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setUseNull(boolean useNull) {
|
||||
this.useNull = useNull;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setExcludeFrequent(ExcludeFrequent excludeFrequent) {
|
||||
this.excludeFrequent = excludeFrequent;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setRules(List<DetectionRule> rules) {
|
||||
this.rules = rules;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setDetectorIndex(int detectorIndex) {
|
||||
this.detectorIndex = detectorIndex;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Detector build() {
|
||||
return new Detector(detectorDescription, function, fieldName, byFieldName, overFieldName, partitionFieldName,
|
||||
useNull, excludeFrequent, rules, detectorIndex);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,95 @@
|
|||
/*
|
||||
* 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 java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.Locale;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public enum DetectorFunction {
|
||||
|
||||
COUNT,
|
||||
LOW_COUNT,
|
||||
HIGH_COUNT,
|
||||
NON_ZERO_COUNT("nzc"),
|
||||
LOW_NON_ZERO_COUNT("low_nzc"),
|
||||
HIGH_NON_ZERO_COUNT("high_nzc"),
|
||||
DISTINCT_COUNT("dc"),
|
||||
LOW_DISTINCT_COUNT("low_dc"),
|
||||
HIGH_DISTINCT_COUNT("high_dc"),
|
||||
RARE,
|
||||
FREQ_RARE,
|
||||
INFO_CONTENT,
|
||||
LOW_INFO_CONTENT,
|
||||
HIGH_INFO_CONTENT,
|
||||
METRIC,
|
||||
MEAN,
|
||||
LOW_MEAN,
|
||||
HIGH_MEAN,
|
||||
AVG,
|
||||
LOW_AVG,
|
||||
HIGH_AVG,
|
||||
MEDIAN,
|
||||
LOW_MEDIAN,
|
||||
HIGH_MEDIAN,
|
||||
MIN,
|
||||
MAX,
|
||||
SUM,
|
||||
LOW_SUM,
|
||||
HIGH_SUM,
|
||||
NON_NULL_SUM,
|
||||
LOW_NON_NULL_SUM,
|
||||
HIGH_NON_NULL_SUM,
|
||||
VARP,
|
||||
LOW_VARP,
|
||||
HIGH_VARP,
|
||||
TIME_OF_DAY,
|
||||
TIME_OF_WEEK,
|
||||
LAT_LONG;
|
||||
|
||||
private Set<String> shortcuts;
|
||||
|
||||
DetectorFunction() {
|
||||
shortcuts = Collections.emptySet();
|
||||
}
|
||||
|
||||
DetectorFunction(String... shortcuts) {
|
||||
this.shortcuts = Arrays.stream(shortcuts).collect(Collectors.toSet());
|
||||
}
|
||||
|
||||
public String getFullName() {
|
||||
return name().toLowerCase(Locale.ROOT);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return getFullName();
|
||||
}
|
||||
|
||||
public static DetectorFunction fromString(String op) {
|
||||
for (DetectorFunction function : values()) {
|
||||
if (function.getFullName().equals(op) || function.shortcuts.contains(op)) {
|
||||
return function;
|
||||
}
|
||||
}
|
||||
throw new IllegalArgumentException("Unknown detector function [" + op + "]");
|
||||
}
|
||||
}
|
|
@ -0,0 +1,107 @@
|
|||
/*
|
||||
* 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.ConstructingObjectParser;
|
||||
import org.elasticsearch.common.xcontent.ObjectParser;
|
||||
import org.elasticsearch.common.xcontent.ToXContentObject;
|
||||
import org.elasticsearch.common.xcontent.XContentBuilder;
|
||||
import org.elasticsearch.common.xcontent.XContentParser;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Locale;
|
||||
import java.util.Objects;
|
||||
|
||||
public class FilterRef implements ToXContentObject {
|
||||
|
||||
public static final ParseField FILTER_REF_FIELD = new ParseField("filter_ref");
|
||||
public static final ParseField FILTER_ID = new ParseField("filter_id");
|
||||
public static final ParseField FILTER_TYPE = new ParseField("filter_type");
|
||||
|
||||
public enum FilterType {
|
||||
INCLUDE, EXCLUDE;
|
||||
|
||||
public static FilterType fromString(String value) {
|
||||
return valueOf(value.toUpperCase(Locale.ROOT));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return name().toLowerCase(Locale.ROOT);
|
||||
}
|
||||
}
|
||||
|
||||
public static final ConstructingObjectParser<FilterRef, Void> PARSER =
|
||||
new ConstructingObjectParser<>(FILTER_REF_FIELD.getPreferredName(), true, a -> new FilterRef((String) a[0], (FilterType) a[1]));
|
||||
|
||||
static {
|
||||
PARSER.declareString(ConstructingObjectParser.constructorArg(), FILTER_ID);
|
||||
PARSER.declareField(ConstructingObjectParser.optionalConstructorArg(), p -> {
|
||||
if (p.currentToken() == XContentParser.Token.VALUE_STRING) {
|
||||
return FilterType.fromString(p.text());
|
||||
}
|
||||
throw new IllegalArgumentException("Unsupported token [" + p.currentToken() + "]");
|
||||
}, FILTER_TYPE, ObjectParser.ValueType.STRING);
|
||||
}
|
||||
|
||||
private final String filterId;
|
||||
private final FilterType filterType;
|
||||
|
||||
public FilterRef(String filterId, FilterType filterType) {
|
||||
this.filterId = Objects.requireNonNull(filterId);
|
||||
this.filterType = filterType == null ? FilterType.INCLUDE : filterType;
|
||||
}
|
||||
|
||||
@Override
|
||||
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
|
||||
builder.startObject();
|
||||
builder.field(FILTER_ID.getPreferredName(), filterId);
|
||||
builder.field(FILTER_TYPE.getPreferredName(), filterType);
|
||||
builder.endObject();
|
||||
return builder;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (obj instanceof FilterRef == false) {
|
||||
return false;
|
||||
}
|
||||
|
||||
FilterRef other = (FilterRef) obj;
|
||||
return Objects.equals(filterId, other.filterId) && Objects.equals(filterType, other.filterType);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(filterId, filterType);
|
||||
}
|
||||
|
||||
public String getFilterId() {
|
||||
return filterId;
|
||||
}
|
||||
|
||||
public FilterType getFilterType() {
|
||||
return filterType;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,155 @@
|
|||
/*
|
||||
* 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.Nullable;
|
||||
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.List;
|
||||
import java.util.Objects;
|
||||
import java.util.SortedSet;
|
||||
import java.util.TreeSet;
|
||||
|
||||
public class MlFilter implements ToXContentObject {
|
||||
|
||||
public static final ParseField TYPE = new ParseField("type");
|
||||
public static final ParseField ID = new ParseField("filter_id");
|
||||
public static final ParseField DESCRIPTION = new ParseField("description");
|
||||
public static final ParseField ITEMS = new ParseField("items");
|
||||
|
||||
// For QueryPage
|
||||
public static final ParseField RESULTS_FIELD = new ParseField("filters");
|
||||
|
||||
public static final ObjectParser<Builder, Void> PARSER = new ObjectParser<>(TYPE.getPreferredName(), true, Builder::new);
|
||||
|
||||
static {
|
||||
PARSER.declareString((builder, s) -> {}, TYPE);
|
||||
PARSER.declareString(Builder::setId, ID);
|
||||
PARSER.declareStringOrNull(Builder::setDescription, DESCRIPTION);
|
||||
PARSER.declareStringArray(Builder::setItems, ITEMS);
|
||||
}
|
||||
|
||||
private final String id;
|
||||
private final String description;
|
||||
private final SortedSet<String> items;
|
||||
|
||||
private MlFilter(String id, String description, SortedSet<String> items) {
|
||||
this.id = Objects.requireNonNull(id);
|
||||
this.description = description;
|
||||
this.items = Collections.unmodifiableSortedSet(items);
|
||||
}
|
||||
|
||||
@Override
|
||||
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
|
||||
builder.startObject();
|
||||
builder.field(ID.getPreferredName(), id);
|
||||
if (description != null) {
|
||||
builder.field(DESCRIPTION.getPreferredName(), description);
|
||||
}
|
||||
builder.field(ITEMS.getPreferredName(), items);
|
||||
// Don't include TYPE as it's fixed
|
||||
builder.endObject();
|
||||
return builder;
|
||||
}
|
||||
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public String getDescription() {
|
||||
return description;
|
||||
}
|
||||
|
||||
public SortedSet<String> getItems() {
|
||||
return items;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (obj == this) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (obj instanceof MlFilter == false) {
|
||||
return false;
|
||||
}
|
||||
|
||||
MlFilter other = (MlFilter) obj;
|
||||
return id.equals(other.id) && Objects.equals(description, other.description) && items.equals(other.items);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(id, description, items);
|
||||
}
|
||||
|
||||
public static Builder builder(String filterId) {
|
||||
return new Builder().setId(filterId);
|
||||
}
|
||||
|
||||
public static class Builder {
|
||||
|
||||
private String id;
|
||||
private String description;
|
||||
private SortedSet<String> items = new TreeSet<>();
|
||||
|
||||
private Builder() {
|
||||
}
|
||||
|
||||
public Builder setId(String id) {
|
||||
this.id = Objects.requireNonNull(id);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public Builder setDescription(String description) {
|
||||
this.description = description;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setItems(SortedSet<String> items) {
|
||||
this.items = Objects.requireNonNull(items);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setItems(List<String> items) {
|
||||
this.items = new TreeSet<>(items);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setItems(String... items) {
|
||||
setItems(Arrays.asList(items));
|
||||
return this;
|
||||
}
|
||||
|
||||
public MlFilter build() {
|
||||
return new MlFilter(id, description, items);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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.protocol.xpack.ml.job.config;
|
||||
|
||||
import org.elasticsearch.common.ParseField;
|
||||
|
||||
import java.util.Locale;
|
||||
|
||||
/**
|
||||
* Enum representing logical comparisons on doubles
|
||||
*/
|
||||
public enum Operator {
|
||||
GT {
|
||||
@Override
|
||||
public boolean test(double lhs, double rhs) {
|
||||
return Double.compare(lhs, rhs) > 0;
|
||||
}
|
||||
},
|
||||
GTE {
|
||||
@Override
|
||||
public boolean test(double lhs, double rhs) {
|
||||
return Double.compare(lhs, rhs) >= 0;
|
||||
}
|
||||
},
|
||||
LT {
|
||||
@Override
|
||||
public boolean test(double lhs, double rhs) {
|
||||
return Double.compare(lhs, rhs) < 0;
|
||||
}
|
||||
},
|
||||
LTE {
|
||||
@Override
|
||||
public boolean test(double lhs, double rhs) {
|
||||
return Double.compare(lhs, rhs) <= 0;
|
||||
}
|
||||
};
|
||||
// EQ was considered but given the oddity of such a
|
||||
// condition and the fact that it would be a numerically
|
||||
// unstable condition, it was rejected.
|
||||
|
||||
public static final ParseField OPERATOR_FIELD = new ParseField("operator");
|
||||
|
||||
public boolean test(double lhs, double rhs) {
|
||||
return false;
|
||||
}
|
||||
|
||||
public static Operator fromString(String name) {
|
||||
return valueOf(name.trim().toUpperCase(Locale.ROOT));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return name().toLowerCase(Locale.ROOT);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,41 @@
|
|||
/*
|
||||
* 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 java.util.Locale;
|
||||
|
||||
public enum RuleAction {
|
||||
SKIP_RESULT,
|
||||
SKIP_MODEL_UPDATE;
|
||||
|
||||
/**
|
||||
* Case-insensitive from string method.
|
||||
*
|
||||
* @param value String representation
|
||||
* @return The rule action
|
||||
*/
|
||||
public static RuleAction fromString(String value) {
|
||||
return RuleAction.valueOf(value.toUpperCase(Locale.ROOT));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return name().toLowerCase(Locale.ROOT);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,129 @@
|
|||
/*
|
||||
* 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.ConstructingObjectParser;
|
||||
import org.elasticsearch.common.xcontent.ObjectParser.ValueType;
|
||||
import org.elasticsearch.common.xcontent.ToXContentObject;
|
||||
import org.elasticsearch.common.xcontent.XContentBuilder;
|
||||
import org.elasticsearch.common.xcontent.XContentParser;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Locale;
|
||||
import java.util.Objects;
|
||||
|
||||
public class RuleCondition implements ToXContentObject {
|
||||
|
||||
public static final ParseField RULE_CONDITION_FIELD = new ParseField("rule_condition");
|
||||
|
||||
public static final ParseField APPLIES_TO_FIELD = new ParseField("applies_to");
|
||||
public static final ParseField VALUE_FIELD = new ParseField("value");
|
||||
|
||||
public static final ConstructingObjectParser<RuleCondition, Void> PARSER =
|
||||
new ConstructingObjectParser<>(RULE_CONDITION_FIELD.getPreferredName(), true,
|
||||
a -> new RuleCondition((AppliesTo) a[0], (Operator) a[1], (double) a[2]));
|
||||
|
||||
static {
|
||||
PARSER.declareField(ConstructingObjectParser.constructorArg(), p -> {
|
||||
if (p.currentToken() == XContentParser.Token.VALUE_STRING) {
|
||||
return AppliesTo.fromString(p.text());
|
||||
}
|
||||
throw new IllegalArgumentException("Unsupported token [" + p.currentToken() + "]");
|
||||
}, APPLIES_TO_FIELD, ValueType.STRING);
|
||||
PARSER.declareField(ConstructingObjectParser.constructorArg(), p -> {
|
||||
if (p.currentToken() == XContentParser.Token.VALUE_STRING) {
|
||||
return Operator.fromString(p.text());
|
||||
}
|
||||
throw new IllegalArgumentException("Unsupported token [" + p.currentToken() + "]");
|
||||
}, Operator.OPERATOR_FIELD, ValueType.STRING);
|
||||
PARSER.declareDouble(ConstructingObjectParser.constructorArg(), VALUE_FIELD);
|
||||
}
|
||||
|
||||
private final AppliesTo appliesTo;
|
||||
private final Operator operator;
|
||||
private final double value;
|
||||
|
||||
public RuleCondition(AppliesTo appliesTo, Operator operator, double value) {
|
||||
this.appliesTo = appliesTo;
|
||||
this.operator = operator;
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
|
||||
builder.startObject();
|
||||
builder.field(APPLIES_TO_FIELD.getPreferredName(), appliesTo);
|
||||
builder.field(Operator.OPERATOR_FIELD.getPreferredName(), operator);
|
||||
builder.field(VALUE_FIELD.getPreferredName(), value);
|
||||
builder.endObject();
|
||||
return builder;
|
||||
}
|
||||
|
||||
public AppliesTo getAppliesTo() {
|
||||
return appliesTo;
|
||||
}
|
||||
|
||||
public Operator getOperator() {
|
||||
return operator;
|
||||
}
|
||||
|
||||
public double getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (obj instanceof RuleCondition == false) {
|
||||
return false;
|
||||
}
|
||||
|
||||
RuleCondition other = (RuleCondition) obj;
|
||||
return appliesTo == other.appliesTo && operator == other.operator && value == other.value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(appliesTo, operator, value);
|
||||
}
|
||||
|
||||
public static RuleCondition createTime(Operator operator, long epochSeconds) {
|
||||
return new RuleCondition(AppliesTo.TIME, operator, epochSeconds);
|
||||
}
|
||||
|
||||
public enum AppliesTo {
|
||||
ACTUAL,
|
||||
TYPICAL,
|
||||
DIFF_FROM_TYPICAL,
|
||||
TIME;
|
||||
|
||||
public static AppliesTo fromString(String value) {
|
||||
return valueOf(value.toUpperCase(Locale.ROOT));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return name().toLowerCase(Locale.ROOT);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,133 @@
|
|||
/*
|
||||
* 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;
|
||||
import org.elasticsearch.common.xcontent.ContextParser;
|
||||
import org.elasticsearch.common.xcontent.LoggingDeprecationHandler;
|
||||
import org.elasticsearch.common.xcontent.NamedXContentRegistry;
|
||||
import org.elasticsearch.common.xcontent.ToXContentObject;
|
||||
import org.elasticsearch.common.xcontent.XContentBuilder;
|
||||
import org.elasticsearch.common.xcontent.XContentFactory;
|
||||
import org.elasticsearch.common.xcontent.XContentParser;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class RuleScope implements ToXContentObject {
|
||||
|
||||
public static ContextParser<Void, RuleScope> parser() {
|
||||
return (p, c) -> {
|
||||
Map<String, Object> unparsedScope = p.map();
|
||||
if (unparsedScope.isEmpty()) {
|
||||
return new RuleScope();
|
||||
}
|
||||
Map<String, FilterRef> scope = new HashMap<>();
|
||||
for (Map.Entry<String, Object> entry : unparsedScope.entrySet()) {
|
||||
try (XContentBuilder builder = XContentFactory.jsonBuilder()) {
|
||||
@SuppressWarnings("unchecked")
|
||||
Map<String, ?> value = (Map<String, ?>) entry.getValue();
|
||||
builder.map(value);
|
||||
try (XContentParser scopeParser = XContentFactory.xContent(builder.contentType()).createParser(
|
||||
NamedXContentRegistry.EMPTY, LoggingDeprecationHandler.INSTANCE, Strings.toString(builder))) {
|
||||
scope.put(entry.getKey(), FilterRef.PARSER.parse(scopeParser, null));
|
||||
}
|
||||
}
|
||||
}
|
||||
return new RuleScope(scope);
|
||||
};
|
||||
}
|
||||
|
||||
private final Map<String, FilterRef> scope;
|
||||
|
||||
public RuleScope() {
|
||||
scope = Collections.emptyMap();
|
||||
}
|
||||
|
||||
public RuleScope(Map<String, FilterRef> scope) {
|
||||
this.scope = Collections.unmodifiableMap(scope);
|
||||
}
|
||||
|
||||
@Override
|
||||
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
|
||||
return builder.map(scope);
|
||||
}
|
||||
|
||||
public boolean isEmpty() {
|
||||
return scope.isEmpty();
|
||||
}
|
||||
|
||||
public Set<String> getReferencedFilters() {
|
||||
return scope.values().stream().map(FilterRef::getFilterId).collect(Collectors.toSet());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (obj instanceof RuleScope == false) {
|
||||
return false;
|
||||
}
|
||||
|
||||
RuleScope other = (RuleScope) obj;
|
||||
return Objects.equals(scope, other.scope);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(scope);
|
||||
}
|
||||
|
||||
public static Builder builder() {
|
||||
return new Builder();
|
||||
}
|
||||
|
||||
public static class Builder {
|
||||
|
||||
private Map<String, FilterRef> scope = new HashMap<>();
|
||||
|
||||
public Builder() {
|
||||
}
|
||||
|
||||
public Builder(RuleScope otherScope) {
|
||||
scope = new HashMap<>(otherScope.scope);
|
||||
}
|
||||
|
||||
public Builder exclude(String field, String filterId) {
|
||||
scope.put(field, new FilterRef(filterId, FilterRef.FilterType.EXCLUDE));
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder include(String field, String filterId) {
|
||||
scope.put(field, new FilterRef(filterId, FilterRef.FilterType.INCLUDE));
|
||||
return this;
|
||||
}
|
||||
|
||||
public RuleScope build() {
|
||||
return new RuleScope(scope);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Request and Response objects for the default distribution's Machine
|
||||
* Learning APIs.
|
||||
*/
|
||||
package org.elasticsearch.protocol.xpack.ml;
|
|
@ -0,0 +1,93 @@
|
|||
/*
|
||||
* 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.xcontent.XContentParser;
|
||||
import org.elasticsearch.test.AbstractXContentTestCase;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.EnumSet;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public class DetectionRuleTests extends AbstractXContentTestCase<DetectionRule> {
|
||||
|
||||
@Override
|
||||
protected DetectionRule createTestInstance() {
|
||||
DetectionRule.Builder builder = new DetectionRule.Builder();
|
||||
|
||||
if (randomBoolean()) {
|
||||
EnumSet<RuleAction> actions = EnumSet.noneOf(RuleAction.class);
|
||||
int actionsCount = randomIntBetween(1, RuleAction.values().length);
|
||||
for (int i = 0; i < actionsCount; ++i) {
|
||||
actions.add(randomFrom(RuleAction.values()));
|
||||
}
|
||||
builder.setActions(actions);
|
||||
}
|
||||
|
||||
boolean hasScope = randomBoolean();
|
||||
boolean hasConditions = randomBoolean();
|
||||
|
||||
if (!hasScope && !hasConditions) {
|
||||
// at least one of the two should be present
|
||||
if (randomBoolean()) {
|
||||
hasScope = true;
|
||||
} else {
|
||||
hasConditions = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (hasScope) {
|
||||
Map<String, FilterRef> scope = new HashMap<>();
|
||||
int scopeSize = randomIntBetween(1, 3);
|
||||
for (int i = 0; i < scopeSize; i++) {
|
||||
scope.put(randomAlphaOfLength(20), new FilterRef(randomAlphaOfLength(20), randomFrom(FilterRef.FilterType.values())));
|
||||
}
|
||||
builder.setScope(new RuleScope(scope));
|
||||
}
|
||||
|
||||
if (hasConditions) {
|
||||
int size = randomIntBetween(1, 5);
|
||||
List<RuleCondition> ruleConditions = new ArrayList<>(size);
|
||||
for (int i = 0; i < size; i++) {
|
||||
// no need for random condition (it is already tested)
|
||||
ruleConditions.addAll(createCondition(randomDouble()));
|
||||
}
|
||||
builder.setConditions(ruleConditions);
|
||||
}
|
||||
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected DetectionRule doParseInstance(XContentParser parser) {
|
||||
return DetectionRule.PARSER.apply(parser, null).build();
|
||||
}
|
||||
|
||||
private static List<RuleCondition> createCondition(double value) {
|
||||
return Collections.singletonList(new RuleCondition(RuleCondition.AppliesTo.ACTUAL, Operator.GT, value));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean supportsUnknownFields() {
|
||||
return false;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,137 @@
|
|||
/*
|
||||
* 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.xcontent.XContentParser;
|
||||
import org.elasticsearch.test.AbstractXContentTestCase;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.EnumSet;
|
||||
import java.util.List;
|
||||
|
||||
public class DetectorTests extends AbstractXContentTestCase<Detector> {
|
||||
|
||||
public void testEquals_GivenEqual() {
|
||||
Detector.Builder builder = new Detector.Builder("mean", "field");
|
||||
builder.setByFieldName("by_field");
|
||||
builder.setOverFieldName("over_field");
|
||||
builder.setPartitionFieldName("partition");
|
||||
builder.setUseNull(false);
|
||||
Detector detector1 = builder.build();
|
||||
|
||||
builder = new Detector.Builder("mean", "field");
|
||||
builder.setByFieldName("by_field");
|
||||
builder.setOverFieldName("over_field");
|
||||
builder.setPartitionFieldName("partition");
|
||||
builder.setUseNull(false);
|
||||
Detector detector2 = builder.build();
|
||||
|
||||
assertTrue(detector1.equals(detector2));
|
||||
assertTrue(detector2.equals(detector1));
|
||||
assertEquals(detector1.hashCode(), detector2.hashCode());
|
||||
}
|
||||
|
||||
public void testEquals_GivenDifferentDetectorDescription() {
|
||||
Detector detector1 = createDetector().build();
|
||||
Detector.Builder builder = createDetector();
|
||||
builder.setDetectorDescription("bar");
|
||||
Detector detector2 = builder.build();
|
||||
|
||||
assertFalse(detector1.equals(detector2));
|
||||
}
|
||||
|
||||
public void testEquals_GivenDifferentByFieldName() {
|
||||
Detector detector1 = createDetector().build();
|
||||
Detector detector2 = createDetector().build();
|
||||
|
||||
assertEquals(detector1, detector2);
|
||||
|
||||
Detector.Builder builder = new Detector.Builder(detector2);
|
||||
builder.setByFieldName("by2");
|
||||
detector2 = builder.build();
|
||||
assertFalse(detector1.equals(detector2));
|
||||
}
|
||||
|
||||
private Detector.Builder createDetector() {
|
||||
Detector.Builder detector = new Detector.Builder("mean", "field");
|
||||
detector.setByFieldName("by_field");
|
||||
detector.setOverFieldName("over_field");
|
||||
detector.setPartitionFieldName("partition");
|
||||
detector.setUseNull(true);
|
||||
DetectionRule rule = new DetectionRule.Builder(RuleScope.builder().exclude("partition", "partition_filter"))
|
||||
.setActions(RuleAction.SKIP_RESULT)
|
||||
.build();
|
||||
detector.setRules(Collections.singletonList(rule));
|
||||
return detector;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Detector createTestInstance() {
|
||||
DetectorFunction function = randomFrom(EnumSet.allOf(DetectorFunction.class));
|
||||
Detector.Builder detector = new Detector.Builder(function, randomBoolean() ? null : randomAlphaOfLengthBetween(1, 20));
|
||||
if (randomBoolean()) {
|
||||
detector.setDetectorDescription(randomAlphaOfLengthBetween(1, 20));
|
||||
}
|
||||
if (randomBoolean()) {
|
||||
detector.setPartitionFieldName(randomAlphaOfLengthBetween(6, 20));
|
||||
} else if (randomBoolean()) {
|
||||
detector.setOverFieldName(randomAlphaOfLengthBetween(6, 20));
|
||||
} else if (randomBoolean()) {
|
||||
detector.setByFieldName(randomAlphaOfLengthBetween(6, 20));
|
||||
}
|
||||
if (randomBoolean()) {
|
||||
detector.setExcludeFrequent(randomFrom(Detector.ExcludeFrequent.values()));
|
||||
}
|
||||
if (randomBoolean()) {
|
||||
int size = randomInt(10);
|
||||
List<DetectionRule> rules = new ArrayList<>(size);
|
||||
for (int i = 0; i < size; i++) {
|
||||
// no need for random DetectionRule (it is already tested)
|
||||
rules.add(new DetectionRule.Builder(Collections.singletonList(RuleConditionTests.createRandom())).build());
|
||||
}
|
||||
detector.setRules(rules);
|
||||
}
|
||||
if (randomBoolean()) {
|
||||
detector.setUseNull(randomBoolean());
|
||||
}
|
||||
return detector.build();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Detector doParseInstance(XContentParser parser) {
|
||||
return Detector.PARSER.apply(parser, null).build();
|
||||
}
|
||||
|
||||
public void testExcludeFrequentForString() {
|
||||
assertEquals(Detector.ExcludeFrequent.ALL, Detector.ExcludeFrequent.forString("all"));
|
||||
assertEquals(Detector.ExcludeFrequent.ALL, Detector.ExcludeFrequent.forString("ALL"));
|
||||
assertEquals(Detector.ExcludeFrequent.NONE, Detector.ExcludeFrequent.forString("none"));
|
||||
assertEquals(Detector.ExcludeFrequent.NONE, Detector.ExcludeFrequent.forString("NONE"));
|
||||
assertEquals(Detector.ExcludeFrequent.BY, Detector.ExcludeFrequent.forString("by"));
|
||||
assertEquals(Detector.ExcludeFrequent.BY, Detector.ExcludeFrequent.forString("BY"));
|
||||
assertEquals(Detector.ExcludeFrequent.OVER, Detector.ExcludeFrequent.forString("over"));
|
||||
assertEquals(Detector.ExcludeFrequent.OVER, Detector.ExcludeFrequent.forString("OVER"));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean supportsUnknownFields() {
|
||||
return true;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,42 @@
|
|||
/*
|
||||
* 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.xcontent.XContentParser;
|
||||
import org.elasticsearch.test.AbstractXContentTestCase;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
public class FilterRefTests extends AbstractXContentTestCase<FilterRef> {
|
||||
|
||||
@Override
|
||||
protected FilterRef createTestInstance() {
|
||||
return new FilterRef(randomAlphaOfLength(20), randomFrom(FilterRef.FilterType.values()));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected FilterRef doParseInstance(XContentParser parser) throws IOException {
|
||||
return FilterRef.PARSER.parse(parser, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean supportsUnknownFields() {
|
||||
return true;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,86 @@
|
|||
/*
|
||||
* 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.xcontent.XContentParser;
|
||||
import org.elasticsearch.test.AbstractXContentTestCase;
|
||||
|
||||
import java.util.SortedSet;
|
||||
import java.util.TreeSet;
|
||||
|
||||
import static org.hamcrest.Matchers.contains;
|
||||
|
||||
public class MlFilterTests extends AbstractXContentTestCase<MlFilter> {
|
||||
|
||||
public static MlFilter createTestFilter() {
|
||||
return new MlFilterTests().createTestInstance();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected MlFilter createTestInstance() {
|
||||
return createRandom();
|
||||
}
|
||||
|
||||
public static MlFilter createRandom() {
|
||||
return createRandom(randomAlphaOfLength(10));
|
||||
}
|
||||
|
||||
public static MlFilter createRandom(String filterId) {
|
||||
String description = null;
|
||||
if (randomBoolean()) {
|
||||
description = randomAlphaOfLength(20);
|
||||
}
|
||||
|
||||
int size = randomInt(10);
|
||||
SortedSet<String> items = new TreeSet<>();
|
||||
for (int i = 0; i < size; i++) {
|
||||
items.add(randomAlphaOfLengthBetween(1, 20));
|
||||
}
|
||||
return MlFilter.builder(filterId).setDescription(description).setItems(items).build();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected MlFilter doParseInstance(XContentParser parser) {
|
||||
return MlFilter.PARSER.apply(parser, null).build();
|
||||
}
|
||||
|
||||
public void testNullId() {
|
||||
expectThrows(NullPointerException.class, () -> MlFilter.builder(null).build());
|
||||
}
|
||||
|
||||
public void testNullItems() {
|
||||
expectThrows(NullPointerException.class,
|
||||
() -> MlFilter.builder(randomAlphaOfLength(10)).setItems((SortedSet<String>) null).build());
|
||||
}
|
||||
|
||||
public void testItemsAreSorted() {
|
||||
MlFilter filter = MlFilter.builder("foo").setItems("c", "b", "a").build();
|
||||
assertThat(filter.getItems(), contains("a", "b", "c"));
|
||||
}
|
||||
|
||||
public void testGetItemsReturnsUnmodifiable() {
|
||||
MlFilter filter = MlFilter.builder("foo").setItems("c", "b", "a").build();
|
||||
expectThrows(UnsupportedOperationException.class, () -> filter.getItems().add("x"));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean supportsUnknownFields() {
|
||||
return true;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,80 @@
|
|||
/*
|
||||
* 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.xcontent.XContentParser;
|
||||
import org.elasticsearch.test.AbstractXContentTestCase;
|
||||
|
||||
public class RuleConditionTests extends AbstractXContentTestCase<RuleCondition> {
|
||||
|
||||
@Override
|
||||
protected RuleCondition createTestInstance() {
|
||||
return createRandom();
|
||||
}
|
||||
|
||||
public static RuleCondition createRandom() {
|
||||
RuleCondition.AppliesTo appliesTo = randomFrom(RuleCondition.AppliesTo.values());
|
||||
Operator operator = randomFrom(Operator.LT, Operator.LTE, Operator.GT, Operator.GTE);
|
||||
return new RuleCondition(appliesTo, operator, randomDouble());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected RuleCondition doParseInstance(XContentParser parser) {
|
||||
return RuleCondition.PARSER.apply(parser, null);
|
||||
}
|
||||
|
||||
public void testEqualsGivenSameObject() {
|
||||
RuleCondition condition = createRandom();
|
||||
assertTrue(condition.equals(condition));
|
||||
}
|
||||
|
||||
public void testEqualsGivenString() {
|
||||
assertFalse(createRandom().equals("a string"));
|
||||
}
|
||||
|
||||
public void testCreateTimeBased() {
|
||||
RuleCondition timeBased = RuleCondition.createTime(Operator.GTE, 100L);
|
||||
assertEquals(RuleCondition.AppliesTo.TIME, timeBased.getAppliesTo());
|
||||
assertEquals(Operator.GTE, timeBased.getOperator());
|
||||
assertEquals(100.0, timeBased.getValue(), 0.000001);
|
||||
}
|
||||
|
||||
public void testAppliesToFromString() {
|
||||
assertEquals(RuleCondition.AppliesTo.ACTUAL, RuleCondition.AppliesTo.fromString("actual"));
|
||||
assertEquals(RuleCondition.AppliesTo.ACTUAL, RuleCondition.AppliesTo.fromString("ACTUAL"));
|
||||
assertEquals(RuleCondition.AppliesTo.TYPICAL, RuleCondition.AppliesTo.fromString("typical"));
|
||||
assertEquals(RuleCondition.AppliesTo.TYPICAL, RuleCondition.AppliesTo.fromString("TYPICAL"));
|
||||
assertEquals(RuleCondition.AppliesTo.DIFF_FROM_TYPICAL, RuleCondition.AppliesTo.fromString("diff_from_typical"));
|
||||
assertEquals(RuleCondition.AppliesTo.DIFF_FROM_TYPICAL, RuleCondition.AppliesTo.fromString("DIFF_FROM_TYPICAL"));
|
||||
assertEquals(RuleCondition.AppliesTo.TIME, RuleCondition.AppliesTo.fromString("time"));
|
||||
assertEquals(RuleCondition.AppliesTo.TIME, RuleCondition.AppliesTo.fromString("TIME"));
|
||||
}
|
||||
|
||||
public void testAppliesToToString() {
|
||||
assertEquals("actual", RuleCondition.AppliesTo.ACTUAL.toString());
|
||||
assertEquals("typical", RuleCondition.AppliesTo.TYPICAL.toString());
|
||||
assertEquals("diff_from_typical", RuleCondition.AppliesTo.DIFF_FROM_TYPICAL.toString());
|
||||
assertEquals("time", RuleCondition.AppliesTo.TIME.toString());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean supportsUnknownFields() {
|
||||
return true;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,66 @@
|
|||
/*
|
||||
* 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.xcontent.XContentParser;
|
||||
import org.elasticsearch.test.AbstractXContentTestCase;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import static org.hamcrest.Matchers.contains;
|
||||
|
||||
public class RuleScopeTests extends AbstractXContentTestCase<RuleScope> {
|
||||
|
||||
@Override
|
||||
protected RuleScope createTestInstance() {
|
||||
RuleScope.Builder scope = RuleScope.builder();
|
||||
int count = randomIntBetween(0, 3);
|
||||
for (int i = 0; i < count; ++i) {
|
||||
if (randomBoolean()) {
|
||||
scope.include(randomAlphaOfLength(20), randomAlphaOfLength(20));
|
||||
} else {
|
||||
scope.exclude(randomAlphaOfLength(20), randomAlphaOfLength(20));
|
||||
}
|
||||
}
|
||||
return scope.build();
|
||||
}
|
||||
|
||||
public void testGetReferencedFilters_GivenEmpty() {
|
||||
assertTrue(RuleScope.builder().build().getReferencedFilters().isEmpty());
|
||||
}
|
||||
|
||||
public void testGetReferencedFilters_GivenMultipleFields() {
|
||||
RuleScope scope = RuleScope.builder()
|
||||
.include("foo", "filter1")
|
||||
.exclude("bar", "filter2")
|
||||
.include("foobar", "filter3")
|
||||
.build();
|
||||
assertThat(scope.getReferencedFilters(), contains("filter1", "filter2", "filter3"));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected RuleScope doParseInstance(XContentParser parser) throws IOException {
|
||||
return RuleScope.parser().parse(parser, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean supportsUnknownFields() {
|
||||
return false;
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue