summaryrefslogtreecommitdiffstats
path: root/searchlib
diff options
context:
space:
mode:
authorLester Solbakken <lesters@yahoo-inc.com>2017-11-15 14:39:56 +0100
committerLester Solbakken <lesters@yahoo-inc.com>2017-11-15 14:39:56 +0100
commit9b913de9cd46de589d8f29436bfc46c1159a53de (patch)
tree4db87949d8b854e88c572b04900cea4b10756fa5 /searchlib
parent797270c0a6d6d59e55eb59f6d1a43ec323645026 (diff)
Add boolean operators to Java ranking evaluation
Diffstat (limited to 'searchlib')
-rw-r--r--searchlib/src/main/java/com/yahoo/searchlib/rankingexpression/evaluation/DoubleCompatibleValue.java15
-rw-r--r--searchlib/src/main/java/com/yahoo/searchlib/rankingexpression/evaluation/StringValue.java23
-rw-r--r--searchlib/src/main/java/com/yahoo/searchlib/rankingexpression/evaluation/TensorValue.java20
-rw-r--r--searchlib/src/main/java/com/yahoo/searchlib/rankingexpression/evaluation/Value.java6
-rwxr-xr-xsearchlib/src/main/java/com/yahoo/searchlib/rankingexpression/rule/ArithmeticNode.java2
-rw-r--r--searchlib/src/main/java/com/yahoo/searchlib/rankingexpression/rule/ArithmeticOperator.java20
-rw-r--r--searchlib/src/main/java/com/yahoo/searchlib/rankingexpression/rule/NotNode.java50
-rwxr-xr-xsearchlib/src/main/javacc/RankingExpressionParser.jj29
-rw-r--r--searchlib/src/test/java/com/yahoo/searchlib/rankingexpression/evaluation/EvaluationTestCase.java33
-rw-r--r--searchlib/src/test/java/com/yahoo/searchlib/rankingexpression/evaluation/EvaluationTester.java8
10 files changed, 187 insertions, 19 deletions
diff --git a/searchlib/src/main/java/com/yahoo/searchlib/rankingexpression/evaluation/DoubleCompatibleValue.java b/searchlib/src/main/java/com/yahoo/searchlib/rankingexpression/evaluation/DoubleCompatibleValue.java
index 0ed2bdd6331..0868af9bc72 100644
--- a/searchlib/src/main/java/com/yahoo/searchlib/rankingexpression/evaluation/DoubleCompatibleValue.java
+++ b/searchlib/src/main/java/com/yahoo/searchlib/rankingexpression/evaluation/DoubleCompatibleValue.java
@@ -44,6 +44,21 @@ public abstract class DoubleCompatibleValue extends Value {
}
@Override
+ public Value and(Value value) {
+ return new BooleanValue(asBoolean() && value.asBoolean());
+ }
+
+ @Override
+ public Value or(Value value) {
+ return new BooleanValue(asBoolean() || value.asBoolean());
+ }
+
+ @Override
+ public Value not() {
+ return new BooleanValue(!asBoolean());
+ }
+
+ @Override
public Value compare(TruthOperator operator, Value value) {
return new BooleanValue(operator.evaluate(asDouble(), value.asDouble()));
}
diff --git a/searchlib/src/main/java/com/yahoo/searchlib/rankingexpression/evaluation/StringValue.java b/searchlib/src/main/java/com/yahoo/searchlib/rankingexpression/evaluation/StringValue.java
index 5374a9d3ce6..b62081f2c6a 100644
--- a/searchlib/src/main/java/com/yahoo/searchlib/rankingexpression/evaluation/StringValue.java
+++ b/searchlib/src/main/java/com/yahoo/searchlib/rankingexpression/evaluation/StringValue.java
@@ -54,22 +54,37 @@ public class StringValue extends Value {
@Override
public Value subtract(Value value) {
- throw new UnsupportedOperationException("String values ('" + value + "') does not support subtraction");
+ throw new UnsupportedOperationException("String values ('" + value + "') do not support subtraction");
}
@Override
public Value multiply(Value value) {
- throw new UnsupportedOperationException("String values ('" + value + "') does not support multiplication");
+ throw new UnsupportedOperationException("String values ('" + value + "') do not support multiplication");
}
@Override
public Value divide(Value value) {
- throw new UnsupportedOperationException("String values ('" + value + "') does not support division");
+ throw new UnsupportedOperationException("String values ('" + value + "') do not support division");
}
@Override
public Value modulo(Value value) {
- throw new UnsupportedOperationException("String values ('" + value + "') does not support modulo");
+ throw new UnsupportedOperationException("String values ('" + value + "') do not support modulo");
+ }
+
+ @Override
+ public Value and(Value value) {
+ throw new UnsupportedOperationException("String values ('" + value + "') do not support and");
+ }
+
+ @Override
+ public Value or(Value value) {
+ throw new UnsupportedOperationException("String values ('" + value + "') do not support or");
+ }
+
+ @Override
+ public Value not() {
+ throw new UnsupportedOperationException("String values ('" + value + "') do not support not");
}
@Override
diff --git a/searchlib/src/main/java/com/yahoo/searchlib/rankingexpression/evaluation/TensorValue.java b/searchlib/src/main/java/com/yahoo/searchlib/rankingexpression/evaluation/TensorValue.java
index b283603e713..919a23eeaf5 100644
--- a/searchlib/src/main/java/com/yahoo/searchlib/rankingexpression/evaluation/TensorValue.java
+++ b/searchlib/src/main/java/com/yahoo/searchlib/rankingexpression/evaluation/TensorValue.java
@@ -89,6 +89,26 @@ public class TensorValue extends Value {
return new TensorValue(value.map((value) -> value % argument.asDouble()));
}
+ @Override
+ public Value and(Value argument) {
+ if (argument instanceof TensorValue)
+ return new TensorValue(value.join(((TensorValue)argument).value, (a, b) -> ((a!=0.0) && (b!=0.0)) ? 1.0 : 0.0 ));
+ else
+ return new TensorValue(value.map((value) -> ((value!=0.0) && argument.asBoolean()) ? 1 : 0));
+ }
+
+ @Override
+ public Value or(Value argument) {
+ if (argument instanceof TensorValue)
+ return new TensorValue(value.join(((TensorValue)argument).value, (a, b) -> ((a!=0.0) || (b!=0.0)) ? 1.0 : 0.0 ));
+ else
+ return new TensorValue(value.map((value) -> ((value!=0.0) || argument.asBoolean()) ? 1 : 0));
+ }
+
+ @Override
+ public Value not() {
+ return new TensorValue(value.map((value) -> (value==0.0) ? 1.0 : 0.0));
+ }
private Tensor asTensor(Value value, String operationName) {
if ( ! (value instanceof TensorValue))
diff --git a/searchlib/src/main/java/com/yahoo/searchlib/rankingexpression/evaluation/Value.java b/searchlib/src/main/java/com/yahoo/searchlib/rankingexpression/evaluation/Value.java
index f42082321b3..bcbce6e646f 100644
--- a/searchlib/src/main/java/com/yahoo/searchlib/rankingexpression/evaluation/Value.java
+++ b/searchlib/src/main/java/com/yahoo/searchlib/rankingexpression/evaluation/Value.java
@@ -43,6 +43,12 @@ public abstract class Value {
public abstract Value modulo(Value value);
+ public abstract Value and(Value value);
+
+ public abstract Value or(Value value);
+
+ public abstract Value not();
+
/** Perform the comparison specified by the operator between this value and the given value */
public abstract Value compare(TruthOperator operator, Value value);
diff --git a/searchlib/src/main/java/com/yahoo/searchlib/rankingexpression/rule/ArithmeticNode.java b/searchlib/src/main/java/com/yahoo/searchlib/rankingexpression/rule/ArithmeticNode.java
index 91d8abec1be..518a15bcc87 100755
--- a/searchlib/src/main/java/com/yahoo/searchlib/rankingexpression/rule/ArithmeticNode.java
+++ b/searchlib/src/main/java/com/yahoo/searchlib/rankingexpression/rule/ArithmeticNode.java
@@ -77,7 +77,7 @@ public final class ArithmeticNode extends CompositeNode {
Iterator<ExpressionNode> child = children.iterator();
Deque<ValueItem> stack = new ArrayDeque<>();
- stack.push(new ValueItem(ArithmeticOperator.PLUS, child.next().evaluate(context)));
+ stack.push(new ValueItem(ArithmeticOperator.OR, child.next().evaluate(context)));
for (Iterator<ArithmeticOperator> it = operators.iterator(); it.hasNext() && child.hasNext();) {
ArithmeticOperator op = it.next();
if (!stack.isEmpty()) {
diff --git a/searchlib/src/main/java/com/yahoo/searchlib/rankingexpression/rule/ArithmeticOperator.java b/searchlib/src/main/java/com/yahoo/searchlib/rankingexpression/rule/ArithmeticOperator.java
index 2187a96ba4d..aae59fe2af8 100644
--- a/searchlib/src/main/java/com/yahoo/searchlib/rankingexpression/rule/ArithmeticOperator.java
+++ b/searchlib/src/main/java/com/yahoo/searchlib/rankingexpression/rule/ArithmeticOperator.java
@@ -14,22 +14,28 @@ import java.util.List;
*/
public enum ArithmeticOperator {
- PLUS(0, "+") { public Value evaluate(Value x, Value y) {
+ OR(0, "||") { public Value evaluate(Value x, Value y) {
+ return x.or(y);
+ }},
+ AND(1, "&&") { public Value evaluate(Value x, Value y) {
+ return x.and(y);
+ }},
+ PLUS(2, "+") { public Value evaluate(Value x, Value y) {
return x.add(y);
}},
- MINUS(1, "-") { public Value evaluate(Value x, Value y) {
+ MINUS(3, "-") { public Value evaluate(Value x, Value y) {
return x.subtract(y);
}},
- MULTIPLY(2, "*") { public Value evaluate(Value x, Value y) {
+ MULTIPLY(4, "*") { public Value evaluate(Value x, Value y) {
return x.multiply(y);
}},
- DIVIDE(3, "/") { public Value evaluate(Value x, Value y) {
+ DIVIDE(5, "/") { public Value evaluate(Value x, Value y) {
return x.divide(y);
}},
- MODULO(4, "%") { public Value evaluate(Value x, Value y) {
+ MODULO(6, "%") { public Value evaluate(Value x, Value y) {
return x.modulo(y);
}};
-
+
/** A list of all the operators in this in order of decreasing precedence */
public static final List<ArithmeticOperator> operatorsByPrecedence = operatorsByPrecedence();
@@ -60,6 +66,8 @@ public enum ArithmeticOperator {
operators.add(MULTIPLY);
operators.add(MINUS);
operators.add(PLUS);
+ operators.add(AND);
+ operators.add(OR);
return Collections.unmodifiableList(operators);
}
diff --git a/searchlib/src/main/java/com/yahoo/searchlib/rankingexpression/rule/NotNode.java b/searchlib/src/main/java/com/yahoo/searchlib/rankingexpression/rule/NotNode.java
new file mode 100644
index 00000000000..8c459a032bd
--- /dev/null
+++ b/searchlib/src/main/java/com/yahoo/searchlib/rankingexpression/rule/NotNode.java
@@ -0,0 +1,50 @@
+// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.searchlib.rankingexpression.rule;
+
+import com.yahoo.searchlib.rankingexpression.evaluation.Context;
+import com.yahoo.searchlib.rankingexpression.evaluation.Value;
+
+import java.util.Collections;
+import java.util.Deque;
+import java.util.List;
+
+/**
+ * A node which flips the logical value produced from the nested expression.
+ *
+ * @author lesters
+ */
+public class NotNode extends BooleanNode {
+
+ private final ExpressionNode value;
+
+ public NotNode(ExpressionNode value) {
+ this.value = value;
+ }
+
+ public ExpressionNode getValue() {
+ return value;
+ }
+
+ @Override
+ public List<ExpressionNode> children() {
+ return Collections.singletonList(value);
+ }
+
+ @Override
+ public String toString(SerializationContext context, Deque<String> path, CompositeNode parent) {
+ return "!" + value.toString(context, path, parent);
+ }
+
+ @Override
+ public Value evaluate(Context context) {
+ return value.evaluate(context).not();
+ }
+
+ @Override
+ public NotNode setChildren(List<ExpressionNode> children) {
+ if (children.size() != 1) throw new IllegalArgumentException("Expected 1 children but got " + children.size());
+ return new NotNode(children.get(0));
+ }
+
+}
+
diff --git a/searchlib/src/main/javacc/RankingExpressionParser.jj b/searchlib/src/main/javacc/RankingExpressionParser.jj
index 01fed00202c..035a92b0365 100755
--- a/searchlib/src/main/javacc/RankingExpressionParser.jj
+++ b/searchlib/src/main/javacc/RankingExpressionParser.jj
@@ -86,6 +86,10 @@ TOKEN :
<IN: "in"> |
<F: "f"> |
+ <NOT: "!"> |
+ <AND: "&&"> |
+ <OR: "||"> |
+
<ABS: "abs"> |
<ACOS: "acos"> |
<ASIN: "asin"> |
@@ -204,7 +208,9 @@ ArithmeticOperator arithmetic() : { }
<SUB> { return ArithmeticOperator.MINUS; } |
<DIV> { return ArithmeticOperator.DIVIDE; } |
<MUL> { return ArithmeticOperator.MULTIPLY; } |
- <MOD> { return ArithmeticOperator.MODULO; } )
+ <MOD> { return ArithmeticOperator.MODULO; } |
+ <AND> { return ArithmeticOperator.AND; } |
+ <OR> { return ArithmeticOperator.OR; } )
{ return null; }
}
@@ -224,16 +230,23 @@ ExpressionNode value() :
{
ExpressionNode ret;
boolean neg = false;
+ boolean not = false;
}
{
- ( [ LOOKAHEAD(2) <SUB> { neg = true; } ]
- ( ret = constantPrimitive() |
- LOOKAHEAD(2) ret = ifExpression() |
- LOOKAHEAD(4) ret = function() |
- ret = feature() |
- ret = queryFeature() |
+ (
+ [ <NOT> { not = true; } ]
+ [ LOOKAHEAD(2) <SUB> { neg = true; } ]
+ ( ret = constantPrimitive() |
+ LOOKAHEAD(2) ret = ifExpression() |
+ LOOKAHEAD(4) ret = function() |
+ ret = feature() |
+ ret = queryFeature() |
( <LBRACE> ret = expression() <RBRACE> { ret = new EmbracedNode(ret); } ) ) )
- { return neg ? new NegativeNode(ret) : ret; }
+ {
+ ret = not ? new NotNode(ret) : ret;
+ ret = neg ? new NegativeNode(ret) : ret;
+ return ret;
+ }
}
IfNode ifExpression() :
diff --git a/searchlib/src/test/java/com/yahoo/searchlib/rankingexpression/evaluation/EvaluationTestCase.java b/searchlib/src/test/java/com/yahoo/searchlib/rankingexpression/evaluation/EvaluationTestCase.java
index 5d357777657..26d3695dd07 100644
--- a/searchlib/src/test/java/com/yahoo/searchlib/rankingexpression/evaluation/EvaluationTestCase.java
+++ b/searchlib/src/test/java/com/yahoo/searchlib/rankingexpression/evaluation/EvaluationTestCase.java
@@ -37,6 +37,7 @@ public class EvaluationTestCase {
tester.assertEvaluates(26, "2*3+4*5");
tester.assertEvaluates(1, "2/6+4/6");
tester.assertEvaluates(2 * 3 * 4 + 3 * 4 * 5 - 4 * 200 / 10, "2*3*4+3*4*5-4*200/10");
+ tester.assertEvaluates(3, "1 + 10 % 6 / 2");
// Conditionals
tester.assertEvaluates(2 * (3 * 4 + 3) * (4 * 5 - 4 * 200) / 10, "2*(3*4+3)*(4*5-4*200)/10");
@@ -89,6 +90,38 @@ public class EvaluationTestCase {
}
@Test
+ public void testBooleanEvaluation() {
+ EvaluationTester tester = new EvaluationTester();
+
+ // and
+ tester.assertEvaluates(false, "0 && 0");
+ tester.assertEvaluates(false, "0 && 1");
+ tester.assertEvaluates(false, "1 && 0");
+ tester.assertEvaluates(true, "1 && 1");
+ tester.assertEvaluates(true, "1 && 2");
+ tester.assertEvaluates(true, "1 && 0.1");
+
+ // or
+ tester.assertEvaluates(false, "0 || 0");
+ tester.assertEvaluates(true, "0 || 0.1");
+ tester.assertEvaluates(true, "0 || 1");
+ tester.assertEvaluates(true, "1 || 0");
+ tester.assertEvaluates(true, "1 || 1");
+
+ // not
+ tester.assertEvaluates(true, "!0");
+ tester.assertEvaluates(false, "!1");
+ tester.assertEvaluates(false, "!2");
+ tester.assertEvaluates(true, "!0 && 1");
+
+ // precedence
+ tester.assertEvaluates(0, "2 * (0 && 1)");
+ tester.assertEvaluates(2, "2 * (1 && 1)");
+ tester.assertEvaluates(true, "2 + 0 && 1");
+ tester.assertEvaluates(true, "1 && 0 + 2");
+ }
+
+ @Test
public void testTensorEvaluation() {
EvaluationTester tester = new EvaluationTester();
tester.assertEvaluates("{}", "tensor0", "{}");
diff --git a/searchlib/src/test/java/com/yahoo/searchlib/rankingexpression/evaluation/EvaluationTester.java b/searchlib/src/test/java/com/yahoo/searchlib/rankingexpression/evaluation/EvaluationTester.java
index d67c9dfd9dc..ee2b1c147e3 100644
--- a/searchlib/src/test/java/com/yahoo/searchlib/rankingexpression/evaluation/EvaluationTester.java
+++ b/searchlib/src/test/java/com/yahoo/searchlib/rankingexpression/evaluation/EvaluationTester.java
@@ -58,10 +58,18 @@ public class EvaluationTester {
return assertEvaluates(value, expressionString, defaultContext);
}
+ public RankingExpression assertEvaluates(boolean value, String expressionString) {
+ return assertEvaluates(value, expressionString, defaultContext);
+ }
+
public RankingExpression assertEvaluates(double value, String expressionString, Context context) {
return assertEvaluates(new DoubleValue(value), expressionString, context, "");
}
+ public RankingExpression assertEvaluates(boolean value, String expressionString, Context context) {
+ return assertEvaluates(new BooleanValue(value), expressionString, context, "");
+ }
+
public RankingExpression assertEvaluates(Value value, String expressionString, Context context, String explanation) {
try {
RankingExpression expression = new RankingExpression(expressionString);