My table structure is below where time
is the unique key and that "01" was the id of the count, the item of a month will create right after any id get the count result of that month.
{
"details": {
"01": {
"a": {
"count": [10, 5]
},
"b": {
"count": [10, 5]
}
}
},
"time": "2020_06"
}
And I use update function to save the count result:
var params = {
TableName: tableName,
Key: { time },
UpdateExpression: `ADD details.#id.#type.count[0] :count0, details.#id.#type.count[1] :count1`,
ExpressionAttributeNames: {
'#id': id,
'#type': type
},
ExpressionAttributeValues: {
':count0': count[0],
':count1': count[1]
},
ReturnValues: 'ALL_NEW'
}
If the result of the id already exists then that's fine. But if it doesn't, I'll get an error says that
ValidationException: The document path provided in the update expression is invalid for update
How to let the map be constructed before updating data? I had tried to use SET if_not_exists()
but it'll overlap the path what I really want to update (the count array)
In short you cannot, there are 2 options I can think on.
When creating the item create an empty map for details
. This will allow you to update nested values for details
without raising an exception about an invalid path.
Add some exception handling which will update the entire map should it throw and exception on the first execution:
import boto3
from botocore.exceptions import ClientError
table = boto3.resource('dynamodb', region_name='eu-west-1').Table('test1')
try:
table.update_item(
Key={
'pk': '1'
},
UpdateExpression="SET #buyer.#value = :val1, #buyer.#label = :val2",
ExpressionAttributeNames={
'#label': 'label',
'#value': 'value',
'#buyer': 'buyer',
},
ExpressionAttributeValues={
':val1': 'id1',
':val2': 'Test'
}
)
except ClientError as e:
if e.response['Error']['Code'] == 'ValidationException':
# Creating new top level attribute `buyer` (with nested props)
# if the previous query failed
response = table.update_item(
Key={
'pk': '1'
},
UpdateExpression="set #buyer = :val1",
ExpressionAttributeNames={
'#buyer': 'buyer'
},
ExpressionAttributeValues={
':val1': {
'value': 'id1',
'label': 'Test'
}
}
)
else:
raise