---
title: "Operations and expressions"
description: "How the two Aerospike server-side computation surfaces relate, and when to reach for each."
---

# Operations and expressions

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

Aerospike computes on the server in two ways. Each data type has an **operation API**, and most types also have an **expression** form of the same logic. Both execute inside the same command, against the same record, under the same record lock.

They are not competing choices. A single command routinely uses both.

## What each one does

An **operation** targets one named bin and acts on it in place. The [`operate`](https://aerospike.com/docs/develop/learn/bin-operations) command takes an ordered list of operations and applies them to an in-memory copy of the record, which is persisted if the list contained any write.

An **expression** evaluates to a typed value. Because it produces a value rather than acting on storage, it composes: the result of one expression becomes the input of another, and a single expression can read more than one bin. To use one inside `operate`, wrap it in a read expression or a write expression — see [Operation expressions](https://aerospike.com/docs/develop/expressions/#operation-expressions).

## The same logic, two outcomes

Where a data type offers both forms, they compute the same result. What differs is what happens to it.

An operation writes its result to the bin. An expression hands you the value, and how you wrap it decides whether that value is stored. As a read expression the value comes back under a name that exists only in the response, and the record is unchanged. As a write expression it is persisted to a bin.

A modify expression is therefore not a write. Uppercasing a string, appending to a list, or adding to a HyperLogLog through an expression yields the transformed value and leaves the stored bin as it was, unless you store it.

What an operation returns to the caller depends on the data type and on the operation itself. Each data type’s own reference documents that.

## What only expressions can do

An expression evaluates to a value anywhere the server needs one, so it reaches three places an operation cannot:

-   **Record filters** — a filter expression decides whether a command applies to a record at all. See [Record filtering](https://aerospike.com/docs/develop/expressions/#record-filtering-with-expressions).
-   **Query projections** — a read expression returns a computed value alongside a query’s results. See [Projection](https://aerospike.com/docs/develop/learn/queries/projection).
-   **Secondary index definitions** — an index can be built over a computed value rather than a stored bin. See [Secondary index expressions](https://aerospike.com/docs/develop/expressions/#secondary-index-expression).

## Choosing between them

Use the operation API when you want to change what is stored: normalize a value in place, append to a collection, trim a string. It is the shorter path, it names the bin directly, and the change is durable when the command commits.

Use an expression when you want a value rather than a change — to filter records, to return something computed that you do not want written, or to feed one computation into another. Reach for a write expression when the value you want to store is derived from more than one bin, which an operation cannot do.

## Where each data type documents its own

Both surfaces are documented per data type. Start from the type you are working with:

| Data type | Operations | Expressions |
| :-- | :-- | :-- |
| [String](https://aerospike.com/docs/develop/data-types/string) | [String operations](https://aerospike.com/docs/develop/data-types/string/operations) | [String expressions](https://aerospike.com/docs/develop/expressions/string) |
| [List](https://aerospike.com/docs/develop/data-types/collections/list) | [List operations](https://aerospike.com/docs/develop/data-types/collections/list/operations) | [List expressions](https://aerospike.com/docs/develop/expressions/list) |
| [Map](https://aerospike.com/docs/develop/data-types/collections/map) | [Map operations](https://aerospike.com/docs/develop/data-types/collections/map/operations) | [Map expressions](https://aerospike.com/docs/develop/expressions/map) |
| [Blob/bytes](https://aerospike.com/docs/develop/data-types/blob) | [Blob operations](https://aerospike.com/docs/develop/data-types/blob) | [Bit expressions](https://aerospike.com/docs/develop/expressions/bit) |
| [HyperLogLog](https://aerospike.com/docs/develop/data-types/hll) | [HLL operations](https://aerospike.com/docs/develop/data-types/hll) | [HLL expressions](https://aerospike.com/docs/develop/expressions/hll) |

For collections specifically, [Working with nested collection data types](https://aerospike.com/docs/develop/expressions/nesting) compares the two approaches with worked examples.