MongoDB
 sql >> Base de Dados >  >> NoSQL >> MongoDB

MongoDB - documento aninhado $project para o nível raiz


Para MongoDB 3.6 e mais recente, use a estrutura de agregação com um $replaceRoot pipeline que pode ser aplicado em conjunto com o $mergeObjects operador como o newRoot expressão.

Esta expressão
{ "$mergeObjects": ["$subdoc", "$$ROOT"] }

irá mesclar os campos de nível superior no documento com os campos incorporados do subdoc, então, no final, sua operação agregada será a seguinte:
db.collection.aggregate([
    { "$replaceRoot": { 
        "newRoot": { 
            "$mergeObjects": [ "$subdoc", "$$ROOT" ] 
        } 
    } },
    { "$project": { "subdoc": 0 } }  
])

Caso contrário, você precisaria de um mecanismo para obter todas as chaves dinâmicas necessárias para montar o $project dinâmico documento. Isso é possível por meio do Map-Reduce . A operação mapreduce a seguir preencherá uma coleção separada com todas as chaves 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 obter uma lista de todas as chaves dinâmicas, execute distinct na coleção resultante:
db[mr.result].distinct("_id")
["field2", "field3", ...]

Agora, dada a lista acima, você pode montar seu $project documento de pipeline de agregação criando um objeto que terá suas propriedades definidas em um loop. Normalmente seu $project documento terá esta estrutura:
var project = {
    "$project": {
        "field1": 1,
        "field2": "$subdoc.field2",
        "field3": "$subdoc.field3"
    }
};

Portanto, usando a lista acima de chaves de subdocumento, você pode construir dinamicamente o acima usando o 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]);