$ lookup在嵌套数组中加入

时间:2018-06-15 05:33:35

标签: mongodb mongodb-query aggregation-framework

我有两个集ClientsForms

Clients架构有以下记录

{
"_id" : ObjectId("5b0bd79adcbf901ee404d8c0"),
"Name" : "Danielle",
"Email" : "Janet@test.com",
"Projects" : [{
        "_id" : ObjectId("5b1e6f3410ef671cf82404be"),
        "Name" : "test",
        "Description" : "ttet",
        "Forms" : [
            ObjectId("5b03ff291c70c513bc9dbfa8"),
            ObjectId("5b16238f30491d1c643f7f28"),
            ObjectId("5afc23f3382646009c5210ab"),
        ],
        "IsActive" : true
    }, {
        "_id" : ObjectId("5b03ffc11c70c513bc9dbfb1"),
        "Name" : "apadei ief",
        "Description" : "ttasdadet",
        "Forms" : [
            ObjectId("5b03ff291c70c513bc9dbfa8"),
            ObjectId("5b16238f30491d1c643f7f28")
        ],
        "IsActive" : true
    }, {
        // array of projects
    }
],
"IsDeleted" : false,

}

Forms架构有以下记录

{
    "_id" : ObjectId("5b03ff291c70c513bc9dbfa8"),
    "Name" : "Employee Information",
    "Description" : "",
    "IsActive" : true
},
{
    "_id" : ObjectId("5b16238f30491d1c643f7f28"),
    "Name" : "test form",
    "Description" : "",
    "IsActive" : true
},
{
    "_id" : ObjectId("5afc23f3382646009c5210ab"),
    "Name" : "Android test",
    "Description" : "",
    "IsActive" : true
},
{
    "_id" : ObjectId("5a6304ffc3c3f119fc0e60c8"),
    "Name" : "feedback form",
    "Description" : "",
    "IsActive" : true
}

我希望输出如下所示

    {
    "_id" : ObjectId("5b0bd79adcbf901ee404d8c0"),
    "Name" : "Danielle",
    "Email" : "Janet@test.com",
    "Projects" : [{
            "_id" : ObjectId("5b1e6f3410ef671cf82404be"),
            "Name" : "test",
            "Description" : "ttet",
            "Forms" : [{
                    "_id" : ObjectId("5b03ff291c70c513bc9dbfa8"),
                    "Name" : "Employee Information",
                    "Description" : "",
                    "IsActive" : true
                }, {
                    "_id" : ObjectId("5b16238f30491d1c643f7f28"),
                    "Name" : "test form",
                    "Description" : "",
                    "IsActive" : true
                }, {
                    "_id" : ObjectId("5afc23f3382646009c5210ab"),
                    "Name" : "Android test",
                    "Description" : "",
                    "IsActive" : true
                }
            ],
            "IsActive" : true
        }, {
            "_id" : ObjectId("5b03ffc11c70c513bc9dbfb1"),
            "Name" : "apadei ief",
            "Description" : "ttasdadet",
            "Forms" : [{
                    "_id" : ObjectId("5b03ff291c70c513bc9dbfa8"),
                    "Name" : "Employee Information",
                    "Description" : "",
                    "IsActive" : true
                }, {
                    "_id" : ObjectId("5b16238f30491d1c643f7f28"),
                    "Name" : "test form",
                    "Description" : "",
                    "IsActive" : true
                }
            ],
            "IsActive" : true
        }, {
            // array of projects
        }
    ],
    "IsDeleted" : false
}

根据输出,我希望表单应该来自Forms个集合。

为此,我正在进行如下聚合,

db.Clients.aggregate([{
            $match : {
                _id : ObjectId("5a8528ed0290f7eca89e9a5f"),
                IsDeleted : false
            }
        }, {
            $addFields : {
                "Forms" : {
                    $map : {
                        input : {
                            $map : {
                                input : "$Projects",
                                in : {
                                    $arrayElemAt : [{
                                            $objectToArray : "$$this"
                                        }, 1]
                                },
                            }
                        },
                        in : "$$this.v"
                    }
                }
            }
        }, {
            $lookup : {
                from : "Forms",
                localField : "Projects.Forms",
                foreignField : "_id",
                as : "Forms"
            }
        }, {
            $addFields : {
                "Forms" : {
                    $arrayElemAt : ["$Forms", 0]
                }
            }
        }
    ])

但是它给了我错误的输出,它只从一个项目中返回一个Form。我希望每个Forms中的每个Projects

你的答案对我来说很好,但如果我想在$ map中为

制作过滤器怎么办?
$and: [{
   $eq: ["$Projects.IsActive", true]
}, {
   $eq: ["$Projects.IsDeleted", false]
}]

1 个答案:

答案 0 :(得分:1)

由于您尝试使用的某些功能似乎可以使用MongoDB 3.6,因此您可以使用真正重要的功能。即$lookup的“子管道”形式,允许声明表达式匹配:

db.Clients.aggregate([
  { "$match" : { "_id": ObjectId("5a8528ed0290f7eca89e9a5f"), "IsDeleted": false } },
  { "$lookup": {
    "from": "Forms",
    "let": {
      "join": {
        "$reduce": {
          "input": "$Projects.Forms",
          "initialValue": [],
          "in": { "$concatArrays": [ "$$value", "$$this" ] }
        }
      }
    },
    "as": "join",
    "pipeline": [
      { "$match": { 
        "$expr": { "$in": [ "$_id", "$$join" ] }
      }}
    ]
  }},
  { "$addFields": {
    "Projects": {
      "$map": {
        "input": "$Projects",
        "in": {
          "$mergeObjects": [
            "$$this",
            { 
              "Forms": {
                "$map": {
                  "input": "$$this.Forms",
                  "in": {
                    "$arrayElemAt": [
                      "$join",
                      { "$indexOfArray": [ "$join._id", "$$this" ] }
                    ]
                  }
                }
              }
            }
          ]
        }
      }
    },
    "join": "$$REMOVE"
  }}
])

在使用$lookup$reduce后,您基本上执行$concatArrays,以便将嵌套数组详细信息“展平”为匹配"Forms" {{1}的单个列表值。这些可以与国外集合进行比较,以返回正确的相关项目。

如果外部集合中没有“缺失”项并且所有引用的项都匹配,那么实际上只需要使用$map处理数组并交换{{}中的“已连接”内容。 1}}表示现有的ObjectId值。再次,因为您正在使用MongoDB 3.6,您可以使用$mergeObjects以使$map操作更灵活,而无需明确命名所有属性。

此处的匹配是使用$indexOfArray查找“匹配”和$arrayElemAt以便提取该值并在$map期间将其换出。

即使没有MongoDB 3.6,你仍然可以做同样的事情:

"Forms"

稍长一点,因为您需要在$lookup之前添加“flattened”数组内容,然后还没有其他功能来完全启用$addFields重新映射数组,以便我们请改用$project

两种方式都返回相同的东西:

ObjectId