aws / aws/aws-sdk-java-v2

Add support for attribute deletion in `DynamoDbTable.updateItem` when using `IgnoreNullsMode.SCALAR_ONLY`

Open
#6,468 0 comments 0 reactions 0 assignees View on GitHub
feature-request needs-triage
Dominant language
Java
Stars
2.6k
Forks
1k
Avg merge
2d 9h
Merged PRs (30d)
51

Description

### Describe the feature

The `DynamoDbTable.updateItem` method provides two modes for handling `null` values via the `IgnoreNullsMode` setting:

- `IgnoreNullsMode.DEFAULT`: Attributes with `null` values are removed, resulting in a full item update (the entire item must be specified).
- `IgnoreNullsMode.SCALAR_ONLY`: Attributes with `null` values are ignored, allowing a partial update where only the provided attributes are written.

While partial updates are efficient and convenient, since they only write the modified fields, they currently do not support attribute deletion. The only way to delete an attribute is by using `IgnoreNullsMode.DEFAULT`, which forces a full item overwrite and requires all existing attributes to be specified, even when only a few need updating. In practice, this means the existing item must first be retrieved from DynamoDB before performing the update, so that all current attributes can be included in the overwrite request.

This limitation becomes especially problematic for items with a large number of attributes, where a full update incurs unnecessary read and write overhead, adds latency, and increases code complexity.

### Use Case

Consider a `BigItem` entity with a large number of attributes (e.g., 50 fields such as `attr1`, `attr2`, …, `attr50`):

```java
@Getter
@Builder(toBuilder = true)
@DynamoDbImmutable(builder = BigItem.BigItemBuilder.class)
public class BigItem {

@Getter(onMethod_ = @DynamoDbPartitionKey)
private final String id;

private final String attr1;
private final String attr2;
private final String attr3;
// More attributes to make the item "big"
private final String attr50;
}
```

Suppose we need to remove `attr3`.

With the current API, this requires first retrieving the existing item from DynamoDB, and then performing a full item update using `IgnoreNullsMode.DEFAULT`, meaning all other attributes must be re-specified, even if they have not changed:

```java
BigItem bigItem = dynamoDbTable.getItem(
b -> b.key(k -> k.partitionValue(id).build()));

BigItem fullUpdate = bigItem.toBuilder()
.attr3(null)
.build();

dynamoDbTable.updateItem(
b -> {
b.item(fullUpdate);
b.ignoreNullsMode(IgnoreNullsMode.DEFAULT);
}
);
```

While it is technically possible to remove a single attribute using a low-level `UpdateItemRequest` with `DynamoDbClient`, constructing the appropriate update expression becomes cumbersome, especially in more complex scenarios where both attribute updates and deletions must occur together.

### Proposed Solution

Introduce support for fields of type `Optional` within the enhanced client data model.

When performing a partial update with `IgnoreNullsMode.SCALAR_ONLY`:

- Attributes with `null` values would continue to be ignored (unchanged behavior).
- Attributes with `Optional.empty()` would be removed from the item.

In the case of a full update, both `null` and `Optional.empty()` should indicate that the corresponding attribute must be deleted.

This approach enables developers to explicitly mark attributes for deletion without requiring a full item overwrite, while maintaining backward compatibility with existing behavior.

Example:

```java
@Getter
@Builder(toBuilder = true)
@DynamoDbImmutable(builder = BigItem.BigItemBuilder.class)
public class BigItem {

@Getter(onMethod_ = @DynamoDbPartitionKey)
private final String id;

private final String attr1;
private final Optional attr2;
private final Optional attr3;
// More attributes to make the item "big"
private final String attr50;
}
```

In this example, we assume that `attr2` and `attr3` are nullable fields represented as `Optional`. If we want to delete `attr3`, we can explicitly set it to `Optional.empty()` in a partial update:

```java
BigItem partialUpdate = BigItem.builder()
.id(id)
.attr3(Optional.empty())
.build();

dynamoDbTable.updateItem(
b -> {
b.item(partialUpdate);
b.ignoreNullsMode(IgnoreNullsMode.SCALAR_ONLY);
}
);
````

This approach allows clear intent for attribute deletion within partial updates, without needing to retrieve or rewrite the full item.

### Other Information

_No response_

### Acknowledgements

- [x] I may be able to implement this feature request
- [ ] This feature might incur a breaking change

### AWS Java SDK version used

2.33.1

### JDK version used

21.0.3

### Operating System and version

Ubuntu 24.04

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.