Reputation: 41
How do I populate an embedded and nested object from another collection?
User Schema:
const walletSchema = new mongoose.Schema(
{
name: {
type: String,
required: true
},
balance: {
type: Number,
required: true
}
}
);
const userSchema = new mongoose.Schema(
{
username: {
type: String,
required: true
},
wallets: {
type: [walletSchema],
default: []
}
}
);
const User = mongoose.model('User',userSchema);
export default User;
Expense schema:
const expenseSchema = new mongoose.Schema(
{
userId: {
type: mongoose.Schema.Types.ObjectId,
ref: 'User',
required: true
},
walletId: { // \<-- will store the respective wallet id of the User
type: mongoose.Schema.Types.ObjectId,
ref: 'User.walets.walletId', // \<-- THE PROBLEM
required: true
},
amount: {
type: Number,
required: true
}
);
const Expense = mongoose.model('Expense',expenseSchema);
export default Expense;
I embedded the wallets in the User object as I don't expect the user to have a thousand wallets and I will be accessing the wallets property frequently. Therefore, I didn't separate it into its own collection.
Is there a way to populate this field? Or do I have to do it manually? The user's expenses will be shown in a table displaying each expense's information
Expected result:
[{
_id: new ObjectId('666418ed4e1b1ffa8ec19232'),
userId: {
username: 'JohnDoe',
wallets:[{
_id: new ObjectId('666323b5fb1a3011bdc3ec29'),
name: 'Savings',
amount: 1234
}]
},
walletId: {
_id: new ObjectId('666323b5fb1a3011bdc3ec29'),
name: 'Savings',
amount: 1234
},
amount: 200,
}]
Upvotes: 0
Views: 88
Reputation: 8027
You can use an aggregation without changing your schema like so:
const expense = await Expense.aggregate([
{
// Optional Stage: Match an expenses document
$match: {
"_id": req.params.id //< or from wherever you get an id
}
},
{
//Populate the expenses document with user details
$lookup: {
from: "users",
localField: "userId",
foreignField: "_id",
as: "userId"
}
},
{
// Turn the new userId array field into just an object
$unwind: "$userId"
},
{
// Filter the wallets array to just get the one that matches walletId
$set: {
"walletId": {
$filter: {
input: "$userId.wallets",
as: "wallet",
cond: {
$eq: [
"$$wallet._id",
"$walletId"
]
}
}
}
}
},
{
// Turn the new array into just an object
$unwind: "$walletId"
}
])
See HERE for a working example.
Upvotes: 0