Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

    }
like image 322
Jaspreet Chhabra Avatar asked Feb 02 '21 13:02

Jaspreet Chhabra


People also ask

How do I update a DynamoDB item?

To update an existing item in an Amazon DynamoDB table, you use the UpdateItem operation. You must provide the key of the item that you want to update. You must also provide an update expression, indicating the attributes that you want to modify and the values that you want to assign to them.

Can we change primary key value in DynamoDB?

You cannot update the primary key attributes using UpdateItem. Instead, delete the item and use PutItem to create a new item with new attributes. The UpdateItem operation includes an Action parameter, which defines how to perform the update. You can put, delete, or add attribute values.

How many attributes can a DynamoDB item have?

There is no limit to the number of attributes but the total item size is limited to 400kb. The maximum item size in DynamoDB is 400 KB, which includes both attribute name binary length (UTF-8 length) and attribute value lengths (again binary length). The attribute name counts towards the size limit.


Video Answer


2 Answers

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.

like image 140
smac2020 Avatar answered Oct 12 '22 14:10

smac2020


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);
}
like image 28
cgledezma Avatar answered Oct 12 '22 14:10

cgledezma