---
title: "Author AEL filter and operation expressions"
description: "Author Aerospike Expression Language (AEL) text for filter, read, and write operations in the Java and Python Developer SDKs."
---

# Author AEL filter and operation expressions

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

Task guide: pass Aerospike Expression Language (AEL) text to filter records, project computed read results, and persist computed write results on single-key, batch, and set-query commands in the Java and Python Developer SDKs.

## Applies to

-   Aerospike Developer SDKs (Java 21+ and Python 3.10+)
-   Aerospike Database **8.2.0.0** or later, for both filter APIs (`.where(...)`) and operation-expression APIs (`selectFrom`, `upsertFrom`, `insertFrom`, `updateFrom`) — AEL is compiled and parsed on the server, so there is no lower version tier for either

AEL text is parsed and compiled entirely on the server — there is no client-side AEL parser in the shipping SDKs. (An ANTLR grammar, `Condition.g4`, exists in both SDK repos, but it is not part of the branches these SDKs ship from; it does not run for `.where()`, `selectFrom`, `upsertFrom`, `insertFrom`, or `updateFrom`.) That server-side AEL compiler is what the 8.2.0 requirement gates.

## Audience

Application developers using the Java or Python Developer SDK (**Intermediate**).

## Prerequisites

-   A connected [`session`](https://aerospike.com/docs/develop/client/sdk/connect)
-   Basic familiarity with [Aerospike Expression Language (AEL)](https://aerospike.com/docs/develop/client/sdk/concepts/ael)

## Outcome

You can pass AEL strings to `.where(...)`, `selectFrom(...)` / `select_from(...)`, and write-side `upsertFrom` / `insertFrom` / `updateFrom` builders on supported commands.

::: code examples
Examples assume a connected `session` from [Connect to Aerospike](https://aerospike.com/docs/develop/client/sdk/connect). Each usage guide lists shared imports in a tabbed block near the top of the page; snippet blocks add imports only for types not already shown. The first code block in each section includes dataset setup; later blocks in that section reuse those values.
:::

The Developer SDK accepts AEL as plain text on fluent builders. You author the expression string; the SDK sends it to Aerospike Database as-is, and the server parses, compiles, and evaluates it. For grammar details (paths, operators, `let`, `when`, and CDT selectors), see [AEL reference](https://aerospike.com/docs/develop/client/sdk/concepts/ael/reference). When a string fails to compile or a filter silently excludes records, see [Debug AEL expressions](https://aerospike.com/docs/develop/client/sdk/concepts/ael/debugging-ael-expressions).

## Three roles for AEL text

| Role | SDK entry point | Expression must evaluate to | Typical use |
| --- | --- | --- | --- |
| **Filter** | `.where("...")` | Boolean | Skip records that do not match |
| **Read / projection** | `.bin("out").selectFrom("...")` / `.select_from(...)` | Any value | Return a computed virtual bin without storing it |
| **Write** | `.bin("out").upsertFrom("...")` / `.upsert_from(...)` (and `insertFrom` / `updateFrom`) | Any value | Compute on the server and persist to a bin |

Filter expressions decide whether an operation runs on a record. Operation expressions compute a value, which is either returned to the client (read) or written into a bin (write).

::: server-side evaluation model
Build the full expression string in application code on each request and pass it to `.where(...)`, `selectFrom(...)`, or `upsertFrom(...)`. The server parses and compiles the AEL text on every request; there is no client-side parser and no prepare/bind cache.
:::

### Dynamic values in AEL text

Both languages let you pass extra arguments to `.where(...)` and the operation-expression builders alongside the AEL string: `.where("$.age >= %d", age)` in Java, `.where("$.age >= %d", age)` in Python. These substitute using `printf`/`String.format`\-style specifiers (`%s`, `%d`, and so on) on the client, before the text is sent. **This substitution does not quote or escape anything** — it is no safer than building the string yourself with concatenation or an f-string. A value that contains a stray quote can still break out of a string literal and change the query, filter, or write semantics sent to the server.

::: do not interpolate untrusted values without validation
Whether you concatenate, f-string, or use the `%s`/`%d` argument form above, an externally-supplied value (such as user input) that isn’t validated or escaped first can break out of a string literal in the AEL text and alter what gets sent to the server. Validate or escape untrusted values before they reach any of these forms.
:::

For Java only, [`PreparedAel`](https://javadoc.io/doc/com.aerospike/aerospike-client-sdk/latest/com/aerospike/client/sdk/query/PreparedAel.html) is a safer alternative when a value might not be fully trusted. It uses zero-based `?0`, `?1`, … placeholders and formats each bound value into a quoted, escaped AEL literal for you (strings are quoted and checked for embedded quote characters; numbers, lists, and maps are formatted directly) — so the same untrusted-input risk does not apply. See [Reuse a filter template across calls](#reuse-a-filter-template-across-calls) below. Python has no equivalent safe-binding type; validate or escape untrusted values by hand before interpolating them into Python AEL text.

## AEL text vs `Exp` builder expressions

For **filter** predicates, the Developer SDK accepts readable AEL strings or programmatic `Exp.*` builders. This guide shows AEL text; see [AEL overview: AEL vs expressions](https://aerospike.com/docs/develop/client/sdk/concepts/ael#ael-vs-expressions) for side-by-side filter examples. Canonical grammar is in [AEL reference](https://aerospike.com/docs/develop/client/sdk/concepts/ael/reference).

For **operation expressions** (read projection using `selectFrom` / `select_from`, write using `upsertFrom` / `insertFrom` / `updateFrom`), author AEL text on the Developer SDK builders in the following sections. Write expressions apply to single-key and batch `operate` chains, not to query projection (read-only). Legacy per-client pages document `Exp` / `Operation` builder composition in the [expressions reference guide](https://aerospike.com/docs/develop/expressions); those builders remain for advanced programmatic cases.

## Filter expressions with `.where()`

Pass a Boolean AEL string to `.where(...)` on any command that supports record filters.

### Filter a set query

-   [Java](#tab-panel-6056)
-   [Python](#tab-panel-6057)

```java
DataSet orders = DataSet.of("test", "orders");

RecordStream stream = session.query(orders)

    .where("$.status == 'open' and ($.price * $.qty) + $.shipping > 100.0")

    .execute();

stream.forEach(result -> {

    Record row = result.recordOrThrow();

    // Process matching rows

});

stream.close();
```

> 📖 **API reference**: [`DataSet.of(...)`](https://javadoc.io/doc/com.aerospike/aerospike-client-sdk/latest/com/aerospike/client/sdk/DataSet.html#of%28java.lang.String%2Cjava.lang.String%29) | [`Session.query(DataSet)`](https://javadoc.io/doc/com.aerospike/aerospike-client-sdk/latest/com/aerospike/client/sdk/Session.html#query%28com.aerospike.client.sdk.DataSet%29) | [`ChainableQueryBuilder.where(...)`](https://javadoc.io/doc/com.aerospike/aerospike-client-sdk/latest/com/aerospike/client/sdk/ChainableQueryBuilder.html#where%28java.lang.String%2Cjava.lang.Object...%29) | [`ChainableQueryBuilder.execute()`](https://javadoc.io/doc/com.aerospike/aerospike-client-sdk/latest/com/aerospike/client/sdk/ChainableQueryBuilder.html#execute%28%29) | [`RecordStream.forEach(...)`](https://javadoc.io/doc/com.aerospike/aerospike-client-sdk/latest/com/aerospike/client/sdk/RecordStream.html#forEach%28java.util.function.Consumer%29) | [`RecordResult.recordOrThrow()`](https://javadoc.io/doc/com.aerospike/aerospike-client-sdk/latest/com/aerospike/client/sdk/RecordResult.html#recordOrThrow%28%29) | [`RecordStream.close()`](https://javadoc.io/doc/com.aerospike/aerospike-client-sdk/latest/com/aerospike/client/sdk/RecordStream.html#close%28%29)

```python
orders = DataSet.of("test", "orders")

stream = await session.query(orders).where(

    "$.status == 'open' and ($.price * $.qty) + $.shipping > 100.0"

).execute()

async for row in stream:

    row.record_or_raise()

stream.close()
```

> 📖 **API reference**: [`DataSet.of()`](https://aerospike-python-sdk.readthedocs.io/en/latest/api/dataset.html#aerospike%5Fsdk.dataset.DataSet.of) | [`Session.query()`](https://aerospike-python-sdk.readthedocs.io/en/latest/api/session.html#aerospike%5Fsdk.aio.session.Session.query) | [`QueryBuilder.where()`](https://aerospike-python-sdk.readthedocs.io/en/latest/api/query.html#aerospike%5Fsdk.aio.operations.query.QueryBuilder.where) | [`QueryBuilder.execute()`](https://aerospike-python-sdk.readthedocs.io/en/latest/api/query.html#aerospike%5Fsdk.aio.operations.query.QueryBuilder.execute) | [`RecordResult.record_or_raise()`](https://aerospike-python-sdk.readthedocs.io/en/latest/api/record-result.html#aerospike%5Fsdk.record%5Fresult.RecordResult.record%5For%5Fraise) | [`RecordStream.close()`](https://aerospike-python-sdk.readthedocs.io/en/latest/api/record-stream.html#aerospike%5Fsdk.record%5Fstream.RecordStream.close)

### Filter batch key reads

-   [Java](#tab-panel-6058)
-   [Python](#tab-panel-6059)

```java
DataSet users = DataSet.of("test", "users");

RecordStream stream = session.query(users.ids("u1", "u2", "u3"))

    .where("$.active == true")

    .execute();

stream.forEach(result -> {

    if (result.isOk()) {

        Record row = result.recordOrThrow();

        // Process row

    }

});

stream.close();
```

> 📖 **API reference**: [`DataSet.ids(...)`](https://javadoc.io/doc/com.aerospike/aerospike-client-sdk/latest/com/aerospike/client/sdk/DataSet.html#ids%28java.lang.String...%29) | [`Session.query(List)`](https://javadoc.io/doc/com.aerospike/aerospike-client-sdk/latest/com/aerospike/client/sdk/Session.html#query%28java.util.List%29) | [`ChainableQueryBuilder.where(...)`](https://javadoc.io/doc/com.aerospike/aerospike-client-sdk/latest/com/aerospike/client/sdk/ChainableQueryBuilder.html#where%28java.lang.String%2Cjava.lang.Object...%29) | [`ChainableQueryBuilder.execute()`](https://javadoc.io/doc/com.aerospike/aerospike-client-sdk/latest/com/aerospike/client/sdk/ChainableQueryBuilder.html#execute%28%29) | [`RecordResult.isOk()`](https://javadoc.io/doc/com.aerospike/aerospike-client-sdk/latest/com/aerospike/client/sdk/RecordResult.html#isOk%28%29) | [`RecordResult.recordOrThrow()`](https://javadoc.io/doc/com.aerospike/aerospike-client-sdk/latest/com/aerospike/client/sdk/RecordResult.html#recordOrThrow%28%29) | [`RecordStream.close()`](https://javadoc.io/doc/com.aerospike/aerospike-client-sdk/latest/com/aerospike/client/sdk/RecordStream.html#close%28%29)

```python
users = DataSet.of("test", "users")

stream = await (

    session.query(users.ids("u1", "u2", "u3"))

    .where("$.active == true")

    .execute()

)

async for row in stream:

    if row.is_ok:

        row.record_or_raise()

stream.close()
```

> 📖 **API reference**: [`DataSet.of()`](https://aerospike-python-sdk.readthedocs.io/en/latest/api/dataset.html#aerospike%5Fsdk.dataset.DataSet.of) | [`DataSet.ids()`](https://aerospike-python-sdk.readthedocs.io/en/latest/api/dataset.html#aerospike%5Fsdk.dataset.DataSet.ids) | [`Session.query()`](https://aerospike-python-sdk.readthedocs.io/en/latest/api/session.html#aerospike%5Fsdk.aio.session.Session.query) | [`QueryBuilder.where()`](https://aerospike-python-sdk.readthedocs.io/en/latest/api/query.html#aerospike%5Fsdk.aio.operations.query.QueryBuilder.where) | [`QueryBuilder.execute()`](https://aerospike-python-sdk.readthedocs.io/en/latest/api/query.html#aerospike%5Fsdk.aio.operations.query.QueryBuilder.execute) | [`RecordResult.is_ok`](https://aerospike-python-sdk.readthedocs.io/en/latest/api/record-result.html#aerospike%5Fsdk.record%5Fresult.RecordResult.is%5Fok) | [`RecordResult.record_or_raise()`](https://aerospike-python-sdk.readthedocs.io/en/latest/api/record-result.html#aerospike%5Fsdk.record%5Fresult.RecordResult.record%5For%5Fraise) | [`RecordStream.close()`](https://aerospike-python-sdk.readthedocs.io/en/latest/api/record-stream.html#aerospike%5Fsdk.record%5Fstream.RecordStream.close)

### Filter conditional writes on one key

-   [Java](#tab-panel-6060)
-   [Python](#tab-panel-6061)

```java
DataSet tasks = DataSet.of("test", "tasks");

session.update(tasks.id("task-1"))

    .bin("status").setTo("COMPLETED")

    .where("$.status == 'PENDING' and $.terminated == false")

    .execute();
```

> 📖 **API reference**: [`DataSet.id(...)`](https://javadoc.io/doc/com.aerospike/aerospike-client-sdk/latest/com/aerospike/client/sdk/DataSet.html#id%28java.lang.String%29) | [`Session.update(DataSet)`](https://javadoc.io/doc/com.aerospike/aerospike-client-sdk/latest/com/aerospike/client/sdk/Session.html#update%28com.aerospike.client.sdk.DataSet%29) | [`ChainableOperationBuilder.bin(...)`](https://javadoc.io/doc/com.aerospike/aerospike-client-sdk/latest/com/aerospike/client/sdk/ChainableOperationBuilder.html#bin%28java.lang.String%29) | [`BinBuilder.setTo(...)`](https://javadoc.io/doc/com.aerospike/aerospike-client-sdk/latest/com/aerospike/client/sdk/BinBuilder.html#setTo%28java.lang.Object%29) | [`ChainableOperationBuilder.where(...)`](https://javadoc.io/doc/com.aerospike/aerospike-client-sdk/latest/com/aerospike/client/sdk/ChainableOperationBuilder.html#where%28java.lang.String%2Cjava.lang.Object...%29) | [`ChainableOperationBuilder.execute()`](https://javadoc.io/doc/com.aerospike/aerospike-client-sdk/latest/com/aerospike/client/sdk/ChainableOperationBuilder.html#execute%28%29)

```python
tasks = DataSet.of("test", "tasks")

await (

    session.update(tasks.id("task-1"))

    .bin("status").set_to("COMPLETED")

    .where("$.status == 'PENDING' and $.terminated == false")

    .execute()

)
```

> 📖 **API reference**: [`DataSet.of()`](https://aerospike-python-sdk.readthedocs.io/en/latest/api/dataset.html#aerospike%5Fsdk.dataset.DataSet.of) | [`DataSet.id()`](https://aerospike-python-sdk.readthedocs.io/en/latest/api/dataset.html#aerospike%5Fsdk.dataset.DataSet.id) | [`Session.update()`](https://aerospike-python-sdk.readthedocs.io/en/latest/api/session.html#aerospike%5Fsdk.aio.session.Session.update) | [`WriteSegmentBuilder.bin()`](https://aerospike-python-sdk.readthedocs.io/en/latest/api/write-segment.html#aerospike%5Fsdk.aio.operations.query.WriteSegmentBuilder.bin) | [`WriteBinBuilder.set_to()`](https://aerospike-python-sdk.readthedocs.io/en/latest/api/query.html) | [`WriteSegmentBuilder.where()`](https://aerospike-python-sdk.readthedocs.io/en/latest/api/write-segment.html#aerospike%5Fsdk.aio.operations.query.WriteSegmentBuilder.where) | [`WriteSegmentBuilder.execute()`](https://aerospike-python-sdk.readthedocs.io/en/latest/api/write-segment.html#aerospike%5Fsdk.aio.operations.query.WriteSegmentBuilder.execute)

When the filter is false, the server skips the mutation for that key. Combine per-operation filters with `.defaultWhere(...)` / `.default_where(...)` on mixed batch chains — see [Batch operate with operation expressions](#batch-operate-with-operation-expressions).

### Reuse a filter template across calls

For Java, [`PreparedAel`](https://javadoc.io/doc/com.aerospike/aerospike-client-sdk/latest/com/aerospike/client/sdk/query/PreparedAel.html) stores an AEL template with `?0`, `?1`, … placeholders once and safely substitutes different bound values on each call — each value is formatted into a quoted, escaped AEL literal, so this is the recommended pattern when a bound value might not be fully trusted:

-   [Java](#tab-panel-6062)
-   [Python](#tab-panel-6063)

```java
DataSet users = DataSet.of("test", "users");

PreparedAel activeInDept = PreparedAel.prepare("$.active == true and $.department == ?0");

RecordStream engineering = session.query(users)

    .where(activeInDept, "engineering")

    .execute();

engineering.forEach(result -> {

    Record row = result.recordOrThrow();

    // Process matching rows

});

engineering.close();

RecordStream marketing = session.query(users)

    .where(activeInDept, "marketing")

    .execute();

marketing.forEach(result -> {

    Record row = result.recordOrThrow();

    // Process matching rows

});

marketing.close();
```

> 📖 **API reference**: [`DataSet.of(...)`](https://javadoc.io/doc/com.aerospike/aerospike-client-sdk/latest/com/aerospike/client/sdk/DataSet.html#of%28java.lang.String%2Cjava.lang.String%29) | [`PreparedAel.prepare(...)`](https://javadoc.io/doc/com.aerospike/aerospike-client-sdk/latest/com/aerospike/client/sdk/query/PreparedAel.html#prepare%28java.lang.String%29) | [`Session.query(DataSet)`](https://javadoc.io/doc/com.aerospike/aerospike-client-sdk/latest/com/aerospike/client/sdk/Session.html#query%28com.aerospike.client.sdk.DataSet%29) | [`ChainableQueryBuilder.where(PreparedAel, ...)`](https://javadoc.io/doc/com.aerospike/aerospike-client-sdk/latest/com/aerospike/client/sdk/ChainableQueryBuilder.html#where%28com.aerospike.client.sdk.query.PreparedAel%2Cjava.lang.Object...%29) | [`ChainableQueryBuilder.execute()`](https://javadoc.io/doc/com.aerospike/aerospike-client-sdk/latest/com/aerospike/client/sdk/ChainableQueryBuilder.html#execute%28%29) | [`RecordStream.forEach(...)`](https://javadoc.io/doc/com.aerospike/aerospike-client-sdk/latest/com/aerospike/client/sdk/RecordStream.html#forEach%28java.util.function.Consumer%29) | [`RecordResult.recordOrThrow()`](https://javadoc.io/doc/com.aerospike/aerospike-client-sdk/latest/com/aerospike/client/sdk/RecordResult.html#recordOrThrow%28%29) | [`RecordStream.close()`](https://javadoc.io/doc/com.aerospike/aerospike-client-sdk/latest/com/aerospike/client/sdk/RecordStream.html#close%28%29)

```python
users = DataSet.of("test", "users")

# No prepared-template type exists in Python — reuse a plain string and

# apply the same printf-style substitution shown above on each call.

# Only pass trusted values this way; validate or escape anything else first.

template = "$.active == true and $.department == %s"

stream_eng = await session.query(users).where(template, "engineering").execute()

async for row in stream_eng:

    row.record_or_raise()

    # Process matching rows

stream_eng.close()

stream_mkt = await session.query(users).where(template, "marketing").execute()

async for row in stream_mkt:

    row.record_or_raise()

    # Process matching rows

stream_mkt.close()
```

> 📖 **API reference**: [`DataSet.of()`](https://aerospike-python-sdk.readthedocs.io/en/latest/api/dataset.html#aerospike%5Fsdk.dataset.DataSet.of) | [`Session.query()`](https://aerospike-python-sdk.readthedocs.io/en/latest/api/session.html#aerospike%5Fsdk.aio.session.Session.query) | [`QueryBuilder.where()`](https://aerospike-python-sdk.readthedocs.io/en/latest/api/query.html#aerospike%5Fsdk.aio.operations.query.QueryBuilder.where) | [`QueryBuilder.execute()`](https://aerospike-python-sdk.readthedocs.io/en/latest/api/query.html#aerospike%5Fsdk.aio.operations.query.QueryBuilder.execute) | [`RecordResult.record_or_raise()`](https://aerospike-python-sdk.readthedocs.io/en/latest/api/record-result.html#aerospike%5Fsdk.record%5Fresult.RecordResult.record%5For%5Fraise) | [`RecordStream.close()`](https://aerospike-python-sdk.readthedocs.io/en/latest/api/record-stream.html#aerospike%5Fsdk.record%5Fstream.RecordStream.close)

## Read operation expressions with `selectFrom`

Use `selectFrom` / `select_from` to evaluate an AEL expression server-side and return the result under the bin name you pass to `.bin(...)`. The output bin does not need to exist on the record beforehand.

### Single-key read

-   [Java](#tab-panel-6064)
-   [Python](#tab-panel-6065)

```java
DataSet products = DataSet.of("test", "products");

Record rec = session.query(products.id("sku-42"))

    .bin("lineTotal").selectFrom("$.price * $.qty.toFloat()")

    .execute()

    .getFirstRecord();

// price and qty are FLOAT bins in typical order fixtures

double lineTotal = rec.getDouble("lineTotal");
```

> 📖 **API reference**: [`DataSet.id(...)`](https://javadoc.io/doc/com.aerospike/aerospike-client-sdk/latest/com/aerospike/client/sdk/DataSet.html#id%28java.lang.String%29) | [`Session.query(Key)`](https://javadoc.io/doc/com.aerospike/aerospike-client-sdk/latest/com/aerospike/client/sdk/Session.html#query%28com.aerospike.client.sdk.Key%29) | [`ChainableQueryBuilder.bin(...)`](https://javadoc.io/doc/com.aerospike/aerospike-client-sdk/latest/com/aerospike/client/sdk/ChainableQueryBuilder.html#bin%28java.lang.String%29) | [`QueryBinBuilder.selectFrom(...)`](https://javadoc.io/doc/com.aerospike/aerospike-client-sdk/latest/com/aerospike/client/sdk/QueryBinBuilder.html#selectFrom%28java.lang.String%29) | [`ChainableQueryBuilder.execute()`](https://javadoc.io/doc/com.aerospike/aerospike-client-sdk/latest/com/aerospike/client/sdk/ChainableQueryBuilder.html#execute%28%29) | [`RecordStream.getFirstRecord()`](https://javadoc.io/doc/com.aerospike/aerospike-client-sdk/latest/com/aerospike/client/sdk/RecordStream.html#getFirstRecord%28%29) | [`Record.getDouble(...)`](https://javadoc.io/doc/com.aerospike/aerospike-client-sdk/latest/com/aerospike/client/sdk/Record.html#getDouble%28java.lang.String%29)

```python
products = DataSet.of("test", "products")

stream = await (

    session.query(products.id("sku-42"))

    .bin("lineTotal").select_from("$.price * $.qty.toFloat()")

    .execute()

)

row = await stream.first_or_raise()

line_total = row.record_or_raise().bins["lineTotal"]

stream.close()
```

> 📖 **API reference**: [`DataSet.id()`](https://aerospike-python-sdk.readthedocs.io/en/latest/api/dataset.html#aerospike%5Fsdk.dataset.DataSet.id) | [`Session.query()`](https://aerospike-python-sdk.readthedocs.io/en/latest/api/session.html#aerospike%5Fsdk.aio.session.Session.query) | [`QueryBuilder.bin()`](https://aerospike-python-sdk.readthedocs.io/en/latest/api/query.html#aerospike%5Fsdk.aio.operations.query.QueryBuilder.bin) | [`QueryBinBuilder.select_from()`](https://aerospike-python-sdk.readthedocs.io/en/latest/api/query.html) | [`QueryBuilder.execute()`](https://aerospike-python-sdk.readthedocs.io/en/latest/api/query.html#aerospike%5Fsdk.aio.operations.query.QueryBuilder.execute) | [`RecordStream.first_or_raise()`](https://aerospike-python-sdk.readthedocs.io/en/latest/api/record-stream.html#aerospike%5Fsdk.record%5Fstream.RecordStream.first%5For%5Fraise) | [`RecordResult.record_or_raise()`](https://aerospike-python-sdk.readthedocs.io/en/latest/api/record-result.html#aerospike%5Fsdk.record%5Fresult.RecordResult.record%5For%5Fraise) | [`RecordStream.close()`](https://aerospike-python-sdk.readthedocs.io/en/latest/api/record-stream.html#aerospike%5Fsdk.record%5Fstream.RecordStream.close)

### Batch key read with projection

-   [Java](#tab-panel-6066)
-   [Python](#tab-panel-6067)

```java
DataSet products = DataSet.of("test", "products");

RecordStream stream = session.query(products.ids("sku-1", "sku-2"))

    .bin("lineTotal").selectFrom("$.price * $.qty.toFloat()")

    .execute();

stream.forEach(result -> {

    if (result.isOk()) {

        Record rec = result.recordOrThrow();

        // rec.getDouble("lineTotal")

    }

});

stream.close();
```

> 📖 **API reference**: [`DataSet.ids(...)`](https://javadoc.io/doc/com.aerospike/aerospike-client-sdk/latest/com/aerospike/client/sdk/DataSet.html#ids%28java.lang.String...%29) | [`Session.query(List)`](https://javadoc.io/doc/com.aerospike/aerospike-client-sdk/latest/com/aerospike/client/sdk/Session.html#query%28java.util.List%29) | [`ChainableQueryBuilder.bin(...)`](https://javadoc.io/doc/com.aerospike/aerospike-client-sdk/latest/com/aerospike/client/sdk/ChainableQueryBuilder.html#bin%28java.lang.String%29) | [`QueryBinBuilder.selectFrom(...)`](https://javadoc.io/doc/com.aerospike/aerospike-client-sdk/latest/com/aerospike/client/sdk/QueryBinBuilder.html#selectFrom%28java.lang.String%29) | [`ChainableQueryBuilder.execute()`](https://javadoc.io/doc/com.aerospike/aerospike-client-sdk/latest/com/aerospike/client/sdk/ChainableQueryBuilder.html#execute%28%29) | [`RecordResult.isOk()`](https://javadoc.io/doc/com.aerospike/aerospike-client-sdk/latest/com/aerospike/client/sdk/RecordResult.html#isOk%28%29) | [`RecordStream.close()`](https://javadoc.io/doc/com.aerospike/aerospike-client-sdk/latest/com/aerospike/client/sdk/RecordStream.html#close%28%29)

```python
products = DataSet.of("test", "products")

stream = await (

    session.query(products.ids("sku-1", "sku-2"))

    .bin("lineTotal").select_from("$.price * $.qty.toFloat()")

    .execute()

)

async for row in stream:

    if row.is_ok:

        row.record_or_raise()

stream.close()
```

> 📖 **API reference**: [`DataSet.of()`](https://aerospike-python-sdk.readthedocs.io/en/latest/api/dataset.html#aerospike%5Fsdk.dataset.DataSet.of) | [`DataSet.ids()`](https://aerospike-python-sdk.readthedocs.io/en/latest/api/dataset.html#aerospike%5Fsdk.dataset.DataSet.ids) | [`Session.query()`](https://aerospike-python-sdk.readthedocs.io/en/latest/api/session.html#aerospike%5Fsdk.aio.session.Session.query) | [`QueryBuilder.bin()`](https://aerospike-python-sdk.readthedocs.io/en/latest/api/query.html#aerospike%5Fsdk.aio.operations.query.QueryBuilder.bin) | [`QueryBinBuilder.select_from()`](https://aerospike-python-sdk.readthedocs.io/en/latest/api/query.html) | [`QueryBuilder.execute()`](https://aerospike-python-sdk.readthedocs.io/en/latest/api/query.html#aerospike%5Fsdk.aio.operations.query.QueryBuilder.execute) | [`RecordResult.is_ok`](https://aerospike-python-sdk.readthedocs.io/en/latest/api/record-result.html#aerospike%5Fsdk.record%5Fresult.RecordResult.is%5Fok) | [`RecordStream.close()`](https://aerospike-python-sdk.readthedocs.io/en/latest/api/record-stream.html#aerospike%5Fsdk.record%5Fstream.RecordStream.close)

### Project during a filtered set query

Both Java and Python support read-side operation expressions (`selectFrom` / `select_from`) on filtered set queries, not just single-key and batch key reads.

-   [Java](#tab-panel-6068)
-   [Python](#tab-panel-6069)

```java
DataSet users = DataSet.of("test", "users");

RecordStream stream = session.query(users)

    .where("$.status == 'active'")

    .bin("ageIn10Years").selectFrom("$.age + 10")

    .execute();

stream.forEach(result -> {

    Record row = result.recordOrThrow();

    // Process matching rows

});

stream.close();
```

> 📖 **API reference**: [`DataSet.of(...)`](https://javadoc.io/doc/com.aerospike/aerospike-client-sdk/latest/com/aerospike/client/sdk/DataSet.html#of%28java.lang.String%2Cjava.lang.String%29) | [`Session.query(DataSet)`](https://javadoc.io/doc/com.aerospike/aerospike-client-sdk/latest/com/aerospike/client/sdk/Session.html#query%28com.aerospike.client.sdk.DataSet%29) | [`ChainableQueryBuilder.where(...)`](https://javadoc.io/doc/com.aerospike/aerospike-client-sdk/latest/com/aerospike/client/sdk/ChainableQueryBuilder.html#where%28java.lang.String%2Cjava.lang.Object...%29) | [`QueryBinBuilder.selectFrom(...)`](https://javadoc.io/doc/com.aerospike/aerospike-client-sdk/latest/com/aerospike/client/sdk/QueryBinBuilder.html#selectFrom%28java.lang.String%29) | [`ChainableQueryBuilder.execute()`](https://javadoc.io/doc/com.aerospike/aerospike-client-sdk/latest/com/aerospike/client/sdk/ChainableQueryBuilder.html#execute%28%29) | [`RecordStream.close()`](https://javadoc.io/doc/com.aerospike/aerospike-client-sdk/latest/com/aerospike/client/sdk/RecordStream.html#close%28%29)

```python
users = DataSet.of("test", "users")

stream = await (

    session.query(users)

    .where("$.status == 'active'")

    .bin("ageIn10Years").select_from("$.age + 10")

    .execute()

)

async for row in stream:

    row.record_or_raise()

stream.close()
```

> 📖 **API reference**: [`DataSet.of()`](https://aerospike-python-sdk.readthedocs.io/en/latest/api/dataset.html#aerospike%5Fsdk.dataset.DataSet.of) | [`Session.query()`](https://aerospike-python-sdk.readthedocs.io/en/latest/api/session.html#aerospike%5Fsdk.aio.session.Session.query) | [`QueryBuilder.where()`](https://aerospike-python-sdk.readthedocs.io/en/latest/api/query.html#aerospike%5Fsdk.aio.operations.query.QueryBuilder.where) | [`QueryBuilder.bin()`](https://aerospike-python-sdk.readthedocs.io/en/latest/api/query.html#aerospike%5Fsdk.aio.operations.query.QueryBuilder.bin) | [`QueryBinBuilder.select_from()`](https://aerospike-python-sdk.readthedocs.io/en/latest/api/query.html) | [`QueryBuilder.execute()`](https://aerospike-python-sdk.readthedocs.io/en/latest/api/query.html#aerospike%5Fsdk.aio.operations.query.QueryBuilder.execute) | [`RecordResult.record_or_raise()`](https://aerospike-python-sdk.readthedocs.io/en/latest/api/record-result.html#aerospike%5Fsdk.record%5Fresult.RecordResult.record%5For%5Fraise) | [`RecordStream.close()`](https://aerospike-python-sdk.readthedocs.io/en/latest/api/record-stream.html#aerospike%5Fsdk.record%5Fstream.RecordStream.close)

See [Project reads with ops projection](https://aerospike.com/docs/develop/client/sdk/usage/read#project-reads-with-ops-projection) for platform differences and server version notes.

::: write expressions are read-only in query projection
`selectFrom` / `select_from` are read operations. Do not use `upsertFrom`, `insertFrom`, or `updateFrom` in a query projection chain. Queries are read-only on the server. Persist computed values with `upsert`, `update`, or batch write builders instead.
:::

### Tolerate evaluation failures on read

When an expression cannot evaluate (for example, a missing bin or divide-by-zero), the operation fails unless you opt out. In raw AEL, attach the `:NO_FAIL` postfix to path terminals (see [Postfix flags](https://aerospike.com/docs/develop/client/sdk/concepts/ael/reference/control-and-flags#postfix-flags)). The Developer SDK exposes the same behavior through builder options:

A `.where(...)` filter expression that fails to evaluate behaves differently: there is no `ignoreEvalFailure()` / `ignore_eval_failure=True` option for filters. Instead, the record is excluded from the result set with no error, as if the filter had evaluated to `false`.

-   [Java](#tab-panel-6070)
-   [Python](#tab-panel-6071)

```java
DataSet users = DataSet.of("test", "users");

RecordStream stream = session.query(users.id("user-1"))

    .bin("ratio").selectFrom("$.numerator:INT / $.denominator", opt -> opt.ignoreEvalFailure())

    .execute();

stream.close();
```

> 📖 **API reference**: [`DataSet.id(...)`](https://javadoc.io/doc/com.aerospike/aerospike-client-sdk/latest/com/aerospike/client/sdk/DataSet.html#id%28java.lang.String%29) | [`Session.query(Key)`](https://javadoc.io/doc/com.aerospike/aerospike-client-sdk/latest/com/aerospike/client/sdk/Session.html#query%28com.aerospike.client.sdk.Key%29) | [`QueryBinBuilder.selectFrom(...)`](https://javadoc.io/doc/com.aerospike/aerospike-client-sdk/latest/com/aerospike/client/sdk/QueryBinBuilder.html#selectFrom%28java.lang.String%29) | [`ChainableQueryBuilder.execute()`](https://javadoc.io/doc/com.aerospike/aerospike-client-sdk/latest/com/aerospike/client/sdk/ChainableQueryBuilder.html#execute%28%29) | [`RecordStream.close()`](https://javadoc.io/doc/com.aerospike/aerospike-client-sdk/latest/com/aerospike/client/sdk/RecordStream.html#close%28%29)

```python
users = DataSet.of("test", "users")

stream = await (

    session.query(users.id("user-1"))

    .bin("ratio").select_from("$.numerator:INT / $.denominator", ignore_eval_failure=True)

    .execute()

)

stream.close()
```

> 📖 **API reference**: [`DataSet.id()`](https://aerospike-python-sdk.readthedocs.io/en/latest/api/dataset.html#aerospike%5Fsdk.dataset.DataSet.id) | [`Session.query()`](https://aerospike-python-sdk.readthedocs.io/en/latest/api/session.html#aerospike%5Fsdk.aio.session.Session.query) | [`QueryBinBuilder.select_from()`](https://aerospike-python-sdk.readthedocs.io/en/latest/api/query.html) | [`QueryBuilder.execute()`](https://aerospike-python-sdk.readthedocs.io/en/latest/api/query.html#aerospike%5Fsdk.aio.operations.query.QueryBuilder.execute) | [`RecordStream.close()`](https://aerospike-python-sdk.readthedocs.io/en/latest/api/record-stream.html#aerospike%5Fsdk.record%5Fstream.RecordStream.close)

On read projection, `ignoreEvalFailure()` / `ignore_eval_failure=True` prevents the request from failing when the expression cannot evaluate. Java omits the record from results; Python returns `None` for the projected bin.

## Write operation expressions

Write-side AEL computes a value on the server and stores it in the target bin. Choose the method by existence semantics:

| Method | Behavior if bin exists | Behavior if bin missing |
| --- | --- | --- |
| `upsertFrom` / `upsert_from` | Overwrites | Creates |
| `insertFrom` / `insert_from` | Fails ([`BIN_EXISTS_ERROR`](https://aerospike.com/docs/develop/client/sdk/concepts/errors#exception-hierarchy)) | Creates |
| `updateFrom` / `update_from` | Overwrites | Fails ([`BIN_NOT_FOUND`](https://aerospike.com/docs/develop/client/sdk/concepts/errors#exception-hierarchy)) |

### Single-record write

-   [Java](#tab-panel-6072)
-   [Python](#tab-panel-6073)

```java
DataSet orders = DataSet.of("test", "orders");

session.upsert(orders.id("order-1"))

    .bin("lineTotal").upsertFrom("$.price:INT * $.qty")

    .bin("discount").insertFrom("$.coupon_value:INT")

    .execute();
```

> 📖 **API reference**: [`DataSet.id(...)`](https://javadoc.io/doc/com.aerospike/aerospike-client-sdk/latest/com/aerospike/client/sdk/DataSet.html#id%28java.lang.String%29) | [`Session.upsert(Key)`](https://javadoc.io/doc/com.aerospike/aerospike-client-sdk/latest/com/aerospike/client/sdk/Session.html#upsert%28com.aerospike.client.sdk.Key%29) | [`ChainableOperationBuilder.bin(...)`](https://javadoc.io/doc/com.aerospike/aerospike-client-sdk/latest/com/aerospike/client/sdk/ChainableOperationBuilder.html#bin%28java.lang.String%29) | [`BinBuilder.upsertFrom(...)`](https://javadoc.io/doc/com.aerospike/aerospike-client-sdk/latest/com/aerospike/client/sdk/BinBuilder.html#upsertFrom%28java.lang.String%29) | [`BinBuilder.insertFrom(...)`](https://javadoc.io/doc/com.aerospike/aerospike-client-sdk/latest/com/aerospike/client/sdk/BinBuilder.html#insertFrom%28java.lang.String%29) | [`ChainableOperationBuilder.execute()`](https://javadoc.io/doc/com.aerospike/aerospike-client-sdk/latest/com/aerospike/client/sdk/ChainableOperationBuilder.html#execute%28%29)

```python
orders = DataSet.of("test", "orders")

await (

    session.upsert(orders.id("order-1"))

    .bin("lineTotal").upsert_from("$.price:INT * $.qty")

    .bin("discount").insert_from("$.coupon_value:INT")

    .execute()

)
```

> 📖 **API reference**: [`DataSet.of()`](https://aerospike-python-sdk.readthedocs.io/en/latest/api/dataset.html#aerospike%5Fsdk.dataset.DataSet.of) | [`DataSet.id()`](https://aerospike-python-sdk.readthedocs.io/en/latest/api/dataset.html#aerospike%5Fsdk.dataset.DataSet.id) | [`Session.upsert()`](https://aerospike-python-sdk.readthedocs.io/en/latest/api/session.html#aerospike%5Fsdk.aio.session.Session.upsert) | [`WriteSegmentBuilder.bin()`](https://aerospike-python-sdk.readthedocs.io/en/latest/api/write-segment.html#aerospike%5Fsdk.aio.operations.query.WriteSegmentBuilder.bin) | [`WriteBinBuilder.upsert_from()`](https://aerospike-python-sdk.readthedocs.io/en/latest/api/query.html) | [`WriteBinBuilder.insert_from()`](https://aerospike-python-sdk.readthedocs.io/en/latest/api/query.html) | [`WriteSegmentBuilder.execute()`](https://aerospike-python-sdk.readthedocs.io/en/latest/api/write-segment.html#aerospike%5Fsdk.aio.operations.query.WriteSegmentBuilder.execute)

### Write options

-   [Java](#tab-panel-6074)
-   [Python](#tab-panel-6075)

```java
DataSet orders = DataSet.of("test", "orders");

session.upsert(orders.id("order-1"))

    .bin("discount").upsertFrom("$.coupon_value:INT", opt -> opt

        .ignoreEvalFailure()   // skip mutation for this key when expression cannot evaluate

        .deleteIfNull())       // remove bin when expression returns null

    .bin("bonus").insertFrom("$.base * 2", opt -> opt

        .ignoreOpFailure())    // skip when bin already exists (insertFrom)

    .execute();
```

> 📖 **API reference**: [`Session.upsert(Key)`](https://javadoc.io/doc/com.aerospike/aerospike-client-sdk/latest/com/aerospike/client/sdk/Session.html#upsert%28com.aerospike.client.sdk.Key%29) | [`BinBuilder.upsertFrom(...)`](https://javadoc.io/doc/com.aerospike/aerospike-client-sdk/latest/com/aerospike/client/sdk/BinBuilder.html#upsertFrom%28java.lang.String%29) | [`BinBuilder.insertFrom(...)`](https://javadoc.io/doc/com.aerospike/aerospike-client-sdk/latest/com/aerospike/client/sdk/BinBuilder.html#insertFrom%28java.lang.String%29) | [`ChainableOperationBuilder.execute()`](https://javadoc.io/doc/com.aerospike/aerospike-client-sdk/latest/com/aerospike/client/sdk/ChainableOperationBuilder.html#execute%28%29)

```python
orders = DataSet.of("test", "orders")

await (

    session.upsert(orders.id("order-1"))

    .bin("discount").upsert_from(

        "$.coupon_value:INT",

        ignore_eval_failure=True,

        delete_if_null=True,

    )

    .bin("bonus").insert_from("$.base * 2", ignore_op_failure=True)

    .execute()

)
```

> 📖 **API reference**: [`Session.upsert()`](https://aerospike-python-sdk.readthedocs.io/en/latest/api/session.html#aerospike%5Fsdk.aio.session.Session.upsert) | [`WriteBinBuilder.upsert_from()`](https://aerospike-python-sdk.readthedocs.io/en/latest/api/query.html) | [`WriteBinBuilder.insert_from()`](https://aerospike-python-sdk.readthedocs.io/en/latest/api/query.html) | [`WriteSegmentBuilder.execute()`](https://aerospike-python-sdk.readthedocs.io/en/latest/api/write-segment.html#aerospike%5Fsdk.aio.operations.query.WriteSegmentBuilder.execute)

On writes, `ignoreEvalFailure()` / `ignore_eval_failure=True` skips the mutation for that key when the expression cannot evaluate (for example, a missing bin or divide-by-zero). Use `ignoreOpFailure()` / `ignore_op_failure=True` on `insertFrom` / `insert_from` to skip when the target bin already exists.

Combine write expressions with a filter so the mutation runs only when the record matches:

-   [Java](#tab-panel-6076)
-   [Python](#tab-panel-6077)

```java
DataSet orders = DataSet.of("test", "orders");

session.upsert(orders.id("order-1"))

    .bin("lineTotal").upsertFrom("$.price:INT * $.qty")

    .where("$.status == 'open'")

    .execute();
```

> 📖 **API reference**: [`Session.upsert(Key)`](https://javadoc.io/doc/com.aerospike/aerospike-client-sdk/latest/com/aerospike/client/sdk/Session.html#upsert%28com.aerospike.client.sdk.Key%29) | [`BinBuilder.upsertFrom(...)`](https://javadoc.io/doc/com.aerospike/aerospike-client-sdk/latest/com/aerospike/client/sdk/BinBuilder.html#upsertFrom%28java.lang.String%29) | [`ChainableOperationBuilder.where(...)`](https://javadoc.io/doc/com.aerospike/aerospike-client-sdk/latest/com/aerospike/client/sdk/ChainableOperationBuilder.html#where%28java.lang.String%2Cjava.lang.Object...%29) | [`ChainableOperationBuilder.execute()`](https://javadoc.io/doc/com.aerospike/aerospike-client-sdk/latest/com/aerospike/client/sdk/ChainableOperationBuilder.html#execute%28%29)

```python
orders = DataSet.of("test", "orders")

await (

    session.upsert(orders.id("order-1"))

    .bin("lineTotal").upsert_from("$.price:INT * $.qty")

    .where("$.status == 'open'")

    .execute()

)
```

> 📖 **API reference**: [`Session.upsert()`](https://aerospike-python-sdk.readthedocs.io/en/latest/api/session.html#aerospike%5Fsdk.aio.session.Session.upsert) | [`WriteBinBuilder.upsert_from()`](https://aerospike-python-sdk.readthedocs.io/en/latest/api/query.html) | [`WriteSegmentBuilder.where()`](https://aerospike-python-sdk.readthedocs.io/en/latest/api/write-segment.html#aerospike%5Fsdk.aio.operations.query.WriteSegmentBuilder.where) | [`WriteSegmentBuilder.execute()`](https://aerospike-python-sdk.readthedocs.io/en/latest/api/write-segment.html#aerospike%5Fsdk.aio.operations.query.WriteSegmentBuilder.execute)

## Batch operate with operation expressions

Chain multiple keys in one request. Each key can mix filters, read projections, and write expressions.

`defaultWhere` / `default_where` applies only to keys that don’t have their own `.where(...)` clause. In the example below, `sku-1` has its own filter (`$.on_hand:INT > $.available`), so `defaultWhere` does **not** apply to it. Only `sku-2`, which has no per-key filter, is gated by `$.active == true`.

-   [Java](#tab-panel-6078)
-   [Python](#tab-panel-6079)

```java
DataSet inventory = DataSet.of("test", "inventory");

session

    .upsert(inventory.id("sku-1"))

        .bin("reserved").upsertFrom("$.on_hand:INT - $.available")

        .where("$.on_hand:INT > $.available")

    .upsert(inventory.id("sku-2"))

        .bin("reserved").upsertFrom("$.on_hand:INT - $.available")

    .defaultWhere("$.active == true")

    .execute();
```

> 📖 **API reference**: [`DataSet.id(...)`](https://javadoc.io/doc/com.aerospike/aerospike-client-sdk/latest/com/aerospike/client/sdk/DataSet.html#id%28java.lang.String%29) | [`Session.upsert(Key)`](https://javadoc.io/doc/com.aerospike/aerospike-client-sdk/latest/com/aerospike/client/sdk/Session.html#upsert%28com.aerospike.client.sdk.Key%29) | [`BinBuilder.upsertFrom(...)`](https://javadoc.io/doc/com.aerospike/aerospike-client-sdk/latest/com/aerospike/client/sdk/BinBuilder.html#upsertFrom%28java.lang.String%29) | [`ChainableOperationBuilder.where(...)`](https://javadoc.io/doc/com.aerospike/aerospike-client-sdk/latest/com/aerospike/client/sdk/ChainableOperationBuilder.html#where%28java.lang.String%2Cjava.lang.Object...%29) | [`ChainableOperationBuilder.defaultWhere(...)`](https://javadoc.io/doc/com.aerospike/aerospike-client-sdk/latest/com/aerospike/client/sdk/ChainableOperationBuilder.html#defaultWhere%28java.lang.String%29) | [`ChainableOperationBuilder.execute()`](https://javadoc.io/doc/com.aerospike/aerospike-client-sdk/latest/com/aerospike/client/sdk/ChainableOperationBuilder.html#execute%28%29)

```python
inventory = DataSet.of("test", "inventory")

await (

    session.upsert(inventory.id("sku-1"))

        .bin("reserved").upsert_from("$.on_hand:INT - $.available")

        .where("$.on_hand:INT > $.available")

    .upsert(inventory.id("sku-2"))

        .bin("reserved").upsert_from("$.on_hand:INT - $.available")

    .default_where("$.active == true")

    .execute()

)
```

> 📖 **API reference**: [`DataSet.of()`](https://aerospike-python-sdk.readthedocs.io/en/latest/api/dataset.html#aerospike%5Fsdk.dataset.DataSet.of) | [`DataSet.id()`](https://aerospike-python-sdk.readthedocs.io/en/latest/api/dataset.html#aerospike%5Fsdk.dataset.DataSet.id) | [`Session.upsert()`](https://aerospike-python-sdk.readthedocs.io/en/latest/api/session.html#aerospike%5Fsdk.aio.session.Session.upsert) | [`WriteBinBuilder.upsert_from()`](https://aerospike-python-sdk.readthedocs.io/en/latest/api/query.html) | [`WriteSegmentBuilder.where()`](https://aerospike-python-sdk.readthedocs.io/en/latest/api/write-segment.html#aerospike%5Fsdk.aio.operations.query.WriteSegmentBuilder.where) | [`QueryBuilder.default_where()`](https://aerospike-python-sdk.readthedocs.io/en/latest/api/query.html#aerospike%5Fsdk.aio.operations.query.QueryBuilder.default_where) | [`WriteSegmentBuilder.execute()`](https://aerospike-python-sdk.readthedocs.io/en/latest/api/write-segment.html#aerospike%5Fsdk.aio.operations.query.WriteSegmentBuilder.execute)

### Batch read with computed projection

-   [Java](#tab-panel-6080)
-   [Python](#tab-panel-6081)

```java
DataSet inventory = DataSet.of("test", "inventory");

RecordStream stream = session.query(inventory.ids("sku-1", "sku-2"))

    .bin("availablePct").selectFrom("($.available * 100) / $.on_hand")

    .execute();

stream.close();
```

> 📖 **API reference**: [`DataSet.ids(...)`](https://javadoc.io/doc/com.aerospike/aerospike-client-sdk/latest/com/aerospike/client/sdk/DataSet.html#ids%28java.lang.String...%29) | [`Session.query(List)`](https://javadoc.io/doc/com.aerospike/aerospike-client-sdk/latest/com/aerospike/client/sdk/Session.html#query%28java.util.List%29) | [`QueryBinBuilder.selectFrom(...)`](https://javadoc.io/doc/com.aerospike/aerospike-client-sdk/latest/com/aerospike/client/sdk/QueryBinBuilder.html#selectFrom%28java.lang.String%29) | [`ChainableQueryBuilder.execute()`](https://javadoc.io/doc/com.aerospike/aerospike-client-sdk/latest/com/aerospike/client/sdk/ChainableQueryBuilder.html#execute%28%29) | [`RecordStream.close()`](https://javadoc.io/doc/com.aerospike/aerospike-client-sdk/latest/com/aerospike/client/sdk/RecordStream.html#close%28%29)

```python
inventory = DataSet.of("test", "inventory")

stream = await (

    session.query(inventory.ids("sku-1", "sku-2"))

    .bin("availablePct").select_from("($.available * 100) / $.on_hand")

    .execute()

)

stream.close()
```

> 📖 **API reference**: [`DataSet.of()`](https://aerospike-python-sdk.readthedocs.io/en/latest/api/dataset.html#aerospike%5Fsdk.dataset.DataSet.of) | [`DataSet.ids()`](https://aerospike-python-sdk.readthedocs.io/en/latest/api/dataset.html#aerospike%5Fsdk.dataset.DataSet.ids) | [`Session.query()`](https://aerospike-python-sdk.readthedocs.io/en/latest/api/session.html#aerospike%5Fsdk.aio.session.Session.query) | [`QueryBinBuilder.select_from()`](https://aerospike-python-sdk.readthedocs.io/en/latest/api/query.html) | [`QueryBuilder.execute()`](https://aerospike-python-sdk.readthedocs.io/en/latest/api/query.html#aerospike%5Fsdk.aio.operations.query.QueryBuilder.execute) | [`RecordStream.close()`](https://aerospike-python-sdk.readthedocs.io/en/latest/api/record-stream.html#aerospike%5Fsdk.record%5Fstream.RecordStream.close)

See [Batch operations](https://aerospike.com/docs/develop/client/sdk/usage/batch) for mixed read/write/delete chains and partial-failure handling.

## Verify your expressions

[Aerospike Voyager](https://aerospike.com/download/voyager/) is the recommended way to build and verify AEL text: author the expression against real data, see errors immediately, and copy the working AEL string directly into your Java or Python code.

To verify an expression from within application code:

1.  Start with a filter-only query (`.where(...)`) on a small test set to confirm Boolean logic before adding operation expressions.
2.  For read projections, read back the virtual bin and compare against an expected value computed in application code.
3.  For write expressions, query the persisted bin after `execute()` completes.
4.  If a filter matches zero rows unexpectedly, check [Type consistency in `let` bindings](https://aerospike.com/docs/develop/client/sdk/concepts/ael/reference/control-and-flags#type-consistency-in-let-bindings) (`let` bindings and mixed INT/FLOAT arithmetic can return zero matches with no error).

The server parses AEL text as part of each request; there is no local syntax check before sending. A malformed expression comes back as an `AerospikeError` from that request — typically `ResultCode.PARAMETER_ERROR` for a syntax error, or `ResultCode.OP_NOT_APPLICABLE` on a cluster that doesn’t support server-side AEL compilation. See [Handle errors gracefully](https://aerospike.com/docs/develop/client/sdk/concepts/errors).

## Troubleshoot

| Symptom | Likely cause | Fix |
| --- | --- | --- |
| Filter returns zero rows | INT/FLOAT type mismatch in the expression | Use matching literal types (for example `100.0` for FLOAT bins) or `.toFloat()` / `.toInt()` |
| `AerospikeError` with `ResultCode.PARAMETER_ERROR` | Malformed AEL syntax; the server parses AEL text as part of the request and returns the error | Use `==` for equality; use [Aerospike Voyager](https://aerospike.com/download/voyager/) to quickly test and validate AEL syntax, then paste it into your code |
| Write expression rejected in a query projection chain | Write-side methods (`upsertFrom`, `insertFrom`, `updateFrom`) aren’t valid on query commands | Use `session.upsert(...)`, batch upsert, or `update` builders to persist computed values |
| Write expression skipped for a key | Filter evaluated false for that record | Confirm `.where(...)` logic; check per-key filters vs `.defaultWhere(...)` on batch chains |

## API reference summary

| Method | Description | API reference |
| --- | --- | --- |
| `.where(...)` | Attach a Boolean AEL filter | [Java](https://javadoc.io/doc/com.aerospike/aerospike-client-sdk/latest/com/aerospike/client/sdk/ChainableQueryBuilder.html#where%28java.lang.String%2Cjava.lang.Object...%29) | [Python](https://aerospike-python-sdk.readthedocs.io/en/latest/api/query.html#aerospike_sdk.aio.operations.query.QueryBuilder.where) |
| `.selectFrom(...)` / `.select_from(...)` | Read-side operation expression (projection) | [Java](https://javadoc.io/doc/com.aerospike/aerospike-client-sdk/latest/com/aerospike/client/sdk/QueryBinBuilder.html#selectFrom%28java.lang.String%29) | [Python](https://aerospike-python-sdk.readthedocs.io/en/latest/api/query.html) |
| `.upsertFrom(...)` / `.upsert_from(...)` | Write expression; create or overwrite bin | [Java](https://javadoc.io/doc/com.aerospike/aerospike-client-sdk/latest/com/aerospike/client/sdk/BinBuilder.html#upsertFrom%28java.lang.String%29) | [Python](https://aerospike-python-sdk.readthedocs.io/en/latest/api/query.html) |
| `.insertFrom(...)` / `.insert_from(...)` | Write expression; create bin only | [Java](https://javadoc.io/doc/com.aerospike/aerospike-client-sdk/latest/com/aerospike/client/sdk/BinBuilder.html#insertFrom%28java.lang.String%29) | [Python](https://aerospike-python-sdk.readthedocs.io/en/latest/api/query.html) |
| `.updateFrom(...)` / `.update_from(...)` | Write expression; update existing bin only | [Java](https://javadoc.io/doc/com.aerospike/aerospike-client-sdk/latest/com/aerospike/client/sdk/BinBuilder.html#updateFrom%28java.lang.String%29) | [Python](https://aerospike-python-sdk.readthedocs.io/en/latest/api/query.html) |
| `.defaultWhere(...)` / `.default_where(...)` | Default filter for mixed batch chains | [Java](https://javadoc.io/doc/com.aerospike/aerospike-client-sdk/latest/com/aerospike/client/sdk/ChainableOperationBuilder.html#defaultWhere%28java.lang.String%29) | [Python](https://aerospike-python-sdk.readthedocs.io/en/latest/api/query.html#aerospike_sdk.aio.operations.query.QueryBuilder.default_where) |
| `PreparedAel` (Java only) | Reusable AEL template with safe `?0`, `?1`, … placeholder binding | [Java](https://javadoc.io/doc/com.aerospike/aerospike-client-sdk/latest/com/aerospike/client/sdk/query/PreparedAel.html) |

## Next steps

AEL reference

Canonical grammar, CDT path patterns, metadata functions, and `let` / `when` syntax.

[AEL reference →](https://aerospike.com/docs/develop/client/sdk/concepts/ael/reference)

AEL overview

Comparison operators, nested data access, and performance tips for filters.

[Overview →](https://aerospike.com/docs/develop/client/sdk/concepts/ael)

Query records

Set queries, limits, bin projection, and streaming results.

[Query records →](https://aerospike.com/docs/develop/client/sdk/usage/query)

Read records

Ops projection with `selectFrom` and CDT path reads.

[Read records →](https://aerospike.com/docs/develop/client/sdk/usage/read)