使用命令MapReduce无法解码来自服务器的文档

时间:2016-06-15 10:23:48

标签: php mongodb mapreduce aggregation-framework

我有一个Test数据库,其中包含一个名为collection的集合:

 {
    "_id": "576008e5b47a6120c800418d",
    "UserID": "Paul",
    "Page": "A"
 }

我想记录webactivity并使用mapreduce来获得像

这样的结果
{
    "_id": "Paul",
    "value": {
        "A": 1,
        "B": 0,
        "C": 0,
        "D": 0,
        "E": 0
    }
}

首先,我尝试使用PHP 7 MongoDB Driver 1.1.7 MapReduce的简单代码,该命令无法从服务器解码文档:

<?php
$manager = new MongoDB\Driver\Manager("mongodb://localhost:27017");
$command = new MongoDB\Driver\Command(array(
  "mapReduce" => "collection",
  "map" => "function() { emit(this.UserID, 1); }",
  "reduce" => "function(Users, Pages){".
  "return Pages;}",
  "out" => "ex"
));
try {
  $cursor = $manager->executeCommand('Test.collection', $command);
  $response = $cursor->toArray()[0];
} catch(MongoDB\Driver\Exception $e) {
  echo $e->getMessage(), "\n";
  exit;
}
var_dump($response);
?>

任何想法都将不胜感激。

1 个答案:

答案 0 :(得分:0)

不太确定我是否会推荐MapReduce用于此类操作,会说聚合框架将以更好的性能进行聚合,因为操作都是在本机代码中完成的,而不会将代码生成JavaScript以进行编译(在MapReduce中)情况)。

使用聚合操作,您只需要一个 $group 管道,它可以使用 $cond 运算符来转换一个逻辑条件成一个值。在这种情况下,您需要将pages指定为键,将其计数指定为值,文档按UserID分组。

考虑在mongo shell中运行以下聚合操作:

db.collection.aggregate([
    {
        "$group": {
            "_id": "$UserID",
            "A": { 
                "$sum": {
                    "$cond": [
                        { "$eq": [ "$Page", "A" ] },
                        1,
                        0
                    ]
                }
            },
            "B": { 
                "$sum": {
                    "$cond": [
                        { "$eq": [ "$Page", "B" ] },
                        1,
                        0
                    ]
                }
            },
            "C": { 
                "$sum": {
                    "$cond": [
                        { "$eq": [ "$Page", "C" ] },
                        1,
                        0
                    ]
                }
            },
            "D": { 
                "$sum": {
                    "$cond": [
                        { "$eq": [ "$Page", "D" ] },
                        1,
                        0
                    ]
                }
            },
            "E": { 
                "$sum": {
                    "$cond": [
                        { "$eq": [ "$Page", "E" ] },
                        1,
                        0
                    ]
                }
            }
        }
    }
])

将产生输出:

{
    "_id": "Paul",    
    "A": 1,
    "B": 0,
    "C": 0,
    "D": 0,
    "E": 0    
}

以上示例文档。

为简洁起见,假设您预先有一个页面列表,您可以按如下方式动态生成管道:

var groupOperation = { "$group": { "_id": "$UserID" } },
    pages = ["A", "B", "C", "D", "E"];

pages.forEach(function (page){
    groupOperation["$group"][page] = {
        "$sum": {
            "$cond": [
                { "$eq": [ "$Page", page ] },
                1,
                0
            ]
        }
    };
})

db.collection.aggregate([groupOperation]);

现在,将其翻译成PHP如下:

<?php

$group_pipeline = [
    '$group' => [
        '_id' => '$UserID',
        'A' =>  [ 
            '$sum' =>  [
                '$cond' =>  [ [ '$eq' =>  [ '$Page', 'A' ] ], 1, 0 ]
            ]
        ],
        'B' =>  [ 
            '$sum' =>  [
                '$cond' =>  [ [ '$eq' =>  [ '$Page', 'B' ] ], 1, 0 ]
            ]
        ],
        'C' =>  [ 
            '$sum' =>  [
                '$cond' =>  [ [ '$eq' =>  [ '$Page', 'C' ] ], 1, 0 ]
            ]
        ],
        'D' =>  [ 
            '$sum' =>  [
                '$cond' =>  [ [ '$eq' =>  [ '$Page', 'D' ] ], 1, 0 ]
            ]
        ],
        'E' =>  [ 
            '$sum' =>  [
                '$cond' =>  [ [ '$eq' =>  [ '$Page', 'E' ] ], 1, 0 ]
            ]
        ]
    ],
];
$aggregation = $collection->aggregate([ group_pipeline ]);

?>

如果您坚持使用MapReduce,请考虑更改地图并将功能缩减为:

db.collection.mapReduce(
    function() {
        var obj = {};
        ["A", "B", "C", "D", "E"].forEach(function (page){ obj[page] = 0; } ); 
        obj[this.Page] = 1;        
        emit(this.UserID, obj);
    },
    function(key, values) {
        var obj = {};
        values.forEach(function(value) {
            Object.keys(value).forEach(function(key) {
                if (!obj.hasOwnProperty(key)){
                    obj[key] = 0;
                } 
                obj[key]++;
            });
        });
        return obj;
    },
    { "out": { "inline": 1 } }
)

给出了输出:

{
    "results" : [ 
        {
            "_id" : "Paul",
            "value" : {
                "A" : 1,
                "B" : 0,
                "C" : 0,
                "D" : 0,
                "E" : 0
            }
        }
    ]
}

将上述mapReduce操作转换为PHP是微不足道的。