Reputation: 6512
I am working on gaming application will have thousands of users.
My current aggregate pipeline output
[
{
"USERID": "U0004",
"Total_Points": 10
},
{
"USERID": "U0001",
"Total_Points": 8
},
{
"USERID": "U0006",
"Total_Points": 8
},
{
"USERID": "U0002",
"Total_Points": 2
},
{
"USERID": "U0003",
"Total_Points": 1
},
{
"USERID": "U0005",
"Total_Points": 1
}
]
Expected Output
[
{
"USERID": "U0004",
"Total_Points": 10,
"Rank": 1
},
{
"USERID": "U0001",
"Total_Points": 8,
"Rank": 2
},
{
"USERID": "U0006",
"Total_Points": 8,
"Rank": 2
},
{
"USERID": "U0002",
"Total_Points": 2,
"Rank": 4
},
{
"USERID": "U0003",
"Total_Points": 1,
"Rank": 5
},
{
"USERID": "U0005",
"Total_Points": 1,
"Rank": 5
}
]
Which is the best and most optimised way to get the output from mongodb aggregate with the leaderboard rank concept.
I am using mongoose mongodb connection in my project.
I can manipulate data using a for loop but it seems rather inefficent.
Thanks in advance.
Comments are greatly appreciated.
Below is image of example leader board
Upvotes: 3
Views: 2255
Reputation: 131
you can do this with aggregate. you can check my answer on a similiar question (this seems like a duplicate): https://stackoverflow.com/a/62402340/6134150
To summarize, in the aggregate pipeline you can: 1) sort documents by score or points 2) push the players in a new array while preserving the index, this index can act as the rank 3) find the top n players from the newly formed array and/or the player in question, this player/s will have a field depicting the player's rank.
[{
"$sort": {
"wins": -1
}
},
{
"$group": {
"_id": false,
"players": {
"$push": {
"_id": "$_id",
"playerId": "$playerId",
"wins": "$wins"
}
}
}
},
{
"$unwind": {
"path": "$players",
"includeArrayIndex": "rank"
}
},
{
"$match": {
"players._id": 1234567890
}
}]
Upvotes: 0
Reputation: 4678
Don't think you can do this with the aggregate.
You can do it in one line with javascript. Sort your array then map to get the rank with the index.
a = [
{
"USERID": "U0004",
"Total_Points": 10
},
{
"USERID": "U0001",
"Total_Points": 8
},
{
"USERID": "U0006",
"Total_Points": 8
},
{
"USERID": "U0002",
"Total_Points": 2
},
{
"USERID": "U0003",
"Total_Points": 1
},
{
"USERID": "U0005",
"Total_Points": 1
}
]
a = a.sort(function(a, b){
return b.Total_Points - a.Total_Points;
}).map(function(e, i){
e.Rank = (i + 1);
return e;
});
{USERID: "U0004", Total_Points: 10, Rank: 1}
{USERID: "U0001", Total_Points: 8, Rank: 2}
{USERID: "U0006", Total_Points: 8, Rank: 3}
{USERID: "U0002", Total_Points: 2, Rank: 4}
{USERID: "U0003", Total_Points: 1, Rank: 5}
{USERID: "U0005", Total_Points: 1, Rank: 6}
Upvotes: 1
Reputation: 2173
Here's what I came up with.
var users = [
{ "USERID": "U0001", "Total_Points": 8 },
{ "USERID": "U0004", "Total_Points": 10 },
{ "USERID": "U0003", "Total_Points": 1 },
{ "USERID": "U0006", "Total_Points": 8 },
{ "USERID": "U0002", "Total_Points": 2 },
{ "USERID": "U0005", "Total_Points": 1 }
];
users.sort((a, b) => b.Total_Points - a.Total_Points);
for (let i = 0; i < users.length; i++) {
let totalPoints = users[i].Total_Points;
let usersWithRank = users.filter(user => user.Total_Points === totalPoints);
for (let user of usersWithRank) {
user.Rank = i + 1;
}
i += usersWithRank.length - 1;
}
console.log(users);
Upvotes: 0