Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Updating nested arrays in mongodb

I have a document in mongodb with 2 level deep nested array of objects that I need to update, something like this:

{     id: 1,     items: [         {             id: 2,             blocks: [                 {                     id: 3                     txt: 'hello'                 }             ]         }     ]  } 

If there was only one level deep array I could use positional operator to update objects in it but for second level the only option I've came up is to use positional operator with nested object's index, like this:

db.objects.update({'items.id': 2}, {'$set': {'items.$.blocks.0.txt': 'hi'}}) 

This approach works but it seems dangerous to me since I'm building a web service and index number should come from client which can send say 100000 as index and this will force mongodb to create an array with 100000 indexes with null value.

Are there any other ways to update such nested objects where I can refer to object's ID instead of it's position or maybe ways to check if supplied index is out of bounds before using it in query?

like image 793
src091 Avatar asked Nov 08 '10 06:11

src091


People also ask

How do I update a nested array in MongoDB?

Update Nested Arrays in Conjunction with $[]The $[<identifier>] filtered positional operator, in conjunction with the $[] all positional operator, can be used to update nested arrays. The following updates the values that are greater than or equal to 8 in the nested grades. questions array if the associated grades.

How do you update an array object in MongoDB?

Update Documents in an ArrayThe positional $ operator facilitates updates to arrays that contain embedded documents. Use the positional $ operator to access the fields in the embedded documents with the dot notation on the $ operator. You must include the array field as part of the query document.


2 Answers

Here's the big question, do you need to leverage Mongo's "addToSet" and "push" operations? If you really plan to modify just individual items in the array, then you should probably build these arrays as objects.

Here's how I would structure this:

{     id: 1,     items:          {            "2" : { "blocks" : { "3" : { txt : 'hello' } } },           "5" : { "blocks" : { "1" : { txt : 'foo'}, "2" : { txt : 'bar'} } }         } } 

This basically transforms everything in to JSON objects instead of arrays. You lose the ability to use $push and $addToSet but I think this makes everything easier. For example, your query would look like this:

db.objects.update({'items.2': {$exists:true} }, {'$set': {'items.2.blocks.0.txt': 'hi'}})

You'll also notice that I've dumped the "IDs". When you're nesting things like this you can generally replace "ID" with simply using that number as an index. The "ID" concept is now implied.

This feature has been added in 3.6 with expressive updates.

db.objects.update( {id: 1 }, { $set: { 'items.$[itm].blocks.$[blk].txt': "hi", } }, { multi: false, arrayFilters: [ { 'itm.id': 2 }, { 'blk.id': 3} ] } )

like image 76
Gates VP Avatar answered Oct 11 '22 08:10

Gates VP


The ids which you are using are linear number and it has to come from somewhere like an additional field such 'max_idx' or something similar. This means one lookup for the id and then update. UUID/ObjectId can be used for ids which will ensure that you can use Distributed CRUD as well.

like image 38
Seraj Avatar answered Oct 11 '22 06:10

Seraj