MongoDB MapReduce 奇怪的结果

标签 mongodb mapreduce aggregation-framework

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

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

map 功能:

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

如果这是预期的行为,或者我做错了什么,有人可以解释一下吗?

提前致谢!

最佳答案

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

  • MongoDB can invoke the reduce function more than once for the same key. In this case, the previous output from the reduce function for that key will become one of the input values to the next reduce function invocation for that key.

这里基本上说的是,您当前的操作只希望“reduce”函数被调用一次,但事实并非如此。输入实际上将被“分解”并作为可管理的大小传递到此处。 “reduce”的多次调用现在使另一点变得非常重要。

Because it is possible to invoke the reduce function more than once for the same key, the following properties need to be true:

  • the type of the return object must be identical to the type of the value emitted by the map function to ensure that the following operations is true:

从本质上讲,这意味着您的“映射器”和“缩减器”都必须承担更多的复杂性才能产生您想要的结果。本质上确保“映射器”的输出以与它在“缩减器”中出现的方式相同的形式发送,并且缩减过程本身会注意到这一点。

所以首先修改映射器:

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 函数的每次调用都需要与输出相同的输入,即计数字段和 id 数组。这基本上是通过

  • 减少一大块结果#chunk1
  • 减少另一 block 结果#chunk2
  • 在减少的 block #chunk1 和 #chunk2 上合并 reduce

这可能不会立即显现出来,但这种行为是设计使然,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 操作所需时间的一小部分。

关于MongoDB MapReduce 奇怪的结果,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/23923136/

相关文章:

node.js - 只获取每个用户的一份文档 - mongoDB

mongodb - 仅当文档早于 MongoDB 中的新文档时,如何更新插入?

java - 调用作业的区别

java - JAVA中的Hadoop MapReduce输出

sql-server - Mongodb 中的 Max 和 group by

MongoDB "count"使用 "$in"变得太慢

安卓+NoSQL

java - (Spring Data MongoDB)多对多关系场景

c++ - 移动拷贝和/或分配 mongodb cxx 游标

Mongodb_Hadoop MapReduce