Hi guys,
Thanks for adding the virtual population, it's saved a lot of time! I still have an issue with the many-to-many. I want to be able to use "classics relationships" and virtuals as well.
// Populate
const User = new Schema({
name: String,
articles: [{ type: Schema.Types.ObjectId, ref: 'Article' }]
});
const Article = new Schema({
title: String,
authors: [{ type: Schema.Types.ObjectId, ref: 'User' }]
});
const article = new Articles({ title: 'My post', authors: ['1234', '5678'] });
article.save((err, article) => {...});
Article.find({}).populate('authors') .exec((err, articles) => {...}); // Work fine, with populated values
User.find({}).populate('articles') .exec((err, articles) => {...}); // Normal behavior, the articles attributes is an empty array.
With the new virtual population, I was trying to make something to not have to maintain the consistency between my many-to-many relationships. I would be able to create an article or user and spent some user's ID or article's ID when I create them and didn't have to think about the relationships direction. I tried this below but it does not work.
// Populate + virtuals
const User = new Schema({
name: String,
articles: [{ type: Schema.Types.ObjectId, ref: 'Article' }]
});
const Article = new Schema({
title: String,
authors: [{ type: Schema.Types.ObjectId, ref: 'User' }]
});
// Try to also apply virtual on existing relationships attributes
User.virtual('articles', {
ref: 'Article',
localField: '_id',
foreignField: 'authors'
});
Article.virtual('authors', {
ref: 'User',
localField: '_id',
foreignField: 'articles'
});
const user1 = new User({ name: 'John Doe' }) // _id = 1234
const user2 = new User({ name: 'Mickey Mouse' }) // _id = 5678
const article = new Articles({ title: 'My post', authors: ['1234', '5678'] });
article.save((err, article) => {...});
Article.find({}).populate('authors') .exec((err, articles) => {...}); // Work fine, with populated values
User.find({}).populate('articles') .exec((err, articles) => {...}); // I would also get the articles thanks to the virtual population
Any solution to do that?
Thanks :)
That code should work as written, I'll try it and see what's wrong later
Hmm I'd imagine the issue is due to the fact that you have a virtual field with the same name as a real field. That's not a good idea. If you want to model this sort of many-to-many, I'd recommend having putting the array on the smaller side of the many-to-many and using a virtual for the other side:
// Populate + virtuals
const User = new Schema({
name: String
});
// Since an article will likely not have hundreds of authors but an author may have hundreds of articles
const Article = new Schema({
title: String,
authors: [{ type: Schema.Types.ObjectId, ref: 'User' }]
});
// Try to also apply virtual on existing relationships attributes
User.virtual('articles', {
ref: 'Article',
localField: '_id',
foreignField: 'authors'
});
// Now you should be able to do User.find().populate('articles') and Article.find().populate('authors')
In the above example is it required to .push
authors into the Article collection before it will be returned in a populate?
@ckee83 yep
I tried the below sample code and getting the same error.
Can someone please help?
mongoose : v5.1.2
const UserSchema = new mongoose.Schema({
name : String
})
const ArticleSchema = new mongoose.Schema({
title : String,
authors : [{
type: mongoose.Schema.Types.ObjectId,
ref: 'User'
}]
})
UserSchema.virtual('articles', {
ref: 'ArticleSchema',
localField: '_id',
foreignField: 'authors'
})
var User = mongoose.model('User', UserSchema);
var Article = mongoose.model('Article', ArticleSchema);
var userA = new User({name: 'UserA'})
userA.save()
var userB = new User({name: 'UserB'})
userB.save()
var newArticle = new Article({
title : 'New Article',
authors : [userA._id, userB._id]
})
newArticle.save()
Article.find({}).populate('authors') .exec((err, articles) => {
console.log(articles)
}); // Work fine as articles authors are pushed while saving the article
User.find({}).populate('articles') .exec((err, articles) => {
console.log(articles)
}); // prints undefined
User.find({'name':'userA'}).populate('articles').exec((err, a) => {
console.log(a)
}) // print []
@patil16nit are you actually waiting for userA.save()
, userB.save()
, and newArticle.save()
to finish before executing the queries? That might explain the issue, you need to wait for the documents to actually be saved before querying.
Not working with me until adding this
toJSON: {
virtuals: true,
},
Most helpful comment
Hmm I'd imagine the issue is due to the fact that you have a virtual field with the same name as a real field. That's not a good idea. If you want to model this sort of many-to-many, I'd recommend having putting the array on the smaller side of the many-to-many and using a virtual for the other side: