Mongodb updates deeply nested subdocument

I have a document structure that is deeply nested, for example:

{id: 1, forecasts: [ { forecast_id: 123, name: "Forecast 1", levels: [ { level: "proven", configs: [ { config: "Custom 1", variables: [{ x: 1, y:2, z:3}] }, { config: "Custom 2", variables: [{ x: 10, y:20, z:30}] }, ] }, { level: "likely", configs: [ { config: "Custom 1", variables: [{ x: 1, y:2, z:3}] }, { config: "Custom 2", variables: [{ x: 10, y:20, z:30}] }, ] } ] }, ] } 

I am trying to update a collection to insert a new configuration that looks like this:

 newdata = { config: "Custom 1", variables: [{ x: 111, y:2222, z:3333}] } 

I am trying to do something like this in mongo (in Python):

 db.myCollection.update({"id": 1, "forecasts.forecast-id": 123, "forecasts.levels.level": "proven", "forecasts.levels.configs.config": "Custom 1" }, {"$set": {"forecasts.$.levels.$.configs.$": newData}} ) 

I get "It is not possible to apply the positioning operator without the corresponding query field containing the array error." What is the right way to do this in mongo? This is mongo v2.4.1.

+44
mongodb
Aug 11 '13 at 15:25
source share
8 answers

Unfortunately, you cannot use the $ operator more than once for each key, so you need to use numeric values ​​for the rest. How in:

 db.myCollection.update({ "id": 1, "forecasts.forecast-id": 123, "forecasts.levels.level": "proven", "forecasts.levels.configs.config": "Custom 1" }, {"$set": {"forecasts.$.levels.0.configs.0": newData}} ) 

MongoDB support for updating nested arrays is poor. Therefore, you should better avoid using them if you need to update data frequently and use multiple collections instead.

One possibility: make forecasts your own collection and assuming that you have a fixed set of level values, make a level object instead of an array:

 { _id: 123, parentId: 1, name: "Forecast 1", levels: { proven: { configs: [ { config: "Custom 1", variables: [{ x: 1, y:2, z:3}] }, { config: "Custom 2", variables: [{ x: 10, y:20, z:30}] }, ] }, likely: { configs: [ { config: "Custom 1", variables: [{ x: 1, y:2, z:3}] }, { config: "Custom 2", variables: [{ x: 10, y:20, z:30}] }, ] } } } 

Then you can update it using:

 db.myCollection.update({ _id: 123, 'levels.proven.configs.config': 'Custom 1' }, { $set: { 'levels.proven.configs.$': newData }} ) 
+31
Aug 11 '13 at 16:29
source share

It was possible to solve the problem using mongoose:

All you need to know is the "_id of the entire subdocument in the chain (mongoose automatically creates a" _id "for each subdocument).

eg -

  SchemaName.findById(_id, function (e, data) { if (e) console.log(e); data.sub1.id(_id1).sub2.id(_id2).field = req.body.something; // or if you want to change more then one field - //=> var t = data.sub1.id(_id1).sub2.id(_id2); //=> t.field = req.body.something; data.save(); }); 

Read more about the _id method supporting document in the mongoose documentation .

Explanation: _id for SchemaName, _id1 for sub1 and _id2 for sub2 β€” you can save the chain this way.

* You do not need to use the findById method, but it seems to me the most convenient, since you still need to know the rest of _id.

+13
Mar 09 '15 at 22:35
source share

This is a very OLD error in MongoDB

https://jira.mongodb.org/browse/SERVER-831

+4
May 23 '15 at 19:31
source share

Given that MongoDB does not seem to be a good mechanism for this, I find it prudent to use mongoose to simply extract an item from the mongo collection using .findOne(...) , run a for-loop search in the relevant subelements (search by According to ObjectID), change this JSON, then do Schema.markModified('your.subdocument'); Schema.save(); Schema.markModified('your.subdocument'); Schema.save(); It is probably inefficient, but it is very simple and works great.

+2
Sep 12 '13 at 11:33
source share

Corrected. https://jira.mongodb.org/browse/SERVER-831

But this feature is available starting from MongoDB development version 3.5.12.

Note. This question was asked on Aug 11 2013 , and it was resolved on Aug 11 2017

+1
Sep 01 '17 at 10:48 on
source share

Okkk.we can update our subdocument in mongodb.this - our scheme.

 var Post = new mongoose.Schema({ name:String, post:[{ like:String, comment:[{ date:String, username:String, detail:{ time:String, day:String } }] }] }) 

for this circuit

  Test.update({"post._id":"58206a6aa7b5b99e32b7eb58"}, {$set:{"post.$.comment.0.detail.time":"aajtk"}}, function(err,data){ //data is updated }) 
0
Nov 07 '16 at 12:19
source share

Sharing my lessons. I recently came across the same requirement when I need to update a nested array element. My structure is as follows

  { "main": { "id": "ID_001", "name": "Fred flinstone Inc" }, "types": [ { "typeId": "TYPE1", "locations": [ { "name": "Sydney", "units": [ { "unitId": "PHG_BTG1" } ] }, { "name": "Brisbane", "units": [ { "unitId": "PHG_KTN1" }, { "unitId": "PHG_KTN2" } ] } ] } ] } 

My requirement is to add some fields to specific units []. My solution is to first find the index of the nested array element (e.g. foundUnitIdx) I used two methods:

  • use the keyword $ set
  • specify a dynamic field in $ set using the syntax []

      query = { "locations.units.unitId": "PHG_KTN2" }; var updateItem = { $set: { ["locations.$.units."+ foundUnitIdx]: unitItem } }; var result = collection.update( query, updateItem, { upsert: true } ); 

Hope this helps others. :)

0
Nov 08 '17 at 23:51 on
source share

EASY SOLUTION FOR Mongodb 3.2 + https://docs.mongodb.com/manual/reference/method/db.collection.replaceOne/

I had a similar situation and I decided it this way. I used mongoose, but it should still work in vanilla MongoDB. Hope this is helpful to someone.

 const MyModel = require('./model.js') const query = {id: 1} // First get the doc MyModel.findOne(query, (error, doc) => { // Do some mutations doc.foo.bar.etc = 'some new value' // Pass in the mutated doc and replace MyModel.replaceOne(query, doc, (error, newDoc) => { console.log('It worked!') }) } 

Depending on your use case, you may skip the initial findOne () function

0
Dec 24 '17 at 22:26
source share



All Articles