Beertastic
Beertastic

Reputation: 711

Mongodb - getting highest result from nested item

I need to get a users top score from a variety of score. Here's a sample mongo dataset:

{
    _id: "peter",
    name: "Peter Griffin",
    scores : [
        {
           event: "Peter Copter race",
           score: 100,
        },
        {
           event: "World domination",
           score: 0,
        },
        {
           event: "Listening",
           score: 10,
        }
    ]
},
{
   _id: "stewie",
   name: "Stuart Griffin",
   scores : [
       {
           event: "Peter Copter race",
           score: 0,
       },
       {
           event: "World domination",
           score: 1000,
       },
       {
           event: "Listening",
           score: 100,
       }
   ]
}

I want to get the top score for each user, returning s'thing like

[{"_id": "peter", "top_score" : 100}, {"_id": "stewie", "top_score" : 1000}]

Here's what I've got so far:

   db.famguyMongo.find({ "scores": { $exists: true } }).forEach(function(famMember) {
    var newScore = 0
    famMember.scores.forEach(function(getScore) {
        newScore = {$max: getScore.score}
    });
    print(newScore )
    newScore = 0
});

As you can see I'm trying to set an var to be checked each time.. I've also tried this:

db.famguyMongo.find({ "scores": { $exists: true } }).forEach(function(famMember) {
    var newScore = 0
    famMember.scores.forEach(function(getScore) {
        newScore = { $cond: { $gte: [getScore.score, newScore] } getScore.score, 0 }
    });
    print(newScore)
    newScore = 0
});

Upvotes: 3

Views: 346

Answers (2)

ʰᵈˑ
ʰᵈˑ

Reputation: 11375

I'd recommended using chridam's answer, but if you wanted a JavaScript alternative;

var objScores = {}; //Instantiate object
db.test.find({ "scores": { $exists: true } }).forEach(function(famMember) {
    var newScore = 0;
    objScores[famMember._id] = 0; //Set default for each person as score 0
    famMember.scores.forEach(function(getScore) { //loop through persons score
        if( getScore.score > objScores[famMember._id] ) {
            objScores[famMember._id] = getScore.score; //Record the highest score
        }
    });
});
print( objScores ); //print output

And output would be;

{
    "peter" : 100,
    "stewie" : 1000
}

Upvotes: 1

chridam
chridam

Reputation: 103445

You could try MongoDB's aggregation framework to calculate the maximum score per user. In your aggregation pipeline, the first step is to deconstruct the scores array field from the input documents using the $unwind operator to output a document for each element that you can then aggregate on later down the pipeline. Each output document replaces the array with an element value. The next pipeline stage will be the $group operator where you can then calculate the highest score using the $max aggregation operator on the scores.score field. The final step is the $project operation where you can reshape each document in the stream to display the required fields in the appropriate format:

db.collection.aggregate([
    {
        "$unwind": "$scores"
    },
    {
        "$group": {
            "_id": "$_id",
            "highest_score": { "$max": "$scores.score" }
        }
    },
    {
        "$project": {
            "_id": 0,
            "name": "$_id",
            "highest_score": 1
        }
    }
]);

Result:

/* 0 */
{
    "result" : [ 
        {
            "highest_score" : 1000,
            "name" : "stewie"
        }, 
        {
            "highest_score" : 100,
            "name" : "peter"
        }
    ],
    "ok" : 1
}

Upvotes: 3

Related Questions