You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@activemq.apache.org by jb...@apache.org on 2022/08/17 05:47:41 UTC

[activemq] branch activemq-5.17.x updated: AMQ-8613 Improve performance of selectors with a big sequence of OR and AND logical expressions

This is an automated email from the ASF dual-hosted git repository.

jbonofre pushed a commit to branch activemq-5.17.x
in repository https://gitbox.apache.org/repos/asf/activemq.git


The following commit(s) were added to refs/heads/activemq-5.17.x by this push:
     new 8d9fdd3b0 AMQ-8613 Improve performance of selectors with a big sequence of OR and AND logical expressions
8d9fdd3b0 is described below

commit 8d9fdd3b07fde1b3c1f2e606b1ea6cdd895ab154
Author: Enrico Olivelli <eo...@apache.org>
AuthorDate: Fri May 27 11:21:40 2022 +0200

    AMQ-8613 Improve performance of selectors with a big sequence of OR and AND logical expressions
    
    (cherry picked from commit 4b08978062d05beb15cfb83b4e47cea298ae95ba)
---
 .../apache/activemq/filter/LogicExpression.java    | 179 ++++++++++++++++-----
 .../org/apache/activemq/selector/SelectorTest.java |   5 +-
 2 files changed, 140 insertions(+), 44 deletions(-)

diff --git a/activemq-client/src/main/java/org/apache/activemq/filter/LogicExpression.java b/activemq-client/src/main/java/org/apache/activemq/filter/LogicExpression.java
index fdc022bae..e9145a971 100644
--- a/activemq-client/src/main/java/org/apache/activemq/filter/LogicExpression.java
+++ b/activemq-client/src/main/java/org/apache/activemq/filter/LogicExpression.java
@@ -17,78 +17,171 @@
 package org.apache.activemq.filter;
 
 import javax.jms.JMSException;
+import java.util.ArrayList;
+import java.util.List;
 
 /**
- * A filter performing a comparison of two objects
- * 
- * 
+ * A sequence of expressions, to be combined with OR or AND conjunctions.
+ *
  */
-public abstract class LogicExpression extends BinaryExpression implements BooleanExpression {
+public abstract class LogicExpression implements BooleanExpression {
+
+    protected final List<BooleanExpression> expressions = new ArrayList<>(2);
+
+    private LogicExpression(BooleanExpression lvalue, BooleanExpression rvalue) {
+        expressions.add(lvalue);
+        expressions.add(rvalue);
+    }
+
+    protected void addExpression(BooleanExpression expression) {
+        expressions.add(expression);
+    }
+
+    public BooleanExpression getLeft() {
+        if (expressions.size() == 2) {
+            return expressions.get(0);
+        }
+        throw new IllegalStateException("This expression is not binary: " + this);
+    }
+
+    public BooleanExpression getRight() {
+        if (expressions.size() == 2) {
+            return expressions.get(1);
+        }
+        throw new IllegalStateException("This expression is not binary: " + this);
+    }
 
     /**
-     * @param left
-     * @param right
+     * Returns the symbol that represents this binary expression.  For example, addition is
+     * represented by "+"
+     *
+     * @return
      */
-    public LogicExpression(BooleanExpression left, BooleanExpression right) {
-        super(left, right);
+    public abstract String getExpressionSymbol();
+
+    @Override
+    public String toString() {
+        if (expressions.size() == 2) {
+            return "( " + expressions.get(0) + " " + getExpressionSymbol() + " " + expressions.get(1) + " )";
+        }
+        StringBuilder result = new StringBuilder("(");
+        int count = 0;
+        for (BooleanExpression expression : expressions) {
+            if (count++ > 0) {
+                result.append(" " + getExpressionSymbol() + " ");
+            }
+            result.append(expression.toString());
+        }
+        result.append(")");
+        return result.toString();
     }
 
     public static BooleanExpression createOR(BooleanExpression lvalue, BooleanExpression rvalue) {
-        return new LogicExpression(lvalue, rvalue) {
+        if (lvalue instanceof ORExpression) {
+            ORExpression orExpression = (ORExpression) lvalue;
+            orExpression.addExpression(rvalue);
+            return orExpression;
+        } else {
+            return new ORExpression(lvalue, rvalue);
+        }
+    }
+
+    public static BooleanExpression createAND(BooleanExpression lvalue, BooleanExpression rvalue) {
+        if (lvalue instanceof ANDExpression) {
+            ANDExpression orExpression = (ANDExpression) lvalue;
+            orExpression.addExpression(rvalue);
+            return orExpression;
+        } else {
+            return new ANDExpression(lvalue, rvalue);
+        }
+    }
+
+    @Override
+    public abstract Object evaluate(MessageEvaluationContext message) throws JMSException;
 
-            public Object evaluate(MessageEvaluationContext message) throws JMSException {
+    @Override
+    public abstract boolean matches(MessageEvaluationContext message) throws JMSException;
 
-                Boolean lv = (Boolean)left.evaluate(message);
+    public static class ORExpression extends LogicExpression {
+
+        public ORExpression(BooleanExpression lvalue, BooleanExpression rvalue) {
+            super(lvalue, rvalue);
+        }
+
+        @Override
+        public Object evaluate(MessageEvaluationContext message) throws JMSException {
+            boolean someNulls = false;
+            for (BooleanExpression expression : expressions) {
+                Boolean lv = (Boolean)expression.evaluate(message);
                 if (lv != null && lv.booleanValue()) {
                     return Boolean.TRUE;
                 }
-                Boolean rv = (Boolean)right.evaluate(message);
-                if (rv != null && rv.booleanValue()) {
-                    return Boolean.TRUE;
+                if (lv == null) {
+                    someNulls = true;
                 }
-                if (lv == null || rv == null) {
-                    return null;
-                }
-                return Boolean.FALSE;
             }
+            if (someNulls) {
+                return null;
+            }
+            return Boolean.FALSE;
+        }
 
-            public String getExpressionSymbol() {
-                return "OR";
+        @Override
+        public boolean matches(MessageEvaluationContext message) throws JMSException {
+            for (BooleanExpression expression : expressions) {
+                boolean lv = expression.matches(message);
+                if (lv) {
+                    return true;
+                }
             }
-        };
-    }
+            return false;
+        }
 
-    public static BooleanExpression createAND(BooleanExpression lvalue, BooleanExpression rvalue) {
-        return new LogicExpression(lvalue, rvalue) {
+        @Override
+        public String getExpressionSymbol() {
+            return "OR";
+        }
+
+    }
 
-            public Object evaluate(MessageEvaluationContext message) throws JMSException {
+    private static class ANDExpression extends LogicExpression {
 
-                Boolean lv = (Boolean)left.evaluate(message);
+        public ANDExpression(BooleanExpression lvalue, BooleanExpression rvalue) {
+            super(lvalue, rvalue);
+        }
 
+        @Override
+        public Object evaluate(MessageEvaluationContext message) throws JMSException {
+            boolean someNulls = false;
+            for (BooleanExpression expression : expressions) {
+                Boolean lv = (Boolean)expression.evaluate(message);
                 if (lv != null && !lv.booleanValue()) {
                     return Boolean.FALSE;
                 }
-                Boolean rv = (Boolean)right.evaluate(message);
-                if (rv != null && !rv.booleanValue()) {
-                    return Boolean.FALSE;
-                }
-                if (lv == null || rv == null) {
-                    return null;
+                if (lv == null) {
+                    someNulls = true;
                 }
-                return Boolean.TRUE;
             }
-
-            public String getExpressionSymbol() {
-                return "AND";
+            if (someNulls) {
+                return null;
             }
-        };
-    }
+            return Boolean.TRUE;
+        }
 
-    public abstract Object evaluate(MessageEvaluationContext message) throws JMSException;
+        @Override
+        public boolean matches(MessageEvaluationContext message) throws JMSException {
+            for (BooleanExpression expression : expressions) {
+                boolean lv = expression.matches(message);
+                if (!lv) {
+                    return false;
+                }
+            }
+            return true;
+        }
 
-    public boolean matches(MessageEvaluationContext message) throws JMSException {
-        Object object = evaluate(message);
-        return object != null && object == Boolean.TRUE;
+        @Override
+        public String getExpressionSymbol() {
+            return "AND";
+        }
     }
-
 }
diff --git a/activemq-unit-tests/src/test/java/org/apache/activemq/selector/SelectorTest.java b/activemq-unit-tests/src/test/java/org/apache/activemq/selector/SelectorTest.java
index 3ad84302f..00d71a2bf 100644
--- a/activemq-unit-tests/src/test/java/org/apache/activemq/selector/SelectorTest.java
+++ b/activemq-unit-tests/src/test/java/org/apache/activemq/selector/SelectorTest.java
@@ -40,7 +40,10 @@ public class SelectorTest extends TestCase {
         assertSelector(message, "(trueProp OR falseProp) AND trueProp", true);
         assertSelector(message, "(trueProp OR falseProp) AND falseProp", false);
         assertSelector(message, "trueProp", true);
-
+        assertSelector(message, "(falseProp OR falseProp OR falseProp OR falseProp OR falseProp OR falseProp OR trueProp)", true);
+        assertSelector(message, "(falseProp OR falseProp OR falseProp OR falseProp OR falseProp OR falseProp OR falseProp)", false);
+        assertSelector(message, "(trueProp AND trueProp AND trueProp AND trueProp AND trueProp AND trueProp AND falseProp)", false);
+        assertSelector(message, "(trueProp AND trueProp AND trueProp AND trueProp AND trueProp AND trueProp AND trueProp)", true);
     }
 
     public void testXPathSelectors() throws Exception {