MapReduce с помощью команды Не удалось декодировать документ с сервера

у меня есть Test база данных с коллекцией под названием collection:

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

Я хочу записать веб-активность и использовать 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

Решение

Не слишком уверенный, если бы я рекомендовал MapReduce для этого типа операций, сказал бы, что структура агрегации будет выполнять агрегацию с большей производительностью, поскольку все операции выполняются в собственном коде, не вызывая код для JavaScript для компиляции (в случае MapReduce).

С операцией агрегирования все, что вам нужно, это $group трубопровод, который использует $cond оператор, который позволяет преобразовать логическое условие в значение. В этом случае вы хотите указать pages в качестве ключей и их количество в качестве значения, с документами, сгруппированными по UserID,

Рассмотрите возможность выполнения следующей операции агрегирования в оболочке mongo:


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 тривиален.

0

Другие решения

Других решений пока нет …

По вопросам рекламы [email protected]