MongoDB Map / Reduce Array聚合问题
|
我有一个MongoDB集合,其文档使用了多个层次的嵌套,我希望从中提取从其字段子集编译的多维数组。我有一个适合我的解决方案,但是我想更好地理解“幂等”的概念及其与reduce函数相关的后果。
{
\"host_name\" : \"gateway\",
\"service_description\" : \"PING\",
\"last_update\" : 1305777787,
\"performance_object\" : [
[ \"rta\", 0.105, \"ms\", 100, 500, 0 ],
[ \"pl\", 0, \"%\", 20, 60, 0 ]
]
}
这是map / reduce函数
var M = function() {
var hn = this.host_name,
sv = this.service_description,
ts = this.last_update;
this.performance_object.forEach(function(P){
emit( {
host: hn,
service: sv,
metric: P[0]
}, {
time: ts,
value: P[1]
} );
});
}
var R = function(key,values) {
var result = {
time: [],
value: []
};
values.forEach(function(V){
result.time.push(V.time);
result.value.push(V.value);
});
return result;
}
db.runCommand({
mapreduce: <colname>,
out: <col2name>,
map: M,
reduce: R
});
数据以有用的结构返回,我将其格式化/排序并最终确定图形。
{
\"_id\" : {
\"host\" : \"localhost\",
\"service\" : \"Disk Space\",
\"metric\" : \"/var/bck\"
},
\"value\" : {
\"time\" : [
[ 1306719302, 1306719601, 1306719903, ... ],
[ 1306736404, 1306736703, 1306737002, ... ],
[ 1306766401, 1306766701, 1306767001, ... ]
],
\"value\" : [
[ 122, 23423, 25654, ... ],
[ 336114, 342511, 349067, ... ],
[ 551196, 551196, 551196, ... ]
]
}
}
最后...
[ [1306719302,122], [1306719601,23423], [1306719903,25654], ... ]
TL; DR:阵列结果中出现“块化”的预期行为是什么?
我知道,在发射值的数组上可以多次调用reduce函数,这就是为什么完整数组中有多个“块”而不是单个数组的原因。数组块通常为25至50个项目,并且很容易在finalize()中清除它。我concat()数组,将它们交织为[time,value]并排序。但是我真正想知道的是,这是否会变得更加复杂:
1)是否因为我的代码,MongoDB的实现或Map / Reduce算法本身而发现了分块?
2)在分片配置中是否会出现更深(递归)的数组块嵌套,甚至仅仅是因为我的草率实现?这将破坏concat()方法。
3)是否有一个更好的策略来获得如上所述的数组结果?
编辑:修改为发出数组:
我接受了Thomas的建议,并重新编写了它以发出阵列。拆分这些值绝对没有任何意义。
var M = function() {
var hn = this.host_name,
sv = this.service_description,
ts = this.last_update;
this.performance_object.forEach(function(P){
emit( {
host: hn,
service: sv,
metric: P[0]
}, {
value: [ ts, P[1] ]
} );
});
}
var R = function(key,values) {
var result = {
value: []
};
values.forEach(function(V){
result.value.push(V.value);
});
return result;
}
db.runCommand({
mapreduce: <colname>,
out: <col2name>,
map: M,
reduce: R
});
现在输出类似于:
{
\"_id\" : {
\"host\" : \"localhost\",
\"service\" : \"Disk Space\",
\"metric\" : \"/var/bck\"
},
\"value\" : {
\"value\" : [
[ [1306736404,336114],[1306736703,342511],[1306737002,349067], ... ],
[ [1306766401,551196],[1306766701,551196],[1306767001,551196], ... ],
[ [1306719302,122],[1306719601,122],[1306719903,122], ... ]
]
}
}
然后,我使用了这个finalize函数来连接数组块并对它们进行排序。
...
var F = function(key,values) {
return (Array.concat.apply([],values.value)).sort(function(a,b){
if (a[0] < b[0]) return -1;
if (a[0] > b[0]) return 1;
return 0;
});
}
db.runCommand({
mapreduce: <colname>,
out: <col2name>,
map: M,
reduce: R,
finalize: F
});
哪个很好用:
{
\"_id\" : {
\"host\" : \"localhost\",
\"service\" : \"Disk Space\",
\"metric\" : \"/mnt/bck\"
},
\"value\" : [ [1306719302,122],[1306719601,122],[1306719903,122],, ... ]
}
我猜唯一一个困扰我的问题是,是否可以始终信任此Array.concat.apply([],values.value)来清理reduce的输出。
最后编辑:更简单...
自从上面给出原始示例以来,我已经修改了文档结构,但这仅通过使map函数真正简单来更改示例。
我仍在努力地思考为什么Array.prototype.push.apply(result,V.data)的工作原理与result.push(V.data)如此不同...但是可以工作。
var M = function() {
emit( {
host: this.host,
service: this.service,
metric: this.metric
} , {
data: [ [ this.timestamp, this.data ] ]
} );
}
var R = function(key,values) {
var result = [];
values.forEach(function(V){
Array.prototype.push.apply(result, V.data);
});
return { data: result };
}
var F = function(key,values) {
return values.data.sort(function(a,b){
return (a[0]<b[0]) ? -1 : (a[0]>b[0]) ? 1 : 0;
});
}
它具有与LAST EDIT标题上方相同的输出。
谢谢,托马斯!
没有找到相关结果
已邀请:
1 个回复
弛保矮瘦敖
,或先前调用reduce函数返回的
。 我不知道它是否会在实践中发生,但它可以在理论上发生。 只需让您的map函数发出与reduce函数返回的对象相同的对象(即
),并相应地更改reduce函数(即
)和类似的
。 好吧,我实际上不明白为什么您不使用时间/值对数组而不是数组,即map函数中的function14ѭ或
以及reduce函数中的
。这样,您甚至不需要finalize函数(可能要从pairs属性“解包”数组:因为reduce函数无法返回数组,因此您必须将其包装在对象中)