Aggiornamento dell’array annidato all’interno dell’array mongodb

Ho la seguente struttura del documento mongodb:

[ { "_id": "04", "name": "test service 4", "id": "04", "version": "0.0.1", "title": "testing", "description": "test", "protocol": "test", "operations": [ { "_id": "99", "oName": "test op 52222222222", "sid": "04", "name": "test op 52222222222", "oid": "99", "description": "testing", "returntype": "test", "parameters": [ { "oName": "Param1", "name": "Param1", "pid": "011", "type": "582", "description": "testing", "value": "" }, { "oName": "Param2", "name": "Param2", "pid": "012", "type": "58222", "description": "testing", "value": "" } ] } ] } ] 

Sono stato in grado di utilizzare $ elemMatch per aggiornare i campi nelle operazioni, ma quando provo a fare la stessa cosa (modificata) per i parametri, non sembra funzionare. Mi chiedevo quale altro approccio dovrei esaminare per provare ad essere in grado di aggiornare correttamente i campi in un parametro specifico, osservandolo dal suo pid.

Il codice di aggiornamento che attualmente ho e non funziona assomiglia a questo:

 var oid = req.params.operations; var pid = req.params.parameters; collection.update({"parameters":{"$elemMatch": {"pid": pid}}},{"$set": {"parameters.$.name":req.body.name, "parameters.$.description": req.body.description,"parameters.$.oName": req.body.oName,"parameters.$.type": req.body.type} }, function(err, result) { if (err) { console.log('Error updating service: ' + err); res.send({'error':'An error has occurred'}); } else { // console.log('' + result + ' document(s) updated'); res.send(result); } }); 

Come @wdberkeley ha menzionato nel suo commento:

MongoDB non supporta la corrispondenza in più di un livello di un array. Valutare la possibilità di modificare il modello del documento in modo che ogni documento rappresenti un’operazione, con informazioni comuni a un insieme di operazioni duplicate nei documenti operativi.

Concordo con quanto sopra e raccomanderei la riprogettazione dello schema in quanto il motore MongoDB non supporta più operatori posizionali (Vedi Uso multiplo dell’operatore $ posizionale per aggiornare gli array annidati )

Tuttavia, se si conosce l’indice dell’array delle operazioni con l’object parametri da aggiornare in anticipo, la query di aggiornamento sarà:

 db.collection.update( { "_id" : "04", "operations.parameters.pid": "011" }, { "$set": { "operations.0.parameters.$.name": "foo", "operations.0.parameters.$.description": "bar", "operations.0.parameters.$.type": "foo" } } ) 

MODIFICARE:

Se desideri creare al volo le condizioni $set , cioè qualcosa che ti aiuti a ottenere gli indici per gli oggetti e poi a modificare di conseguenza, allora considera di utilizzare MapReduce .

Attualmente questo sembra non essere ansible usando la struttura di aggregazione. C’è un problema JIRA aperto non risolto collegato ad esso. Tuttavia, una soluzione alternativa è ansible con MapReduce . L’idea di base con MapReduce è che utilizza JavaScript come linguaggio di query, ma tende ad essere più lento del framework di aggregazione e non dovrebbe essere usato per l’analisi dei dati in tempo reale.

Nell’operazione MapReduce, è necessario definire un paio di passaggi, ad esempio il passo di mapping (che esegue il mapping di un’operazione in ogni documento nella raccolta e l’operazione non può fare nulla o emettere qualche object con chiavi e valori proiettati) e ridurre il passo ( che prende la lista dei valori emessi e la riduce ad un singolo elemento).

Per il passo della mappa, idealmente vorrai ottenere per ogni documento nella collezione, l’indice per ogni campo dell’array di operations e un’altra chiave che contiene le chiavi $set .

Il tuo passo di riduzione sarebbe una funzione (che non fa nulla) semplicemente definita come var reduce = function() {};

Il passaggio finale nell’operazione MapReduce creerà quindi operazioni di raccolta separate che contengono l’object matrice operazioni emesse insieme a un campo con le condizioni $set . Questa raccolta può essere aggiornata periodicamente quando si esegue l’operazione MapReduce sulla raccolta originale. Complessivamente, questo metodo MapReduce sarà simile a:

 var map = function(){ for(var i = 0; i < this.operations.length; i++){ emit( { "_id": this._id, "index": i }, { "index": i, "operations": this.operations[i], "update": { "name": "operations." + i.toString() + ".parameters.$.name", "description": "operations." + i.toString() + ".parameters.$.description", "type": "operations." + i.toString() + ".parameters.$.type" } } ); } }; var reduce = function(){}; db.collection.mapReduce( map, reduce, { "out": { "replace": "operations" } } ); 

Interrogare le operations raccolta dell'output operations MapReduce ti darà in genere il risultato:

 db.operations.findOne() 

Uscita :

 { "_id" : { "_id" : "03", "index" : 0 }, "value" : { "index" : 0, "operations" : { "_id" : "96", "oName" : "test op 52222222222", "sid" : "04", "name" : "test op 52222222222", "oid" : "99", "description" : "testing", "returntype" : "test", "parameters" : [ { "oName" : "Param1", "name" : "foo", "pid" : "011", "type" : "foo", "description" : "bar", "value" : "" }, { "oName" : "Param2", "name" : "Param2", "pid" : "012", "type" : "58222", "description" : "testing", "value" : "" } ] }, "update" : { "name" : "operations.0.parameters.$.name", "description" : "operations.0.parameters.$.description", "type" : "operations.0.parameters.$.type" } } } 

È quindi ansible utilizzare il cursore dal metodo db.operations.find() per eseguire l'iterazione e aggiornare di conseguenza la raccolta:

 var oid = req.params.operations; var pid = req.params.parameters; var cur = db.operations.find({"_id._id": oid, "value.operations.parameters.pid": pid }); // Iterate through results and update using the update query object set dynamically by using the array-index syntax. while (cur.hasNext()) { var doc = cur.next(); var update = { "$set": {} }; // set the update query object update["$set"][doc.value.update.name] = req.body.name; update["$set"][doc.value.update.description] = req.body.description; update["$set"][doc.value.update.type] = req.body.type; db.collection.update( { "_id" : oid, "operations.parameters.pid": pid }, update ); }; 

Se si tratta di dati modificati frequentemente, è necessario appiattire la struttura e separare i dati che variano molto da quelli che non lo sono.

Se si tratta di dati che non cambiano spesso e l’intero object dati non è enorme, basta modificare l’object lato client e aggiornare l’intero object.

Cercheremo di trovare l’indice dell’array esterno (i) e dell’array interno (j) e quindi aggiornare

 collection.findById(04) .then(result =>{ for(let i = 0; i res.json(dbModel)) } } } } }) 

A partire da mongo versione 3.6 è ansible utilizzare $ [] in Conjunction con $ [] per aggiornare le matrici nidificate

Aggiorna le matrici annidate in congiunzione con $ []

L’operatore posizionale $ [] filtrato, in combinazione con tutti gli operatori posizionali $ [] può essere utilizzato per aggiornare gli array annidati.

https://docs.mongodb.com/manual/reference/operator/update/positional-filtered/#position-nested-arrays-filtered