Jaspreet Chhabra
Jaspreet Chhabra

Reputation: 407

Update specific attribute using UpdateItemEnhancedRequest DynamoDb java sdk2

We have a DynamoDB table which has an attribute counter, which will be decremented asynchronously by multiple lambda based on an event. I am trying to update the counter using UpdateItemEnhancedRequest (using the Dynamodb Enhanced Client. - JAVA SDK 2). I am able to build the condition for updating the counter but it updates the entire item and not just the counter. Can somebody please guide on how to update a single attribute using DynamoDb Enhanced Client?

Code Sample

    public void update(String counter, T item) {

    AttributeValue value = AttributeValue.builder().n(counter).build();

    Map<String, AttributeValue> expressionValues = new HashMap<>();
    expressionValues.put(":value", value);

    Expression myExpression = Expression.builder()
            .expression("nqctr = :value")
            .expressionValues(expressionValues)
            .build();

    UpdateItemEnhancedRequest<T> updateItemEnhancedRequest =
            UpdateItemEnhancedRequest.builder(collectionClassName)
                    .item(item)
                    .conditionExpression(myExpression)
                    .build();

    getTable().updateItem(updateItemEnhancedRequest);

    }

Upvotes: 9

Views: 14185

Answers (2)

cgledezma
cgledezma

Reputation: 622

In your original solution, you're misinterpreting the meaning of the conditionExpression attribute. This is used to validate conditions that must be true on the item that matches the key in order to perform the update, not the expression to perform the update itself.

There is a way to perform this operation with the enhanced client without needing to fetch the object before making an update. The UpdateItemEnhancedRequest class has an ignoreNulls attribute that will exclude all null attributes from the update. This is false by default, which is what causes a full overwrite of the object.

Let's assume this is the structure of your item (without all the enhanced client annotations and boilerplate, you can add those):

class T {
  public String partitionKey;
  public Int counter;
  public String someOtherAttribute

  public T(String partitionKey) {
    this.partitionKey = partitionKey;
    this.counter = null;
    this.someOtherAttribute = null
  }
}

You can issue an update of just the counter, and only if the item exists, like this:

public void update(Int counter, String partitionKey) {
    T updateItem = new T(partitionKey)
    updateItem.counter = counter

    Expression itemExistsExpression = Expression.builder()
            .expression("attribute_exists(partitionKey)")
            .build();

    UpdateItemEnhancedRequest<T> updateItemEnhancedRequest =
            UpdateItemEnhancedRequest.builder(collectionClassName)
                    .item(item)
                    .conditionExpression(itemExistsExpression)
                    .ignoreNulls(true)
                    .build();

    getTable().updateItem(updateItemEnhancedRequest);
}

Upvotes: 2

smac2020
smac2020

Reputation: 10734

When you update a specific column, you need to specify which column to update. Assume we have this table:

enter image description here

Now assume we want to update the archive column. You need to specify the column in your code. Here we change the archive column of the item that corresponds to the key to Closed (a single column update). Notice we specify the column name by using the HashMap object named updatedValues.

// Archives an item based on the key
public String archiveItem(String id){
        DynamoDbClient ddb = getClient();

        HashMap<String,AttributeValue> itemKey = new HashMap<String,AttributeValue>();
        itemKey.put("id", AttributeValue.builder()
                .s(id)
                .build());

        HashMap<String, AttributeValueUpdate> updatedValues =
                new HashMap<String,AttributeValueUpdate>();

        // Update the column specified by name with updatedVal
        updatedValues.put("archive", AttributeValueUpdate.builder()
                .value(AttributeValue.builder()
                        .s("Closed").build())
                .action(AttributeAction.PUT)
                .build());

        UpdateItemRequest request = UpdateItemRequest.builder()
                .tableName("Work")
                .key(itemKey)
                .attributeUpdates(updatedValues)
                .build();

        try {
            ddb.updateItem(request);
            return"The item was successfully archived";

NOTE: This is not the Enhanced Client.

This code is from the AWS Tutorial that show how to build a Java web app by using Spring Boot. Full tutorial here:

Creating the DynamoDB web application item tracker

TO update a single column using the Enhanced Client, call the Table method. This returns a DynamoDbTable instance. Now you can call the updateItem method.

Here is the logic to update the the archive column using the Enhanced Client. Notice you get a Work object, call its setArchive then pass the Work object. workTable.updateItem(r->r.item(work));

Java code:

  // Update the archive column by using the Enhanced Client.
    public String archiveItemEC(String id) {

        DynamoDbClient ddb = getClient();

        try {

            DynamoDbEnhancedClient enhancedClient = DynamoDbEnhancedClient.builder()
                    .dynamoDbClient(getClient())
                    .build();

            DynamoDbTable<Work> workTable = enhancedClient.table("Work", TableSchema.fromBean(Work.class));

            //Get the Key object.
            Key key = Key.builder()
                    .partitionValue(id)
                    .build();

            // Get the item by using the key.
            Work work = workTable.getItem(r->r.key(key));
            work.setArchive("Closed");

            workTable.updateItem(r->r.item(work));
            return"The item was successfully archived";
        } catch (DynamoDbException e) {
            System.err.println(e.getMessage());
            System.exit(1);
        }
        return "";
    }

This answer shows both ways to update a single column in a DynamoDB table. The above tutorial now shows this way.

Upvotes: 5

Related Questions