Skip to content

Query records

For the complete documentation index see: llms.txt

All documentation pages available in markdown.

Applies to

  • Aerospike Developer SDK preview (Java 21+ and Python 3.10+)
  • Aerospike Database 6.0 or later unless a section states otherwise

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"));
});

📖 API reference: DataSet.of(...) | Session.query(...) | ChainableQueryBuilder.where(...) | ChainableQueryBuilder.execute() | RecordStream.forEach(...) | RecordResult.recordOrThrow() | Record.getString(...)

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 integers are just digits.

Comparison operators

OperatorDescriptionExample
==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 the 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 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) -> { /* ... */ });

📖 API reference: Session.query(...) | ChainableQueryBuilder.where(...) | ChainableQueryBuilder.execute() | RecordStream.forEach(...) | RecordResult.recordOrThrow()

Logical operators

Combine conditions with and, or, and not:

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) -> { /* ... */ });

📖 API reference: Session.query(...) | ChainableQueryBuilder.where(...) | ChainableQueryBuilder.execute() | RecordStream.forEach(...) | RecordResult.recordOrThrow()

Membership and existence operators

OperatorDescriptionExample
inMembership 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 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) -> { /* ... */ });

📖 API reference: Session.query(...) | ChainableQueryBuilder.where(...) | ChainableQueryBuilder.execute() | RecordStream.forEach(...) | RecordResult.recordOrThrow()

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"));
});

📖 API reference: Session.query(...) | ChainableQueryBuilder.where(...) | ChainableQueryBuilder.readingOnlyBins(...) | ChainableQueryBuilder.execute() | RecordStream.forEach(...) | RecordResult.recordOrThrow() | Record.getString(...)

Limit results

Limit the number of records returned:

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) -> { /* ... */ });

📖 API reference: Session.query(...) | ChainableQueryBuilder.where(...) | ChainableQueryBuilder.limit(...) | ChainableQueryBuilder.execute() | RecordStream.forEach(...) | RecordResult.recordOrThrow()

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 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"));
}
}
});

📖 API reference: Session.query(...) | ChainableQueryBuilder.where(...) | ChainableQueryBuilder.execute() | RecordStream.forEach(...) | RecordResult.isOk() | RecordResult.recordOrThrow() | Record.getString(...)

Java iteration patterns and key access

Accessing the record key

The Record class holds only bin data (bins, generation, expiration). The key is on the RecordResult wrapper, accessible via result.key():

stream.forEach(result -> {
Key k = result.key();
// String key:
String strKey = k.userKey.toString();
// Integer key (userKey is a Value; call toLong()):
long intKey = k.userKey.toLong();
Record record = result.recordOrThrow();
// use record.getString("name"), record.getInt("age"), etc.
});

record.key and record.getKey() do not exist. Always use result.key() on the RecordResult, not on the Record itself.

Accessing bins on a Record

Record exposes typed getters (getString, getInt, getLong, getBoolean, getMap, getList) as well as the raw bins map and getValue(String):

// Typed getter (preferred):
String name = record.getString("name");
int age = record.getInt("age");
// Generic:
Object raw = record.getValue("profile"); // same as record.bins.get("profile")
Object raw2 = record.bins.get("profile"); // also fine

There is no record.get("binName") method. Use record.getValue("binName") or record.bins.get("binName") for untyped access.

record.getMap() returns AerospikeMap<?,?>, not Map<String, Object>

record.getMap("binName") returns AerospikeMap<?, ?>. Because of Java’s wildcard bounds, this cannot be directly assigned to Map<String, Object> without a cast, even though AerospikeMap does implement Map. The two simplest approaches:

// Option 1: via bins field (cleanest, no annotation needed):
// record.bins is Map<String,Object>, so .get() returns Object
Map<?, ?> profile = (Map<?, ?>) record.bins.get("profile");
String level = (String) profile.get("level");
// Option 2: via getValue(), with unchecked cast to typed Map:
@SuppressWarnings("unchecked")
Map<String, Object> profile2 = (Map<String, Object>) record.getValue("profile");
String level2 = (String) profile2.get("level");

Record import disambiguation: don’t use wildcard imports

Don’t use import com.aerospike.client.sdk.*;. It causes a compile error in Java 16+ because both com.aerospike.client.sdk.Record and java.lang.Record are pulled into scope and the compiler cannot resolve the ambiguity. Always import every class explicitly:

import com.aerospike.client.sdk.Record; // required to avoid java.lang.Record conflict
import com.aerospike.client.sdk.RecordResult;
import com.aerospike.client.sdk.RecordStream;

Mutable captures in lambdas

Java lambdas require all captured local variables to be effectively final. This applies to every mutable type: booleans, integer counters, long accumulators, and any other variable modified inside the lambda. Use the matching Atomic* wrapper:

import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;
AtomicBoolean found = new AtomicBoolean(false);
AtomicInteger chunkNumber = new AtomicInteger(1);
AtomicLong recordCount = new AtomicLong(0L);
stream.forEach(result -> {
if (result.isOk()) {
found.set(true);
chunkNumber.getAndIncrement();
recordCount.incrementAndGet();
}
});
if (found.get()) { System.out.println("Record found!"); }

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;
import java.util.ArrayList;
import java.util.List;
// Get all users (use with caution on large datasets!)
RecordStream allStream = session.query(users).execute();
List<Record> allUsers = new ArrayList<>();
allStream.forEach(result -> {
if (result.isOk()) {
allUsers.add(result.recordOrThrow());
}
});
// forEach closes allStream.
// Better: stream one record at a time without building a full list
RecordStream scan = session.query(users).execute();
scan.forEach(result -> {
if (result.isOk()) {
Record user = result.recordOrThrow();
System.out.println(user.getString("name"));
}
});

📖 API reference: Session.query(...) | ChainableQueryBuilder.execute() | RecordStream.forEach(...) | RecordResult.isOk() | RecordResult.recordOrThrow() | Record.getString(...)

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);
}
}
}

📖 API reference: ClusterDefinition(String,int) | ClusterDefinition.connect() | Cluster.createSession(Behavior) | Cluster.close() | DataSet.of(...) | DataSet.ids(...) | Session.delete(...) | ChainableNoBinsBuilder.execute() | RecordStream.close() | Session.insert(DataSet) | OperationObjectBuilder.bins(...) | IdValuesBuilder.id(...) | IdValuesRowBuilder.id(...) | IdValuesRowBuilder.values(...) | IdValuesRowBuilder.execute() | Session.query(...) | ChainableQueryBuilder.where(...) | ChainableQueryBuilder.readingOnlyBins(...) | ChainableQueryBuilder.execute() | RecordStream.forEach(...) | RecordResult.isOk() | RecordResult.recordOrThrow() | Record.getString(...) | Record.getInt(...) | RecordStream.hasNext() | RecordStream.next()

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

MethodDescription
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 forIterate results; call stream.close() when done

Next steps

Feedback

Was this page helpful?

What type of feedback are you giving?

What would you like us to know?

+Capture screenshot

Can we reach out to you?