在子文档的子文档展开后重新组合

时间:2017-02-08 18:34:17

标签: mongodb mongodb-query aggregation-framework

这是我的文件。

{ 
    "_id" : ObjectId("589b6132fafb5a09549b46cb"), 
    "name" : "foo", 
    "users" : [
        {
            "_id" : ObjectId("589b6132fafb5a09549b46cc"), 
            "name" : "Peter", 
            "emails" : [
                {
                    "address" : "peter@email.com"
                }, 
                {
                    "address" : "test2@email.com"
                }
            ]
        }, 
        {
            "_id" : ObjectId("589b6132fafb5a09549b46cd"), 
            "name" : "Joe", 
            "emails" : []
        }
    ]
}

我正在展开用户和users.email 当我尝试重新组合时,我得到一个名为Peter的用户的副本,因为它有2封电子邮件。

查询:

db.test.aggregate([
  { "$unwind": {
    "path": "$users",
    "preserveNullAndEmptyArrays": true
  } },
  { "$unwind": {
    "path": "$users.emails",
    "preserveNullAndEmptyArrays": true
  } },
  {
    "$group": {
      "_id": "$_id",
      "name": { "$first": "$name" },
      "users": { "$addToSet": "$users"},
      "allEmails": { "$push": "$users.emails.address" }
    }
  }
])

结果:

{ 
    "_id" : ObjectId("589b6132fafb5a09549b46cb"), 
    "name" : "foo", 
    "users" : [
        {
            "_id" : ObjectId("589b6132fafb5a09549b46cd"), 
            "name" : "Joe"
        }, 
        {
            "_id" : ObjectId("589b6132fafb5a09549b46cc"), 
            "name" : "Peter", 
            "emails" : {
                "address" : "test2@email.com"
            }
        }, 
        {
            "_id" : ObjectId("589b6132fafb5a09549b46cc"), 
            "name" : "Peter", 
            "emails" : {
                "address" : "peter@email.com"
            }
        }
    ], 
    "allEmails" : [
        "peter@email.com", 
        "test2@email.com"
    ]
}

在使用文档父级上的allEmails展开之前,我需要用户对象完全相同,如下例所示。

{ 
    "_id" : ObjectId("589b6132fafb5a09549b46cb"), 
    "name" : "foo", 
    "users" : [
        {
            "_id" : ObjectId("589b6132fafb5a09549b46cc"), 
            "name" : "Peter", 
            "emails" : [
                { "address" : "test2@email.com" },
                { "address" : "peter@email.com" }
            ]
        },
        {
            "_id" : ObjectId("589b6132fafb5a09549b46cd"), 
            "name" : "Joe", 
            "emails" : []
        }
    ], 
    "allEmails" : [
        "peter@email.com", 
        "test2@email.com"
    ]
}

1 个答案:

答案 0 :(得分:1)

运行以下聚合管道应该可以得到所需的结果:

db.test.aggregate([   
    {
        "$addFields": {
            "allEmails": { 
                "$reduce": {
                    "input": {
                        "$map": {
                            "input": "$users",
                            "as": "user",
                            "in": "$$user.emails"
                        }
                    },
                    "initialValue": [],
                    "in": { "$concatArrays": ["$$value", "$$this.address"] }
                }
            }
        }
    }     
])

上述管道最初使用 $map 创建一个二维电子邮件数组来处理对象。显示通过应用表达式

生成的示例结果
{
    "$map": {
        "input": "$users",
        "as": "user",
        "in": "$$user.emails"
    }
}

运行一个测试管道,只包含一个包含结果的字段:

db.test.aggregate([   
    {
        "$project": {
            "twoDarray": { 
                    "$map": {
                        "input": "$users",
                        "as": "user",
                        "in": "$$user.emails"
                    }
                }
            }
        }
    }     
])

将生成2D数组

{
    "_id" : ObjectId("589b6132fafb5a09549b46cb"),
    "twoDarray" : [ 
        [ 
            { "address" : "peter@email.com" }, 
            { "address" : "test2@email.com" }
        ], 
        []
    ]
}

现在,对这个二维数组进行非规范化

[ 
    [ 
        { "address" : "peter@email.com" }, 
        { "address" : "test2@email.com" }
    ], 
    []
]

使用 $reduce 运算符,该运算符将表达式应用于数组中的每个元素,并将它们组合为单个值。在 $concatArrays 运算符的帮助下,您可以连接 $reduce 表达式中的每个元素,以形成最终所需的数组

[
    "peter@email.com", 
    "test2@email.com"
]