# Query records

Learn how to find records in Aerospike using the Developer SDK’s AEL (Aerospike Expression Language) queries. AEL provides a readable, SQL-like syntax for filtering data.

## Basic query

All operations which read data are queries, regardless of whether you are reading one record, 100 records, or performing an index query. Use `query()` to find records, and a `where` clause to provide filtering if desired:

-   [Java](#tab-panel-3162)
-   [Python](#tab-panel-3163)

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

import com.aerospike.client.sdk.Record;

import com.aerospike.client.sdk.RecordStream;

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

RecordStream stream = session.query(users)

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

    .execute();

stream.forEach(result -> {

    Record user = result.recordOrThrow();

    System.out.println("Name: " + user.getString("name"));

});
```

```python
async def demo(session):

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

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

    async for row in stream:

        record = row.record_or_raise()

        print(f"Name: {record.bins.get('name')}")

    stream.close()
```

## AEL operators

Use `$.binName` to get the contents of a bin called `binName`. String literals can be in either single or double quotes, floats are numbers with a decimal point and integer are just digits.

### Comparison operators

| Operator | Description | Example |
| --- | --- | --- |
| `==` | Equal | `"$.status == 'active'"` |
| `!=` | Not equal | `"$.status != 'inactive'"` |
| `>` | Greater than | `"$.age > 21"` |
| `>=` | Greater than or equal | `"$.age >= 18"` |
| `<` | Less than | `"$.balance < 0"` |
| `<=` | Less than or equal | `"$.score <= 100"` |

See [AEL reference](https://aerospike.com/docs/develop/client/sdk/concepts/ael/ael-reference) for a complete list of AEL operators.

-   [Java](#tab-panel-3164)
-   [Python](#tab-panel-3165)

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

import com.aerospike.client.sdk.RecordResult;

import com.aerospike.client.sdk.RecordStream;

// Numeric comparisons

RecordStream s1 = session.query(users).where("$.age >= 18").execute();

s1.forEach((RecordResult result) -> { /* use result.recordOrThrow() */ });

// String equality

RecordStream s2 = session.query(users).where("$.role == 'admin'").execute();

s2.forEach((RecordResult result) -> { /* ... */ });

// Not equal

RecordStream s3 = session.query(users).where("$.deleted != true").execute();

s3.forEach((RecordResult result) -> { /* ... */ });
```

```python
async def demo(session):

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

    # Numeric comparisons

    stream = await session.query(users).where("$.age >= 18").execute()

    async for row in stream:

        pass

    stream.close()

    # String equality

    stream = await session.query(users).where("$.role == 'admin'").execute()

    async for row in stream:

        pass

    stream.close()

    # Not equal

    stream = await session.query(users).where("$.deleted != true").execute()

    async for row in stream:

        pass

    stream.close()
```

### Logical operators

Combine conditions with `and`, `or`, and `not`:

-   [Java](#tab-panel-3166)
-   [Python](#tab-panel-3167)

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

import com.aerospike.client.sdk.RecordStream;

// AND

RecordStream a = session.query(users)

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

    .execute();

a.forEach((RecordResult result) -> { /* ... */ });

// OR

RecordStream b = session.query(users)

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

    .execute();

b.forEach((RecordResult result) -> { /* ... */ });

// NOT

RecordStream c = session.query(users)

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

    .execute();

c.forEach((RecordResult result) -> { /* ... */ });

// Complex combinations

RecordStream d = session.query(users)

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

    .execute();

d.forEach((RecordResult result) -> { /* ... */ });
```

```python
async def demo(session):

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

    # AND

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

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

    ).execute()

    async for row in stream:

        pass

    stream.close()

    # OR

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

        "$.role == 'admin' or $.role == 'moderator'"

    ).execute()

    async for row in stream:

        pass

    stream.close()

    # NOT

    stream = await session.query(users).where("not($.status == 'banned')").execute()

    async for row in stream:

        pass

    stream.close()

    # Complex combinations

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

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

    ).execute()

    async for row in stream:

        pass

    stream.close()
```

### Membership and existence operators

| Operator | Description | Example |
| --- | --- | --- |
| `in` | Membership in a list | `"'admin' in $.roles"` |
| `exists()` | Path/bin existence check | `"$.email.exists()"` |
| `count()` | Collection cardinality | `"$.tags.count() > 3"` |

-   [Java](#tab-panel-3168)
-   [Python](#tab-panel-3169)

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

import com.aerospike.client.sdk.RecordStream;

// Membership in list

RecordStream s1 = session.query(users)

    .where("'admin' in $.roles")

    .execute();

s1.forEach((RecordResult result) -> { /* ... */ });

// Exists tests for the presence of the bin in the record

RecordStream s2 = session.query(users)

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

    .execute();

s2.forEach((RecordResult result) -> { /* ... */ });

// Collection size

RecordStream s3 = session.query(users)

    .where("$.tags.count() > 3")

    .execute();

s3.forEach((RecordResult result) -> { /* ... */ });
```

```python
async def demo(session):

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

    # Membership in list

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

        "'admin' in $.roles"

    ).execute()

    async for row in stream:

        pass

    stream.close()

    # Exists tests for the presence of the bin in the record

    stream = await session.query(users).where("$.email.exists()").execute()

    async for row in stream:

        pass

    stream.close()

    # Collection size

    stream = await session.query(users).where("$.tags.count() > 3").execute()

    async for row in stream:

        pass

    stream.close()
```

## Select specific bins

Return only the bins you need:

-   [Java](#tab-panel-3170)
-   [Python](#tab-panel-3171)

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

import com.aerospike.client.sdk.RecordResult;

import com.aerospike.client.sdk.RecordStream;

RecordStream stream = session.query(users)

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

    .readingOnlyBins("name", "email")  // Only return name and email

    .execute();

stream.forEach((RecordResult result) -> {

    Record user = result.recordOrThrow();

    System.out.println(user.getString("name"));

});
```

```python
async def demo(session):

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

    stream = await (

        session.query(users)

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

        .bins(["name", "email"])

        .execute()

    )

    async for row in stream:

        record = row.record_or_raise()

        print(record.bins.get("name"))

    stream.close()
```

## Limit results

Limit the number of records returned:

-   [Java](#tab-panel-3172)
-   [Python](#tab-panel-3173)

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

import com.aerospike.client.sdk.RecordStream;

// Get first 10 active users

RecordStream stream = session.query(users)

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

    .limit(10)

    .execute();

stream.forEach((RecordResult result) -> { /* ... */ });
```

```python
async def demo(session):

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

    # Get first 10 active users

    stream = await (

        session.query(users)

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

        .limit(10)

        .execute()

    )

    async for row in stream:

        pass

    stream.close()
```

## Stream results

The record streams buffer results returned from the server efficiently when queries which return large numbers of results are executed. This is transparent to the API, but it means that large result sets are not loaded into memory in their entirety.

Record streams should be closed when finished, freeing up resources on both the client side and server side. In Java, terminal helpers such as `getFirst`, `getFirstRecord`, and `forEach` close the stream when the call finishes. In Python, helpers like `first()`, `first_or_raise()`, `collect()`, and `failures()` consume the stream but do **not** auto-close it; always call `stream.close()` (or wrap the work in a `try` / `finally`) to release client and server resources promptly.

For large result sets, stream records instead of loading all into memory:

-   [Java](#tab-panel-3174)
-   [Python](#tab-panel-3175)

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

import com.aerospike.client.sdk.RecordResult;

import com.aerospike.client.sdk.RecordStream;

// Process records one at a time

RecordStream stream = session.query(users)

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

    .execute();

stream.forEach((RecordResult result) -> {

    if (result.isOk()) {

        Record user = result.recordOrThrow();

        if (user != null) {

            System.out.println("Processing: " + user.getString("name"));

        }

    }

});
```

```python
async def demo(session):

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

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

    async for row in stream:

        record = row.record_or_raise()

        print(f"Processing: {record.bins.get('name')}")

    stream.close()
```

## Query without filter (scan)

Query all records in a set:

-   [Java](#tab-panel-3176)
-   [Python](#tab-panel-3177)

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

import com.aerospike.client.sdk.RecordResult;

import com.aerospike.client.sdk.RecordStream;

// Get all users (use with caution on large datasets!)

RecordStream allStream = session.query(users).execute();

List<Record> allUsers = allStream.stream().asList();

// Better: stream to avoid memory issues

RecordStream scan = session.query(users).execute();

scan.forEach(result -> {

    Record user = result.recordOrThrow();

    System.out.println(user.getString("name"));

});
```

```python
async def demo(session):

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

    # Get all users (use with caution on large datasets!)

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

    all_users = []

    async for row in stream:

        all_users.append(row.record_or_raise())

    stream.close()

    # Better: iterate without building a full list

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

    async for row in stream:

        record = row.record_or_raise()

        print(record.bins.get("name"))

    stream.close()
```

::: full scans
Queries without a `where` clause scan all records in the set. Use with caution on large datasets—consider using limits or streaming.
:::

## Complete example

-   [Java](#tab-panel-3178)
-   [Python](#tab-panel-3179)

```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.RecordResult;

import com.aerospike.client.sdk.RecordStream;

import com.aerospike.client.sdk.Session;

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

public class QueryRecordsExample {

    public static void main(String[] args) {

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

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

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

            String k1 = "query-example-1";

            String k2 = "query-example-2";

            String k3 = "query-example-3";

            String k4 = "query-example-4";

            // Cleanup so the example is repeatable.

            session.delete(users.ids(k1, k2, k3, k4)).execute().close();

            // Create sample data

            session.insert(users)

                .bins("name", "age", "status")

                .id(k1).values("Alice", 28, "active")

                .id(k2).values("Bob", 35, "active")

                .id(k3).values("Carol", 22, "inactive")

                .id(k4).values("David", 45, "active")

                .execute();

            // Simple query

            System.out.println("Active users:");

            RecordStream q1 = session.query(users)

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

                .execute();

            q1.forEach((RecordResult result) -> {

                Record u = result.recordOrThrow();

                System.out.println("  - " + u.getString("name"));

            });

            // Complex query

            System.out.println("\nActive adults over 30:");

            RecordStream q2 = session.query(users)

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

                .readingOnlyBins("name", "age")

                .execute();

            q2.forEach(result -> {

                if (result.isOk()) {

                    Record u = result.recordOrThrow();

                    System.out.println(

                        "  - " + u.getString("name") + " (" + u.getInt("age") + ")"

                    );

                }

            });

            // Count

            RecordStream q3 = session.query(users)

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

                .execute();

            long activeCount = 0;

            while (q3.hasNext()) {

                RecordResult rr = q3.next();

                activeCount++;

            }

            q3.close();

            System.out.println("\nActive user count: " + activeCount);

        }

    }

}
```

```python
import asyncio

from aerospike_sdk import Behavior, DataSet, Client

async def main():

    async with Client("localhost:3000") as client:

        session = client.create_session(Behavior.DEFAULT)

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

        k1 = users.id("query-example-1")

        k2 = users.id("query-example-2")

        k3 = users.id("query-example-3")

        k4 = users.id("query-example-4")

        # Cleanup so the example is repeatable.

        stream = await session.batch().delete(k1).delete(k2).delete(k3).delete(k4).execute()

        stream.close()

        # Create sample data

        await (

            session.batch()

            .insert(k1).put({"name": "Alice", "age": 28, "status": "active"})

            .insert(k2).put({"name": "Bob", "age": 35, "status": "active"})

            .insert(k3).put({"name": "Carol", "age": 22, "status": "inactive"})

            .insert(k4).put({"name": "David", "age": 45, "status": "active"})

            .execute()

        )

        # Simple query

        print("Active users:")

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

        async for row in stream:

            record = row.record_or_raise()

            print(f"  - {record.bins.get('name')}")

        stream.close()

        # Complex query

        print("\nActive adults over 30:")

        stream = await (

            session.query(users)

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

            .bins(["name", "age"])

            .execute()

        )

        async for row in stream:

            r = row.record_or_raise()

            print(f"  - {r.bins.get('name')} ({r.bins.get('age')})")

        stream.close()

        # Count

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

        active_count = 0

        async for row in stream:

            row.record_or_raise()

            active_count += 1

        stream.close()

        print(f"\nActive user count: {active_count}")

if __name__ == "__main__":

    asyncio.run(main())
```

## AEL and secondary indexes

AEL text does not force a full scan. The planner can use available secondary indexes for selective predicates and fall back to scans when no useful index exists.

> 📖 **Learn more**: [AEL Reference](https://aerospike.com/docs/develop/client/sdk/concepts/ael) covers the full AEL syntax.

## API reference summary

| Method | Description |
| --- | --- |
| `session.query(dataSet)` | Start a query on a DataSet |
| `.where("expression")` | Filter with AEL expression (use `$.bin` for bin paths) |
| `.readingOnlyBins(bins...)` / `.bins([...])` on `session.query(DataSet)` | Select specific bins to return |
| `.limit(n)` | Limit number of results |
| `.execute()` | Run the query and return a `RecordStream` |
| `RecordStream` / `async for` | Iterate results; call `stream.close()` when done |

## Next steps

AEL Reference

Full AEL syntax and operators.

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

Batch Operations

Efficient multi-record operations.

[Batch Operations →](https://aerospike.com/docs/develop/client/sdk/usage/batch)

Async Operations

Non-blocking queries for high throughput.

[Async Operations →](https://aerospike.com/docs/develop/client/sdk/usage/async)