MongoDB Map Reduce newbie (PHP)


MongoDB Map Reduce newbie (PHP)

我是地图减少概念的新手,尽管我进展缓慢,但我发现一些需要帮助的问题。

我有一个简单的集合,由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);
    }

执行预期的分组。我的reduce函数是这样的:

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

这与只有一个项目的事实有关吗?有什么办法可以得到这个吗?

谢谢。

我看到你在问一个PHP问题,但你正在使用javascript来问,所以我假设javascript的答案会帮助你推动事情的发展。 因此,这里是 shell 中运行聚合所需的 JavaScript。 我强烈建议你让你的聚合在shell(或其他一些javascript编辑器)中工作,然后将其翻译成你选择的语言。 使用这种方法更容易看到正在发生的事情,并且在那里更快。 然后,您可以运行:

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

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

使用 Mongo 对聚合框架 [AF] 中的路由求和是相当困难的。 自动对焦比地图缩减更快、更容易使用[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);
};

但是,必须使用单独的功能来美化它。

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

最好查理