好的,我想出了一些可能会实现你想要的功能的东西。请注意,由于我不完全确定您的架构(考虑到您的示例显示在类型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实现的了解而得出的。