MongoDB
 sql >> Database >  >> NoSQL >> MongoDB

MongoDB - Documento annidato $project a livello radice

Per MongoDB 3.6 e versioni successive, utilizzare il framework di aggregazione con un $replaceRoot pipeline che può essere applicata insieme a $mergeObjects operatore come newRoot espressione.

Questa espressione

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

unirà i campi di livello superiore nel documento con quelli nei campi incorporati del sottodoc, quindi alla fine l'operazione di aggregazione sarà la seguente:

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

Altrimenti avresti bisogno di un meccanismo per ottenere tutte le chiavi dinamiche di cui hai bisogno per assemblare il $project dinamico documento. Ciò è possibile tramite Map-Reduce . La seguente operazione mapreduce popolerà una raccolta separata con tutte le chiavi come _id valori:

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

Per ottenere un elenco di tutte le chiavi dinamiche, esegui distinto sulla raccolta risultante:

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

Ora, dato l'elenco sopra, puoi assemblare il tuo $project documento della pipeline di aggregazione creando un oggetto con le sue proprietà impostate all'interno di un ciclo. Normalmente il tuo $project il documento avrà questa struttura:

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

Quindi, usando l'elenco sopra di chiavi di documenti secondari, puoi costruire dinamicamente quanto sopra usando reduce() metodo:

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