jca*_*lly 8 mapreduce mongodb nosql
我有一个MongoDB集合,其文档使用几个级别的嵌套,我想从中提取从其字段的子集编译的多维数组.我现在有一个适合我的解决方案,但我想更好地理解"幂等"的概念及其与减少功能相关的后果.
{
"host_name" : "gateway",
"service_description" : "PING",
"last_update" : 1305777787,
"performance_object" : [
[ "rta", 0.105, "ms", 100, 500, 0 ],
[ "pl", 0, "%", 20, 60, 0 ]
]
}
Run Code Online (Sandbox Code Playgroud)
这是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
});
Run Code Online (Sandbox Code Playgroud)
数据以有用的结构返回,我将其重新格式化/排序并使用finalize进行绘图.
{
"_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, ... ]
]
}
}
Run Code Online (Sandbox Code Playgroud)
最后...
[ [1306719302,122], [1306719601,23423], [1306719903,25654], ... ]
Run Code Online (Sandbox Code Playgroud)
TL; DR:对于数组结果的超级"分块",预期的行为是什么?
我知道可以在发射值的数组上多次调用reduce函数,这就是为什么有几个完整数组的"块"而不是单个数组.阵列块通常是25-50个项目,并且很容易在finalize()中清除它.我concat()数组,将它们交错为[时间,值]和排序.但我真正想知道的是,这会变得更复杂:
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
});
Run Code Online (Sandbox Code Playgroud)
现在输出类似于:
{
"_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], ... ]
]
}
}
Run Code Online (Sandbox Code Playgroud)
我使用这个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
});
Run Code Online (Sandbox Code Playgroud)
哪个很好用:
{
"_id" : {
"host" : "localhost",
"service" : "Disk Space",
"metric" : "/mnt/bck"
},
"value" : [ [1306719302,122],[1306719601,122],[1306719903,122],, ... ]
}
Run Code Online (Sandbox Code Playgroud)
我想唯一能够扼杀我的问题是,是否可以信任这个Array.concat.apply([],values.value)来清理减少所有时间的输出.
自上面给出的原始示例以来,我修改了文档结构,但这只是通过使map函数非常简单来改变示例.
我仍然试图将我的大脑包裹起来,为什么Array.prototype.push.apply(结果,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;
});
}
Run Code Online (Sandbox Code Playgroud)
它具有与LAST EDIT标题正上方相同的输出.
谢谢,托马斯!
“分块”来自您的代码:您的reduce 函数的values 参数可以包含{time:<timestamp>,value:<value>}从map 函数发出的值,也可以{time:[<timestamps>],value:[<values]}包含从之前对reduce 函数的调用返回的值。
我不知道它在实践中是否会发生,但理论上是可以发生的。
只需让您的map函数发出与您的reduce函数返回相同类型的对象,即emit(<id>, {time: [ts], value: [P[1]]}),并相应地更改您的reduce函数,即Array.push.apply(result.time, V.time),类似地result.value。
好吧,我实际上不明白为什么你不使用时间/值对的数组,而不是一对数组,即emit(<id>, { pairs: [ {time: ts, value: P[1] ] })在emit(<id>, { pairs: [ [ts, P[1]] ] })map函数和Array.push.apply(result.pairs, V.pairs)reduce函数中。这样,您甚至不需要 Finalize 函数(除了从pairs属性中“解开”数组:因为reduce函数无法返回数组,所以您必须以这种方式将其包装在对象中)