За MongoDB 3.6 и по-нови, използвайте рамка за агрегиране с $replaceRoot
тръбопровод, който може да се прилага във връзка с $mergeObjects
оператор като newRoot
израз.
Този израз
{ "$mergeObjects": ["$subdoc", "$$ROOT"] }
ще обедини полетата от най-високо ниво в документа с тези във вградените полета на поддокумента, така че в крайна сметка вашата обобщена операция ще бъде както следва:
db.collection.aggregate([
{ "$replaceRoot": {
"newRoot": {
"$mergeObjects": [ "$subdoc", "$$ROOT" ]
}
} },
{ "$project": { "subdoc": 0 } }
])
В противен случай ще ви трябва механизъм, за да получите всички динамични ключове, които са ви необходими, за да сглобите динамичния $project
документ. Това е възможно чрез Map-Reduce
. Следната операция mapreduce ще попълни отделна колекция с всички ключове като _id
стойности:
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"
})
За да получите списък с всички динамични ключове, изпълнете distinct в получената колекция:
db[mr.result].distinct("_id")
["field2", "field3", ...]
Сега като имате предвид списъка по-горе, можете да сглобите своя $project
тръбопроводен документ за агрегиране чрез създаване на обект, чиито свойства ще бъдат зададени в рамките на цикъл. Обикновено вашият $project
документ ще има следната структура:
var project = {
"$project": {
"field1": 1,
"field2": "$subdoc.field2",
"field3": "$subdoc.field3"
}
};
Така че, използвайки горния списък с ключове на поддокументи, можете динамично да конструирате горното, като използвате reduce()
метод:
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]);