[ML] Support multiple rule actions and renames (elastic/x-pack-elasticsearch#3356)
Relates elastic/x-pack-elasticsearch#3325 Original commit: elastic/x-pack-elasticsearch@a7f400aeeb
This commit is contained in:
parent
f1f1be3927
commit
08a35d44c6
|
@ -150,7 +150,7 @@ public class SpecialEvent implements ToXContentObject, Writeable {
|
|||
conditions.add(RuleCondition.createTime(Operator.LT, bucketEndTime));
|
||||
|
||||
DetectionRule.Builder builder = new DetectionRule.Builder(conditions);
|
||||
builder.setRuleAction(RuleAction.SKIP_SAMPLING_AND_FILTER_RESULTS);
|
||||
builder.setActions(RuleAction.FILTER_RESULTS, RuleAction.SKIP_SAMPLING);
|
||||
builder.setConditionsConnective(Connective.AND);
|
||||
return builder.build();
|
||||
}
|
||||
|
|
|
@ -21,8 +21,10 @@ import org.elasticsearch.xpack.ml.utils.ExceptionsHelper;
|
|||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.EnumMap;
|
||||
import java.util.EnumSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
|
@ -32,11 +34,11 @@ import java.util.stream.Collectors;
|
|||
public class DetectionRule implements ToXContentObject, Writeable {
|
||||
|
||||
public static final ParseField DETECTION_RULE_FIELD = new ParseField("detection_rule");
|
||||
public static final ParseField RULE_ACTION_FIELD = new ParseField("rule_action");
|
||||
public static final ParseField ACTIONS_FIELD = new ParseField("actions");
|
||||
public static final ParseField TARGET_FIELD_NAME_FIELD = new ParseField("target_field_name");
|
||||
public static final ParseField TARGET_FIELD_VALUE_FIELD = new ParseField("target_field_value");
|
||||
public static final ParseField CONDITIONS_CONNECTIVE_FIELD = new ParseField("conditions_connective");
|
||||
public static final ParseField RULE_CONDITIONS_FIELD = new ParseField("rule_conditions");
|
||||
public static final ParseField CONDITIONS_FIELD = new ParseField("conditions");
|
||||
|
||||
// These parsers follow the pattern that metadata is parsed leniently (to allow for enhancements), whilst config is parsed strictly
|
||||
public static final ObjectParser<Builder, Void> METADATA_PARSER =
|
||||
|
@ -51,12 +53,7 @@ public class DetectionRule implements ToXContentObject, Writeable {
|
|||
for (MlParserType parserType : MlParserType.values()) {
|
||||
ObjectParser<Builder, Void> parser = PARSERS.get(parserType);
|
||||
assert parser != null;
|
||||
parser.declareField(Builder::setRuleAction, p -> {
|
||||
if (p.currentToken() == XContentParser.Token.VALUE_STRING) {
|
||||
return RuleAction.fromString(p.text());
|
||||
}
|
||||
throw new IllegalArgumentException("Unsupported token [" + p.currentToken() + "]");
|
||||
}, RULE_ACTION_FIELD, ValueType.STRING);
|
||||
parser.declareStringArray(Builder::setActions, ACTIONS_FIELD);
|
||||
parser.declareString(Builder::setTargetFieldName, TARGET_FIELD_NAME_FIELD);
|
||||
parser.declareString(Builder::setTargetFieldValue, TARGET_FIELD_VALUE_FIELD);
|
||||
parser.declareField(Builder::setConditionsConnective, p -> {
|
||||
|
@ -65,33 +62,38 @@ public class DetectionRule implements ToXContentObject, Writeable {
|
|||
}
|
||||
throw new IllegalArgumentException("Unsupported token [" + p.currentToken() + "]");
|
||||
}, CONDITIONS_CONNECTIVE_FIELD, ValueType.STRING);
|
||||
parser.declareObjectArray(Builder::setRuleConditions, (p, c) ->
|
||||
RuleCondition.PARSERS.get(parserType).apply(p, c), RULE_CONDITIONS_FIELD);
|
||||
parser.declareObjectArray(Builder::setConditions, (p, c) ->
|
||||
RuleCondition.PARSERS.get(parserType).apply(p, c), CONDITIONS_FIELD);
|
||||
}
|
||||
}
|
||||
|
||||
private final RuleAction ruleAction;
|
||||
private final EnumSet<RuleAction> actions;
|
||||
private final String targetFieldName;
|
||||
private final String targetFieldValue;
|
||||
private final Connective conditionsConnective;
|
||||
private final List<RuleCondition> ruleConditions;
|
||||
private final List<RuleCondition> conditions;
|
||||
|
||||
private DetectionRule(RuleAction ruleAction, @Nullable String targetFieldName, @Nullable String targetFieldValue,
|
||||
Connective conditionsConnective, List<RuleCondition> ruleConditions) {
|
||||
this.ruleAction = Objects.requireNonNull(ruleAction);
|
||||
private DetectionRule(EnumSet<RuleAction> actions, @Nullable String targetFieldName, @Nullable String targetFieldValue,
|
||||
Connective conditionsConnective, List<RuleCondition> conditions) {
|
||||
this.actions = Objects.requireNonNull(actions);
|
||||
this.targetFieldName = targetFieldName;
|
||||
this.targetFieldValue = targetFieldValue;
|
||||
this.conditionsConnective = Objects.requireNonNull(conditionsConnective);
|
||||
this.ruleConditions = Collections.unmodifiableList(ruleConditions);
|
||||
this.conditions = Collections.unmodifiableList(conditions);
|
||||
}
|
||||
|
||||
public DetectionRule(StreamInput in) throws IOException {
|
||||
ruleAction = RuleAction.readFromStream(in);
|
||||
int actionsCount = in.readVInt();
|
||||
actions = EnumSet.noneOf(RuleAction.class);
|
||||
for (int i = 0; i < actionsCount; ++i) {
|
||||
actions.add(RuleAction.readFromStream(in));
|
||||
}
|
||||
|
||||
conditionsConnective = Connective.readFromStream(in);
|
||||
int size = in.readVInt();
|
||||
ruleConditions = new ArrayList<>(size);
|
||||
conditions = new ArrayList<>(size);
|
||||
for (int i = 0; i < size; i++) {
|
||||
ruleConditions.add(new RuleCondition(in));
|
||||
conditions.add(new RuleCondition(in));
|
||||
}
|
||||
targetFieldName = in.readOptionalString();
|
||||
targetFieldValue = in.readOptionalString();
|
||||
|
@ -99,10 +101,14 @@ public class DetectionRule implements ToXContentObject, Writeable {
|
|||
|
||||
@Override
|
||||
public void writeTo(StreamOutput out) throws IOException {
|
||||
ruleAction.writeTo(out);
|
||||
out.writeVInt(actions.size());
|
||||
for (RuleAction action : actions) {
|
||||
action.writeTo(out);
|
||||
}
|
||||
|
||||
conditionsConnective.writeTo(out);
|
||||
out.writeVInt(ruleConditions.size());
|
||||
for (RuleCondition condition : ruleConditions) {
|
||||
out.writeVInt(conditions.size());
|
||||
for (RuleCondition condition : conditions) {
|
||||
condition.writeTo(out);
|
||||
}
|
||||
out.writeOptionalString(targetFieldName);
|
||||
|
@ -112,9 +118,9 @@ public class DetectionRule implements ToXContentObject, Writeable {
|
|||
@Override
|
||||
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
|
||||
builder.startObject();
|
||||
builder.field(RULE_ACTION_FIELD.getPreferredName(), ruleAction);
|
||||
builder.field(ACTIONS_FIELD.getPreferredName(), actions);
|
||||
builder.field(CONDITIONS_CONNECTIVE_FIELD.getPreferredName(), conditionsConnective);
|
||||
builder.field(RULE_CONDITIONS_FIELD.getPreferredName(), ruleConditions);
|
||||
builder.field(CONDITIONS_FIELD.getPreferredName(), conditions);
|
||||
if (targetFieldName != null) {
|
||||
builder.field(TARGET_FIELD_NAME_FIELD.getPreferredName(), targetFieldName);
|
||||
}
|
||||
|
@ -125,8 +131,8 @@ public class DetectionRule implements ToXContentObject, Writeable {
|
|||
return builder;
|
||||
}
|
||||
|
||||
public RuleAction getRuleAction() {
|
||||
return ruleAction;
|
||||
public EnumSet<RuleAction> getActions() {
|
||||
return actions;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
|
@ -143,12 +149,12 @@ public class DetectionRule implements ToXContentObject, Writeable {
|
|||
return conditionsConnective;
|
||||
}
|
||||
|
||||
public List<RuleCondition> getRuleConditions() {
|
||||
return ruleConditions;
|
||||
public List<RuleCondition> getConditions() {
|
||||
return conditions;
|
||||
}
|
||||
|
||||
public Set<String> extractReferencedFilters() {
|
||||
return ruleConditions.stream().map(RuleCondition::getValueFilter).filter(Objects::nonNull).collect(Collectors.toSet());
|
||||
return conditions.stream().map(RuleCondition::getFilterId).filter(Objects::nonNull).collect(Collectors.toSet());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -162,34 +168,46 @@ public class DetectionRule implements ToXContentObject, Writeable {
|
|||
}
|
||||
|
||||
DetectionRule other = (DetectionRule) obj;
|
||||
return Objects.equals(ruleAction, other.ruleAction)
|
||||
return Objects.equals(actions, other.actions)
|
||||
&& Objects.equals(targetFieldName, other.targetFieldName)
|
||||
&& Objects.equals(targetFieldValue, other.targetFieldValue)
|
||||
&& Objects.equals(conditionsConnective, other.conditionsConnective)
|
||||
&& Objects.equals(ruleConditions, other.ruleConditions);
|
||||
&& Objects.equals(conditions, other.conditions);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(ruleAction, targetFieldName, targetFieldValue, conditionsConnective, ruleConditions);
|
||||
return Objects.hash(actions, targetFieldName, targetFieldValue, conditionsConnective, conditions);
|
||||
}
|
||||
|
||||
public static class Builder {
|
||||
private RuleAction ruleAction = RuleAction.FILTER_RESULTS;
|
||||
private EnumSet<RuleAction> actions = EnumSet.of(RuleAction.FILTER_RESULTS);
|
||||
private String targetFieldName;
|
||||
private String targetFieldValue;
|
||||
private Connective conditionsConnective = Connective.OR;
|
||||
private List<RuleCondition> ruleConditions = Collections.emptyList();
|
||||
private List<RuleCondition> conditions = Collections.emptyList();
|
||||
|
||||
public Builder(List<RuleCondition> ruleConditions) {
|
||||
this.ruleConditions = ExceptionsHelper.requireNonNull(ruleConditions, RULE_CONDITIONS_FIELD.getPreferredName());
|
||||
public Builder(List<RuleCondition> conditions) {
|
||||
this.conditions = ExceptionsHelper.requireNonNull(conditions, CONDITIONS_FIELD.getPreferredName());
|
||||
}
|
||||
|
||||
private Builder() {
|
||||
}
|
||||
|
||||
public Builder setRuleAction(RuleAction ruleAction) {
|
||||
this.ruleAction = ExceptionsHelper.requireNonNull(ruleAction, RULE_ACTION_FIELD.getPreferredName());
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -208,8 +226,8 @@ public class DetectionRule implements ToXContentObject, Writeable {
|
|||
return this;
|
||||
}
|
||||
|
||||
public Builder setRuleConditions(List<RuleCondition> ruleConditions) {
|
||||
this.ruleConditions = ExceptionsHelper.requireNonNull(ruleConditions, RULE_ACTION_FIELD.getPreferredName());
|
||||
public Builder setConditions(List<RuleCondition> conditions) {
|
||||
this.conditions = ExceptionsHelper.requireNonNull(conditions, CONDITIONS_FIELD.getPreferredName());
|
||||
return this;
|
||||
}
|
||||
|
||||
|
@ -218,18 +236,18 @@ public class DetectionRule implements ToXContentObject, Writeable {
|
|||
String msg = Messages.getMessage(Messages.JOB_CONFIG_DETECTION_RULE_MISSING_TARGET_FIELD_NAME, targetFieldValue);
|
||||
throw ExceptionsHelper.badRequestException(msg);
|
||||
}
|
||||
if (ruleConditions == null || ruleConditions.isEmpty()) {
|
||||
if (conditions == null || conditions.isEmpty()) {
|
||||
String msg = Messages.getMessage(Messages.JOB_CONFIG_DETECTION_RULE_REQUIRES_AT_LEAST_ONE_CONDITION);
|
||||
throw ExceptionsHelper.badRequestException(msg);
|
||||
}
|
||||
for (RuleCondition condition : ruleConditions) {
|
||||
if (condition.getConditionType() == RuleConditionType.CATEGORICAL && targetFieldName != null) {
|
||||
for (RuleCondition condition : conditions) {
|
||||
if (condition.getType() == RuleConditionType.CATEGORICAL && targetFieldName != null) {
|
||||
String msg = Messages.getMessage(Messages.JOB_CONFIG_DETECTION_RULE_CONDITION_CATEGORICAL_INVALID_OPTION,
|
||||
DetectionRule.TARGET_FIELD_NAME_FIELD.getPreferredName());
|
||||
throw ExceptionsHelper.badRequestException(msg);
|
||||
}
|
||||
}
|
||||
return new DetectionRule(ruleAction, targetFieldName, targetFieldValue, conditionsConnective, ruleConditions);
|
||||
return new DetectionRule(actions, targetFieldName, targetFieldValue, conditionsConnective, conditions);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -83,7 +83,7 @@ public class Detector implements ToXContentObject, Writeable {
|
|||
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 DETECTOR_RULES_FIELD = new ParseField("detector_rules");
|
||||
public static final ParseField RULES_FIELD = new ParseField("rules");
|
||||
public static final ParseField DETECTOR_INDEX = new ParseField("detector_index");
|
||||
|
||||
// These parsers follow the pattern that metadata is parsed leniently (to allow for enhancements), whilst config is parsed strictly
|
||||
|
@ -110,8 +110,8 @@ public class Detector implements ToXContentObject, Writeable {
|
|||
}
|
||||
throw new IllegalArgumentException("Unsupported token [" + p.currentToken() + "]");
|
||||
}, EXCLUDE_FREQUENT_FIELD, ObjectParser.ValueType.STRING);
|
||||
parser.declareObjectArray(Builder::setDetectorRules, (p, c) ->
|
||||
DetectionRule.PARSERS.get(parserType).apply(p, c).build(), DETECTOR_RULES_FIELD);
|
||||
parser.declareObjectArray(Builder::setRules, (p, c) ->
|
||||
DetectionRule.PARSERS.get(parserType).apply(p, c).build(), RULES_FIELD);
|
||||
parser.declareInt(Builder::setDetectorIndex, DETECTOR_INDEX);
|
||||
}
|
||||
}
|
||||
|
@ -329,7 +329,7 @@ public class Detector implements ToXContentObject, Writeable {
|
|||
private final String partitionFieldName;
|
||||
private final boolean useNull;
|
||||
private final ExcludeFrequent excludeFrequent;
|
||||
private final List<DetectionRule> detectorRules;
|
||||
private final List<DetectionRule> rules;
|
||||
private final int detectorIndex;
|
||||
|
||||
public Detector(StreamInput in) throws IOException {
|
||||
|
@ -341,7 +341,7 @@ public class Detector implements ToXContentObject, Writeable {
|
|||
partitionFieldName = in.readOptionalString();
|
||||
useNull = in.readBoolean();
|
||||
excludeFrequent = in.readBoolean() ? ExcludeFrequent.readFromStream(in) : null;
|
||||
detectorRules = in.readList(DetectionRule::new);
|
||||
rules = in.readList(DetectionRule::new);
|
||||
if (in.getVersion().onOrAfter(Version.V_5_5_0)) {
|
||||
detectorIndex = in.readInt();
|
||||
} else {
|
||||
|
@ -365,7 +365,7 @@ public class Detector implements ToXContentObject, Writeable {
|
|||
} else {
|
||||
out.writeBoolean(false);
|
||||
}
|
||||
out.writeList(detectorRules);
|
||||
out.writeList(rules);
|
||||
if (out.getVersion().onOrAfter(Version.V_5_5_0)) {
|
||||
out.writeInt(detectorIndex);
|
||||
}
|
||||
|
@ -394,7 +394,7 @@ public class Detector implements ToXContentObject, Writeable {
|
|||
if (excludeFrequent != null) {
|
||||
builder.field(EXCLUDE_FREQUENT_FIELD.getPreferredName(), excludeFrequent);
|
||||
}
|
||||
builder.field(DETECTOR_RULES_FIELD.getPreferredName(), detectorRules);
|
||||
builder.field(RULES_FIELD.getPreferredName(), rules);
|
||||
// negative means "unknown", which should only happen for a 5.4 job
|
||||
if (detectorIndex >= 0
|
||||
// no point writing this to cluster state, as the indexes will get reassigned on reload anyway
|
||||
|
@ -406,7 +406,7 @@ public class Detector implements ToXContentObject, Writeable {
|
|||
}
|
||||
|
||||
private Detector(String detectorDescription, String function, String fieldName, String byFieldName, String overFieldName,
|
||||
String partitionFieldName, boolean useNull, ExcludeFrequent excludeFrequent, List<DetectionRule> detectorRules,
|
||||
String partitionFieldName, boolean useNull, ExcludeFrequent excludeFrequent, List<DetectionRule> rules,
|
||||
int detectorIndex) {
|
||||
this.function = function;
|
||||
this.fieldName = fieldName;
|
||||
|
@ -415,7 +415,7 @@ public class Detector implements ToXContentObject, Writeable {
|
|||
this.partitionFieldName = partitionFieldName;
|
||||
this.useNull = useNull;
|
||||
this.excludeFrequent = excludeFrequent;
|
||||
this.detectorRules = Collections.unmodifiableList(detectorRules);
|
||||
this.rules = Collections.unmodifiableList(rules);
|
||||
this.detectorDescription = detectorDescription != null ? detectorDescription : DefaultDetectorDescription.of(this);
|
||||
this.detectorIndex = detectorIndex;
|
||||
}
|
||||
|
@ -491,8 +491,8 @@ public class Detector implements ToXContentObject, Writeable {
|
|||
return excludeFrequent;
|
||||
}
|
||||
|
||||
public List<DetectionRule> getDetectorRules() {
|
||||
return detectorRules;
|
||||
public List<DetectionRule> getRules() {
|
||||
return rules;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -514,8 +514,8 @@ public class Detector implements ToXContentObject, Writeable {
|
|||
}
|
||||
|
||||
public Set<String> extractReferencedFilters() {
|
||||
return detectorRules == null ? Collections.emptySet()
|
||||
: detectorRules.stream().map(DetectionRule::extractReferencedFilters)
|
||||
return rules == null ? Collections.emptySet()
|
||||
: rules.stream().map(DetectionRule::extractReferencedFilters)
|
||||
.flatMap(Set::stream).collect(Collectors.toSet());
|
||||
}
|
||||
|
||||
|
@ -556,15 +556,14 @@ public class Detector implements ToXContentObject, Writeable {
|
|||
Objects.equals(this.partitionFieldName, that.partitionFieldName) &&
|
||||
Objects.equals(this.useNull, that.useNull) &&
|
||||
Objects.equals(this.excludeFrequent, that.excludeFrequent) &&
|
||||
Objects.equals(this.detectorRules, that.detectorRules) &&
|
||||
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,
|
||||
detectorRules, detectorIndex);
|
||||
return Objects.hash(detectorDescription, function, fieldName, byFieldName, overFieldName, partitionFieldName, useNull,
|
||||
excludeFrequent, rules, detectorIndex);
|
||||
}
|
||||
|
||||
public static class Builder {
|
||||
|
@ -587,7 +586,7 @@ public class Detector implements ToXContentObject, Writeable {
|
|||
private String partitionFieldName;
|
||||
private boolean useNull = false;
|
||||
private ExcludeFrequent excludeFrequent;
|
||||
private List<DetectionRule> detectorRules = Collections.emptyList();
|
||||
private List<DetectionRule> rules = Collections.emptyList();
|
||||
// negative means unknown, and is expected for v5.4 jobs
|
||||
private int detectorIndex = -1;
|
||||
|
||||
|
@ -603,8 +602,7 @@ public class Detector implements ToXContentObject, Writeable {
|
|||
partitionFieldName = detector.partitionFieldName;
|
||||
useNull = detector.useNull;
|
||||
excludeFrequent = detector.excludeFrequent;
|
||||
detectorRules = new ArrayList<>(detector.detectorRules.size());
|
||||
detectorRules.addAll(detector.getDetectorRules());
|
||||
rules = new ArrayList<>(detector.getRules());
|
||||
detectorIndex = detector.detectorIndex;
|
||||
}
|
||||
|
||||
|
@ -645,8 +643,8 @@ public class Detector implements ToXContentObject, Writeable {
|
|||
this.excludeFrequent = excludeFrequent;
|
||||
}
|
||||
|
||||
public void setDetectorRules(List<DetectionRule> detectorRules) {
|
||||
this.detectorRules = detectorRules;
|
||||
public void setRules(List<DetectionRule> rules) {
|
||||
this.rules = rules;
|
||||
}
|
||||
|
||||
public void setDetectorIndex(int detectorIndex) {
|
||||
|
@ -704,12 +702,12 @@ public class Detector implements ToXContentObject, Writeable {
|
|||
}
|
||||
|
||||
String function = this.function == null ? Detector.METRIC : this.function;
|
||||
if (detectorRules.isEmpty() == false) {
|
||||
if (rules.isEmpty() == false) {
|
||||
if (FUNCTIONS_WITHOUT_RULE_SUPPORT.contains(function)) {
|
||||
String msg = Messages.getMessage(Messages.JOB_CONFIG_DETECTION_RULE_NOT_SUPPORTED_BY_FUNCTION, function);
|
||||
throw ExceptionsHelper.badRequestException(msg);
|
||||
}
|
||||
for (DetectionRule rule : detectorRules) {
|
||||
for (DetectionRule rule : rules) {
|
||||
checkScoping(rule);
|
||||
}
|
||||
}
|
||||
|
@ -764,7 +762,7 @@ public class Detector implements ToXContentObject, Writeable {
|
|||
}
|
||||
|
||||
return new Detector(detectorDescription, function, fieldName, byFieldName, overFieldName, partitionFieldName,
|
||||
useNull, excludeFrequent, detectorRules, detectorIndex);
|
||||
useNull, excludeFrequent, rules, detectorIndex);
|
||||
}
|
||||
|
||||
public List<String> extractAnalysisFields() {
|
||||
|
@ -802,7 +800,7 @@ public class Detector implements ToXContentObject, Writeable {
|
|||
String targetFieldName = rule.getTargetFieldName();
|
||||
checkTargetFieldNameIsValid(extractAnalysisFields(), targetFieldName);
|
||||
List<String> validOptions = getValidFieldNameOptions(rule);
|
||||
for (RuleCondition condition : rule.getRuleConditions()) {
|
||||
for (RuleCondition condition : rule.getConditions()) {
|
||||
if (!validOptions.contains(condition.getFieldName())) {
|
||||
String msg = Messages.getMessage(Messages.JOB_CONFIG_DETECTION_RULE_CONDITION_INVALID_FIELD_NAME, validOptions,
|
||||
condition.getFieldName());
|
||||
|
|
|
@ -315,7 +315,7 @@ public class JobUpdate implements Writeable, ToXContentObject {
|
|||
detectorbuilder.setDetectorDescription(dd.getDescription());
|
||||
}
|
||||
if (dd.getRules() != null) {
|
||||
detectorbuilder.setDetectorRules(dd.getRules());
|
||||
detectorbuilder.setRules(dd.getRules());
|
||||
}
|
||||
ac.getDetectors().set(dd.getDetectorIndex(), detectorbuilder.build());
|
||||
}
|
||||
|
@ -435,13 +435,11 @@ public class JobUpdate implements Writeable, ToXContentObject {
|
|||
new ConstructingObjectParser<>("detector_update", a -> new DetectorUpdate((int) a[0], (String) a[1],
|
||||
(List<DetectionRule>) a[2]));
|
||||
|
||||
public static final ParseField RULES = new ParseField("rules");
|
||||
|
||||
static {
|
||||
PARSER.declareInt(ConstructingObjectParser.optionalConstructorArg(), Detector.DETECTOR_INDEX);
|
||||
PARSER.declareStringOrNull(ConstructingObjectParser.optionalConstructorArg(), Job.DESCRIPTION);
|
||||
PARSER.declareObjectArray(ConstructingObjectParser.optionalConstructorArg(),
|
||||
(parser, parseFieldMatcher) -> DetectionRule.CONFIG_PARSER.apply(parser, parseFieldMatcher).build(), RULES);
|
||||
PARSER.declareObjectArray(ConstructingObjectParser.optionalConstructorArg(), (parser, parseFieldMatcher) ->
|
||||
DetectionRule.CONFIG_PARSER.apply(parser, parseFieldMatcher).build(), Detector.RULES_FIELD);
|
||||
}
|
||||
|
||||
private int detectorIndex;
|
||||
|
@ -495,7 +493,7 @@ public class JobUpdate implements Writeable, ToXContentObject {
|
|||
builder.field(Job.DESCRIPTION.getPreferredName(), description);
|
||||
}
|
||||
if (rules != null) {
|
||||
builder.field(RULES.getPreferredName(), rules);
|
||||
builder.field(Detector.RULES_FIELD.getPreferredName(), rules);
|
||||
}
|
||||
builder.endObject();
|
||||
|
||||
|
|
|
@ -14,8 +14,7 @@ import java.util.Locale;
|
|||
|
||||
public enum RuleAction implements Writeable {
|
||||
FILTER_RESULTS,
|
||||
SKIP_SAMPLING,
|
||||
SKIP_SAMPLING_AND_FILTER_RESULTS;
|
||||
SKIP_SAMPLING;
|
||||
|
||||
/**
|
||||
* Case-insensitive from string method.
|
||||
|
|
|
@ -26,11 +26,10 @@ import java.util.Map;
|
|||
import java.util.Objects;
|
||||
|
||||
public class RuleCondition implements ToXContentObject, Writeable {
|
||||
public static final ParseField CONDITION_TYPE_FIELD = new ParseField("condition_type");
|
||||
public static final ParseField TYPE_FIELD = new ParseField("type");
|
||||
public static final ParseField RULE_CONDITION_FIELD = new ParseField("rule_condition");
|
||||
public static final ParseField FIELD_NAME_FIELD = new ParseField("field_name");
|
||||
public static final ParseField FIELD_VALUE_FIELD = new ParseField("field_value");
|
||||
public static final ParseField VALUE_FILTER_FIELD = new ParseField("value_filter");
|
||||
|
||||
// These parsers follow the pattern that metadata is parsed leniently (to allow for enhancements), whilst config is parsed strictly
|
||||
public static final ConstructingObjectParser<RuleCondition, Void> METADATA_PARSER =
|
||||
|
@ -53,60 +52,60 @@ public class RuleCondition implements ToXContentObject, Writeable {
|
|||
return RuleConditionType.fromString(p.text());
|
||||
}
|
||||
throw new IllegalArgumentException("Unsupported token [" + p.currentToken() + "]");
|
||||
}, CONDITION_TYPE_FIELD, ValueType.STRING);
|
||||
}, TYPE_FIELD, ValueType.STRING);
|
||||
parser.declareStringOrNull(ConstructingObjectParser.optionalConstructorArg(), FIELD_NAME_FIELD);
|
||||
parser.declareStringOrNull(ConstructingObjectParser.optionalConstructorArg(), FIELD_VALUE_FIELD);
|
||||
parser.declareObject(ConstructingObjectParser.optionalConstructorArg(), Condition.PARSER, Condition.CONDITION_FIELD);
|
||||
parser.declareStringOrNull(ConstructingObjectParser.optionalConstructorArg(), VALUE_FILTER_FIELD);
|
||||
parser.declareStringOrNull(ConstructingObjectParser.optionalConstructorArg(), MlFilter.ID);
|
||||
}
|
||||
}
|
||||
|
||||
private final RuleConditionType conditionType;
|
||||
private final RuleConditionType type;
|
||||
private final String fieldName;
|
||||
private final String fieldValue;
|
||||
private final Condition condition;
|
||||
private final String valueFilter;
|
||||
private final String filterId;
|
||||
|
||||
public RuleCondition(StreamInput in) throws IOException {
|
||||
conditionType = RuleConditionType.readFromStream(in);
|
||||
type = RuleConditionType.readFromStream(in);
|
||||
condition = in.readOptionalWriteable(Condition::new);
|
||||
fieldName = in.readOptionalString();
|
||||
fieldValue = in.readOptionalString();
|
||||
valueFilter = in.readOptionalString();
|
||||
filterId = in.readOptionalString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeTo(StreamOutput out) throws IOException {
|
||||
conditionType.writeTo(out);
|
||||
type.writeTo(out);
|
||||
out.writeOptionalWriteable(condition);
|
||||
out.writeOptionalString(fieldName);
|
||||
out.writeOptionalString(fieldValue);
|
||||
out.writeOptionalString(valueFilter);
|
||||
out.writeOptionalString(filterId);
|
||||
}
|
||||
|
||||
RuleCondition(RuleConditionType conditionType, String fieldName, String fieldValue, Condition condition, String valueFilter) {
|
||||
this.conditionType = conditionType;
|
||||
RuleCondition(RuleConditionType type, String fieldName, String fieldValue, Condition condition, String filterId) {
|
||||
this.type = type;
|
||||
this.fieldName = fieldName;
|
||||
this.fieldValue = fieldValue;
|
||||
this.condition = condition;
|
||||
this.valueFilter = valueFilter;
|
||||
this.filterId = filterId;
|
||||
|
||||
verifyFieldsBoundToType(this);
|
||||
verifyFieldValueRequiresFieldName(this);
|
||||
}
|
||||
|
||||
public RuleCondition(RuleCondition ruleCondition) {
|
||||
this.conditionType = ruleCondition.conditionType;
|
||||
this.type = ruleCondition.type;
|
||||
this.fieldName = ruleCondition.fieldName;
|
||||
this.fieldValue = ruleCondition.fieldValue;
|
||||
this.condition = ruleCondition.condition;
|
||||
this.valueFilter = ruleCondition.valueFilter;
|
||||
this.filterId = ruleCondition.filterId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
|
||||
builder.startObject();
|
||||
builder.field(CONDITION_TYPE_FIELD.getPreferredName(), conditionType);
|
||||
builder.field(TYPE_FIELD.getPreferredName(), type);
|
||||
if (condition != null) {
|
||||
builder.field(Condition.CONDITION_FIELD.getPreferredName(), condition);
|
||||
}
|
||||
|
@ -116,15 +115,15 @@ public class RuleCondition implements ToXContentObject, Writeable {
|
|||
if (fieldValue != null) {
|
||||
builder.field(FIELD_VALUE_FIELD.getPreferredName(), fieldValue);
|
||||
}
|
||||
if (valueFilter != null) {
|
||||
builder.field(VALUE_FILTER_FIELD.getPreferredName(), valueFilter);
|
||||
if (filterId != null) {
|
||||
builder.field(MlFilter.ID.getPreferredName(), filterId);
|
||||
}
|
||||
builder.endObject();
|
||||
return builder;
|
||||
}
|
||||
|
||||
public RuleConditionType getConditionType() {
|
||||
return conditionType;
|
||||
public RuleConditionType getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -153,8 +152,8 @@ public class RuleCondition implements ToXContentObject, Writeable {
|
|||
* The unique identifier of a filter. Required when the rule type is
|
||||
* categorical. Should be null for all other types.
|
||||
*/
|
||||
public String getValueFilter() {
|
||||
return valueFilter;
|
||||
public String getFilterId() {
|
||||
return filterId;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -168,14 +167,14 @@ public class RuleCondition implements ToXContentObject, Writeable {
|
|||
}
|
||||
|
||||
RuleCondition other = (RuleCondition) obj;
|
||||
return Objects.equals(conditionType, other.conditionType) && Objects.equals(fieldName, other.fieldName)
|
||||
return Objects.equals(type, other.type) && Objects.equals(fieldName, other.fieldName)
|
||||
&& Objects.equals(fieldValue, other.fieldValue) && Objects.equals(condition, other.condition)
|
||||
&& Objects.equals(valueFilter, other.valueFilter);
|
||||
&& Objects.equals(filterId, other.filterId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(conditionType, fieldName, fieldValue, condition, valueFilter);
|
||||
return Objects.hash(type, fieldName, fieldValue, condition, filterId);
|
||||
}
|
||||
|
||||
public static RuleCondition createCategorical(String fieldName, String valueFilter) {
|
||||
|
@ -195,7 +194,7 @@ public class RuleCondition implements ToXContentObject, Writeable {
|
|||
}
|
||||
|
||||
private static void verifyFieldsBoundToType(RuleCondition ruleCondition) throws ElasticsearchParseException {
|
||||
switch (ruleCondition.getConditionType()) {
|
||||
switch (ruleCondition.getType()) {
|
||||
case CATEGORICAL:
|
||||
verifyCategorical(ruleCondition);
|
||||
break;
|
||||
|
@ -215,7 +214,7 @@ public class RuleCondition implements ToXContentObject, Writeable {
|
|||
private static void verifyCategorical(RuleCondition ruleCondition) throws ElasticsearchParseException {
|
||||
checkCategoricalHasNoField(Condition.CONDITION_FIELD.getPreferredName(), ruleCondition.getCondition());
|
||||
checkCategoricalHasNoField(RuleCondition.FIELD_VALUE_FIELD.getPreferredName(), ruleCondition.getFieldValue());
|
||||
checkCategoricalHasField(RuleCondition.VALUE_FILTER_FIELD.getPreferredName(), ruleCondition.getValueFilter());
|
||||
checkCategoricalHasField(MlFilter.ID.getPreferredName(), ruleCondition.getFilterId());
|
||||
}
|
||||
|
||||
private static void checkCategoricalHasNoField(String fieldName, Object fieldValue) throws ElasticsearchParseException {
|
||||
|
@ -233,7 +232,7 @@ public class RuleCondition implements ToXContentObject, Writeable {
|
|||
}
|
||||
|
||||
private static void verifyNumerical(RuleCondition ruleCondition) throws ElasticsearchParseException {
|
||||
checkNumericalHasNoField(RuleCondition.VALUE_FILTER_FIELD.getPreferredName(), ruleCondition.getValueFilter());
|
||||
checkNumericalHasNoField(MlFilter.ID.getPreferredName(), ruleCondition.getFilterId());
|
||||
checkNumericalHasField(Condition.CONDITION_FIELD.getPreferredName(), ruleCondition.getCondition());
|
||||
if (ruleCondition.getFieldName() != null && ruleCondition.getFieldValue() == null) {
|
||||
String msg = Messages.getMessage(Messages.JOB_CONFIG_DETECTION_RULE_CONDITION_NUMERICAL_WITH_FIELD_NAME_REQUIRES_FIELD_VALUE);
|
||||
|
|
|
@ -107,8 +107,8 @@ public class FieldConfigWriter {
|
|||
StringBuilder contents) throws IOException {
|
||||
|
||||
List<DetectionRule> rules = new ArrayList<>();
|
||||
if (detector.getDetectorRules() != null) {
|
||||
rules.addAll(detector.getDetectorRules());
|
||||
if (detector.getRules() != null) {
|
||||
rules.addAll(detector.getRules());
|
||||
}
|
||||
rules.addAll(specialEvents);
|
||||
|
||||
|
|
|
@ -22,6 +22,7 @@ import java.time.Instant;
|
|||
import java.time.ZoneOffset;
|
||||
import java.time.ZonedDateTime;
|
||||
import java.util.ArrayList;
|
||||
import java.util.EnumSet;
|
||||
import java.util.List;
|
||||
|
||||
public class SpecialEventTests extends AbstractSerializingTestCase<SpecialEvent> {
|
||||
|
@ -60,14 +61,14 @@ public class SpecialEventTests extends AbstractSerializingTestCase<SpecialEvent>
|
|||
DetectionRule rule = event.toDetectionRule(TimeValue.timeValueSeconds(bucketSpanSecs));
|
||||
|
||||
assertEquals(Connective.AND, rule.getConditionsConnective());
|
||||
assertEquals(RuleAction.SKIP_SAMPLING_AND_FILTER_RESULTS, rule.getRuleAction());
|
||||
assertEquals(rule.getActions(), EnumSet.of(RuleAction.FILTER_RESULTS, RuleAction.SKIP_SAMPLING));
|
||||
assertNull(rule.getTargetFieldName());
|
||||
assertNull(rule.getTargetFieldValue());
|
||||
|
||||
List<RuleCondition> conditions = rule.getRuleConditions();
|
||||
List<RuleCondition> conditions = rule.getConditions();
|
||||
assertEquals(2, conditions.size());
|
||||
assertEquals(RuleConditionType.TIME, conditions.get(0).getConditionType());
|
||||
assertEquals(RuleConditionType.TIME, conditions.get(1).getConditionType());
|
||||
assertEquals(RuleConditionType.TIME, conditions.get(0).getType());
|
||||
assertEquals(RuleConditionType.TIME, conditions.get(1).getType());
|
||||
assertEquals(Operator.GTE, conditions.get(0).getCondition().getOperator());
|
||||
assertEquals(Operator.LT, conditions.get(1).getCondition().getOperator());
|
||||
|
||||
|
|
|
@ -61,7 +61,7 @@ public class DetectionRulesIT extends MlNativeAutodetectIntegTestCase {
|
|||
DetectionRule rule = new DetectionRule.Builder(Arrays.asList(condition1, condition2, condition3)).build();
|
||||
|
||||
Detector.Builder detector = new Detector.Builder("max", "value");
|
||||
detector.setDetectorRules(Arrays.asList(rule));
|
||||
detector.setRules(Arrays.asList(rule));
|
||||
detector.setByFieldName("by_field");
|
||||
|
||||
AnalysisConfig.Builder analysisConfig = new AnalysisConfig.Builder(
|
||||
|
|
|
@ -431,10 +431,10 @@ public class JobProviderIT extends XPackSingleNodeTestCase {
|
|||
}
|
||||
|
||||
DetectionRule.Builder rule = new DetectionRule.Builder(conditions)
|
||||
.setRuleAction(RuleAction.FILTER_RESULTS)
|
||||
.setActions(RuleAction.FILTER_RESULTS)
|
||||
.setConditionsConnective(Connective.OR);
|
||||
|
||||
detector.setDetectorRules(Collections.singletonList(rule.build()));
|
||||
detector.setRules(Collections.singletonList(rule.build()));
|
||||
}
|
||||
|
||||
return new AnalysisConfig.Builder(Collections.singletonList(detector.build()));
|
||||
|
|
|
@ -461,9 +461,9 @@ public class AnalysisConfigTests extends AbstractSerializingTestCase<AnalysisCon
|
|||
"filter2"))).build();
|
||||
Detector.Builder detector1 = new Detector.Builder("count", null);
|
||||
detector1.setByFieldName("foo");
|
||||
detector1.setDetectorRules(Collections.singletonList(rule1));
|
||||
detector1.setRules(Collections.singletonList(rule1));
|
||||
Detector.Builder detector2 = new Detector.Builder("count", null);
|
||||
detector2.setDetectorRules(Collections.singletonList(rule2));
|
||||
detector2.setRules(Collections.singletonList(rule2));
|
||||
detector2.setByFieldName("foo");
|
||||
AnalysisConfig config = new AnalysisConfig.Builder(
|
||||
Arrays.asList(detector1.build(), detector2.build(), new Detector.Builder("count", null).build())).build();
|
||||
|
|
|
@ -13,6 +13,7 @@ import java.io.IOException;
|
|||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.EnumSet;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
|
||||
|
@ -63,7 +64,7 @@ public class DetectionRuleTests extends AbstractSerializingTestCase<DetectionRul
|
|||
|
||||
public void testEqualsGivenRules() {
|
||||
DetectionRule rule1 = createFullyPopulated().build();
|
||||
DetectionRule rule2 = createFullyPopulated().setRuleConditions(createRule("10")).build();
|
||||
DetectionRule rule2 = createFullyPopulated().setConditions(createRule("10")).build();
|
||||
assertFalse(rule1.equals(rule2));
|
||||
assertFalse(rule2.equals(rule1));
|
||||
}
|
||||
|
@ -78,7 +79,7 @@ public class DetectionRuleTests extends AbstractSerializingTestCase<DetectionRul
|
|||
|
||||
private static DetectionRule.Builder createFullyPopulated() {
|
||||
return new DetectionRule.Builder(createRule("5"))
|
||||
.setRuleAction(RuleAction.FILTER_RESULTS)
|
||||
.setActions(EnumSet.of(RuleAction.FILTER_RESULTS, RuleAction.SKIP_SAMPLING))
|
||||
.setTargetFieldName("targetField")
|
||||
.setTargetFieldValue("targetValue")
|
||||
.setConditionsConnective(Connective.AND);
|
||||
|
@ -91,26 +92,33 @@ public class DetectionRuleTests extends AbstractSerializingTestCase<DetectionRul
|
|||
|
||||
@Override
|
||||
protected DetectionRule createTestInstance() {
|
||||
RuleAction ruleAction = randomFrom(RuleAction.values());
|
||||
String targetFieldName = null;
|
||||
String targetFieldValue = null;
|
||||
Connective connective = randomFrom(Connective.values());
|
||||
if (randomBoolean()) {
|
||||
targetFieldName = randomAlphaOfLengthBetween(1, 20);
|
||||
targetFieldValue = randomAlphaOfLengthBetween(1, 20);
|
||||
}
|
||||
int size = 1 + randomInt(20);
|
||||
List<RuleCondition> ruleConditions = new ArrayList<>(size);
|
||||
for (int i = 0; i < size; i++) {
|
||||
// no need for random condition (it is already tested)
|
||||
ruleConditions.addAll(createRule(Double.toString(randomDouble())));
|
||||
}
|
||||
return new DetectionRule.Builder(ruleConditions)
|
||||
.setRuleAction(ruleAction)
|
||||
.setTargetFieldName(targetFieldName)
|
||||
.setTargetFieldValue(targetFieldValue)
|
||||
.setConditionsConnective(connective)
|
||||
.build();
|
||||
DetectionRule.Builder builder = new DetectionRule.Builder(ruleConditions);
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
if (randomBoolean()) {
|
||||
builder.setConditionsConnective(randomFrom(Connective.values()));
|
||||
}
|
||||
|
||||
if (randomBoolean()) {
|
||||
builder.setTargetFieldName(randomAlphaOfLengthBetween(1, 20));
|
||||
builder.setTargetFieldValue(randomAlphaOfLengthBetween(1, 20));
|
||||
}
|
||||
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -125,16 +133,16 @@ public class DetectionRuleTests extends AbstractSerializingTestCase<DetectionRul
|
|||
|
||||
@Override
|
||||
protected DetectionRule mutateInstance(DetectionRule instance) throws IOException {
|
||||
List<RuleCondition> ruleConditions = instance.getRuleConditions();
|
||||
RuleAction ruleAction = instance.getRuleAction();
|
||||
List<RuleCondition> conditions = instance.getConditions();
|
||||
EnumSet<RuleAction> actions = instance.getActions();
|
||||
String targetFieldName = instance.getTargetFieldName();
|
||||
String targetFieldValue = instance.getTargetFieldValue();
|
||||
Connective connective = instance.getConditionsConnective();
|
||||
|
||||
switch (between(0, 3)) {
|
||||
case 0:
|
||||
ruleConditions = new ArrayList<>(ruleConditions);
|
||||
ruleConditions.addAll(createRule(Double.toString(randomDouble())));
|
||||
conditions = new ArrayList<>(conditions);
|
||||
conditions.addAll(createRule(Double.toString(randomDouble())));
|
||||
break;
|
||||
case 1:
|
||||
targetFieldName = randomAlphaOfLengthBetween(5, 10);
|
||||
|
@ -156,7 +164,7 @@ public class DetectionRuleTests extends AbstractSerializingTestCase<DetectionRul
|
|||
throw new AssertionError("Illegal randomisation branch");
|
||||
}
|
||||
|
||||
return new DetectionRule.Builder(ruleConditions).setRuleAction(ruleAction).setTargetFieldName(targetFieldName)
|
||||
return new DetectionRule.Builder(conditions).setActions(actions).setTargetFieldName(targetFieldName)
|
||||
.setTargetFieldValue(targetFieldValue).setConditionsConnective(connective).build();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -64,11 +64,11 @@ public class DetectorTests extends AbstractSerializingTestCase<Detector> {
|
|||
Condition condition = new Condition(Operator.GT, "5");
|
||||
DetectionRule rule = new DetectionRule.Builder(
|
||||
Collections.singletonList(new RuleCondition(RuleConditionType.NUMERICAL_ACTUAL, "by2", "val", condition, null)))
|
||||
.setRuleAction(RuleAction.FILTER_RESULTS).setTargetFieldName("over_field")
|
||||
.setActions(RuleAction.FILTER_RESULTS).setTargetFieldName("over_field")
|
||||
.setTargetFieldValue("targetValue")
|
||||
.setConditionsConnective(Connective.AND)
|
||||
.build();
|
||||
builder.setDetectorRules(Collections.singletonList(rule));
|
||||
builder.setRules(Collections.singletonList(rule));
|
||||
detector2 = builder.build();
|
||||
assertFalse(detector1.equals(detector2));
|
||||
}
|
||||
|
@ -84,22 +84,22 @@ public class DetectorTests extends AbstractSerializingTestCase<Detector> {
|
|||
Condition condition = new Condition(Operator.GT, "5");
|
||||
DetectionRule rule = new DetectionRule.Builder(
|
||||
Collections.singletonList(new RuleCondition(RuleConditionType.NUMERICAL_ACTUAL, null, null, condition, null)))
|
||||
.setRuleAction(RuleAction.FILTER_RESULTS)
|
||||
.setActions(RuleAction.FILTER_RESULTS)
|
||||
.setTargetFieldName("over_field")
|
||||
.setTargetFieldValue("targetValue")
|
||||
.setConditionsConnective(Connective.AND)
|
||||
.build();
|
||||
builder.setDetectorRules(Collections.singletonList(rule));
|
||||
builder.setRules(Collections.singletonList(rule));
|
||||
builder.setByFieldName(null);
|
||||
detector = builder.build();
|
||||
assertEquals(Collections.singletonList("over_field"), detector.extractAnalysisFields());
|
||||
builder = new Detector.Builder(detector);
|
||||
rule = new DetectionRule.Builder(
|
||||
Collections.singletonList(new RuleCondition(RuleConditionType.NUMERICAL_ACTUAL, null, null, condition, null)))
|
||||
.setRuleAction(RuleAction.FILTER_RESULTS)
|
||||
.setActions(RuleAction.FILTER_RESULTS)
|
||||
.setConditionsConnective(Connective.AND)
|
||||
.build();
|
||||
builder.setDetectorRules(Collections.singletonList(rule));
|
||||
builder.setRules(Collections.singletonList(rule));
|
||||
builder.setOverFieldName(null);
|
||||
detector = builder.build();
|
||||
assertTrue(detector.extractAnalysisFields().isEmpty());
|
||||
|
@ -107,7 +107,7 @@ public class DetectorTests extends AbstractSerializingTestCase<Detector> {
|
|||
|
||||
public void testExtractReferencedLists() {
|
||||
Detector.Builder builder = createDetector();
|
||||
builder.setDetectorRules(Arrays.asList(
|
||||
builder.setRules(Arrays.asList(
|
||||
new DetectionRule.Builder(Collections.singletonList(RuleCondition.createCategorical("by_field", "list1"))).build(),
|
||||
new DetectionRule.Builder(Collections.singletonList(RuleCondition.createCategorical("by_field", "list2"))).build()));
|
||||
|
||||
|
@ -140,12 +140,12 @@ public class DetectorTests extends AbstractSerializingTestCase<Detector> {
|
|||
Condition condition = new Condition(Operator.GT, "5");
|
||||
DetectionRule rule = new DetectionRule.Builder(
|
||||
Collections.singletonList(new RuleCondition(RuleConditionType.NUMERICAL_ACTUAL, "by_field", "val", condition, null)))
|
||||
.setRuleAction(RuleAction.FILTER_RESULTS)
|
||||
.setActions(RuleAction.FILTER_RESULTS)
|
||||
.setTargetFieldName("over_field")
|
||||
.setTargetFieldValue("targetValue")
|
||||
.setConditionsConnective(Connective.AND)
|
||||
.build();
|
||||
detector.setDetectorRules(Collections.singletonList(rule));
|
||||
detector.setRules(Collections.singletonList(rule));
|
||||
return detector;
|
||||
}
|
||||
|
||||
|
@ -176,15 +176,15 @@ public class DetectorTests extends AbstractSerializingTestCase<Detector> {
|
|||
}
|
||||
if (randomBoolean()) {
|
||||
int size = randomInt(10);
|
||||
List<DetectionRule> detectorRules = new ArrayList<>(size);
|
||||
List<DetectionRule> rules = new ArrayList<>(size);
|
||||
for (int i = 0; i < size; i++) {
|
||||
// no need for random DetectionRule (it is already tested)
|
||||
Condition condition = new Condition(Operator.GT, "5");
|
||||
detectorRules.add(new DetectionRule.Builder(
|
||||
rules.add(new DetectionRule.Builder(
|
||||
Collections.singletonList(new RuleCondition(RuleConditionType.NUMERICAL_ACTUAL, null, null, condition, null)))
|
||||
.setTargetFieldName(fieldName).build());
|
||||
}
|
||||
detector.setDetectorRules(detectorRules);
|
||||
detector.setRules(rules);
|
||||
}
|
||||
if (randomBoolean()) {
|
||||
detector.setUseNull(randomBoolean());
|
||||
|
@ -508,14 +508,14 @@ public class DetectorTests extends AbstractSerializingTestCase<Detector> {
|
|||
}
|
||||
}
|
||||
|
||||
public void testVerify_GivenInvalidDetectionRuleTargetFieldName() {
|
||||
public void testVerify_GivenInvalidRuleTargetFieldName() {
|
||||
Detector.Builder detector = new Detector.Builder("mean", "metricVale");
|
||||
detector.setByFieldName("metricName");
|
||||
detector.setPartitionFieldName("instance");
|
||||
RuleCondition ruleCondition =
|
||||
new RuleCondition(RuleConditionType.NUMERICAL_ACTUAL, "metricName", "metricVale", new Condition(Operator.LT, "5"), null);
|
||||
DetectionRule rule = new DetectionRule.Builder(Collections.singletonList(ruleCondition)).setTargetFieldName("instancE").build();
|
||||
detector.setDetectorRules(Collections.singletonList(rule));
|
||||
detector.setRules(Collections.singletonList(rule));
|
||||
|
||||
ElasticsearchException e = ESTestCase.expectThrows(ElasticsearchException.class, detector::build);
|
||||
|
||||
|
@ -524,14 +524,14 @@ public class DetectorTests extends AbstractSerializingTestCase<Detector> {
|
|||
e.getMessage());
|
||||
}
|
||||
|
||||
public void testVerify_GivenValidDetectionRule() {
|
||||
public void testVerify_GivenValidRule() {
|
||||
Detector.Builder detector = new Detector.Builder("mean", "metricVale");
|
||||
detector.setByFieldName("metricName");
|
||||
detector.setPartitionFieldName("instance");
|
||||
RuleCondition ruleCondition =
|
||||
new RuleCondition(RuleConditionType.NUMERICAL_ACTUAL, "metricName", "CPU", new Condition(Operator.LT, "5"), null);
|
||||
DetectionRule rule = new DetectionRule.Builder(Collections.singletonList(ruleCondition)).setTargetFieldName("instance").build();
|
||||
detector.setDetectorRules(Collections.singletonList(rule));
|
||||
detector.setRules(Collections.singletonList(rule));
|
||||
detector.build();
|
||||
}
|
||||
|
||||
|
|
|
@ -165,7 +165,7 @@ public class JobUpdateTests extends AbstractSerializingTestCase<JobUpdate> {
|
|||
updatedJob.getAnalysisConfig().getDetectors().get(detectorUpdate.getDetectorIndex()).getDetectorDescription());
|
||||
assertNotNull(updatedJob.getAnalysisConfig().getDetectors().get(detectorUpdate.getDetectorIndex()).getDetectorDescription());
|
||||
assertEquals(detectorUpdate.getRules(),
|
||||
updatedJob.getAnalysisConfig().getDetectors().get(detectorUpdate.getDetectorIndex()).getDetectorRules());
|
||||
updatedJob.getAnalysisConfig().getDetectors().get(detectorUpdate.getDetectorIndex()).getRules());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -11,14 +11,12 @@ import org.elasticsearch.test.ESTestCase;
|
|||
|
||||
import static org.hamcrest.Matchers.equalTo;
|
||||
|
||||
public class
|
||||
RuleActionTests extends ESTestCase {
|
||||
public class RuleActionTests extends ESTestCase {
|
||||
|
||||
public void testForString() {
|
||||
assertEquals(RuleAction.FILTER_RESULTS, RuleAction.fromString("filter_results"));
|
||||
assertEquals(RuleAction.FILTER_RESULTS, RuleAction.fromString("FILTER_RESULTS"));
|
||||
assertEquals(RuleAction.SKIP_SAMPLING, RuleAction.fromString("SKip_sampLing"));
|
||||
assertEquals(RuleAction.SKIP_SAMPLING_AND_FILTER_RESULTS, RuleAction.fromString("skip_sampling_and_filter_results"));
|
||||
}
|
||||
|
||||
public void testToString() {
|
||||
|
@ -34,9 +32,9 @@ RuleActionTests extends ESTestCase {
|
|||
}
|
||||
|
||||
try (BytesStreamOutput out = new BytesStreamOutput()) {
|
||||
out.writeVInt(2);
|
||||
out.writeVInt(1);
|
||||
try (StreamInput in = out.bytes().streamInput()) {
|
||||
assertThat(RuleAction.readFromStream(in), equalTo(RuleAction.SKIP_SAMPLING_AND_FILTER_RESULTS));
|
||||
assertThat(RuleAction.readFromStream(in), equalTo(RuleAction.SKIP_SAMPLING));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -52,7 +52,7 @@ public class RuleConditionTests extends AbstractSerializingTestCase<RuleConditio
|
|||
|
||||
public void testConstructor() {
|
||||
RuleCondition condition = new RuleCondition(RuleConditionType.CATEGORICAL, null, null, null, "valueFilter");
|
||||
assertEquals(RuleConditionType.CATEGORICAL, condition.getConditionType());
|
||||
assertEquals(RuleConditionType.CATEGORICAL, condition.getType());
|
||||
assertNull(condition.getFieldName());
|
||||
assertNull(condition.getFieldValue());
|
||||
assertNull(condition.getCondition());
|
||||
|
@ -122,16 +122,16 @@ public class RuleConditionTests extends AbstractSerializingTestCase<RuleConditio
|
|||
assertEquals("Invalid detector rule: a categorical rule_condition does not support field_value", e.getMessage());
|
||||
}
|
||||
|
||||
public void testVerify_GivenCategoricalWithoutValueFilter() {
|
||||
public void testVerify_GivenCategoricalWithoutFilterId() {
|
||||
ElasticsearchException e = expectThrows(ElasticsearchException.class,
|
||||
() -> new RuleCondition(RuleConditionType.CATEGORICAL, null, null, null, null));
|
||||
assertEquals("Invalid detector rule: a categorical rule_condition requires value_filter to be set", e.getMessage());
|
||||
assertEquals("Invalid detector rule: a categorical rule_condition requires filter_id to be set", e.getMessage());
|
||||
}
|
||||
|
||||
public void testVerify_GivenNumericalActualWithValueFilter() {
|
||||
public void testVerify_GivenNumericalActualWithFilterId() {
|
||||
ElasticsearchException e = expectThrows(ElasticsearchException.class,
|
||||
() -> new RuleCondition(RuleConditionType.NUMERICAL_ACTUAL, null, null, null, "myFilter"));
|
||||
assertEquals("Invalid detector rule: a numerical rule_condition does not support value_filter", e.getMessage());
|
||||
assertEquals("Invalid detector rule: a numerical rule_condition does not support filter_id", e.getMessage());
|
||||
}
|
||||
|
||||
public void testVerify_GivenNumericalActualWithoutCondition() {
|
||||
|
@ -146,10 +146,10 @@ public class RuleConditionTests extends AbstractSerializingTestCase<RuleConditio
|
|||
assertEquals("Invalid detector rule: a numerical rule_condition with field_name requires that field_value is set", e.getMessage());
|
||||
}
|
||||
|
||||
public void testVerify_GivenNumericalTypicalWithValueFilter() {
|
||||
public void testVerify_GivenNumericalTypicalWithFilterId() {
|
||||
ElasticsearchException e = expectThrows(ElasticsearchException.class,
|
||||
() -> new RuleCondition(RuleConditionType.NUMERICAL_ACTUAL, null, null, null, "myFilter"));
|
||||
assertEquals("Invalid detector rule: a numerical rule_condition does not support value_filter", e.getMessage());
|
||||
assertEquals("Invalid detector rule: a numerical rule_condition does not support filter_id", e.getMessage());
|
||||
}
|
||||
|
||||
public void testVerify_GivenNumericalTypicalWithoutCondition() {
|
||||
|
@ -158,10 +158,10 @@ public class RuleConditionTests extends AbstractSerializingTestCase<RuleConditio
|
|||
assertEquals("Invalid detector rule: a numerical rule_condition requires condition to be set", e.getMessage());
|
||||
}
|
||||
|
||||
public void testVerify_GivenNumericalDiffAbsWithValueFilter() {
|
||||
public void testVerify_GivenNumericalDiffAbsWithFilterId() {
|
||||
ElasticsearchException e = expectThrows(ElasticsearchException.class,
|
||||
() -> new RuleCondition(RuleConditionType.NUMERICAL_DIFF_ABS, null, null, null, "myFilter"));
|
||||
assertEquals("Invalid detector rule: a numerical rule_condition does not support value_filter", e.getMessage());
|
||||
assertEquals("Invalid detector rule: a numerical rule_condition does not support filter_id", e.getMessage());
|
||||
}
|
||||
|
||||
public void testVerify_GivenNumericalDiffAbsWithoutCondition() {
|
||||
|
@ -220,12 +220,12 @@ public class RuleConditionTests extends AbstractSerializingTestCase<RuleConditio
|
|||
|
||||
public void testCreateTimeBased() {
|
||||
RuleCondition timeBased = RuleCondition.createTime(Operator.GTE, 100L);
|
||||
assertEquals(RuleConditionType.TIME, timeBased.getConditionType());
|
||||
assertEquals(RuleConditionType.TIME, timeBased.getType());
|
||||
assertEquals(Operator.GTE, timeBased.getCondition().getOperator());
|
||||
assertEquals("100", timeBased.getCondition().getValue());
|
||||
assertNull(timeBased.getFieldName());
|
||||
assertNull(timeBased.getFieldValue());
|
||||
assertNull(timeBased.getValueFilter());
|
||||
assertNull(timeBased.getFilterId());
|
||||
}
|
||||
|
||||
public void testCreateTimeBased_GivenOperatorMatch() {
|
||||
|
@ -237,11 +237,11 @@ public class RuleConditionTests extends AbstractSerializingTestCase<RuleConditio
|
|||
public void testCreateNumerical() {
|
||||
RuleCondition ruleCondition = RuleCondition.createNumerical(RuleConditionType.NUMERICAL_ACTUAL, "foo", "bar",
|
||||
new Condition(Operator.GTE, "100"));
|
||||
assertEquals(RuleConditionType.NUMERICAL_ACTUAL, ruleCondition.getConditionType());
|
||||
assertEquals(RuleConditionType.NUMERICAL_ACTUAL, ruleCondition.getType());
|
||||
assertEquals(Operator.GTE, ruleCondition.getCondition().getOperator());
|
||||
assertEquals("100", ruleCondition.getCondition().getValue());
|
||||
assertEquals("foo", ruleCondition.getFieldName());
|
||||
assertEquals("bar", ruleCondition.getFieldValue());
|
||||
assertNull(ruleCondition.getValueFilter());
|
||||
assertNull(ruleCondition.getFilterId());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,7 +6,6 @@
|
|||
package org.elasticsearch.xpack.ml.job.process.autodetect.writer;
|
||||
|
||||
import org.elasticsearch.test.ESTestCase;
|
||||
import org.elasticsearch.xpack.ml.calendars.SpecialEvent;
|
||||
import org.elasticsearch.xpack.ml.job.config.Condition;
|
||||
import org.elasticsearch.xpack.ml.job.config.Connective;
|
||||
import org.elasticsearch.xpack.ml.job.config.DetectionRule;
|
||||
|
@ -22,13 +21,8 @@ import org.mockito.InOrder;
|
|||
import org.mockito.Mockito;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.time.Instant;
|
||||
import java.time.ZoneOffset;
|
||||
import java.time.ZonedDateTime;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.stream.IntStream;
|
||||
|
@ -202,11 +196,11 @@ public class ControlMsgToProcessWriterTests extends ESTestCase {
|
|||
inOrder.verify(lengthEncodedWriter).writeNumFields(4);
|
||||
inOrder.verify(lengthEncodedWriter, times(3)).writeField("");
|
||||
inOrder.verify(lengthEncodedWriter).writeField("u[detectorRules]\ndetectorIndex=2\n" +
|
||||
"rulesJson=[{\"rule_action\":\"filter_results\",\"conditions_connective\":\"and\",\"rule_conditions\":" +
|
||||
"[{\"condition_type\":\"numerical_actual\",\"condition\":{\"operator\":\"gt\",\"value\":\"5\"}}]," +
|
||||
"rulesJson=[{\"actions\":[\"filter_results\"],\"conditions_connective\":\"and\",\"conditions\":" +
|
||||
"[{\"type\":\"numerical_actual\",\"condition\":{\"operator\":\"gt\",\"value\":\"5\"}}]," +
|
||||
"\"target_field_name\":\"targetField1\",\"target_field_value\":\"targetValue\"}," +
|
||||
"{\"rule_action\":\"filter_results\",\"conditions_connective\":\"and\",\"rule_conditions\":[" +
|
||||
"{\"condition_type\":\"numerical_actual\",\"condition\":{\"operator\":\"gt\",\"value\":\"5\"}}]," +
|
||||
"{\"actions\":[\"filter_results\"],\"conditions_connective\":\"and\",\"conditions\":[" +
|
||||
"{\"type\":\"numerical_actual\",\"condition\":{\"operator\":\"gt\",\"value\":\"5\"}}]," +
|
||||
"\"target_field_name\":\"targetField2\",\"target_field_value\":\"targetValue\"}]");
|
||||
verifyNoMoreInteractions(lengthEncodedWriter);
|
||||
}
|
||||
|
|
|
@ -26,7 +26,6 @@ import org.mockito.ArgumentCaptor;
|
|||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.io.OutputStreamWriter;
|
||||
import java.io.StringReader;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
@ -36,7 +35,6 @@ import java.time.ZonedDateTime;
|
|||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
@ -195,7 +193,7 @@ public class FieldConfigWriterTests extends ESTestCase {
|
|||
RuleCondition ruleCondition = RuleCondition.createNumerical
|
||||
(RuleConditionType.NUMERICAL_ACTUAL, "metricName", "metricValue", new Condition(Operator.LT, "5"));
|
||||
DetectionRule rule = new DetectionRule.Builder(Arrays.asList(ruleCondition)).setTargetFieldName("instance").build();
|
||||
detector.setDetectorRules(Arrays.asList(rule));
|
||||
detector.setRules(Arrays.asList(rule));
|
||||
|
||||
AnalysisConfig.Builder builder = new AnalysisConfig.Builder(Arrays.asList(detector.build()));
|
||||
analysisConfig = builder.build();
|
||||
|
@ -251,12 +249,12 @@ public class FieldConfigWriterTests extends ESTestCase {
|
|||
createFieldConfigWriter().write();
|
||||
|
||||
verify(writer).write("detector.0.clause = count\n" +
|
||||
"detector.0.rules = [{\"rule_action\":\"skip_sampling_and_filter_results\",\"conditions_connective\":\"and\"," +
|
||||
"\"rule_conditions\":[{\"condition_type\":\"time\",\"condition\":{\"operator\":\"gte\",\"value\":\"1511395200\"}}," +
|
||||
"{\"condition_type\":\"time\",\"condition\":{\"operator\":\"lt\",\"value\":\"1515369600\"}}]}," +
|
||||
"{\"rule_action\":\"skip_sampling_and_filter_results\",\"conditions_connective\":\"and\"," +
|
||||
"\"rule_conditions\":[{\"condition_type\":\"time\",\"condition\":{\"operator\":\"gte\",\"value\":\"1519603200\"}}," +
|
||||
"{\"condition_type\":\"time\",\"condition\":{\"operator\":\"lt\",\"value\":\"1519862400\"}}]}]" +
|
||||
"detector.0.rules = [{\"actions\":[\"filter_results\",\"skip_sampling\"],\"conditions_connective\":\"and\"," +
|
||||
"\"conditions\":[{\"type\":\"time\",\"condition\":{\"operator\":\"gte\",\"value\":\"1511395200\"}}," +
|
||||
"{\"type\":\"time\",\"condition\":{\"operator\":\"lt\",\"value\":\"1515369600\"}}]}," +
|
||||
"{\"actions\":[\"filter_results\",\"skip_sampling\"],\"conditions_connective\":\"and\"," +
|
||||
"\"conditions\":[{\"type\":\"time\",\"condition\":{\"operator\":\"gte\",\"value\":\"1519603200\"}}," +
|
||||
"{\"type\":\"time\",\"condition\":{\"operator\":\"lt\",\"value\":\"1519862400\"}}]}]" +
|
||||
"\n");
|
||||
|
||||
verifyNoMoreInteractions(writer);
|
||||
|
|
|
@ -152,12 +152,12 @@ setup:
|
|||
"analysis_config" : {
|
||||
"bucket_span": "3600s",
|
||||
"detectors" :[{"function":"mean","field_name":"airline",
|
||||
"detector_rules": [
|
||||
"rules": [
|
||||
{
|
||||
"rule_conditions": [
|
||||
"conditions": [
|
||||
{
|
||||
"condition_type": "categorical",
|
||||
"value_filter": "filter-foo"
|
||||
"type": "categorical",
|
||||
"filter_id": "filter-foo"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
|
@ -301,7 +301,7 @@
|
|||
"groups": ["group-1", "group-2"],
|
||||
"description":"Post update description",
|
||||
"detectors": [{"detector_index": 0, "rules": {"target_field_name": "airline",
|
||||
"rule_conditions": [ { "condition_type": "numerical_actual",
|
||||
"conditions": [ { "type": "numerical_actual",
|
||||
"condition": {"operator": "gt", "value": "10" } } ] } },
|
||||
{"detector_index": 1, "description": "updated description"}],
|
||||
"model_plot_config": {
|
||||
|
@ -327,7 +327,7 @@
|
|||
- match: { model_plot_config.terms: "foobar" }
|
||||
- match: { analysis_limits.model_memory_limit: "20mb" }
|
||||
- match: { analysis_config.categorization_filters: ["cat3.*"] }
|
||||
- match: { analysis_config.detectors.0.detector_rules.0.target_field_name: "airline" }
|
||||
- match: { analysis_config.detectors.0.rules.0.target_field_name: "airline" }
|
||||
- match: { analysis_config.detectors.0.detector_index: 0 }
|
||||
- match: { analysis_config.detectors.1.detector_description: "updated description" }
|
||||
- match: { analysis_config.detectors.1.detector_index: 1 }
|
||||
|
|
Loading…
Reference in New Issue