Instead of storing the ranks in the db, I would compute it instead. Something like:
const myScore = Meteor.user().score;
const rank = 1 + Meteor.users.find({ score: { $gt: myScore }}).count();
Since your rank is equal to the number of people with a higher score + 1
With an index on score, this should be almost as fast as having it in the document, and you don’t have the write overhead of updating each individual user in separate db calls
To make this work you might need to publish a subset of the users collection (ie. just the scores):
Meteor.publish('scores' function () {
return Meteor.users.find({}, { fields: { score: 1 } });
});
And query that on the client
OR a fake collection that just stores your rank:
// Server
Meteor.publish("myRank", function() {
const user = Meteor.user();
if (!user) return;
const myScore = user.score;
let rank = 1;
let initializing = true;
// `observeChanges` only returns after the initial `added` callbacks have run.
// Until then, we don't want to send a lot of `changed` messages—hence
// tracking the `initializing` state.
const handle = Meteor.users.find({ score: { $gt: myScore } }).observeChanges({
added: () => {
// for each person with a higher score, increment the rank
rank = rank + 1;
if (!initializing) {
this.changed("ranks", this.userId, { rank });
}
},
removed: () => {
// When the list of people with a higher rank shrinks, decriment the rank
rank = rank - 1;
if (!initializing) {
this.changed("ranks", this.userId, { rank });
}
},
});
initializing = false;
// Add the initial computed rank
this.added("ranks", this.userId, { rank });
this.ready();
// Stop observing the cursor when the client unsubscribes. Stopping a
// subscription automatically takes care of sending the client any `removed`
// messages.
this.onStop(() => handle.stop());
});
// Client
const ranks = Meteor.collection('ranks');
Meteor.subscribe('myRank', function () {
const myRank = rank.findOne({ _id: Meteor.userId })
});
Which will be totally reactive and only send the client data about your rank.
The second one will be more efficient for many users (don’t need to send everyone’s scores to determine your rank)
EDIT: Just realised that the publication won’t re-run when the user’s score changes… In which case you’d need to observe that query as well and invalidate the main one on a change and re-run the cursor. That might be a bit intensive