Anda memiliki dua kemungkinan cara di mana pengguna dapat mengikuti pengguna lain; baik secara langsung, atau tidak langsung melalui grup, dalam hal ini pengguna langsung mengikuti kelompok. Mari kita mulai dengan menyimpan langsung ini hubungan antara pengguna dan grup:
{
_id: "userA",
followingUsers: [ "userB", "userC" ],
followingGroups: [ "groupX", "groupY" ]
}
Sekarang, Anda ingin dapat dengan cepat mengetahui pengguna mana yang diikuti pengguna A, baik secara langsung maupun tidak langsung. Untuk mencapai ini, Anda dapat mendenormalisasi grup yang diikuti pengguna A. Misalkan grup X dan Y didefinisikan sebagai berikut:
{
_id: "groupX",
members: [ "userC", "userD" ]
},
{
_id: "groupY",
members: [ "userD", "userE" ]
}
Berdasarkan grup ini, dan hubungan langsung yang dimiliki pengguna A, Anda dapat membuat langganan antar pengguna. Asal langganan disimpan dengan setiap langganan. Untuk contoh data langganan akan terlihat seperti ini:
// abusing exclamation mark to indicate a direct relation
{ ownerId: "userA", userId: "userB", origins: [ "!" ] },
{ ownerId: "userA", userId: "userC", origins: [ "!", "groupX" ] },
{ ownerId: "userA", userId: "userD", origins: [ "groupX", "groupY" ] },
{ ownerId: "userA", userId: "userE", origins: [ "groupY" ] }
Anda dapat membuat langganan ini dengan cukup mudah, menggunakan panggilan pengurangan peta untuk pengguna individu. Jika grup diperbarui, Anda hanya perlu menjalankan kembali pengurangan peta untuk semua pengguna yang mengikuti grup dan langganan akan diperbarui kembali.
Pengurangan peta
Fungsi pengurangan peta berikut akan menghasilkan langganan untuk satu pengguna.
map = function () {
ownerId = this._id;
this.followingUsers.forEach(function (userId) {
emit({ ownerId: ownerId, userId: userId } , { origins: [ "!" ] });
});
this.followingGroups.forEach(function (groupId) {
group = db.groups.findOne({ _id: groupId });
group.members.forEach(function (userId) {
emit({ ownerId: ownerId, userId: userId } , { origins: [ group._id ] });
});
});
}
reduce = function (key, values) {
origins = [];
values.forEach(function (value) {
origins = origins.concat(value.origins);
});
return { origins: origins };
}
finalize = function (key, value) {
db.subscriptions.update(key, { $set: { origins: value.origins }}, true);
}
Anda kemudian dapat menjalankan pengurangan peta untuk satu pengguna, dengan menentukan kueri, dalam hal ini untuk userA
.
db.users.mapReduce(map, reduce, { finalize: finalize, query: { _id: "userA" }})
Beberapa catatan:
- Anda harus menghapus langganan pengguna sebelumnya, sebelum menjalankan pengurangan peta untuk pengguna tersebut.
- Jika Anda memperbarui grup, Anda harus menjalankan pengurangan peta untuk semua pengguna yang mengikuti grup tersebut.
Saya harus mencatat bahwa fungsi pengurangan peta ini ternyata lebih kompleks daripada yang saya pikirkan , karena MongoDB tidak mendukung array sebagai nilai pengembalian dari fungsi pengurangan. Secara teori, fungsi bisa menjadi jauh lebih sederhana, tetapi tidak akan kompatibel dengan MongoDB. Namun, solusi yang lebih kompleks ini dapat digunakan untuk memetakan-mengurangi seluruh users
koleksi dalam satu panggilan, jika perlu.