This will be a bit of a reference guide to updating a CosmosDB collections and nested items in a nice manner. This thread will not cover how to use the CosmosDB package in Node.JS and it will assume you’ve successfully connected to a database.
I will use the example of updating the signature of a user of a forum where that signature is stored in a collection which is the overall thread and then nested collections which are the individual comments.
Example Collection
The collection below will be the example I’m using through this article, this is a simplified representation of a forum post collection –
{
"lastActivity": 1656354969213,
"id": "aefe1362-4fe4-4bc7-8d77-8250fa948aaa",
"title": "Testing!",
"content": "<p>Hello!</p>",
"signature": "<p>An amazing signature v15</p>",
"authorId": 1,
"comments": [
{
"content": "<p>test 2</p>",
"authorId": 1,
"commentId": 0,
"liked": [],
"author": "Diablo",
"signature": "<p>An amazing signature v15</p>",
"disliked": [],
"date": 1656353275267
},
{
"content": "<p>another</p>",
"authorId": 1,
"commentId": 1,
"liked": [],
"author": "Diablo",
"signature": "<p>An amazing signature v15</p>",
"disliked": [],
"date": 1656354969213
}
],
"adminOnlyResponse": false,
"categoryId": 1,
"_ts": 1656444244
}
Query for Collections where Property or Nested Propert Equals To
The code below (non-complete) returns just the values of the collections that we want to update or use to update the signature.
// Define a query which returns a collection where authorId or where the "comments" nested collection array contains a record with authorId equal to a given userId
const querySpec = `SELECT r.id, r.categoryId, r.authorId, r.comments, r.signature FROM r WHERE r.authorId = ${userId} OR ARRAY_CONTAINS(r.comments, {"authorId": ${userId}}, true)`
var existingThreads = await this.find(querySpec)
Patch each collection and nested collections
The code below will loop through each thread that is returned by the query above and will also loop through each nested comment collection within that thread record.
If the thread needs to have the signature updated then we will push a new patch operation to the operations array.
Finally if there are any operations required we will then perform a patch operation, updating the original record.
// Loop through all returned threads
existingThreads.forEach(thread => {
var operations = []
// If the thread is created by the given user and the signature is different then add a replace patch operation
if(thread.authorId == userId && thread.signature != newSignature) {
operations.push({op: 'replace', path:'/signature', value: newSignature})
}
// For each comment in thread, update the signature value of each collection array
thread.comments.forEach((comment, index) => {
if(comment.authorId == userId && comment.signature != newSignature) {
operations.push({op: 'replace', path:`/comments/${index}/signature`, value: newSignature})
}
})
// If there are any operations to perform then perform a patch operation
if(operations.length > 0) {
// @ts-ignore
this.container
.item(
thread.id,
thread.categoryId
)
// @ts-ignore
.patch(operations)
}
})