Dingo Expression
Dingo Expression is the expression engine used by DingoDB. It is special for its runtime codebase is separated from the parsing and compiling codebase. The classes in runtime are serializable so that they are suitable for runtime of distributed computing system, like Apache Flink.
Getting Started
Here is an example to use Dingo Expression.
class Example1 {
Object test() {
// The original expression string.
String exprString = "(1 + 2) * (5 - (3 + 4))";
// parse it into an Expr object, the compiler can be reused.
DingoExprCompiler compiler = new DingoExprCompiler();
Expr expr = compiler.parse(exprString);
// Compile in a CompileContext (can be null without variables in the expression) and get an RtExpr object.
RtExpr rtExpr = expr.compileIn(null);
// Evaluate it in an EvalContext (can be null without variables in the expression).
return rtExpr.eval(null);
}
}
Operators
Category |
Operator |
Associativity |
|---|---|---|
Parenthesis |
|
|
Function Call |
|
Left to right |
Name Index |
|
Left to right |
Array Index |
|
Left to right |
Unary |
|
Right to left |
Multiplicative |
|
Left to right |
Additive |
|
Left to right |
Relational |
|
Left to right |
String |
|
Left to right |
Logical NOT |
|
Left to right |
Logical AND |
|
Left to right |
Logical OR |
|
Left to right |
Data Types
Type Name |
SQL type |
JSON Schema Type |
Hosting Java Type |
Literal in Expression |
|---|---|---|---|---|
NULL |
NULL |
|
|
|
INT |
INTEGER |
|
|
|
LONG |
LONG |
integer |
|
|
DOUBLE |
DOUBLE |
number |
|
|
BOOL |
BOOLEAN |
boolean |
|
|
STRING |
VARCHAR |
string |
|
|
BINARY |
BLOB |
|
||
DECIMAL |
DECIMAL |
|
||
DATE |
DATE |
|
||
TIME |
TIME |
|
||
TIMESTAMP |
TIMESTAMP |
|
||
OBJECT |
ANY |
object |
|
|
ARRAY |
|
|||
LIST |
ARRAY |
array |
|
|
MAP |
MAP |
object |
|
Dingo Expression parses integer literals adaptively, if an integer literal’s value exceeds the range of Java int type,
it will be parsed to LONG; if the value exceeds the range of Java type long, it will be parsed to DECIMAL. On the
contrary, float literals are parsed to DECIMAL to keep the precision of value, you can then get a DOUBLE value by
casting functions.
Three-valued Logic
Dingo Expression has NULL type and support three-valued logic as in SQL. Generally, operators or functions evaluates
to NULL if any of its operands is NULL except some logical operators/functions.
Constants
Name |
Value |
|---|---|
TAU |
6.283185307179586476925 |
E |
2.7182818284590452354 |
There is not “3.14159265” but TAU. See The Tau Manifesto.
Functions
Function names are case-insensitive.
Casting
Function |
Java function based on |
Description |
|---|---|---|
|
Convert |
|
|
Convert |
|
|
Convert |
|
|
Convert |
|
|
Convert |
|
|
Convert |
|
|
Convert |
|
|
Convert |
|
|
Convert |
|
|
Convert |
NOTE: there are no DATE, TIME and TIMESTAMP literals, but you can write TIMESTAMP('2020-12-21'), etc.
Logical
Function |
Description |
|---|---|
|
The same as |
|
The same as |
|
The same as |
|
Test if |
|
Test if |
|
Test if |
|
Test if |
|
Test if |
|
Test if |
NOTE: null is not true or false and you can not test if a value is null by x == null because it
returns null.
Mathematical
See Math (Java Platform SE 8).
Function |
Java function based on |
Description |
|---|---|---|
|
Min value of |
|
|
Max value of |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
String
See String (Java Platform SE 8).
Function |
Java function based on |
Description |
|---|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Variables and Contexts
Variables can be used in expressions. In order to compile an expression containing variables, A CompileContext must be
provided to define the types of variables. A JSON Schema definition can be used as a source
of CompileContext. For example (in YAML format for simplicity, but you can surely use JSON format)
type: object
properties:
a:
type: integer
b:
type: number
c:
type: boolean
d:
type: string
additionalProperties: false
where variables a, b, c, d are defined with specified types. According to the schema, you can provide a data
source as (in YAML format)
{ a: 3, b: 4.0, c: false, d: bar }
Then an RtExpr can be compiled and evaluated as following
class Example2 {
Object test() {
// jsonSchemaInYamlFormat is a String/InputStream contains the JSON Schema definition.
RtSchemaRoot schemaRoot = SchemaParser.YAML.parse(jsonSchemaInYamlFormat);
DingoExprCompiler compiler = new DingoExprCompiler();
Expr expr = compiler.parse("a + b");
RtExpr rtExpr = expr.compileIn(schemaRoot.getSchema());
DataParser parser = DataParser.yaml(schemaRoot);
// dataInYamlFormat is a String contains the JSON Schema definition.
Object[] tuple = parser.parse(dataInYamlFormat);
return rtExpr.eval(new TupleEvalContext(tuple));
}
}
Nested Context
In a JSON Schema definition, objects and arrays can be nested into each other, for example
type: object
properties:
a:
type: object
properties:
b:
type: number
c:
type: boolean
additionalProperties: false
d:
type: array
items:
- type: integer
- type: string
additionalItems: false
additionalProperties: false
For JSON Schema of type array, if its additionalItems is set to true, the number and types of its elements are
determined, so each of its elements will be compiled to a standalone variable. This also happens to JSON Schema of
type object with a non-null properties, if its properties is not null and additionalProperties is set to false
, each of its properties will be compiled to a standalone variable. Otherwise, an array is compiled to LIST and
a map to MAP.
If an object type does not have properties defined, it is compiled to OBJECT.
As in the JSON schema above, you can use a.b and a.c to access the variables of number and boolean types,
separately. The syntax looks the same as map index, but they are really different variables and a is not an existing
variable at all in runtime. Also, you can use d[0] and d[1] to access the integer and the string variables
and d is not an existing variable.
The special variable $ can be used to access the whole context, so $.a is the same as a. $ is useful for a
context with an array as root. The parser also looks on a.b as a['b'], so the syntax to access variables is much
like JSONPath.