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.
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.
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.
def update_player_score(game_id, player_id, score_val):
dynamo = boto3.resource('dynamodb')
tbl = dynamo.Table('<TableName>')
result = tbl.update_item(
UpdateExpression="SET players.#player_id.score = :score_val",
The function takes parameters for the
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"
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,
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
ExpressionAttributeValues definitions provide a mapping to what those placeholders should be.
The values provided in
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
:score_val are made from
In DynamoDB an ExpressionAttributeNames 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
Similarly, ExpressionAttributeValues 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:
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
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!