EQL: Add IsNull/IsNotNull checks (#52791)

* EQL: Add IsNull/IsNotNull checks
* EQL: Simplify IsNull/IsNotNull optimization
* EQL: Split string tests over multiple lines
This commit is contained in:
Ross Wolf 2020-03-09 10:40:20 -06:00
parent f78bd00f57
commit f5f922c6f6
No known key found for this signature in database
GPG Key ID: 6A4E50040D9A723A
2 changed files with 84 additions and 2 deletions

View File

@ -8,6 +8,8 @@ package org.elasticsearch.xpack.eql.optimizer;
import org.elasticsearch.xpack.ql.expression.Expression;
import org.elasticsearch.xpack.ql.expression.predicate.logical.Not;
import org.elasticsearch.xpack.ql.expression.predicate.nulls.IsNotNull;
import org.elasticsearch.xpack.ql.expression.predicate.nulls.IsNull;
import org.elasticsearch.xpack.ql.expression.predicate.operator.comparison.BinaryComparison;
import org.elasticsearch.xpack.ql.expression.predicate.operator.comparison.Equals;
import org.elasticsearch.xpack.ql.expression.predicate.operator.comparison.NotEquals;
@ -43,6 +45,7 @@ public class Optimizer extends RuleExecutor<LogicalPlan> {
new BooleanLiteralsOnTheRight(),
// needs to occur before BinaryComparison combinations
new ReplaceWildcards(),
new ReplaceNullChecks(),
new PropagateEquals(),
new CombineBinaryComparisons(),
// prune/elimination
@ -102,4 +105,28 @@ public class Optimizer extends RuleExecutor<LogicalPlan> {
});
}
}
private static class ReplaceNullChecks extends OptimizerRule<Filter> {
@Override
protected LogicalPlan rule(Filter filter) {
return filter.transformExpressionsUp(e -> {
// expr == null || expr != null
if (e instanceof Equals || e instanceof NotEquals) {
BinaryComparison cmp = (BinaryComparison) e;
if (cmp.right().foldable() && cmp.right().fold() == null) {
if (e instanceof Equals) {
e = new IsNull(e.source(), cmp.left());
} else {
e = new IsNotNull(e.source(), cmp.left());
}
}
}
return e;
});
}
}
}

View File

@ -15,6 +15,8 @@ import org.elasticsearch.xpack.eql.parser.EqlParser;
import org.elasticsearch.xpack.ql.expression.FieldAttribute;
import org.elasticsearch.xpack.ql.expression.predicate.logical.And;
import org.elasticsearch.xpack.ql.expression.predicate.logical.Not;
import org.elasticsearch.xpack.ql.expression.predicate.nulls.IsNotNull;
import org.elasticsearch.xpack.ql.expression.predicate.nulls.IsNull;
import org.elasticsearch.xpack.ql.expression.predicate.regex.Like;
import org.elasticsearch.xpack.ql.index.EsIndex;
import org.elasticsearch.xpack.ql.index.IndexResolution;
@ -24,6 +26,8 @@ import org.elasticsearch.xpack.ql.plan.logical.OrderBy;
import org.elasticsearch.xpack.ql.type.EsField;
import org.elasticsearch.xpack.ql.type.TypesTests;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
public class OptimizerTests extends ESTestCase {
@ -32,6 +36,7 @@ public class OptimizerTests extends ESTestCase {
private static final String INDEX_NAME = "test";
private EqlParser parser = new EqlParser();
private IndexResolution index = loadIndexResolution("mapping-default.json");
private static Map<String, EsField> loadEqlMapping(String name) {
return TypesTests.loadMapping(name);
}
@ -51,9 +56,54 @@ public class OptimizerTests extends ESTestCase {
return accept(index, eql);
}
public void testIsNull() {
List<String> tests = Arrays.asList(
"foo where command_line == null",
"foo where null == command_line"
);
for (String q : tests) {
LogicalPlan plan = accept(q);
assertTrue(plan instanceof OrderBy);
plan = ((OrderBy) plan).child();
assertTrue(plan instanceof Filter);
Filter filter = (Filter) plan;
And condition = (And) filter.condition();
assertTrue(condition.right() instanceof IsNull);
IsNull check = (IsNull) condition.right();
assertEquals(((FieldAttribute) check.field()).name(), "command_line");
}
}
public void testIsNotNull() {
List<String> tests = Arrays.asList(
"foo where command_line != null",
"foo where null != command_line"
);
for (String q : tests) {
LogicalPlan plan = accept(q);
assertTrue(plan instanceof OrderBy);
plan = ((OrderBy) plan).child();
assertTrue(plan instanceof Filter);
Filter filter = (Filter) plan;
And condition = (And) filter.condition();
assertTrue(condition.right() instanceof IsNotNull);
IsNotNull check = (IsNotNull) condition.right();
assertEquals(((FieldAttribute) check.field()).name(), "command_line");
}
}
public void testEqualsWildcard() {
for (String q : new String[]{"foo where command_line == '* bar *'", "foo where '* bar *' == command_line"}) {
List<String> tests = Arrays.asList(
"foo where command_line == '* bar *'",
"foo where '* bar *' == command_line"
);
for (String q : tests) {
LogicalPlan plan = accept(q);
assertTrue(plan instanceof OrderBy);
plan = ((OrderBy) plan).child();
@ -72,7 +122,12 @@ public class OptimizerTests extends ESTestCase {
}
public void testNotEqualsWildcard() {
for (String q : new String[]{"foo where command_line != '* baz *'", "foo where '* baz *' != command_line"}) {
List<String> tests = Arrays.asList(
"foo where command_line != '* baz *'",
"foo where '* baz *' != command_line"
);
for (String q : tests) {
LogicalPlan plan = accept(q);
assertTrue(plan instanceof OrderBy);