[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); throw ExceptionsHelper.badRequestException(msg);
} }
for (RuleCondition condition : conditions) { 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, String msg = Messages.getMessage(Messages.JOB_CONFIG_DETECTION_RULE_CONDITION_CATEGORICAL_INVALID_OPTION,
DetectionRule.TARGET_FIELD_NAME_FIELD.getPreferredName()); DetectionRule.TARGET_FIELD_NAME_FIELD.getPreferredName());
throw ExceptionsHelper.badRequestException(msg); throw ExceptionsHelper.badRequestException(msg);

View File

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

View File

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

View File

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

View File

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

View File

@ -31,6 +31,7 @@ public class RuleConditionTypeTests extends ESTestCase {
public void testToString() { public void testToString() {
assertEquals("categorical", RuleConditionType.CATEGORICAL.toString()); assertEquals("categorical", RuleConditionType.CATEGORICAL.toString());
assertEquals("categorical_complement", RuleConditionType.CATEGORICAL_COMPLEMENT.toString());
assertEquals("numerical_actual", RuleConditionType.NUMERICAL_ACTUAL.toString()); assertEquals("numerical_actual", RuleConditionType.NUMERICAL_ACTUAL.toString());
assertEquals("numerical_typical", RuleConditionType.NUMERICAL_TYPICAL.toString()); assertEquals("numerical_typical", RuleConditionType.NUMERICAL_TYPICAL.toString());
assertEquals("numerical_diff_abs", RuleConditionType.NUMERICAL_DIFF_ABS.toString()); assertEquals("numerical_diff_abs", RuleConditionType.NUMERICAL_DIFF_ABS.toString());
@ -122,7 +123,18 @@ public class RuleConditionTypeTests extends ESTestCase {
} else { } else {
assertFalse(isNumerical); 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);
}
} }
} }
} }