In my opinion, there's no need for aggregation in this scenario since the data appears to be simple. It would be more efficient to keep track of "locked" and "unlocked" achievements for each user and/or game in an array.
Consider a document structured like this:
{
"_id": "hofjew233332j4",
"userId": "fhewojfw34324",
"gameId": "v3XWHHvFSHwYxxk6H",
"achievementsCount": 5,
"locked": ["One", "Two", "Four", "Five"],
"lockedCount": 4,
"unlocked": ["Three"],
"unlockedCount": 1
}
Initially, you would set up each user and game with all achievements in the "locked" array, except for one already placed in the "unlocked" array. The "count" fields indicate the number of elements in each array.
To "unlock" a new achievement, you can simply update the document to remove it from the "locked" array and add it to the "unlocked" array, while adjusting the "count" values:
Achievements.update({
"userId": "fhewojfw34324",
"gameId": "v3XWHHvFSHwYxxk6H",
"locked": "Four",
"unlocked": { "$ne": "Four" }},
{
"$push": { "unlocked": "Four" },
"$pull": { "locked": "Four" },
"$inc": {
"lockedCount": -1,
"unlockedCount": 1
}
}
)
After this update, the document will look like this:
{
"_id": "hofjew233332j4",
"userId": "fhewojfw34324",
"gameId": "v3XWHHvFSHwYxxk6H",
"achievementsCount": 5,
"locked": ["One", "Two", "Five"],
"lockedCount": 3,
"unlocked": ["Three", "Four"],
"unlockedCount": 2
}
This approach is simple to follow as each update maintains the correct data values. If you need additional information like a "percentage," you can easily calculate it using a simple aggregation:
Achievements.aggregate([
{ "$project": {
"userId": 1,
"gameId": 1,
"percentUnlocked": { "$divide": [ "$unlockedCount", "$achivementsCount" ] }
])
Alternatively, you can perform this calculation in the client-side code. This model not only simplifies real aggregations but also provides more extensive information. Additionally, calculating data dynamically is more efficient than relying on a separate process to sum up the data.