Skip to content

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))

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")))

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))));

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-value

It 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"));

Useful syntax patterns

Here is a table that summarizes some useful expression syntax patterns.

ExpressionSyntax Example
3Exp.val(3)
”abc”Exp.val("abc")
|-3|Exp.abs(Exp.val(-3))
1 + 2Exp.add(Exp.val(1), Exp.val(2))
var a = 5Exp.def("a", Exp.val(5))
2 > 3Exp.gt(Exp.val(2), Exp.val(3))
Function lastUpdateTimeExp.lastUpdateTime()
List API listSizeListExp.listSize(Exp.listBin("list"))
Composition a + 2 * bExp.add(Exp.var("a"), Exp.mul(Exp.val(2), Exp.var("b")))
Conditional eval if (a == 1) then 2; else 3Exp.cond(Exp.eq(Exp.var("a"), Exp.val(1)), Exp.val(2), Exp.val(3))
Integer “bin” valueExp.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")));

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.