Roy Fox
Roy Fox

Reputation: 313

How to update a Map or a List on AWS DynamoDB document API?

The new AWS DynamoDB document API allows 2 new data types that correspond directly to the underlying JSON representation: Map (aka JSON object) and List (aka JSON array).

However, I can't find a way to update attributes of these data types without completely overwriting them. In contrast, a Number attribute can be updated by ADDing another number, so in Java you can do something like:

new AttributeUpdate("Some numeric attribute").addNumeric(17);

Similarly you can addElements to an attribute of a Set data type. (In the old API you would use AttributeAction.ADD for both purposes.)

But for a Map or a List, it seems you must update the previous value locally, then PUT it instead of that value, for example in Java:

List<String> list = item.getList("Some list attribute");
list.add("new element");
new AttributeUpdate("Some list attribute").put(list);

This is much less readable, and under some circumstances much less efficient.

So my questions are:

  1. Is there a way to update an attribute of a Map or a List data type without overwriting the previous value? For example, to add an element to a List, or to put an element in a Map?

  2. How would you implement it using the Java API?

  3. Do you know of plans to support this in the future?

Upvotes: 16

Views: 29253

Answers (3)

Muhammad Soliman
Muhammad Soliman

Reputation: 23756

A generic function to add or update a key/value pairs. attribute updateColumn should be of type map.

Update tableName attribute name should be passed as attributeName under key:value pairs where primaryKey = primaryKeyValue

public boolean insertKeyValue(String tableName, String primaryKey, String 
    primaryKeyValue, String attributeName, String newKey, String newValue) {

    //Configuration to connect to DynamoDB
    Table table = dynamoDB.getTable(tableName);
    boolean insertAppendStatus = false;
    try {
        //Updates when map is already exist in the table
        UpdateItemSpec updateItemSpec = new UpdateItemSpec()
            .withPrimaryKey(primaryKey, primaryKeyValue)
            .withReturnValues(ReturnValue.ALL_NEW)
            .withUpdateExpression("set #columnName." + newKey + " = :columnValue")
            .withNameMap(new NameMap().with("#columnName", attributeName))
            .withValueMap(new ValueMap().with(":columnValue", newValue))
            .withConditionExpression("attribute_exists("+ attributeName +")");

        table.updateItem(updateItemSpec);
        insertAppendStatus = true;
    //Add map column when it's not exist in the table
    } catch (ConditionalCheckFailedException e) {
        HashMap<String, String> map =  new HashMap<>();
        map.put(newKey, newValue);
        UpdateItemSpec updateItemSpec = new UpdateItemSpec()
            .withPrimaryKey(primaryKey,primaryKeyValue)
            .withReturnValues(ReturnValue.ALL_NEW)
            .withUpdateExpression("set #columnName = :m")
            .withNameMap(new NameMap().with("#columnName", attributeName))
            .withValueMap(new ValueMap().withMap(":m", map));

        table.updateItem(updateItemSpec);
        insertAppendStatus = true;
    } catch(Exception e) {
        e.printStackTrace();
    }
    return insertAppendStatus;
}

Upvotes: 0

Ivan Gonzalez
Ivan Gonzalez

Reputation: 82

Base on DynamoDB examples, this also work (scala)

val updateItemSpec:UpdateItemSpec = new UpdateItemSpec()
    .withPrimaryKey("hashkey", my_key)
  .withUpdateExpression("set my_list = list_append(:prepend_value, my_list)")
  .withValueMap(new ValueMap()
      .withList(":prepend_value", "1"))
  .withReturnValues(ReturnValue.UPDATED_NEW)
println("Updating the item...")
val outcome: UpdateItemOutcome = table.updateItem(updateItemSpec)
println("UpdateItem succeeded:\n" + outcome.getItem.toJSONPretty)

Upvotes: 6

Ben Schwartz
Ben Schwartz

Reputation: 1756

Please take a look at UpdateExpression in the UpdateItem API

For example given an item with a list:

{
    "hashkey": {"S" : "my_key"},
    "my_list" : {"L": 
        [{"N":"3"},{"N":"7"} ]
}

You can update the list with code like the following:

UpdateItemRequest request = new UpdateItemRequest();
request.setTableName("myTableName");
request.setKey(Collections.singletonMap("hashkey", 
    new AttributeValue().withS("my_key")));
request.setUpdateExpression("list_append(:prepend_value, my_list)");
request.setExpressionAttributeValues(
    Collections.singletonMap(":prepend_value", 
        new AttributeValue().withN("1"))
    );
dynamodb.updateItem(request);`

You can also append to the list by reversing the order of the arguments in the list_append expression.

An expression like: SET user.address.zipcode = :zip would address a JSON map element combined with expression attribute values {":zip" : {"N":"12345"}}

Upvotes: 13

Related Questions