Updating Values in DyanmoDB Map Attributes
Tags: aws, dynamodb, python
Published: 2019-10-24 12:00:34

---
title: Updating Values in DyanmoDB Map Attributes
published: true
description: Demonstrating an example pattern for updating nested items in a DynamoDB Map.
tags: aws, dynamodb, python
---

One project I’m working on translates a favorite party game among my friends into a SMS-based version that can be played anywhere. A requirement for the project is being able to manage the game state across players.  To do this I created a DynamoDB record for each game that maintains the state of the game.  Each player has an entry in a Map attribute, which stores data in key-value pairs, with details like their name and score.  Throughout the game the score for players needs to be updated by accessing the nested Map attribute of each player.  This post will explain a pattern for how you can dynamically build a pattern for updating individual data elements within DynamoDB Maps.

### Setting Up an Example

Let's suppose we have a DynamoDB record for a game where a `game_id` uniquely identify the game and there is a `players`  Map attribute  containing all the information about each player, in this case just a `score`, like the example record below.  In this example there are two players, Charlotte and Becky, who each have their own record nested in the `players` Map containing the player's score.  As the game progresses the score for each player needs to be updated.

```python
Item={  
    "game_id": "test",
    "players": {  
            "Charlotte": {  
                "score": 0  
             },  
             "Becky": {  
                "score": 0  
             }  
        }  
    }
```


### Building the Update Pattern

As the game progresses the `score` attribute needs to be updated for each player.  The function below shows how the DynamoDB `update_item` function can be used to update new nested Map attributes.

```python
def update_player_score(game_id, player_id, score_val):  
    dynamo = boto3.resource('dynamodb')  
    tbl = dynamo.Table('<TableName>')  
  
    result = tbl.update_item(  
        Key={  
            "game_id": game_id  
        },  
        UpdateExpression="SET players.#player_id.score = :score_val",  
        ExpressionAttributeNames={  
            "#player_id": player_id  
        },  
        ExpressionAttributeValues={  
            ":score_val": score_val  
        }  
    )
```

The function takes parameters for the `game_id`, `player_id`, and `score_val`.  Given those parameters the function will update the player's score to be equal to what is provided in `score_val`.  For example, if you were to call, `update_player_score("test", "Charlotte", 1)` we would change the score of Charlotte to be 1.  Let's walkthrough how that happens.

The first part of `update_item`. defines what key we will be updating.  In this case `game_id` is the Key to the table, so we'll be updating records associated with the "test" `game_id`.

Next the `UpdateExpression` is defined.  In the `UpdateExpression` we define the **pattern** for the value we want to update.  For this function we want to update the nested parameters within the `players` map that could vary by the name of the player.  In `UpdateExpression` the name of the `players` map is hardcoded, but accessing the nested Map attribute needs to be dynamic.  There are two placeholders, `#player_id` and `:score_val`, in `UpdateExpression` defining the location of the specific attribute in the map that needs to be updated and the value to set to that nested attributed.  The subsequent `ExpressionAttributeNames` and `ExpressionAttributeValues` definitions provide a mapping to what those placeholders should be.

The values provided in `ExpressionAttributeNames` and `ExpressionAttributeValues` will replace the placeholders that are in `UpdateExpression` when the function is called.  For example consider the case where Charlotte's score becomes 1.  When the function is called the `UpdateExpression` would be equivalent to `SET players.Charlotte.score = 1` after the substitutions for `#player_id` and `:score_val` are made from `ExpressionAttributeNames` and `ExpressionAttributeValues`.

In DynamoDB an [ExpressionAttributeNames](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Expressions.ExpressionAttributeNames.html) is the placeholder for dynamic attribute values.  When constructing the `UpdateExpression` all attribute values must be prefaced with a `#`.  In our example we want to update the score for a player, but since the player that's being updated is dynamic the `ExpressionAttributeNames` fills in the `#player_id` placeholder within `UpdateExpression`.

Similarly, [ExpressionAttributeValues](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Expressions.ExpressionAttributeValues.html) are placeholders for when the the value of an attribute are not known until runtime.  In our case the score to assign to the player.  All `ExpressionAttributeValues` are prefaced with a `:`.

If we wanted to update Becky's score to give her 3 points we could use this function call and the `UpdateExpression` would handle the proper mapping to where Becky is located in the `players` attribute map:

```python
update_player_score("test", "Becky", 3)
```

And that’s it!  Patterns similar to this are helpful when you have Map attributes in DynamoDB where the path to the attribute you want to update will be dynamoic.  When working with Map attributes in DynamoDB it's important to define the `ExpressionAttributeNames` and `ExpressionAttributeValues` that will be dynamic when building your `UpdateExpression` then construct the expression accordingly.  Hopefully this was a helpful exercise for you! There are a couple more projects I’m working on that I can’t wait to share so stay tuned!