Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to set a DynamoDB Map property value, when the map doesn't exist yet

Tags:

How do you "upsert" a property to a DynamoDB row. E.g. SET address.state = "MA" for some item, when address does not yet exist?

I feel like I'm having a chicken-and-egg problem because DynamoDB doesn't let you define a sloppy schema in advance.

If address DID already exist on that item, of type M (for Map), the internet tells me I could issue an UpdateExpression like:

SET #address.#state = :value

with #address, #state, and :value appropriately mapped to address, state, and MA, respectively.

But if the address property does not already exist, this gives an error:

''' ValidationException: The document path provided in the update expression is invalid for update '''

So.. it appears I either need to:

  1. Figure out a way to "upsert" address.state (e.g., SET address = {}; SET address.state = 'MA' in a single command)

or

  1. Issue three (!!!) roundtrips in which I try it, SET address = {}; on failure, and then try it again.

If the latter.... how do I set a blank map?!?

Ugh.. I like Dynamo, but unless I'm missing something obvious this is a bit crazy..

like image 445
Ted Benson Avatar asked Feb 13 '16 08:02

Ted Benson


2 Answers

You can do it with two round trips, the first conditionally sets an empty map for address if it doesn't already exist, and the second sets the state:

db.update({     UpdateExpression: 'SET #a = :value',     ConditionExpression: 'attribute_not_exists(#a)',     ExpressionAttributeValues: {         ":value": {},     },     ExpressionAttributeNames: {         '#a': 'address'     } }, ...); 

Then:

db.update({     UpdateExpression: 'SET #a.#b = :v',     ExpressionAttributeNames: {         '#a': 'address',         '#b': 'state'     },     ExpressionAttributeValues: {         ':v': 'whatever'     } }, ...); 
like image 94
leftclickben Avatar answered Nov 04 '22 09:11

leftclickben


You cannot set nested attributes if the parent document does not exist. Since address does not exist you cannot set the attribute province inside it. You can achieve your goal if you set address to an empty map when you create the item. Then, you can use the following parameters to condition an update on an attribute address.province not existing yet.

var params = {     TableName: 'Image',     Key: {         Id: 'dynamodb.png'     },     UpdateExpression: 'SET address.province = :ma',     ConditionExpression: 'attribute_not_exists(address.province)',     ExpressionAttributeValues: {         ':ma': 'MA'     },     ReturnValues: 'ALL_NEW' }; docClient.update(params, function(err, data) {     if (err) ppJson(err); // an error occurred     else ppJson(data); // successful response }); 

By the way, I had to replace state with province as state is a reserved word.

like image 34
Alexander Patrikalakis Avatar answered Nov 04 '22 08:11

Alexander Patrikalakis