Reputation: 13422
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 }
});
Upvotes: 1
Views: 2895
Reputation: 311975
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.
Upvotes: 2
Reputation: 151122
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" } }
Upvotes: 1