【问题标题】:Return Date Object when Aggregating by Hour按小时聚合时返回日期对象
【发布时间】:2015-09-10 16:08:35
【问题描述】:

我正在尝试通过将日期时间减少到几小时来聚合记录。有没有办法在 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”是结果中的一个字符串,而我需要日期时间,像这样:

{'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)}

【问题讨论】:

    标签: python mongodb mongodb-query aggregation-framework mongoengine


    【解决方案1】:

    是的。您可以对对象使用“日期数学”,然后它们将保留为 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 我们称其为未记录的“功能”。但这就是日期对象的“数学”在聚合管道中的行为方式。现在你知道了。
    猜你喜欢
    • 1970-01-01
    • 2016-01-13
    • 1970-01-01
    • 1970-01-01
    • 2014-04-12
    • 2021-07-21
    • 1970-01-01
    • 2012-03-25
    • 1970-01-01
    相关资源
    最近更新 更多