Sort the most recent score of each player

Hy,

I’m developing a game where each one can play more than once, and his scores are saved in server;
what I want to do is to print only the most recent score of each player. I did something like that ( see the code below) but seem not working properly;

like you can see in the picture below; it print two scores of player"abdou"; but i want that it print only the recent one of each player

the function in publication.js

Meteor.publish(‘PlayersList’, function(){
// return PlayersList.find({}, {sort: {score: 1, name: 1}, limit:50 });
// return PlayersList.find({}, {sort: { age: -1 } });
return PlayersList.find({}, {sort: {date_created: -1}});
});

methods of collection

Meteor.methods({
‘add_player’: function(h,player,j){
PlayersList.insert({
name: h,
score: player,
userId: j,
date_created: new Date().getTime()
});
},

 'remove_player': function(h,player,j){
    PlayersList.remove({
        name: h,
        score: player,
        userId: j
        // date_created: date
    });
},
 'update_player': function(player){
    PlayersList.update({
        name: 'player',
        score: player
    });
}

});

PlayersList = new Mongo.Collection(‘players’);

edit:

To make the things more clear

in my collection I ve some thing like that ( just an example)

{name:‘A’,score: 2, date: 4456546}
{name:‘A’,score:33, date:3453454}
{name:‘A’,score:34, date: 3453}
{name:‘A’,score:0, date: 12334}
{name:‘B’,score: 2, date: 4456546}
{name:‘B’,score:33, date:3453454}
{name:‘B’,score:34, date: 3453}
{name:‘B’,score:0, date: 12334}
{name:‘C’,score: 2, date: 4456546}
{name:‘C’,score:33, date:3453454}
{name:‘C’,score:34, date: 3453}
{name:‘C’,score:0, date: 12334}

and what I want is some thing like this ( the very recent score of each ):

{name:‘A’,score: 2, date: 4456546}
{name:‘B’,score: 2, date: 4456546}
{name:‘C’,score: 2, date: 4456546}

I’m thinking for cloning the the old collection and deal with the results and put it in new one;

any help please!! I’m newbie

We do something similar with our app. I found the publish counts-by-room example from the docs helpful.

The basic premise is to use the user’s _id as the id of the published score. That way each user only ever has one record in the client-side collection.

In your publish function you check the timestamp of each score. For each user, ignore older scores and update the user’s score entry with each newer score.

To prevent going through all score’s (querying all scores from MongoDB), use a time cutoff to get only reasonably recent scores to sort through.


One common alternative way this is handled is to use something like Redis to track latest score. I believe it was actually one of the earliest popular use cases for Redis (game scoreboards).

You could keep another document in another collection which is more like an aggregated view. Whenever an insert happens in the main scores collection, issue a mongodb $max update on the aggregated view document for each user. You can then publish the aggregated view.

Problem here is the lack of transactions in mongoDB. Should the insert succeed and update break. You know the rest :smile:

@alanning Thank’s for your reply; if you give me an example I ll understand better; I try to apply to my case but no success for the moment

@int64, Thanks for reply; I thought to create anthoer collection in where I insert only the last score of each player; it’s like a filtre for the first collection; but it’s a long processus … with insert it should filtre and print the score

I would probably just $push to Player’s scores array
So it look like
Player:

    { _id: "sadasd",
      name: "whatever",
      scores: [
          1000,
          50
      ]
    }

And than
Players.find({something},{fields: {scores: {$slice: -1}}});

Ofc if you need just last_score so set it as property. Or if you need to sort based on that too

Yes, just updating a lastScore field on the user’s record will be simplest by far. It could be a single value or an array.

One consideration is that using an insert-only policy (where each action/score is a separate document) means your actions are idempotent. Nice for both scaling and tracking historical state. But insert-only means more complex publish functions and I think for this use case its probably overkill.

Make sure you keep an eye on db performance. Even with an appropriate index, there’s a reason most games use an external service like Redis to handle leaderboards.

@shock: thank’s: you means that I can push the score on collection?

@alanning; I ve used something like that

‘update_player2’: function(h,score,j){
PlayersList2.update({
name: h,
userId: j,
date_created: new Date().getTime(),
{ $push: { score: score } }
});
}

but it doesn’t seem working !! :frowning:

any example to flow please !!

Your mongo update isn’t formatted properly:

'update_player2': function(h, score, j) {
  PlayersList2.update({
    name: h,
    userId: j, 
    date_created: new Date().getTime(),
    { $push: { score: score } }    // <== problem here
  });
}

You probably want something like this:

'update_player2': function(name, score, userId) {
  var alreadyExists,
      query,
      update;

  alreadyExists = !!PlayersList2.findOne({userId: userId},
                                         {fields: {_id: 1}});

  if (!alreadyExists) {

    PlayersList2.insert({
      userId: userId,
      name: name,
      date_created: new Date().getTime(),
      scores: [score]    // also note the plural field name
    });

  } else {

    query = {userId: userId};
    update = {$push: {scores: score}};   // also note the plural field name

    PlayersList2.update(query, update);

  }
}

** EDIT: But if I was doing it I would probably just add a scores field onto the Meteor.users user record…

@alanning Thanks man for your help; finally I find the solution with replacing the previous score of each player with the recent; so, we have on the collection only the recent score of each player after each insert call;