【问题标题】:MongoDB: aggregate and group by splitting the idMongoDB:通过拆分id进行聚合和分组
【发布时间】:2019-02-16 03:55:58
【问题描述】:

我的架构实现受到本教程on official mongo site的影响

{  
  _id: String,
  data:[
        {
         point_1: Number,
         ts: Date
        }
  ]
}

这基本上是为时间序列数据设计的模式,我将每个设备每小时的数据存储在单个文档的数组中。我创建了_id 字段组合设备ID,它正在发送数据和时间。例如,如果 ID 为xyz1234 的设备在2018-09-11 12:30:00 发送数据,那么我的_id 字段将变为xyz1234:2018091112

如果该设备的那个小时的文档不存在,我将创建新文档,否则我只是将我的数据推送到 data 数组。

client.db('iot')
.collection('iotdata')
.update({_id:id},{$push:{data:{point_1,ts:date}}},{upsert:true});

现在我在进行聚合时遇到问题。我正在尝试获取这些类型的值

  • 过去 24 小时内许多设备的最小 point_1 值(按设备 ID 分组)
  • 过去 24 小时内许多设备的最大 point_1 值(按设备 ID 分组)
  • 过去 24 小时内许多设备的平均 point_1 按设备 ID 分组

我认为这是非常简单的聚合,然后我意识到设备 ID 不是直接的,而是与时间数据混合在一起,因此按设备 ID 对数据进行分组并不是那么直接。如何根据设备 ID 拆分 _id 和组?我已尽力将问题写得尽可能清楚,如果问题的任何部分不清楚,请在 cmets 中提问。

【问题讨论】:

    标签: mongodb aggregation-framework


    【解决方案1】:

    您可以从数据上的$unwind 开始,以获取每个条目的单个文档。然后您可以使用$substr$indexOfBytes 运算符获得deviceId。然后你可以应用你的过滤条件(最后24小时)并使用$group得到minmaxavg

    db.col.aggregate([
        {
            $unwind: "$data"
        },
        {
            $project: {
                point_1: "$data.point_1",
                deviceId: { $substr: [ "$_id", 0, { $indexOfBytes: [ "$_id", ":" ] } ] },
                dateTime: "$data.ts"
            }
        },
        {
            $match: {
                dateTime: { $gte: ISODate("2018-09-10T12:00:00Z") }
            }
        },
        {
            $group: {
                _id: "$deviceId",
                min: { $min: "$point_1" },
                max: { $max: "$point_1" },
                avg: { $avg: "$point_1" }
            }
        }
    ])
    

    【讨论】:

    • 太酷了。这部分是{ $substr: [ "$_id", 0, { $indexOfBytes: [ "$_id", ":" ] } ] } 对我来说很新。非常感谢。
    • 目前要查找哪个设备的最低 point_1 我在上述查询的结果中使用了简单的 javascript 代码,但是有没有办法对上述查询添加更改,以便它也返回最低全部?
    • 最简单的方法就是像 { $sort: { "min": 1} } 那样对它们进行排序,否则你必须处理 $facet 这可能会使这个管道复杂化
    • sort 为我工作。谢谢你。我不能使用facet,因为我使用的是 3.2
    【解决方案2】:

    您可以在 3.6 中使用以下查询。

    db.colname.aggregate([
      {"$project":{
        "deviceandtime":{"$split":["$_id", ":"]},
        "minpoint":{"$min":"$data.point_1"},
        "maxpoint":{"$min":"$data.point_1"},
        "sumpoint":{"$sum":"$data.point_1"},
        "count":{"$size":"$data.point_1"}
      }},
      {"$match":{"$expr":{"$gte":[{"$arrayElemAt":["$deviceandtime",1]},"2018-09-10 00:00:00"]}}},
      {"$group":{
        "_id":{"$arrayElemAt":["$deviceandtime",0]},
        "minpoint":{"$min":"$minpoint"},
        "maxpoint":{"$max":"$maxpoint"},
        "sumpoint":{"$sum":"$sumpoint"},
        "countpoint":{"$sum":"$count"}
      }},
      {"$project":{
        "minpoint":1,
        "maxpoint":1,
        "avgpoint":{"$divide":["$sumpoint","$countpoint"]}
      }}
    ])
    

    【讨论】:

    • 我想这是停止获取任何值{"$match":{"$expr":{"$gte":[{"$arrayElemAt":["$deviceandtime",1]},"2018-09-10 00:00:00"]}}},我的 id 是这种格式xyz1234:2018091112
    • 你可以试试{"$match":{"$expr":{"$gte":[{"$arrayElemAt":["$deviceandtime",1]},"2018091000"]}}}
    • 很抱歉我没有提到我的数据库是3.2,我猜match 部分不起作用。但是您的查询看起来很有趣,因为没有展开并且您非常巧妙地使用了我的 _id。我尝试了您和 mickl 的查询,没有时间限制(没有匹配表达式),您的查询花了 2.1 秒,而 mickl 花了 3.8 秒。这是因为放松吗?
    • 不用担心。是的。您能否将设备 ID 和日期(作为日期类型)存储在两个单独的字段中?一种是存储数据的更好方法,另一种我可以使用 3.2 聚合管道,您可以有效地使用 $match 并添加索引。
    • 观看链接教程后,我正在使用这样的_id。如果我为设备 ID 和仅小时时间戳创建单独的字段,那么我必须在它们上创建一个索引,并且在“物联网环境数据存储”中创建索引的成本非常高。我应该非常聪明地使用 _id 这个字段,但我无法解决这个问题。
    猜你喜欢
    • 2021-11-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-04-19
    • 1970-01-01
    • 2023-01-20
    • 2020-11-16
    • 2015-10-20
    相关资源
    最近更新 更多