5

如何按 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}

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

谢谢!

4

3 回答 3

7

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

没有办法用 mongodb 的聚合框架来完成你所要求的,因为没有聚合运算符可以将 ObjectId 变成类似日期的东西(不过有一个JIRA 票证)。您应该能够使用 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};
}
于 2013-09-18T21:39:56.427 回答
3

llovett指出的Jira Ticket已经解决,所以现在您可以使用日期运算符,如和从.$isoWeek$yearObjectId

您的聚合将如下所示:

{
    "$project":
        {

            "_id": {
                "$dateFromParts" : {
                    "year": { "$year": "$_id"},
                    "month": { "$month": "$_id"},
                    "day": { "$dayOfMonth": "$_id"}
                }
            }
        }
}
于 2018-08-09T12:10:10.980 回答
2

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

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

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

将代码转换为漂亮、惯用的 Python

于 2013-09-10T00:47:06.287 回答