首页 > 解决方案 > 基于通过 $lookup 检索的字段的多阶段聚合管道匹配数据

问题描述

我正在尝试在 MongoDB(4.4.9 社区版,使用pymongoPython 3.10 的驱动程序)中构建一个复杂的嵌套聚合管道。

我想将不同集合中的相关数据点汇总到一个新的(理想情况下)视图(或者,如果这不起作用)集合中。

集合和其中的相关字段遵循层次结构。有members,其中包含要在其上合并其他数据的顶级键, membershipNumber

> members.find_one()
{'_id': ObjectId('61153299af6122XXXXXXXXXXXXX'), 'membershipNumber': 'N03XXXXXX'}

然后,有一个不同的集合,其中包含membershipNumber,但也有一个不同的链接字段,an_user_idan_user_id在其他集合中用于表示与该特定用户相关的数组中的记录/字段。

我“加入”members并且an_users喜欢这样:

result = members.aggregate([
    {
        '$lookup': {
            'from': 'an_users',                   
            'localField': 'membershipNumber',     
            'foreignField': 'memref',             
            'as': 'an_users'                      
        }
    },
    {   '$unwind' : '$an_users' },     
    {   
        '$project' : {
            '_id' : 1,
            'membershipNumber' : 1,
            'an_user_id' : '$an_users.user_id'
        } 
    }
]);

到目前为止一切顺利,这将返回所需的聚合记录:

{'_id': ObjectId('61153253aBBBBBBBBBBBB'),
  'membershipNumber': 'N0XXXXXXXX',
  'an_user_id': '48XXXXXX'}

现在,我有第三个集合,它包含an_user_id数组中的字符串,表示用户单击给定电子邮件的位置,其中记录是电子邮件(数组中的an_user_idsclicks是单击该电子邮件中链接的用户。

{'_id': ObjectId('blah'),
 'email_id': '407XXX',
 'actions_count': 17,
 'administrative_title': 'test',
 'bounce': ['3440XXXX'],
 'click': ['38294CCC',
  '418FFFF',
  '48XXXXXX',
  '38eGGGG'}

我想计算集合中数组(例如,,)中给定an_user_id(我从聚合中获得)的出现次数,并将其包含在调用中,以检索如下内容:clicksbouncesopensemails.aggregate

{'_id': ObjectId('61153253aBBBBBBBBBBBB'),
  'membershipNumber': 'N0XXXXXXXX',
  'an_user_id': '48XXXXXX',
  'n_email_clicks' : 412,
  'n_email_bounces' : 12
}

此外,我可能还想an_user_id在我的数据库中附加其他集合中的计数。

考虑,例如,这个集合称为events

{
    "_id": "617ffa96ee11844e143a63dd",
    "id": "12345",
    "administrative_title": "my_event",
    "created_at": {
        "$date": "2020-01-15T16:28:50.000Z"
    },
    "event_creator_id": "123456",
    "event_title": "my_event",
    "group_id": "123456",
    "permalink": "event_id",
    "rsvp_count": 54,
    "rsvps": [{
        "rsvp_id": "56789",
        "display_name": "John Doe",
        "rsvp_user_id": "48XXXXXX",
        "rsvp_created_at": {
            "$date": "2020-01-28T15:38:50.000Z"
        },
        "rsvp_updated_at": {
            "$date": "2020-01-28T15:38:50.000Z"
        },
        "first_name": "John",
        "last_name": "Doe",
    }, {
        "rsvp_id": "543895",
        "display_name": "James Appleslice",
        "rsvp_user_id": "N03XXXXXX",
        "rsvp_created_at": {
            "$date": "2020-02-05T13:15:14.000Z"
        },
        "rsvp_updated_at": {
            "$date": "2020-02-05T13:15:14.000Z"
        },
        "first_name": "James",
        "last_name": "Appleslice"}
  ]
}

所以,最终产品看起来像这样:

{'_id': ObjectId('61153253aBBBBBBBBBBBB'),
  'membershipNumber': 'N0XXXXXXXX',
  'an_user_id': '48XXXXXX',
  'n_email_clicks' : 412,
  'n_email_bounces' : 12,
  'n_rsvps' : 12
}

我的想法是使用$lookup参数 - 但是,我只知道如何使用它来匹配我正在执行聚合的父集合中的字段,而不是在过程中生成的字段聚合。

任何帮助将不胜感激!

标签: mongodbaggregation-frameworkpymongo

解决方案


你可以使用$lookup管道。首先,您将$lookup用户 ID 后跟另一个$lookup以验证用户 ID 是否存在于电子邮件中。最后还有几个阶段可以根据您的需要收集结果和格式。此外,$out如果您想将结果写入另一个集合,您可以添加阶段。

db.members.aggregate([{
  $lookup: {
    from: "an_users",
    let: {
      membershipNumber: "$membershipNumber"
    },
    pipeline: [
      {
        $match: {
          $expr: {
            $eq: [
              "$memref",
              "$$membershipNumber"
            ]
          },
          
        }
      },
      {
        "$lookup": {
          "from": "emails",
          "localField": "user_id",
          "foreignField": "click",
          "as": "clicks"
        }
      },
      {
        "$project": {
          "_id": 1,
          "membershipNumber": 1,
          "an_user_id": "$user_id",
          "n_email_clicks": {
            $size: "$clicks"
          }
        }
      }
    ],
    as: "details"
  }
},
{
  $replaceRoot: {
    newRoot: {
      $mergeObjects: [
        {
          $arrayElemAt: [
            "$details",
            0
          ]
        },
        "$$ROOT"
      ]
    }
  }
},
{
  $project: {
    details: 0
  }
}])

工作示例 - https://mongoplayground.net/p/yrFsNp44hpi


推荐阅读