Skip to content

Read 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 read records from Aerospike using the Developer SDK. This guide covers getting records by key, selecting specific bins, handling missing records, and reading metadata.

Except where noted, snippets on this page use the imports below. A snippet lists additional import lines only when it needs a type not shown here. When this page includes a Complete example section, that block is fully self-contained with every import required to run it.

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 java.util.List;
import java.util.Map;
import java.util.Optional;

Get a record by key

Use query() with a key to read a single record (one row in the stream):

DataSet users = DataSet.of("test", "users");
RecordStream stream = session.query(users.id("user-1")).execute();
stream.getFirst().ifPresent(result -> {
Record user = result.recordOrThrow();
System.out.println("Name: " + user.getString("name"));
System.out.println("Email: " + user.getString("email"));
System.out.println("Age: " + user.getInt("age"));
});
stream.close();

📖 API reference: DataSet.of(...) | DataSet.id(...) | Session.query(Key) | ChainableQueryBuilder.execute() | RecordStream.getFirst() | RecordStream.close() | RecordResult.recordOrThrow() | Record.getString(...) | Record.getInt(...)

Handle missing records

When a record doesn’t exist, the stream has no row (or the row signals not found):

DataSet users = DataSet.of("test", "users");
RecordStream stream = session.query(users.id("nonexistent")).execute();
if (stream.getFirst().isEmpty()) {
System.out.println("Record not found");
}
Optional<RecordResult> rec = session.query(users.id("nonexistent")).execute().getFirst();
rec.ifPresentOrElse(
rr -> System.out.println("value found: " + rr.recordOrThrow()),
() -> System.out.println("Record not found")
);
// Or use a default when present
RecordStream s2 = session.query(users.id("user-1")).execute();
String user = s2.getFirst()
.filter(RecordResult::isOk)
.map(result-> result.recordOrThrow().getString("name"))
.orElse("Not found");
s2.close();
// Or throw if not found
Record s3 = session.query(users.id("user-1"))
.includeMissingKeys() // Include errors for missing records in stream
.execute()
.getFirstRecord(); // Throws if record is missing

📖 API reference: DataSet.id(...) | Session.query(Key) | ChainableQueryBuilder.includeMissingKeys() | ChainableQueryBuilder.execute() | RecordStream.getFirstRecord() | RecordStream.getFirst() | RecordStream.close() | RecordResult.recordOrThrow() | Record.getString(...)

Select specific bins

By default, a key query returns all bins. Use bins() to retrieve only the bins you need:

DataSet users = DataSet.of("test", "users");
// Only fetch name and email
RecordStream stream = session.query(users.id("user-1"))
.readingOnlyBins("name", "email")
.execute();
stream.getFirst().ifPresent(result -> {
Record user = result.recordOrThrow();
System.out.println("Name: " + user.getString("name"));
System.out.println("Email: " + user.getString("email"));
// user.getInt("age") would be null - not fetched
});
stream.close();

📖 API reference: DataSet.id(...) | Session.query(Key) | ChainableQueryBuilder.readingOnlyBins(...) | ChainableQueryBuilder.execute() | RecordStream.getFirst() | RecordStream.close() | RecordResult.recordOrThrow() | Record.getString(...) | Record.getInt(...)

Project reads with ops projection

Chain read operations on session.query(...) so the server returns operation results (selected bins, CDT paths, or computed values) instead of full records. This is an alternative to .readingOnlyBins(...) / .bins([...]) when a plain bin list is not enough.

SDKAPITypical scope
JavaChainableQueryBuilder.bin(...)QueryBinBuilderKey reads and batch key reads
PythonQueryBuilder.bin(...)QueryBinBuilderKey reads and filtered set queries (see Query records)

When ops are chained on a query, they take precedence over bin projection (.readingOnlyBins(...) / .bins([...])). Write, touch, and delete operations in a read query are rejected by the server.

Server compatibility (Python set queries with extended ops)

Server versionSupported projection ops
Prior to 8.1.2Basic reads only: Operation.get_bin(...), Operation.get_header()
8.1.2 and laterCDT, expression, bit, and HLL read operations in addition to basic reads

For plain bin selection (a flat list of whole bins), use .readingOnlyBins(...) / .bins([...]) — see Select specific bins. Ops projection is for when a bin list isn’t enough: nested map/list slices or server-computed values, shown below.

Project a nested map value

DataSet products = DataSet.of("test", "products");
RecordStream stream = session.query(products.id("sku-42"))
.bin("name").get()
.bin("settings").onMapKey("theme").getValues()
.execute();
stream.getFirst().ifPresent(result -> {
Record rec = result.recordOrThrow();
System.out.printf("name: %s, theme: %s\n",
rec.getString("name"),
rec.getString("settings"));
});
stream.close();

📖 API reference: QueryBinBuilder.onMapKey(...) | CdtReadContextBuilder.getValues()

Project a computed expression

Use QueryBinBuilder.selectFrom(String) with an AEL expression. The bin name in .bin(...) is the output bin for the computed result.

DataSet users = DataSet.of("test", "users");
RecordStream stream = session.query(users.id("user-1"))
.bin("name").get()
.bin("ageIn10Years").selectFrom("$.age + 10")
.execute();
stream.getFirst().ifPresent(result -> {
Record user = result.recordOrThrow();
System.out.println("Name: " + user.getString("name"));
System.out.println("ageIn10Years: " + user.getLong("ageIn10Years"));
});
stream.close();

📖 API reference: QueryBinBuilder.selectFrom(...) | QueryBinBuilder.get()

The Python sync client exposes the same chained builder methods (bin().get(), bin().on_map_key().get_values(), bin().select_from()) on sync query builders. For advanced cases where ops must be composed dynamically — e.g. building a list of operations from app logic — use QueryBuilder.with_op_projection(...), which accepts a sequence of pre-built Operation objects.

Read record metadata

Records have metadata you can access:

DataSet users = DataSet.of("test", "users");
RecordStream stream = session.query(users.id("user-1")).execute();
stream.getFirst().ifPresent(result -> {
Record user = result.recordOrThrow();
// Generation: incremented on each update
int generation = user.generation;
// TTL: seconds until expiration (0 = never)
int ttlSeconds = user.getTimeToLive();
// User key (if sendKey was true during write)
String key = result.key().userKey;
System.out.println("Generation: " + generation);
System.out.println("TTL: " + ttlSeconds + "s");
System.out.println("Key: " + key);
});
stream.close();

📖 API reference: DataSet.id(...) | Session.query(Key) | ChainableQueryBuilder.execute() | RecordStream.getFirst() | RecordStream.close() | RecordResult.recordOrThrow()

MetadataDescription
GenerationVersion counter, incremented on each update. Use for optimistic locking.
TTLSeconds until expiration. On read, Java’s getTimeToLive() returns 0 for a never-expire record; Python’s record.ttl is None when the record has no TTL set. (On write, 0 means never expire and -1 means use the namespace default.)
KeyOriginal user key (only available if send-key was enabled on the writing behavior — sendKey(true) in Java, send_key=True in Python).

Read typed bin values

Java exposes typed getters that coerce the server value to the requested type. Python returns native types directly from record.bins, so no getter or cast is needed:

DataSet users = DataSet.of("test", "users");
RecordStream stream = session.query(users.id("user-1")).execute();
Record user = stream.getFirst()
.filter(RecordResult::isOk)
.map(RecordResult::recordOrThrow)
.orElseThrow();
stream.close();
// String values
String name = user.getString("name");
// Integer values
int age = user.getInt("age");
long bigNumber = user.getLong("big_number");
// Double values
double balance = user.getDouble("balance");
// Boolean values
boolean active = user.getBoolean("active");
// List values
List<String> tags = user.getList("tags");
// Map values
Map<String, Object> preferences = user.getMap("preferences");
// Raw value (when type is unknown)
Object value = user.getValue("unknown_field");

📖 API reference: DataSet.id(...) | Session.query(Key) | ChainableQueryBuilder.execute() | RecordStream.getFirst() | RecordStream.close() | Record.getString(...) | Record.getInt(...) | Record.getLong(...) | Record.getList(...) | Record.getValue(...)

Check if a record exists

To check existence without fetching data:

DataSet users = DataSet.of("test", "users");
boolean exists = session.exists(users.id("user-1")).execute().getFirstBoolean().orElse(false);
if (exists) {
System.out.println("User exists");
} else {
System.out.println("User not found");
}

📖 API reference: DataSet.id(...) | Session.exists(Key) | ChainableQueryBuilder.execute()

Read header only

To get only metadata (generation, TTL) without bin values:

DataSet users = DataSet.of("test", "users");
RecordStream stream = session.query(users.id("user-1"))
.withNoBins()
.execute();
stream.getFirst().ifPresent(result -> {
Record header = result.recordOrThrow();
System.out.println("Generation: " + header.generation);
System.out.println("TTL: " + header.getTimeToLive());
// Bins are not populated
});
stream.close();

📖 API reference: DataSet.id(...) | Session.query(Key) | ChainableQueryBuilder.execute() | RecordStream.getFirst() | RecordStream.close() | RecordResult.recordOrThrow()

Batch read

For reading multiple records efficiently, see Batch Operations.

DataSet users = DataSet.of("test", "users");
// Quick preview - see Batch Operations for full details
RecordStream usersStream = session.query(users.ids("user-1", "user-2", "user-3")).execute();
List<Record> users = usersStream.stream().toList();

📖 API reference: DataSet.ids(...) | Session.query(List) | ChainableQueryBuilder.execute() | RecordStream.stream()

Complete example

This example is self-contained—it lists every import needed to run standalone.

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 ReadRecordsExample {
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 key = "read-example-user";
// Seed data so the example is repeatable.
session.upsert(users)
.bins("name", "email", "age")
.id(key).values("Alice Smith", "alice@example.com", 28)
.execute();
// Full record read
RecordStream full = session.query(users.id(key)).execute();
full.getFirst().ifPresent(result -> {
Record user = result.recordOrThrow();
System.out.println("=== Full Record ===");
System.out.println("Name: " + user.bins.get("name"));
System.out.println("Generation: " + user.generation);
});
full.close();
// Partial read
RecordStream partial = session.query(users.id(key))
.readingOnlyBins("name", "email")
.execute();
partial.getFirst().ifPresent(result -> {
Record user = result.recordOrThrow();
System.out.println("\n=== Selected Bins ===");
System.out.println("Name: " + user.getString("name"));
System.out.println("Email: " + user.getString("email"));
});
partial.close();
// Existence check
boolean exists = session.exists(users.id(key)).execute().getFirstBoolean().orElse(false);
System.out.println("\nUser exists: " + exists);
}
}
}

📖 API reference: ClusterDefinition(String,int) | ClusterDefinition.connect() | Cluster.createSession(Behavior) | Cluster.close() | DataSet.of(...) | Session.upsert(DataSet) | Session.exists(Key) | Session.query(Key) | OperationObjectBuilder.bins(...) | IdValuesBuilder.id(...) | IdValuesRowBuilder.values(...) | ChainableQueryBuilder.readingOnlyBins(...) | ChainableQueryBuilder.execute() | RecordStream.getFirst() | RecordStream.close() | RecordResult.recordOrThrow() | Record.getString(...)

API reference summary

MethodDescription
query(key)Read a record by key (single-row stream)
exists()Check if a record exists
.readingOnlyBins(...) (Java) · .bins([...]) (Python)Bin projection on queries; see Query records
.bin(...).get() / .selectFrom(...) / CDT paths (Java and Python)Return server-side read operation results instead of whole bins
.withNoBins() (Java) · .with_no_bins() (Python)Read only metadata (no bin payload)

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?