【问题标题】:Join multiple collections after parallel aggregation in Mongodb在MongoDB中并行聚合后加入多个集合
【发布时间】:2020-09-17 15:04:27
【问题描述】:

我有一个名为“Reel”的集合,其中嵌入了对象。

{
    "_id":"reel_1",
    "category":[
        {
            "_id" : "cat_1",
            "videos": [ {"_id":"vid_1"},{"_id":"vid_2"} ] //_id is reference of Video collection
        },
        {
            "_id" : "cat_2",
            "videos": [ {"_id":"vid_3"},{"_id":"vid_4"} ]
        }
    ]
}

Video 是另一个集合,其 _idreel-> 类别 -> 视频 -> _id

中被引用
{
    "_id":"vid_1",
    "title":"title 1",
    "groups":[{"_id":"group_1"},{"_id":"group_2"}]
},
{
    "_id":"vid_2",
    "title":"title 2",
    "groups":[{"_id":"group_1"},{"_id":"group_4"}]
},
{
    "_id":"vid_3",
    "title":"title 3",
    "groups":[{"_id":"group_1"},{"_id":"group_2"}]
},
{
    "_id":"vid_4",
    "title":"title 4",
    "groups":[{"_id":"group_3"},{"_id":"group_4"}]
}

Document 集合,其中包含 Reel_idCategory

_id
{
    "_id":"doc_1",
    "title":"document title",
    "assessments":[
        {
            "reel":"reel_1",   // reel reference _id
            "category":"cat_1", // category reference _id
            "groups":[{"_id":"group_1"},{"_id":"group_2"}
            ]
        }
    ]
    
}

我需要加入并找到所有具有group_1 的相关嵌入对象。 我已经完成了 Reel 集合和 Video 集合之间的连接并且工作正常,

{ $unwind: { path: '$category', preserveNullAndEmptyArrays: true }}, 
{ $unwind: { path: '$category.videos', preserveNullAndEmptyArrays: true }}, 
{
    $lookup: {
        from: 'video',
        localField: 'category.videos._id',
        foreignField: '_id',
        as: 'joinVideo'
    }
}, 
{ $unwind: { path: "$joinVideo", preserveNullAndEmptyArrays: true }}, 
{ $unwind: { path: "$joinVideo.groups", preserveNullAndEmptyArrays: true }}, 
{ $match: { "joinVideo.groups._id": "group_1" }}, 
{ $addFields: { "category.videos": "$joinVideo" }}, 
{
    $group: {
        _id: {
            _id: "$_id",
            category: "$category._id"
        },
        videos: {
            $addToSet: "$category.videos"
        }
    }
}, {
    $group: {
        _id: "$_id._id",
        category: {
            $addToSet: {
                "_id": "$_id.category",
                "videos": "$videos"
            }
        }
    }
}

文档集合应嵌入 reel _idcategory _id 过滤的 category 对象中 group_1。我的预期结果是

{
    "_id":"reel_1",
    "category":[
        {
            "_id" : "cat_1",
            "videos": [
                {
                    "_id":"vid_1",
                    "title":"title 1",
                    "groups":[ {"_id":"group_1"},{"_id":"group_2"}]
                },  
                {
                    "_id":"vid_2",
                    "title":"title 2",
                    "groups":[{"_id":"group_1"},{"_id":"group_4"}]
                }   
            ],
            "documents":[
                { // this document comes by reel="reel_1", category="cat_1", filtered by "group_1"
                    "_id":"doc_1",
                    "title":"document title",
                }
            ]
        },
        {
            "_id" : "cat_2",
            "videos": [
                {
                    "_id":"vid_3",
                    "title":"title 3",
                    "groups":[{"_id":"group_1"},{"_id":"group_2"}]
                }               
            ]
        }
    ]
}

我尝试了很多方法。由于我是 Mongodb 的新手,所以我无法解决这个问题。

【问题讨论】:

    标签: mongodb mongodb-query aggregation-framework


    【解决方案1】:

    由于 MongoDB v3.6$lookup 允许执行 uncorrelated sub-queries。这允许我们执行非标准查询来连接两个或更多集合。

    注意:Explanation 为什么我们需要在$lookup 管道中使用$expr


    说明

    1. 我们申请$unwind 来扁平化$category

    2. 我们在两个条件下执行$lookup

      video.groups._id == 'group_1' and video._id in reel.category.videos._id

    由于$reel.category.videos._id返回一个数组,我们需要使用$in操作符

    1. 我们再次使用 2 个条件执行 $lookup。它为每个文档创建documents 字段
    2. 要动态删除字段,我们需要使用名为 $$REMOVEAggregation expressions,它允许我们有条件地从文档中排除字段
    3. 我们执行$group 阶段以转换为所需的结果

    db.reel.aggregate([
      {
        $unwind: {
          path: "$category",
          preserveNullAndEmptyArrays: true
        }
      },
      {
        $lookup: {
          from: "video",
          let: {
            videos: "$category.videos._id"
          },
          pipeline: [
            {
              $match: {
                "groups._id": "group_1",
                $expr: {
                  $in: [
                    "$_id",
                    "$$videos"
                  ]
                }
              }
            }
          ],
          as: "category.videos"
        }
      },
      {
        $lookup: {
          from: "document",
          let: {
            reel_id: "$_id",
            category_id: "$category._id"
          },
          pipeline: [
            {
              $match: {
                $expr: {
                  $and: [
                    {
                      $in: [
                        "$$reel_id",
                        "$assessments.reel"
                      ]
                    },
                    {
                      $in: [
                        "$$category_id",
                        "$assessments.category"
                      ]
                    }
                  ]
                }
              }
            },
            {
              $project: {
                _id: 1,
                title: 1
              }
            }
          ],
          as: "category.documents"
        }
      },
      {
        $addFields: {
          "category.documents": {
            $cond: [
              {
                $eq: [
                  {
                    $size: "$category.documents"
                  },
                  0
                ]
              },
              "$$REMOVE",
              "$category.documents"
            ]
          }
        }
      },
      {
        $group: {
          _id: "$_id",
          category: {
            $push: "$category"
          }
        }
      }
    ])
    

    MongoPlayground

    【讨论】:

    • 非常感谢,很棒的解释。我只需要澄清一下,为什么我们不在视频和卷轴之间进行正常查找?两个集合都有关系。
    • @varman 我们需要应用 2 个条件:groups._id == "group_1" AND _id == category.videos._id。标准$lookup只能应用1个条件,所以我们必须使用uncorrelated sub-queries。在这种情况下我们也可以执行标准的$lookup,但是我们需要应用很多额外的阶段来删除不需要的数据......
    • ndeed.. 感谢您的明确解释。我为第一次查找写了一个很长的查询。
    • @varman 您可以使用$ifNull$type 来处理可能出现的错误
    • 我有个小问题,这里 category 是一个数组,videos 是一个嵌套的嵌套数组。在您的回答中,您只是 $unwind 类别,您没有展开 **videos**(它仍然是一个数组)。 stackoverflow.com/questions/63226464/… 这是我回答的问题之一。但我需要展开数组.. 为什么会这样?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-06-26
    • 2018-08-31
    • 2018-03-06
    • 2021-11-04
    • 2020-02-16
    相关资源
    最近更新 更多