[ML] Add categorical exclude condition (elastic/x-pack-elasticsearch#4326)

Original commit: elastic/x-pack-elasticsearch@6c80988e08
This commit is contained in:
David Kyle 2018-04-10 13:19:00 +01:00 committed by GitHub
parent 411f683521
commit 7e4e1dabcf
6 changed files with 40 additions and 16 deletions

View File

@ -251,7 +251,7 @@ public class DetectionRule implements ToXContentObject, Writeable {
throw ExceptionsHelper.badRequestException(msg);
}
for (RuleCondition condition : conditions) {
if (condition.getType() == RuleConditionType.CATEGORICAL && targetFieldName != null) {
if (condition.getType().isCategorical() && 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);

View File

@ -698,6 +698,7 @@ public class Detector implements ToXContentObject, Writeable {
List<String> validOptions = Collections.emptyList();
switch (condition.getType()) {
case CATEGORICAL:
case CATEGORICAL_COMPLEMENT:
validOptions = extractAnalysisFields();
break;
case NUMERICAL_ACTUAL:

View File

@ -197,6 +197,7 @@ public class RuleCondition implements ToXContentObject, Writeable {
private static void verifyFieldsBoundToType(RuleCondition ruleCondition) throws ElasticsearchParseException {
switch (ruleCondition.getType()) {
case CATEGORICAL:
case CATEGORICAL_COMPLEMENT:
verifyCategorical(ruleCondition);
break;
case NUMERICAL_ACTUAL:

View File

@ -6,6 +6,7 @@
package org.elasticsearch.xpack.core.ml.job.config;
import org.elasticsearch.Version;
import org.elasticsearch.common.io.stream.StreamInput;
import org.elasticsearch.common.io.stream.StreamOutput;
import org.elasticsearch.common.io.stream.Writeable;
@ -14,22 +15,29 @@ import java.io.IOException;
import java.util.Locale;
public enum RuleConditionType implements Writeable {
CATEGORICAL(false),
NUMERICAL_ACTUAL(true),
NUMERICAL_TYPICAL(true),
NUMERICAL_DIFF_ABS(true),
TIME(false);
CATEGORICAL(false, true),
NUMERICAL_ACTUAL(true, false),
NUMERICAL_TYPICAL(true, false),
NUMERICAL_DIFF_ABS(true, false),
TIME(false, false),
CATEGORICAL_COMPLEMENT(false, true);
private final boolean isNumerical;
private final boolean isCategorical;
RuleConditionType(boolean isNumerical) {
RuleConditionType(boolean isNumerical, boolean isCategorical) {
this.isNumerical = isNumerical;
this.isCategorical = isCategorical;
}
public boolean isNumerical() {
return isNumerical;
}
public boolean isCategorical() {
return isCategorical;
}
/**
* Case-insensitive from string method.
*
@ -47,7 +55,11 @@ public enum RuleConditionType implements Writeable {
@Override
public void writeTo(StreamOutput out) throws IOException {
out.writeEnum(this);
if (this == CATEGORICAL_COMPLEMENT && out.getVersion().before(Version.V_6_3_0)) {
out.writeEnum(CATEGORICAL);
} else {
out.writeEnum(this);
}
}
@Override

View File

@ -19,25 +19,22 @@ public class RuleConditionTests extends AbstractSerializingTestCase<RuleConditio
String fieldName = null;
String valueFilter = null;
String fieldValue = null;
RuleConditionType r = randomFrom(RuleConditionType.values());
switch (r) {
case CATEGORICAL:
RuleConditionType type = randomFrom(RuleConditionType.values());
if (type.isCategorical()) {
valueFilter = randomAlphaOfLengthBetween(1, 20);
if (randomBoolean()) {
fieldName = randomAlphaOfLengthBetween(1, 20);
}
break;
default:
// no need to randomize, it is properly randomily tested in
} else {
// no need to randomize, it is properly randomly tested in
// ConditionTest
condition = new Condition(Operator.LT, Long.toString(randomLong()));
if (randomBoolean()) {
fieldName = randomAlphaOfLengthBetween(1, 20);
fieldValue = randomAlphaOfLengthBetween(1, 20);
}
break;
}
return new RuleCondition(r, fieldName, fieldValue, condition, valueFilter);
return new RuleCondition(type, fieldName, fieldValue, condition, valueFilter);
}
@Override
@ -201,6 +198,7 @@ public class RuleConditionTests extends AbstractSerializingTestCase<RuleConditio
public void testVerify_GivenValidCategorical() {
// no validation error:
new RuleCondition(RuleConditionType.CATEGORICAL, "metric", null, null, "myFilter");
new RuleCondition(RuleConditionType.CATEGORICAL_COMPLEMENT, "metric", null, null, "myFilter");
}
public void testVerify_GivenValidNumericalActual() {

View File

@ -31,6 +31,7 @@ public class RuleConditionTypeTests extends ESTestCase {
public void testToString() {
assertEquals("categorical", RuleConditionType.CATEGORICAL.toString());
assertEquals("categorical_complement", RuleConditionType.CATEGORICAL_COMPLEMENT.toString());
assertEquals("numerical_actual", RuleConditionType.NUMERICAL_ACTUAL.toString());
assertEquals("numerical_typical", RuleConditionType.NUMERICAL_TYPICAL.toString());
assertEquals("numerical_diff_abs", RuleConditionType.NUMERICAL_DIFF_ABS.toString());
@ -122,7 +123,18 @@ public class RuleConditionTypeTests extends ESTestCase {
} else {
assertFalse(isNumerical);
}
}
}
public void testIsCategorical() {
for (RuleConditionType type : EnumSet.allOf(RuleConditionType.class)) {
boolean isCategorical = type.isCategorical();
if (type == RuleConditionType.CATEGORICAL ||
type == RuleConditionType.CATEGORICAL_COMPLEMENT) {
assertTrue(isCategorical);
} else {
assertFalse(isCategorical);
}
}
}
}