För MongoDB 3.6 och senare, använd aggregeringsramverk med en $replaceRoot
pipeline som kan tillämpas tillsammans med $mergeObjects
operatorn som newRoot
uttryck.
Detta uttryck
{ "$mergeObjects": ["$subdoc", "$$ROOT"] }
kommer att slå samman fälten på översta nivån i dokumentet med de i de inbäddade subdoc-fälten så att din sammanlagda operation i slutändan blir som följer:
db.collection.aggregate([
{ "$replaceRoot": {
"newRoot": {
"$mergeObjects": [ "$subdoc", "$$ROOT" ]
}
} },
{ "$project": { "subdoc": 0 } }
])
Annars skulle du behöva en mekanism för att få alla dynamiska nycklar som du behöver för att montera det dynamiska $project
dokumentera. Detta är möjligt genom Map-Reduce
. Följande mapreduce-operation kommer att fylla en separat samling med alla nycklar som _id
värden:
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"
})
För att få en lista över alla dynamiska nycklar, kör distinkt på den resulterande samlingen:
db[mr.result].distinct("_id")
["field2", "field3", ...]
Med tanke på listan ovan kan du sätta ihop ditt $project
aggregeringspipelinedokument genom att skapa ett objekt som kommer att ha sina egenskaper inställda i en loop. Normalt är ditt $project
dokument kommer att ha denna struktur:
var project = {
"$project": {
"field1": 1,
"field2": "$subdoc.field2",
"field3": "$subdoc.field3"
}
};
Så genom att använda listan ovan med underdokumentnycklar kan du dynamiskt konstruera ovanstående med JavaScripts reduce()
metod:
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]);