# Overview

Learn how to query Aerospike using the Developer SDK’s Aerospike Expression Language (AEL), a readable, intuitive syntax for filtering records.

## What is AEL?

AEL lets you write filter expressions using natural, readable syntax instead of complex builder patterns:

-   [Java](#tab-panel-3498)
-   [Python](#tab-panel-3499)

```java
import com.aerospike.client.sdk.Record;

import com.aerospike.client.sdk.RecordStream;

// AEL: Clean, readable syntax

RecordStream stream = session.query(users)

    .where("$.status == 'active' and $.age >= 21")

    .execute();

stream.forEach(result -> {

    Record row = result.recordOrThrow();

    // Process row

});

stream.close();

// Compare to traditional expression builders (more verbose)

// Expression filter = Exp.and(

//     Exp.eq(Exp.stringBin("status"), Exp.val("active")),

//     Exp.ge(Exp.intBin("age"), Exp.val(21))

// );
```

```python
# AEL: Clean, readable syntax

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

    "$.status == 'active' and $.age >= 21"

).execute()

async for row in stream:

    record = row.record_or_raise()

    pass  # Process record

stream.close()

# Compare to traditional expression builders (more verbose)

# filter = Exp.and_([

#     Exp.eq(Exp.string_bin("status"), val("active")),

#     Exp.ge(Exp.int_bin("age"), val(21)),

# ])
```

AEL parses your string into an optimized filter expression at query time, giving you the best of both worlds: developer ergonomics and Aerospike performance.

## Basic syntax

AEL expressions are infix expressions:

```plaintext
left_operand operator right_operand
```

-   [Java](#tab-panel-3500)
-   [Python](#tab-panel-3501)

```java
// String comparison

session.query(users).where("$.status == 'active'").execute();

// Numeric comparison

session.query(users).where("$.age > 21").execute();

// Boolean check

session.query(users).where("$.verified == true").execute();
```

```python
# String comparison

await session.query(users).where("$.status == 'active'").execute()

# Numeric comparison

await session.query(users).where("$.age > 21").execute()

# Boolean check

await session.query(users).where("$.verified == true").execute()
```

## Comparison operators

| Operator | Meaning | Example |
| --- | --- | --- |
| `==` | Equal to | `"$.status == 'active'"` |
| `!=` | Not equal to | `"$.status != 'deleted'"` |
| `>` | Greater than | `"$.age > 21"` |
| `>=` | Greater than or equal | `"$.age >= 18"` |
| `<` | Less than | `"$.score < 100"` |
| `<=` | Less than or equal | `"$.score <= 50"` |

### String values

You can use either single or double quotes:

-   [Java](#tab-panel-3502)
-   [Python](#tab-panel-3503)

```java
// Single or double quotes are both valid

session.query(users).where("$.country == 'USA'").execute();

session.query(users).where("$.country == \"USA\"").execute();

session.query(users).where("$.email != 'test@example.com'").execute();

// Strings with spaces

session.query(users).where("$.city == 'New York'").execute();
```

```python
# Single or double quotes are both valid

await session.query(users).where("$.country == 'USA'").execute()

await session.query(users).where("$.country == \"USA\"").execute()

await session.query(users).where("$.email != 'test@example.com'").execute()

# Strings with spaces

await session.query(users).where("$.city == 'New York'").execute()
```

### Numeric values

Use integers or decimals directly:

-   [Java](#tab-panel-3504)
-   [Python](#tab-panel-3505)

```java
// Integers

session.query(orders).where("$.quantity > 10").execute();

// Decimals

session.query(products).where("$.price <= 99.99").execute();

// Negative numbers

session.query(accounts).where("$.balance >= -100").execute();
```

```python
# Integers

await session.query(orders).where("$.quantity > 10").execute()

# Decimals

await session.query(products).where("$.price <= 99.99").execute()

# Negative numbers

await session.query(accounts).where("$.balance >= -100").execute()
```

### Boolean values

Use `true` or `false` (lowercase):

-   [Java](#tab-panel-3506)
-   [Python](#tab-panel-3507)

```java
session.query(users).where("$.verified == true").execute();

session.query(users).where("$.deleted == false").execute();
```

```python
await session.query(users).where("$.verified == true").execute()

await session.query(users).where("$.deleted == false").execute()
```

## Logical operators

Combine conditions using `and`, `or`, `not`, and `exclusive(...)`:

| Operator | Meaning | Example |
| --- | --- | --- |
| `and` | Both conditions must be true | `"$.age > 21 and $.status == 'active'"` |
| `or` | Either condition can be true | `"$.role == 'admin' or $.role == 'moderator'"` |
| `not` | Negates a condition | `"not ($.status == 'deleted')"` |
| `exclusive(...)` | Exactly one argument expression evaluates to true | `"exclusive($.tier == 1, $.tier == 2, $.tier == 3)"` |

### Combining conditions

-   [Java](#tab-panel-3508)
-   [Python](#tab-panel-3509)

```java
// AND: Both conditions required

session.query(users)

    .where("$.age >= 18 and $.status == 'active'")

    .execute();

// OR: Either condition matches

session.query(users)

    .where("$.role == 'admin' or $.role == 'superuser'")

    .execute();

// NOT: Exclude matches

session.query(users)

    .where("not ($.status == 'banned')")

    .execute();

// EXCLUSIVE: exactly one condition can match

session.query(users)

    .where("exclusive($.tier == 1, $.tier == 2, $.tier == 3)")

    .execute();
```

```python
# AND: Both conditions required

(

    session.query(users)

    .where("$.age >= 18 and $.status == 'active'")

    .execute()

)

# OR: Either condition matches

(

    session.query(users)

    .where("$.role == 'admin' or $.role == 'superuser'")

    .execute()

)

# NOT: Exclude matches

(

    session.query(users)

    .where("not ($.status == 'banned')")

    .execute()

)

# EXCLUSIVE: exactly one condition can match

(

    session.query(users)

    .where("exclusive($.tier == 1, $.tier == 2, $.tier == 3)")

    .execute()

)
```

### Complex expressions with parentheses

Use parentheses to control evaluation order:

-   [Java](#tab-panel-3510)
-   [Python](#tab-panel-3511)

```java
// Without parentheses: AND has higher precedence than OR

// "a or b and c" evaluates as "a or (b and c)"

// With parentheses: Explicit grouping

session.query(users)

    .where("($.role == 'admin' or $.role == 'moderator') and $.verified == true")

    .execute();

// Multiple levels

session.query(orders)

    .where("($.status == 'shipped' or $.status == 'delivered') and ($.total > 100 or $.priority == 'high')")

    .execute();
```

```python
# Without parentheses: AND has higher precedence than OR

# "a or b and c" evaluates as "a or (b and c)"

# With parentheses: Explicit grouping

(

    session.query(users)

    .where("($.role == 'admin' or $.role == 'moderator') and $.verified == true")

    .execute()

)

# Multiple levels

(

    session.query(orders)

    .where("($.status == 'shipped' or $.status == 'delivered') and ($.total > 100 or $.priority == 'high')")

    .execute()

)
```

## Collection operators

Work with lists and maps using special operators:

| Operator | Use Case | Example |
| --- | --- | --- |
| `in` | Membership test | `"'sale' in $.tags"` |
| `count()` | Collection size/cardinality | `"$.items.count() > 5"` |

### List membership

Check whether a value is present in a list:

-   [Java](#tab-panel-3512)
-   [Python](#tab-panel-3513)

```java
// Find users with the 'premium' tag

session.query(users)

    .where("'premium' in $.tags")

    .execute();

// Find orders containing a specific product

session.query(orders)

    .where("'SKU-12345' in $.product_ids")

    .execute();
```

```python
# Find users with the 'premium' tag

(

    session.query(users)

    .where("'premium' in $.tags")

    .execute()

)

# Find orders containing a specific product

(

    session.query(orders)

    .where("'SKU-12345' in $.product_ids")

    .execute()

)
```

### Collection size

Filter by the size of a list or map:

-   [Java](#tab-panel-3514)
-   [Python](#tab-panel-3515)

```java
// Users with more than 5 orders

session.query(users)

    .where("$.order_history.count() > 5")

    .execute();

// Products with at least 3 images

session.query(products)

    .where("$.images.count() >= 3")

    .execute();
```

```python
# Users with more than 5 orders

(

    session.query(users)

    .where("$.order_history.count() > 5")

    .execute()

)

# Products with at least 3 images

(

    session.query(products)

    .where("$.images.count() >= 3")

    .execute()

)
```

## Working with nested data

Access nested map values using dot notation:

-   [Java](#tab-panel-3516)
-   [Python](#tab-panel-3517)

```java
// Given record:

// { "address": { "city": "San Francisco", "zip": "94102" } }

// Query nested field

session.query(users)

    .where("$.address.city == 'San Francisco'")

    .execute();

// Multiple nesting levels

// { "profile": { "settings": { "theme": "dark" } } }

session.query(users)

    .where("$.profile.settings.theme == 'dark'")

    .execute();
```

```python
# Given record:

# { "address": { "city": "San Francisco", "zip": "94102" } }

# Query nested field

(

    session.query(users)

    .where("$.address.city == 'San Francisco'")

    .execute()

)

# Multiple nesting levels

# { "profile": { "settings": { "theme": "dark" } } }

(

    session.query(users)

    .where("$.profile.settings.theme == 'dark'")

    .execute()

)
```

### Combining nested access with other operators

-   [Java](#tab-panel-3518)
-   [Python](#tab-panel-3519)

```java
// Nested field with comparison

session.query(users)

    .where("$.profile.age >= 21 and $.address.country == 'USA'")

    .execute();

// Nested list contains

session.query(users)

    .where("'email' in $.preferences.notifications")

    .execute();
```

```python
# Nested field with comparison

(

    session.query(users)

    .where("$.profile.age >= 21 and $.address.country == 'USA'")

    .execute()

)

# Nested list contains

(

    session.query(users)

    .where("'email' in $.preferences.notifications")

    .execute()

)
```

## Existence checks

Use `exists()` to test whether a bin/path is present:

-   [Java](#tab-panel-3520)
-   [Python](#tab-panel-3521)

```java
// Find records where email is not set

session.query(users)

    .where("not($.email.exists())")

    .execute();

// Find records where email exists

session.query(users)

    .where("$.email.exists()")

    .execute();
```

```python
# Find records where email is not set

(

    session.query(users)

    .where("not($.email.exists())")

    .execute()

)

# Find records where email exists

(

    session.query(users)

    .where("$.email.exists()")

    .execute()

)
```

## AEL vs filter expressions

The Developer SDK also supports raw filter expressions for advanced use cases:

| Approach | Best For | Example |
| --- | --- | --- |
| AEL | Most queries, readable code | `"$.age > 21 and $.status == 'active'"` |
| Filter Expressions | Complex logic, programmatic building | `Exp.and(Exp.gt(...), Exp.eq(...))` |

### When to use filter expressions

Use raw expressions when you need:

-   Programmatic expression composition/reuse in application code
-   Compatibility with existing expression code

-   [Java](#tab-panel-3522)
-   [Python](#tab-panel-3523)

```java
import com.aerospike.client.sdk.exp.Exp;

// AEL (recommended for most cases)

session.query(users).where("$.age > 21").execute();

// Raw Exp API (for advanced cases)

Exp exp = Exp.gt(Exp.intBin("age"), Exp.val(21));

session.query(users).where(exp).execute();
```

```python
from aerospike_sdk import Exp, val

# AEL (recommended for most cases)

await session.query(users).where("$.age > 21").execute()

# Raw expression (for advanced cases)

filter = Exp.gt(Exp.int_bin("age"), val(21))

await session.query(users).where(filter).execute()
```

## AEL beyond full-set queries

AEL is not limited to `session.query(dataSet)` over an entire set. You can apply `.where(...)` anywhere the fluent API accepts expressions, including batch key reads and conditional writes.

-   [Java](#tab-panel-3524)
-   [Python](#tab-panel-3525)

```java
// Batch key read with AEL filter

session.query(users.ids(1, 2, 3, 4))

    .where("$.score > 90")

    .execute();

// Conditional single-record update

session.upsert(users.id("task-1"))

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

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

    .execute();
```

```python
# Batch key read with AEL filter

await (

    session.query(users.ids(1, 2, 3, 4))

    .where("$.score > 90")

    .execute()

)

# Conditional single-record update

await (

    session.upsert(users.id("task-1"))

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

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

    .execute()

)
```

## Performance considerations

### Use secondary indexes

AEL expressions are parsed once and compiled before execution, then evaluated on candidate records. For large datasets, create secondary indexes on frequently-filtered bins.

Index choice is transparent to your code: keep the same AEL query, and the SDK/query planner can use an index when available (or scan when none exists).

-   [Java](#tab-panel-3526)
-   [Python](#tab-panel-3527)

```java
// Without index: Scans ALL records, applies filter

// ⚠️ Slow for large sets

session.query(users)

    .where("$.status == 'active'")  // Filter applied during scan

    .execute();

// With index on 'status': Planner can use the index transparently

// ✅ Fast for selective queries

session.query(users)

    .where("$.status == 'active'")  // Index lookup + filter

    .execute();
```

```python
# Without index: Scans ALL records, applies filter

# ⚠️ Slow for large sets

(

    session.query(users)

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

    .execute()

)

# With index on 'status': Planner can use the index transparently

# ✅ Fast for selective queries

(

    session.query(users)

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

    .execute()

)
```

::: index recommendation
Create secondary indexes on bins you frequently filter. See your database administrator or use `asadm` to create indexes.
:::

### Limit results

Always limit results when you don’t need everything:

-   [Java](#tab-panel-3528)
-   [Python](#tab-panel-3529)

```java
import com.aerospike.client.sdk.Record;

import com.aerospike.client.sdk.RecordStream;

// Get only the first 100 matching records (server-side limit)

RecordStream stream = session.query(users)

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

    .limit(100)

    .execute();

stream.forEach(result -> {

    Record row = result.recordOrThrow();

    // Process row

});

stream.close();
```

```python
# Get only the first 100 matching records (server-side limit)

stream = await session.query(users).where("$.status == 'active'").limit(100).execute()

async for row in stream:

    record = row.record_or_raise()

    pass  # Process record

stream.close()
```

### Keep expressions readable

Complex expressions are valid, but readability and maintainability matter:

```plaintext
Hard to maintain:

($.a and $.b) or ($.c and $.d) or ($.e and $.f) and not ($.g or $.h)

Prefer clear grouping and intent:

($.status == 'active' and $.age > 21) or $.priority == 'high'
```

The primary performance factor is index selectivity and record volume, not whether you wrote the filter as AEL text or an expression builder tree.

### Use projections

Request only the bins you need:

-   [Java](#tab-panel-3530)
-   [Python](#tab-panel-3531)

```java
import com.aerospike.client.sdk.Record;

import com.aerospike.client.sdk.RecordStream;

// Only retrieve 'name' and 'email' bins (projection)

RecordStream stream = session.query(users)

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

    .readingOnlyBins("name", "email")

    .execute();

stream.forEach(result -> {

    Record row = result.recordOrThrow();

    // Process row

});

stream.close();
```

```python
# Only retrieve 'name' and 'email' bins (projection)

stream = await session.query(users).where("$.status == 'active'").bins(

    ["name", "email"]

).execute()

async for row in stream:

    record = row.record_or_raise()

    pass  # record.bins.get("name"), etc.

stream.close()
```

## Complete example

-   [Java](#tab-panel-3532)
-   [Python](#tab-panel-3533)

```java
import com.aerospike.client.sdk.Cluster;

import com.aerospike.client.sdk.ClusterDefinition;

import com.aerospike.client.sdk.DataSet;

import com.aerospike.client.sdk.Record;

import com.aerospike.client.sdk.RecordStream;

import com.aerospike.client.sdk.Session;

import com.aerospike.client.sdk.policy.Behavior;

import java.util.concurrent.atomic.AtomicInteger;

public class AELQueryExample {

    public static void main(String[] args) {

        try (Cluster cluster = new ClusterDefinition("localhost", 3000).connect()) {

            Session session = cluster.createSession(Behavior.DEFAULT);

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

            String p1 = "ael-product-1";

            String p2 = "ael-product-2";

            String p3 = "ael-product-3";

            String p4 = "ael-product-4";

            // Seed sample data so the example is repeatable.

            session.delete(products.ids(p1, p2, p3, p4)).execute().close();

            session.insert(products)

                .bins("name", "category", "price", "rating", "tags", "metadata")

                .id(p1).values("TV", "electronics", 499.99, 4.6, java.util.List.of("sale"), java.util.Map.of("featured", true))

                .id(p2).values("Headphones", "electronics", 39.99, 4.2, java.util.List.of("clearance"), java.util.Map.of("featured", false))

                .id(p3).values("Notebook", "office", 12.50, 3.9, java.util.List.of("stationery"), java.util.Map.of("featured", false))

                .id(p4).values("Monitor", "electronics", 199.99, 4.8, java.util.List.of("sale", "new"), java.util.Map.of("featured", true))

                .execute();

            // Simple equality

            AtomicInteger electronicsCount = new AtomicInteger(0);

            RecordStream s1 = session.query(products)

                .where("$.category == 'electronics'")

                .execute();

            s1.forEach(result -> {

                Record row = result.recordOrThrow();

                electronicsCount.incrementAndGet();

            });

            s1.close();

            // Range query

            AtomicInteger affordableCount = new AtomicInteger(0);

            RecordStream s2 = session.query(products)

                .where("$.price >= 10 and $.price <= 50")

                .execute();

            s2.forEach(result -> {

                Record row = result.recordOrThrow();

                affordableCount.incrementAndGet();

            });

            s2.close();

            // Complex filter with nested access

            AtomicInteger featuredCount = new AtomicInteger(0);

            RecordStream s3 = session.query(products)

                .where("$.metadata.featured == true and $.rating >= 4.0")

                .readingOnlyBins("name", "price", "rating")

                .limit(10)

                .execute();

            s3.forEach(result -> {

                Record row = result.recordOrThrow();

                featuredCount.incrementAndGet();

            });

            s3.close();

            // List contains

            AtomicInteger taggedCount = new AtomicInteger(0);

            RecordStream s4 = session.query(products)

                .where("'sale' in $.tags or 'clearance' in $.tags")

                .execute();

            s4.forEach(result -> {

                Record row = result.recordOrThrow();

                taggedCount.incrementAndGet();

            });

            s4.close();

            System.out.println("Found " + electronicsCount.get() + " electronics");

            System.out.println("Found " + affordableCount.get() + " affordable products");

            System.out.println("Found " + featuredCount.get() + " featured products");

            System.out.println("Found " + taggedCount.get() + " sale/clearance products");

            // Cleanup

            session.delete(products.ids(p1, p2, p3, p4)).execute().close();

        }

    }

}
```

```python
import asyncio

from aerospike_sdk import Behavior, ClusterDefinition, DataSet

async def main():

    cluster_def = ClusterDefinition("localhost", 3000)

    # If your Dockerized server advertises an internal bridge IP,

    # map it back to localhost for host-based examples.

    cluster_def = cluster_def.with_ip_map({"172.17.0.2": "127.0.0.1"})

    async with await cluster_def.connect() as cluster:

        session = cluster.create_session(Behavior.DEFAULT)

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

        p1 = products.id("ael-product-1")

        p2 = products.id("ael-product-2")

        p3 = products.id("ael-product-3")

        p4 = products.id("ael-product-4")

        # Seed sample data so the example is repeatable.

        stream = await session.batch().delete(p1).delete(p2).delete(p3).delete(p4).execute()

        stream.close()

        await (

            session.batch()

            .insert(p1).put({

                "name": "TV",

                "category": "electronics",

                "price": 499.99,

                "rating": 4.6,

                "tags": ["sale"],

                "metadata": {"featured": True},

            })

            .insert(p2).put({

                "name": "Headphones",

                "category": "electronics",

                "price": 39.99,

                "rating": 4.2,

                "tags": ["clearance"],

                "metadata": {"featured": False},

            })

            .insert(p3).put({

                "name": "Notebook",

                "category": "office",

                "price": 12.50,

                "rating": 3.9,

                "tags": ["stationery"],

                "metadata": {"featured": False},

            })

            .insert(p4).put({

                "name": "Monitor",

                "category": "electronics",

                "price": 199.99,

                "rating": 4.8,

                "tags": ["sale", "new"],

                "metadata": {"featured": True},

            })

            .execute()

        )

        # Simple equality

        electronics_count = 0

        stream = await session.query(products).where("$.category == 'electronics'").execute()

        async for row in stream:

            row.record_or_raise()

            electronics_count += 1

        stream.close()

        # Range query

        # Float bins need float literals in AEL — "$.price >= 10" against a

        # float bin returns no rows because the comparison types don't align.

        affordable_count = 0

        stream = await session.query(products).where("$.price >= 10.0 and $.price <= 50.0").execute()

        async for row in stream:

            row.record_or_raise()

            affordable_count += 1

        stream.close()

        # Complex filter with nested access

        featured_count = 0

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

            "$.metadata.featured == true and $.rating >= 4.0"

        ).bins(["name", "price", "rating"]).limit(10).execute()

        async for row in stream:

            row.record_or_raise()

            featured_count += 1

        stream.close()

        # List contains

        tagged_count = 0

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

            "'sale' in $.tags or 'clearance' in $.tags"

        ).execute()

        async for row in stream:

            row.record_or_raise()

            tagged_count += 1

        stream.close()

        print(f"Found {electronics_count} electronics")

        print(f"Found {affordable_count} affordable products")

        print(f"Found {featured_count} featured products")

        print(f"Found {tagged_count} sale/clearance products")

        # Cleanup

        stream = await session.batch().delete(p1).delete(p2).delete(p3).delete(p4).execute()

        stream.close()

asyncio.run(main())
```

## AEL quick reference

| Expression | Meaning |
| --- | --- |
| `"$.age == 25"` | Age equals 25 |
| `"$.price > 100"` | Price greater than 100 |
| `"$.status != 'deleted'"` | Status is not “deleted” |
| `"$.age >= 18 and $.age <= 65"` | Age between 18 and 65 |
| `"$.role == 'admin' or $.role == 'mod'"` | Role is admin or mod |
| `"not($.banned == true)"` | Not banned |
| `"'vip' in $.tags"` | Tags list contains “vip” |
| `"$.items.count() > 0"` | Items list is not empty |
| `"$.address.city == 'NYC'"` | Nested city equals NYC |
| `"$.email.exists()"` | Email is set |
| `"let (total = $.price * $.qty) then (${total} > 1000)"` | Variable binding with `let` |
| `"when ($.tier == 1 => 'gold', default => 'other')"` | Conditional with `when` |

## More AEL capabilities

This page focuses on common query filters, but AEL also supports more expressive constructs from the Java AEL reference and post-preview language design notes.

### Variable binding with `let (...) then (...)`

Use `let` to define reusable intermediate values so long expressions stay readable.

-   [Java](#tab-panel-3534)
-   [Python](#tab-panel-3535)

```java
// Reuse intermediate calculations in a single predicate.

session.query(orders)

    .where("let (subtotal = $.price * $.qty, tax = ${subtotal} * 0.1) then (${subtotal} + ${tax} > 1000)")

    .execute();
```

```python
# Reuse intermediate calculations in a single predicate.

await (

    session.query(orders)

    .where("let (subtotal = $.price * $.qty, tax = ${subtotal} * 0.1) then (${subtotal} + ${tax} > 1000)")

    .execute()

)
```

### Conditional logic with `when (..., default => ...)`

Use `when` for inline branching inside an expression.

-   [Java](#tab-panel-3536)
-   [Python](#tab-panel-3537)

```java
// Compare a bin against a conditionally-computed value.

session.query(users)

    .where("$.badge == (when ($.tier == 1 => 'gold', $.tier == 2 => 'silver', default => 'bronze'))")

    .execute();
```

```python
# Compare a bin against a conditionally-computed value.

await (

    session.query(users)

    .where("$.badge == (when ($.tier == 1 => 'gold', $.tier == 2 => 'silver', default => 'bronze'))")

    .execute()

)
```

### String and map literals

String literals use single quotes. In map literals, keys and string values should also be quoted.

-   [Java](#tab-panel-3538)
-   [Python](#tab-panel-3539)

```java
session.query(users)

    .where("$.name == 'Tim' and $.labels == {'role': 'user', 'tier': 2}")

    .execute();
```

```python
await (

    session.query(users)

    .where("$.name == 'Tim' and $.labels == {'role': 'user', 'tier': 2}")

    .execute()

)
```

### Arithmetic and bitwise operators

AEL supports arithmetic expressions (`+`, `-`, `*`, `/`) and integer bitwise operators (`&`, `|`, `^`, `<<`, `>>`, `>>>`) in predicates.

-   [Java](#tab-panel-3540)
-   [Python](#tab-panel-3541)

```java
// Arithmetic comparison.

session.query(items)

    .where("($.unitPrice * $.quantity) - $.discount > 500")

    .execute();

// Bitmask check: confirm enabled flag bit is set.

session.query(items)

    .where("(($.flags >> 2) & 1) == 1")

    .execute();
```

```python
# Arithmetic comparison.

await (

    session.query(items)

    .where("($.unitPrice * $.quantity) - $.discount > 500")

    .execute()

)

# Bitmask check: confirm enabled flag bit is set.

await (

    session.query(items)

    .where("(($.flags >> 2) & 1) == 1")

    .execute()

)
```

### Record metadata functions

You can filter by record metadata such as remaining TTL, on-device size, and tombstone status.

-   [Java](#tab-panel-3542)
-   [Python](#tab-panel-3543)

```java
session.query(events)

    .where("$.ttl() < 3600 and $.recordSize() > 1024")

    .execute();

session.query(events)

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

    .execute();
```

```python
await (

    session.query(events)

    .where("$.ttl() < 3600 and $.recordSize() > 1024")

    .execute()

)

await (

    session.query(events)

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

    .execute()

)
```

### Richer CDT path reads and typed returns

Beyond simple dot navigation, AEL design work includes deeper path forms and typed getters, so complex nested list/map content can be filtered more precisely.

-   [Java](#tab-panel-3544)
-   [Python](#tab-panel-3545)

```java
// Example of typed path access used inside a predicate.

session.query(profiles)

    .where("$.prefs.theme.get(type: STRING) == 'dark' and $.prefs.flags.get(type: INT) > 0")

    .execute();
```

```python
# Example of typed path access used inside a predicate.

await (

    session.query(profiles)

    .where("$.prefs.theme.get(type: STRING) == 'dark' and $.prefs.flags.get(type: INT) > 0")

    .execute()

)
```

### Post-preview roadmap examples

The post-preview AEL roadmap adds additional language areas. The examples below are syntax previews from the reference docs, not guaranteed to be available in every SDK release today.

-   [Java](#tab-panel-3546)
-   [Python](#tab-panel-3547)

```java
// Regex filtering (ICU syntax).

session.query(users).where("$.email =~ /@example\\.com$/i").execute();

// Deeper CDT path filtering/mutation forms.

session.query(catalog).where("$.listbin.[=a:z].remove(return: NONE)").execute();

session.query(catalog).where("$.mapbin.{a,c}.get(return: VALUE).[].count() == 2").execute();

// Method-style function families (string / blob-bit / HLL / geo).

session.query(metrics).where("$.name.uppercase() == 'CPU'").execute();

session.query(metrics).where("$.blob.bitGet(offset: 0, size: 8) > 0").execute();

session.query(metrics).where("$.hll.hllCount() > 100").execute();

session.query(metrics).where("geoCompare($.region, geoJson('{\"type\":\"Point\",\"coordinates\":[-122.4,37.8]}'))").execute();
```

```python
# Regex filtering (ICU syntax).

await session.query(users).where("$.email =~ /@example\\.com$/i").execute()

# Deeper CDT path filtering/mutation forms.

await session.query(catalog).where("$.listbin.[=a:z].remove(return: NONE)").execute()

await session.query(catalog).where("$.mapbin.{a,c}.get(return: VALUE).[].count() == 2").execute()

# Method-style function families (string / blob-bit / HLL / geo).

await session.query(metrics).where("$.name.uppercase() == 'CPU'").execute()

await session.query(metrics).where("$.blob.bitGet(offset: 0, size: 8) > 0").execute()

await session.query(metrics).where("$.hll.hllCount() > 100").execute()

await session.query(metrics).where("geoCompare($.region, geoJson('{\"type\":\"Point\",\"coordinates\":[-122.4,37.8]}'))").execute()
```

For full details, see:

-   [Aerospike Expression AEL Reference](https://github.com/aerospike/aerospike-client-java-sdk/blob/init/docs/ael-documentation.md)

## Next steps

Query Records

Explore pagination, sorting, and more query options.

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

Data Model

Understand the data structures AEL filters operate on.

[Data Model →](https://aerospike.com/docs/develop/client/sdk/concepts/data-model)

Error Handling

Handle query errors gracefully.

[Error Handling →](https://aerospike.com/docs/develop/client/sdk/concepts/errors)

Behaviors

Configure query timeouts and retries.

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