Inserting and updating items to DynamoDB lists and maps
Should I use a map or a list? A simple rule:
- If you need to update or remove an item, lists only allow you to do it by index. While maps let you do it by key.
Their simplified representation goes like this:
{
"someMap": {
"key1": "value1",
"key2": "value2",
},
"someList": ["value1", "value2"]
}
Inserting and updating to a map
The following example adds or updates an instance of “SomeItem” as a json string into a map field called someMapField
. A sample simplified record looks like this:
{
"id": "someKey",
"someMapField": {
"itemKey": "itemValue as json string"
}
}
The code to add or update the record includes a SET
command followed by a path, in this case the-field-name.the-item's-key
. If the value does not exist, it will be added; otherwise it will be updated.
def update(someKey: String, item: SomeItem): Future[SomeItem] = {
val updateExpression: String =
s"SET someMapField.#itemKey = :itemValue"
val names: Map[String, String] = Map(
"#itemKey" -> item.key
val values: Map[String, AttributeValue] = Map(
":itemValue" -> new AttributeValue().withS(Json.toJson(item).toString))
val request = new UpdateItemRequest()
.withTableName("tableName")
.withKey(Map("id" -> new AttributeValue().withS(someKey)))
.withUpdateExpression(updateExpression)
.withExpressionAttributeNames(names)
.withExpressionAttributeValues(values)
.withReturnValues(ReturnValue.ALL_NEW)
Future {
val result = dynamodb.getClient.updateItem(request).getAttributes.toMap
fromAttributeMap(result)
}
}
A condition can be added to insert an item only when it does not exist:
UpdateExpression = "SET map.#number = :string"
ExpressionAttributeNames = { "#number" : "1" }
ExpressionAttributeValues = { ":string" : "the string to store in the map at key value 1" }
ConditionExpression = "attribute_not_exists(map.#number)"
Inserting and updating to a list
We can insert an item to a list by using the ADD
command.
val updateExpression: String =
s"ADD fieldName :fieldValue"
val values: Map[String, AttributeValue] = Map(
":fieldValue" -> new AttributeValue().withSS("value")
val request = new UpdateItemRequest()
.withTableName("tableName")
.withKey(Map("id" -> new AttributeValue().withS("someId")))
.withUpdateExpression(updateExpression)
.withExpressionAttributeValues(values)
.withReturnValues(ReturnValue.ALL_NEW)
Future {
val result = dynamodb.getClient.updateItem(request).getAttributes.toMap
fromAttributeMap(result)
}
As we mentioned previously, when we want to update an item from a list we have to know its index beforehand. This example gets the index of an item and then uses SET
to modify its value.
const userIdx = users.map(user => user.M.id.S).indexOf(userId)
const deactivateUserParams = {
TableName: USERS_CATEGORY_TABLE_DEV,
Key: {id: {S: catId}},
UpdateExpression: `SET updatedAt = :updated_at, #users[${userIdx}].#status = :new_status`,
ExpressionAttributeNames: {
"#users": 'users',
"#status": "status"
},
ExpressionAttributeValues: {
":new_status": {"S": "INACTIVE"},
":updated_at": {"S": new Date().toISOString()}
},
ReturnValues: "UPDATED_NEW",
}