需要使用php在mongo中使用map reduce函数

时间:2012-06-11 11:11:22

标签: php mongodb mapreduce

在php中需要mongo的地图缩小功能

这是我的mongo结构

[_id] => MongoId Object (
    [$id] => 4fcf2f2313cfcd2454500000d
)
[id] => 454
[table] => people
[news] => Array (
    [03-06-2012] => 2
    [04-06-2012] => 3
    [05-06-2012] => 5
    [06-06-2012] => 4
)

在这里,我尝试使用以下代码对数组新闻进行求和,

    $map = new MongoCode('function() { emit(this.news, 1); }');
    $reduce = new MongoCode('function(previous, current) {
                    var count = 0;
                    for (index in current) {
                        count = count + current[index];
                    }
                    return count;
                }');

    $sales = $db->command(array(
        'mapreduce' => 'mycollection',
        'map' => $map,
        'reduce' => $reduce,
        'query' => array('table' => 'people'),
        'out'  => 'news'
    ));

    //pr($sales);exit;

    $users = $db->selectCollection($sales['result'])->find();

    foreach ($users as $user) {
        //echo "{$user['_id']} had {$user['value']} sale(s).\n";
        pr($user);
    }

pr($user)

Array
(
    [_id] => Array
    (
        [04-06-2012] => 0
        [08-06-2012] => 2
        [11-06-2012] => 6
    )

    [value] => 39540
)

我预期的值是8而不是39540。

如何更正此功能以及如何将字段和作为“新闻”的数组总和添加到原始集合(mycollection)?

我不熟悉mongo中的map reduce函数。

2 个答案:

答案 0 :(得分:3)

调用emit()时,第一个参数是您要减少的键(或者为此示例分组)。第二个参数是为该键发出的值,可以是任何值。对于您的示例,您可能意味着使用文档的ID作为关键字,在news字段中发出所有值的总和:

var map = function() {
    var total = 0;
    for (count in this.news) {
        total += count;
    }
    emit(this._id, total);
}

在这种情况下,可以使用占位符缩减功能(因为每个发出的键都是唯一的,只需要做很少的缩减):

var reduce = function(key, values) {
    var total = 0;
    values.forEach(function(v) { total += v; });
    return total;
}

但是,正如我在Google Group post中所提到的,使用纯PHP可能会更好:

$cursor = $collection->find(array(), array('news' => 1));
$cursor->snapshot();

foreach ($cursor as $document) {
    $collection->update(
        array('_id' => $document['_id']),
        array('$set' => array('sum' => array_sum($document['news']))),
        array('multiple' => false)
    );
}

使用map / reduce,您仍然需要检查其结果并更新您的记录。这将避免通过Mongo执行JavaScript的需要,并且应该更高效。如果您可以利用$ inc更新总和,因为news字段是基于每个文档修改的,那将更好。上面的代码段仍可用于初始化整个集合中的sum字段,或者如果事情与每个文档的增量不同步,则可以纠正任何偏差。

注意:请参阅文档中的snapshot(),了解上述示例中该方法调用背后的原因。

答案 1 :(得分:1)

虽然jmikola的回答让我可以直接处理mongo map reduce函数。

我正在添加此答案,以帮助未来的访问者。

以下map-reduce功能完全符合我的要求。 这将通过添加(news)将新闻字段中的所有值汇总到命令中创建的名为"out" => "news"的新集合。

地图缩减功能

$map = new MongoCode('function() {
            var total = 0;
            for (count in this.news) {
            total +=  this.news[count];
            }
            emit(this._id, {id: this.id, total: total});
        }');
$reduce = new MongoCode('function(key, values) {
            var result = {id: null, total: 0};
            values.forEach(function(v) {
            result.id = v.id;
            result.total = v.total;
             });
            return result;
        }');

$sales = $db->command(array(
    'mapreduce' => 'mycollection', // collection name
    'map' => $map,
    'reduce' => $reduce,
    'query' => array('table' => 'people'),
    "out" => "news" // new collection name
));

结果将news收集,其中总和为totalid的实际文件

<强>输出

[_id] => MongoId Object (
    [$id] => 4fd8993a13cfcd4e42000000
)
[value] => Array (
    [id] => 454
    [total] => 14
)