Skip to content

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;

Update specific bins

Use update() to modify specific bins in an existing record:

DataSet users = DataSet.of("test", "users");
// Update only the email bin
session.update(users.id("user-1"))
.bin("email").setTo("newemail@example.com")
.execute();
// Update multiple bins
session.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()

See also: Insert vs upsert vs other operations

Increment numbers

Atomically increment numeric values:

DataSet users = DataSet.of("test", "users");
// Increment view count by 1
session.update(users.id("user-1"))
.bin("view_count").add(1)
.execute();
// Increment by a larger amount
session.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 operation
session.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()

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(...)

Append to strings

Append text to string bins:

DataSet users = DataSet.of("test", "users");
// Append to a log field
session.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()

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(...)

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 now
try {
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

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 bin
session.update(users.id("user-1"))
.bin("phone").remove()
.execute();
// Remove multiple bins
session.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()

Conditional update (optimistic locking)

Use generation checks to prevent concurrent update conflicts:

DataSet users = DataSet.of("test", "users");
// Read current record
RecordStream readStream = session.query(users.id("user-1")).execute();
Record user = readStream.getFirstRecord();
int currentGeneration = user.generation;
// Update only if generation hasn't changed
try {
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()

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(...)

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()

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()

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(...)

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

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()

API reference summary

MethodDescription
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

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?