如何在 AWS DynamoDB 文档 API 上更新地图或列表?

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

新的 AWS DynamoDB document API 允许直接对应底层 JSON 表示的 2 种新数据类型:Map(又名 JSON 对象)和 List(又名 JSON 数组).

但是,我找不到一种方法来更新这些数据类型的属性而不完全覆盖它们。相反,可以通过添加另一个数字来更新数字属性,因此在 Java 中,您可以执行以下操作:

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

同样,您可以 addElements 设置数据类型的属性。 (在旧的 API 中,您将使用 AttributeAction.ADD 来实现这两个目的。)

但是对于 Map 或 List,似乎你必须在本地更新以前的值,然后用 PUT 代替那个值,例如 Java:

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

这可读性差得多,在某些情况下效率也低得多。

所以我的问题是:

  1. 有没有办法在不覆盖先前值的情况下更新 Map 或 List 数据类型的属性?例如,将元素添加到 List,或将元素放入 Map?

  2. 您将如何使用 Java API 实现它?

  3. 你知道未来有支持这个的计划吗?

请查看 UpdateItem API

中的 UpdateExpression

例如给定一个带有列表的项目:

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

您可以使用如下代码更新列表:

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

您还可以通过反转 list_append 表达式中参数的顺序来附加到列表。

像这样的表达式:SET user.address.zipcode = :zip 将寻址 JSON 映射元素并结合表达式属性值 {":zip" : {"N":"12345"}}

基于 DynamoDB 示例,这也有效 (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)

添加或更新 key/value 对的通用函数。属性 updateColumn 应该是地图类型。

更新 tableName 属性名称应在 key:value 对下作为 attributeName 传递,其中 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;
}