2

I have two models, Post and Comment:

My Post model (models/post.js):

var mongoose = require('mongoose');
var Comment = require('../models/comment');
var Schema = mongoose.Schema;

module.exports = mongoose.model('Post', new Schema({
    text: {type: String, trim: true},
    postedBy: String,
    comments: [Comment]
}));

My Comment model (models/comment.js):

var mongoose = require('mongoose');
var Schema = mongoose.Schema;

module.exports = mongoose.model('Comment', new Schema({
    user: String,
    comment: {type: String, trim: true},
    created: {type: Date, default: Date.now(), select: false}
}));

When I attempt to create a new post without any comments, the post is created perfectly fine.

Although when I try to $push a comment to the post after creation, nothing happens.

Post.findOneAndUpdate(
    {"_id": req.params.id}, 
    {$push: {comments: {
        comment: "Hello World",
        user: "933ujrfn393r"
    }}
}).exec(function(err, post) {
    console.log(post);
    res.json({success: true});
});

Why is this failing to push the comment to the post? My console.log(post) line simply logs undefined, so not too sure what is happening here. I tried a simple test of Post.findOne({"_id": req.params.id}) and it returned the post successfully, so there is no problem with the find query.

Fizzix
  • 23,679
  • 38
  • 110
  • 176
  • Instead of using two different collections for this, use single collection and in that collection you can separately define schema for the field comments. – Jitendra Khatri May 31 '16 at 06:44

2 Answers2

13

Embedded sub documents

Your usage implies an embedded sub document inside the model which only requires a schema definition for the sub document. This will store both schema's in a single document in a single collection in MongoDB

var mongoose = require('mongoose');
var Schema = mongoose.Schema;

var CommentSchema = new Schema({
    user: String,
    comment: {type: String, trim: true},
    created: {type: Date, default: Date.now(), select: false}
});

var PostSchema = new Schema({
    text: {type: String, trim: true},
    postedBy: String,
    comments: [CommentSchema]
});

module.exports = mongoose.model('Post', PostSchema);

Then create comments as you were.

Post.findOneAndUpdate(
    {"_id": req.params.id}, 
    {$push: {comments: {
        comment: "Hello World",
        user: "933ujrfn393r"
    }}
}).then(function (post) {
    console.log(post);
    res.json({success: true});
});

Document references

If you want to keep the two models then you would need to use a reference in your Post schema instead. This will create seperate documents in seperate collections in MongoDB and use the _id to look up the second document.

var PostSchema = new Schema({
    text: {type: String, trim: true},
    postedBy: String,
    comments: {
      type: mongoose.Schema.Types.ObjectId, 
      ref: 'Comment'
    }
});

Then comments need to be created before you can reference them in the Post model.

c = new Comment({ comment: 'x' })
c.save().then(function (result) {
  return Post.findOneAndUpdate(
    { _id: req.params.id },
    { $push: { comments: result._id } }
  );
}).then(function (result) {
  console.log('updated post');
});

Population can be used to easily retrieve the "foreign" documents.

Matt
  • 68,711
  • 7
  • 155
  • 158
  • If using the first method you mention (different models for each, although same document), how would I save the comment to the post document? I have tried creating the new comment like `c = new Comment({ comment: 'x' })` and then `$push`ing to the `Post`, although it doesn't seem to push anything... – Fizzix May 31 '16 at 11:47
  • `new Comment` is only for the second method where `Comment` is a model. When comments are just a Schema and embedded documents, the query should work as you have in your question. Note that schema's and models are separate things in Mongoose. – Matt May 31 '16 at 12:53
0

Based on this question, I believe your problem is that you're embedding the Comment Model instead of the Comment Schema.

Try changing post.js from:

var Comment = require('../models/comment');

to:

var Comment = require('../models/comment').schema;

This also makes sense after looking at the example on the Mongoose docs regarding sub-docs.

P.S. What helped me investigate this was outputting the err object of the exec callback to see what was actually going on...

Community
  • 1
  • 1
OzW
  • 848
  • 1
  • 11
  • 24
  • 1
    That would work but creating a model also creates a separate collection in the database. If you are not using the model, then there's no point instantiating the model from the schema in the first place. – Matt May 31 '16 at 07:12