I have a DynamoDB table where each item in the table has an array of comments as an attribute. My schema looks something like this:
{
"id": "abc",
"name": "This is an item"
"comments": [
"commentId": "abcdefg",
"commentAuthor": "Alice",
"commentDetails": "This item is my favourite!"
]
}
I want to be able to edit an individual comment by its commentId
, but it's not clear how to write a DynamoDB expression for this update (I'm using DocumentClient).
How can we update an entry in an embedded array in Dynamo? Is it possible to update by array index, or by a query expression?
I have a DynamoDB table where each item in the table has an array of comments as an attribute. My schema looks something like this:
{
"id": "abc",
"name": "This is an item"
"comments": [
"commentId": "abcdefg",
"commentAuthor": "Alice",
"commentDetails": "This item is my favourite!"
]
}
I want to be able to edit an individual comment by its commentId
, but it's not clear how to write a DynamoDB expression for this update (I'm using DocumentClient).
How can we update an entry in an embedded array in Dynamo? Is it possible to update by array index, or by a query expression?
Share Improve this question edited Mar 2, 2018 at 23:44 Uddhav P. Gautam 7,6263 gold badges51 silver badges67 bronze badges asked Feb 1, 2018 at 12:39 JimJim 4,66916 gold badges53 silver badges80 bronze badges 04 Answers
Reset to default 8I think there should be some modification to your data model.You should have used dynamodb map for this just make your commentId as a key to access each comment
{
"id": "abc",
"name": "This is an item"
"comments": {
"abcdefg":{
"commentAuthor": "Alice",
"commentDetails": "This item is my favourite!"
}
}
}
Then the query would look like this and you must know two things to update the comment 1 is Item id and 2 is comment id
var params = {
TableName: Tablename,
Key: {id: "abc"},
UpdateExpression:"set comments.#abcdefg.commentDetails=:val1",
ExpressionAttributeNames: {
"#abcdefg":"abcdefg"
},
ExpressionAttributeValues:{
":val1":"New comment text"
}
}
db.update(param,callback);
Please Let me know and pardon me,explain your question if i misunderstood as i did not have enough points to clear my doubts in comment.
Personally, I would not put items I want to edit or access frequently this far away from the primary key. Remember DynamoDB is basically a key-value store, so whenever you want to access stuff it's always better to have those items exposed through primary keys.
So what could you do about this, in my opinion, the best way is to slightly alter your data model. So I would create a table with a primary key ID and a sort key of the item type. In your case, you would now have two item types, "Item", and "comment_[commentID]". Each comment could have the author and details fields as its attributes so something like this:
- [ID - ITEMTYPE] - NAME
- "abc" - "item" - "This is an item"
- [ID - ITEMTYPE] - AUTHOR - DETAILS
- "abc" - "comment_abcdefg" - "Alice" - "This item is my favourite!"
Now if you want to get all comments for an item you simply query for the item ID with the sort key starts with "comment". If you want to edit a comment you could simply get this item by adding the ID to the sort key which would result in an exact match. (also this would be VERY fast). There are several ways in which you could make this even more flexible but this should be enough to do what you asked for.
So sorry I just read your comment on the other answer, of course, you can add a sorting number in between the comment and the ID field in the item type sortkey this way the comments would sort automatically in that order(or reverse off course).
Is it possible to update by array index...?
Yes, it's quite easy if you know the index. This worked for me in my local localhost:8000/shell/
just now. I assumed that each comment element in the comments
list is an object and that you forgot the brackets {}
around the comment attributes in your example. Please let me know if I misunderstood or if there's more to your needs.
var params = {
TableName: 'table_name',
Key: { // The primary key of the item (a map of attribute name to AttributeValue)
id: 'abc', //(string | number | boolean | null | Binary)
// more attributes...
},
UpdateExpression: 'SET comments[1] = :value', // String representation of the update to an attribute
// SET set-action , ...
// REMOVE remove-action , ... (for document support)
// ADD add-action , ...
// DELETE delete-action , ... (previous DELETE equivalent)
ExpressionAttributeValues: { // a map of substitutions for all attribute values
':value': {
commentId: 'abcdefg1',
commentAuthor: 'Alice',
commentDetails: 'changed'
}
},
ReturnValues: 'NONE', // optional (NONE | ALL_OLD | UPDATED_OLD | ALL_NEW | UPDATED_NEW)
ReturnConsumedCapacity: 'NONE', // optional (NONE | TOTAL | INDEXES)
ReturnItemCollectionMetrics: 'NONE', // optional (NONE | SIZE)
};
docClient.update(params, function(err, data) {
if (err) ppJson(err); // an error occurred
else ppJson(data); // successful response
});
Output:
This is the way I used to deactivate users from a UsersCategoryTable:
const userIdx = users.map(user => user.M.id.S).indexOf(userId)
const deactivateUserParams = {
TableName: USERS_CATEGORY_TABLE_DEV,
Key: {id: {S: catId}},
UpdateExpression: `SET updatedAt = :updated_at, #users[${userIdx}].#status = :new_status`,
ExpressionAttributeNames: {
"#users": 'users',
"#status": "status"
},
ExpressionAttributeValues: {
":new_status": {"S": "INACTIVE"},
":updated_at": {"S": new Date().toISOString()}
},
ReturnValues: "UPDATED_NEW",
}