Mongo Map Reduce 首次体验

12

我是第一次使用Map/Reduce,也在使用MongoDB。我有很多页面访问数据,希望通过使用Map/Reduce来理解这些数据。以下就是我的需求,但作为一个完全不懂Map/Reduce的初学者,我认为这超出了我的知识范围!

  1. 查找最近30天内所有外部访问过的页面。
  2. 然后对每个页面查找其所有的访问记录。
  3. 将所有访问记录按照转介地点分组。
  4. 对于每个转介地点,计算有多少人访问了包含特定“类型”和特定“标签”单词的页面。

数据库和集合组织如下:

$mongo->dbname->visits

一个示例文档如下:

{"url": "www.example.com", "type": "a", "refer": {"external": true, "domain": "twitter.com", "url": "http://www.twitter.com/page"}, "page": "1235", "user": "1232", "time": 1234567890}

然后我想要找到某个标签下的类型为B的文档。

{"url": "www.example.com", "type": "b", "page": "745", "user": "1232", "time": 1234567890, "tags": {"a", "b", "c"}}

我正在使用普通的Mongo PHP扩展,如果有影响请注意。


1
你的数据库结构是什么样子的?你的集合和文档是如何组织的? - Emil Vikström
好的,你提供的样本文件中没有包括“推荐”、“外部”或“标签”字段。你所建议的确实很复杂,因此你可能需要展示不止一个文件,并且需要展示所有细节。 - Gates VP
我一直在做一个和这个完全相同的东西(使用Mongo进行访问跟踪),请发一些更多的细节,也许我可以帮忙。 - Dal Hundal
更新了,这样对大家提供足够的信息了吗?谢谢。 - James
当你说“按引荐位置分组所有访问”时,你具体指的是什么?#4(计算有多少人随后访问了带有特定标签类型和特定单词的页面)也是如此……?你能否提供一个小数据集和预期的输出结果(4或5行就足够了)? - ircmaxell
显示剩余2条评论
2个回答

16

好的,我想出了一些可能会实现你想要的功能的东西。请注意,由于我不完全确定您的架构(考虑到您的示例显示在类型a中可用的refer,但在类型b中不可用(我不确定这是否是遗漏,还是因为您想按引用者查看)),因此可能无法完全奏效。无论如何,以下是我的想法:

map函数:

function() {
    var obj = {
        "types": {},
        "tags": {},
    }
    obj.types[this.type] = 1;
    if (this.tags) {
        for (var tag in this.tags) {
            obj.tags[this.tags[tag]] = 1;
        }
    }
    emit(this.refer.url, obj);
}

Reduce函数:

function(key, values) {
    var obj = {
        "types": {},
        "tags": {},
    }
    for (var i = 0; i < values.length; i++) {
        for (var type in values[i].types) {
            if (!type in obj.types) {
                obj.types[type] = 0;
            }
            obj.types[type] += values[i].types[type];
        }
        for (var tag in values[i].tags) {
            if (!tag in obj.tags) {
                obj.tags[tag] = 0;
            }
            obj.tags[tag] += values[i].tags[tag];
        }
    }
    return obj;
}
基本上,它的工作原理如下。Map函数使用refer.url作为键(基于您的描述我猜测这是这样的)。因此,最终结果将看起来像一个数组,其中_id等于refer.url(它根据url进行分组)。然后创建一个具有两个对象(types和tags)的对象。对象的原因是可以让map和reduce发出相同格式的对象。除此之外,我认为它应该相对容易理解(如果您不理解,我可以尝试解释更多)...
因此,让我们在PHP中实现它(假设$map$reduce是包含上述内容以保持简洁的字符串):
$mapFunc = new MongoCode($map);
$reduceFunc = new MongoCode($reduce);
$query = array(
    'time' => array('$gte' => time() - (60*60*60*24*30)),
    'refer.external' => true
);
$collection = 'visits';
$command = array(
    'mapreduce' => $collection,
    'map' => $mapFunc,
    'reduce' => $reduceFunc,
    'query' => $query,
);

$statsInfo = $db->command($command);

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

$stats = $statsCollection->find();

foreach ($stats as $stat) {
    echo $stats['_id'] .' Visited ';
    foreach ($stats['value']['types'] as $type => $times) {
        echo "Type $type $times Times, ";
    }
    foreach ($stats['value']['tags'] as $tag => $times) {
        echo "Tag $tag $times Times, ";
    }
    echo "\n";
}

请注意,我没有测试过这个代码。这只是我根据你的模式理解和我对Mongo及其Map-Reduce实现的了解而得出的。


$statsCollection = $db->selectCollection($sales['result']); $sales是什么? - Toby


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接