使用聚合管道聚合 MongoDB 中的时间戳集合

Posted

技术标签:

【中文标题】使用聚合管道聚合 MongoDB 中的时间戳集合【英文标题】:Aggregate a collection of timestamps in MongoDB using the Aggregation Pipeline 【发布时间】:2020-01-12 05:23:55 【问题描述】:

我有一组时间戳记录用户在什么时间执行了哪些操作。目前,该集合仅包含两个操作 startend。每个用户只能有一个 end 操作,而每个用户可以有多个 start 操作。

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

我的集合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

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

【问题讨论】:

【参考方案1】:

以下查询可以得到我们预期的输出:

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 进行分组,并将类型和相关日期作为键值对推送到数组中 第四阶段:将键值对数组转换为对象 第五阶段:过滤结束日期与开始日期之差小于 60000 毫秒的文档。 (毫秒相当于 1 分钟) 第六阶段:将所有过滤后的名称推送到一个数组中

【讨论】:

非常感谢您的询问和解释!由于某种原因,比赛阶段在 MongoDB Compass 中不起作用。我将其替换为 redact-Stage 以删除具有相同条件的文档。

以上是关于使用聚合管道聚合 MongoDB 中的时间戳集合的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 MongoDB 聚合管道从集合中的两个子文档中返回最小值?

mongoDB表与表的关系及聚合管道查询

MongoDB——聚合管道之$group操作

MongoDB——聚合管道之$group操作

mongodb聚合命令

MongoDB 聚合管道(Aggregation Pipeline)