Reputation: 28410
Consider the blog/comment schemas where nesting is appropriate (even if you disagree):
var CommentSchema = new Schema({ name: String, body: String });
var BlogPostSchema = new Schema({ title: String, comments: [CommentSchema] });
I understand how to add, update, delete comments for a blog post, but all of these methods require the save()
method to be called on the parent blog post document:
blog_post.comments.push( new Comment({...}) );
blog_post.save();
I would like to be able to make the Comment schema aware that it is nested inside of another schema so that I can call save()
on a comment document and it's smart enough to update the parent blog post. In my app logic, I already know the blog post id, so I would like to do something like this:
CommentSchema.virtual('blog_post_id');
CommentSchema.pre('save', function (next) {
var comment = this;
if( !comment.blog_post_id ) throw new Error('Need a blog post id');
BlogModel.findById( comment.blog_post_id, function(err, post) {
post.comments.push( comment );
post.save(next);
});
});
var comment = new Comment({ blog_post_id: 123, name: 'Joe', body: 'foo' });
comment.save();
The above works, but I still end up with a top-level Comments collection separate from the blog posts (this is just how mongoose works, I accept that).
Question: How do I prevent Mongoose from creating a separate "Comments" collection. In the pre-save method I would like to call next()
without any write operations taking place afterwards. Any thoughts?
Upvotes: 0
Views: 612
Reputation: 51
Using the mongoose-relationship plugin from https://www.npmjs.org/package/mongoose-relationship it is possible to make your documents aware of their relations.
Corresponding references are updated by the plugin when adding/removing documents.
There is a good example on the github page: https://github.com/sabymike/mongoose-relationship
Upvotes: 0
Reputation: 28410
This has earned me the Tumbleweed badge... hooray!?!?
So I have written a lot of code which accomplished the above. I don't want to release it until I have done more testing. But if anybody is interested in this, please let me know by posting here. I will gladly hand over what I have (which is going into production soon).
Right now my code doesn't support deep nesting... meaning you can only work with "simple" nesting similar to the blog/comments example above. I have the architecture in place to handle more complex nesting in the future, but I don't have the time to test right now (darn deadlines). Here are some of the big points about my solution so far:
find
, findOne
, save
, and remove
directly on a nested modelfindById
doesn't (can't) work - well it maybe could work but would require searching the entire collection, which is slow. Must use findOne
+ parent id instead (see examples).Model.update()
on the parent model (which is really fast).// setup the "nestedSchema" plugin
var nestedSchema = require("./plugins/nestedSchema");
CommentSchema.plugin(nestedSchema, {
path: 'comments',
ownerModelPath: './BlogPostModel',
ownerIdFieldName: 'blogpost_id'
});
blogpost_id
is ALWAYS used - this is a requirement which makes it stay fast (callbacks and error handling removed for brevity):// create a new comment
var comment = new CommentModel({
blogpost_id: [id],
name: 'Joe Schmoe',
body: 'The content of the comment'
});
comment.save();
// use findOne in leu of findById
CommentModel.findOne({blogpost_id: [id], _id: [id]}, function( err, comment ) {
comment.set('body', 'This comment has been updated directly!');
comment.save();
});
// find all hateful comments and remove
CommentModel.find({blogpost_id: [id], body: /sucks|stupid|dumb/gi}).remove();
Upvotes: 2