当我使用少量文档对MongoDB集合执行Mapreduce操作时,一切正常。
但是当我用一个包含大约140,000个文档的集合运行它时,我得到了一些奇怪的结果:
地图功能:
function() { emit(this.featureType, this._id); }
减少功能:
function(key, values) { return { count: values.length, ids: values };
因此,我希望(对于每个映射键):
{
"_id": "FEATURE_TYPE_A",
"value": { "count": 140000,
"ids": [ "9b2066c0-811b-47e3-ad4d-e8fb6a8a14e7",
"db364b3f-045f-4cb8-a52e-2267df40066c",
"d2152826-6777-4cc0-b701-3028a5ea4395",
"7ba366ae-264a-412e-b653-ce2fb7c10b52",
"513e37b8-94d4-4eb9-b414-6e45f6e39bb5", .......}
但我得到了这个奇怪的文档结构:
{
"_id": "FEATURE_TYPE_A",
"value": {
"count": 706,
"ids": [
{
"count": 101,
"ids": [
{
"count": 100,
"ids": [
"9b2066c0-811b-47e3-ad4d-e8fb6a8a14e7",
"db364b3f-045f-4cb8-a52e-2267df40066c",
"d2152826-6777-4cc0-b701-3028a5ea4395",
"7ba366ae-264a-412e-b653-ce2fb7c10b52",
"513e37b8-94d4-4eb9-b414-6e45f6e39bb5".....}
有人可以解释我这是预期的行为,还是我做错了什么?
提前致谢!
答案 0 :(得分:7)
这里的情况并不常见,我不确定这是否是您真正想要的,因为生成了大型数组。但是documentation中有一点在mapReduce如何工作的假设中被忽略了。
- MongoDB可以为同一个密钥多次调用reduce函数。在这种情况下,该键的reduce函数的先前输出将成为该键的下一个reduce函数调用的输入值之一。
这里基本上说的是你当前的操作只是期望"减少"函数被称为一次,但事实并非如此。事实上输入将被打破"并在这里作为可管理的大小传递。多次调用"减少"现在提出另一点非常重要。
因为可以为同一个键多次调用reduce函数,所以需要满足以下属性:
- 返回对象的类型必须与地图函数发出的值的类型相同,以确保以下操作成立:
基本上这意味着你的" mapper"和"减速机"为了产生你想要的结果,必须采取更多的复杂性。基本上确保" mapper"的输出发送方式与它在" reducer"中的显示方式相同。而减少过程本身也注意到这一点。
首先修改了映射器:
function () { emit(this.type, { count: 1, ids: [this._id] }); }
现在与最终输出表格一致。在考虑您现在知道的多次调用的reducer时,这很重要:
function (key, values) {
var ids = [];
var count = 0;
values.forEach(function(value) {
count += value.count;
value.ids.forEach(function(id) {
ids.push( id );
});
});
return { count: count, ids: ids };
}
这意味着reduce函数的每次调用都需要与输出相同的输入,即count字段和id数组。这基本上是
的最终结果这似乎不是很明显,但行为是设计的,其中reducer以这种方式被多次调用以处理大量的发射数据,因此它逐渐地聚合"而不是迈出一大步。
聚合框架使这更简单,从MongoDB 2.6及以上版本甚至可以将结果输出到集合,因此如果您有多个结果并且组合输出大于16MB,那么这不会是一个问题。
db.collection.aggregate([
{ "$group": {
"_id": "$featureType",
"count": { "$sum": 1 },
"ids": { "$push": "$_id" }
}},
{ "$out": "ouputCollection" }
])
所以这不会破坏并且会按预期实际返回,由于操作确实非常简单,复杂性大大降低。
但我已经说过你回归" _id"的目的了。鉴于其庞大的规模,这里的值似乎不清楚。所以如果你真正想要的只是" featureType"然后你会使用基本相同的方法,而不是试图强制mapReduce找到一个非常大的数组的长度:
db.collection.aggregate([
{ "$group": {
"_id": "$featureType",
"count": { "$sum": 1 },
}}
])
在任何一种形式中,结果都是正确的,并且在构建的mapReduce操作的一小部分时间内运行。