Update 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 update existing records in Aerospike using the Developer SDK. This guide covers updating bins, using operations, conditional updates, and optimistic locking.
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 time
from aerospike_sdk import DataSet, GenerationErrorfrom aerospike_sdk.exceptions import AerospikeErrorUpdate specific bins
Use update() to modify specific bins in an existing record:
DataSet users = DataSet.of("test", "users");
// Update only the email binsession.update(users.id("user-1")) .bin("email").setTo("newemail@example.com") .execute();
// Update multiple binssession.update(users.id("user-1")) .bin("email").setTo("newemail@example.com") .bin("phone").setTo("+1-555-1234") .bin("updated_at").setTo(System.currentTimeMillis()) .execute();📖 API reference:
DataSet.of(...)|DataSet.id(...)|Session.update(DataSet)|ChainableOperationBuilder.bin(...)|ChainableQueryBuilder.bin(...)|ChainableQueryBuilder.execute()
users = DataSet.of("test", "users")
# Update only the email binawait ( session.update(users.id("user-1")) .bin("email").set_to("newemail@example.com") .execute())
# Update multiple binsawait ( session.update(users.id("user-1")) .bin("email").set_to("newemail@example.com") .bin("phone").set_to("+1-555-1234") .bin("updated_at").set_to(int(time.time() * 1000)) .execute())📖 API reference:
DataSet.of()|DataSet.id()|Session.update()|QueryBuilder.bin()|WriteSegmentBuilder.set_to()|WriteSegmentBuilder.bin()|WriteSegmentBuilder.execute()
See also: Insert vs upsert vs other operations
Increment numbers
Atomically increment numeric values:
DataSet users = DataSet.of("test", "users");
// Increment view count by 1session.update(users.id("user-1")) .bin("view_count").add(1) .execute();
// Increment by a larger amountsession.update(users.id("user-1")) .bin("points").add(100) .execute();
// Decrement (negative increment)session.update(users.id("user-1")) .bin("credits").add(-10) .execute();
// Multiple increments in one operationsession.update(users.id("user-1")) .bin("login_count").add(1) .bin("points").add(5) .bin("last_login").setTo(System.currentTimeMillis()) .execute();📖 API reference:
DataSet.id(...)|Session.update(DataSet)|ChainableOperationBuilder.bin(...)|ChainableQueryBuilder.bin(...)|BinBuilder.add(...)|ChainableQueryBuilder.execute()
users = DataSet.of("test", "users")
# Increment view count by 1await ( session.update(users.id("user-1")) .bin("view_count").add(1) .execute())
# Increment by a larger amountawait ( session.update(users.id("user-1")) .bin("points").add(100) .execute())
# Decrement (negative increment)await ( session.update(users.id("user-1")) .bin("credits").add(-10) .execute())
# Multiple increments in one operationawait ( session.update(users.id("user-1")) .bin("login_count").add(1) .bin("points").add(5) .bin("last_login").set_to(int(time.time() * 1000)) .execute())📖 API reference:
DataSet.of()|DataSet.id()|Session.update()|QueryBuilder.bin()|WriteSegmentBuilder.set_to()|WriteSegmentBuilder.bin()|WriteSegmentBuilder.execute()
Read back after incrementing:
try (RecordStream writeResult = session.update(users.id("user-1")) .bin("view_count").add(1) .bin("view_count").get() .execute()) { Record updated = writeResult.getFirstRecord(); // When multiple operations target the same bin, bins.get() returns an // OpResults list: one entry per operation in order. The add() result is // null; the get() result is the new value. Extract the last element: List<?> ops = (List<?>) updated.bins.get("view_count"); long newViewCount = ((Number) ops.get(ops.size() - 1)).longValue(); System.out.println("view_count: " + newViewCount);}📖 API reference:
DataSet.id(...)|Session.update(DataSet)|ChainableOperationBuilder.bin(...)|ChainableQueryBuilder.bin(...)|BinBuilder.add(...)|ChainableQueryBuilder.execute()|RecordStream.getFirstRecord()|Record.getInt(...)
# Increment first.await ( session.update(users.id("user-1")) .bin("view_count").add(1) .execute())
# Read back the updated value.stream = await ( session.query(users.id("user-1")) .bins(["view_count"]) .execute())row = await stream.first_or_raise()record = row.record_or_raise()stream.close()print(f"view_count: {record.bins['view_count']}")📖 API reference:
DataSet.of()|DataSet.id()|Session.query()|Session.update()|QueryBuilder.bins()|QueryBuilder.bin()|WriteSegmentBuilder.bin()|RecordResult.record_or_raise()|RecordStream.first_or_raise()|RecordStream.close()|QueryBuilder.execute()|WriteSegmentBuilder.execute()
Append to strings
Append text to string bins:
DataSet users = DataSet.of("test", "users");
// Append to a log fieldsession.update(users.id("user-1")) .bin("activity_log").append("\n2024-01-15: Logged in") .execute();
// Prepend (add to beginning)session.update(users.id("user-1")) .bin("activity_log").prepend("Latest: ") .execute();📖 API reference:
DataSet.id(...)|Session.update(DataSet)|ChainableOperationBuilder.bin(...)|ChainableQueryBuilder.bin(...)|ChainableQueryBuilder.execute()
users = DataSet.of("test", "users")
# Append to a log fieldawait ( session.update(users.id("user-1")) .bin("activity_log").append("\n2024-01-15: Logged in") .execute())
# Prepend (add to beginning)await ( session.update(users.id("user-1")) .bin("activity_log").prepend("Latest: ") .execute())📖 API reference:
DataSet.of()|DataSet.id()|Session.update()|WriteSegmentBuilder.execute()
Update list/map bins (CDT)
Collection updates use the bin(...) builder with list/map path methods.
This lets you update nested structures without a full read-modify-write cycle.
// Additional imports for this example:import com.aerospike.client.sdk.cdt.MapOrder;
DataSet hotels = DataSet.of("test", "hotels");String key = "hotel:1";
// CD-1 style: set a map entry value.session.update(hotels.id(key)) // Note: the following call fails if `rooms` does not contain a map key `room1`. To create it automatically if missing, use: // .bin("rooms").onMapKey("room1", MapOrder.KEY_ORDERED).setTo(150) .bin("rooms").onMapKey("room1").setTo(150) .execute();
// CD-2 style: one round-trip with range read + count.try (RecordStream rs = session.update(hotels.id(key)) .bin("rooms").onMapKeyRange("room1", "room4").getKeysAndValues() .bin("rooms").onMapKeyRange("room1", "room4").count() .execute()) { var rec = rs.getFirstRecord(); List<?> roomData = rec.getList("rooms"); System.out.println("rooms: " + roomData.get(0)); System.out.println("room count: " + roomData.get(1)); // rangeResult = map entries in range, countResult = count}
// CD-3 style: append to list and read list size in one round-trip.try (RecordStream rs = session.update(hotels.id(key)) .bin("tags").listAppend("vip") .bin("tags").listSize() .execute()) { // Second result row contains size information.}
// CD-4 style: nested path update (rooms -> room1 -> rates).session.update(hotels.id(key)) .bin("rooms").onMapKey("room1", MapOrder.KEY_ORDERED) .onMapKey("rates").setTo(110) .execute();📖 API reference:
DataSet.of(...)|Session.update(DataSet)|ChainableOperationBuilder.bin(...)|ChainableQueryBuilder.bin(...)|BinBuilder.onMapKey(...)|BinBuilder.onMapKeyRange(...)|CdtReadOnlyBuilder.getKeysAndValues()|CdtReadOnlyBuilder.count()|ChainableQueryBuilder.execute()|RecordStream.getFirstRecord()|Record.getList(...)
hotels = DataSet.of("test", "hotels")key = hotels.id("hotel:1")
# CD-1 style: set a map entry value.await session.update(key).bin("rooms").on_map_key("room1").set_to(150).execute()
# CD-2 style: one round-trip with range read + count.stream = await ( session.update(key) .bin("rooms").on_map_key_range("room1", "room4").get_keys_and_values() .bin("rooms").on_map_key_range("room1", "room4").count() .execute())row = await stream.first_or_raise()record = row.record_or_raise()stream.close()room_data = record.bins["rooms"]print(f"rooms: {room_data[0]}")print(f"room count: {room_data[1]}")# range_result = map entries in range, count_result = count
# CD-3 style: append to list and read list size in one round-trip.await ( session.update(key) .bin("tags").list_append("vip") .bin("tags").list_size() .execute())
# CD-4 style: nested path update (rooms -> room1 -> rates).await ( session.update(key) .bin("rooms").on_map_key("room1") .on_map_key("rates").set_to(110) .execute())📖 API reference:
DataSet.of()|DataSet.id()|Session.update()|WriteSegmentBuilder.bin()|WriteBinBuilder.on_map_key()|WriteBinBuilder.on_map_key_range()|WriteBinBuilder.list_append()|WriteBinBuilder.list_size()|WriteSegmentBuilder.execute()|RecordStream.first_or_raise()|RecordStream.close()|RecordResult.record_or_raise()
Touch (update metadata only)
“Touch” a record to reset its TTL without changing bin values:
// Additional imports for this example:import java.time.Duration;import com.aerospike.client.sdk.AerospikeException;
DataSet users = DataSet.of("test", "users");
// Reset TTL to 30 days from nowtry { session.touch(users.id("user-1")) .expireRecordAfter(Duration.ofDays(30)) .execute();} catch (AerospikeException e) { // If server/namespace TTL prerequisites are not met, you can get "Operation not allowed at this time". System.err.println("Touch failed: " + e.getMessage());}📖 API reference:
DataSet.id(...)|Session.touch(Key)|ChainableQueryBuilder.execute()|AerospikeException
users = DataSet.of("test", "users")
# Reset TTL to 30 days from nowtry: await ( session.touch(users.id("user-1")) .expire_record_after_seconds(30 * 24 * 3600) .execute() )except AerospikeError as e: # If server/namespace TTL prerequisites are not met, you can get "Operation not allowed at this time". print(f"Touch failed: {e}")📖 API reference:
DataSet.of()|DataSet.id()|Session.touch()|WriteSegmentBuilder.expire_record_after_seconds()|WriteSegmentBuilder.execute()|AerospikeError
Delete bins
Remove specific bins from a record without deleting the record. Note: if all bins of a record are removed, the record is automatically deleted:
DataSet users = DataSet.of("test", "users");
// Remove the phone binsession.update(users.id("user-1")) .bin("phone").remove() .execute();
// Remove multiple binssession.update(users.id("user-1")) .bin("phone").remove() .bin("fax").remove() .bin("pager").remove() .execute();📖 API reference:
DataSet.id(...)|Session.update(DataSet)|ChainableOperationBuilder.bin(...)|ChainableQueryBuilder.bin(...)|BinBuilder.remove()|ChainableQueryBuilder.execute()
users = DataSet.of("test", "users")
# Remove the phone binawait ( session.update(users.id("user-1")) .bin("phone").remove() .execute())
# Remove multiple binsawait ( session.update(users.id("user-1")) .bin("phone").remove() .bin("fax").remove() .bin("pager").remove() .execute())📖 API reference:
DataSet.of()|DataSet.id()|Session.update()|WriteSegmentBuilder.execute()
Conditional update (optimistic locking)
Use generation checks to prevent concurrent update conflicts:
DataSet users = DataSet.of("test", "users");
// Read current recordRecordStream readStream = session.query(users.id("user-1")).execute();Record user = readStream.getFirstRecord();
int currentGeneration = user.generation;
// Update only if generation hasn't changedtry { session.update(users.id("user-1")) .bin("email").setTo("newemail@example.com") .ensureGenerationIs(currentGeneration) .execute(); System.out.println("Update successful");} catch (GenerationException e) { System.out.println("Record was modified by another process");}📖 API reference:
DataSet.id(...)|Session.update(DataSet)|Session.query(Key)|ChainableOperationBuilder.bin(...)|ChainableQueryBuilder.bin(...)|ChainableQueryBuilder.execute()|RecordStream.getFirstRecord()
users = DataSet.of("test", "users")
# Read current recordstream = await session.query(users.id("user-1")).execute()row = await stream.first_or_raise()user = row.record_or_raise()
current_generation = user.generation
# Update only if generation hasn't changedtry: await ( session.update(users.id("user-1")) .bin("email").set_to("newemail@example.com") .ensure_generation_is(current_generation) .execute() ) print("Update successful")except GenerationError: print("Record was modified by another process")📖 API reference:
DataSet.of()|DataSet.id()|Session.query()|Session.update()|QueryBuilder.bin()|WriteSegmentBuilder.set_to()|WriteSegmentBuilder.bin()|WriteSegmentBuilder.ensure_generation_is()|RecordResult.record_or_raise()|RecordStream.first_or_raise()|QueryBuilder.execute()|WriteSegmentBuilder.execute()
Optimistic locking pattern
public void updateWithRetry(DataSet users, String userId, int maxRetries) { for (int attempt = 0; attempt < maxRetries; attempt++) { // Read current state RecordStream rs = session.query(users.id(userId)).execute(); Record user = rs.getFirstRecord();
int newBalance = user.getInt("balance") + 100;
try { // Attempt update with generation check session.update(users.id(userId)) .bin("balance").setTo(newBalance) .ensureGenerationIs(user.generation) .execute(); return; // Success } catch (GenerationException e) { // Retry on conflict System.out.println("Conflict, retrying..."); } } throw new RuntimeException("Max retries exceeded");}📖 API reference:
Session.update(DataSet)|Session.query(Key)|ChainableOperationBuilder.bin(...)|ChainableQueryBuilder.bin(...)|ChainableQueryBuilder.execute()|RecordStream.getFirstRecord()|Record.getInt(...)
async def update_with_retry(users: DataSet, user_id: str, max_retries: int) -> None: for attempt in range(max_retries): # Read current state stream = await session.query(users.id(user_id)).execute() row = await stream.first_or_raise() user = row.record_or_raise()
new_balance = user.bins["balance"] + 100
try: # Attempt update with generation check await ( session.update(users.id(user_id)) .bin("balance").set_to(new_balance) .ensure_generation_is(user.generation) .execute() ) return # Success except GenerationError: # Retry on conflict print("Conflict, retrying...") raise RuntimeError("Max retries exceeded")📖 API reference:
Session.query()|Session.update()|QueryBuilder.bin()|WriteSegmentBuilder.set_to()|WriteSegmentBuilder.bin()|WriteSegmentBuilder.ensure_generation_is()|RecordResult.record_or_raise()|RecordStream.first_or_raise()|QueryBuilder.execute()|WriteSegmentBuilder.execute()
Update only if exists
Ensure the record exists before updating:
// Additional imports for this example:import com.aerospike.client.sdk.AerospikeException.RecordNotFoundException; // nested class, not a top-level import
DataSet users = DataSet.of("test", "users");
try { session.update(users.id("user-1")) .bin("email").setTo("newemail@example.com") .execute();} catch (RecordNotFoundException e) { System.out.println("Record doesn't exist");}📖 API reference:
DataSet.id(...)|Session.update(DataSet)|ChainableOperationBuilder.bin(...)|ChainableQueryBuilder.bin(...)|ChainableQueryBuilder.execute()
# Additional imports for this example:from aerospike_sdk.exceptions import ResultCode
users = DataSet.of("test", "users")
try: await ( session.update(users.id("user-1")) .bin("email").set_to("newemail@example.com") .execute() )except AerospikeError as e: if e.result_code != ResultCode.KEY_NOT_FOUND_ERROR: raise print("Record doesn't exist")📖 API reference:
DataSet.of()|DataSet.id()|Session.update()|QueryBuilder.bin()|WriteSegmentBuilder.set_to()|WriteSegmentBuilder.bin()|WriteSegmentBuilder.execute()
Replace all bins (atomic full-record overwrite)
Use session.replace(key) to atomically overwrite a record with a new set of bins, removing any bins that were on the record before. This is the correct way to do a full-record replace in a single write without reading first. The record is created if it does not already exist.
DataSet users = DataSet.of("test", "users");
// Atomically replace all bins; old bins like "active" are removed.// session.replace() creates the record if it does not already exist.session.replace(users.id("user-1")) .bin("name").setTo("Alice Smith") .bin("age").setTo(31) .execute();📖 API reference:
DataSet.of(...)|DataSet.id(...)|Session.replace(DataSet)|ChainableOperationBuilder.bin(...)|BinBuilder.setTo(...)|ChainableQueryBuilder.execute()
users = DataSet.of("test", "users")
# Atomically replace all bins; old bins like "active" are removed.# session.replace() creates the record if it does not already exist.await ( session.replace(users.id("user-1")) .put({"name": "Alice Smith", "age": 31}) .execute())📖 API reference:
Session.replace()|WriteSegmentBuilder.put()|WriteSegmentBuilder.execute()
GeoJSON bins
Write a bin as a GeoJSON value. The server stores a GEOJSON particle type (not a string), which makes the bin eligible for GEO2DSPHERE indexing and geoCompare(...) queries.
DataSet places = DataSet.of("test", "places");
session.upsert(places.id("space_needle")) .bin("loc").setToGeoJson("{\"type\":\"Point\",\"coordinates\":[-122.349,47.620]}") .execute();📖 API reference:
BinBuilder.setToGeoJson(...)
places = DataSet.of("test", "places")
await ( session.upsert(places.id("space_needle")) .bin("loc").set_to_geo_json('{"type":"Point","coordinates":[-122.349,47.620]}') .execute())📖 API reference:
WriteSegmentBuilder.set_to_geo_json()
HyperLogLog (HLL) bins
Use HllConfig to describe sketch precision, then initialize and add values on a bin:
// Additional imports for this example:import com.aerospike.client.sdk.HllConfig;
DataSet visitors = DataSet.of("test", "visitors");
session.upsert(visitors.id("day_1")) .bin("h").hllInit(HllConfig.of(14)) .bin("h").hllAdd(List.of("user-1", "user-2", "user-3")) .execute();
// Create the sketch only if it doesn't exist; no-op if it already does.session.upsert(visitors.id("day_2")) .bin("h").hllInit(HllConfig.of(14), opt -> opt.createOnly().noFail()) .execute();📖 API reference:
HllConfig.of(...)|BinBuilder.hllInit(...)|BinBuilder.hllAdd(...)|HllWriteOptions
# Additional imports for this example:from aerospike_sdk import HllConfig
visitors = DataSet.of("test", "visitors")
await ( session.upsert(visitors.id("day_1")) .bin("h").hll_init(HllConfig.of(14)) .bin("h").hll_add(["user-1", "user-2", "user-3"]) .execute())
# Create the sketch only if it doesn't exist; no-op if it already does.await ( session.upsert(visitors.id("day_2")) .bin("h").hll_init(HllConfig.of(14), create_only=True, no_fail=True) .execute())📖 API reference:
HllConfig|WriteSegmentBuilder.bin()|WriteBinBuilder.hll_init()|WriteBinBuilder.hll_add()
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 UpdateRecordsExample { 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 = "update-example-user";
// Seed data so the example is repeatable. session.upsert(users) .bins("name", "login_count", "points") .id(key).values("Alice Smith", 0, 0) .execute();
// Simple update session.update(users.id(key)) .bin("status").setTo("active") .bin("updated_at").setTo(System.currentTimeMillis()) .execute();
// Increment counters session.update(users.id(key)) .bin("login_count").add(1) .bin("points").add(10) .execute();
// Conditional update with generation check RecordStream rs = session.query(users.id(key)).execute(); Record user = rs.getFirstRecord(); session.update(users.id(key)) .bin("verified").setTo(true) .ensureGenerationIs(user.generation) .execute();
System.out.println("All updates completed!"); } }}📖 API reference:
ClusterDefinition(String,int)|ClusterDefinition.connect()|Cluster.createSession(Behavior)|Cluster.close()|DataSet.of(...)|Session.upsert(DataSet)|Session.update(DataSet)|Session.query(Key)|OperationObjectBuilder.bins(...)|IdValuesBuilder.id(...)|IdValuesRowBuilder.values(...)|ChainableOperationBuilder.bin(...)|ChainableQueryBuilder.bin(...)|BinBuilder.add(...)|ChainableQueryBuilder.execute()|RecordStream.getFirstRecord()
import asyncioimport timefrom 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("update-example-user")
# Seed data so the example is repeatable. await session.upsert(key).put( {"name": "Alice Smith", "login_count": 0, "points": 0} ).execute()
# Simple update await ( session.update(key) .bin("status").set_to("active") .bin("updated_at").set_to(int(time.time() * 1000)) .execute() )
# Increment counters await ( session.update(key) .bin("login_count").add(1) .bin("points").add(10) .execute() )
# Conditional update with generation check stream = await session.query(key).execute() row = await stream.first_or_raise() user = row.record_or_raise() await ( session.update(key) .bin("verified").set_to(True) .ensure_generation_is(user.generation) .execute() )
print("All updates completed!")
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.update()|QueryBuilder.bin()|WriteSegmentBuilder.put()|WriteSegmentBuilder.set_to()|WriteSegmentBuilder.bin()|WriteSegmentBuilder.ensure_generation_is()|RecordResult.record_or_raise()|RecordStream.first_or_raise()|QueryBuilder.execute()|WriteSegmentBuilder.execute()
API reference summary
| Method | Description |
|---|---|
update() | Update an existing record, fails if the record doesn’t exist |
replace() / replace_if_exists() | Atomic full-record overwrite (drops bins not in the call) |
touch() | Update metadata/TTL only |
.bin(name).add(delta) (Java and Python; Python also aliases as .increment_by(delta)) | Atomically increment a numeric bin |
.bin(name).setToGeoJson(json) (Java) · .bin(name).set_to_geo_json(json) (Python) | Write a GeoJSON bin |
.bin(name).hllInit(...) / .hllAdd(...) (Java) · .bin(name).hll_init(...) / .hll_add(...) (Python) | HyperLogLog sketch operations |
.bin(name).append(text) / .prepend(text) (Java and Python) | Append or prepend a string bin |
.bin(name).remove() (Java and Python) | Remove a bin |
.ensureGenerationIs(gen) (Java) · .ensure_generation_is(gen) (Python) | Conditional update |
Next steps
Delete Records
Remove records from the database.
Batch Operations
Update multiple records efficiently.
Error Handling
Handle update conflicts and errors.