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 {