MongoDB - 时间序列子文档的范围查询

Posted

技术标签:

【中文标题】MongoDB - 时间序列子文档的范围查询【英文标题】:MongoDB - range queries on time series subdocuments 【发布时间】:2017-12-18 01:01:05 【问题描述】:

我对 Mongo 很陌生,我只是在了解核心概念...我正在为时间序列数据实现一个模式,并计划尝试这里建议的模式:MongoDB as a Time Series Database,也出现在一些 Mongo 演示文稿中。

我了解架构,但很难弄清楚如何查询它以获取一系列日期。更具体地说,有人可以举例说明如何在上面的链接中查询模式以检索跨越多个小时/天的 1 分钟系列吗?理想情况下,不需要在 Mongo 之外进行后期处理。

Mongo 文档和聚合管道似乎主要关注处理数组而不是嵌套子文档...TIA。

编辑:为了让我试图解决的具体问题更加清晰......

假设我以 1 分钟的间隔存储数据,每天使用一个父文档,使用以下架构(摘自上面链接的帖子):


  timestamp_hour: ISODate("2013-10-10T23:00:00.000Z"),
  type: “spot_EURUSD”,
  values: 
    0:  0: 1.2343, 1: 1.2343, …, 59: 1.2343,
    1:  0: 1.2343, 1: 1.2343, …, 59: 1.2343,
    …,
    22:  0: 1.2343, 1: 1.2343, …, 59: 1.2343,
    23:  0: 1.2343, 1: 1.2343, …, 59: 1.2343
  

什么是满足表单查询的最有效/最高效的方法:“给我一个按时间顺序排列的值列表,每分钟 1 个,从 2013 年 9 月 25 日下午 1:37 开始,到 2013 年 10 月结束-15 下午 2:56”?

【问题讨论】:

【参考方案1】:

@jtromans 对您提供的链接发表了评论,该评论应为您指明正确的方向:

...您应该继续将您的数据“分箱”到满足标准所需的最精细分辨率

所以假设您有如下架构:


    timestamp_hour: ISODate(...),
    values: 
        0: ,
        1: ,
        ...
        59: 
    

然后您每分钟有一个子文档,这将使您可以相当简单地满足您的查询(在第 4 分钟找到每个时间,例如:

collection.find(, "values.3": 1)

这只是一个仅过滤您感兴趣的分钟值的投影。由于它是全表扫描,您可能希望在 timestamp_hour 字段中包含一个日期范围以限制搜索。如果您想投影值以更好地适应您期望的格式,您可以使用聚合,例如:

collection.aggregate([
    $project: val: "$values.1"
])

如果您需要能够按小时、秒或其他时间进行过滤,那么您将需要用于架构中的 bin 或键,例如分秒和分:


    timestamp_hour: ISODate(...),
    minutes: 
        0: 
            seconds: 
                0: ...
            
        ,
        ...
    

例如添加其他值作为键,以便它们可以被索引和过滤:


    timestamp_hour: ISODate(...),
    hour_of_day: 0,
    day_of_month: 1
    minutes: 
        ...
    

请注意,我在这里使用了每小时文档的方法,您必须根据您的数据和要求来决定是否适合您,或者您是否需要每分钟、每天等文档。

编辑:这是一个更符合已编辑问题的示例:

db.ts.aggregate([
    
        $match: 
            timestamp_hour: $lte: ISODate("2013-09-25"), $gte: ISODate("2013-10-15")
        
    ,
    
        $project: 
            hours: $objectToArray: "$values"
        
    ,
    
        $unwind: "$hours"
    ,
    
        $project: 
            hour_index: "$hours.k",
            minutes: $objectToArray: "$hours.v"
        
    ,
    
        $unwind: "$minutes"
    ,
    
        $project: 
            reconstructed_date: $dateFromParts: 
                year: $year: "$timestamp_hour",
                month: $month: "$timestamp_hour",
                day: $day: "$timestamp_hour",
                hour: "$hour_index",
                minute: "$minutes.k",
            
            value: "$minutes.v"
        
    ,
    
        $match: 
            reconstructed_date: $lte: ISODate("2013-09-25T13:37:00.000Z"), $gte: ISODate("2013-10-15T14:56:00.000Z")
        
    
])

我没有试图在这个时区中设置正确的时区,这取决于你!

【讨论】:

感谢您的回答,但我认为我正在寻找的内容可能比您所描述的更简单。我只想做一个普通的日期范围查询,而不是过滤特定的时间段。为了清楚起见,我编辑了我的问题。有什么想法吗? 啊,我明白了。您需要在$project 阶段使用$objectToArray,这样您就可以在稍后阶段使用$unwind(展开仅适用于数组;出于这个特定原因,您可能希望从对象切换到数组,因为您的聚合会变得混乱);要获取所有分钟,而不是特定的分钟,然后在那一分钟内选择某些内容(第一项,如果值是数组或子文档而不是值),您需要执行此操作 两次. 时间范围查询也会变得复杂 - 您需要在 timestamp_hour 字段上设置一个初始 $match 阶段(将文档设置为合理的大小,您需要小心你的界限),你需要一个$project 阶段来创建具有小时/分钟/秒值的新字段,这样你就有一个最终的$match 阶段来过滤第一天/最后一天不需要的值。总体而言,变得相当复杂,我想对生产规模的数据集进行测试,以确保查询性能正常。 查看更新的答案以获取示例 - 此时它远非优雅! 这真的很棒,洛根。您的示例具有很强的指导性,不仅用于解决这个特定问题,而且还用于对如何组装复杂的管道有一个扎实的认识。此外,现在我可以清楚地看到示例模式的写入优化程度,并且我倾向于同意数组可能更适合我的用例。谢谢!!

以上是关于MongoDB - 时间序列子文档的范围查询的主要内容,如果未能解决你的问题,请参考以下文章

mongodb基础学习5

MongoDB - 查询难题 - 文档参考或子文档

MongoDB - 查询难题 - 文档参考或子文档

MongoDB 查询子文档数组

Yii,MongoDB查询子文档以插入Mysql表

基于多个子文档的MongoDB/Mongoose查询