在 mongo 2.6 文档中,请参阅下面的几个
nms:PRIMARY> db.checkpointstest4.find()
{ "_id" : 1, "cpu" : [ 100, 20, 60 ], "hostname" : "host1" }
{ "_id" : 2, "cpu" : [ 40, 30, 80 ], "hostname" : "host1" }
我需要找到每个主机的平均 cpu(每个 cpu 阵列索引(,即基于上述两个,host1
的平均值将被[70,25,70]
,因为cpu[0]
是100+40=70
等
当我有 3 个数组元素而不是两个数组元素时,我迷路了,请参阅 mongodb 数组元素的总平均值
最后,下面对我有用:
var map = function () {
for (var idx = 0; idx < this.cpu.length; idx++) {
var mapped = {
idx: idx,
val: this.cpu[idx]
};
emit(this.hostname, {"cpu": mapped});
}
};
var reduce = function (key, values) {
var cpu = []; var sum = [0,0,0]; cnt = [0,0,0];
values.forEach(function (value) {
sum[value.cpu.idx] += value.cpu.val;
cnt[value.cpu.idx] +=1;
cpu[value.cpu.idx] = sum[value.cpu.idx]/cnt[value.cpu.idx]
});
return {"cpu": cpu};
};
db.checkpointstest4.mapReduce(map, reduce, {out: "checkpointstest4_result"});
在 MongoDB 3.2 中,includeArrayIndex
出现,你可以这样做;
db.test.aggregate(
{$unwind: {path:"$cpu", includeArrayIndex:"index"}},
{$group: {_id:{h:"$hostname",i:"$index"}, cpu:{$avg:"$cpu"}}},
{$sort:{"_id.i":1}},
{$group:{_id:"$_id.h", cpu:{$push:"$cpu"}}}
)
// Make a row for each array element with an index field added.
{$unwind: {path:"$cpu", includeArrayIndex:"index"}},
// Group by hostname+index, calculate average for each group.
{$group: {_id:{h:"$hostname",i:"$index"}, cpu:{$avg:"$cpu"}}},
// Sort by index (to get the array in the next step sorted correctly)
{$sort:{"_id.i":1}},
// Group by host, pushing the averages into an array in order.
{$group:{_id:"$_id.h", cpu:{$push:"$cpu"}}}
升级将是你最好的选择,如MongoDB 3.2开始可用于$unwind
includeArrayIndex
所述。
如果你不能做到这一点,那么你总是可以使用mapReduce来处理:
db.checkpointstest4.mapReduce(
function() {
var mapped = this.cpu.map(function(val) {
return { "val": val, "cnt": 1 };
});
emit(this.hostname,{ "cpu": mapped });
},
function(key,values) {
var cpu = [];
values.forEach(function(value) {
value.cpu.forEach(function(item,idx) {
if ( cpu[idx] == undefined )
cpu[idx] = { "val": 0, "cnt": 0 };
cpu[idx].val += item.val;
cpu[idx].cnt += item.cnt
});
});
return { "cpu": cpu };
},
{
"out": { "inline": 1 },
"finalize": function(key,value) {
return {
"cpu": value.cpu.map(function(cpu) {
return cpu.val / cpu.cnt;
})
};
}
}
)
因此,"映射器"函数中的步骤是将数组内容转换为包含元素中的"值"和"计数"的对象数组,以便以后作为"reduce"函数的输入引用。您需要这与化简器的工作方式保持一致,并且对于获得平均值所需的总计数是必要的。
在"化简器"本身中,您基本上是将"值"和"计数"的每个位置的数组内容相加。这很重要,因为"reduce"函数可以在整个归约过程中多次调用,在后续调用中将其输出为"输入"。所以这就是为什么映射器和化简器都以这种格式工作的原因。
对于最终的简化结果,调用 finalize
函数来简单地查看每个求和的"值"和"计数",然后除以计数以返回平均值。
里程可能会因现代聚合管道处理或此mapReduce过程是否执行最佳而有所不同,主要取决于数据。以规定的方式使用$unwind
肯定会增加要分析的文档数量,从而产生开销。相反,虽然与聚合框架中的本机运算符相比,JavaScript 处理通常会更慢,但这里的文档处理开销减少了,因为这是保留数组。
我给出的建议是,如果升级到 3.2 不是一种选择,那么即使是一个选项,那么至少根据您的数据和预期增长对两者进行基准测试,看看哪个最适合您。
返回
{
"results" : [
{
"_id" : "host1",
"value" : {
"cpu" : [
70,
25,
70
]
}
}
],
"timeMillis" : 38,
"counts" : {
"input" : 2,
"emit" : 2,
"reduce" : 1,
"output" : 1
},
"ok" : 1
}