类似Twitter的应用程序使用MongoDB [英] Twitter-like app using MongoDB
问题描述
我正在制作一个应用程序,它使用经典的跟随机制(Twitter使用的)和网络上的许多其他应用程序。我正在使用MongoDB。
我的系统有所不同,但用户可以按照组使用用户。这意味着,如果您遵循一个群组,您将自动跟踪该群组成员的所有用户。当然用户可以属于多个组。
这是我想出的:
- 当用户A 遵循用户B ,用户B的ID被添加到嵌入式数组(称为
/ code>)在用户A的文档
- 中取消关注,我从
以下
数组中删除跟随用户的ID -
组的工作方式相同:当用户A 遵循组X ,组X的ID被添加到
以下
数组。 (我实际上添加了一个DBRef
,所以我知道连接是用户还是组。) - p>当我必须检查用户A 是否遵循组X ,我只是在用户A 的以下数组中搜索组的ID。
- 当我必须检查用户A 是否遵循用户B ,事情会变得更棘手。每个用户的文档都有一个嵌入式数组,列出用户所属的所有组。因此,我使用
$或
条件来检查用户A是直接或通过组跟随用户B.像这样:
db.users.find({'$ or':{'following.ref。$ id':$ user_id,'following.ref。$ ref','users'},{'following.ref。$ id':{'$ in':$ group_ids},'following.ref。$ ref' group'}}})
这工作正常,但我想我有几个问题。例如,如何显示特定用户的关注者列表,包括分页?我不能在嵌入式文档上使用skip()和limit()。
我可以更改设计并使用 userfollow
集合,它将执行嵌入式跟随
文档的工作。我尝试的这种方法的问题是,在我之前使用的 $或
条件下,包含相同用户的两个组之后的用户将被列出两次。为了避免这种情况,我可以使用组或MapReduce,我实际上是这样做的,但是我很乐意避免这样做,使事情更简单。也许我只需要开箱即用。或者也许我尝试错误的方法。任何人都必须做类似的事情,并提出一个更好的解决方案?
db.users.find({'$or':{'following.ref.$id':$user_id,'following.ref.$ref','users'},{'following.ref.$id':{'$in':$group_ids},'following.ref.$ref':'groups'}}})
This works fine, but I think I have a few issues. For example how do I show a list of followers for a particular user, including pagination? I can't use skip() and limit() on an embedded document.
I could change the design and use an userfollow
collection, which would do the same job of the embedded following
document. The problem with this approach, which I tried, is that with the $or
condition I used earlier, users following two groups containing the same user would be listed twice. To avoid this I could use group or MapReduce, which I actually did and it works, but I'd love to avoid this to keep things simpler. Maybe I just need to think out of the box. Or maybe I took the wrong approach with both tries. Anyone already had to do a similar thing and came up with a better solution?
(This is actually a follow-up to this older question of mine. I decided to post a new question to explain my new situation better; I hope it's not a problem.)
You have two possible ways in which a user can follow another user; either directly, or indirectly through a group, in which case the user directly follows the group. Let's begin with storing these direct relations between users and groups:
{
_id: "userA",
followingUsers: [ "userB", "userC" ],
followingGroups: [ "groupX", "groupY" ]
}
Now, you'll want to be able to quickly find out which users user A is following, either directly or indirectly. To achieve this, you can denormalize the groups that user A is following. Let's say that group X and Y are defined as follows:
{
_id: "groupX",
members: [ "userC", "userD" ]
},
{
_id: "groupY",
members: [ "userD", "userE" ]
}
Based on these groups, and the direct relations user A has, you can generate subscriptions between users. The origin(s) of a subscription are stored with each subscription. For the example data the subscriptions would look like this:
// 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" ] }
You can generate these subscriptions pretty easily, using a map-reduce-finalize call for an individual user. If a group is updated, you only have to re-run the map-reduce for all users that are following the group and the subscriptions will be up-to-date again.
Map-reduce
The following map-reduce functions will generate the subscriptions for a single user.
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);
}
You can then run the map-reduce for a single user, by specifying a query, in this case for userA
.
db.users.mapReduce(map, reduce, { finalize: finalize, query: { _id: "userA" }})
A few notes:
- You should delete the previous subscriptions of a user, before running map-reduce for that user.
- If you update a group, you should run map-reduce for all the users that follow the group.
I should note that these map-reduce functions turned out to be more complex than what I had in mind, because MongoDB doesn't support arrays as return values of reduce functions. In theory, the functions could be much simpler, but wouldn't be compatible with MongoDB. However, this more complex solution can be used to map-reduce the entire users
collection in a single call, if you ever have to.
这篇关于类似Twitter的应用程序使用MongoDB的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!