I am trying to model the concept of games
where teams
of players
compete against each other in MongoDB.
I have two collections: players
and games
.
This is how a document in games
looks like.
{
"_id": { "$oid": "1" },
"teams": [
{
"players": [
{
"player": { "$oid": "2" },
"score": 500,
},
{
"player": { "$oid": "3" },
"score": 550,
}
]
},
{
"players": [
{
"player": { "$oid": "4" },
"score": 500,
},
{
"player": { "$oid": "5" },
"score": 550,
}
]
}
]
}
Here's the task: given a player ID I want to find all games in which this player participated.
What I've tried:
db.games.find( { "teams.players.player._id": "2" } )
However, this does not return anything.
By the way, I'm using Mongoose with the following schema:
playerSchema = Schema
player: { type: Schema.ObjectId, ref: 'Player' }
score: { type: Number }
teamSchema = Schema
players: [ playerSchema ]
gameSchema = Schema
teams: [ teamSchema ]
with the following CoffeeScript query:
Game.find 'teams.players.player._id': playerId
which returns no results for any player ID.
In your document:
"players": [
{
"player": { "$oid": "4" },
"score": 500,
},
{
"player": { "$oid": "5" },
"score": 550,
}
]
The player
field in the embedded collection of players
is a BSON Id (i.e. it looks something like ObjectId("4e208e070347a90001000008")
), so I think you should structure your query like so:
db.games.find( { "teams.players.player": ObjectId("2") } )
Note, I've dropped the _id
-- provided that works in a mongo console, then I suspect the Coffee query will be similar (drop the _id
portion).
I used your feedback to restrict this example to a home and away team. Remember that Mongo is not a relational database and using relationships is often an indication of a relational-database mindset. Duplication is the key.
Instead of references from the game to specific player documents, I have stored the name of the player. I assume this is an immutable unique index. The player has been given a games array containing references to each game he played in. This is actually quite bad as we have to query again and populate this array. It would be better to store a name here as well, but since I don't know your situation I'm not sure if a game has an immutable representable name.
This basic idea has to be improved with error checking (e.g. middleware) but I leave that up to you.
// Schemas
var playerSchema = new Schema({
name: {type: String, index: true, unique: true},
games: {type: [Schema.ObjectId], ref: 'Game'}
});
var gameSchema = new Schema({
homeTeam: [{player: {name: String}, score: Number}]
awayTeam: [{player: {name: String}, score: Number}]
});
// Models
var Player = mongoose.model('Player', playerSchema);
var Team = mongoose.model('Team', teamSchema);
var Game = mongoose.model('Game', gameSchema);
// Middleware
gameSchema.post('save', function (game) {
var addMatchToPlayer = function(name) {
Player.findOne({name: name}, function(err, player) {
if (!err && player && player.games.indexOf(game._id) === -1) {
player.games.push(game._id);
player.save();
}
});
}
for (var i = 0; i < game.homeTeam.length; i++) {
addMatchToPlayer(game.homeTeam[i].name);
}
for (var i = 0; i < game.awayTeam.length; i++) {
addMatchToPlayer(game.awayTeam[i].name);
}
});
// Queries
Player.findOne({name: 'Roel van Uden'}).populate('games').exec(function (err, player) {
});