如何在MongoDb中聚合非关联数组中的数据



在问这个问题之前我搜索了很多,但是没有找到任何相关的信息。

在这里:

我的问题是我有一个mongo集合在以下格式:

{
    "_id" : ObjectId("55bf4031eb8ac118a4b3110e"),
    "sid" : 1,
    "plugin_count" : [
      0,
      0,
      0,
      1,
      0,
      1,
      0,
      0,
      0, 
      0
    ],
},
{
    "_id" : ObjectId("55bf4031eb8ac118a4b3110e"),
    "sid" : 1,
    "plugin_count" : [
      2,
      1,
      0,
      6,
      0,
      10,
      12,
      0,
      16, 
      22
    ],
}

现在我要做的是,我要把非关联数组中的所有元素单独相加然后像这样输出

{
    "result": [
        {
            "_id": 1,
            "plugin_count": [
                2,
                1,
                0,
                7,
                0,
                11,
                12,
                0,
                16,
                22
            ]
        }
    ],
    "ok": 1
}

我的查询如下

db.plugin_table.aggregate([
    {
        "$match" : {
            "sid" : {
                "$in" : [1]
            }
        }
    },
    {
        "$unwind" : "$plugin_count"
    },
    {
        "$group" : {
            "_id": 1,
            "plugin_count" : {
                "$sum" : "$plugin_count"
            }
        }
    }
]);

但我得到以下输出:

{
    "result": [
        {
            "_id": 1,
            "plugin_count": 0
        }
    ],
    "ok": 1
}

请帮帮我,我真的要把头发拔出来了。(

对于聚合框架,确实没有相同的方法来做到这一点。这里的问题是跟踪数组元素的"索引"位置,其中没有什么可以做到这一点。

最好的选择是mapReduce,它可以非常简单地处理问题,并且可以很好地扩展到任意数量的分组键:

db.plugin_table.mapReduce(
    function () {
      emit(this.sid, { plugin_count: this.plugin_count });
    },
    function (key,values) {
      var result = { plugin_count: [] };
      values.forEach(function(value) {
        value.plugin_count.forEach(function(plugin,idx) {
          if ( result.plugin_count[idx] === undefined ) {
            result.plugin_count[idx] = plugin;
          } else {
            result.plugin_count[idx] += plugin;
          }
        });
      });
      return result;
    },
    { 
      "query": { "sid": 1 },
      "out": { "inline": 1 }
    }
)

生成所需的输出,以mapReduce总是生成具有顶级键_idvalue的输出的形式:

            {
                    "_id" : 1,
                    "value" : {
                            "plugin_count" : [
                                    2,
                                    1,
                                    0,
                                    7,
                                    0,
                                    11,
                                    12,
                                    0,
                                    16,
                                    22
                            ]
                    }
            }

请注意,这里的reduce算法还可以轻松地处理不同长度的数组,因为所有内容都是按索引位置配对的,或者如果该位置不存在于每个键的组合结果中,则以其他方式创建。


唯一真正的聚合框架可以处理这个问题的方法是已经将数据实际上放在一个"关联数组"中,或者至少将每个元素本身作为一个关联数组,就像这样:

{
    "_id" : ObjectId("55d32ffde12af47feb19bce7"),
    "sid" : 1,
    "plugin_count" : [
            {
                    "pos" : 0,
                    "value" : 0
            },
            {
                    "pos" : 1,
                    "value" : 0
            },
            {
                    "pos" : 2,
                    "value" : 0
            },
            {
                    "pos" : 3,
                    "value" : 1
            },
            {
                    "pos" : 4,
                    "value" : 0
            },
            {
                    "pos" : 5,
                    "value" : 1
            },
            {
                    "pos" : 6,
                    "value" : 0
            },
            {
                    "pos" : 7,
                    "value" : 0
            },
            {
                    "pos" : 8,
                    "value" : 0
            },
            {
                    "pos" : 9,
                    "value" : 0
            }
    ]
}

下面是一个基本的转换:

  db.junk.find().forEach(function(doc) { 
    doc.plugin_count = doc.plugin_count.map(function(value,idx) { 
      return { "pos": idx, "value": value };
    });
    db.newjunk.insert(doc);
  });

然后,通过简单地对"pos"元素上的"first"进行分组并将结果相加,就得到了一个基本的聚合。最后的数组可以通过分组返回到"sid":

来形成。
  db.newjunk.aggregate([
    { "$match": { "sid": 1 } },
    { "$unwind": "$plugin_count" },
    { "$group": {
      "_id": {
        "sid": "$sid",
        "pos": "$plugin_count.pos"
      },
      "value": { "$sum": "$plugin_count.value" }
    }},
    { "$sort": { "_id": 1 } },
    { "$group": {
      "_id": "$_id.sid",
      "plugin_count": { "$push": "$value" }
    }}
  ])

输出与前面相同:

{ "_id" : 1, "plugin_count" : [ 2, 1, 0, 7, 0, 11, 12, 0, 16, 22 ] }
这里还需要注意的是,通过保留关联信息,可以避免出现 $sort 阶段。使用 $group 不能保证位置保持不变,但是对于关联信息,这不是真正需要的。

所以这一切都归结为你能忍受什么。如果你想在数据中保留普通数组,那么你将需要mapReduce来获得结果。但是,如果您愿意更改数据格式,那么聚合方法是完全可行的。

然而,这可能是一个很好的情况,在"大规模"mapReduce进程可以通过避免处理 $unwind 的开销来击败聚合进程。

相关内容

  • 没有找到相关文章

最新更新