Skip to main content

DynamoDB: Conditional updates on nested fields

I always struggle with the ConditionalUpdate syntax for DynamoDB, so here’s a snippet for the Java SDK that works (with a bit of Scanamo magic to turn case class instances into instances of AttributeValue):

import com.gu.scanamo.DynamoFormat
import com.amazonaws.services.dynamodbv2.model.{
  AttributeValue,
  UpdateItemRequest
}

import scala.collection.JavaConverters._

case class Versioned[T](payload: T, version: Version)

val value: Versioned[T]

val evidenceV: DynamoFormat[Version]
val evidenceT: DynamoFormat[T]

val versionAv: AttributeValue = evidenceV.write(value.version)
val payloadAv: AttributeValue = evidenceT.write(value.payload)

val updateItemRequest = new UpdateItemRequest()
  .withTableName(dynamoConfig.table)
  .addKeyEntry("id", new AttributeValue().withS(value.version.id))
  .withUpdateExpression(
    "SET payload = :payload, version = :version"
  )
  .withConditionExpression("attribute_not_exists(id) OR version < :currentVersion")
  .withExpressionAttributeValues(
    Map(
      ":currentVersion" -> new AttributeValue(value.version.version.toString),
      ":payload" -> payloadAv,
      ":version" -> versionAv
    ).asJava
  )

Here is the equivalent code in the Scanamo DSL:

val ops = table
  .given(
    not(attributeExists('id)) or
      (attributeExists('id) and 'version \ 'version < value.version.version)
  )
  .update(
    'id -> value.version.id,
    set('version -> value.version) and set('payload -> value.payload)
  )

Scanamo.exec(dynamoClient)(ops)

Note especially the use of 'version \ 'version to access the nested field.

References: