Question

Here is an idea of what my mongodb document looks like (json format of course)

[
    {
        _id: 50, 
        team_name: 'bulls',
        players: [
            { 
                _id: 100, 
                player_name: 'Jokim'
            }
        ]
    }
] 

So when the route api/teams/50/players/100 hits I want to serve Jokim. I am trying to read other posts on mongoose nested documents, but I can't seem to find an answer for what I am looking for here. I simply want to find the document, I already have the route setup.

models.Team.find({'players._id':req.params.id }, function(err, player) {
    if (err) {
        res.json({error: 'player not found.'});
    } else {
        res.json(player);
    }
});

This is my SCHEMA

var Team = new Schema({
    team_name:   { type: String },
    players: [
        {
            player_name:  { type: String },
            points:       { type: Number },
            made_one:     { type: Number },
            made_two:     { type: Number },
            made_three:   { type: Number },
            missed_one:   { type: Number },
            missed_two:   { type: Number },
            missed_three: { type: Number },
            percentage:   { type: Number },
            assists:      { type: Number },
            rebounds:     { type: Number },
            steals:       { type: Number },
            blocks:       { type: Number },
            fouls:        { type: Number },  
            feed:         { type: String },
            facebook_id:  { type: Number }
        }
    ],
    points:       { type: Number },
    made_one:     { type: Number },
    made_two:     { type: Number },
    made_three:   { type: Number },
    missed_one:   { type: Number },
    missed_two:   { type: Number },
    missed_three: { type: Number },
    percentage:   { type: Number },
    assists:      { type: Number },
    rebounds:     { type: Number },
    steals:       { type: Number },
    blocks:       { type: Number },
    fouls:        { type: Number },  
    feed: { type: String }
});
Was it helpful?

Solution

When you query Team, the docs that are returned by Mongoose have the same structure as your schema, so you need to pull out the player you're looking for from that structure.

Use the $ positional projection operator to identify the first player matched in your query so that your result only contains that player in the players array field:

models.Team.findOne(
    {'players._id': req.params.id }, 
    {'players.$': 1}, 
    function(err, team) {
        if (err || !team || !team.players || !team.players.length) {
            res.json({error: 'player not found.'});
        } else {
            res.json(team.players[0]);
        }
    }
);

Note that findOne is used instead of find because you're only looking to find the single team containing that player.

OTHER TIPS

So probably the approach you are looking for uses the aggregate method. Of course you are going to extract your team_id and player_id values from your route:

models.Team.aggregate(
    [
        // Makes sense to match the document
        { "$match": { 
            "_id": team_id,
            "players._id": player_id
        }},

        // Unwind the players array
        { "$unwind": "$players" },

        // Match the player only
        { "$match": { "players._id": player_id } },

        // Just project the player
        { "$project": { "_id": 0, "player": "$players" } }
    ],
    function(err,docs) {

       // do work here
    }
);

So while positional projection would allow you to project a single array element that was matching as part of the parent document, the projection form in the aggregation pipeline allows you to completely re-shape to just contain the matching array element in the response.

For further information on aggregation, see the operator reference in the manual.

But for a basic example with the document you present:

db.teams.aggregate([
    { "$match": { 
        "_id": 50,
        "players._id": 100
    }},
    { "$unwind": "$players" },
    { "$match": { "players._id": 100 } },
    { "$project": { "_id": 0, "player": "$players" } }
])

Gives the result:

{ "player" : { "_id" : 100, "player_name" : "Jokim" } }
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top