Вы можете фактически передать произвольный объект по второму параметру вызова emit. Это означает, что вы можете воспользоваться этим и сохранить в нем идентификатор пользователя. Например, ваша функция карты может выглядеть так:
var mapFunc = function() {
if (this.track_redirect) {
var tempDoc = {};
tempDoc[this.track_userid] = 1;
emit(this.track_redirect, {
users_clicked: tempDoc,
total_clicks: 1
});
}
};
И ваша функция приведения может выглядеть следующим образом:
var reduceFunc = function(key, values) {
var summary = {
users_clicked: {},
total_clicks: 0
};
values.forEach(function (doc) {
summary.total_clicks += doc.total_clicks;
// Merge the properties of 2 objects together
// (and these are actually the userids)
Object.extend(summary.users_clicked, doc.users_clicked);
});
return summary;
};
Свойство users_clicked объекта сводки в основном сохраняет идентификатор каждого пользователя как свойство (поскольку у вас не может быть повторяющихся свойств, вы можете гарантировать, что в нем будут храниться уникальные пользователи). Также обратите внимание, что вы должны быть осторожны с тем фактом, что некоторые значения, передаваемые в функцию сокращения, могут быть результатом предыдущего сокращения, и приведенный выше пример кода учитывает это. Вы можете найти больше об этом поведении в документации здесь .
Чтобы получить уникальный счетчик, вы можете передать функцию финализатора, которая вызывается после завершения фазы сокращения:
var finalFunc = function(key, value) {
// Counts the keys of an object. Taken from:
// /20828/kak-naiti-klychi-hesha
var countKeys = function(obj) {
var count = 0;
for(var i in obj) {
if (obj.hasOwnProperty(i))
{
count++;
}
}
return count;
};
return {
redirect: key,
total_clicks: value.total_clicks,
unique_clicks: countKeys(value.users_clicked)
};
};
Наконец, вы можете выполнить задание сокращения карты следующим образом (измените атрибут out в соответствии со своими потребностями):
db.users.mapReduce(mapFunc, reduceFunc, { finalize: finalFunc, out: { inline: 1 }});