W rzeczywistości możesz przekazać dowolny obiekt do drugiego parametru wywołania emit. Oznacza to, że możesz z tego skorzystać i przechowywać w nim identyfikator użytkownika. Na przykład twoja funkcja mapy może wyglądać tak:
var mapFunc = function() {
if (this.track_redirect) {
var tempDoc = {};
tempDoc[this.track_userid] = 1;
emit(this.track_redirect, {
users_clicked: tempDoc,
total_clicks: 1
});
}
};
Twoja funkcja zmniejszania może wyglądać tak:
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;
};
Właściwość users_clicked obiektu Summary zasadniczo przechowuje id każdego użytkownika jako właściwość (ponieważ nie możesz mieć zduplikowanych właściwości, możesz zagwarantować, że będzie przechowywać unikalnych użytkowników). Zauważ też, że musisz uważać na fakt, że niektóre wartości przekazane do funkcji Reduce mogą być wynikiem wcześniejszej redukcji i przykładowy kod powyżej bierze to pod uwagę. Więcej informacji na temat tego zachowania znajdziesz w dokumentach tutaj .
Aby uzyskać unikalną liczbę, możesz przekazać funkcję finalizatora, która zostanie wywołana po zakończeniu fazy redukcji:
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)
};
};
Na koniec możesz wykonać zadanie redukujące mapę w ten sposób (zmodyfikuj atrybut out, aby dopasować go do swoich potrzeb):
db.users.mapReduce(mapFunc, reduceFunc, { finalize: finalFunc, out: { inline: 1 }});