How would you model a friendship relationship in MongoDB?

I know a lot of answers have been given that advise embedding an array of references to other users, but what all answers of this sort neglect is that friendship is a two-way relationship and that when Alice is Bob's friend, Bob is automatically Alice's friend. I'm not modeling followers.

So I don't want to keep two references every time a new user enters the system. I need a model that takes into account the two-way nature of the relationship.

I was thinking a collection of Friendship 'edges' where each document holds a reference to the two users. Wanted to know if literature regarding this already exists out there.

Upvotes: 2

Views: 3333

Answers (1)

Philipp
Philipp

Reputation: 69773

MongoDB is generally not a good fit for modeling graph relations. There are specialized graph databases which excel at this task.

However, when you do not want to add another database technology to the mix, I would recommend to create a new collection frienships and model each friend-relation as a document with an array of two entries, each of them an object with the abbreviated information about one of the users which you need to display an entry in your friend-lists:

{
    friendship: [
       {
            id:123,
            name: "Bob",
            avatar: "Bob.jpg"
       },
       {
            id:456,
            name: "Alice",
            avatar: "Alice.jpg"
       }
    ]
}

The reason for duplicating information from the user documents in the friendship documents is to avoid a second query to the users collection to obtain all the data for displaying a users friend-list. MongoDB can not do JOINs can only perform JOINs on unsharded collections, so you should avoid spreading data you need for a specific use-case over multiple collections, even when it means that you create redundancies. Otherwise you would need to perform multiple queries one after another, which slows down the response time of your application significantly.

When you want to get the friend-list of user 123, you would perform a db.friendships.find({"friendship.id", 123}) (an index on friendship.id will improve performance) and then receive a list of documents where Bob is either the first or the second friend.

You would then iterate these documents and output the short info of the array-entry which is not user 123.

Alternatively you can filter out the Bob-entries on the database with an aggregation pipeline. Use the $match query above, $unwind the friendship-array and then $match those documents where id is not 123. This would be a trade-off: You conserve bandwidth at the expense of CPU load on the database server.

To query if a friendship-relation already exists, use:

db.friendships.find( { $and: [ 
       { "friendship.id": 123 }, 
       { "friendship.id": 456 } 
    ] } ).count();

Upvotes: 6

Related Questions