我有一个文档的平面集合,其中一些文档有一个 parent: ObjectId
字段,该字段指向同一集合中的另一个文档,即:
{id: 1, metadata: {text: "I'm a parent"}}
{id: 2, metadata: {text: "I'm child 1", parent: 1}}
现在我想检索 所有 metadata.text = "I'm a Parent"
及其子元素的父元素。但我希望该数据采用嵌套格式,因此我可以在之后简单地处理它,而无需查看 metadata.parent
。输出应如下所示:
{
id: 1,
metadata: {text: "I'm a parent"},
children: [
{id: 2, metadata: {text: "I'm child 1", parent: 1}}
]
}
(如果更容易的话,children
也可以是父级 metadata
对象的一部分)
为什么我不将文档保存在嵌套结构中?我不想将数据以嵌套格式存储在数据库中,因为这些文档是 GridFS 的一部分。
主要问题是:如何告诉 MongoDB 嵌套整个文档?或者我是否必须使用 Mongo 的聚合框架来完成该任务?
最佳答案
对于您要求的那种“投影”,那么聚合框架是正确的工具,因为只有那里才真正支持这种“文档 reshape ”。
另一种情况是“父/子”的情况,在使用聚合框架进行分组时,您再次需要“创造性”。完整的操作显示了主要涉及的内容:
db.collection.aggregate([
// Group parent and children together with conditionals
{ "$group": {
"_id": { "$ifNull": [ "$metadata.parent", "$_id" ] },
"metadata": {
"$addToSet": {
"$cond": [
{ "$ifNull": [ "$metadata.parent", false ] },
false,
"$metadata"
]
}
},
"children": {
"$push": {
"$cond": [
{ "$ifNull": [ "$metadata.parent", false ] },
"$$ROOT",
false
]
}
}
}},
// Filter out "false" values
{ "$project": {
"metadata": { "$setDifference": [ "$metadata", [false] ] },
"children": { "$setDifference": [ "$children", [false] ] }
}},
// metadata is an array but should only have one item
{ "$unwind": "$metadata" },
// This is essentially sorting the children as "sets" are un-ordered
{ "$unwind": "$children" },
{ "$sort": { "_id": 1, "children._id": 1 } },
{ "$group": {
"_id": "$_id",
"metadata": { "$first": "$metadata" },
"children": { "$push": "$children" }
}}
])
这里最主要的是$ifNull
用于分组 _id
的运算符。这里会选择$group
在存在的“父”字段上,否则使用通用文档 _id
。
使用 $cond
完成类似的操作稍后进行运算符,其中评估要添加到数组或“集合”中的数据。以下$project
使用 $setDifference
过滤掉 false
值运算符。
如果最后$sort
和 $group
似乎令人困惑,那么实际原因是因为使用的运算符是“集合”运算符,因此结果“集合”被认为是无序的。所以实际上该部分只是为了确保数组内容按照其自己的 _id
字段的顺序显示。
如果没有 MongoDB 2.6 中的额外运算符,这仍然可以完成,但只是有点不同。
db.collection.aggregate([
{ "$group": {
"_id": { "$ifNull": [ "$metadata.parent", "$_id" ] },
"metadata": {
"$addToSet": {
"$cond": [
{ "$ifNull": [ "$metadata.parent", false ] },
false,
"$metadata"
]
}
},
"children": {
"$push": {
"$cond": [
{ "$ifNull": [ "$metadata.parent", false ] },
{ "_id": "$_id","metadata": "$metadata" },
false
]
}
}
}},
{ "$unwind": "$metadata" },
{ "$match": { "metadata": { "$ne": false } } },
{ "$unwind": "$children" },
{ "$match": { "children": { "$ne": false } } },
{ "$sort": { "_id": 1, "children._id": 1 } },
{ "$group": {
"_id": "$_id",
"metadata": { "$first": "$metadata" },
"children": { "$push": "$children" }
}}
])
本质上是一样的,但没有 MongoDB 2.6 中引入的新运算符,因此这也适用于早期版本。
只要你们的关系是单层的 parent 和 child ,这一切都很好。对于嵌套级别,您需要调用 mapReduce 进程。
关于mongodb - 使用 MongoDB 投影来嵌套整个文档?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/25017543/