# Update records

Learn how to update existing records in Aerospike using the Developer SDK. This guide covers updating bins, using operations, conditional updates, and optimistic locking.

## Update specific bins

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

-   [Java](#tab-panel-3210)
-   [Python](#tab-panel-3211)

```java
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();
```

```python
import time

async def demo(session):

    users = DataSet.of("test", "users")

    # Update only the email bin

    await (

        session.update(users.id("user-1"))

        .bin("email").set_to("newemail@example.com")

        .execute()

    )

    # Update multiple bins

    await (

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

        .execute()

    )
```

::: boolean bin values in verification tools
When updating boolean bins, some low-level verification tools may report `true/false` as `1/0`. That representation is expected for Aerospike’s underlying value encoding.
:::
::: update vs upsert
`update()` only modifies existing records—it fails if the record doesn’t exist. Use `upsert()` if you want to create the record when it doesn’t exist.
:::

See also: [Insert vs upsert vs other operations](https://aerospike.com/docs/develop/client/sdk/usage/create/#insert-vs-upsert-vs-other-operations)

## Increment numbers

Atomically increment numeric values:

-   [Java](#tab-panel-3212)
-   [Python](#tab-panel-3213)

```java
// 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();
```

```python
import time

async def demo(session):

    users = DataSet.of("test", "users")

    # Increment view count by 1

    await (

        session.update(users.id("user-1"))

        .bin("view_count").increment_by(1)

        .execute()

    )

    # Increment by a larger amount

    await (

        session.update(users.id("user-1"))

        .bin("points").increment_by(100)

        .execute()

    )

    # Decrement (negative increment)

    await (

        session.update(users.id("user-1"))

        .bin("credits").increment_by(-10)

        .execute()

    )

    # Multiple increments in one operation

    await (

        session.update(users.id("user-1"))

        .bin("login_count").increment_by(1)

        .bin("points").increment_by(5)

        .bin("last_login").set_to(time.time())

        .execute()

    )
```

::: atomicity
Numeric increments are atomic — `bin(...).add(...)` in Java, `bin(...).increment_by(...)` in Python — so multiple clients can safely bump the same bin without race conditions.
:::

Read back after incrementing:

-   [Java](#tab-panel-3214)
-   [Python](#tab-panel-3215)

```java
try (RecordStream writeResult = session.update(users.id("user-1"))

        .bin("view_count").add(1)

        .bin("view_count").get()

        .execute()) {

    Record updated = writeResult.getFirstRecord();

    System.out.println("view_count: " + updated.getInt("view_count"));

}
```

```python
async def demo(session):

    users = DataSet.of("test", "users")

    # Increment first.

    stream = await session.update(users.id("user-1")) \

        .bin("view_count").increment_by(1) \

        .execute()

    stream.close()

    # Read back the updated value.

    read_stream = await session.query(users.id("user-1")) \

        .bins(["view_count"]) \

        .execute()

    row = await read_stream.first_or_raise()

    record = row.record_or_raise()

    read_stream.close()

    print(f"view_count: {record.bins['view_count']}")
```

## Append to strings

Append text to string bins:

-   [Java](#tab-panel-3216)
-   [Python](#tab-panel-3217)

```java
// 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();
```

```python
async def demo(session):

    users = DataSet.of("test", "users")

    # Append to a log field

    await (

        session.update(users.id("user-1"))

        .append("activity_log", "\n2024-01-15: Logged in")

        .execute()

    )

    # Prepend (add to beginning)

    await (

        session.update(users.id("user-1"))

        .prepend("activity_log", "Latest: ")

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

::: use cdt path methods for in-place updates
Prefer bin-path updates (for example `onMapKey(...)`, `listAppend(...)`) when modifying part of a list/map bin. Whole-bin replacement is still useful when replacing the entire structure.
:::

-   [Java](#tab-panel-3218)
-   [Python](#tab-panel-3219)

```java
import com.aerospike.client.sdk.DataSet;

import com.aerospike.client.sdk.RecordResult;

import com.aerospike.client.sdk.RecordStream;

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

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

```python
from aerospike_async import MapOrder

async def demo_cdt(session):

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

    )

    stream.close()

    # CD-3 style: append to list and read list size in one round-trip.

    stream = await session.update(key).bin("tags").list_append("vip").bin("tags").list_size().execute()

    stream.close()

    # CD-4 style: nested path update (rooms -> room1 -> rates).

    await (

        session.update(key)

        .bin("rooms").on_map_key("room1", create_type=MapOrder.KEY_ORDERED)

        .on_map_key("rates").set_to(110)

        .execute()

    )
```

## Touch (update metadata only)

“Touch” a record to reset its TTL without changing bin values:

-   [Java](#tab-panel-3220)
-   [Python](#tab-panel-3221)

```java
import java.time.Duration;

import com.aerospike.client.sdk.AerospikeException;

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

}
```

```python
async def demo(session):

    users = DataSet.of("test", "users")

    # Reset TTL to 30 days from now

    await (

        session.touch(users.id("user-1"))

        .expire_record_after_seconds(30 * 24 * 3600)

        .execute()

    )
```

::: touch/ttl prerequisites
Per-record TTL updates can fail with `Operation not allowed at this time` when namespace settings disallow TTL writes (for example `nsup-period=0` and `allow-ttl-without-nsup=false`). Enable NSUP or adjust namespace settings before relying on `touch(...).expireRecordAfter(...)`.
:::

## Delete bins

Remove specific bins from a record (without deleting the record):

-   [Java](#tab-panel-3222)
-   [Python](#tab-panel-3223)

```java
// 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();
```

```python
async def demo(session):

    users = DataSet.of("test", "users")

    # Remove the phone bin

    await (

        session.update(users.id("user-1"))

        .remove_bin("phone")

        .execute()

    )

    # Remove multiple bins

    await (

        session.update(users.id("user-1"))

        .remove_bin("phone")

        .remove_bin("fax")

        .remove_bin("pager")

        .execute()

    )
```

::: note
If all the bins in a record are removed, the record is automatically removed.
:::

## Conditional update (optimistic locking)

Use generation checks to prevent concurrent update conflicts:

-   [Java](#tab-panel-3224)
-   [Python](#tab-panel-3225)

```java
import com.aerospike.client.sdk.Record;

import com.aerospike.client.sdk.RecordResult;

import com.aerospike.client.sdk.RecordStream;

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

}
```

```python
from aerospike_sdk import GenerationError

async def demo(session):

    users = DataSet.of("test", "users")

    # Read current record

    stream = await session.query(users.id("user-1")).execute()

    row = await stream.first_or_raise()

    user = row.record_or_raise()

    stream.close()

    current_generation = user.generation

    # Update only if generation hasn't changed

    try:

        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")
```

### Optimistic locking pattern

-   [Java](#tab-panel-3226)
-   [Python](#tab-panel-3227)

```java
import com.aerospike.client.sdk.Record;

import com.aerospike.client.sdk.RecordResult;

import com.aerospike.client.sdk.RecordStream;

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

}
```

```python
from aerospike_sdk import GenerationError

async def demo(session):

    users = DataSet.of("test", "users")

    # Read current record

    stream = await session.query(users.id("user-1")).execute()

    row = await stream.first_or_raise()

    user = row.record_or_raise()

    stream.close()

    current_generation = user.generation

    # Update only if generation hasn't changed

    try:

        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")
```

## Update only if exists

Ensure the record exists before updating:

-   [Java](#tab-panel-3228)
-   [Python](#tab-panel-3229)

```java
try {

    session.update(users.id("user-1"))

        .bin("email").setTo("newemail@example.com")

        .execute();

} catch (RecordNotFoundException e) {

    System.out.println("Record doesn't exist");

}
```

```python
from aerospike_sdk import AerospikeError, DataSet

from aerospike_async.exceptions import ResultCode

async def demo(session):

    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")
```

## Complete example

-   [Java](#tab-panel-3230)
-   [Python](#tab-panel-3231)

```java
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!");

        }

    }

}
```

```python
import asyncio

import time

from aerospike_sdk import Behavior, DataSet, Client

async def main():

    async with Client("localhost:3000") as client:

        session = client.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=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(time.time())

            .execute()

        )

        # Increment counters

        await (

            session.update(key)

            .bin("login_count").increment_by(1)

            .bin("points").increment_by(10)

            .execute()

        )

        # Conditional update with generation check

        stream = await session.query(key).execute()

        row = await stream.first_or_raise()

        user = row.record_or_raise()

        stream.close()

        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 summary

| Method | Description | Link |
| --- | --- | --- |
| `update()` | Update an existing record, fails if the record doesn’t exist | Java · Python |
| `touch()` | Update metadata/TTL only | Java · Python |
| `.bin(name).add(delta)` / `.bin(name).increment_by(delta)` | Atomically increment a numeric bin | — |
| `.bin(name).append(text)` / `.prepend(text)` | Append or prepend a string bin | — |
| `.bin(name).remove()` | Remove a bin | — |
| `.ensureGenerationIs(gen)` / `.ensure_generation_is(gen)` | Conditional update | — |

## Next steps

Delete Records

Remove records from the database.

[Delete Records →](https://aerospike.com/docs/develop/client/sdk/usage/delete)

Batch Operations

Update multiple records efficiently.

[Batch Operations →](https://aerospike.com/docs/develop/client/sdk/usage/batch)

Error Handling

Handle update conflicts and errors.

[Error Handling →](https://aerospike.com/docs/develop/client/sdk/concepts/errors)