MongoDB按非完整时钟小时进行聚合
首先,我必须说我在mongoDB方面没有丰富的经验,可能由于这个原因,我无法解决以下问题。 问题是,我需要聚合记录,不是按完整的时钟小时数,而是按第一条记录到达时开始的小时数,以及距离前一个事件一小时的时间 例如: 时间 柜台 还没有记录 0 11:55 1. 11:58 1. 12:02 1. 12:05 1. 12:55 1. 12:56 2. 13:04 2.MongoDB按非完整时钟小时进行聚合,mongodb,aggregation-framework,Mongodb,Aggregation Framework,首先,我必须说我在mongoDB方面没有丰富的经验,可能由于这个原因,我无法解决以下问题。 问题是,我需要聚合记录,不是按完整的时钟小时数,而是按第一条记录到达时开始的小时数,以及距离前一个事件一小时的时间 例如: 时间 柜台 还没有记录 0 11:55 1. 11:58 1. 12:02 1. 12:05 1. 12:55 1. 12:56 2. 13:04 2. 因为这在MongoDB中不是一项简单的任务,所以最好在业务逻辑中完成。您想要实现的结果基本上是一种范围内的聚合形式。问题是,您的范
因为这在MongoDB中不是一项简单的任务,所以最好在业务逻辑中完成。您想要实现的结果基本上是一种范围内的聚合形式。问题是,您的范围不是预定义的集合,而是动态的,并基于第一个条目+附加时间值进行计算。所以,基本上你有:
[firstTime->firstTime+60m]
,[firstTime+60m+1s->firstTime+60m+60m]
。。。无限远。这是可行的,但需要对分组阶段或投影计算使用粗糙的动态表达式。对于您的用例来说,这两种方法似乎都不必要复杂
如果你真的想走这条路,你可以查看一些相关的场景
如果决定在业务逻辑中执行此操作,则根据数据集的大小,可以批处理数据,使其不超过可用内存,但只有在首次使用大小合适的未处理数据集计算聚合时,这才应该是一个问题。此外,在任何情况下,都应该缓存计算结果,因为它们不会改变(当然,上次打开的日期范围除外)。考虑到你这样做是为了统计,而且你没有性能限制,你绝对应该帮自己一个忙,用更易于维护的方式在代码中完成它。你的示例数据仍然是无用的!移除所有不相关的东西。为满足所有要求,请提供2份以上的文件 无论如何,我会尽力的 将日期/时间值存储为字符串(或数字)是一种非常糟糕的设计。始终使用适当的
Date
对象
db.collection.aggregate([
// convert string into proper `Date` object
{ $set: { ts: { $dateFromString: { dateString: "event.dateTime", format: "%Y%m%d%H:%M:%S" } } } }
{ $sort: { ts: 1 } },
// put documents into an array
{ $group: { _id: { date: "$event.date", organisationId: "$organisationId" }, data: { $push: "$$ROOT" } } },
{
$set: {
data: {
$reduce: {
input: "$data",
initialValue: [],
in: {
$concatArrays: [
"$$value",
[
{
$cond: {
// compare timestamp with boundary of previous element
if: { $gt: ["$$this.ts", { $last: "$$value.boundary" }] },
// new interval: increase bucket number and set new boudnary
then: {
$mergeObjects: [
"$$this", {
bucket: { $add: [{ $ifNull: [{ $last: "$$value.bucket" }, 0] }, 1] },
boundary: { $add: ["$$this.ts", 1000 * 60 * 60] }
}
]
},
// same interval: append element
else: { $mergeObjects: ["$$this", { boundary: { $last: "$$value.boundary" }, bucket: { $last: "$$value.bucket" } }] }
}
}
]
]
}
}
}
}
},
{ $unwind: "$data" }, // transpose array back to documents
// count the documents by bucket number
{
$group: {
_id: { date: "$data.date", organisationId: "$data.organisationId", bucket: "$data.bucket" },
ts_min: { $min: "$data.ts" },
ts_max: { $max: "$data.ts" },
count: { $sum: 1 }
}
},
// some cosmetic
{ $replaceRoot: { newRoot: { $mergeObjects: ["$$ROOT", "$_id"] } } },
{ $unset: "_id" }
])
请提供一些样本数据。到目前为止,您尝试了什么?这是可能的,但它需要大量的投影计算和阶段,如果您不限制性能,那么复杂性确实不值得。考虑到电视机不会超过你的容量,你最好在内存中完成。如果是这样,您只需对记录进行批量分割。在任何情况下,缓存计算结果。此外,你还依赖于第一条记录的记录时间,对吗?@zhulien yep我依赖于第一条记录,我只需要统计数据所需的信息,所以性能应该不会有问题,因此,例如,如果程序将收集一周的统计数据,这是可以的。Thanx zhulien感谢您的努力。@exStas我实际上已经开始为您提供一个示例解决方案,但小组的范围是(11:55-12:55,12:56-13:56)应该针对每个单独的日期进行计算,也应该跨日期混合,这样会使mongo代码变得非常复杂,所以为了您自己的理智,我会坚持使用off db实施策略。@zhulien如果您愿意,您可以发布答案并给出建议,我将接受。感谢您的努力!我刚刚验证了查询并确认它工作正常!我不完全理解的是为什么我们需要$concatarray在这里运作?它到底在这里干什么?提前谢谢你!我需要构建一个数组,在这里我可以比较当前时间戳和以前的时间戳。如果没有
$concatarray
,数据
字段将只具有最后一个值(对于13:04),没有其他值。如果我们在$group
操作中将文档推入此字段,为什么数据
字段将为空?