mongodb - 使用 MongoDB 投影来嵌套整个文档?

标签 mongodb mongodb-query aggregation-framework

我有一个文档的平面集合,其中一些文档有一个 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/

相关文章:

mongodb - 如何在AWS上运行mongodb

node.js - Sails 中模型的独特属性

python - 展平mongodb字符串数组的根级别?

mongodb - Mongoose 中的自动递增ID

node.js - 如何使用 MongoDb 在 NodeJS 中获取收件箱的 session 用户及其最后消息

node.js - 在node.js-mongodb中分组

node.js - 如何使用mongoose.connect检查mongodb连接期间数据库是否存在?

json - 如何处理文档中json字段不一致的情况

java - 使用 Java 在 MongoDB 中进行查询优化

mongodb - 如何按数组的第一个元素分组?