Skip to content

DynamoDB Enhanced: Add support for if_not_exists in Update operations #1822

Closed
@whablake

Description

@whablake

DynamoDB supports preventing an overwrite of an existing value in an update operation through the use of the function if_not_exists(path, value) DynamoDB Update Expression Docs. The DynamoDB Enhanced client does not have a mechanism to achieve the same behavior. You must do a read from dynamodb, then omit the values you do not want to overwrite in a subsequent write operation.

Describe the Feature

Allow a user to specify attributes in their table schema that should not be overwritten in an update operation.

This is the basic use case, I have a class that has attributes similar to:

public class Customer {
    private String accountId;
    private String name;
    private Instant createdDate; // Once this is written, never overwrite
    private Instant updatedDate;

    static final TableSchema<Customer> CUSTOMER_TABLE_SCHEMA =
       TableSchema.builder(Customer.class)
           .newItemSupplier(Customer::new)
           .addAttribute(String.class, a -> a.name("account_id")
                                      .getter(Customer::getAccountId)
                                      .setter(Customer::setAccountId)
                                      .tags(primaryPartitionKey()))
           .addAttribute(String.class, a -> a.name("name")
                                      .getter(Customer::getName)
                                      .setter(Customer::setName))
           .addAttribute(Instant.class, a -> a.name("created_date")
                                      .getter(Customer::getCreatedDate)
                                      .setter(Customer::setCreatedDate))
           .addAttribute(Instant.class, a -> a.name("updated_date")
                                      .getter(Customer::getUpdatedDate)
                                      .setter(Customer::setUpdatedDate))
           .build();
}

If the name field is updated, I would like to update both "name" and "updated_date" but not "created_date" in a single operation. This is possible with the low level client, but not the enhanced client.

The sequence of events is as follows:

Customer "xyz" specifies their name for the first time

{
"accountId": "xyz",
"name" : "foo",
"createdDate" : "2020-05-01T00:00:00Z",
"updatedDate" : "2020-05-01T00:00:00Z"
}

At some point later, account "xyz" updates their name to "bar". At this point the application could do a read in DDB to check if the customer already exists and then only add a createdDate value if the customer does not exist. However, DDB allows you to do it in a single UpdateItem request, so I would like to do one update with this:

{
"accountId": "xyz",
"name" : "bar",
"createdDate" : "2020-05-05T05:05:05Z",
"updatedDate" : "2020-05-05T05:05:05Z"
}

The final expected state in DDB is:

{
"accountId": "xyz",
"name" : "bar",
"createdDate" : "2020-05-01T00:00:00Z",
"updatedDate" : "2020-05-05T05:05:05Z"
}

Proposed Solution

One way I could see this implemented is by adding an attribute tag for attributes that should not be overwritten on an update.

Something like

TableSchema.builder(Customer.class)
    .addAttribute(Instant.class, 
        a -> a.name("created_date")
            .getter(Customer::getCreatedDate)
            .setter(Customer::setCreatedDate)
            .tags(updateBehavior(UpdateBehavior.DO_NOT_OVERWRITE)))

There could also be a corresponding annotation for use with @DynamoDbBean

When UpdateItemOperation.java sees an attribute with UpdateBehavior.DO_NOT_OVERWRITE, it uses "SET A = if_not_exists(A, :a)" instead of the default "SET A = :a" in the update expression.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions