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'.
Your Note schema looks fine. You have an
itemsproperty that has been correctly defined as an array ofmongoose.Schema.Types.ObjectIdwith the correspondingref:'Note'so that looks textbook.My advice would be to explicitly pass the
pathandmodelproperties to thepopulatemethod and adoptasync/awaitpattern like so:Note: please ensure that there are
ObjectIdvalues in theitemsarray. It is not clear from your compass screenshot because theitemsarray is collapsed in the display. Lastly, your instrincts are correct and you don't need virtuals for this.