I'm making an app that uses the classic "follow" mechanism (the one used by Twitter and a lot of other apps around the web). I'm using MongoDB. My system has a difference, though: an user can follow groups of users. That means that if you follow a group, you'll automatically follow all the users who are members of that group. Of course users can belong to more than one group.
This is what I came up with:
- when user A follows user B, id of user B gets added to an embedded array (called
following
) in user A's document - for unfollowing, I remove the id of the followed user from the
following
array groups work in the same way: when user A follows group X, id of group X gets added to the
following
array. (I actually add aDBRef
so I know if the connection is to an user or a group.)when I have to check if user A follows group X, I just search for the group's id in user A's following array.
- when I have to check if user A follows user B, things gets a little trickier. Each user's document has an embedded array listing all the groups the user belongs to. So I use an
$or
condition to check if user A is either following user B directly or via a group. Like this: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.)