V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
推荐工具
RoboMongo
推荐书目
50 Tips and Tricks for MongoDB Developers
Related Blogs
Snail in a Turtleneck
threedream
V2EX  ›  MongoDB

mongoDB 在 aggregate 中使用$dateToString 格式化字符串时间戳出现好坑的问题,求助各位哥哥姐姐

  •  
  •   threedream · 2017-12-08 15:56:35 +08:00 · 10713 次点击
    这是一个创建于 2321 天前的主题,其中的信息可能已经有所发展或是发生改变。

    最近使用 mongoDB 存储了大量的用户登录记录,文档结构如下:

    {
      "_id" : ObjectId("5a1fde257756dca1c86f2d23"),
      "_class" : "com.aaa.LoginHistory",
      "uid" : "94989242",
      "ip" : "192.168.1.219",
      "imei" : "865736037366641",
      "epid" : "33189023-9472-4f30-81cf-8c7d13132aae5",
      "platform" : "Android",
      "timestamp" : "1512037117100",
      "address" : "中国,江苏省,南京市,建邺区"
    }
    
    • 现在有个需求,需要统计每一天( yyyy-MM-dd )的登录用户数( uid 对应用户,每天存在多次登陆记录,即 uid 可能重复)。
    • 由于技术菜,选择了比较 low 的方法实现:根据需要查询的时间间隔的时间,就是开始时间戳和结束时间戳,把它分成每天一个间隔,然后利用以下方法去统计:
    db.loginHistory.aggregate([
    	{$match:{"timestamp":{"$lte":"1513412162000","$gte":"1512548162000"}}},
        {$group:{_id:"$uid"}}
    ])
    

    因此出现了很大的问题:

    • uid 的重复,导致登录用户数量统计出现重复。
    • 如果需要查询一年以内的话,则需分成 365 个时间区不断得循环调用,导致响应时间超级慢。

    最后经过各种百度找到了一种实现:

    db.loginHistory.aggregate(
        [
            {
                $match:{
                    timestamp:{
                        $lte:"1513399696240",
                        $gte:"1513389696240"
                    }
                }
            },
            {
                $project:{
                    uid:1,
                    timestamp:1,
                    _id:0
                }
            },
            {
                $group:{
                    _id:{
                        uid:"$uid",
                        date:{
                            $dateToString:{
                                format:"%Y-%m-%d",
                                date:{
                                    $add:[new Date(0),parseInt("$timestamp")]
                                }
                            }
                        }
                    }
                }
            },
            {
                $group:{
                    _id:{
                        date:"$_id.date"
                    },
                    count:{"$sum":1}
                }
            }
        ]
    )
    

    但是问题又来了,执行报错:

    assert: command failed: {
        "ok" : 0,
        "errmsg" : "$dateToString is only defined on year 0-9999, tried to use year 292278994",
        "code" : 18537
    } : aggregate failed
    _getErrorWithCode@src/mongo/shell/utils.js:25:13
    doassert@src/mongo/shell/assert.js:13:14
    assert.commandWorked@src/mongo/shell/assert.js:287:5
    DBCollection.prototype.aggregate@src/mongo/shell/collection.js:1312:5
    

    百思不得其解,尝试性的把 parseInt("$timestamp")改成 parseInt("1513399696240"),执行得到结果:

    { "_id" : { "date" : "2017-12-16" }, "count" : 19 }
    

    。。。。。。。。。。。。。求助各位大哥

    1 条回复    2017-12-08 16:03:54 +08:00
    rails3
        1
    rails3  
       2017-12-08 16:03:54 +08:00
    map reduce 可以解决
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   我们的愿景   ·   实用小工具   ·   3628 人在线   最高记录 6543   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 27ms · UTC 11:02 · PVG 19:02 · LAX 04:02 · JFK 07:02
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.