فهرست منبع

EQL: Add IsNull/IsNotNull checks (#52791)

* EQL: Add IsNull/IsNotNull checks
* EQL: Simplify IsNull/IsNotNull optimization
* EQL: Split string tests over multiple lines
Ross Wolf 5 سال پیش
والد
کامیت
d6f827d154

+ 27 - 0
x-pack/plugin/eql/src/main/java/org/elasticsearch/xpack/eql/optimizer/Optimizer.java

@@ -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;
+            });
+        }
+    }
 }

+ 57 - 2
x-pack/plugin/eql/src/test/java/org/elasticsearch/xpack/eql/optimizer/OptimizerTests.java

@@ -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);