Máte dva možné způsoby, jak může uživatel sledovat jiného uživatele; buď přímo, nebo nepřímo prostřednictvím skupiny, v takovém případě uživatel přímo následuje skupinu. Začněme ukládáním těchto přímých vztahy mezi uživateli a skupinami:
{
_id: "userA",
followingUsers: [ "userB", "userC" ],
followingGroups: [ "groupX", "groupY" ]
}
Nyní budete chtít umět rychle zjistit, které uživatele uživatel A přímo nebo nepřímo sleduje. Chcete-li toho dosáhnout, můžete denormalizovat skupiny, které uživatel A sleduje. Řekněme, že skupina X a Y jsou definovány následovně:
{
_id: "groupX",
members: [ "userC", "userD" ]
},
{
_id: "groupY",
members: [ "userD", "userE" ]
}
Na základě těchto skupin a přímých vztahů uživatele A můžete generovat předplatné mezi uživateli. Původ(y) předplatného jsou uloženy u každého předplatného. Pro ukázková data by předplatná vypadala takto:
// 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" ] }
Tato předplatná můžete vygenerovat docela snadno pomocí volání map-reduce-finalize pro jednotlivého uživatele. Pokud je skupina aktualizována, stačí znovu spustit map-reduce pro všechny uživatele, kteří skupinu sledují, a odběry budou znovu aktuální.
Zmenšení mapy
Následující funkce redukce mapy vygenerují předplatné pro jednoho uživatele.
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);
}
Poté můžete spustit map-reduce pro jednoho uživatele zadáním dotazu, v tomto případě pro userA
.
db.users.mapReduce(map, reduce, { finalize: finalize, query: { _id: "userA" }})
Několik poznámek:
- Před spuštěním map-reduce pro daného uživatele byste měli smazat předchozí odběry uživatele.
- Pokud aktualizujete skupinu, měli byste spustit map-reduce pro všechny uživatele, kteří skupinu sledují.
Měl bych poznamenat, že tyto funkce redukce mapy se ukázaly být složitější, než jsem měl na mysli , protože MongoDB nepodporuje pole jako návratové hodnoty funkcí snížení. Teoreticky by funkce mohly bylo mnohem jednodušší, ale nebylo by kompatibilní s MongoDB. Toto složitější řešení však může být použito k redukci všech users
vyzvednutí v jediném hovoru, pokud někdy budete muset.