avatar

Andres Jaimes

Inserting and updating items to DynamoDB lists and maps

By Andres Jaimes

- 2 minutes read - 421 words

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",
}

References