ユーザーが別のユーザーをフォローする方法は2つあります。直接、またはグループを介して間接的に、この場合、ユーザーは直接 グループをフォローします。これらの直接を保存することから始めましょう ユーザーとグループの関係:
{
_id: "userA",
followingUsers: [ "userB", "userC" ],
followingGroups: [ "groupX", "groupY" ]
}
これで、すばやくできるようになります。 ユーザーAが直接または間接的にフォローしているユーザーを見つけます。これを実現するために、ユーザーAがフォローしているグループを非正規化できます。グループXとYが次のように定義されているとします。
{
_id: "groupX",
members: [ "userC", "userD" ]
},
{
_id: "groupY",
members: [ "userD", "userE" ]
}
これらのグループと、ユーザーAの直接の関係に基づいて、サブスクリプションを生成できます。 ユーザー間。サブスクリプションのオリジンは、サブスクリプションごとに保存されます。サンプルデータの場合、サブスクリプションは次のようになります。
// 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" ] }
個々のユーザーに対してmap-reduce-finalize呼び出しを使用すると、これらのサブスクリプションを非常に簡単に生成できます。グループが更新された場合は、グループをフォローしているすべてのユーザーに対してmap-reduceを再実行するだけで、サブスクリプションが再び最新になります。
Map-reduce
次のmap-reduce関数は、単一ユーザーのサブスクリプションを生成します。
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);
}
次に、クエリ(この場合はuserA
)を指定することで、単一のユーザーに対してmap-reduceを実行できます。 。
db.users.mapReduce(map, reduce, { finalize: finalize, query: { _id: "userA" }})
いくつかの注意事項:
- そのユーザーに対してmap-reduceを実行する前に、ユーザーの以前のサブスクリプションを削除する必要があります。
- グループを更新する場合は、グループをフォローしているすべてのユーザーに対してmap-reduceを実行する必要があります。
これらのmap-reduce関数は、私が考えていたものよりも複雑であることが判明したことに注意する必要があります。 、MongoDBはreduce関数の戻り値として配列をサポートしていないためです。理論的には、関数は可能 はるかに単純ですが、MongoDBとの互換性はありません。ただし、このより複雑なソリューションを使用して、users
全体をマップリデュースすることができます。 必要に応じて、1回の呼び出しで収集します。