Actualización de matriz anidada dentro de la matriz mongodb

Tengo la siguiente estructura de 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": "" } ] } ] } ] 

He podido usar $ elemMatch para actualizar los campos en las operaciones, pero cuando trato de hacer lo mismo (modificado) para los parámetros, parece que no funciona. Me preguntaba qué otro enfoque debería considerar para poder actualizar campos con éxito en un parámetro específico, buscándolo por su pid.

El código de actualización que tengo actualmente y que no funciona tiene el siguiente aspecto:

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

Como @wdberkeley mencionó en su comentario:

MongoDB no admite la coincidencia en más de un nivel de una matriz. Considere modificar su modelo de documento para que cada documento represente una operación, con información común para un conjunto de operaciones duplicadas en los documentos de operación.

Estoy de acuerdo con lo anterior y recomendaría rediseñar su esquema ya que el motor MongoDB no admite múltiples operadores posicionales (consulte Uso múltiple del operador posicional $ para actualizar matrices anidadas )

Sin embargo, si conoce el índice de la matriz de operaciones que tiene el objeto de parámetros que se actualizará de antemano, la consulta de actualización será:

 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" } } ) 

EDITAR:

Si desea crear las condiciones $set sobre la marcha, es decir, algo que lo ayude a obtener los índices de los objetos y luego modificar en consecuencia, entonces considere usar MapReduce .

Actualmente esto parece no ser posible usando el marco de agregación. Hay un problema JIRA abierto sin resolver vinculado a él. Sin embargo, una solución alternativa es posible con MapReduce . La idea básica de MapReduce es que utiliza JavaScript como lenguaje de consulta, pero esto tiende a ser bastante más lento que el marco de agregación y no debe utilizarse para el análisis de datos en tiempo real.

En su operación de MapReduce, necesita definir un par de pasos, es decir, el paso de asignación (que mapea una operación en cada documento de la colección, y la operación no puede hacer nada o emitir algún objeto con claves y valores proyectados) y reducir el paso ( que toma la lista de valores emitidos y la reduce a un solo elemento).

Para el paso del mapa, lo ideal es obtener cada documento de la colección, el índice de cada campo de matriz de operations y otra clave que contiene las claves $set .

Su paso de reducción sería una función (que no hace nada) simplemente definida como var reduce = function() {};

El paso final en su operación de MapReduce creará entonces operaciones de recolección separadas que contienen el objeto de matriz de operaciones emitidas junto con un campo con las condiciones $set . Esta colección puede actualizarse periódicamente cuando ejecuta la operación MapReduce en la colección original. En conjunto, este método MapReduce se vería así:

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

Consultar las operations recolección de salida desde la operación de MapReduce generalmente le dará el resultado:

 db.operations.findOne() 

Salida :

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

A continuación, puede usar el cursor del método db.operations.find() para iterar y actualizar su colección en consecuencia:

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

Si se cambian los datos con frecuencia, debe aplanar la estructura y separar los datos que cambian mucho de los que no.

Si los datos no cambian con frecuencia y el objeto de datos completo no es masivo, simplemente modifique el objeto del lado del cliente y actualice todo el objeto.

Intentaremos encontrar el índice de la matriz externa (i) y la matriz interna (j) y luego actualizaremos

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

A partir de la versión 3.6 de mongo, puede usar $ [] en Conjunción con $ [] para actualizar matrices anidadas

Actualizar matrices anidadas en conjunción con $ []

El operador posicional filtrado $ [], junto con todo el operador posicional $ [] se puede usar para actualizar matrices anidadas.

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