Saltar al contenido

Actualizando anidado array dentro array mongodb

Esta es la solución más completa que te podemos dar, sin embargo obsérvala detenidamente y analiza si se adapta a tu trabajo.

Solución:

MongoDB 3.6 y más reciente

Con MongoDB 3.6 y superior viene una nueva característica que le permite actualizar matrices anidadas utilizando el filtro posicional $[] sintaxis para hacer coincidir los elementos específicos y aplicar diferentes condiciones a través de arrayFilters en la declaración de actualización:

const  oid, pid  = req.params;
const  name, oName, description, type  = req.body; 

collection.update(
    
        "_id": 1,
        "operations": 
            "$elemMatch": 
                oid, "parameters.pid": pid
            
        
    ,
     "$set":  
        "operations.$[outer].parameters.$[inner].name": name,
        "operations.$[outer].parameters.$[inner].description": description,
        "operations.$[outer].parameters.$[inner].oName": oName,
        "operations.$[outer].parameters.$[inner].type": type 
     ,
     "arrayFilters": [
         "outer.oid": oid ,
         "inner.pid": pid 
    ] , (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);
    
);

Para MongoDB 3.4 y versiones anteriores:

Como @wdberkeley mencionó en su comentario:

MongoDB no admite la coincidencia en más de un nivel de un array. Considere modificar su modelo de documento para que cada documento represente una operación, con información común a 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 posicional $ operador para actualizar matrices anidadas)

Sin embargo, si conoce el índice de las operaciones array que tiene el objeto de parámetros a 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 el $set condiciones sobre la marcha, es decir, algo que lo ayude a obtener los índices de los objetos y luego modificarlos en consecuencia, luego considere usar Mapa reducido.

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

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

Para el paso del mapa, lo ideal sería obtener para cada documento de la colección, el índice de cada operations array campo y otro key que contiene el $set keys.

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 MapReduce creará una colección de operaciones separada que contiene las operaciones emitidas array objeto junto con un campo con el $set condiciones. Esta colección se puede actualizar periódicamente cuando ejecuta la operación MapReduce en la colección original. En total, 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"
        
    
);

Consultando la colección de salida operations de la operación MapReduce normalmente le dará el resultado:

db.operations.findOne()

Producción:


    "_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 utilizar el cursor de la db.operations.find() método 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 trata de datos que se cambian con frecuencia, debe aplanar la estructura y separar los datos que cambian mucho de los que no lo hacen.

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

Recuerda que puedes dar difusión a este artículo si lograste el éxito.

¡Haz clic para puntuar esta entrada!
(Votos: 0 Promedio: 0)



Utiliza Nuestro Buscador

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *