0

我是 map reduce 概念的新手,尽管我进展缓慢,但我发现了一些需要帮助的问题。

我有一个简单的集合,由 id、城市和目的地组成,如下所示:

{ "_id" : "5230e7e00000000000000000", "city" : "Boston", "to" : "Chicago" },
{ "_id" : "523fe7e00000000000000000", "city" : "New York", "to" : "Miami" },
{ "_id" : "5240e1e00000000000000000", "city" : "Boston", "to" : "Miami" },
{ "_id" : "536fe4e00000000000000000", "city" : "Washington D.C.", "to" : "Boston" },
{ "_id" : "53ffe7e00000000000000000", "city" : "New York", "to" : "Boston" },
{ "_id" : "5740e1e00000000000000000", "city" : "Boston", "to" : "Miami" },
...

(请注意,此数据仅供参考)

我想按城市对目的地进行分组,包括计数:

{ "city" : "Boston", values : [{"Chicago",1}, {"Miami",2}] }
{ "city" : "New York", values : [{"Miami",1}, {"Boston",1}] }
{ "city" : "Washington D.C.", values : [{"Boston", 1}] }

为此,我开始使用此功能进行映射:

    function() {
        emit(this.city, this.to);
    }

它执行预期的分组。我的减少功能是这样的:

    function(key, values) {
        var reduced = {"to":[]};

        for (var i in values) {
            var item = values[i];
            reduced.to.push(item);
        }

        return reduced;
    }

这给出了一些预期的输出:

{ "_id" : ObjectId("522f8a9181f01e671a853adb"), "value" : { "to" : [    "Boston", "Miami" ] } }
{ "_id" : ObjectId("522f933a81f01e671a853ade"), "value" : { "to" : [  "Chicago",  "Miami", "Miami" ] } }
{ "_id" : ObjectId("5231f0ed81f01e671a853ae0"), "value" : "Boston" }

如您所见,我仍然没有计算重复的城市,但是从上面可以看出,由于某种原因,输出中的最后一个结果看起来不太好。我希望它是

{ "_id" : ObjectId("5231f0ed81f01e671a853ae0"), "value" : { "to" : ["Boston"] } }

这与只有一个项目有关吗?有没有办法获得这个?

谢谢你。

4

1 回答 1

2

我看到您在询问 PHP 问题,但您使用的是 javascript 来询问,所以我假设 javascript 的答案将帮助您继续前进。因此,这里是 shell 中运行聚合所需的 javascript。我强烈建议让您的聚合通常在 shell(或其他一些 javascript 编辑器)中工作,然后将其翻译成您选择的语言。使用此方法可以更轻松地查看正在发生的事情并且更快。然后你可以运行:

use admin
db.runCommand( { setParameter: 1, logLevel: 2 } )

检查您选择的语言的 bson 输出与外壳的外观。如果 mongo 在前台,这将出现在终端中,否则您将无法查看日志。

将聚合框架 [AF] 中的路由与 Mongo 相加是相当困难的。AF 比 map reduce[MR] 更快更容易使用。尽管在这种情况下它们都有类似的问题,但简单地推送到数组本身不会产生计数(在 MR 中,您需要在 reduce 函数中使用更多逻辑或使用 finalize 函数)。

对于使用提供的示例数据的 AF,此管道很有用:

db.agg1.aggregate([
     {$group:{
         _id: { city: "$city", to: "$to" },  
         count: { $sum: 1 }
     }},
     {$group: {
         _id: "$_id.city",
         to:{ $push: {to: "$_id.to", count: "$count"}}
     }}
]);

聚合框架只能对已知字段进行操作,但是许多管道操作因此需要将其分解为一个问题。上面,第一阶段计算需要的数字,其中有 3 个固定字段:源、目标和计数。第二阶段有 2 个固定字段,其中一个是一个数组,它只是被推送到(最终表单的所有数据都在那里)。

对于 MR,您可以这样做:

var map = function() {
    var key = {source:this.city, dest:this.to};
    emit(key, 1);
};

var reduce = function(key, values) {
    return Array.sum(values);
};

然而,一个单独的功能将不得不美化它。

如果您有任何其他问题,请随时提出。

最好的,查理

于 2013-09-18T22:17:39.590 回答