首页 > 解决方案 > MongoDB每天为每个用户显示第一个和最后一个事件

问题描述

每次用户登录或注销时,都会在 mongo 中保存一个事件。用户可以每天多次登录和/或注销。

例如,Bob 登录 2 次,注销 1 次:

{
  username: ‘bob’,
  type: ‘login’,
  eventDate: ISODate(‘2018-09-21T12:39:50.676Z’)
}

{
  username: ‘bob’,
  type: ‘login’,
  eventDate: ISODate(‘2018-09-21T13:55:50.676Z’)
}

{
  username: ‘bob’,
  type: ‘logout,
  eventDate: ISODate(‘2018-09-21T22:10:50.676Z’)
}

詹姆斯只有 1 个登录事件:

{
  username: ‘james’,
  type: ‘login,
  eventDate: ISODate(‘2018-09-21T10:10:50.676Z’)
}

我想执行一个查询,该查询将检索每个用户每天的第一次登录和当天的最后一次注销(假设在过去一周)。

所以结果会是这样的:

[{
  username: ‘bob’,
  firstLogin: ISODate(‘2018-09-21T12:39:50.676Z’),
  lastLogout: ISODate(‘2018-09-21T22:10:50.676Z’)
}

{
  username: ‘james’,
  firstLogin: ISODate(‘2018-09-22T10:19:50.676Z’),
  lastLogout: null,
}]

我相信我必须处理“聚合”,但不确定。

标签: mongodbmongodb-query

解决方案


通过使用两个级别$group(MongoDB V3.2.18)

我相信userName是独一无二的。

  1. $sort 首先是事件日期
  2. $group用户名类型
  3. $project区分firstLoginlastLogin
  4. $group再次由用户名获得最终结果。

db.getCollection('test').aggregate([
  {$sort: {'eventDate' : 1}},
  {
    "$group" : {
        _id: {"username" : "$username", "type": "$type"},
        eventDate: {$push: "$eventDate"}
    }
  },
  {
    $project : {
      _id:1,
      eventDate:1,
      firstLogin: {
        $cond: [ { "$eq": ["$_id.type", "login" ]}, { $arrayElemAt: [ "$eventDate", 0 ] }, null]
      },
      lastLogout: {
        $cond: [ { "$eq": ["$_id.type", "logout" ]}, { $arrayElemAt: [ "$eventDate", -1 ] }, null]
      }
    }
  },
  {
    "$group" : {
        _id: "$_id.username",
        firstLogin: {$first: "$firstLogin"},
        lastLogout: {$last: "$lastLogout"}
    }
  }
]);

输出:

/* 1 */
{
    "_id" : "james",
    "firstLogin" : ISODate("2018-09-21T10:10:50.676Z"),
    "lastLogout" : null
}

/* 2 */
{
    "_id" : "bob",
    "firstLogin" : ISODate("2018-09-21T12:39:50.676Z"),
    "lastLogout" : ISODate("2018-09-21T22:10:50.676Z")
}

推荐阅读