実際には、emit呼び出しの2番目のパラメーターで任意のオブジェクトを渡すことができます。つまり、これを利用してユーザーIDを保存できます。たとえば、マップ関数は次のようになります。
var mapFunc = function() {
if (this.track_redirect) {
var tempDoc = {};
tempDoc[this.track_userid] = 1;
emit(this.track_redirect, {
users_clicked: tempDoc,
total_clicks: 1
});
}
};
また、reduce関数は次のようになります。
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プロパティは、基本的にすべてのユーザーのIDをプロパティとして格納します(重複するプロパティを持つことはできないため、一意のユーザーが格納されることを保証できます)。また、reduce関数に渡される値の一部は、以前のreduceの結果である可能性があり、上記のサンプルコードではそれが考慮されていることに注意する必要があります。上記の動作の詳細については、ドキュメントこちら をご覧ください。 。
一意のカウントを取得するために、reduceフェーズが完了したときに呼び出されるファイナライザー関数を渡すことができます。
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 }});