Simon Revill
Simon Revill

Reputation: 127

Deleting an object from a nested array in DynamoDB - AWS JavaScript SDK

I'm building an app where I need to delete items stored in the database. Here's a (shortened) example of user data I have in my DynamoDB table called 'registeredUsers':

{
  "userId": "f3a0f858-57b4-4420-81fa-1f0acdec979d"
  "aboutMe": "My name is Mary, and I just love jigsaw puzzles! My favourite jigsaw category is Architecture, but I also like ones with plants in them.",
  "age": 27,
  "email": "[email protected]",
  "favourites": {
    "imageLibrary": [
      {
        "id": "71ff8060-fcf2-4523-98e5-f48127d7d88b",
        "name": "bird.jpg",
        "rating": 5,
        "url": "https://s3.eu-west-2.amazonaws.com/jigsaw-image-library/image-library/images/bird.jpg"
      },
      {
        "id": "fea4fd2a-851b-411f-8dc2-1ae0e144188a",
        "name": "porsche.jpg",
        "rating": 3,
        "url": "https://s3.eu-west-2.amazonaws.com/jigsaw-image-library/image-library/images/porsche.jpg"
      },
      {
        "id": "328b913f-b364-47df-929d-925676156e97",
        "name": "rose.jpg",
        "rating": 0,
        "url": "https://s3.eu-west-2.amazonaws.com/jigsaw-image-library/image-library/images/rose.jpg"
      }
    ]
  }
}

I want to be able to delete the item 'rose.jpg' in the user.favourites.imageLibrary array. In order to select the correct user, I can provide the userId as the primary key. Then, in order to select the correct image in the array, I can pass the AWS.DocumentClient the 'id' of the item in order to delete it. However, I'm having trouble understanding the AWS API Reference docs. The examples given in the developer guide do not describe how to delete an item by looking at one of it's attributes. I know I have to provide an UpdateExpression and an ExpressionAttributeValues object. When I wanted to change a user setting, I found it pretty easy to do:

const params = {
    TableName: REGISTERED_USERS_TABLE,
    Key: { userId },
    UpdateExpression: "set userPreferences.difficulty.showGridOverlay = :d",
    ExpressionAttributeValues: {
      ":d": !showGridOverlay
    },
    ReturnValues: "UPDATED_NEW"
  };

To conclude, I need a suitable Key, UpdateExpression and ExpressionAttributeValues object to access the rose.jpg item in the favourites array.

Upvotes: 3

Views: 1373

Answers (1)

Nadav Har'El
Nadav Har'El

Reputation: 13731

Unfortunately, the UpdateExpression syntax is not as powerful as you would have liked. It supports entire nested documents inside the item, but not sophisticated expressions to search in them or to modify them. The only ability it gives you inside a list is to access or modify its Nth element. For example:

REMOVE #favorites.#imagelibrary[3]

Will remove the 3rd element of imagelibrary (note that the "#imagelibrary" will need to be defined in ExpressionAttributeNames), and you can also have a condition on #favorites.#imagelibrary[3].#id, for example, in ConditionExpression. But unfortunately, there is no way to specify more complex combinations of conditions and updates, such as "find me the i where #favorites.#imagelibrary[i].#id is equal something, and then REMOVE this specific element".

Your remaining option is to read the full value of the item (or with ProjectionExpression just the #favorties.#imagelibrary array), and then in your own code find which of the elements you want to remove (e.g., discover that it is the 3rd element), and then in a separate update, remove the 3rd element.

Note that if there's a possibility that some other parallel operation also changes the item, you must use a conditional update (both UpdateExpression and ConditionExpression) for the element removal, to ensure the element that you are removing still has the id you expected. If the condition fails, you need to repeat the whole operation again - read the modified item again, find the element again, and try to remove it again. This is an example of the so-called "optimistic locking" technique which is often used with DynamoDB.

Upvotes: 4

Related Questions