'Mongoose populating refs inside refs
I'm trying to save and retrieve a collection of game matches with associated games, and who played in it. My schema looks like this,
const TournamentSchema = new mongoose.Schema({
matches: [{
games: [{
type: mongoose.Schema.Types.Mixed,
ref: 'Game',
players: [{
type: mongoose.Schema.Types.ObjectId,
ref: 'Player',
}],
}],
}],
});
This is what the object in the database looks like,
{
"__v": 0,
"_id": "5a50ed6b267ddd32c4523327",
"matches": [
{
"_id": "5a50ed6b267ddd32c4523328",
"games": [
{
"players": [
{ "_id": "5a4fa908d9d55465ac4fdbe6" },
{ "_id": "5a50cf3d09176c2bb0f98fe1" }
],
"_id": "5a498918ffc6220edbe8a403"
},
{
"players": [
{ "_id": "5a50cf5609176c2bb0f98fe2" },
{ "_id": "5a50cf6009176c2bb0f98fe3" }
],
"_id": "5a50cf9007c2bb0c73f3783a"
}
]
}
],
}
I'm trying to retrieve it like this,
async function list(req, res, next) {
logger.log('info', 'Incoming request to retrieve all tournaments');
const tournaments = await Tournament.find().populate('matches.games.players');
return res.json(tournaments);
}
However, what I get from the database is the same as what was saved. i.e the refs don't get resolved. If I change type: Mixed from games to type: ObjectId it wont persist players, but populate will resolve games. How do I work with refs inside refs?
As requested this is what my Game schema looks like,
const GameSchema = new mongoose.Schema({
name: {
type: String,
unique: true,
required: true,
lowercase: true,
},
scoring: {
type: Object,
required: true,
rank: {
first_place: Number,
second_place: Number,
third_place: Number,
},
},
max_num_players: Number,
min_num_players: Number,
}, { runSettersOnQuery: true });
Each Game can have different scoring percentage per rank. For example for Counter Strike if you were first place, you would get 100% of points, second 80%, third 50%. However, for League of Legends first place would be 85%, second 60%, and third 50%.
Solution 1:[1]
I think the problem is that you name "game" both the game definition in the game collection and the tournament game (which actually is game + players).
I would write the schema like this (not tested):
const TournamentSchema = new mongoose.Schema({
matches: [{
games: [{
game: {
type: mongoose.Schema.Types.ObjectId,
ref: 'Game',
},
players: [{
type: mongoose.Schema.Types.ObjectId,
ref: 'Player',
}],
}],
}],
});
And you would query like this:Tournament.find().populate('matches.games.game matches.games.players')
I still find unclear what the Game schema contains and why the Game itself does not have a list of players.
Solution 2:[2]
Maybe it's a bit late, but I'll share what I've understood through researching of a similar case of nested populating (as the title says). In the documentation it says that you could solve population across multiple levels in this way: We have an Schema:
const CustomerSchema = new Schema({
orders: [
{
type: Schema.Types.ObjectId || null,
ref: "Order",
},
],
(...)
})
But the orders at the same time have products refs:
const OrderSchema = new Schema({
products: {
type: [{ type: Schema.Types.ObjectId, ref: "Product" }],
},
(...)
});
Now you could do a deep population like this:
const user = await CustomerModel.findById(id)
.populate("user", "-password")
.populate({ path: "orders", populate: { path: "products" } }) <---
.exec();
Hope this might help someone with a similar case.
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | XCS |
| Solution 2 | Luciano Cavallo |
