---
title: "Expressions Tutorial: Syntax and Coding Patterns"
description: "Polish notation, composition, variables, conditional evaluation, and how expressions are built and sent to the server."
---

# Syntax and coding patterns

> For the complete documentation index see: [llms.txt](https://aerospike.com/docs/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:

-   [Java](#tab-panel-5685)
-   [Python](#tab-panel-5686)
-   [Go](#tab-panel-5687)
-   [C#](#tab-panel-5688)
-   [Node.js](#tab-panel-5689)
-   [Rust](#tab-panel-5690)

```java
Exp.add(

    Exp.val(5),

    Exp.val(3))
```

```python
exp.Add(

    exp.Val(5),

    exp.Val(3))
```

```go
as.ExpNumAdd(

    as.ExpIntVal(5),

    as.ExpIntVal(3))
```

```csharp
Exp.Add(

    Exp.Val(5),

    Exp.Val(3))
```

```js
exp.add(

    exp.int(5),

    exp.int(3))
```

```rust
num_add(vec![

    int_val(5),

    int_val(3)])
```

::: note
The overloaded `val` method wraps a value in a `Value` object, an abstraction used across all supported types. The Go client’s numeric operators (`ExpNumAdd`, `ExpNumSub`, and so on) are named with a `Num` prefix to distinguish them from other same-named non-numeric expressions. The Rust client’s equivalents (`num_add`, `num_sub`, and so on) are variadic free functions that take a `Vec<Expression>`.
:::

## 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:

-   [Java](#tab-panel-5691)
-   [Python](#tab-panel-5692)
-   [Go](#tab-panel-5693)
-   [C#](#tab-panel-5694)
-   [Node.js](#tab-panel-5695)
-   [Rust](#tab-panel-5696)

```java
Exp.div(

    Exp.sub(

        Exp.intBin("a"),

        Exp.intBin("b")),

    Exp.add(

        Exp.intBin("a"),

        Exp.intBin("b")))
```

```python
exp.Div(

    exp.Sub(

        exp.IntBin("a"),

        exp.IntBin("b")),

    exp.Add(

        exp.IntBin("a"),

        exp.IntBin("b")))
```

```go
as.ExpNumDiv(

    as.ExpNumSub(

        as.ExpIntBin("a"),

        as.ExpIntBin("b")),

    as.ExpNumAdd(

        as.ExpIntBin("a"),

        as.ExpIntBin("b")))
```

```csharp
Exp.Div(

    Exp.Sub(

        Exp.IntBin("a"),

        Exp.IntBin("b")),

    Exp.Add(

        Exp.IntBin("a"),

        Exp.IntBin("b")))
```

```js
exp.div(

    exp.sub(

        exp.binInt('a'),

        exp.binInt('b')),

    exp.add(

        exp.binInt('a'),

        exp.binInt('b')))
```

```rust
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`:

-   [Java](#tab-panel-5697)
-   [Python](#tab-panel-5698)
-   [Go](#tab-panel-5699)
-   [C#](#tab-panel-5700)
-   [Node.js](#tab-panel-5701)
-   [Rust](#tab-panel-5702)

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

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

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

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

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

```rust
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:

```text
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:

-   [Java](#tab-panel-5703)
-   [Python](#tab-panel-5704)
-   [Go](#tab-panel-5705)
-   [C#](#tab-panel-5706)
-   [Node.js](#tab-panel-5707)
-   [Rust](#tab-panel-5708)

```java
// 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"));
```

```python
# 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")
```

```go
// 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"))
```

```csharp
// 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"))
```

```js
// 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'))
```

```rust
// 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:

-   [Java](#tab-panel-5709)
-   [Python](#tab-panel-5710)
-   [Go](#tab-panel-5711)
-   [C#](#tab-panel-5712)
-   [Node.js](#tab-panel-5713)
-   [Rust](#tab-panel-5714)

```java
Expression simpleExp = Exp.build(

                            Exp.eq(

                                Exp.stringBin("fname"),

                                Exp.val("Frank")));
```

```python
simple_exp = exp.Eq(

    exp.StringBin("fname"),

    "Frank").compile()
```

```go
simpleExp := as.ExpEq(

    as.ExpStringBin("fname"),

    as.ExpStringVal("Frank"))
```

```csharp
Expression simpleExp = Exp.Build(

    Exp.EQ(

        Exp.StringBin("fname"),

        Exp.Val("Frank")));
```

```js
const simpleExp = exp.eq(

    exp.binStr('fname'),

    exp.str('Frank'))
```

```rust
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](https://aerospike.com/docs/develop/tutorials/operations/expressions/filter-expressions).