Всъщност можете да подадете произволен обект на втория параметър на извикването за излъчване. Това означава, че можете да се възползвате от това и да съхраните потребителския идентификатор в него. Например функцията ви за карта може да изглежда така:
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:
// http://stackoverflow.com/questions/18912/how-to-find-keys-of-a-hash
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 }});