I'm building a REST API with express and mongoose. I have the following schemas :
var PostSchema= new Schema({
name: String,
_comments: [{ type: Schema.Types.ObjectId, ref: 'Comment'}]
});
var CommentSchema = new Schema({
text: String,
_post: { type: Schema.Types.ObjectId, ref: 'Post'}
});
I want to add a new comment to my post :
/* POST a comment*/
router.post('/', function(req, res, next) {
var comment= new Comment(req.body);
comment.save(function (err, post) {
if (err) {
return next(err);
}
res.json(post);
});
});
It does save the comment with the following data :
{text: "bla", _post: *postId*}
However, when I retrieve my posts with populated comments :
/* GET post*/
router.get('/', function(req, res, next) {
Post.find().populate('_comments').exec(function (err, posts) {
if (err) return next(err);
res.json(posts);
});
});
The comments array is empty.
So I guess that when I'm adding a new comment to a post, I also need to add the comment id to the post.comments array and save it ? Is there a clean way to do that ?
After saving comment (in callback passed to comment.save), add it to post._comments with $addToSet. You will avoid duplicates by doing this.
Post.update({ _id: comment._post }, { $addToSet: { _comments: comment._id } }, {}).exec();
Related
I have two schemas, defined as following:
var userSchema = new Schema({
email: String,
name: String,
role: String,
password: String
})
var raceSchema = new Schema({
date: Date,
name: String,
location: String,
time: String,
register: String,
participants: [{ type: Schema.Types.ObjectId, ref: 'User'}],
registered_participants: [{ type: Schema.Types.ObjectId, ref: 'User'}],
})
As you can see, I reference the first schema twice in the second schema. If I add a reference to a user in one of the lists, everything is fine. But when I add a reference to the same user to the other list I get the following error: Cast to [undefined] failed for value
What causes this error? Is it related to the fact that the same schema is used twice in the second schema?
Edit:
I get the error when I call the following Express endpoint:
app.post('/race/:id/registered', passport.authenticate('jwt', { session: false}), (req, res) =>
Race.findOne({ _id: req.params.id }, function (err, race) {
if (err) return res.json({'Error': err})
if (!race) return res.json({'Error': 'Race not found'})
race.registered_participants.push(req.user)
race.save(function (err, updatedRace) {
if (err) return res.json({'Error': err})
res.send(updatedRace)
})
})
)
Edit 2: The model definitions:
var User = mongoose.model('User', userSchema);
var Race = mongoose.model('Race', raceSchema);
Try using findByIdAndUpdate in your POST method instead:
app.post('/race/:id/registered', passport.authenticate('jwt', { session: false}), (req, res) =>
Race.findByIdAndUpdate(req.params.id,
{ $push: { registered_participants: req.user } },
function (err, race) {
if (err) return res.json({'Error': err})
res.send(race)
})
)
I have a pretty simple setup where I'm trying to populate my Mongoose JSON responses with all Comments that belong to a Post
I thought that calling 'populate' on Post would return all comments related to the Post, but instead I'm getting an empty array. I just don't get what I'm doing wrong.
post.js
const mongoose = require('mongoose');
const db = require('./init');
const postSchema = new mongoose.Schema({
title: String,
url: String,
body: String,
votes: Number,
_comments: [{type: mongoose.Schema.Types.ObjectId, ref: "Comment"}]
});
const Post = mongoose.model('Post', postSchema);
module.exports = Post;
comment.js
const mongoose = require('mongoose');
const db = require('./init');
const commentSchema = new mongoose.Schema({
// post_id: post_id,
_post: { type: String, ref: 'Post'},
content: String,
posted: { type: Date, default: Date.now() }
});
const Comment = mongoose.model('Comment', commentSchema);
module.exports = Comment;
posts.js
router.get('/', function(req, res, next) {
// An empty find method will return all Posts
Post.find()
.populate('_comments')
.then(posts => {
res.json(posts)
})
.catch(err => {
res.json({ message: err.message })
})
});
and within the posts.js file I've set up a route to create a comment when a post request is sent to posts/post_id/comments
commentsRouter.post('/', function(req, res, next) {
console.log(req.params.id)
//res.json({response: 'hai'})
comment = new Comment;
comment.content = req.body.content;
comment._post = req.params.id
comment.save((err) => {
if (err)
res.send(err);
res.json({comment});
});
});
Comments are being created when I post to this route, and they are created with the correct _post value, however populate isn't picking them up.
For example, this post has been created, and it doesn't populate the associated comment below:
{
"post": {
"__v": 0,
"votes": 0,
"body": "Test Body",
"url": "Test URL",
"title": "Test Title",
"_id": "587f4b0a4e8c5b2879c63a8c",
"_comments": []
}
}
{
"comment": {
"__v": 0,
"_post": "587f4b0a4e8c5b2879c63a8c",
"content": "Test Comment Content",
"_id": "587f4b6a4e8c5b2879c63a8d",
"posted": "2017-01-18T10:37:55.935Z"
}
}
When you create a comment, you also have to save the comment instance _id to a post. So within the save() callback, you can do something like
commentsRouter.post('/', function(req, res, next) {
console.log(req.params.id)
//res.json({response: 'hai'})
comment = new Comment({
content: req.body.content;
_post: req.params.id
});
comment.save((err, doc) => {
if (err)
res.send(err);
Post.findByIdAndUpdate(req.params.id,
{ $push: { _comments: doc._id } },
{ new: true },
(err, post) => {
if (err)
res.send(err);
res.json({doc});
}
)
});
});
I am building a RESTful service for querying a movie database using
Express.js, Node.js and MongoDB and I am a beginner on all of them.
My question is what is the best practice for structuring db queries with Node so that I take advantage of the callback mechanisms and not block the server but at the same time not write bloated code.
I modified the code provided by the express-generator and I think it achieves the former but not the latter. What are your comments?
If you could provide a general skeleton for an Express route that handles db queries, I would appreciate it.
Below is my code
var findMovie = function(db, callback, req, res) {
var path = req.path.split("\/");
var cursor = db.collection('movies').find({"_id" : ObjectId(path[path.length - 2])});
var query = [];
cursor.each(function(err, doc) {
assert.equal(err, null);
if (doc != null) {
query.push(doc);
} else {
res.json(query);
callback();
}
});
}
router.get('/movies/:id/info/', function(req, res, next){
MongoClient.connect(url, function(err, db) {
assert.equal(null, err);
findMovie(db, function() {
db.close();
}, req, res);
});
});
First if you use mongoDB in node i will definately recommend to use mongoose or some other object modeling tool, its much easier than native driver.
then it might look like this:
/model/user.js
var mongoose = require('mongoose');
var UserSchema = new mongoose.Schema({
createdAt: {type: Date, default: Date.now},
updatedAt: {type: Date, default: Date.now},
email: {type: String, unique: true, required: true},
password: {type: String, required: true},
active: {type: Boolean, default: true},
role: {type: String, default: 'user'},
accessLevel: {type: Number, default: 1}
}, {
collection: 'users'
});
module.exports = mongoose.model('User', UserSchema);
/controllers/users.js
var User = require('../model/user');
exports.create (req, res, next) {
var newUser = new User(req.body);
newUser.save(function (err) {
if (err)
return next(err);
res.json({
message: 'User created'
});
});
}
exports.listAll (req, res, next) {
User.find({}, function (err, users) {
if (err)
return next(err);
res.json(users)
});
}
exports.getById (req, res, next) {
User.findById(req.params.id, function (err, user) {
if (err)
return next(err);
res.json(user)
});
}
/routes/users.js
var controller = require('../controllers/users');
var router = require('express').Router();
router.route('/users')
.post(controller.create)
.get(controller.listAll)
router.route('/users/:id')
.get(controller.getById)
.delete(controller.remove)
I am trying to return an updated object as JSON, where the update was to set an array of objectIDs. I want the returned objected to have that array populated. For example, I have the following (simplified) model:
var UserSchema = new mongoose.Schema({
username: {type: String, unique: true, required: true},
friends: [{type: mongoose.Schema.Types.ObjectId, ref: 'User'}]
});
In my controller, I have:
exports.saveFriends = function(req, res) {
User.findById(req.params.user_id, function(err, user) {
// req.body.friends is JSON list of objectIDs for other users
user.friends = req.body.friends
user.save(function(err) {
user.populate({path: 'friends'}, function(err, ticket) {
if (err) {
res.send(err);
} else {
res.json(user);
}
});
});
});
}
This does in fact save the array properly as ObjectIDs, but the response user always shows "[]" as the array of friends.
Anyone see my issue?
I have an existing document that contains a nested array of elements (I'm not exactly sure of the terminology here). I have no problem creating the document. The problem arises when I need to insert a new element into the existing document. The code below may clarify what I'm trying to do:
Controller:
var Post = require('./models/post');
app.post('/post/:id/comment', function(req, res) {
var updateData = {
comments.comment: req.body.comment
comments.name: req.body.name,
};
Post.update({_id: req.params.id},updateData, function(err,affected) {
console.log('affected rows %d', affected);
});
});
Model:
var mongoose = require('mongoose');
var postSchema = mongoose.Schema({
post : String,
name : String,
created : {
type: Date,
default: Date.now
},
comments : [{
comment : String,
name : String,
created : {
type: Date,
default: Date.now
}
}]
});
module.exports = mongoose.model('Posts', postSchema);
So, each post can contain multiple comments. I'm just not sure how to insert a new comment into an existing post.
Since comments is declared as array, try to use
Post.update({_id:yourid}, { $push : { comments: { comment: '', name: '' } } }, ...
You can convert the object returned from mongodb in to an js object, and push new comment into the comments array. See the following:
var postSchema = require('./postSchema'); // your postSchema model file
postSchema.findOne({name: 'name-of-the-post'}, function (err, doc) { //find the post base on post name or whatever criteria
if (err)
console.log(err);
else {
if (!doc) { //if not found, create new post and insert into db
var obj = new postSchema({
post: '...'
name: '...'
...
});
obj.save(function (err) {
if (err)
console.log(err);
});
} else {
// if found, convert the post into an object, delete the _id field, and add new comment to this post
var obj = doc.toObject();
delete obj._id;
obj.comments.push(req.body.comment); // push new comment to comments array
postSchema.update(
{
'_id': doc._id
}, obj, {upsert: true}, function (err) { // upsert: true
if (err)
console.log(err);
});
}
console.log('Done');
}
});