使用聚合管道在 MongoDB 中聚合 collection 个时间戳

Aggregate a collection of timestamps in MongoDB using the Aggregation Pipeline

我有一个 collection 的时间戳,它记录了用户在哪个时间执行了哪些操作。目前,collection 仅包含两个动作 startend。只能有一个 end 操作,而每个用户可以有多个 start 操作。

现在我想要生成一个用户列表,其中最后一个 start 操作和 end 操作之间的时间差是 - 例如 - 不到一分钟。

我的 collection timestamps 中的简化文档如下所示:

document #1

{
  id: 123,
  user: "user1",
  type: "start",
  date: 2019-09-10
}

document #2

{
  id: 234,
  user: "user1",
  type: "end",
   date: 2019-09-11
}

现在我想要的结果应该是这样的:

{
  id: null,
  list: ["user1, user2"]
}

字段 list 应包含每个用户,其中 startend 操作之间的时间差小于一分钟。

我在合并包含 startend 属性的文档时遇到问题。我试图将它们组合成如下所示的文档:

{
  id: 345
  user: "user1"
  date_start: 2019-09-10
  date_end: 2019-09-11
}

我不知道从哪里开始聚合管道以及如何拆分和组合不同类型的时间戳。此外,我还需要添加一个包含两个日期之间差异的字段。

以下查询可以获得预期的输出:

db.collection.aggregate([
    {
        $sort:{
            "date":-1
        }
    },
    {
        $group:{
            "_id":{
                "id":"$id",
                "type":"$type"
            },
            "id":{
                $first:"$id"
            },
            "user":{
                $first:"$user"
            },
            "type":{
                $first:"$type"
            },
            "date":{
                $first:"$date"
            }
        }
    },
    {
        $group:{
            "_id":"$id",
            "user":{
                $first:"$user"
            },
            "info":{
                $push:{
                    "k":"$type",
                    "v":"$date"
                }
            }
        }
    },
    {
        $addFields:{
            "info":{
                $arrayToObject:"$info"
            }
        }
    },
    {
        $match:{
            $expr:{
                $lt:[
                    {
                        $subtract:[
                            {
                                $toDate:"$info.end"
                            },
                            {
                                $toDate:"$info.start"
                            }
                        ]
                    },
                    60000
                ]
            }
        }
    },
    {
        $group:{
            "_id":null,
            "users":{
                $push:"$user"
            }
        }
    },
    {
        $project:{
            "_id":0
        }
    }
]).pretty()

数据集:

{
    "_id" : ObjectId("5d77a117bd4e75c58d598214"),
    "id" : 123,
    "user" : "user1",
    "type" : "start",
    "date" : "2019-09-10T13:01:14.242Z"
}
{
    "_id" : ObjectId("5d77a117bd4e75c58d598215"),
    "id" : 123,
    "user" : "user1",
    "type" : "start",
    "date" : "2019-09-10T13:04:14.242Z"
}
{
    "_id" : ObjectId("5d77a117bd4e75c58d598216"),
    "id" : 123,
    "user" : "user1",
    "type" : "start",
    "date" : "2019-09-10T13:09:02.242Z"
}
{
    "_id" : ObjectId("5d77a117bd4e75c58d598217"),
    "id" : 123,
    "user" : "user1",
    "type" : "end",
    "date" : "2019-09-10T13:09:14.242Z"
}
{
    "_id" : ObjectId("5d77a117bd4e75c58d598218"),
    "id" : 234,
    "user" : "user2",
    "type" : "start",
    "date" : "2019-09-10T13:02:02.242Z"
}
{
    "_id" : ObjectId("5d77a117bd4e75c58d598219"),
    "id" : 234,
    "user" : "user2",
    "type" : "end",
    "date" : "2019-09-10T13:09:14.242Z"
}
{
    "_id" : ObjectId("5d77a117bd4e75c58d59821a"),
    "id" : 345,
    "user" : "user3",
    "type" : "start",
    "date" : "2019-09-10T13:08:55.242Z"
}
{
    "_id" : ObjectId("5d77a117bd4e75c58d59821b"),
    "id" : 345,
    "user" : "user3",
    "type" : "end",
    "date" : "2019-09-10T13:09:14.242Z"
}

输出:

{ "users" : [ "user3", "user1" ] }

查询分析:

  • 第一阶段:按日期降序排列文档
  • 第二阶段:[id, type] 分组并选择第一个日期 每种类型,即每种类型的最晚日期
  • 第三阶段: 仅在 id 上分组并将类型和关联日期作为键值对推送到数组中
  • 第四阶段:将键值对数组转换为对象
  • 阶段 V: 过滤结束日期和开始日期之间的差异小于 60000 毫秒的文档。 (毫秒等于 1 分钟)
  • 阶段 VI: 将所有筛选的名称推入数组