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