列出一个集合中的文档并计算它们在另一个集合中的出现次数

List documents from a collection and count their occurrence in another collection

我是 MongoDB 聚合的新手,我正在摸索如何用 Mongo:

做类似的事情

示例SQL查询(如果这是一个关系数据库):

select id, name, (select max(createdAt) from events where user_id=u.id) 
from users u

或者..

select users.id, users.name, max(events.created_at) 
from users inner join events on users.id=events.user_id
group by users.id, users.name

最后的结果是一样的。我想列出所有用户,以及他们最近活动的最大日期。

如何在 Mongo 上完成此操作?

假设我有一个具有相同字段的用户和事件集合。

我想我应该从 $lookup 开始,它会把事件和用户文档放在一起。

[{
    $match: {
        accountId: '629a251af534a3600aa1a150'
    }
}, {
    $lookup: {
        from: 'productevents',
        localField: 'id',
        foreignField: 'userId',
        as: 'userEvents'
    }
}, {}]

只是不确定下一步要做什么才能计算剩余事件集合中的 get max(created_at)。

示例数据:

用户:

{
  "id": "1",
  "accountId": "629a251af534a3600aa1a150",
  "name": "Some User",
  "createdAt": {
    "$date": {
      "$numberLong": "1654269244479"
    }
  },
  "properties": {
    "age": "39"
  },
  "__v": 0
}

产品事件:

{
  "name": "login",
  "accountId": "629a251af534a3600aa1a150",
  "userId": "1",
  "groupId": "1",
  "properties": {
    "client": "mobile"
  },
  "createdAt": {
    "$date": {
      "$numberLong": "1654269289432"
    }
  },
  "__v": 0
}

找到了一种方法(尽管不确定它是否是最佳方法)。

[
  {
    '$lookup': {
      'from': 'productevents', 
      'localField': 'id', 
      'foreignField': 'userId', 
      'as': 'events'
    }
  }, {
    '$unwind': {
      'path': '$events', 
      'preserveNullAndEmptyArrays': true
    }
  }, {
    '$group': {
      '_id': {
        'id': '$id', 
        'name': '$name', 
        '_id': '$_id', 
        'createdAt': '$createdAt', 
        'properties': '$properties'
      }, 
      'lastActivity': {
        '$max': '$events.createdAt'
      }
    }
  }, {
    '$project': {
      '_id': '$_id._id', 
      'id': '$_id.id', 
      'createdAt': '$_id.createdAt', 
      'properties': '$_id.properties', 
      'lastActivity': 1
    }
  }
]

您可以使用 $lookup pipeline 进行优化,方法是仅从 productevents 集合中获取您需要的内容:

db.users.aggregate([
  {
    $lookup: {
      from: "productevents",
      let: {id: "$id"},
      pipeline: [
        {$match: {$expr: {$eq: ["$userId", "$$id"]}}}
        {$sort: {createdAt: -1}},
        {$limit: 1},
        {$project: {createdAt: 1, _id: 0}}
      ],
      as: "lastActivity"
    }
  },
  {
    $set: {lastActivity: {$arrayElemAt: ["$lastActivity", 0]}}
  },
  {
    $project: {
      id: 1,
      createdAt: 1,
      properties: 1,
      lastActivity: "$lastActivity.createdAt"
    }
  }
])

Playground example

编辑: 如果您需要计算每个用户的事件数,您也可以在 $lookup 管道内进行:

db.users.aggregate([
  {
    $lookup: {
      from: "productevents",
      let: {id: "$id"},
      pipeline: [
        {$match: {$expr: {$eq: ["$userId", "$$id"]}}}
        {$group: {_id: 0, count: {$sum: 1}, createdAt: {$max: "$createdAt"}}}
      ],
      as: "lastActivity"
    }
  },
  {
    $set: {lastActivity: {$arrayElemAt: ["$lastActivity", 0]}}
  },
  {
    $project: {
      id: 1,
      createdAt: 1,
      properties: 1,
      lastActivity: "$lastActivity.createdAt",
      activityCount: "$lastActivity.count"
    }
  }
])

Playground example