如何使用 mongodb 驱动程序以时间间隔聚合文档
How to aggregate documents with mongodb driver for nodejs with time interval
我目前卡在了这个问题上。
我 collection 喜欢:
{
"_id": ObjectId("55820292e3dc84aa0c9c63bc"),
"creation_at": ISODate("2015-06-17T23:28:18.896Z"),
"cpu": 36,
"mem": "1.08"
}
并且每 30 秒将一些日志放入其中。
我想按时间段获取摘要信息,例如:
[
{
'interval': ['2015-06-07 13:00:00', '2015-06-07 13:59:59'],
'cpu': {
'max': 20,
'min': 1,
'avg': 3
},
'memory': {
'max': 40,
'min': 35,
'avg': 38
}
},
{
'interval': ['2015-06-07 14:00:00', '2015-06-07 14:59:59'],
'cpu': { ... },
'memory': { ... }
},
...
...
...
]
从 mongo 文档中我发现这可以通过聚合框架来实现,但我坚持使用时间间隔的生成。
你能给我一些建议吗?
你的数据中的内存似乎是 "string",所以除非你转换它,否则这将是一个问题。使用数值,这对于 Date Aggregation Operators:
的聚合框架来说很容易
db.collection.aggregate([
{ "$group": {
"_id": {
"year": { "$year": "$creation_at" },
"month": { "$month": "$creation_at" },
"day": { "$day": "$creation_at" },
"hour": { "$hour": "$creation_at" }
},
"minCpu": { "$min": "$cpu" },
"maxCpu": { "$max": "$cpu" },
"avgCpu": { "$avg": "$cpu" },
"minMem": { "$min": "$mem" },
"maxMem": { "$max": "$mem" },
"avgMem": { "$avg": "$mem" }
}}
])
这是一小时的间隔,但当然还有更细粒度的运算符。当然,如果没有进一步投影,聚合字段不能像您拥有的那样显示为 "nested"。但如果你愿意,你也可以这样做。
如果日期聚合运算符看起来过于简洁或 "timestamp" 值更适合您,那么您可以改用日期数学:
db.collection.aggregate([
{ "$group": {
"_id": {
"$subtract": [
{ "$subtract": [ "$creation_at", new Date("1970-01-01") ] },
{ "$mod": [
{ "$subtract": [ "$creation_at", new Date("1970-01-01") ] },
1000 * 60 * 60
]}
]
},
"minCpu": { "$min": "$cpu" },
"maxCpu": { "$max": "$cpu" },
"avgCpu": { "$avg": "$cpu" },
"minMem": { "$min": "$mem" },
"maxMem": { "$max": "$mem" },
"avgMem": { "$avg": "$mem" }
}}
])
因为 "subtracting" 来自日期 returns 的 "epoch date" 值当前时间以毫秒为数字。
如果你的 "string" 是一个问题,你可以随时回退到 mapReduce:
db.collection.mapReduce(
function() {
var date = this.creation_at.valueOf()
- ( this.creation_at.valueOf % ( 1000 * 60 * 60 ) );
emit( new Date(date), {
cpu: {
min: parseInt(this.cpu),
max: parseInt(this.cpu),
avg: parseInt(this.cpu)
},
mem: {
min: parseFloat(this.mem),
max: parseFloat(this.mem),
avg: parseFloat(this.mem)
}
})
},
function(key,values) {
var result = {
cpu: { min: 0, max: 0, avg: 0 },
mem: { min: 0, max: 0, avg: 0 }
};
result.cpu.min = Math.min.apply( null, values.map(function(el) {
return el.cpu.min;
}));
result.cpu.max = Math.max.apply( null, values.map(function(el) {
return el.cpu.max;
}));
result.cpu.avg = Array.sum(values.map(function(el) {
return el.cpu.avg
})) / values.length;
result.mem.min = Math.min.apply( null, values.map(function(el) {
return el.mem.min;
}));
result.mem.max = Math.max.apply( null, values.map(function(el) {
return el.mem.max;
}));
result.mem.avg = Array.sum(values.map(function(el) {
return el.mem.avg
})) / values.length;
return result;
},
{ "out": { "inline": 1 } }
)
并且可能比那种快速的 hacky 方法更好地清理 reducer 逻辑。
但基本原理是从日期中提取间隔以进行分组,然后对结果字段进行数学计算。
我目前卡在了这个问题上。 我 collection 喜欢:
{
"_id": ObjectId("55820292e3dc84aa0c9c63bc"),
"creation_at": ISODate("2015-06-17T23:28:18.896Z"),
"cpu": 36,
"mem": "1.08"
}
并且每 30 秒将一些日志放入其中。 我想按时间段获取摘要信息,例如:
[
{
'interval': ['2015-06-07 13:00:00', '2015-06-07 13:59:59'],
'cpu': {
'max': 20,
'min': 1,
'avg': 3
},
'memory': {
'max': 40,
'min': 35,
'avg': 38
}
},
{
'interval': ['2015-06-07 14:00:00', '2015-06-07 14:59:59'],
'cpu': { ... },
'memory': { ... }
},
...
...
...
]
从 mongo 文档中我发现这可以通过聚合框架来实现,但我坚持使用时间间隔的生成。 你能给我一些建议吗?
你的数据中的内存似乎是 "string",所以除非你转换它,否则这将是一个问题。使用数值,这对于 Date Aggregation Operators:
的聚合框架来说很容易db.collection.aggregate([
{ "$group": {
"_id": {
"year": { "$year": "$creation_at" },
"month": { "$month": "$creation_at" },
"day": { "$day": "$creation_at" },
"hour": { "$hour": "$creation_at" }
},
"minCpu": { "$min": "$cpu" },
"maxCpu": { "$max": "$cpu" },
"avgCpu": { "$avg": "$cpu" },
"minMem": { "$min": "$mem" },
"maxMem": { "$max": "$mem" },
"avgMem": { "$avg": "$mem" }
}}
])
这是一小时的间隔,但当然还有更细粒度的运算符。当然,如果没有进一步投影,聚合字段不能像您拥有的那样显示为 "nested"。但如果你愿意,你也可以这样做。
如果日期聚合运算符看起来过于简洁或 "timestamp" 值更适合您,那么您可以改用日期数学:
db.collection.aggregate([
{ "$group": {
"_id": {
"$subtract": [
{ "$subtract": [ "$creation_at", new Date("1970-01-01") ] },
{ "$mod": [
{ "$subtract": [ "$creation_at", new Date("1970-01-01") ] },
1000 * 60 * 60
]}
]
},
"minCpu": { "$min": "$cpu" },
"maxCpu": { "$max": "$cpu" },
"avgCpu": { "$avg": "$cpu" },
"minMem": { "$min": "$mem" },
"maxMem": { "$max": "$mem" },
"avgMem": { "$avg": "$mem" }
}}
])
因为 "subtracting" 来自日期 returns 的 "epoch date" 值当前时间以毫秒为数字。
如果你的 "string" 是一个问题,你可以随时回退到 mapReduce:
db.collection.mapReduce(
function() {
var date = this.creation_at.valueOf()
- ( this.creation_at.valueOf % ( 1000 * 60 * 60 ) );
emit( new Date(date), {
cpu: {
min: parseInt(this.cpu),
max: parseInt(this.cpu),
avg: parseInt(this.cpu)
},
mem: {
min: parseFloat(this.mem),
max: parseFloat(this.mem),
avg: parseFloat(this.mem)
}
})
},
function(key,values) {
var result = {
cpu: { min: 0, max: 0, avg: 0 },
mem: { min: 0, max: 0, avg: 0 }
};
result.cpu.min = Math.min.apply( null, values.map(function(el) {
return el.cpu.min;
}));
result.cpu.max = Math.max.apply( null, values.map(function(el) {
return el.cpu.max;
}));
result.cpu.avg = Array.sum(values.map(function(el) {
return el.cpu.avg
})) / values.length;
result.mem.min = Math.min.apply( null, values.map(function(el) {
return el.mem.min;
}));
result.mem.max = Math.max.apply( null, values.map(function(el) {
return el.mem.max;
}));
result.mem.avg = Array.sum(values.map(function(el) {
return el.mem.avg
})) / values.length;
return result;
},
{ "out": { "inline": 1 } }
)
并且可能比那种快速的 hacky 方法更好地清理 reducer 逻辑。
但基本原理是从日期中提取间隔以进行分组,然后对结果字段进行数学计算。