mongodb最佳实践 mongodb实时计算
本文详细介绍了如何利用MongoDB的聚合管道(Aggregation) Pipeline)功能,计算时间高效的序列数据中特定字段(如属性值)在不同时间(如按小时)之间的增量。通过结合$sort、$group、$setWindowFields、$match和$project等阶段,教程展示了如何针对不同分组(如设备代码)独立计算每个经常时间段内的首个记录值,并求取相邻时间段内的差值,从而实现精确的数据分析。引言
在处理时间序列数据时,我们需要分析不同时间点或时间段内的某个指标的变化趋势。例如,计算操作系统的索引增量,或者设备在特定时间段内的读数变化。对于存储在MongoDB中的此类数据,利用其强大的聚合管道功能,可以高效地在服务器端完成复杂的计算,避免将大量数据传输到客户端进行处理
本教程将一个具体的例子:给定包含时间戳、代码(设备场景代码)和能量(能量读取)的集合,我们需要计算每个设备在托盘开始时的能量读取与前一小时开始时的能量读取之间的差值。
数据结构示例
假设我们的集合名为readings,其文档结构如下:[ { quot;_idquot;: 1, quot;timestampquot;: quot;2023-05-15T10:00:00Zquot;, quot;codequot;: quot;abcquot;, quot;energyquot;: 2333 }, { quot;_idquot;: 2, quot;timestampquot;: quot;2023-05-15T10:10:00Zquot;, quot;codequot;: quot;abcquot;, quot;energyquot;: 2340 }, // ...其他在10:00到11:00之间的quot;abcquot;设备数据{ quot;_idquot;: 6, quot;timestampquot;: quot;2023-05-15T11:00:00Zquot;, quot;codequot;: quot;abcquot;, quot;energyquot;: 2370 }, // ... quot;defquot;设备数据 { quot;_idquot;: 7, quot;timestampquot;: quot;2023-05-15T10:00:00Zquot;, ”code”;:“def”;,“energy”;:3455 },{“_id”;:12,“timestamp”;:“2023-05-15T11:00:00Z”;,“code”;:“def”;,“energy”;:3500 }]登录后复制
我们的目标是计算出类似以下的结果:[ { quot;timestampquot;: quot;2023-05-15T11:00:00Zquot;, quot;codequot;: quot;abcquot;, quot;energyquot;: 37 }, // 2370 (11:00) - 2333 (10:00) { quot;timestampquot;: ”2023-05-15T11:00:00Z”;,“代码”;:“def”;,“能量”;:45 } // 3500 (11:00) - 3455 (10:00)]登录后复制
这里的“energy”值表示的是当前小时开始时的能量值与上一小时开始时的能量值之间的差。使用聚合管道计算增量
为了实现上述目标,我们将构建一个多级的聚合管道。
db.collection.aggregate([ // 1.排序数据 { $sort: { timestamp: 1 } }, // 2.按设备代码和小时分组,获取完成的第一个能量解读 { $group: { _id: { hour: { $dateTrunc: { date: quot;$timestampquot;,单位: quot;hourquot; } }, code: quot;$codequot; }, energy: { $first: ”将当前和前一个能量值推入仓库窗口: { 文档: [-1, 0] } // 窗口包含前一个文档和当前文档 } } } }, // 4.过滤掉没有前一个小时数据的文档 { $match: { quot;prevEnergy.1quot;: { $exists: true } } }, // 5. 投影结果最终并计算差值 { $project: { _id: 0, // 排除默认的_id时间戳: quot;$_id.hourquot;, //将分组的时间作为新的时间戳字段 code: quot;$_id.codequot;, // 将分组的设备代码作为新的代码字段 energy: { $subtract: [{ $last: quot;$prevEnergyquot; }, { $first: quot;$prevEnergyquot; }] } // 计算差值 } }])) 登录后复制管道阶段详细解
$sort: { timestamp: 1 } 目的:确定数据按时间升序排列。这是后续 $group阶段正确获取$first值的关键,也为$setWindowFields阶段的窗口操作提供了社区的基础。
$group: { _id: { hour: { $dateTrunc: { date: "$timestamp",单位: "hour" } }, code: "$code" }, energy: { $first: "$energy" } } 目的:这一步是核心。将数据按每个文档的代码字段和时间戳字段截断到小时进行分组。
$dateTrunc:这个符操作用于将日期截断到指定的单位(这里是“hour”),例如,2023-05-15T10:10:00Z和2023-05-15T10:30:00Z都会被截断为2023-05-15T10:00:00Z。$first: "$energy":在每个包内(即每个设备代码的每个小时内),$first操作符会返回该包中按 $sort 顺序排列第一个文档的能量值。这确保我们始终获取到完成的第一个能量读数。
$setWindowFields: { partitionBy: "$_id.code", sortBy: { "_id.hour": 1 }, output: { prevEnergy: { $push: "$energy", window: { 文件: [-1, 0] } } } } 目的:这是计算队列文档之间差异的关键阶段,它允许计算在窗口内执行聚合操作。partitionBy: "$_id.code":这个选项告诉MongoDB为每个不同的_id.code值创建一个独立的“分区”。这意味着后续的窗口将只在同一个code的数据行之间进行,确保我们比较和同一个设备的连续小时数据。sortBy: { "_id.hour": 1 }:在每个分区内部,数据会按照_id.hour(即小时计时)包含升序排列。这保证了窗口函数能够正确地识别“前一个小时”的数据。output: { prevEnergy: { $push: "$energy", window: { 文件: [-1, 0] } } }:prevEnergy:定义了一个新的输出字段,这是窗口的计算结果。$push: "$energy":将当前窗口内所有文档的能量值收集到一个数组中。window: { 文件: [-1, 0] }:定义了滑动窗口的范围。[-1, 0]表示窗口包含当前文档(0)和其紧邻的前一个文档(-1)。因此,prevEnergy 队列将包含两个元素:[前一小时的能量值,对于第一个小时的数据,prevEnergy 队列只包含一个元素(当前小时的能量值),因为没有前一个文档。
$match: { "prevEnergy.1": { $exists: true } }:过滤掉那些没有前一个小时数据的文档。由于第一个小时的数据没有前一个小时的数据,其 prevEnergy 队列只包含一个元素(索引为 0)。"prevEnergy.1": { $exists: true }条件保证我们只保留那些prevEnergy分布式中包含第二个元素(即前一小时能量值)的文档。
$project: { _id: 0, timestamp: "$_id.hour", code: "$_id.code", energy: { $subtract: [{ $last: "$prevEnergy" }, { $first: "$prevEnergy" }] } } 目的:清理输出结果,并执行最终的减法计算。_id: 0:排除故障的_id字段。
timestamp: "$_id.hour"和code: "$_id.code":将在$group阶段创建的复合_id中的小时和代码提取出来,作为独立的字段。energy: { $subtract: [{ $last: "$prevEnergy" }, { $first: "$prevEnergy" }] }:这是最终的计算。$last: "$prevEnergy"获取阵列中的第二个元素(当前小时的能量值),$first: "$prevEnergy"获取阵列存储中的第一个元素(前一的能量值),然后使用$subtract计算它们的差值。注意事项时间数据类型:确保时间戳式字段转换转换在MongoDB中为BSON日期类型。如果它们是字符串,你需要在使用$dateTrunc通过$toDate操作符进行类型。在上面的示例中,ISO 8601之前的字符串通常可以被MongoDB的日期操作符隐式处理,但显着可以避免潜在问题。数据丢失: 如果某个小时或某个设备特定在小时内没有数据,那么该小时的增量将不会出现在结果中。这$groupstage只是为实际数据的组合文档生成。性能优化:因为对于大型数据集,为timestamp和code字段创建复合索引(如{ timestamp: 1, code: 1 }或{ code: 1, timestamp: 1 })将显着提高查询聚合的性能,尤其是在$sort和$groupstage。窗口大小: $setWindowFields的窗口选项非常灵活,可以根据需求定义不同的窗口范围,例如计算滑动快捷、累计等。总结
MongoDB的聚合管道提供了一套强大且灵活的工具,用于处理和分析时间序列数据。通过巧妙地组合$sort、$group和$setWindowF这种服务器端的数据处理方式,极大地减少了数据传输量,提升了数据分析的效率和性能。
以上就是MongoDB时间序列数据:计算字段值增量的详细信息,更多请关注乐哥常识网其他相关文章!