---
title: "Expressions Tutorial: Operation Expressions"
description: "Read and write computed values server-side with operation expressions, avoiding read-modify-write round trips."
---

# Operation expressions

> For the complete documentation index see: [llms.txt](https://aerospike.com/docs/llms.txt)
> 
> All documentation pages available in markdown.

This page is for developers using Aerospike client libraries. Complete [Setup](https://aerospike.com/docs/develop/tutorials/operations/expressions/setup) first. After reading this page, you can use operation expressions to read and write computed bin values server-side without a read-modify-write round trip.

Operation expressions as the name suggests are used in an operation - either to read from bins or write to a bin. Specifically they are used in `read` and `write` methods of `ExpOperation`.

The basic computational model of `operate`, where operation expressions are used, remains the same: A series of read or write commands are performed in a given sequence on a single record. What is new is that a read command can be an expression involving zero or more bins. Also, a write command can get the value from an expression (enabling, for example, use of cross-bin data with conditional logic) instead of a simple constant to update a bin.

A read with operation expression can also use an arbitrary name for the “computed bin” similar to the “as” keyword in the SQL statement `SELECT expr AS bin`.

The pattern for coding an Operation Expression is:

1.  Define Expression to read or write the bins.
2.  Use `Expression` object in `ExpOperation.read` or `.write` method that returns an `Operation`.
3.  Use “expression operations” in any API call that takes an operation list.

This is illustrated below.

-   [Java](#tab-panel-5661)
-   [Python](#tab-panel-5662)
-   [Go](#tab-panel-5663)
-   [C#](#tab-panel-5664)
-   [Node.js](#tab-panel-5665)
-   [Rust](#tab-panel-5666)

```java
// operate expression with write

// 1. Define Expression to write the bin with.

//    if (age > 65 && comorbidities) {risk = "high";}

//    else {risk = "normal";}

Expression writeExp = Exp.build(

        Exp.cond(

            Exp.and(

                Exp.gt(

                    Exp.intBin("age"), Exp.val(65)),

                Exp.boolBin("comorbidities")),

            Exp.val("high"),

            Exp.val("normal")));

// 2. Use Expression object in ExpOperation.write method.

Operation writeExpOp = ExpOperation.write("risk",

                             writeExp,   // evaluates bin value to update

                             ExpWriteFlags.DEFAULT);

// operate expression with read

// 1. Define Expression to read bins.

//    read "yes" if (risk == "high" or worktype == "frontline") else "no"

//    as a computed bin "eligible"

Expression readExp = Exp.build(

        Exp.cond(

            Exp.or(

                Exp.eq(

                    Exp.stringBin("risk"), Exp.val("high")),

                Exp.eq(

                    Exp.stringBin("worktype"), Exp.val("frontline"))),

            Exp.val("yes"),

            Exp.val("no")));

// 2. Use Expression object in ExpOperation.read method.

Operation readExpOp = ExpOperation.read("eligible",   // named "computed bin"

                             readExp,      // evaluates value to return

                             ExpReadFlags.DEFAULT);

// 3. Use "expression operations" in any API call that takes an operation list.

Record record = Client.operate(WritePolicy policy, Key key, Operation writeExpOp, Operation readExpOp);
```

```python
# operate expression with write

# 1. Define Expression to write the bin with.

#    if age > 65 and comorbidities: risk = "high"

#    else: risk = "normal"

write_exp = exp.Cond(

    exp.And(

        exp.GT(

            exp.IntBin("age"), 65),

        exp.BoolBin("comorbidities")),

    "high",

    "normal").compile()

# 2. Use the compiled expression in an expression_write operation.

write_exp_op = expression_operations.expression_write(

    "risk",

    write_exp,   # evaluates bin value to update

    aerospike.EXP_WRITE_DEFAULT)

# operate expression with read

# 1. Define Expression to read bins.

#    read "yes" if (risk == "high" or worktype == "frontline") else "no"

#    as a computed bin "eligible"

read_exp = exp.Cond(

    exp.Or(

        exp.Eq(

            exp.StringBin("risk"), "high"),

        exp.Eq(

            exp.StringBin("worktype"), "frontline")),

    "yes",

    "no").compile()

# 2. Use the compiled expression in an expression_read operation.

read_exp_op = expression_operations.expression_read(

    "eligible",   # named "computed bin"

    read_exp,     # evaluates value to return

    aerospike.EXP_READ_DEFAULT)

# 3. Use "expression operations" in any API call that takes an operation list.

_, _, record = client.operate(key, [write_exp_op, read_exp_op], policy=policy)
```

```go
// operate expression with write

// 1. Define Expression to write the bin with.

//    if age > 65 && comorbidities { risk = "high" }

//    else { risk = "normal" }

writeExp := as.ExpCond(

    as.ExpAnd(

        as.ExpGreater(

            as.ExpIntBin("age"), as.ExpIntVal(65)),

        as.ExpBoolBin("comorbidities")),

    as.ExpStringVal("high"),

    as.ExpStringVal("normal"))

// 2. Use the expression in an ExpWriteOp.

writeExpOp := as.ExpWriteOp("risk",

    writeExp, // evaluates bin value to update

    as.ExpWriteFlagDefault)

// operate expression with read

// 1. Define Expression to read bins.

//    read "yes" if (risk == "high" or worktype == "frontline") else "no"

//    as a computed bin "eligible"

readExp := as.ExpCond(

    as.ExpOr(

        as.ExpEq(

            as.ExpStringBin("risk"), as.ExpStringVal("high")),

        as.ExpEq(

            as.ExpStringBin("worktype"), as.ExpStringVal("frontline"))),

    as.ExpStringVal("yes"),

    as.ExpStringVal("no"))

// 2. Use the expression in an ExpReadOp.

readExpOp := as.ExpReadOp("eligible", // named "computed bin"

    readExp, // evaluates value to return

    as.ExpReadFlagDefault)

// 3. Use "expression operations" in any API call that takes an operation list.

record, err := client.Operate(policy, key, writeExpOp, readExpOp)

if err != nil {

    log.Fatal(err)

}
```

```csharp
// operate expression with write

// 1. Define Expression to write the bin with.

//    if (age > 65 && comorbidities) {risk = "high";}

//    else {risk = "normal";}

Expression writeExp = Exp.Build(

    Exp.Cond(

        Exp.And(

            Exp.GT(

                Exp.IntBin("age"), Exp.Val(65)),

            Exp.BoolBin("comorbidities")),

        Exp.Val("high"),

        Exp.Val("normal")));

// 2. Use the Expression object in the ExpOperation.Write method.

Operation writeExpOp = ExpOperation.Write("risk",

    writeExp,   // evaluates bin value to update

    ExpWriteFlags.DEFAULT);

// operate expression with read

// 1. Define Expression to read bins.

//    read "yes" if (risk == "high" or worktype == "frontline") else "no"

//    as a computed bin "eligible"

Expression readExp = Exp.Build(

    Exp.Cond(

        Exp.Or(

            Exp.EQ(

                Exp.StringBin("risk"), Exp.Val("high")),

            Exp.EQ(

                Exp.StringBin("worktype"), Exp.Val("frontline"))),

        Exp.Val("yes"),

        Exp.Val("no")));

// 2. Use the Expression object in the ExpOperation.Read method.

Operation readExpOp = ExpOperation.Read("eligible",   // named "computed bin"

    readExp,      // evaluates value to return

    ExpReadFlags.DEFAULT);

// 3. Use "expression operations" in any API call that takes an operation list.

Record record = client.Operate(policy, key, writeExpOp, readExpOp);
```

```js
// operate expression with write

// 1. Define Expression to write the bin with.

//    if (age > 65 && comorbidities) { risk = 'high' }

//    else { risk = 'normal' }

const writeExpr = exp.cond(

    exp.and(

        exp.gt(

            exp.binInt('age'), exp.int(65)),

        exp.binBool('comorbidities')),

    exp.str('high'),

    exp.str('normal'));

// 2. Use the expression in an exp.operations.write op.

const writeExpOp = exp.operations.write('risk',

    writeExpr, // evaluates bin value to update

    exp.expWriteFlags.DEFAULT);

// operate expression with read

// 1. Define Expression to read bins.

//    read 'yes' if (risk == 'high' or worktype == 'frontline') else 'no'

//    as a computed bin 'eligible'

const readExpr = exp.cond(

    exp.or(

        exp.eq(

            exp.binStr('risk'), exp.str('high')),

        exp.eq(

            exp.binStr('worktype'), exp.str('frontline'))),

    exp.str('yes'),

    exp.str('no'));

// 2. Use the expression in an exp.operations.read op.

const readExpOp = exp.operations.read('eligible', // named "computed bin"

    readExpr, // evaluates value to return

    exp.expReadFlags.DEFAULT);

// 3. Use "expression operations" in any API call that takes an operation list.

const record = await client.operate(key, [writeExpOp, readExpOp], {}, policy);
```

```rust
use aerospike::expressions::{and, bool_bin, cond, eq, gt, int_bin, int_val, or, string_bin, string_val};

use aerospike::operations::exp::{read_exp, write_exp, ExpReadFlags, ExpWriteFlags};

// operate expression with write

// 1. Define Expression to write the bin with.

//    if age > 65 && comorbidities { risk = "high" }

//    else { risk = "normal" }

let write_expr = 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())]);

// 2. Use the expression in a write_exp operation.

let write_exp_op = write_exp("risk",

    write_expr, // evaluates bin value to update

    ExpWriteFlags::Default);

// operate expression with read

// 1. Define Expression to read bins.

//    read "yes" if (risk == "high" or worktype == "frontline") else "no"

//    as a computed bin "eligible"

let read_expr = cond(vec![

    or(vec![

        eq(string_bin("risk".to_string()), string_val("high".to_string())),

        eq(string_bin("worktype".to_string()), string_val("frontline".to_string()))]),

    string_val("yes".to_string()),

    string_val("no".to_string())]);

// 2. Use the expression in a read_exp operation.

let read_exp_op = read_exp("eligible", // named "computed bin"

    read_expr, // evaluates value to return

    ExpReadFlags::Default);

// 3. Use "expression operations" in any API call that takes an operation list.

let record = client.operate(&policy, &key, &[write_exp_op, read_exp_op]).await?;
```

Note that the order of operations is important. The `readExp` relies on the `risk` bin which gets written in the `writeExp`, so the `writeExp` must come first.

With the Developer SDK, the write and read operation expressions above are [AEL](https://aerospike.com/docs/develop/client/sdk/concepts/ael/reference) text passed to `.upsertFrom()` and `.selectFrom()`:

-   [Java](#tab-panel-5667)
-   [Python](#tab-panel-5668)
-   [Go](#tab-panel-5669)
-   [C#](#tab-panel-5670)
-   [Node.js](#tab-panel-5671)
-   [Rust](#tab-panel-5672)

```java
DataSet people = DataSet.of(NAMESPACE, SET_NAME);

Record record = session.upsert(people.id(userId))

    // write: same condition as writeExp above

    .bin("risk").upsertFrom(

        "when ($.age > 65 and $.comorbidities == true => 'high', default => 'normal')")

    // read: same condition as readExp above, as a computed "eligible" bin

    .bin("eligible").selectFrom(

        "when ($.risk == 'high' or $.worktype == 'frontline' => 'yes', default => 'no')")

    .execute()

    .getFirstRecord();
```

```python
people = DataSet.of(NAMESPACE, SET_NAME)

stream = await (

    session.upsert(people.id(user_id))

    # write: same condition as write_exp above

    .bin("risk").upsert_from(

        "when ($.age > 65 and $.comorbidities == true => 'high', default => 'normal')")

    # read: same condition as read_exp above, as a computed "eligible" bin

    .bin("eligible").select_from(

        "when ($.risk == 'high' or $.worktype == 'frontline' => 'yes', default => 'no')")

    .execute()

)

record = await stream.first_or_raise()
```

::: note
AEL text authoring is available for the Developer SDK (Java and Python) only. Go continues to use the `Exp*` builder shown above.
:::

::: note
AEL text authoring is available for the Developer SDK (Java and Python) only. C# continues to use the `Exp` builder shown above.
:::

::: note
AEL text authoring is available for the Developer SDK (Java and Python) only. Node.js continues to use the `exp` builder shown above.
:::

::: note
AEL text authoring is available for the Developer SDK (Java and Python) only. Rust continues to use the `aerospike::expressions` builder shown above.
:::

## Using expression operations vs R-M-W or UDFs

Aerospike developers have multiple ways to perform a record oriented read-write logic.

1.  Read record data to the client, modify, and write back (“R-M-W”).
2.  Create a UDF for the logic and invoke it on the record.
3.  Use expression operations in a multi-op request.

For read-write transactions, fetching the data to the client and writing back is expensive and requires special care to ensure read-write isolation. Lua UDFs can be difficult to implement, less flexible to change, and can be slower. So it is generally beneficial to use expression operations when possible.

Here is a suggested decision process:

1.  Use expression operations. However if expression operations cannot be used because the task, for example, requires unsupported features such as iterators and loops, then:
2.  Use client-side Read-Modify-Write (R-M-W) with version check if amount of data transfer as well as possibility of conflict due to concurrency is limited. Otherwise:
3.  Use UDFs if Lua server side programming model and performance meet the needs. Otherwise must use 2.

Note, Aerospike provides many ways to implement a given data task on one or multiple records. To determine the optimal way for a given task, one should consider and evaluate the options available including the various execution modes (synchronous, asynchronous, background, etc).

## Next

Continue to the full [operation expressions code example](https://aerospike.com/docs/develop/tutorials/operations/expressions/operation-expressions-example).