Reputation: 1
I am new to MongoDB and Mongoose and I want to perform a populate() between two documents. After consulting the documentation and searching for answers, I am turning to you for help. I have two models:
Note model
const mongoose = require('mongoose');
const noteSchema = mongoose.Schema({
_id: mongoose.Schema.Types.ObjectId,
title: {
type: String,
required: true
},
color: {
type: String,
required: true
},
position: {
type: Number,
required: true
},
isCheckBoxMode: {
type: Boolean,
required: true
},
items: [{
type: mongoose.Schema.Types.ObjectId,
ref: 'Item'
}]
}, {timestamps: true });
const Note = mongoose.model('Note', noteSchema);
module.exports = Note;
Item Model
const mongoose = require('mongoose');
const itemSchema = mongoose.Schema({
_id: mongoose.Schema.Types.ObjectId,
text: {
type: String,
required: true
},
isCompleted: {
type: Boolean,
required: true
},
noteId: {
type: mongoose.Schema.Types.ObjectId,
ref: 'Note',
required: true
},
}, {timestamps: true });
const Item = mongoose.model('Item', itemSchema);
module.exports = Item;
In this query, I would like to retrieve an array of all the items linked to the noteId, using the "items" property, which refers to the Item document. As you can see above, I have made sure to reference between the two documents, on one hand with the items property and its ref Item, and on the other hand with the NoteId property whose id corresponds to the correct note and refers to the Note document.
It's better to have an example. I am using Mongo Atlas to quickly deploy a database. I created a note with Postman whose id is "6553448ef2c06064f266f785":
and two items each referring to noteId "6553448ef2c06064f266f785":
Unfortunately, when I send my request with populate('items') to retrieve the content of my items, I receive an empty array:
router.get('/:noteId', (req, res, next) => {
const id = req.params.noteId;
Note.findById(id)
.populate('items')
.exec()
.then(doc => {
if (doc) {
res.status(200).json({
notes: doc,
});
}else {
res.status(404).json({message : 'No Valid entry found for provided ID'});
}
})
.catch(err => {
console.log(err);
res.status(500).json({error: err});
});
});
The request and response in Postman
I found another solution, using "virtuals", but it is not the most optimized solution and I am afraid of losing performance.
const mongoose = require('mongoose');
const noteSchema = mongoose.Schema({
_id: mongoose.Schema.Types.ObjectId,
title: {
type: String,
required: true
},
color: {
type: String,
required: true
},
position: {
type: Number,
required: true
},
isCheckBoxMode: {
type: Boolean,
required: true
},
}, {timestamps: true });
noteSchema.virtual('items', {
ref: 'Item',
localField: '_id',
foreignField: 'noteId'
});
noteSchema.set('toObject', { virtuals: true });
noteSchema.set('toJSON', { virtuals: true });
const Note = mongoose.model('Note', noteSchema);
module.exports = Note;
Sorry if my question is a bit stupid, but I searched for a long time and couldn't find any solutions. thank you in advance for your help
I tried to link the two documents following the Mongoose documentation as well as other Stack Overflow posts, but it didn't work. Also, I set up a 'virtual' which worked and allows me to retrieve all the items, but this is not optimized. Also, when I add item with a POST route, if in my request I also add the item's id to the 'items' array of the note, the populate works. But this is also not an optimized solution. I would like to do a simple populate between two 'refs'.
Upvotes: 0
Views: 84
Reputation: 8027
Your Note schema looks fine. You have an items
property that has been correctly defined as an array of mongoose.Schema.Types.ObjectId
with the corresponding ref:'Note'
so that looks textbook.
My advice would be to explicitly pass the path
and model
properties to the populate
method and adopt async/await
pattern like so:
router.get('/:noteId', async (req, res, next) => { //< Mark as async
try{
const id = req.params.noteId;
const doc = await Note.findById(id).populate({ //< use await
path: 'items', model: Item //< pass in the path and model
});
if(!doc){
return res.status(400).json({
message : 'No Valid entry found for provided ID'
});
}else{
return res.status(200).json({
notes: doc,
});
}
}catch(err){
console.log(err);
res.status(500).json({error: err});
}
});
Note: please ensure that there are ObjectId
values in the items
array. It is not clear from your compass screenshot because the items
array is collapsed in the display. Lastly, your instrincts are correct and you don't need virtuals for this.
Upvotes: 0