按小时聚合时返回日期对象

3
我将尝试通过缩短日期时间到小时的方式来聚合记录。在mongoengine上有没有一种方法可以做到这一点并保存字段类型?
我的数据如下:
{'spent': 7, 'time_started': datetime.datetime(2015, 4, 21, 16, 2, 16, 661000)}
{'spent': 3, 'time_started': datetime.datetime(2015, 4, 21, 17, 8, 5, 415000)}
{'spent': 3, 'time_started': datetime.datetime(2015, 4, 21, 15, 52, 45, 917000)}
{'spent': 1, 'time_started': datetime.datetime(2015, 4, 21, 16, 42, 32, 313000)}
{'spent': 8, 'time_started': datetime.datetime(2015, 4, 21, 16, 35, 46, 863000)}
{'spent': 5, 'time_started': datetime.datetime(2015, 4, 21, 15, 55, 1, 217000)}
{'spent': 10, 'time_started': datetime.datetime(2015, 4, 20, 17, 41, 50, 5000)}

这是我目前的翻译:

pipeline =[
    'match': {
        "time_started": {
            "$gte": datetime.datetime(2015, 4, 21, 0, 0, 0),
            }
        },
    'project': {
        "spent": "$spent",
         "time_started": {"$dateToString": {
                "format": "%Y-%m-%dT%H:00:00",
                "date": "$time_started"
            }}
        },
    'group': {
        "_id": {
            "time_started": "$time_started",
            },
        "spent_total": {"$sum": "$spent"}
        }
    ]

它的功能很好,但是结果中的"time_started"是一个字符串,而我需要datetime(日期时间),就像这样:

{'spent_total': 16, 'time_started': datetime.datetime(2015, 4, 21, 16, 0, 0)}
{'spent_total': 3, 'time_started': datetime.datetime(2015, 4, 21, 17, 0, 0)}
{'spent_total': 8, 'time_started': datetime.datetime(2015, 4, 21, 15, 0, 0)}
1个回答

2

可以使用“日期计算”来操作对象,这样它们将被保留为BSON日期类型,并在驱动程序中转换为本机类型:

pipeline = [
    { '$match': {
        "time_started": {
            "$gte": datetime.datetime(2015, 4, 21, 0, 0, 0),
            }
        }
     }},
     { "$group": {
        "_id": {
             "$add": [
                 { "$subtract": [
                     { "$subtract": [
                         "$time_started", datetime.datetime(1970, 1, 1) 
                     ]},
                     { "$mod": [
                         { "$subtract": [
                             "$time_started", datetime.datetime(1970, 1, 1) 
                         ]},
                         1000 * 60 * 60
                     ]}
                 ]},
                 datetime.datetime(1970, 1, 1)
             ]
        },
        "spent_total": { "$sum": "$spent" }
     }}
 ];

Class._get_collection().aggregate(pipeline);

基本概念是,从日期字段的值中减去“时期日期”,则返回的值为数字。在这里,您将应用模数 $mod 以计算出一小时内毫秒的余数,并将日期向下舍入到小时。
然后,当你“添加”“时期日期”到该数字时,它会返回一个新的 Date 对象,其表示值相等于毫秒数。
由于它只是一个日期,所以驱动程序会相应地处理它,无需进行翻译。比使用字符串或其他运算符要好得多。还要注意,您不需要 $project ,而直接将此类转换应用于 $group 中的 _id ,甚至可以加快速度。

谢谢,这个可行!远非显而易见,但真的很有创意!)) - funkifunki
@funkifunki,我们称之为未记录的“特性”。但这就是聚合管道中日期对象与“数学”运算的行为方式。现在你知道了。 - Blakes Seven

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接