我有一个文件的平面集合,其中一些文件有一个 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 (如果这更容易)

为什么不将文档保存在嵌套结构中? 我不想将数据以嵌套格式存储在DB中,因为这些文档是GridFS的一部分。

主要问题是: 我怎样才能告诉MongoDB嵌套整个文档?或者我必须使用Mongo的聚合框架来完成该任务?

有帮助吗?

解决方案

对于你所要求的那种"投影",那么聚合框架是正确的工具,因为这种"文档重新塑造"只在那里得到真正的支持。

另一种情况是"父/子"的事情,在使用聚合框架进行分组时,您再次需要"创造性"。完整的操作显示了本质上涉及的内容:

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 运算符稍后,其中评估是由哪些数据添加到数组或"设置"。在以下 $projectfalse 值通过使用过滤掉 $setDifference 接线员。

如果决赛 $sort$group 似乎令人困惑,那么实际的原因是因为使用的运算符是"set"运算符所得到的"set"被认为是未排序的。所以实际上,这部分只是为了确保数组内容按自己的顺序出现 _id 场。

如果没有来自MongoDB2.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" }
    }}
])

本质上是同样的事情,但没有在MongoDB2.6中引入的较新的运算符,所以这也适用于早期版本。

只要你的关系是父母和孩子的单一层次,这一切都会很好。对于嵌套级别,您需要调用mapReduce进程。

其他提示

我想要一个类似于Neil Lunn的答案的结果,除了我想获取所有父母,无论他们是否有孩子。我还想将其概括为适用于任何具有单个嵌套子级的集合。

这是我根据Neil Lunn的答案提出的查询

db.collection.aggregate([
  {
    $group: {
      _id: {
        $ifNull: ["$parent", "$_id"]
      },
      parent: {
        $addToSet: {
          $cond: [
            {
              $ifNull: ["$parent", false]
            }, false, "$$ROOT"
          ]
        }
      },
      children: {
        $push: {
          $cond: [
            {
              $ifNull: ["$parent", false]
            }, "$$ROOT", false
          ]
        }
      }
    }
  }, {
    $project: {
      parent: {
        $setDifference: ["$parent", [false]]
      },
      children: {
        $setDifference: ["$children", [false]]
      }
    }
  }, {
    $unwind: "$parent"
  }
])

这将导致返回每个父级,其中父级字段包含整个父级文档,如果父级没有子级或子级文档数组,则返回子级字段。

{
  _id: PARENT_ID
  parent: PARENT_OBJECT
  children: [CHILD_OBJECTS]
}
许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top