Assigning a real-time ranking/order/index to a sorted list using the MongoDB Aggregation framework

Dan Delaney
Dan Delaney
Published in
3 min readJan 20, 2017

--

After a couple hours of Googling and experimentation, I figured out a solution to a weird problem I was having with MongoDB, so I’d like to share that in case anyone else is having the same issue.

I was working on a new algorithm for Players’ Lounge where we needed to display the overall ranking on the website of each player on their profile page. Players are ranked by their Players’ Lounge Rating (or PLR), which can change at any time as matches are played on the site. If I were displaying all users in order of their ranking, this would be easy - I could just grab all users, sort by PLR (saved into their user document), and display in order on some sort of Leaderboard. This was different though - I had to pick out an individual user’s rank out of everyone else, relatively quickly, without pulling out all users and looping through until I found the user in question.

Essentially, I needed to assign some sort of ranking/order number to a sorted list of users. Let me explain my solution using MongoDB’s aggregation framework.

Each user has a PLR which ranges from 1 to 100. Here’s an example of a user document in the database:

{
"_id": 1234567890,
"username":"dandelaney8",
"plr": 78,
...
}

So the first order of business is to sort the users by their “plr” value, highest to lowest. Here’s the pipeline operation for the aggregation framework:

{
"$sort": {
"plr": -1
}
}

Once sorted, I then used $push to add the information about each user into an array. This is key in the next step, when we need to get an array key for each user, but we’ll come back to that. Here’s the pipeline operation:

{
"$group": {
"_id": false,
"users": {
"$push": {
"_id": "$_id",
"username": "$username",
"plr": "$plr"
}
}
}
}

At this stage, the users are grouped into a “users” array. Once in this form, we can break it back out using the $unwind operator which gives an individual document for each element in the specified array. This brings the information back to a similar form it was before the previous step, BUT (since MongoDB 3.2) there’s a feature of $unwind that allows you to keep track of what array index each element had in the specified array. Since the array was already sorted by rating, this is key for us to figure out overall ranking.

Here’s the pipeline operation to complete this step:

{
"$unwind": {
"path": "$users",
"includeArrayIndex": "ranking"
}
}

After this step, each element will hold a “ranking” field, which will be 1 less than their ranking overall. After this I simply grabbed the specific user I wanted to know the ranking of using the $match operator:

{
"$match": {
"users._id": 1234567890
}
}

Once this is done, we just grab the “ranking” value from the result and add 1 to it. With that, we have successfully grabbed an individual user’s overall rank on the site, in real time.

I hope this method is of use to someone else out there that runs into a similar problem! Feel free to reach out to me on Twitter (@dan_delaney8) if you have any questions about this.

--

--