sql >> Base de Datos >  >> NoSQL >> MongoDB

MongoDB - documento anidado de $ proyecto a nivel raíz

Para MongoDB 3.6 y más reciente, use el marco de agregación con un $replaceRoot canalización que se puede aplicar junto con $mergeObjects operador como newRoot expresión.

Esta expresión

{ "$mergeObjects": ["$subdoc", "$$ROOT"] }

combinará los campos de nivel superior en el documento con los de los campos incrustados del subdoc, por lo que al final su operación agregada será la siguiente:

db.collection.aggregate([
    { "$replaceRoot": { 
        "newRoot": { 
            "$mergeObjects": [ "$subdoc", "$$ROOT" ] 
        } 
    } },
    { "$project": { "subdoc": 0 } }  
])

De lo contrario, necesitaría un mecanismo para obtener todas las claves dinámicas que necesita para ensamblar el $project dinámico. documento. Esto es posible a través de Map-Reduce . La siguiente operación mapreduce llenará una colección separada con todas las claves como _id valores:

mr = db.runCommand({
    "mapreduce": "my_collection",
    "map" : function() {
        for (var key in this.subdoc) { emit(key, null); }
    },
    "reduce" : function(key, stuff) { return null; }, 
    "out": "my_collection" + "_keys"
})

Para obtener una lista de todas las claves dinámicas, ejecute distinto en la colección resultante:

db[mr.result].distinct("_id")
["field2", "field3", ...]

Ahora, dada la lista anterior, puede ensamblar su $project documento de canalización de agregación mediante la creación de un objeto que tendrá sus propiedades establecidas dentro de un bucle. Normalmente tu $project documento tendrá esta estructura:

var project = {
    "$project": {
        "field1": 1,
        "field2": "$subdoc.field2",
        "field3": "$subdoc.field3"
    }
};

Entonces, al usar la lista anterior de claves de subdocumento, puede construir dinámicamente lo anterior usando JavaScript reduce() método:

var subdocKeys = db[mr.result].distinct("_id"),
    obj = subdocKeys.reduce(function (o, v){
      o[v] = "$subdoc." + v;
      return o;
    }, { "field1": 1 }),
    project = { "$project": obj };

db.collection.aggregate([project]);