mirror of https://github.com/apache/druid.git
Support string type in math expression (#2836)
* Support string type in math expression addressed comments addressed comments Addressed comments * Updated math function document * Addressed comments
This commit is contained in:
parent
2362effd8c
commit
e10def32f2
|
@ -51,6 +51,10 @@
|
|||
<groupId>commons-codec</groupId>
|
||||
<artifactId>commons-codec</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>commons-lang</groupId>
|
||||
<artifactId>commons-lang</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.commons</groupId>
|
||||
<artifactId>commons-dbcp2</artifactId>
|
||||
|
|
|
@ -11,16 +11,22 @@ expr : ('-'|'!') expr # unaryOpExpr
|
|||
| IDENTIFIER # identifierExpr
|
||||
| DOUBLE # doubleExpr
|
||||
| LONG # longExpr
|
||||
| STRING # string
|
||||
;
|
||||
|
||||
fnArgs : expr (',' expr)* # functionArgs
|
||||
;
|
||||
|
||||
IDENTIFIER : [_$a-zA-Z][_$a-zA-Z0-9]* ;
|
||||
IDENTIFIER : [_$a-zA-Z][_$a-zA-Z0-9]* | '"' [_$a-zA-Z][_$a-zA-Z0-9]* '"';
|
||||
LONG : [0-9]+ ;
|
||||
DOUBLE : [0-9]+ '.' [0-9]* ;
|
||||
WS : [ \t\r\n]+ -> skip ;
|
||||
|
||||
STRING : '\'' (ESC | ~ [\'\\])* '\'';
|
||||
fragment ESC : '\\' ([\'\\/bfnrt] | UNICODE) ;
|
||||
fragment UNICODE : 'u' HEX HEX HEX HEX ;
|
||||
fragment HEX : [0-9a-fA-F] ;
|
||||
|
||||
MINUS : '-' ;
|
||||
NOT : '!' ;
|
||||
POW : '^' ;
|
||||
|
|
|
@ -19,15 +19,18 @@
|
|||
|
||||
package io.druid.math.expr;
|
||||
|
||||
import com.google.common.base.Strings;
|
||||
import com.google.common.math.LongMath;
|
||||
import io.druid.java.util.common.IAE;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
*/
|
||||
public interface Expr
|
||||
{
|
||||
Number eval(ObjectBinding bindings);
|
||||
ExprEval eval(ObjectBinding bindings);
|
||||
|
||||
interface ObjectBinding
|
||||
{
|
||||
|
@ -67,10 +70,32 @@ class LongExpr extends ConstantExpr
|
|||
}
|
||||
|
||||
@Override
|
||||
public Number eval(ObjectBinding bindings)
|
||||
public ExprEval eval(ObjectBinding bindings)
|
||||
{
|
||||
return ExprEval.of(value);
|
||||
}
|
||||
}
|
||||
|
||||
class StringExpr extends ConstantExpr
|
||||
{
|
||||
private final String value;
|
||||
|
||||
public StringExpr(String value)
|
||||
{
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
return value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ExprEval eval(ObjectBinding bindings)
|
||||
{
|
||||
return ExprEval.of(value);
|
||||
}
|
||||
}
|
||||
|
||||
class DoubleExpr extends ConstantExpr
|
||||
|
@ -89,9 +114,9 @@ class DoubleExpr extends ConstantExpr
|
|||
}
|
||||
|
||||
@Override
|
||||
public Number eval(ObjectBinding bindings)
|
||||
public ExprEval eval(ObjectBinding bindings)
|
||||
{
|
||||
return value;
|
||||
return ExprEval.of(value);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -111,14 +136,9 @@ class IdentifierExpr extends ConstantExpr
|
|||
}
|
||||
|
||||
@Override
|
||||
public Number eval(ObjectBinding bindings)
|
||||
public ExprEval eval(ObjectBinding bindings)
|
||||
{
|
||||
Number val = bindings.get(value);
|
||||
if (val == null) {
|
||||
return null;
|
||||
} else {
|
||||
return val instanceof Long ? val : val.doubleValue();
|
||||
}
|
||||
return ExprEval.bestEffortOf(bindings.get(value));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -140,7 +160,7 @@ class FunctionExpr implements Expr
|
|||
}
|
||||
|
||||
@Override
|
||||
public Number eval(ObjectBinding bindings)
|
||||
public ExprEval eval(ObjectBinding bindings)
|
||||
{
|
||||
return Parser.func.get(name.toLowerCase()).apply(args, bindings);
|
||||
}
|
||||
|
@ -180,14 +200,16 @@ class UnaryMinusExpr extends UnaryExpr
|
|||
}
|
||||
|
||||
@Override
|
||||
public Number eval(ObjectBinding bindings)
|
||||
public ExprEval eval(ObjectBinding bindings)
|
||||
{
|
||||
Number valObj = expr.eval(bindings);
|
||||
if (valObj instanceof Long) {
|
||||
return -1 * valObj.longValue();
|
||||
} else {
|
||||
return -1 * valObj.doubleValue();
|
||||
ExprEval ret = expr.eval(bindings);
|
||||
if (ret.type() == ExprType.LONG) {
|
||||
return ExprEval.of(-ret.asLong());
|
||||
}
|
||||
if (ret.type() == ExprType.DOUBLE) {
|
||||
return ExprEval.of(-ret.asDouble());
|
||||
}
|
||||
throw new IAE("unsupported type " + ret.type());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -212,10 +234,16 @@ class UnaryNotExpr extends UnaryExpr
|
|||
}
|
||||
|
||||
@Override
|
||||
public Number eval(ObjectBinding bindings)
|
||||
public ExprEval eval(ObjectBinding bindings)
|
||||
{
|
||||
Number valObj = expr.eval(bindings);
|
||||
return valObj.doubleValue() > 0 ? 0.0d : 1.0d;
|
||||
ExprEval ret = expr.eval(bindings);
|
||||
if (ret.type() == ExprType.LONG) {
|
||||
return ExprEval.of(ret.asBoolean() ? 0L : 1L);
|
||||
}
|
||||
if (ret.type() == ExprType.DOUBLE) {
|
||||
return ExprEval.of(ret.asBoolean() ? 0.0d :1.0d);
|
||||
}
|
||||
throw new IllegalArgumentException("unsupported type " + ret.type());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -238,16 +266,6 @@ abstract class BinaryOpExprBase implements Expr
|
|||
this.right = right;
|
||||
}
|
||||
|
||||
protected boolean isNull(Number left, Number right)
|
||||
{
|
||||
return left == null || right == null;
|
||||
}
|
||||
|
||||
protected boolean isLong(Number left, Number right)
|
||||
{
|
||||
return left instanceof Long && right instanceof Long;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(Visitor visitor)
|
||||
{
|
||||
|
@ -263,346 +281,348 @@ abstract class BinaryOpExprBase implements Expr
|
|||
}
|
||||
}
|
||||
|
||||
class BinMinusExpr extends BinaryOpExprBase
|
||||
abstract class BinaryEvalOpExprBase extends BinaryOpExprBase
|
||||
{
|
||||
public BinaryEvalOpExprBase(String op, Expr left, Expr right)
|
||||
{
|
||||
super(op, left, right);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ExprEval eval(ObjectBinding bindings)
|
||||
{
|
||||
ExprEval leftVal = left.eval(bindings);
|
||||
ExprEval rightVal = right.eval(bindings);
|
||||
if (leftVal.isNull() || rightVal.isNull()) {
|
||||
return ExprEval.of(null);
|
||||
}
|
||||
if (leftVal.type() == ExprType.STRING || rightVal.type() == ExprType.STRING) {
|
||||
return evalString(leftVal.asString(), rightVal.asString());
|
||||
}
|
||||
if (leftVal.type() == ExprType.LONG && rightVal.type() == ExprType.LONG) {
|
||||
return ExprEval.of(evalLong(leftVal.asLong(), rightVal.asLong()));
|
||||
}
|
||||
return ExprEval.of(evalDouble(leftVal.asDouble(), rightVal.asDouble()));
|
||||
}
|
||||
|
||||
protected ExprEval evalString(String left, String right)
|
||||
{
|
||||
throw new IllegalArgumentException("unsupported type " + ExprType.STRING);
|
||||
}
|
||||
|
||||
protected abstract long evalLong(long left, long right);
|
||||
|
||||
protected abstract double evalDouble(double left, double right);
|
||||
}
|
||||
|
||||
class BinMinusExpr extends BinaryEvalOpExprBase
|
||||
{
|
||||
BinMinusExpr(String op, Expr left, Expr right)
|
||||
{
|
||||
super(op, left, right);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Number eval(ObjectBinding bindings)
|
||||
protected final long evalLong(long left, long right)
|
||||
{
|
||||
Number leftVal = left.eval(bindings);
|
||||
Number rightVal = right.eval(bindings);
|
||||
if (isNull(leftVal, rightVal)) {
|
||||
return null;
|
||||
} else if (isLong(leftVal, rightVal)) {
|
||||
return leftVal.longValue() - rightVal.longValue();
|
||||
} else {
|
||||
return leftVal.doubleValue() - rightVal.doubleValue();
|
||||
}
|
||||
return left - right;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected final double evalDouble(double left, double right)
|
||||
{
|
||||
return left - right;
|
||||
}
|
||||
}
|
||||
|
||||
class BinPowExpr extends BinaryOpExprBase
|
||||
class BinPowExpr extends BinaryEvalOpExprBase
|
||||
{
|
||||
|
||||
BinPowExpr(String op, Expr left, Expr right)
|
||||
{
|
||||
super(op, left, right);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Number eval(ObjectBinding bindings)
|
||||
protected final long evalLong(long left, long right)
|
||||
{
|
||||
Number leftVal = left.eval(bindings);
|
||||
Number rightVal = right.eval(bindings);
|
||||
if (isNull(leftVal, rightVal)) {
|
||||
return null;
|
||||
} else if (isLong(leftVal, rightVal)) {
|
||||
return LongMath.pow(leftVal.longValue(), rightVal.intValue());
|
||||
} else {
|
||||
return Math.pow(leftVal.doubleValue(), rightVal.doubleValue());
|
||||
}
|
||||
return LongMath.pow(left, (int)right);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected final double evalDouble(double left, double right)
|
||||
{
|
||||
return Math.pow(left, right);
|
||||
}
|
||||
}
|
||||
|
||||
class BinMulExpr extends BinaryOpExprBase
|
||||
class BinMulExpr extends BinaryEvalOpExprBase
|
||||
{
|
||||
|
||||
BinMulExpr(String op, Expr left, Expr right)
|
||||
{
|
||||
super(op, left, right);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Number eval(ObjectBinding bindings)
|
||||
protected final long evalLong(long left, long right)
|
||||
{
|
||||
Number leftVal = left.eval(bindings);
|
||||
Number rightVal = right.eval(bindings);
|
||||
if (isNull(leftVal, rightVal)) {
|
||||
return null;
|
||||
} else if (isLong(leftVal, rightVal)) {
|
||||
return leftVal.longValue() * rightVal.longValue();
|
||||
} else {
|
||||
return leftVal.doubleValue() * rightVal.doubleValue();
|
||||
}
|
||||
return left * right;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected final double evalDouble(double left, double right)
|
||||
{
|
||||
return left * right;
|
||||
}
|
||||
}
|
||||
|
||||
class BinDivExpr extends BinaryOpExprBase
|
||||
class BinDivExpr extends BinaryEvalOpExprBase
|
||||
{
|
||||
|
||||
BinDivExpr(String op, Expr left, Expr right)
|
||||
{
|
||||
super(op, left, right);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Number eval(ObjectBinding bindings)
|
||||
protected final long evalLong(long left, long right)
|
||||
{
|
||||
Number leftVal = left.eval(bindings);
|
||||
Number rightVal = right.eval(bindings);
|
||||
if (isLong(leftVal, rightVal)) {
|
||||
return leftVal.longValue() / rightVal.longValue();
|
||||
} else {
|
||||
return leftVal.doubleValue() / rightVal.doubleValue();
|
||||
}
|
||||
return left / right;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected final double evalDouble(double left, double right)
|
||||
{
|
||||
return left / right;
|
||||
}
|
||||
}
|
||||
|
||||
class BinModuloExpr extends BinaryOpExprBase
|
||||
class BinModuloExpr extends BinaryEvalOpExprBase
|
||||
{
|
||||
|
||||
BinModuloExpr(String op, Expr left, Expr right)
|
||||
{
|
||||
super(op, left, right);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Number eval(ObjectBinding bindings)
|
||||
protected final long evalLong(long left, long right)
|
||||
{
|
||||
Number leftVal = left.eval(bindings);
|
||||
Number rightVal = right.eval(bindings);
|
||||
if (isNull(leftVal, rightVal)) {
|
||||
return null;
|
||||
} else if (isLong(leftVal, rightVal)) {
|
||||
return leftVal.longValue() % rightVal.longValue();
|
||||
} else {
|
||||
return leftVal.doubleValue() % rightVal.doubleValue();
|
||||
}
|
||||
return left % right;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected final double evalDouble(double left, double right)
|
||||
{
|
||||
return left % right;
|
||||
}
|
||||
}
|
||||
|
||||
class BinPlusExpr extends BinaryOpExprBase
|
||||
class BinPlusExpr extends BinaryEvalOpExprBase
|
||||
{
|
||||
|
||||
BinPlusExpr(String op, Expr left, Expr right)
|
||||
{
|
||||
super(op, left, right);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Number eval(ObjectBinding bindings)
|
||||
protected ExprEval evalString(String left, String right)
|
||||
{
|
||||
Number leftVal = left.eval(bindings);
|
||||
Number rightVal = right.eval(bindings);
|
||||
if (isNull(leftVal, rightVal)) {
|
||||
return null;
|
||||
} else if (isLong(leftVal, rightVal)) {
|
||||
return leftVal.longValue() + rightVal.longValue();
|
||||
} else {
|
||||
return leftVal.doubleValue() + rightVal.doubleValue();
|
||||
}
|
||||
return ExprEval.of(left + right);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected final long evalLong(long left, long right)
|
||||
{
|
||||
return left + right;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected final double evalDouble(double left, double right)
|
||||
{
|
||||
return left + right;
|
||||
}
|
||||
}
|
||||
|
||||
class BinLtExpr extends BinaryOpExprBase
|
||||
class BinLtExpr extends BinaryEvalOpExprBase
|
||||
{
|
||||
|
||||
BinLtExpr(String op, Expr left, Expr right)
|
||||
{
|
||||
super(op, left, right);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Number eval(ObjectBinding bindings)
|
||||
protected ExprEval evalString(String left, String right)
|
||||
{
|
||||
Number leftVal = left.eval(bindings);
|
||||
Number rightVal = right.eval(bindings);
|
||||
if (isNull(leftVal, rightVal)) {
|
||||
return null;
|
||||
} else if (isLong(leftVal, rightVal)) {
|
||||
return leftVal.longValue() < rightVal.longValue() ? 1 : 0;
|
||||
} else {
|
||||
return leftVal.doubleValue() < rightVal.doubleValue() ? 1.0d : 0.0d;
|
||||
}
|
||||
return ExprEval.of(left.compareTo(right) < 0 ? 1L : 0L);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected final long evalLong(long left, long right)
|
||||
{
|
||||
return left < right ? 1L : 0L;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected final double evalDouble(double left, double right)
|
||||
{
|
||||
return left < right ? 1.0d : 0.0d;
|
||||
}
|
||||
}
|
||||
|
||||
class BinLeqExpr extends BinaryOpExprBase
|
||||
class BinLeqExpr extends BinaryEvalOpExprBase
|
||||
{
|
||||
|
||||
BinLeqExpr(String op, Expr left, Expr right)
|
||||
{
|
||||
super(op, left, right);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Number eval(ObjectBinding bindings)
|
||||
protected ExprEval evalString(String left, String right)
|
||||
{
|
||||
Number leftVal = left.eval(bindings);
|
||||
Number rightVal = right.eval(bindings);
|
||||
if (isNull(leftVal, rightVal)) {
|
||||
return null;
|
||||
} else if (isLong(leftVal, rightVal)) {
|
||||
return leftVal.longValue() <= rightVal.longValue() ? 1 : 0;
|
||||
} else {
|
||||
return leftVal.doubleValue() <= rightVal.doubleValue() ? 1.0d : 0.0d;
|
||||
}
|
||||
return ExprEval.of(left.compareTo(right) <= 0 ? 1L : 0L);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected final long evalLong(long left, long right)
|
||||
{
|
||||
return left <= right ? 1L : 0L;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected final double evalDouble(double left, double right)
|
||||
{
|
||||
return left <= right ? 1.0d : 0.0d;
|
||||
}
|
||||
}
|
||||
|
||||
class BinGtExpr extends BinaryOpExprBase
|
||||
class BinGtExpr extends BinaryEvalOpExprBase
|
||||
{
|
||||
|
||||
BinGtExpr(String op, Expr left, Expr right)
|
||||
{
|
||||
super(op, left, right);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Number eval(ObjectBinding bindings)
|
||||
protected ExprEval evalString(String left, String right)
|
||||
{
|
||||
Number leftVal = left.eval(bindings);
|
||||
Number rightVal = right.eval(bindings);
|
||||
if (isNull(leftVal, rightVal)) {
|
||||
return null;
|
||||
} else if (isLong(leftVal, rightVal)) {
|
||||
return leftVal.longValue() > rightVal.longValue() ? 1 : 0;
|
||||
} else {
|
||||
return leftVal.doubleValue() > rightVal.doubleValue() ? 1.0d : 0.0d;
|
||||
}
|
||||
return ExprEval.of(left.compareTo(right) > 0 ? 1L : 0L);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected final long evalLong(long left, long right)
|
||||
{
|
||||
return left > right ? 1L : 0L;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected final double evalDouble(double left, double right)
|
||||
{
|
||||
return left > right ? 1.0d : 0.0d;
|
||||
}
|
||||
}
|
||||
|
||||
class BinGeqExpr extends BinaryOpExprBase
|
||||
class BinGeqExpr extends BinaryEvalOpExprBase
|
||||
{
|
||||
|
||||
BinGeqExpr(String op, Expr left, Expr right)
|
||||
{
|
||||
super(op, left, right);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Number eval(ObjectBinding bindings)
|
||||
protected ExprEval evalString(String left, String right)
|
||||
{
|
||||
Number leftVal = left.eval(bindings);
|
||||
Number rightVal = right.eval(bindings);
|
||||
if (isNull(leftVal, rightVal)) {
|
||||
return null;
|
||||
} else if (isLong(leftVal, rightVal)) {
|
||||
return leftVal.longValue() >= rightVal.longValue() ? 1 : 0;
|
||||
} else {
|
||||
return leftVal.doubleValue() >= rightVal.doubleValue() ? 1.0d : 0.0d;
|
||||
}
|
||||
return ExprEval.of(left.compareTo(right) >= 0 ? 1L : 0L);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected final long evalLong(long left, long right)
|
||||
{
|
||||
return left >= right ? 1L : 0L;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected final double evalDouble(double left, double right)
|
||||
{
|
||||
return left >= right ? 1.0d : 0.0d;
|
||||
}
|
||||
}
|
||||
|
||||
class BinEqExpr extends BinaryOpExprBase
|
||||
class BinEqExpr extends BinaryEvalOpExprBase
|
||||
{
|
||||
|
||||
BinEqExpr(String op, Expr left, Expr right)
|
||||
{
|
||||
super(op, left, right);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Number eval(ObjectBinding bindings)
|
||||
protected ExprEval evalString(String left, String right)
|
||||
{
|
||||
Number leftVal = left.eval(bindings);
|
||||
Number rightVal = right.eval(bindings);
|
||||
if (isNull(leftVal, rightVal)) {
|
||||
return null;
|
||||
} else if (isLong(leftVal, rightVal)) {
|
||||
return leftVal.longValue() == rightVal.longValue() ? 1 : 0;
|
||||
} else {
|
||||
return leftVal.doubleValue() == rightVal.doubleValue() ? 1.0d : 0.0d;
|
||||
}
|
||||
return ExprEval.of(left.equals(right) ? 1L : 0L);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected final long evalLong(long left, long right)
|
||||
{
|
||||
return left == right ? 1L : 0L;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected final double evalDouble(double left, double right)
|
||||
{
|
||||
return left == right ? 1.0d : 0.0d;
|
||||
}
|
||||
}
|
||||
|
||||
class BinNeqExpr extends BinaryOpExprBase
|
||||
class BinNeqExpr extends BinaryEvalOpExprBase
|
||||
{
|
||||
|
||||
BinNeqExpr(String op, Expr left, Expr right)
|
||||
{
|
||||
super(op, left, right);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Number eval(ObjectBinding bindings)
|
||||
protected ExprEval evalString(String left, String right)
|
||||
{
|
||||
Number leftVal = left.eval(bindings);
|
||||
Number rightVal = right.eval(bindings);
|
||||
if (isNull(leftVal, rightVal)) {
|
||||
return null;
|
||||
} else if (isLong(leftVal, rightVal)) {
|
||||
return leftVal.longValue() != rightVal.longValue() ? 1 : 0;
|
||||
} else {
|
||||
return leftVal.doubleValue() != rightVal.doubleValue() ? 1.0d : 0.0d;
|
||||
}
|
||||
return ExprEval.of(!Objects.equals(left, right) ? 1L : 0L);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected final long evalLong(long left, long right)
|
||||
{
|
||||
return left != right ? 1L : 0L;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected final double evalDouble(double left, double right)
|
||||
{
|
||||
return left != right ? 1.0d : 0.0d;
|
||||
}
|
||||
}
|
||||
|
||||
class BinAndExpr extends BinaryOpExprBase
|
||||
{
|
||||
|
||||
BinAndExpr(String op, Expr left, Expr right)
|
||||
{
|
||||
super(op, left, right);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Number eval(ObjectBinding bindings)
|
||||
public ExprEval eval(ObjectBinding bindings)
|
||||
{
|
||||
Number leftVal = left.eval(bindings);
|
||||
Number rightVal = right.eval(bindings);
|
||||
if (isNull(leftVal, rightVal)) {
|
||||
return null;
|
||||
} else if (isLong(leftVal, rightVal)) {
|
||||
long lval = leftVal.longValue();
|
||||
if (lval > 0) {
|
||||
long rval = rightVal.longValue();
|
||||
return rval > 0 ? 1 : 0;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
} else {
|
||||
double lval = leftVal.doubleValue();
|
||||
if (lval > 0) {
|
||||
double rval = rightVal.doubleValue();
|
||||
return rval > 0 ? 1.0d : 0.0d;
|
||||
} else {
|
||||
return 0.0d;
|
||||
}
|
||||
}
|
||||
ExprEval leftVal = left.eval(bindings);
|
||||
return leftVal.asBoolean() ? right.eval(bindings) : leftVal;
|
||||
}
|
||||
}
|
||||
|
||||
class BinOrExpr extends BinaryOpExprBase
|
||||
{
|
||||
|
||||
BinOrExpr(String op, Expr left, Expr right)
|
||||
{
|
||||
super(op, left, right);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Number eval(ObjectBinding bindings)
|
||||
public ExprEval eval(ObjectBinding bindings)
|
||||
{
|
||||
Number leftVal = left.eval(bindings);
|
||||
Number rightVal = right.eval(bindings);
|
||||
if (isNull(leftVal, rightVal)) {
|
||||
return null;
|
||||
} else if (isLong(leftVal, rightVal)) {
|
||||
long lval = leftVal.longValue();
|
||||
if (lval > 0) {
|
||||
return 1;
|
||||
} else {
|
||||
long rval = rightVal.longValue();
|
||||
return rval > 0 ? 1 : 0;
|
||||
}
|
||||
} else {
|
||||
double lval = leftVal.doubleValue();
|
||||
if (lval > 0) {
|
||||
return 1.0d;
|
||||
} else {
|
||||
double rval = rightVal.doubleValue();
|
||||
return rval > 0 ? 1.0d : 0.0d;
|
||||
}
|
||||
}
|
||||
ExprEval leftVal = left.eval(bindings);
|
||||
return leftVal.asBoolean() ? leftVal : right.eval(bindings);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,262 @@
|
|||
/*
|
||||
* Licensed to Metamarkets Group Inc. (Metamarkets) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. Metamarkets licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package io.druid.math.expr;
|
||||
|
||||
import com.google.common.base.Strings;
|
||||
import io.druid.java.util.common.IAE;
|
||||
|
||||
/**
|
||||
*/
|
||||
public abstract class ExprEval<T>
|
||||
{
|
||||
public static ExprEval of(long longValue)
|
||||
{
|
||||
return new LongExprEval(longValue);
|
||||
}
|
||||
|
||||
public static ExprEval of(double longValue)
|
||||
{
|
||||
return new DoubleExprEval(longValue);
|
||||
}
|
||||
|
||||
public static ExprEval of(String stringValue)
|
||||
{
|
||||
return new StringExprEval(stringValue);
|
||||
}
|
||||
|
||||
public static ExprEval of(boolean value, ExprType type)
|
||||
{
|
||||
switch (type) {
|
||||
case DOUBLE:
|
||||
return ExprEval.of(value ? 1D : 0D);
|
||||
case LONG:
|
||||
return ExprEval.of(value ? 1L : 0L);
|
||||
case STRING:
|
||||
return ExprEval.of(String.valueOf(value));
|
||||
default:
|
||||
throw new IllegalArgumentException("invalid type " + type);
|
||||
}
|
||||
}
|
||||
|
||||
public static ExprEval bestEffortOf(Object val)
|
||||
{
|
||||
if (val instanceof ExprEval) {
|
||||
return (ExprEval) val;
|
||||
}
|
||||
if (val instanceof Number) {
|
||||
if (val instanceof Float || val instanceof Double) {
|
||||
return new DoubleExprEval((Number)val);
|
||||
}
|
||||
return new LongExprEval((Number)val);
|
||||
}
|
||||
return new StringExprEval(val == null ? null : String.valueOf(val));
|
||||
}
|
||||
|
||||
final T value;
|
||||
|
||||
private ExprEval(T value)
|
||||
{
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public abstract ExprType type();
|
||||
|
||||
public Object value()
|
||||
{
|
||||
return value;
|
||||
}
|
||||
|
||||
public boolean isNull()
|
||||
{
|
||||
return value == null;
|
||||
}
|
||||
|
||||
public Number numericValue()
|
||||
{
|
||||
return (Number) value;
|
||||
}
|
||||
|
||||
public abstract int asInt();
|
||||
|
||||
public abstract long asLong();
|
||||
|
||||
public abstract double asDouble();
|
||||
|
||||
public String asString()
|
||||
{
|
||||
return value == null ? null : String.valueOf(value);
|
||||
}
|
||||
|
||||
public abstract boolean asBoolean();
|
||||
|
||||
public abstract ExprEval castTo(ExprType castTo);
|
||||
|
||||
private static abstract class NumericExprEval extends ExprEval<Number> {
|
||||
|
||||
private NumericExprEval(Number value)
|
||||
{
|
||||
super(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public final int asInt()
|
||||
{
|
||||
return value.intValue();
|
||||
}
|
||||
|
||||
@Override
|
||||
public final long asLong()
|
||||
{
|
||||
return value.longValue();
|
||||
}
|
||||
|
||||
@Override
|
||||
public final double asDouble()
|
||||
{
|
||||
return value.doubleValue();
|
||||
}
|
||||
}
|
||||
|
||||
private static class DoubleExprEval extends NumericExprEval
|
||||
{
|
||||
private DoubleExprEval(Number value)
|
||||
{
|
||||
super(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public final ExprType type()
|
||||
{
|
||||
return ExprType.DOUBLE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final boolean asBoolean()
|
||||
{
|
||||
return asDouble() > 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final ExprEval castTo(ExprType castTo)
|
||||
{
|
||||
switch (castTo) {
|
||||
case DOUBLE:
|
||||
return this;
|
||||
case LONG:
|
||||
return ExprEval.of(asLong());
|
||||
case STRING:
|
||||
return ExprEval.of(asString());
|
||||
}
|
||||
throw new IAE("invalid type " + castTo);
|
||||
}
|
||||
}
|
||||
|
||||
private static class LongExprEval extends NumericExprEval
|
||||
{
|
||||
private LongExprEval(Number value)
|
||||
{
|
||||
super(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public final ExprType type()
|
||||
{
|
||||
return ExprType.LONG;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final boolean asBoolean()
|
||||
{
|
||||
return asLong() > 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final ExprEval castTo(ExprType castTo)
|
||||
{
|
||||
switch (castTo) {
|
||||
case DOUBLE:
|
||||
return ExprEval.of(asDouble());
|
||||
case LONG:
|
||||
return this;
|
||||
case STRING:
|
||||
return ExprEval.of(asString());
|
||||
}
|
||||
throw new IAE("invalid type " + castTo);
|
||||
}
|
||||
}
|
||||
|
||||
private static class StringExprEval extends ExprEval<String>
|
||||
{
|
||||
private StringExprEval(String value)
|
||||
{
|
||||
super(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public final ExprType type()
|
||||
{
|
||||
return ExprType.STRING;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final boolean isNull()
|
||||
{
|
||||
return Strings.isNullOrEmpty(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public final int asInt()
|
||||
{
|
||||
return Integer.parseInt(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public final long asLong()
|
||||
{
|
||||
return Long.parseLong(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public final double asDouble()
|
||||
{
|
||||
return Double.parseDouble(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public final boolean asBoolean()
|
||||
{
|
||||
return Boolean.valueOf(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public final ExprEval castTo(ExprType castTo)
|
||||
{
|
||||
switch (castTo) {
|
||||
case DOUBLE:
|
||||
return ExprEval.of(asDouble());
|
||||
case LONG:
|
||||
return ExprEval.of(asLong());
|
||||
case STRING:
|
||||
return this;
|
||||
}
|
||||
throw new IAE("invalid type " + castTo);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -23,6 +23,7 @@ import io.druid.math.expr.antlr.ExprBaseListener;
|
|||
import io.druid.math.expr.antlr.ExprParser;
|
||||
import org.antlr.v4.runtime.tree.ParseTree;
|
||||
import org.antlr.v4.runtime.tree.TerminalNode;
|
||||
import org.apache.commons.lang.StringEscapeUtils;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
|
@ -148,6 +149,15 @@ public class ExprListenerImpl extends ExprBaseListener
|
|||
nodes.put(ctx, nodes.get(ctx.getChild(1)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void exitString(ExprParser.StringContext ctx)
|
||||
{
|
||||
String text = ctx.getText();
|
||||
String unquoted = text.substring(1, text.length() - 1);
|
||||
String unescaped = unquoted.indexOf('\\') >= 0 ? StringEscapeUtils.unescapeJava(unquoted) : unquoted;
|
||||
nodes.put(ctx, new StringExpr(unescaped));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void exitLogicalOpExpr(ExprParser.LogicalOpExprContext ctx)
|
||||
{
|
||||
|
@ -289,9 +299,13 @@ public class ExprListenerImpl extends ExprBaseListener
|
|||
@Override
|
||||
public void exitIdentifierExpr(ExprParser.IdentifierExprContext ctx)
|
||||
{
|
||||
String text = ctx.getText();
|
||||
if (text.charAt(0) == '"' && text.charAt(text.length() - 1) == '"') {
|
||||
text = text.substring(1, text.length() - 1);
|
||||
}
|
||||
nodes.put(
|
||||
ctx,
|
||||
new IdentifierExpr(ctx.getText())
|
||||
new IdentifierExpr(text)
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,27 @@
|
|||
/*
|
||||
* Licensed to Metamarkets Group Inc. (Metamarkets) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. Metamarkets licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package io.druid.math.expr;
|
||||
|
||||
/**
|
||||
*/
|
||||
public enum ExprType
|
||||
{
|
||||
DOUBLE, LONG, STRING
|
||||
}
|
|
@ -19,6 +19,12 @@
|
|||
|
||||
package io.druid.math.expr;
|
||||
|
||||
import io.druid.java.util.common.IAE;
|
||||
import org.joda.time.DateTime;
|
||||
import org.joda.time.format.DateTimeFormat;
|
||||
import org.joda.time.format.DateTimeFormatter;
|
||||
import org.joda.time.format.ISODateTimeFormat;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
|
@ -27,40 +33,90 @@ interface Function
|
|||
{
|
||||
String name();
|
||||
|
||||
Number apply(List<Expr> args, Expr.ObjectBinding bindings);
|
||||
ExprEval apply(List<Expr> args, Expr.ObjectBinding bindings);
|
||||
|
||||
abstract class SingleParam implements Function
|
||||
{
|
||||
@Override
|
||||
public Number apply(List<Expr> args, Expr.ObjectBinding bindings)
|
||||
public ExprEval apply(List<Expr> args, Expr.ObjectBinding bindings)
|
||||
{
|
||||
if (args.size() != 1) {
|
||||
throw new RuntimeException("function '" + name() + "' needs 1 argument");
|
||||
throw new IAE("function '%s' needs 1 argument", name());
|
||||
}
|
||||
Expr expr = args.get(0);
|
||||
return eval(expr.eval(bindings));
|
||||
}
|
||||
|
||||
protected abstract Number eval(Number x);
|
||||
protected abstract ExprEval eval(ExprEval param);
|
||||
}
|
||||
|
||||
abstract class DoubleParam implements Function
|
||||
{
|
||||
@Override
|
||||
public Number apply(List<Expr> args, Expr.ObjectBinding bindings)
|
||||
public ExprEval apply(List<Expr> args, Expr.ObjectBinding bindings)
|
||||
{
|
||||
if (args.size() != 2) {
|
||||
throw new RuntimeException("function '" + name() + "' needs 1 argument");
|
||||
throw new IAE("function '%s' needs 2 arguments", name());
|
||||
}
|
||||
Expr expr1 = args.get(0);
|
||||
Expr expr2 = args.get(1);
|
||||
return eval(expr1.eval(bindings), expr2.eval(bindings));
|
||||
}
|
||||
|
||||
protected abstract Number eval(Number x, Number y);
|
||||
protected abstract ExprEval eval(ExprEval x, ExprEval y);
|
||||
}
|
||||
|
||||
class Abs extends SingleParam
|
||||
abstract class SingleParamMath extends SingleParam
|
||||
{
|
||||
@Override
|
||||
protected final ExprEval eval(ExprEval param)
|
||||
{
|
||||
if (param.type() == ExprType.LONG) {
|
||||
return eval(param.asLong());
|
||||
} else if (param.type() == ExprType.DOUBLE) {
|
||||
return eval(param.asDouble());
|
||||
}
|
||||
return ExprEval.of(null);
|
||||
}
|
||||
|
||||
protected ExprEval eval(long param)
|
||||
{
|
||||
return eval((double) param);
|
||||
}
|
||||
|
||||
protected ExprEval eval(double param)
|
||||
{
|
||||
return eval((long) param);
|
||||
}
|
||||
}
|
||||
|
||||
abstract class DoubleParamMath extends DoubleParam
|
||||
{
|
||||
@Override
|
||||
protected final ExprEval eval(ExprEval x, ExprEval y)
|
||||
{
|
||||
if (x.type() == ExprType.STRING || y.type() == ExprType.STRING) {
|
||||
return ExprEval.of(null);
|
||||
}
|
||||
if (x.type() == ExprType.LONG && y.type() == ExprType.LONG) {
|
||||
return eval(x.asLong(), y.asLong());
|
||||
} else {
|
||||
return eval(x.asDouble(), y.asDouble());
|
||||
}
|
||||
}
|
||||
|
||||
protected ExprEval eval(long x, long y)
|
||||
{
|
||||
return eval((double) x, (double) y);
|
||||
}
|
||||
|
||||
protected ExprEval eval(double x, double y)
|
||||
{
|
||||
return eval((long) x, (long) y);
|
||||
}
|
||||
}
|
||||
|
||||
class Abs extends SingleParamMath
|
||||
{
|
||||
@Override
|
||||
public String name()
|
||||
|
@ -69,13 +125,19 @@ interface Function
|
|||
}
|
||||
|
||||
@Override
|
||||
protected Number eval(Number x)
|
||||
protected ExprEval eval(long param)
|
||||
{
|
||||
return x instanceof Long ? Math.abs(x.longValue()) : Math.abs(x.doubleValue());
|
||||
return ExprEval.of(Math.abs(param));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ExprEval eval(double param)
|
||||
{
|
||||
return ExprEval.of(Math.abs(param));
|
||||
}
|
||||
}
|
||||
|
||||
class Acos extends SingleParam
|
||||
class Acos extends SingleParamMath
|
||||
{
|
||||
@Override
|
||||
public String name()
|
||||
|
@ -84,13 +146,13 @@ interface Function
|
|||
}
|
||||
|
||||
@Override
|
||||
protected Number eval(Number x)
|
||||
protected ExprEval eval(double param)
|
||||
{
|
||||
return Math.acos(x.doubleValue());
|
||||
return ExprEval.of(Math.acos(param));
|
||||
}
|
||||
}
|
||||
|
||||
class Asin extends SingleParam
|
||||
class Asin extends SingleParamMath
|
||||
{
|
||||
@Override
|
||||
public String name()
|
||||
|
@ -99,13 +161,13 @@ interface Function
|
|||
}
|
||||
|
||||
@Override
|
||||
protected Number eval(Number x)
|
||||
protected ExprEval eval(double param)
|
||||
{
|
||||
return Math.asin(x.doubleValue());
|
||||
return ExprEval.of(Math.asin(param));
|
||||
}
|
||||
}
|
||||
|
||||
class Atan extends SingleParam
|
||||
class Atan extends SingleParamMath
|
||||
{
|
||||
@Override
|
||||
public String name()
|
||||
|
@ -114,13 +176,13 @@ interface Function
|
|||
}
|
||||
|
||||
@Override
|
||||
protected Number eval(Number x)
|
||||
protected ExprEval eval(double param)
|
||||
{
|
||||
return Math.atan(x.doubleValue());
|
||||
return ExprEval.of(Math.atan(param));
|
||||
}
|
||||
}
|
||||
|
||||
class Cbrt extends SingleParam
|
||||
class Cbrt extends SingleParamMath
|
||||
{
|
||||
@Override
|
||||
public String name()
|
||||
|
@ -129,13 +191,13 @@ interface Function
|
|||
}
|
||||
|
||||
@Override
|
||||
protected Number eval(Number x)
|
||||
protected ExprEval eval(double param)
|
||||
{
|
||||
return Math.cbrt(x.doubleValue());
|
||||
return ExprEval.of(Math.cbrt(param));
|
||||
}
|
||||
}
|
||||
|
||||
class Ceil extends SingleParam
|
||||
class Ceil extends SingleParamMath
|
||||
{
|
||||
@Override
|
||||
public String name()
|
||||
|
@ -144,13 +206,13 @@ interface Function
|
|||
}
|
||||
|
||||
@Override
|
||||
protected Number eval(Number x)
|
||||
protected ExprEval eval(double param)
|
||||
{
|
||||
return Math.ceil(x.doubleValue());
|
||||
return ExprEval.of(Math.ceil(param));
|
||||
}
|
||||
}
|
||||
|
||||
class Cos extends SingleParam
|
||||
class Cos extends SingleParamMath
|
||||
{
|
||||
@Override
|
||||
public String name()
|
||||
|
@ -159,13 +221,13 @@ interface Function
|
|||
}
|
||||
|
||||
@Override
|
||||
protected Number eval(Number x)
|
||||
protected ExprEval eval(double param)
|
||||
{
|
||||
return Math.cos(x.doubleValue());
|
||||
return ExprEval.of(Math.cos(param));
|
||||
}
|
||||
}
|
||||
|
||||
class Cosh extends SingleParam
|
||||
class Cosh extends SingleParamMath
|
||||
{
|
||||
@Override
|
||||
public String name()
|
||||
|
@ -174,13 +236,13 @@ interface Function
|
|||
}
|
||||
|
||||
@Override
|
||||
protected Number eval(Number x)
|
||||
protected ExprEval eval(double param)
|
||||
{
|
||||
return Math.cosh(x.doubleValue());
|
||||
return ExprEval.of(Math.cosh(param));
|
||||
}
|
||||
}
|
||||
|
||||
class Exp extends SingleParam
|
||||
class Exp extends SingleParamMath
|
||||
{
|
||||
@Override
|
||||
public String name()
|
||||
|
@ -189,13 +251,13 @@ interface Function
|
|||
}
|
||||
|
||||
@Override
|
||||
protected Number eval(Number x)
|
||||
protected ExprEval eval(double param)
|
||||
{
|
||||
return Math.exp(x.doubleValue());
|
||||
return ExprEval.of(Math.exp(param));
|
||||
}
|
||||
}
|
||||
|
||||
class Expm1 extends SingleParam
|
||||
class Expm1 extends SingleParamMath
|
||||
{
|
||||
@Override
|
||||
public String name()
|
||||
|
@ -204,13 +266,13 @@ interface Function
|
|||
}
|
||||
|
||||
@Override
|
||||
protected Number eval(Number x)
|
||||
protected ExprEval eval(double param)
|
||||
{
|
||||
return Math.expm1(x.doubleValue());
|
||||
return ExprEval.of(Math.expm1(param));
|
||||
}
|
||||
}
|
||||
|
||||
class Floor extends SingleParam
|
||||
class Floor extends SingleParamMath
|
||||
{
|
||||
@Override
|
||||
public String name()
|
||||
|
@ -219,13 +281,13 @@ interface Function
|
|||
}
|
||||
|
||||
@Override
|
||||
protected Number eval(Number x)
|
||||
protected ExprEval eval(double param)
|
||||
{
|
||||
return Math.floor(x.doubleValue());
|
||||
return ExprEval.of(Math.floor(param));
|
||||
}
|
||||
}
|
||||
|
||||
class GetExponent extends SingleParam
|
||||
class GetExponent extends SingleParamMath
|
||||
{
|
||||
@Override
|
||||
public String name()
|
||||
|
@ -234,13 +296,13 @@ interface Function
|
|||
}
|
||||
|
||||
@Override
|
||||
protected Number eval(Number x)
|
||||
protected ExprEval eval(double param)
|
||||
{
|
||||
return Math.getExponent(x.doubleValue());
|
||||
return ExprEval.of(Math.getExponent(param));
|
||||
}
|
||||
}
|
||||
|
||||
class Log extends SingleParam
|
||||
class Log extends SingleParamMath
|
||||
{
|
||||
@Override
|
||||
public String name()
|
||||
|
@ -249,13 +311,13 @@ interface Function
|
|||
}
|
||||
|
||||
@Override
|
||||
protected Number eval(Number x)
|
||||
protected ExprEval eval(double param)
|
||||
{
|
||||
return Math.log(x.doubleValue());
|
||||
return ExprEval.of(Math.log(param));
|
||||
}
|
||||
}
|
||||
|
||||
class Log10 extends SingleParam
|
||||
class Log10 extends SingleParamMath
|
||||
{
|
||||
@Override
|
||||
public String name()
|
||||
|
@ -264,13 +326,13 @@ interface Function
|
|||
}
|
||||
|
||||
@Override
|
||||
protected Number eval(Number x)
|
||||
protected ExprEval eval(double param)
|
||||
{
|
||||
return Math.log10(x.doubleValue());
|
||||
return ExprEval.of(Math.log10(param));
|
||||
}
|
||||
}
|
||||
|
||||
class Log1p extends SingleParam
|
||||
class Log1p extends SingleParamMath
|
||||
{
|
||||
@Override
|
||||
public String name()
|
||||
|
@ -279,13 +341,13 @@ interface Function
|
|||
}
|
||||
|
||||
@Override
|
||||
protected Number eval(Number x)
|
||||
protected ExprEval eval(double param)
|
||||
{
|
||||
return Math.log1p(x.doubleValue());
|
||||
return ExprEval.of(Math.log1p(param));
|
||||
}
|
||||
}
|
||||
|
||||
class NextUp extends SingleParam
|
||||
class NextUp extends SingleParamMath
|
||||
{
|
||||
@Override
|
||||
public String name()
|
||||
|
@ -294,13 +356,13 @@ interface Function
|
|||
}
|
||||
|
||||
@Override
|
||||
protected Number eval(Number x)
|
||||
protected ExprEval eval(double param)
|
||||
{
|
||||
return Math.nextUp(x.doubleValue());
|
||||
return ExprEval.of(Math.nextUp(param));
|
||||
}
|
||||
}
|
||||
|
||||
class Rint extends SingleParam
|
||||
class Rint extends SingleParamMath
|
||||
{
|
||||
@Override
|
||||
public String name()
|
||||
|
@ -309,13 +371,13 @@ interface Function
|
|||
}
|
||||
|
||||
@Override
|
||||
protected Number eval(Number x)
|
||||
protected ExprEval eval(double param)
|
||||
{
|
||||
return Math.rint(x.doubleValue());
|
||||
return ExprEval.of(Math.rint(param));
|
||||
}
|
||||
}
|
||||
|
||||
class Round extends SingleParam
|
||||
class Round extends SingleParamMath
|
||||
{
|
||||
@Override
|
||||
public String name()
|
||||
|
@ -324,13 +386,13 @@ interface Function
|
|||
}
|
||||
|
||||
@Override
|
||||
protected Number eval(Number x)
|
||||
protected ExprEval eval(double param)
|
||||
{
|
||||
return Math.round(x.doubleValue());
|
||||
return ExprEval.of(Math.round(param));
|
||||
}
|
||||
}
|
||||
|
||||
class Signum extends SingleParam
|
||||
class Signum extends SingleParamMath
|
||||
{
|
||||
@Override
|
||||
public String name()
|
||||
|
@ -339,13 +401,13 @@ interface Function
|
|||
}
|
||||
|
||||
@Override
|
||||
protected Number eval(Number x)
|
||||
protected ExprEval eval(double param)
|
||||
{
|
||||
return Math.signum(x.doubleValue());
|
||||
return ExprEval.of(Math.signum(param));
|
||||
}
|
||||
}
|
||||
|
||||
class Sin extends SingleParam
|
||||
class Sin extends SingleParamMath
|
||||
{
|
||||
@Override
|
||||
public String name()
|
||||
|
@ -354,13 +416,13 @@ interface Function
|
|||
}
|
||||
|
||||
@Override
|
||||
protected Number eval(Number x)
|
||||
protected ExprEval eval(double param)
|
||||
{
|
||||
return Math.sin(x.doubleValue());
|
||||
return ExprEval.of(Math.sin(param));
|
||||
}
|
||||
}
|
||||
|
||||
class Sinh extends SingleParam
|
||||
class Sinh extends SingleParamMath
|
||||
{
|
||||
@Override
|
||||
public String name()
|
||||
|
@ -369,13 +431,13 @@ interface Function
|
|||
}
|
||||
|
||||
@Override
|
||||
protected Number eval(Number x)
|
||||
protected ExprEval eval(double param)
|
||||
{
|
||||
return Math.sinh(x.doubleValue());
|
||||
return ExprEval.of(Math.sinh(param));
|
||||
}
|
||||
}
|
||||
|
||||
class Sqrt extends SingleParam
|
||||
class Sqrt extends SingleParamMath
|
||||
{
|
||||
@Override
|
||||
public String name()
|
||||
|
@ -384,13 +446,13 @@ interface Function
|
|||
}
|
||||
|
||||
@Override
|
||||
protected Number eval(Number x)
|
||||
protected ExprEval eval(double param)
|
||||
{
|
||||
return Math.sqrt(x.doubleValue());
|
||||
return ExprEval.of(Math.sqrt(param));
|
||||
}
|
||||
}
|
||||
|
||||
class Tan extends SingleParam
|
||||
class Tan extends SingleParamMath
|
||||
{
|
||||
@Override
|
||||
public String name()
|
||||
|
@ -399,13 +461,13 @@ interface Function
|
|||
}
|
||||
|
||||
@Override
|
||||
protected Number eval(Number x)
|
||||
protected ExprEval eval(double param)
|
||||
{
|
||||
return Math.tan(x.doubleValue());
|
||||
return ExprEval.of(Math.tan(param));
|
||||
}
|
||||
}
|
||||
|
||||
class Tanh extends SingleParam
|
||||
class Tanh extends SingleParamMath
|
||||
{
|
||||
@Override
|
||||
public String name()
|
||||
|
@ -414,13 +476,13 @@ interface Function
|
|||
}
|
||||
|
||||
@Override
|
||||
protected Number eval(Number x)
|
||||
protected ExprEval eval(double param)
|
||||
{
|
||||
return Math.tanh(x.doubleValue());
|
||||
return ExprEval.of(Math.tanh(param));
|
||||
}
|
||||
}
|
||||
|
||||
class ToDegrees extends SingleParam
|
||||
class ToDegrees extends SingleParamMath
|
||||
{
|
||||
@Override
|
||||
public String name()
|
||||
|
@ -429,13 +491,13 @@ interface Function
|
|||
}
|
||||
|
||||
@Override
|
||||
protected Number eval(Number x)
|
||||
protected ExprEval eval(double param)
|
||||
{
|
||||
return Math.toDegrees(x.doubleValue());
|
||||
return ExprEval.of(Math.toDegrees(param));
|
||||
}
|
||||
}
|
||||
|
||||
class ToRadians extends SingleParam
|
||||
class ToRadians extends SingleParamMath
|
||||
{
|
||||
@Override
|
||||
public String name()
|
||||
|
@ -444,13 +506,13 @@ interface Function
|
|||
}
|
||||
|
||||
@Override
|
||||
protected Number eval(Number x)
|
||||
protected ExprEval eval(double param)
|
||||
{
|
||||
return Math.toRadians(x.doubleValue());
|
||||
return ExprEval.of(Math.toRadians(param));
|
||||
}
|
||||
}
|
||||
|
||||
class Ulp extends SingleParam
|
||||
class Ulp extends SingleParamMath
|
||||
{
|
||||
@Override
|
||||
public String name()
|
||||
|
@ -459,13 +521,13 @@ interface Function
|
|||
}
|
||||
|
||||
@Override
|
||||
protected Number eval(Number x)
|
||||
protected ExprEval eval(double param)
|
||||
{
|
||||
return Math.ulp(x.doubleValue());
|
||||
return ExprEval.of(Math.ulp(param));
|
||||
}
|
||||
}
|
||||
|
||||
class Atan2 extends DoubleParam
|
||||
class Atan2 extends DoubleParamMath
|
||||
{
|
||||
@Override
|
||||
public String name()
|
||||
|
@ -474,13 +536,13 @@ interface Function
|
|||
}
|
||||
|
||||
@Override
|
||||
protected Number eval(Number x, Number y)
|
||||
protected ExprEval eval(double y, double x)
|
||||
{
|
||||
return Math.atan2(x.doubleValue(), y.doubleValue());
|
||||
return ExprEval.of(Math.atan2(y, x));
|
||||
}
|
||||
}
|
||||
|
||||
class CopySign extends DoubleParam
|
||||
class CopySign extends DoubleParamMath
|
||||
{
|
||||
@Override
|
||||
public String name()
|
||||
|
@ -489,13 +551,13 @@ interface Function
|
|||
}
|
||||
|
||||
@Override
|
||||
protected Number eval(Number x, Number y)
|
||||
protected ExprEval eval(double x, double y)
|
||||
{
|
||||
return Math.copySign(x.doubleValue(), y.doubleValue());
|
||||
return ExprEval.of(Math.copySign(x, y));
|
||||
}
|
||||
}
|
||||
|
||||
class Hypot extends DoubleParam
|
||||
class Hypot extends DoubleParamMath
|
||||
{
|
||||
@Override
|
||||
public String name()
|
||||
|
@ -504,13 +566,13 @@ interface Function
|
|||
}
|
||||
|
||||
@Override
|
||||
protected Number eval(Number x, Number y)
|
||||
protected ExprEval eval(double x, double y)
|
||||
{
|
||||
return Math.hypot(x.doubleValue(), y.doubleValue());
|
||||
return ExprEval.of(Math.hypot(x, y));
|
||||
}
|
||||
}
|
||||
|
||||
class Remainder extends DoubleParam
|
||||
class Remainder extends DoubleParamMath
|
||||
{
|
||||
@Override
|
||||
public String name()
|
||||
|
@ -519,13 +581,13 @@ interface Function
|
|||
}
|
||||
|
||||
@Override
|
||||
protected Number eval(Number x, Number y)
|
||||
protected ExprEval eval(double x, double y)
|
||||
{
|
||||
return Math.IEEEremainder(x.doubleValue(), y.doubleValue());
|
||||
return ExprEval.of(Math.IEEEremainder(x, y));
|
||||
}
|
||||
}
|
||||
|
||||
class Max extends DoubleParam
|
||||
class Max extends DoubleParamMath
|
||||
{
|
||||
@Override
|
||||
public String name()
|
||||
|
@ -534,16 +596,19 @@ interface Function
|
|||
}
|
||||
|
||||
@Override
|
||||
protected Number eval(Number x, Number y)
|
||||
protected ExprEval eval(long x, long y)
|
||||
{
|
||||
if (x instanceof Long && y instanceof Long) {
|
||||
return Math.max(x.longValue(), y.longValue());
|
||||
}
|
||||
return Double.compare(x.doubleValue(), y.doubleValue()) >= 0 ? x : y;
|
||||
return ExprEval.of(Math.max(x, y));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ExprEval eval(double x, double y)
|
||||
{
|
||||
return ExprEval.of(Math.max(x, y));
|
||||
}
|
||||
}
|
||||
|
||||
class Min extends DoubleParam
|
||||
class Min extends DoubleParamMath
|
||||
{
|
||||
@Override
|
||||
public String name()
|
||||
|
@ -552,16 +617,19 @@ interface Function
|
|||
}
|
||||
|
||||
@Override
|
||||
protected Number eval(Number x, Number y)
|
||||
protected ExprEval eval(long x, long y)
|
||||
{
|
||||
if (x instanceof Long && y instanceof Long) {
|
||||
return Math.min(x.longValue(), y.longValue());
|
||||
}
|
||||
return Double.compare(x.doubleValue(), y.doubleValue()) <= 0 ? x : y;
|
||||
return ExprEval.of(Math.min(x, y));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ExprEval eval(double x, double y)
|
||||
{
|
||||
return ExprEval.of(Math.min(x, y));
|
||||
}
|
||||
}
|
||||
|
||||
class NextAfter extends DoubleParam
|
||||
class NextAfter extends DoubleParamMath
|
||||
{
|
||||
@Override
|
||||
public String name()
|
||||
|
@ -570,13 +638,13 @@ interface Function
|
|||
}
|
||||
|
||||
@Override
|
||||
protected Number eval(Number x, Number y)
|
||||
protected ExprEval eval(double x, double y)
|
||||
{
|
||||
return Math.nextAfter(x.doubleValue(), y.doubleValue());
|
||||
return ExprEval.of(Math.nextAfter(x, y));
|
||||
}
|
||||
}
|
||||
|
||||
class Pow extends DoubleParam
|
||||
class Pow extends DoubleParamMath
|
||||
{
|
||||
@Override
|
||||
public String name()
|
||||
|
@ -585,9 +653,9 @@ interface Function
|
|||
}
|
||||
|
||||
@Override
|
||||
protected Number eval(Number x, Number y)
|
||||
protected ExprEval eval(double x, double y)
|
||||
{
|
||||
return Math.pow(x.doubleValue(), y.doubleValue());
|
||||
return ExprEval.of(Math.pow(x, y));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -600,9 +668,9 @@ interface Function
|
|||
}
|
||||
|
||||
@Override
|
||||
protected Number eval(Number x, Number y)
|
||||
protected ExprEval eval(ExprEval x, ExprEval y)
|
||||
{
|
||||
return Math.scalb(x.doubleValue(), y.intValue());
|
||||
return ExprEval.of(Math.scalb(x.asDouble(), y.asInt()));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -615,18 +683,113 @@ interface Function
|
|||
}
|
||||
|
||||
@Override
|
||||
public Number apply(List<Expr> args, Expr.ObjectBinding bindings)
|
||||
public ExprEval apply(List<Expr> args, Expr.ObjectBinding bindings)
|
||||
{
|
||||
if (args.size() != 3) {
|
||||
throw new RuntimeException("function 'if' needs 3 argument");
|
||||
throw new IAE("function 'if' needs 3 arguments");
|
||||
}
|
||||
|
||||
Number x = args.get(0).eval(bindings);
|
||||
if (x instanceof Long) {
|
||||
return x.longValue() > 0 ? args.get(1).eval(bindings) : args.get(2).eval(bindings);
|
||||
} else {
|
||||
return x.doubleValue() > 0 ? args.get(1).eval(bindings) : args.get(2).eval(bindings);
|
||||
ExprEval x = args.get(0).eval(bindings);
|
||||
return x.asBoolean() ? args.get(1).eval(bindings) : args.get(2).eval(bindings);
|
||||
}
|
||||
}
|
||||
|
||||
class CastFunc extends DoubleParam
|
||||
{
|
||||
@Override
|
||||
public String name()
|
||||
{
|
||||
return "cast";
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ExprEval eval(ExprEval x, ExprEval y)
|
||||
{
|
||||
ExprType castTo;
|
||||
try {
|
||||
castTo = ExprType.valueOf(y.asString().toUpperCase());
|
||||
}
|
||||
catch (IllegalArgumentException e) {
|
||||
throw new IAE("invalid type '%s'", y.asString());
|
||||
}
|
||||
return x.castTo(castTo);
|
||||
}
|
||||
}
|
||||
|
||||
class TimestampFromEpochFunc implements Function
|
||||
{
|
||||
@Override
|
||||
public String name()
|
||||
{
|
||||
return "timestamp";
|
||||
}
|
||||
|
||||
@Override
|
||||
public ExprEval apply(List<Expr> args, Expr.ObjectBinding bindings)
|
||||
{
|
||||
if (args.size() != 1 && args.size() != 2) {
|
||||
throw new IAE("function '%s' needs 1 or 2 arguments", name());
|
||||
}
|
||||
ExprEval value = args.get(0).eval(bindings);
|
||||
if (value.type() != ExprType.STRING) {
|
||||
throw new IAE("first argument should be string type but got %s type", value.type());
|
||||
}
|
||||
|
||||
DateTimeFormatter formatter = ISODateTimeFormat.dateOptionalTimeParser();
|
||||
if (args.size() > 1) {
|
||||
ExprEval format = args.get(1).eval(bindings);
|
||||
if (format.type() != ExprType.STRING) {
|
||||
throw new IAE("second argument should be string type but got %s type", format.type());
|
||||
}
|
||||
formatter = DateTimeFormat.forPattern(format.asString());
|
||||
}
|
||||
DateTime date;
|
||||
try {
|
||||
date = DateTime.parse(value.asString(), formatter);
|
||||
}
|
||||
catch (IllegalArgumentException e) {
|
||||
throw new IAE(e, "invalid value %s", value.asString());
|
||||
}
|
||||
return toValue(date);
|
||||
}
|
||||
|
||||
protected ExprEval toValue(DateTime date)
|
||||
{
|
||||
return ExprEval.of(date.getMillis());
|
||||
}
|
||||
}
|
||||
|
||||
class UnixTimestampFunc extends TimestampFromEpochFunc
|
||||
{
|
||||
@Override
|
||||
public String name()
|
||||
{
|
||||
return "unix_timestamp";
|
||||
}
|
||||
|
||||
@Override
|
||||
protected final ExprEval toValue(DateTime date)
|
||||
{
|
||||
return ExprEval.of(date.getMillis() / 1000);
|
||||
}
|
||||
}
|
||||
|
||||
class NvlFunc implements Function
|
||||
{
|
||||
@Override
|
||||
public String name()
|
||||
{
|
||||
return "nvl";
|
||||
}
|
||||
|
||||
@Override
|
||||
public ExprEval apply(List<Expr> args, Expr.ObjectBinding bindings)
|
||||
{
|
||||
if (args.size() != 2) {
|
||||
throw new IAE("function 'nvl' needs 2 arguments");
|
||||
}
|
||||
final ExprEval eval = args.get(0).eval(bindings);
|
||||
return eval.isNull() ? args.get(1).eval(bindings) : eval;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -103,7 +103,11 @@ public class Parser
|
|||
@Override
|
||||
public Number get(String name)
|
||||
{
|
||||
return (Number)bindings.get(name);
|
||||
Number number = (Number)bindings.get(name);
|
||||
if (number == null && !bindings.containsKey(name)) {
|
||||
throw new RuntimeException("No binding found for " + name);
|
||||
}
|
||||
return number;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
|
@ -19,114 +19,128 @@
|
|||
|
||||
package io.druid.math.expr;
|
||||
|
||||
import com.google.common.base.Supplier;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
*/
|
||||
public class EvalTest
|
||||
{
|
||||
private Supplier<Number> constantSupplier(final Number number)
|
||||
private long evalLong(String x, Expr.ObjectBinding bindings)
|
||||
{
|
||||
return new Supplier<Number>()
|
||||
{
|
||||
@Override
|
||||
public Number get()
|
||||
{
|
||||
return number;
|
||||
}
|
||||
};
|
||||
ExprEval ret = eval(x, bindings);
|
||||
Assert.assertEquals(ExprType.LONG, ret.type());
|
||||
return ret.asLong();
|
||||
}
|
||||
|
||||
private double evalDouble(String x, Expr.ObjectBinding bindings)
|
||||
{
|
||||
ExprEval ret = eval(x, bindings);
|
||||
Assert.assertEquals(ExprType.DOUBLE, ret.type());
|
||||
return ret.asDouble();
|
||||
}
|
||||
|
||||
private ExprEval eval(String x, Expr.ObjectBinding bindings)
|
||||
{
|
||||
return Parser.parse(x).eval(bindings);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDoubleEval()
|
||||
{
|
||||
Map<String, Supplier<Number>> bindings = new HashMap<>();
|
||||
bindings.put( "x", constantSupplier(2.0d));
|
||||
Expr.ObjectBinding bindings = Parser.withMap(ImmutableMap.of("x", 2.0d));
|
||||
Assert.assertEquals(2.0, evalDouble("x", bindings), 0.0001);
|
||||
Assert.assertEquals(2.0, evalDouble("\"x\"", bindings), 0.0001);
|
||||
Assert.assertEquals(304.0, evalDouble("300 + \"x\" * 2", bindings), 0.0001);
|
||||
|
||||
Assert.assertEquals(2.0, evaluate("x", bindings).doubleValue(), 0.0001);
|
||||
Assert.assertFalse(evalDouble("1.0 && 0.0", bindings) > 0.0);
|
||||
Assert.assertTrue(evalDouble("1.0 && 2.0", bindings) > 0.0);
|
||||
|
||||
Assert.assertFalse(evaluate("1.0 && 0.0", bindings).doubleValue() > 0.0);
|
||||
Assert.assertTrue(evaluate("1.0 && 2.0", bindings).doubleValue() > 0.0);
|
||||
Assert.assertTrue(evalDouble("1.0 || 0.0", bindings) > 0.0);
|
||||
Assert.assertFalse(evalDouble("0.0 || 0.0", bindings) > 0.0);
|
||||
|
||||
Assert.assertTrue(evaluate("1.0 || 0.0", bindings).doubleValue() > 0.0);
|
||||
Assert.assertFalse(evaluate("0.0 || 0.0", bindings).doubleValue() > 0.0);
|
||||
Assert.assertTrue(evalDouble("2.0 > 1.0", bindings) > 0.0);
|
||||
Assert.assertTrue(evalDouble("2.0 >= 2.0", bindings) > 0.0);
|
||||
Assert.assertTrue(evalDouble("1.0 < 2.0", bindings) > 0.0);
|
||||
Assert.assertTrue(evalDouble("2.0 <= 2.0", bindings) > 0.0);
|
||||
Assert.assertTrue(evalDouble("2.0 == 2.0", bindings) > 0.0);
|
||||
Assert.assertTrue(evalDouble("2.0 != 1.0", bindings) > 0.0);
|
||||
|
||||
Assert.assertTrue(evaluate("2.0 > 1.0", bindings).doubleValue() > 0.0);
|
||||
Assert.assertTrue(evaluate("2.0 >= 2.0", bindings).doubleValue() > 0.0);
|
||||
Assert.assertTrue(evaluate("1.0 < 2.0", bindings).doubleValue() > 0.0);
|
||||
Assert.assertTrue(evaluate("2.0 <= 2.0", bindings).doubleValue() > 0.0);
|
||||
Assert.assertTrue(evaluate("2.0 == 2.0", bindings).doubleValue() > 0.0);
|
||||
Assert.assertTrue(evaluate("2.0 != 1.0", bindings).doubleValue() > 0.0);
|
||||
Assert.assertEquals(3.5, evalDouble("2.0 + 1.5", bindings), 0.0001);
|
||||
Assert.assertEquals(0.5, evalDouble("2.0 - 1.5", bindings), 0.0001);
|
||||
Assert.assertEquals(3.0, evalDouble("2.0 * 1.5", bindings), 0.0001);
|
||||
Assert.assertEquals(4.0, evalDouble("2.0 / 0.5", bindings), 0.0001);
|
||||
Assert.assertEquals(0.2, evalDouble("2.0 % 0.3", bindings), 0.0001);
|
||||
Assert.assertEquals(8.0, evalDouble("2.0 ^ 3.0", bindings), 0.0001);
|
||||
Assert.assertEquals(-1.5, evalDouble("-1.5", bindings), 0.0001);
|
||||
|
||||
Assert.assertEquals(3.5, evaluate("2.0 + 1.5", bindings).doubleValue(), 0.0001);
|
||||
Assert.assertEquals(0.5, evaluate("2.0 - 1.5", bindings).doubleValue(), 0.0001);
|
||||
Assert.assertEquals(3.0, evaluate("2.0 * 1.5", bindings).doubleValue(), 0.0001);
|
||||
Assert.assertEquals(4.0, evaluate("2.0 / 0.5", bindings).doubleValue(), 0.0001);
|
||||
Assert.assertEquals(0.2, evaluate("2.0 % 0.3", bindings).doubleValue(), 0.0001);
|
||||
Assert.assertEquals(8.0, evaluate("2.0 ^ 3.0", bindings).doubleValue(), 0.0001);
|
||||
Assert.assertEquals(-1.5, evaluate("-1.5", bindings).doubleValue(), 0.0001);
|
||||
Assert.assertTrue(evalDouble("!-1.0", bindings) > 0.0);
|
||||
Assert.assertTrue(evalDouble("!0.0", bindings) > 0.0);
|
||||
Assert.assertFalse(evalDouble("!2.0", bindings) > 0.0);
|
||||
|
||||
Assert.assertTrue(evaluate("!-1.0", bindings).doubleValue() > 0.0);
|
||||
Assert.assertTrue(evaluate("!0.0", bindings).doubleValue() > 0.0);
|
||||
Assert.assertFalse(evaluate("!2.0", bindings).doubleValue() > 0.0);
|
||||
|
||||
Assert.assertEquals(2.0, evaluate("sqrt(4.0)", bindings).doubleValue(), 0.0001);
|
||||
Assert.assertEquals(2.0, evaluate("if(1.0, 2.0, 3.0)", bindings).doubleValue(), 0.0001);
|
||||
Assert.assertEquals(3.0, evaluate("if(0.0, 2.0, 3.0)", bindings).doubleValue(), 0.0001);
|
||||
}
|
||||
|
||||
private Number evaluate(String in, Map<String, Supplier<Number>> bindings) {
|
||||
return Parser.parse(in).eval(Parser.withSuppliers(bindings));
|
||||
Assert.assertEquals(2.0, evalDouble("sqrt(4.0)", bindings), 0.0001);
|
||||
Assert.assertEquals(2.0, evalDouble("if(1.0, 2.0, 3.0)", bindings), 0.0001);
|
||||
Assert.assertEquals(3.0, evalDouble("if(0.0, 2.0, 3.0)", bindings), 0.0001);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLongEval()
|
||||
{
|
||||
Map<String, Supplier<Number>> bindings = new HashMap<>();
|
||||
bindings.put("x", constantSupplier(9223372036854775807L));
|
||||
Expr.ObjectBinding bindings = Parser.withMap(ImmutableMap.of("x", 9223372036854775807L));
|
||||
|
||||
Assert.assertEquals(9223372036854775807L, evaluate("x", bindings).longValue());
|
||||
Assert.assertEquals(9223372036854775807L, evalLong("x", bindings));
|
||||
Assert.assertEquals(9223372036854775807L, evalLong("\"x\"", bindings));
|
||||
Assert.assertEquals(92233720368547759L, evalLong("\"x\" / 100 + 1", bindings));
|
||||
|
||||
Assert.assertFalse(evaluate("9223372036854775807 && 0", bindings).longValue() > 0);
|
||||
Assert.assertTrue(evaluate("9223372036854775807 && 9223372036854775806", bindings).longValue() > 0);
|
||||
Assert.assertFalse(evalLong("9223372036854775807 && 0", bindings) > 0);
|
||||
Assert.assertTrue(evalLong("9223372036854775807 && 9223372036854775806", bindings) > 0);
|
||||
|
||||
Assert.assertTrue(evaluate("9223372036854775807 || 0", bindings).longValue() > 0);
|
||||
Assert.assertFalse(evaluate("-9223372036854775807 || -9223372036854775807", bindings).longValue() > 0);
|
||||
Assert.assertTrue(evaluate("-9223372036854775807 || 9223372036854775807", bindings).longValue() > 0);
|
||||
Assert.assertFalse(evaluate("0 || 0", bindings).longValue() > 0);
|
||||
Assert.assertTrue(evalLong("9223372036854775807 || 0", bindings) > 0);
|
||||
Assert.assertFalse(evalLong("-9223372036854775807 || -9223372036854775807", bindings) > 0);
|
||||
Assert.assertTrue(evalLong("-9223372036854775807 || 9223372036854775807", bindings) > 0);
|
||||
Assert.assertFalse(evalLong("0 || 0", bindings) > 0);
|
||||
|
||||
Assert.assertTrue(evaluate("9223372036854775807 > 9223372036854775806", bindings).longValue() > 0);
|
||||
Assert.assertTrue(evaluate("9223372036854775807 >= 9223372036854775807", bindings).longValue() > 0);
|
||||
Assert.assertTrue(evaluate("9223372036854775806 < 9223372036854775807", bindings).longValue() > 0);
|
||||
Assert.assertTrue(evaluate("9223372036854775807 <= 9223372036854775807", bindings).longValue() > 0);
|
||||
Assert.assertTrue(evaluate("9223372036854775807 == 9223372036854775807", bindings).longValue() > 0);
|
||||
Assert.assertTrue(evaluate("9223372036854775807 != 9223372036854775806", bindings).longValue() > 0);
|
||||
Assert.assertTrue(evalLong("9223372036854775807 > 9223372036854775806", bindings) > 0);
|
||||
Assert.assertTrue(evalLong("9223372036854775807 >= 9223372036854775807", bindings) > 0);
|
||||
Assert.assertTrue(evalLong("9223372036854775806 < 9223372036854775807", bindings) > 0);
|
||||
Assert.assertTrue(evalLong("9223372036854775807 <= 9223372036854775807", bindings) > 0);
|
||||
Assert.assertTrue(evalLong("9223372036854775807 == 9223372036854775807", bindings) > 0);
|
||||
Assert.assertTrue(evalLong("9223372036854775807 != 9223372036854775806", bindings) > 0);
|
||||
|
||||
Assert.assertEquals(9223372036854775807L, evaluate("9223372036854775806 + 1", bindings).longValue());
|
||||
Assert.assertEquals(9223372036854775806L, evaluate("9223372036854775807 - 1", bindings).longValue());
|
||||
Assert.assertEquals(9223372036854775806L, evaluate("4611686018427387903 * 2", bindings).longValue());
|
||||
Assert.assertEquals(4611686018427387903L, evaluate("9223372036854775806 / 2", bindings).longValue());
|
||||
Assert.assertEquals(7L, evaluate("9223372036854775807 % 9223372036854775800", bindings).longValue());
|
||||
Assert.assertEquals( 9223372030926249001L, evaluate("3037000499 ^ 2", bindings).longValue());
|
||||
Assert.assertEquals(-9223372036854775807L, evaluate("-9223372036854775807", bindings).longValue());
|
||||
Assert.assertEquals(9223372036854775807L, evalLong("9223372036854775806 + 1", bindings));
|
||||
Assert.assertEquals(9223372036854775806L, evalLong("9223372036854775807 - 1", bindings));
|
||||
Assert.assertEquals(9223372036854775806L, evalLong("4611686018427387903 * 2", bindings));
|
||||
Assert.assertEquals(4611686018427387903L, evalLong("9223372036854775806 / 2", bindings));
|
||||
Assert.assertEquals(7L, evalLong("9223372036854775807 % 9223372036854775800", bindings));
|
||||
Assert.assertEquals(9223372030926249001L, evalLong("3037000499 ^ 2", bindings));
|
||||
Assert.assertEquals(-9223372036854775807L, evalLong("-9223372036854775807", bindings));
|
||||
|
||||
Assert.assertTrue(evaluate("!-9223372036854775807", bindings).longValue() > 0);
|
||||
Assert.assertTrue(evaluate("!0", bindings).longValue() > 0);
|
||||
Assert.assertFalse(evaluate("!9223372036854775807", bindings).longValue() > 0);
|
||||
Assert.assertTrue(evalLong("!-9223372036854775807", bindings) > 0);
|
||||
Assert.assertTrue(evalLong("!0", bindings) > 0);
|
||||
Assert.assertFalse(evalLong("!9223372036854775807", bindings) > 0);
|
||||
|
||||
Assert.assertEquals(3037000499L, evaluate("sqrt(9223372036854775807)", bindings).longValue());
|
||||
Assert.assertEquals(9223372036854775807L, evaluate(
|
||||
"if(9223372036854775807, 9223372036854775807, 9223372036854775806)",
|
||||
bindings
|
||||
).longValue());
|
||||
Assert.assertEquals(9223372036854775806L, evaluate(
|
||||
"if(0, 9223372036854775807, 9223372036854775806)",
|
||||
bindings
|
||||
).longValue());
|
||||
Assert.assertEquals(3037000499L, evalLong("cast(sqrt(9223372036854775807), 'long')", bindings));
|
||||
Assert.assertEquals(
|
||||
1L, evalLong("if(x == 9223372036854775807, 1, 0)", bindings)
|
||||
);
|
||||
Assert.assertEquals(
|
||||
0L, evalLong("if(x - 1 == 9223372036854775807, 1, 0)", bindings)
|
||||
);
|
||||
|
||||
Assert.assertEquals(1271030400000L, evalLong("timestamp('2010-04-12')", bindings));
|
||||
Assert.assertEquals(1270998000000L, evalLong("timestamp('2010-04-12T+09:00')", bindings));
|
||||
Assert.assertEquals(1271055781000L, evalLong("timestamp('2010-04-12T07:03:01')", bindings));
|
||||
Assert.assertEquals(1271023381000L, evalLong("timestamp('2010-04-12T07:03:01+09:00')", bindings));
|
||||
Assert.assertEquals(1271023381419L, evalLong("timestamp('2010-04-12T07:03:01.419+09:00')", bindings));
|
||||
|
||||
Assert.assertEquals(1271030400L, evalLong("unix_timestamp('2010-04-12')", bindings));
|
||||
Assert.assertEquals(1270998000L, evalLong("unix_timestamp('2010-04-12T+09:00')", bindings));
|
||||
Assert.assertEquals(1271055781L, evalLong("unix_timestamp('2010-04-12T07:03:01')", bindings));
|
||||
Assert.assertEquals(1271023381L, evalLong("unix_timestamp('2010-04-12T07:03:01+09:00')", bindings));
|
||||
Assert.assertEquals(1271023381L, evalLong("unix_timestamp('2010-04-12T07:03:01.419+09:00')", bindings));
|
||||
|
||||
Assert.assertEquals("NULL", eval("nvl(if(x == 9223372036854775807, '', 'x'), 'NULL')", bindings).asString());
|
||||
Assert.assertEquals("x", eval("nvl(if(x == 9223372036854775806, '', 'x'), 'NULL')", bindings).asString());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -13,21 +13,26 @@ This expression language supports the following operators (listed in decreasing
|
|||
|<, <=, >, >=, ==, !=|Binary Comparison|
|
||||
|&&,\|\||Binary Logical AND, OR|
|
||||
|
||||
Long and double data types are supported. If a number contains a dot, it is interpreted as a double, otherwise it is interpreted as a long. That means, always add a '.' to your number if you want it intepreted as a double value.
|
||||
Long, double and string data types are supported. If a number contains a dot, it is interpreted as a double, otherwise it is interpreted as a long. That means, always add a '.' to your number if you want it interpreted as a double value. String literal should be quoted by single quotation marks.
|
||||
|
||||
Expressions can contain variables. Variable names may contain letters, digits, '\_' and '$'. Variable names must not begin with a digit.
|
||||
Expressions can contain variables. Variable names may contain letters, digits, '\_' and '$'. Variable names must not begin with a digit. To escape other special characters, user can quote it with double quotation marks.
|
||||
|
||||
For logical operators, a number is true if and only if it is positive. (0 means false)
|
||||
For logical operators, a number is true if and only if it is positive (0 or minus value means false). For string type, it's evaluation result of 'Boolean.valueOf(string)'.
|
||||
|
||||
Also, the following in-built functions are supported.
|
||||
Also, the following built-in functions are supported.
|
||||
|
||||
|name|description|
|
||||
|----|-----------|
|
||||
|sqrt|sqrt(x) would return square root of x|
|
||||
|if|if(predicate,then,else) returns 'then' if 'predicate' evaluates to a positive number, otherwise it returns 'else'|
|
||||
|cast|cast(expr,'LONG' or 'DOUBLE' or 'STRING') returns expr with specified type. exception can be thrown |
|
||||
|if|if(predicate,then,else) returns 'then' if 'predicate' evaluates to a positive number, otherwise it returns 'else' |
|
||||
|nvl|nvl(expr,expr-for-null) returns 'expr-for-null' if 'expr' is null (or empty string for string type) |
|
||||
|timestamp|timestamp(expr[,format-string]) parses string expr into date then returns milli-seconds from java epoch. without 'format-string' it's regarded as ISO datetime format |
|
||||
|unix_timestamp|same with 'timestamp' function but returns seconds instead |
|
||||
|
||||
Built-in Math functions. See javadoc of java.lang.Math for detailed explanation for each function.
|
||||
And built-in math functions. See javadoc of java.lang.Math for detailed explanation for each function.
|
||||
|
||||
|name|description|
|
||||
|----|-----------|
|
||||
|abs|abs(x) would return the absolute value of x|
|
||||
|acos|acos(x) would return the arc cosine of x|
|
||||
|asin|asin(x) would return the arc sine of x|
|
||||
|
|
|
@ -95,7 +95,7 @@ public class ExpressionPostAggregator implements PostAggregator
|
|||
@Override
|
||||
public Object compute(Map<String, Object> values)
|
||||
{
|
||||
return parsed.eval(Parser.withMap(values));
|
||||
return parsed.eval(Parser.withMap(values)).value();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -681,7 +681,7 @@ public class RowBasedGrouperHelper
|
|||
@Override
|
||||
public Number get()
|
||||
{
|
||||
return parsed.eval(binding);
|
||||
return parsed.eval(binding).numericValue();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
|
@ -853,7 +853,7 @@ public class QueryableIndexStorageAdapter implements StorageAdapter
|
|||
@Override
|
||||
public Number get()
|
||||
{
|
||||
return parsed.eval(binding);
|
||||
return parsed.eval(binding).numericValue();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
|
@ -323,7 +323,7 @@ public abstract class IncrementalIndex<AggregatorType> implements Iterable<Row>,
|
|||
@Override
|
||||
public Number get()
|
||||
{
|
||||
return parsed.eval(binding);
|
||||
return parsed.eval(binding).numericValue();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
|
@ -589,7 +589,7 @@ public class IncrementalIndexStorageAdapter implements StorageAdapter
|
|||
@Override
|
||||
public Number get()
|
||||
{
|
||||
return parsed.eval(binding);
|
||||
return parsed.eval(binding).numericValue();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
|
@ -52,7 +52,7 @@ public class ArithmeticPostAggregatorTest
|
|||
List<PostAggregator> postAggregatorList =
|
||||
Lists.newArrayList(
|
||||
new ConstantPostAggregator(
|
||||
"roku", 6
|
||||
"roku", 6D
|
||||
),
|
||||
new FieldAccessPostAggregator(
|
||||
"rows", "rows"
|
||||
|
@ -96,7 +96,7 @@ public class ArithmeticPostAggregatorTest
|
|||
List<PostAggregator> postAggregatorList =
|
||||
Lists.newArrayList(
|
||||
new ConstantPostAggregator(
|
||||
"roku", 6
|
||||
"roku", 6D
|
||||
),
|
||||
new FieldAccessPostAggregator(
|
||||
"rows", "rows"
|
||||
|
|
Loading…
Reference in New Issue