Syntax and coding patterns
For the complete documentation index see: llms.txt
All documentation pages available in markdown.
With a better understanding of their structure, it is easier to parse Aerospike expressions.
Notation
Aerospike expressions use Polish Notation (aka prefix notation) which is widely seen in most programming language functions: fn(a, b). So the expression 5 + 3 in Aerospike Java client would be:
Exp.add( Exp.val(5), Exp.val(3))exp.Add( exp.Val(5), exp.Val(3))as.ExpNumAdd( as.ExpIntVal(5), as.ExpIntVal(3))Exp.Add( Exp.Val(5), Exp.Val(3))exp.add( exp.int(5), exp.int(3))num_add(vec![ int_val(5), int_val(3)])Composition
A complex expression can be composed using two or more sub-expressions. For example, with integer bins a and b, the expression (a - b) / (a + b) would be:
Exp.div( Exp.sub( Exp.intBin("a"), Exp.intBin("b")), Exp.add( Exp.intBin("a"), Exp.intBin("b")))exp.Div( exp.Sub( exp.IntBin("a"), exp.IntBin("b")), exp.Add( exp.IntBin("a"), exp.IntBin("b")))as.ExpNumDiv( as.ExpNumSub( as.ExpIntBin("a"), as.ExpIntBin("b")), as.ExpNumAdd( as.ExpIntBin("a"), as.ExpIntBin("b")))Exp.Div( Exp.Sub( Exp.IntBin("a"), Exp.IntBin("b")), Exp.Add( Exp.IntBin("a"), Exp.IntBin("b")))exp.div( exp.sub( exp.binInt('a'), exp.binInt('b')), exp.add( exp.binInt('a'), exp.binInt('b')))num_div(vec![ num_sub(vec![ int_bin("a".to_string()), int_bin("b".to_string())]), num_add(vec![ int_bin("a".to_string()), int_bin("b".to_string())])])Note, there are corresponding access methods to access bin values for other supported types. Since a bin may hold any value type, an incorrect type access results in an error. A conditional type check may be used to prevent a run-time error.
Variable definition and use
The let construct defines the scope of variables and the expression that uses them. The def construct defines a variable and assigns it to an expression. Another expression in the scope can use the variable as a substitute for the expression it defines. For example, in the expression 5 < (a + b) < 10 using a variable x for the sum of integer bins a and b:
Exp.let( // let defines the scope of variables for this expression Exp.def("x", // def defines a variable Exp.sum( // and also assigns it to an expression Exp.intBin("a"), Exp.intBin("b")), Exp.and( // the expression in let scope can use the variable Exp.lt( Exp.val(5), Exp.var("x")), // var to use the variable Exp.lt( Exp.var("x"), Exp.val(10))));exp.Let( # Let defines the scope of variables for this expression exp.Def("x", # Def defines a variable exp.Add( # and also assigns it to an expression exp.IntBin("a"), exp.IntBin("b"))), exp.And( # the expression in Let scope can use the variable exp.LT( exp.Val(5), exp.Var("x")), # Var to use the variable exp.LT( exp.Var("x"), exp.Val(10))))as.ExpLet( // ExpLet defines the scope of variables for this expression as.ExpDef("x", // ExpDef defines a variable as.ExpNumAdd( // and also assigns it to an expression as.ExpIntBin("a"), as.ExpIntBin("b"))), as.ExpAnd( // the expression in Let scope can use the variable as.ExpLess( as.ExpIntVal(5), as.ExpVar("x")), // ExpVar to use the variable as.ExpLess( as.ExpVar("x"), as.ExpIntVal(10))))Exp.Let( // Let defines the scope of variables for this expression Exp.Def("x", // Def defines a variable Exp.Add( // and also assigns it to an expression Exp.IntBin("a"), Exp.IntBin("b"))), Exp.And( // the expression in Let scope can use the variable Exp.LT( Exp.Val(5), Exp.Var("x")), // Var to use the variable Exp.LT( Exp.Var("x"), Exp.Val(10))))exp.let( // let defines the scope of variables for this expression ...exp.def('x', // def defines a variable exp.add( // and also assigns it to an expression exp.binInt('a'), exp.binInt('b'))), exp.and( // the expression in let scope can use the variable exp.lt( exp.int(5), exp.var('x')), // var to use the variable exp.lt( exp.var('x'), exp.int(10))))exp_let(vec![ // exp_let defines the scope of variables for this expression def("x".to_string(), // def defines a variable num_add(vec![ // and also assigns it to an expression int_bin("a".to_string()), int_bin("b".to_string())])), and(vec![ // the expression in exp_let scope can use the variable lt(int_val(5), var("x".to_string())), // var to use the variable lt(var("x".to_string()), int_val(10))])])Note in the above example, the variable x avoids repetitive access to the bins a and b. Also, variables defined in let cannot be used beyond its scope. The Rust client names this function exp_let rather than let, since let is a reserved keyword in Rust.
Conditional evaluation
The cond construct includes one or more pairs of bool exp, value exp followed by a default value:
bool exp1, value exp1, bool exp2, value exp2, ..., default-valueIt evaluates like the if-then-else logic: the expression takes the value of the first value exp in the sequence whose corresponding bool exp evaluates to true. If all boolean conditions fail, then it evaluates to the last default-value.
So an expression to evaluate a simple risk value “high” or “normal” based on int bin age and bool bin comorbidities would be:
// if (age > 65 && comorbidities) {risk = "high";}// else {risk = "normal";}Exp.cond( Exp.and( Exp.gt( Exp.intBin("age"), Exp.val(65)), Exp.boolBin("comorbidities")), Exp.val("high"), Exp.val("normal"));# if age > 65 and comorbidities: risk = "high"# else: risk = "normal"exp.Cond( exp.And( exp.GT( exp.IntBin("age"), 65), exp.BoolBin("comorbidities")), "high", "normal")// if age > 65 && comorbidities { risk = "high" }// else { risk = "normal" }as.ExpCond( as.ExpAnd( as.ExpGreater( as.ExpIntBin("age"), as.ExpIntVal(65)), as.ExpBoolBin("comorbidities")), as.ExpStringVal("high"), as.ExpStringVal("normal"))// if (age > 65 && comorbidities) {risk = "high";}// else {risk = "normal";}Exp.Cond( Exp.And( Exp.GT( Exp.IntBin("age"), Exp.Val(65)), Exp.BoolBin("comorbidities")), Exp.Val("high"), Exp.Val("normal"))// if (age > 65 && comorbidities) { risk = 'high' }// else { risk = 'normal' }exp.cond( exp.and( exp.gt( exp.binInt('age'), exp.int(65)), exp.binBool('comorbidities')), exp.str('high'), exp.str('normal'))// if age > 65 && comorbidities { risk = "high" }// else { risk = "normal" }cond(vec![ and(vec![ gt(int_bin("age".to_string()), int_val(65)), bool_bin("comorbidities".to_string())]), string_val("high".to_string()), string_val("normal".to_string())])Useful syntax patterns
Here is a table that summarizes some useful expression syntax patterns.
| Expression | Syntax Example |
|---|---|
| 3 | Exp.val(3) |
| ”abc” | Exp.val("abc") |
| |-3| | Exp.abs(Exp.val(-3)) |
| 1 + 2 | Exp.add(Exp.val(1), Exp.val(2)) |
| var a = 5 | Exp.def("a", Exp.val(5)) |
| 2 > 3 | Exp.gt(Exp.val(2), Exp.val(3)) |
| Function lastUpdateTime | Exp.lastUpdateTime() |
| List API listSize | ListExp.listSize(Exp.listBin("list")) |
| Composition a + 2 * b | Exp.add(Exp.var("a"), Exp.mul(Exp.val(2), Exp.var("b"))) |
| Conditional eval if (a == 1) then 2; else 3 | Exp.cond(Exp.eq(Exp.var("a"), Exp.val(1)), Exp.val(2), Exp.val(3)) |
| Integer “bin” value | Exp.intBin("bin") |
Coding patterns
An expression object is constructed on the client to be sent to the server where it is evaluated and used.
An expression’s wire protocol representation is constructed with the build() function. A simple expression fname == "Frank" will be built thus:
Expression simpleExp = Exp.build( Exp.eq( Exp.stringBin("fname"), Exp.val("Frank")));simple_exp = exp.Eq( exp.StringBin("fname"), "Frank").compile()simpleExp := as.ExpEq( as.ExpStringBin("fname"), as.ExpStringVal("Frank"))Expression simpleExp = Exp.Build( Exp.EQ( Exp.StringBin("fname"), Exp.Val("Frank")));const simpleExp = exp.eq( exp.binStr('fname'), exp.str('Frank'))let simple_exp = eq( string_bin("fname".to_string()), string_val("Frank".to_string()));Note the wire protocol representation of expression is of type
Expression, whereas a client object is of type Exp. In the Python
client, calling .compile() on an expression produces the equivalent
wire-format representation. The Go, Node.js, and Rust clients have no
separate build or compile step: each builder function already returns a
ready-to-use expression value (an Expression in Rust) that you assign
directly to a policy’s filter-expression field or pass to a read/write
expression operation.
An expression can be used as a filter expression or an operation expression, as described in the next sections.
Both filter and operation expressions can be used independently of each other and also in the same API call.
Next
Continue to Filter expressions.