0

I am making a route in a Node server using Mongoose and Mongo which stores a comment in the comments array in blogPost (I will post model code). When I try to execute the query it gives me the following error: Postman error
These are my models and the route:

blogPost model

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

const BlogPostSchema = new Schema({
  content: {
    type: String,
    validate: {
      validator: (content) => content.length > 5,
      message: 'Content must contain at least 6 characters.'
    },
    required: [true, 'Content must be filled in.']
  },
  rating: Number,
  title: String,
  user: { type: Schema.Types.ObjectId, ref: 'user' },
  board: {type: Schema.Types.ObjectId, ref: 'board'},
  comments: [{
    type: Schema.Types.ObjectId,
    ref: 'comment'
  }]
});

const BlogPost = mongoose.model('blogPost', BlogPostSchema);

module.exports = BlogPost;

comment model

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

const CommentSchema = new Schema({
  content: {
    type: String,
    validate: {
      validator: (content) => content.length > 5,
      message: 'Content must contain at least 6 characters.'
    },
    required: [true, 'Content must be filled in.']
  },
  user: { type: Schema.Types.ObjectId, ref: 'user' },
  rating: Number

  // board: Board
});

// UserSchema.virtual('postCount').get(function(){
//   return this.posts.length;
// });

const Comment = mongoose.model('comment', CommentSchema);

module.exports = Comment;

Route

routes.put('/blogPosts/:id/comment', function(req, res) {
    const blogPostId = req.param('id');
    const commentProps = req.body;

    BlogPost.findById(req.params.id)
        .then((blogPost) => {
          blogPost.comments.push(commentProps);
          blogPost.save();
        })
    .catch((error) => res.status(400).json(error))
});

Any help is greatly appreciated.

  • https://docs.mongodb.com/ecosystem/use-cases/storing-comments/ its in python but you get the idea, of using $push – Gntem Dec 09 '17 at 13:17

2 Answers2

0

You don't want to push the entire comment into the comments array but just its _id. So something like this (untested):

// first save the comment
Comment.create(commentProps, function(err, comment) {
    if(err)
    {
      // save the world
    }
    else
    {
        BlogPost.update({ _id: req.param('id') }, { $push: { comments: comment._id } }, function(err, numberAffected, raw) { /*...*/ });
    });
dnickless
  • 10,733
  • 1
  • 19
  • 34
0

The problem is that you are pushing an entire comment object into an array that's only supposed to have objectids.

dnickless answer uses a solution with referencing, meaning you have a collection for blogposts and a collection for comments. The blogpost documents will refer to their comments with objectids.

You can also change the blogpost model to use embedding rather than referencing, meaning the comments will be a part of the blogpost documents as subdocuments. There are a couple of nice discussions regarding what's better here and here. The short answer is that it depends on the use case. You can choose what you want to use yourself.

Here's how embedding is done:

Blogpost model:

const mongoose = require('mongoose');
const Schema = mongoose.Schema;
const commentSchema = require('./comment.model');

const BlogPostSchema = new Schema({
  content: {
    type: String,
    validate: {
      validator: (content) => content.length > 5,
      message: 'Content must contain at least 6 characters.'
    },
    required: [true, 'Content must be filled in.']
  },
  rating: Number,
  title: String,
  user: { type: Schema.Types.ObjectId, ref: 'user' },
  board: {type: Schema.Types.ObjectId, ref: 'board'},
  comments: [commentSchema]
});

const BlogPost = mongoose.model('blogPost', BlogPostSchema);

module.exports = BlogPost;

Notice how the comments array uses the comment schema rather than being an array of object ids. The comment model has to be changed slightly:

Comment model:

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

const CommentSchema = new Schema({
  content: {
    type: String,
    validate: {
      validator: (content) => content.length > 5,
      message: 'Content must contain at least 6 characters.'
    },
    required: [true, 'Content must be filled in.']
  },
  user: { type: Schema.Types.ObjectId, ref: 'user' },
  rating: Number

  // board: Board
});

// UserSchema.virtual('postCount').get(function(){
//   return this.posts.length;
// });

module.exports = CommentSchema;

const Comment = mongoose.model('comment', CommentSchema); was removed. The schema should no longer be registered and comments will not have their own collection. The schema is now being exported rather than the registered model.

The original code for adding comments should work after that. Comments will be a part of the blogpost document, not be in their own collection.

Mika Sundland
  • 18,120
  • 16
  • 38
  • 50
  • If I do it via the subdocument approach, can I still get a list of a user's comments? Or does blogPost now only have access to the comments? –  Dec 09 '17 at 13:57
  • @Mike You can still get a list of a user's comments, but it's a bit harder than when you have comments in a separate collection. Normalized models (referencing) is easier to use and denormalized models (embedding) can give higher read performance if done right, because they don't have to refer to other collections. There is more info about that [here](https://www.mongodb.com/blog/post/thinking-documents-part-1?jmp=docs&_ga=2.97543773.31888329.1512825673-1213879524.1495890610). – Mika Sundland Dec 09 '17 at 14:52