MongoDB MapReduce奇怪的结果



当我在MongoDB集合上执行Mapreduce操作时,包含少量文档,一切正常。

但是当我用大约140.000个文档的集合运行它时,我得到了一些奇怪的结果:

地图功能:

function() { emit(this.featureType, this._id); }

Reduce函数:

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".....}

有人可以解释我,如果这是预期的行为,还是我做错了什么?

提前感谢!

这里的情况是不寻常的,我不确定这是否是您真正想要生成的大型数组。但是在假设mapReduce是如何工作的时候,文档中有一点被忽略了。

  • MongoDB可以对同一个键多次调用reduce函数。在这种情况下,先前的reduce函数对该键的输出将成为下一次调用该键的reduce函数的输入值之一。

这里基本上说的是,您当前的操作只期望"reduce"函数应该在一次调用,但事实并非如此。输入实际上会被"分解"。并以可控尺寸传递到这里。多重调用&;reduce&;现在我要强调一点,

因为可以对同一个键多次调用reduce函数,所以下列属性必须为真:

  • 返回对象的类型必须与map函数发出的值的类型相同,以确保以下操作为真:

本质上这意味着你的"映射器"one_answers";reducer"为了产生你想要的结果,必须承担更多的复杂性。基本上确保"映射器"的输出以与在"reducer"中显示的形式相同的形式发送。而reduce过程本身也注意到了这一点。

所以首先映射器修改:

function () { emit(this.type, { count: 1, ids: [this._id] }); }

现在与最终输出形式一致。在考虑您现在知道将被多次调用的减速器时,这一点很重要:

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函数都期望得到与输出相同的输入,即一个计数字段和一个id数组。这基本上通过

得到最终结果
  • 减少一个结果块#chunk1
  • 减少另一块结果#chunk2
  • 合并减少的块,#chunk1和#chunk2

这可能看起来不是很明显,但是这种行为是设计的,在这种情况下,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操作所需时间的一小部分。

相关内容

  • 没有找到相关文章

最新更新