diff --git a/README.md b/README.md index 8883e6b82..0a25d6bac 100644 --- a/README.md +++ b/README.md @@ -129,16 +129,16 @@ scala> import com.gu.scanamo.syntax._ scala> val client = LocalDynamoDB.client() scala> import com.amazonaws.services.dynamodbv2.model.ScalarAttributeType._ scala> val teamTableResult = LocalDynamoDB.createTable(client)("teams")('name -> S) -scala> case class Team(name: String, goals: Int, scorers: List[String]) +scala> case class Team(name: String, goals: Int, scorers: List[String], mascot: Option[String]) scala> val teamTable = Table[Team]("teams") scala> val operations = for { - | _ <- teamTable.put(Team("Watford", 1, List("Blissett"))) - | _ <- teamTable.update('name -> "Watford", set('goals -> 2) and append('scorers -> "Barnes")) + | _ <- teamTable.put(Team("Watford", 1, List("Blissett"), Some("Harry the Hornet"))) + | _ <- teamTable.update('name -> "Watford", set('goals -> 2) and append('scorers -> "Barnes") and remove('mascot)) | watford <- teamTable.get('name -> "Watford") | } yield watford scala> Scanamo.exec(client)(operations) -res1: Option[cats.data.Xor[error.DynamoReadError, Team]] = Some(Right(Team(Watford,2,List(Blissett, Barnes)))) +res1: Option[cats.data.Xor[error.DynamoReadError, Team]] = Some(Right(Team(Watford,2,List(Blissett, Barnes),None))) ``` ### Using Indexes diff --git a/src/main/scala/com/gu/scanamo/package.scala b/src/main/scala/com/gu/scanamo/package.scala index 5b1db31e6..2c39da17c 100644 --- a/src/main/scala/com/gu/scanamo/package.scala +++ b/src/main/scala/com/gu/scanamo/package.scala @@ -59,6 +59,8 @@ package object scanamo { AddExpression(fieldValue._1, fieldValue._2) def delete[V: DynamoFormat](fieldValue: (Symbol, V)) = DeleteExpression(fieldValue._1, fieldValue._2) + def remove(field: Symbol) = + RemoveExpression(field) implicit class AndUpdateExpression[X: UpdateExpression](x: X) { def and[Y: UpdateExpression](y: Y) = AndUpdate(x, y) diff --git a/src/main/scala/com/gu/scanamo/update/UpdateExpression.scala b/src/main/scala/com/gu/scanamo/update/UpdateExpression.scala index 1ba3e8db4..6bbd05be9 100644 --- a/src/main/scala/com/gu/scanamo/update/UpdateExpression.scala +++ b/src/main/scala/com/gu/scanamo/update/UpdateExpression.scala @@ -14,9 +14,10 @@ import simulacrum.typeclass } sealed trait UpdateType { val op: String } -final object SET extends UpdateType { override val op = "SET" } -final object ADD extends UpdateType { override val op = "ADD" } -final object DELETE extends UpdateType { override val op = "DELETE" } +case object SET extends UpdateType { override val op = "SET" } +case object ADD extends UpdateType { override val op = "ADD" } +case object DELETE extends UpdateType { override val op = "DELETE" } +case object REMOVE extends UpdateType { override val op = "REMOVE" } case class SetExpression[V: DynamoFormat](field: Symbol, value: V) @@ -74,6 +75,12 @@ object AddExpression { } } +/* +Note the difference between DELETE and REMOVE: + - DELETE is used to delete an element from a set + - REMOVE is used to remove an attribute from an item + */ + case class DeleteExpression[V: DynamoFormat](field: Symbol, value: V) object DeleteExpression { @@ -88,6 +95,20 @@ object DeleteExpression { } } +case class RemoveExpression(field: Symbol) + +object RemoveExpression { + implicit val removeUpdateExpression = + new UpdateExpression[RemoveExpression] { + override def typeExpressions(t: RemoveExpression): Map[UpdateType, String] = + Map(REMOVE -> "#update") + override def attributeNames(t: RemoveExpression): Map[String, String] = + Map("#update" -> t.field.name) + override def attributeValues(t: RemoveExpression): Map[String, AttributeValue] = + Map() + } +} + case class AndUpdate[L: UpdateExpression, R: UpdateExpression](l: L, r: R) object AndUpdate {