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:
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"));});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 for a complete list of AEL operators.
import com.aerospike.client.sdk.Record;import com.aerospike.client.sdk.RecordResult;import com.aerospike.client.sdk.RecordStream;
// Numeric comparisonsRecordStream s1 = session.query(users).where("$.age >= 18").execute();s1.forEach((RecordResult result) -> { /* use result.recordOrThrow() */ });
// String equalityRecordStream s2 = session.query(users).where("$.role == 'admin'").execute();s2.forEach((RecordResult result) -> { /* ... */ });
// Not equalRecordStream s3 = session.query(users).where("$.deleted != true").execute();s3.forEach((RecordResult result) -> { /* ... */ });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:
import com.aerospike.client.sdk.RecordResult;import com.aerospike.client.sdk.RecordStream;
// ANDRecordStream a = session.query(users) .where("$.status == 'active' and $.age >= 18") .execute();a.forEach((RecordResult result) -> { /* ... */ });
// ORRecordStream b = session.query(users) .where("$.role == 'admin' or $.role == 'moderator'") .execute();b.forEach((RecordResult result) -> { /* ... */ });
// NOTRecordStream c = session.query(users) .where("not($.status == 'banned')") .execute();c.forEach((RecordResult result) -> { /* ... */ });
// Complex combinationsRecordStream d = session.query(users) .where("($.status == 'active' or $.status == 'pending') and $.age >= 18") .execute();d.forEach((RecordResult result) -> { /* ... */ });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" |
import com.aerospike.client.sdk.RecordResult;import com.aerospike.client.sdk.RecordStream;
// Membership in listRecordStream s1 = session.query(users) .where("'admin' in $.roles") .execute();s1.forEach((RecordResult result) -> { /* ... */ });
// Exists tests for the presence of the bin in the recordRecordStream s2 = session.query(users) .where("$.email.exists()") .execute();s2.forEach((RecordResult result) -> { /* ... */ });
// Collection sizeRecordStream s3 = session.query(users) .where("$.tags.count() > 3") .execute();s3.forEach((RecordResult result) -> { /* ... */ });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:
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"));});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:
import com.aerospike.client.sdk.RecordResult;import com.aerospike.client.sdk.RecordStream;
// Get first 10 active usersRecordStream stream = session.query(users) .where("$.status == 'active'") .limit(10) .execute();stream.forEach((RecordResult result) -> { /* ... */ });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:
import com.aerospike.client.sdk.Record;import com.aerospike.client.sdk.RecordResult;import com.aerospike.client.sdk.RecordStream;
// Process records one at a timeRecordStream 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")); } }});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:
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 issuesRecordStream scan = session.query(users).execute();scan.forEach(result -> { Record user = result.recordOrThrow(); System.out.println(user.getString("name"));});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()Complete example
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); } }}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 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.
Batch Operations
Efficient multi-record operations.
Async Operations
Non-blocking queries for high throughput.