【问题标题】:Aggregate MongoDB results by ObjectId date按 ObjectId 日期聚合 MongoDB 结果
【发布时间】:2013-09-12 13:20:22
【问题描述】:

如何按 ObjectId 日期聚合我的 MongoDB 结果。示例:

默认光标结果:

cursor = [
    {'_id': ObjectId('5220b974a61ad0000746c0d0'),'content': 'Foo'},
    {'_id': ObjectId('521f541d4ce02a000752763a'),'content': 'Bar'},
    {'_id': ObjectId('521ef350d24a9b00077090a5'),'content': 'Baz'},
]

预计结果:

projected_cursor = [
    {'2013-09-08':
        {'_id': ObjectId('5220b974a61ad0000746c0d0'),'content': 'Foo'},
        {'_id': ObjectId('521f541d4ce02a000752763a'),'content': 'Bar'}
    },
    {'2013-09-07':
        {'_id': ObjectId('521ef350d24a9b00077090a5'),'content': 'Baz'}
    }
]

这是我目前在 PyMongo 中使用来实现这些结果的方法,但它很混乱,我想看看如何使用 MongoDB 的聚合框架(甚至 MapReduce)来做到这一点:

cursor = db.find({}, limit=10).sort("_id", pymongo.DESCENDING)
messages = [x for x in cursor]
this_date = lambda x: x['_id'].generation_time.date()
dates = set([this_date(message) for message in messages])
dates_dict = {date: [m for m in messages if this_date(m) == date] for date in dates}

是的,我知道最简单的方法是简单地为每条记录添加一个新的日期字段,然后按此进行聚合,但这不是我现在想要做的。

谢谢!

【问题讨论】:

    标签: mongodb pymongo aggregation-framework


    【解决方案1】:

    所以这并不能直接回答我的问题,但我确实找到了一种更好的方法来使用 Python 的 setdefault 替换上面所有的 lambda 废话:

    d = {}
    for message in messages:
        key = message['_id'].generation_time.date()
        d.setdefault(key,[]).append(message)
    

    感谢@raymondh 在 PyCon 谈话中的提示:

    Transforming Code into Beautiful, Idiomatic Python

    【讨论】:

      【解决方案2】:

      更新:现在有一种内置方法可以执行此操作,请参阅https://stackoverflow.com/a/51766657/295687

      没有办法用 mongodb 完成你的要求 聚合框架,因为没有聚合运算符 可以将 ObjectId 变成类似日期的东西(不过有一个JIRA ticket)。你 应该能够使用 map-reduce 完成您想要的,但是:

      // map function
      function domap() {
          // turn ObjectId --> ISODate
          var date = this._id.getTimestamp();
          // format the date however you want
          var year = date.getFullYear();
          var month = date.getMonth();
          var day = date.getDate();
      
          // yields date string as key, entire document as value
          emit(year+"-"+month+"-"+day, this);
      }
      
      // reduce function
      function doreduce(datestring, docs) {
          return {"date":datestring, "docs":docs};
      }
      

      【讨论】:

      • 谢谢。我将不得不测试这个与在 Python 中执行它的性能,就像在我的示例中一样。我永远不知道我是否应该将这样的东西推送到数据库。
      • 另外,感谢您的票证,我也将这个帖子发布到那里。
      • 这个答案是错误的。聚合框架中有一个简单的本地方法可以将 ObjectID 转换为日期。看到这个答案:stackoverflow.com/a/51766657/295687
      【解决方案3】:

      llovett 指出的Jira Ticket 已解决,所以现在您可以像$isoWeek$year 一样使用date operatorsObjectId 中提取此信息。

      您的聚合将如下所示:

      {
          "$project":
              {
      
                  "_id": {
                      "$dateFromParts" : {
                          "year": { "$year": "$_id"},
                          "month": { "$month": "$_id"},
                          "day": { "$dayOfMonth": "$_id"}
                      }
                  }
              }
      }
      

      【讨论】:

      • 这就是答案!并将 ObjectID 转换为完整的时间戳,您可以使用:{ $project: _id: { $toDate: "$_id" } }
      猜你喜欢
      • 1970-01-01
      • 2018-04-25
      • 2013-11-25
      • 2019-05-17
      • 1970-01-01
      • 2021-09-28
      • 1970-01-01
      • 1970-01-01
      • 2015-05-25
      相关资源
      最近更新 更多