This is an automated email from the ASF dual-hosted git repository. jbertram pushed a commit to branch main in repository https://gitbox.apache.org/repos/asf/activemq-artemis.git
commit f11b96e7ed8430448fc3aff1d0e46bd8d05d6624 Author: Justin Bertram <[email protected]> AuthorDate: Mon Aug 29 14:45:19 2022 -0500 ARTEMIS-3962 porting changes from AMQ-8613 Improve performance of selectors with a big sequence of OR and AND logical expressions. --- .../artemis/selector/filter/LogicExpression.java | 188 +++++++++++++++------ .../activemq/artemis/selector/SelectorTest.java | 5 +- 2 files changed, 144 insertions(+), 49 deletions(-) diff --git a/artemis-selector/src/main/java/org/apache/activemq/artemis/selector/filter/LogicExpression.java b/artemis-selector/src/main/java/org/apache/activemq/artemis/selector/filter/LogicExpression.java index 4c9c294cfd..8d72551528 100755 --- a/artemis-selector/src/main/java/org/apache/activemq/artemis/selector/filter/LogicExpression.java +++ b/artemis-selector/src/main/java/org/apache/activemq/artemis/selector/filter/LogicExpression.java @@ -16,78 +16,170 @@ */ package org.apache.activemq.artemis.selector.filter; +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. * - * @version $Revision: 1.2 $ */ -public abstract class LogicExpression extends BinaryExpression implements BooleanExpression { +public abstract class LogicExpression implements BooleanExpression { - /** - * @param left - * @param right - */ - public LogicExpression(BooleanExpression left, BooleanExpression right) { - super(left, right); + protected final List<BooleanExpression> expressions = new ArrayList<>(2); + + private LogicExpression(BooleanExpression lvalue, BooleanExpression rvalue) { + expressions.add(lvalue); + expressions.add(rvalue); } - public static BooleanExpression createOR(BooleanExpression lvalue, BooleanExpression rvalue) { - return new LogicExpression(lvalue, rvalue) { + protected void addExpression(BooleanExpression expression) { + expressions.add(expression); + } - @Override - public Object evaluate(Filterable message) throws FilterException { + public BooleanExpression getLeft() { + if (expressions.size() == 2) { + return expressions.get(0); + } + throw new IllegalStateException("This expression is not binary: " + this); + } - Boolean lv = (Boolean) left.evaluate(message); - // Can we do an OR shortcut?? - if (lv != null && lv.booleanValue()) { - return Boolean.TRUE; - } + public BooleanExpression getRight() { + if (expressions.size() == 2) { + return expressions.get(1); + } + throw new IllegalStateException("This expression is not binary: " + this); + } - Boolean rv = (Boolean) right.evaluate(message); - return rv == null ? null : rv; - } + /** + * Returns the symbol that represents this binary expression. For example, addition is + * represented by "+" + * + * @return + */ + public abstract String getExpressionSymbol(); - @Override - public String getExpressionSymbol() { - return "OR"; + @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) { + 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) { - return new LogicExpression(lvalue, 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(Filterable message) throws FilterException; + + @Override + public abstract boolean matches(Filterable message) throws FilterException; - @Override - public Object evaluate(Filterable message) throws FilterException { + private static class ORExpression extends LogicExpression { - Boolean lv = (Boolean) left.evaluate(message); + ORExpression(BooleanExpression lvalue, BooleanExpression rvalue) { + super(lvalue, rvalue); + } - // Can we do an AND shortcut?? + @Override + public Object evaluate(Filterable message) throws FilterException { + boolean someNulls = false; + for (BooleanExpression expression : expressions) { + Boolean lv = (Boolean)expression.evaluate(message); + if (lv != null && lv.booleanValue()) { + return Boolean.TRUE; + } if (lv == null) { - return null; + someNulls = true; } - if (!lv.booleanValue()) { - return Boolean.FALSE; + } + if (someNulls) { + return null; + } + return Boolean.FALSE; + } + + @Override + public boolean matches(Filterable message) throws FilterException { + for (BooleanExpression expression : expressions) { + boolean lv = expression.matches(message); + if (lv) { + return true; } - - Boolean rv = (Boolean) right.evaluate(message); - return rv == null ? null : rv; } + return false; + } - @Override - public String getExpressionSymbol() { - return "AND"; - } - }; + @Override + public String getExpressionSymbol() { + return "OR"; + } } - @Override - public abstract Object evaluate(Filterable message) throws FilterException; + private static class ANDExpression extends LogicExpression { - @Override - public boolean matches(Filterable message) throws FilterException { - Object object = evaluate(message); - return object == Boolean.TRUE; - } + ANDExpression(BooleanExpression lvalue, BooleanExpression rvalue) { + super(lvalue, rvalue); + } + @Override + public Object evaluate(Filterable message) throws FilterException { + boolean someNulls = false; + for (BooleanExpression expression : expressions) { + Boolean lv = (Boolean)expression.evaluate(message); + if (lv != null && !lv.booleanValue()) { + return Boolean.FALSE; + } + if (lv == null) { + someNulls = true; + } + } + if (someNulls) { + return null; + } + return Boolean.TRUE; + } + + @Override + public boolean matches(Filterable message) throws FilterException { + for (BooleanExpression expression : expressions) { + boolean lv = expression.matches(message); + if (!lv) { + return false; + } + } + return true; + } + + @Override + public String getExpressionSymbol() { + return "AND"; + } + } } diff --git a/artemis-selector/src/test/java/org/apache/activemq/artemis/selector/SelectorTest.java b/artemis-selector/src/test/java/org/apache/activemq/artemis/selector/SelectorTest.java index 5f1e4b812c..a177c1f213 100755 --- a/artemis-selector/src/test/java/org/apache/activemq/artemis/selector/SelectorTest.java +++ b/artemis-selector/src/test/java/org/apache/activemq/artemis/selector/SelectorTest.java @@ -126,7 +126,10 @@ public class SelectorTest { assertSelector(message, "(trueProp OR falseProp) AND trueProp", true); assertSelector(message, "(trueProp OR falseProp) AND falseProp", false); - + 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); } @Test
