Mongodb aggiorna il documento secondario profondamente annidato

Ho una struttura del documento che è profondamente annidata, come questa:

{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}] }, ] } ] }, ] } 

Sto cercando di aggiornare la raccolta per inserire una nuova configurazione, che assomiglia a questo:

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

Sto provando qualcosa di simile 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}} ) 

Sto ottenendo “Imansible applicare l’operatore posizionale senza un campo di query corrispondente contenente un errore di” matrice però. Qual è il modo corretto di farlo in Mongo? Questo è mongo v2.4.1.

Sfortunatamente, non è ansible utilizzare l’operatore $ più di una volta per chiave, quindi è necessario utilizzare valori numerici per il resto. Come 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}} ) 

Il supporto di MongoDB per l’aggiornamento degli array annidati è scadente. Pertanto, è meglio evitare il loro utilizzo se è necessario aggiornare frequentemente i dati e prendere in considerazione l’utilizzo di più raccolte.

Una possibilità: fare forecasts propria collezione e assumendo che si abbia un insieme fisso di valori di level , rendi di level un object invece di una matrice:

 { _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}] }, ] } } } 

Quindi puoi aggiornarlo usando:

 db.myCollection.update({ _id: 123, 'levels.proven.configs.config': 'Custom 1' }, { $set: { 'levels.proven.configs.$': newData }} ) 

Riuscito a risolverlo con l’utilizzo di mangusta:

Tutto quello che devi sapere sono gli _id di tutti i sottocodici della catena (la mangusta crea automaticamente ‘_id’ per ogni sotto-documento).

per esempio –

  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(); }); 

Maggiori informazioni sul metodo _id sotto-documento nella documentazione di mangusta .

spiegazione: _id è per SchemaName, _id1 per sub1 e _id2 per sub2: puoi continuare a concatenare in questo modo.

* Non è necessario utilizzare il metodo findById, ma mi sembra il più conveniente in quanto è necessario conoscere il resto degli ID in ogni caso.

Questo è un bug molto vecchio in MongoDB

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

MongoDB ha introdotto ArrayFilters per affrontare questo problema nella versione 3.5.2 e successive.

Novità nella versione 3.6.

A partire da MongoDB 3.6, quando si aggiorna un campo di array, è ansible specificare arrayFilters che determinano quali elementi di array aggiornare.

[ https://docs.mongodb.com/manual/reference/method/db.collection.update/#specify-arrayfilters-for-an-array-update-operations%5D%5B1%5D

Diciamo che il progetto Schema come segue:

 var ProfileSchema = new Schema({ name: String, albums: [{ tour_name: String, images: [{ title: String, image: String }] }] }); 

E il documento creato assomiglia a questo:

 { "_id": "1", "albums": [{ "images": [ { "title": "t1", "url": "url1" }, { "title": "t2", "url": "url2" } ], "tour_name": "london-trip" }, { "images": [.........]: }] } 

Dì che voglio aggiornare “url” di un’immagine. Dato – "document id", "tour_name" and "title"

Per questo la query di aggiornamento:

 Profiles.update({_id : req.body.id}, { $set: { 'albums.$[i].images.$[j].title': req.body.new_name } }, { arrayFilters: [ { "i.tour_name": req.body.tour_name, "j.image": req.body.new_name // tour_name - current tour name, new_name - new tour name }] }) .then(function (resp) { console.log(resp) res.json({status: 'success', resp}); }).catch(function (err) { console.log(err); res.status(500).json('Failed'); }) 

Dato che MongoDB non sembra fornire un buon meccanismo per questo, trovo prudente usare la mangusta per estrarre semplicemente l’elemento dalla collezione mongo usando .findOne(...) , eseguire una ricerca for-loop sui relativi sottoelementi rilevanti (cercando per esempio ObjectID), modificare tale JSON, quindi fare Schema.markModified('your.subdocument'); Schema.save(); Schema.markModified('your.subdocument'); Schema.save(); Probabilmente non è efficiente, ma è molto semplice e funziona bene.

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

Ma questa funzionalità è disponibile a partire dalla versione di sviluppo di MongoDB 3.5.12.

Nota: questa domanda è stata richiesta l’ Aug 11 2013 ed è stata risolta l’ Aug 11 2017

Condividere le mie lezioni apprese. Recentemente ho affrontato lo stesso requisito in cui ho bisogno di aggiornare un elemento dell’array nidificato. La mia struttura è la seguente

  { "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" } ] } ] } ] } 

Il mio requisito è quello di aggiungere alcuni campi in unità specifiche []. La mia soluzione è innanzitutto quella di trovare l’indice dell’elemento dell’array nidificato (ad esempio foundUnitIdx) Le due tecniche che ho usato sono

  1. usa la parola chiave $ set
  2. specificare il campo dinamico in $ set usando la syntax []

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

Spero che questo aiuti gli altri. 🙂

Okkk.we può aggiornare il nostro documento secondario nidificato in mongodb.questo è il nostro schema.

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

soluzione per questo schema

  Test.update({"post._id":"58206a6aa7b5b99e32b7eb58"}, {$set:{"post.$.comment.0.detail.time":"aajtk"}}, function(err,data){ //data is updated }) 

SOLUZIONE FACILE PER Mongodb 3.2+ https://docs.mongodb.com/manual/reference/method/db.collection.replaceOne/

Ho avuto una situazione simile e l’ho risolto in questo modo. Stavo usando mangusta, ma dovrebbe ancora funzionare in vanilla MongoDB. Spero sia utile a qualcuno.

 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!') }) } 

A seconda del tuo caso d’uso, potresti essere in grado di saltare il findOne iniziale ()