是否可以而不编写所有子文档的字段?
我们说我有以下文档结构:
{
field1: a,
subdoc: {
field2: b,
field3: c
}
}
我想使用$project
以便在根级别获取子文件:
{
field1: a,
field2: b,
field3: c
}
这只是在subdoc中有2个字段的示例,我的真实文档有很多字段,将来可能会添加或删除更多字段,所以我希望$project
更加动态而不是分别指定所有字段。
答案 0 :(得分:4)
对于MongoDB 3.6及更高版本,请使用聚合框架和 $replaceRoot
管道,该管道可与 $mergeObjects
运算符一起应用newRoot
表达。
此表达式
{ "$mergeObjects": ["$subdoc", "$$ROOT"] }
会将文档中的顶级字段与subdoc嵌入字段中的顶级字段合并,因此最后您的聚合操作将如下所示:
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"
}
};
因此,使用上面的子文档键列表,您可以使用JavaScript的 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]);