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;from aerospike_sdk import DataSetGet 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(...)
users = DataSet.of("test", "users")
stream = await session.query(users.id("user-1")).execute()row = await stream.first_or_raise()user = row.record_or_raise()stream.close()print(f"Name: {user.bins['name']}")print(f"Email: {user.bins['email']}")print(f"Age: {user.bins['age']}")📖 API reference:
DataSet.of()|DataSet.id()|Session.query()|QueryBuilder.execute()|RecordStream.first_or_raise()|RecordStream.close()|RecordResult.record_or_raise()
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 presentRecordStream 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 foundRecord 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(...)
users = DataSet.of("test", "users")
stream = await session.query(users.id("nonexistent")).execute()row = await stream.first()if row is None: print("Record not found")stream.close()
# Or use a default when presentstream = await session.query(users.id("user-1")).execute()row = await stream.first()if row is None: name = "Not found"else: name = row.record_or_raise().bins["name"]stream.close()
# Or raise if not foundstream = await ( session.query(users.id("user-1")) .respond_all_keys() # Include errors for missing records in stream .execute())record = (await stream.first_or_raise()).record_or_raise() # Raises if missing📖 API reference:
DataSet.of()|DataSet.id()|Session.query()|QueryBuilder.respond_all_keys()|QueryBuilder.execute()|RecordStream.first()|RecordStream.first_or_raise()|RecordStream.close()|RecordResult.record_or_raise()
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 emailRecordStream 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(...)
users = DataSet.of("test", "users")
# Only fetch name and emailstream = await ( session.query(users.id("user-1")) .bins(["name", "email"]) .execute())row = await stream.first_or_raise()user = row.record_or_raise()stream.close()print(f"Name: {user.bins['name']}")print(f"Email: {user.bins['email']}")# user.bins.get("age") would be None - not fetched📖 API reference:
DataSet.of()|DataSet.id()|Session.query()|QueryBuilder.bins()|RecordResult.record_or_raise()|RecordStream.first_or_raise()|RecordStream.close()|QueryBuilder.execute()
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.
| SDK | API | Typical scope |
|---|---|---|
| Java | ChainableQueryBuilder.bin(...) → QueryBinBuilder | Key reads and batch key reads |
| Python | QueryBuilder.bin(...) → QueryBinBuilder | Key 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 version | Supported projection ops |
|---|---|
| Prior to 8.1.2 | Basic reads only: Operation.get_bin(...), Operation.get_header() |
| 8.1.2 and later | CDT, 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()
Requires Aerospike Server 8.1.2+ when used on filtered set queries.
products = DataSet.of("test", "products")
stream = await ( session.query(products.id("sku-42")) .bin("name").get() .bin("settings").on_map_key("theme").get_values() .execute())row = await stream.first_or_raise()record = row.record_or_raise()stream.close()print(f"name: {record.bins['name']}, theme: {record.bins['settings']}")📖 API reference:
QueryBinBuilder.on_map_key()|CdtReadBuilder.get_values()
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()
Requires Aerospike Server 8.1.2+ when used on filtered set queries.
users = DataSet.of("test", "users")
stream = await ( session.query(users.id("user-1")) .bin("age").get() .bin("ageIn10Years").select_from("$.age + 10") .execute())row = await stream.first_or_raise()user = row.record_or_raise()stream.close()print(f"age: {user.bins['age']}, ageIn10Years: {user.bins['ageIn10Years']}")📖 API reference:
QueryBinBuilder.select_from()|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()
users = DataSet.of("test", "users")
stream = await session.query(users.id("user-1")).execute()row = await stream.first_or_raise()user = row.record_or_raise()stream.close()generation = user.generation # incremented on each updatettl_seconds = user.ttl # seconds until expiration; None when the record has no TTLkey = row.key.value # user key (if send_key was True during write)print(f"Generation: {generation}")print(f"TTL: {ttl_seconds}s")print(f"Key: {key}")📖 API reference:
DataSet.of()|DataSet.id()|Session.query()|RecordResult.record_or_raise()|RecordStream.first_or_raise()|RecordStream.close()|QueryBuilder.execute()
| Metadata | Description |
|---|---|
| Generation | Version counter, incremented on each update. Use for optimistic locking. |
| TTL | Seconds 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.) |
| Key | Original 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 valuesString name = user.getString("name");
// Integer valuesint age = user.getInt("age");long bigNumber = user.getLong("big_number");
// Double valuesdouble balance = user.getDouble("balance");
// Boolean valuesboolean active = user.getBoolean("active");
// List valuesList<String> tags = user.getList("tags");
// Map valuesMap<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(...)
users = DataSet.of("test", "users")
stream = await session.query(users.id("user-1")).execute()row = await stream.first_or_raise()user = row.record_or_raise()stream.close()
# Bin values come back as native Python types — no typed getter or cast needed.# The annotations below are optional readability hints, not runtime checks.name: str = user.bins["name"]age: int = user.bins["age"]balance: float = user.bins["balance"]active: bool = user.bins["active"]tags: list = user.bins["tags"]preferences: dict = user.bins["preferences"]value = user.bins.get("unknown_field") # None if absent📖 API reference:
DataSet.of()|DataSet.id()|Session.query()|RecordResult.record_or_raise()|RecordStream.first_or_raise()|RecordStream.close()|QueryBuilder.execute()
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()
users = DataSet.of("test", "users")
stream = await session.exists(users.id("user-1")).execute()row = await stream.first()stream.close()
if row is not None and row.as_bool(): print("User exists")else: print("User not found")📖 API reference:
DataSet.of()|DataSet.id()|Session.exists()|RecordResult.as_bool()|RecordStream.first()|RecordStream.close()|WriteSegmentBuilder.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()
users = DataSet.of("test", "users")
stream = await session.query(users.id("user-1")).with_no_bins().execute()row = await stream.first_or_raise()header = row.record_or_raise()stream.close()
print(f"Generation: {header.generation}")print(f"TTL: {header.ttl}")# header.bins is {} — no bin payload fetched📖 API reference:
DataSet.of()|DataSet.id()|Session.query()|RecordResult.record_or_raise()|RecordStream.first_or_raise()|RecordStream.close()|QueryBuilder.execute()
Batch read
For reading multiple records efficiently, see Batch Operations.
DataSet users = DataSet.of("test", "users");
// Quick preview - see Batch Operations for full detailsRecordStream 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()
users = DataSet.of("test", "users")
# Quick preview - see Batch Operations for full detailsstream = await session.query(users.ids("user-1", "user-2", "user-3")).execute()results = await stream.collect()records = [r.record_or_raise() for r in results if r.is_ok]stream.close()📖 API reference:
DataSet.of()|DataSet.ids()|Session.query()|RecordResult.record_or_raise()|RecordStream.collect()|RecordStream.close()|QueryBuilder.execute()
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(...)
import asynciofrom aerospike_sdk import Behavior, ClusterDefinition, DataSet
async def main(): async with await ClusterDefinition("localhost", 3000).connect() as cluster: session = cluster.create_session(Behavior.DEFAULT) users = DataSet.of("test", "users") key = users.id("read-example-user")
# Seed data so the example is repeatable. await session.upsert(key).put( {"name": "Alice Smith", "email": "alice@example.com", "age": 28} ).execute()
# Full record read stream = await session.query(key).execute() row = await stream.first_or_raise() user = row.record_or_raise() stream.close() print("=== Full Record ===") print(f"Name: {user.bins['name']}") print(f"Generation: {user.generation}")
# Partial read stream = await ( session.query(key) .bins(["name", "email"]) .execute() ) row = await stream.first_or_raise() user = row.record_or_raise() stream.close() print("\n=== Selected Bins ===") print(f"Name: {user.bins['name']}") print(f"Email: {user.bins['email']}")
# Existence check # exists() yields a row only when the record is found; for a missing # key the stream is empty, so use first() + None check rather than # first_or_raise(). stream = await session.exists(key).execute() row = await stream.first() exists = row is not None and row.as_bool() stream.close() print(f"\nUser exists: {exists}")
if __name__ == "__main__": asyncio.run(main())📖 API reference:
ClusterDefinition|ClusterDefinition.connect()|Cluster.create_session()|DataSet.of()|DataSet.id()|Behavior.DEFAULT|Session.query()|Session.upsert()|Session.exists()|QueryBuilder.bins()|WriteSegmentBuilder.put()|RecordResult.record_or_raise()|RecordResult.as_bool()|RecordStream.first()|RecordStream.first_or_raise()|RecordStream.close()|QueryBuilder.execute()|WriteSegmentBuilder.execute()
API reference summary
| Method | Description |
|---|---|
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
Update Records
Modify the records you’ve read.
Query Records
Find records with DSL queries.
Batch Operations
Read multiple records efficiently.