I am making a node API. I am stuck at adding a comment to a story.
I am able to create a comment but instead of pushing it to a given story, it was trying to create a new instance of story.
Story.findOne(req.params.id, (err, foundstory) => {
if(err){
res.status(500).json({msg:err})
}else{
let comment = new Comment()
comment.body = req.body.body
comment.author = req.body.author
console.log(foundstory)
//save comment//
comment.save((err, comment) => {
if(err){
res.status(500).json({msg:err})
}else{
//pushing comment to comments array (ref) in story
foundstory.comments.push(comment)
foundstory.save()
res.status(200).json({msg:"Comment saved"})
}
})
}
})
Story Schema
import mongoose from 'mongoose'
import User from './user'
import Comment from './comment'
const Schema = mongoose.Schema
const ObjectID = mongoose.Schema.Types.ObjectId
const storySchema = new Schema({
//subdoc ref from user
author: {type: ObjectID, ref: 'User'},
//subdoc ref from comment
comments: [{
type: ObjectID,
ref: 'Comment'
}],
//contents of story//
title: {type: String, required: true},
body: {type: String, required: true},
date: {type: Date, default: Date.now()},
tags: [{type: String}]
})
module.exports = mongoose.model('Story', storySchema)
Comment Schema
import mongoose from 'mongoose'
import User from './user'
const Schema = mongoose.Schema
const ObjectID = mongoose.Schema.Types.ObjectId
const commentSchema = new Schema({
body : {type: String, required: true},
author: {type: ObjectID, ref: 'User'}
})
module.exports = mongoose.model('Comment', commentSchema)
I have an array of type "Comment" in my "Story" schema. My attempt is to push those comment to that array.
try changing your code like this:
Story.findById(req.params.id, (err, foundstory) => {
if (err) res.status(500).json({
msg: err
});
else if (!foundStory) res.status(400).json({
msg: "Story Not Found"
});
else {
let comment = new Comment();
comment.body = req.body.body;
comment.author = req.body.author;
//save comment//
comment.save(async (err, comment) => {
if (err) res.status(500).json({
msg: err
});
else {
foundstory.comments.push(comment._id);
await foundstory.save();
res.status(200).json({
msg: "Comment saved"
})
}
})
}
})
I've changed the findOne() method with findById(), also the 'foundstory.save()' method is an asynchronous call, so i used async\await to handle it.
Hope this helps :)
Related
I've been trying to use the mongoose populate function to connect two models. I can save an object but when trying to retrieve using populate the ObjectIds are just replaced with an empty array.
Many questions seem to have been asked but none have a solution that worked for me
user.js
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var Route = require('./route')
var passportLocalMongoose = require('passport-local-mongoose');
const postSchema = new Schema ({
text: {
type: String,
default: '',
required: true
}
}, {
timestamps: true
});
const UserSchema = new Schema({
firstname: {
type: String
},
posts: [postSchema],
route: [{
type: mongoose.Schema.Types.ObjectId,
ref: 'Route'
}]
}, {
timestamps: true
});
UserSchema.plugin(passportLocalMongoose);
const User = mongoose.model('User', UserSchema);
module.exports = User;
route.js
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
const locationSchema = new Schema ({
id: {
type: Number,
default: 0,
required: true
},
address: {
type: String,
default: '',
required: true
},
lat: {
type: Number,
default: 0,
required: true
},
lng: {
type: Number,
default: 0,
required: true
}
},{
timestamps: true })
const routeSchema = new Schema ({
locations: [locationSchema],
description: {
journey1: {
type: String,
default: '',
required: false
},
journey2: {
type: String,
default: '',
required: false
},
journey3: {
type: String,
default: '',
required: false
},
journey4: {
type: String,
default: '',
required: false
}
}
}, {
timestamps: true
});
module.exports = mongoose.model('Route', routeSchema);
within REST POST end point
User.findOne({_id: req.user._id}, function(err,user) {
if(user) {
var routed = new Route();
routed.locations = req.body.locations;
routed.description = req.body.description;
user.route.push(routed);
user.save()
.then((user) => {
res.statusCode = 200;
res.setHeader('Content-Type', 'application/json')
res.json(user)
}, (err) => next(err))
} else {
console.log("errored")
err = new Error('User ' + req.body.username + ' not found');
err.status = 404;
return next(err);
}
})
within REST GET end point
User.findOne({_id: req.user._id})
.populate('route')
.then((user) => {
if(user){
console.log("user")
console.log(user)
console.log("routes")
console.log(user.route)
res.statusCode = 200;
res.setHeader('Content-Type', 'application/json')
res.json({success: true, routes: user.route});
}
}, (err) => next(err))
.catch((err) => next(err));
If I remove populate I'll get something like
[
new ObjectId("61f053af7ba46267f4893f8f")
new ObjectId("61f053af7ba46267f4893f8f")
new ObjectId("61f053af7ba46267f4893f8f")
]
from the GET end point but adding it back in returns
[].
My understanding is that in 'new Route()' I'm creating a new Route Object with an Id that gets stored in the User model/document(?). Then when I call populate mongoose searches the Route document for those Ids and converts them to the objects I want. The only issue I could think of is that I'm not creating the Route objects correctly and so no object is being stored with that Id which is why an empty array is returned when I come to try swap Ids with Route objects.
Any ideas or are we all just stumbling in the dark ?
Not entirely sure this is the correct method but instead of instantiating a Route object as displayed I used the Route.create(...) method and then pushed that to the route array and now populate works as expected
I'm new to node.js, express and MongoDB, and still learning. The program had a runtime error when I tried some data association in MongoDB models. The reference has been included in the model already but push() method can't recognize it. The models look like this:
var mongoose = require('mongoose');
var commentSchema = new mongoose.Schema({
text: String,
author: String
});
var Comment = mongoose.model("comments", commentSchema);
module.exports = Comment;
and
var mongoose = require('mongoose');
var campgroundSchema = new mongoose.Schema({
name: String,
image: String,
description: String,
comments: [
{
type: mongoose.Schema.Types.ObjectId,
ref: "Comment"
}
]
});
var Campground = mongoose.model("campgrounds", campgroundSchema);
module.exports = Campground;
The part where these two models are needed looks like this:
Campground.remove({}, function(err){
if(err){
console.log(err);
}else{
console.log("removed campground!");
data.forEach(function (seed) {
Campground.create(seed, function (err, data) {
if(err){
console.log(err);
} else{
console.log("camp created");
Comment.create(
{
text: "nice campsite!",
author: "haiderboi"
},function (err, comment) {
if(err){
console.log(err);
}else{
Campground.comments.push(comment);
Campground.save();
console.log("comment added");
}
}
);
}
});
});
}
});
It seems like the array is already defined in the Campground model so I can't tell the error. Thanks in advance!
Hey i was have problem like this in my project.And it wasn't working because my blogpost schema was like this:
var campgroundSchema = new mongoose.Schema({
name: String,
image: String,
description: String,
comments: [
{
type: mongoose.Schema.Types.ObjectId,
ref: "Comment"
}
]});
And changed it to this:
var campgroundSchema = new mongoose.Schema({
name: String,
image: String,
description: String,
comments: [
{
type: mongoose.Schema.Types.Mixed,
}
]});
Because up there you're referring ObjectId and you're setting an object inside your code.It's not match.So you should try like this
Found the problem!
Instead of doing
Campground.comments.push(comment);
I should do
data.comments.push(comment);
since I wanna push the comment to the current Campground object, I should use the object returned by the callback.
I'm in process of an app based on Node.js, MongoDB and Express. My goal is to get a fetch API system to work.
When using Postman to check out my status, the GET for "article.js" model file (in my localhost:3000/articles) shows the following error:
{
"error": {
"message": "Schema hasn't been registered for model \"Category\".\nUse mongoose.model(name, schema)",
"name": "MissingSchemaError"
}
}
This error disables the display of my articles or categories in Postman, as they are saved in my MongoDB project area at mongodb cloud.
The model file code "article.js" is the following:
const mongoose = require('mongoose');
const articleSchema = mongoose.Schema({
_id: mongoose.Schema.Types.ObjectId,
title: { type: String, required: true },
description: { type: String, required: true },
content: { type: String, required: true },
categoryId: { type: mongoose.Schema.Types.ObjectId, required: true, ref: 'Category' }
});
module.exports = mongoose.model('Article', articleSchema);
This file connects with the controller named "articles.js", with the following relevant code:
const mongoose = require('mongoose');
const Article = require('../models/article');
const Category = require('../models/category');
module.exports = {
getAllArticles: (req, res) => {
Article.find().populate('categoryId', 'title').then((articles) => {
res.status(200).json({
articles
})
}).catch(error => {
res.status(500).json({
error
})
});
},
createArticle: (req, res) => {
const { title, description, content, categoryId } = req.body;
Category.findById(categoryId).then((category) => {
if (!category) {
return res.status(404).json({
message: 'Category not found'
})
}
const article = new Article({
_id: new mongoose.Types.ObjectId(),
title,
description,
content,
categoryId
});
return article.save();
}).then(() => {
res.status(200).json({
message: 'Created article'
})
}).catch(error => {
res.status(500).json({
error
})
});
},
}
The model file "category.js" code in the app looks like this:
const mongoose = require('mongoose');
const categorySchema = mongoose.Schema({
_id: mongoose.Schema.Types.ObjectId,
title: { type: String, required: true },
description: { type: String, required: true }
});
module.exports = mongoose.model('Category', categorySchema);
I looked up topics from the past here, such as this one - but it didn't solve my problem.
What should I do in order to fix my code?
Is it a syntax mistake or something else?
the code seems to be OK
I don't see here any particular error's
The first problem I am having is that whenever I try to delete the Comment, I also try to find the index of that specific comment inside post.comments as well as inside user.comments, it consistently returns -1, the reason why I am trying to find it, is so that I can splice it from the comments array that user and post do have.
The second problem I am having is that whenever I create a comment, I try to store it in the comments array that user and post have, but it stores it only as a string, although I think it is supposed to be stored as an object right?, So I can access it later by populating?
I have been struggling for days now being very frustrated why it does not work. Please help me!
Below will be my two routes, for deleting and creating comments, and my Schemas, Thank You for all the help!
Creating Comments
// #route POST api/posts/comment/:id
// #desc Comment on a post
// #access Private
router.post(
'/comment/:id',
[
auth,
[
check('text', 'Text is required')
.not()
.isEmpty()
]
],
async (req, res) => {
const errors = validationResult(req);
if (!errors.isEmpty()) {
return res.status(400).json({ errors: errors.array() });
}
try {
const post = await Post.findById(req.params.id);
const user = await User.findById(req.user.id)
const newComment = {
text: req.body.text,
post: post._id,
user: req.user.id
};
const comment = new Comment(newComment);
post.comments.unshift(comment._id);
user.comments.unshift(comment._id)
console.log(user.comments);
console.log(post.comments);
console.log(comment)
await post.save();
await comment.save();
await user.save();
res.json(comment);
} catch (err) {
console.error(err.message);
res.status(500).send('Server Error');
}
}
);
Deleting comments
// #route DELETE api/posts/comment/:id/:comment_id
// #desc Delete comment
// #access Private
router.delete('/comment/:id/:comment_id', auth, async (req, res) => {
try {
const post = await Post.findById(req.params.id);
const user = await User.findById(req.user.id);
// Pull out comment by finding it through its id
const comment = await Comment.findById(req.params.comment_id);
// Make sure comment exists
if (!comment) {
return res.status(404).json({ msg: 'Post do not have this comment' });
}
// Check user
if (comment.user.toString() !== req.user.id) {
return res.status(401).json({ msg: 'User not authorized' });
}
// Get The value to be removed
const postCommentIndex = post.comments.findIndex(postComment => postComment === comment._id);
const userCommentIndex = user.comments.findIndex(userComment => userComment === comment._id);
console.log(`This is the post comment index ${postCommentIndex}`);
console.log(`This is the user comment index ${userCommentIndex}`);
post.comments.splice(postCommentIndex, 1);
user.comments.splice(userCommentIndex, 1);
// save user and post
await post.save();
await user.save();
await comment.remove();
// resend the comments that belongs to that post
res.json(post.comments);
} catch (err) {
console.error(err.message);
res.status(500).send('Server Error');
}
});
Schemas:
const mongoose = require('mongoose');
const UserSchema = new mongoose.Schema({
name: {
type: String,
required: true
},
email: {
type: String,
required: true,
unique: true
},
password: {
type: String,
required: true
},
avatar: {
type: String
},
posts: [{type: mongoose.Schema.Types.ObjectId, ref: "Post"}],
comments: [{type: mongoose.Schema.Types.ObjectId, ref: "Comment"}],
date: {
type: Date,
default: Date.now
}
});
module.exports = mongoose.model('User', UserSchema);
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
const PostSchema = new Schema({
user: {
type: Schema.Types.ObjectId,
ref: 'User'
},
text: {
type: String,
required: true
},
likes: [
{
user: {
type: Schema.Types.ObjectId,
ref: 'User'
}
}
],
dislikes: [
{
user: {
type: Schema.Types.ObjectId,
ref: "User"
}
}
],
comments: [{type: Schema.Types.ObjectId, ref: "Comment"}],
date: {
type: Date,
default: Date.now
}
});
module.exports = mongoose.model('Post', PostSchema);
const mongoose = require("mongoose")
const Schema = mongoose.Schema;
const CommentSchema = new Schema({
user: {
type: Schema.Types.ObjectId,
ref: 'User'
},
post: {
type: Schema.Types.ObjectId,
ref: "Post"
},
text: {
type: String,
required: true
},
date: {
type: Date,
default: Date.now
}
})
module.exports = mongoose.model("Comment", CommentSchema);
I think you need to redesign your schemas in a simpler way, there are too many references between the models, and this causes issues, for example you have 5 db access when you want to create a comment, and 6 db access when you want to delete a comment.
I would create the user schema like this removing the posts and comment references, but later when we want to access the posts from users, I set up virtual populate.
const UserSchema = new Schema(
{
name: {
type: String,
required: true
},
email: {
type: String,
required: true,
unique: true
},
password: {
type: String,
required: true
},
avatar: {
type: String
},
date: {
type: Date,
default: Date.now
}
},
{
toJSON: { virtuals: true }
}
);
UserSchema.virtual("posts", {
ref: "Post",
localField: "_id",
foreignField: "user"
});
And in the posts schema, I removed the comments references.
(For simplicity I removed likes and dislikes fields.)
const PostSchema = new Schema(
{
user: {
type: Schema.Types.ObjectId,
ref: "User"
},
text: {
type: String,
required: true
},
date: {
type: Date,
default: Date.now
}
},
{
toJSON: { virtuals: true }
}
);
PostSchema.virtual("comments", {
ref: "Comment",
localField: "_id",
foreignField: "post"
});
Comment schema can stay as it is.
Now to add a comment to a post, we only need 2 db access, one for checking if post exists, and one for creating the post.
router.post(
"/comment/:id",
[
auth,
[
check("text", "Text is required")
.not()
.isEmpty()
]
],
async (req, res) => {
const errors = validationResult(req);
if (!errors.isEmpty()) {
return res.status(400).json({ errors: errors.array() });
}
try {
const post = await Post.findById(req.params.id);
if (!post) {
return res.status(404).json({ msg: "Post not found" });
}
let comment = new Comment({
text: req.body.text,
post: req.params.id,
user: req.user.id
});
comment = await comment.save();
res.json(comment);
} catch (err) {
console.error(err.message);
res.status(500).send("Server Error");
}
}
);
Let's say we have these 2 users:
{
"_id" : ObjectId("5e216d74e7138b638cac040d"),
"name" : "user1"
}
{
"_id" : ObjectId("5e217192d204a26834d013e8"),
"name" : "user2"
}
User1 with _id:"5e216d74e7138b638cac040d" has this post.
{
"_id": "5e2170e7d204a26834d013e6",
"user": "5e216d74e7138b638cac040d",
"text": "Post 1",
"date": "2020-01-17T08:31:35.699Z",
"__v": 0,
"id": "5e2170e7d204a26834d013e6"
}
Let's say user2 with _id:"5e217192d204a26834d013e8" commented on this post two times like this:
{
"_id" : ObjectId("5e2172a4957c02689c9840d6"),
"text" : "User2 commented on user1 post1",
"post" : ObjectId("5e2170e7d204a26834d013e6"),
"user" : ObjectId("5e217192d204a26834d013e8"),
"date" : ISODate("2020-01-17T11:39:00.396+03:00"),
"__v" : 0
},
{
"_id": "5e21730d468bbb7ce8060ace",
"text": "User2 commented again on user1 post1",
"post": "5e2170e7d204a26834d013e6",
"user": "5e217192d204a26834d013e8",
"date": "2020-01-17T08:40:45.997Z",
"__v": 0
}
To remove a comment we can use the following route, as you see we decreased the db access from 6 to 3, and code is shorter and cleaner.
router.delete("/comment/:id/:comment_id", auth, async (req, res) => {
try {
const comment = await Comment.findById(req.params.comment_id);
if (!comment) {
return res.status(404).json({ msg: "Post do not have this comment" });
}
if (comment.user.toString() !== req.user.id) {
return res.status(401).json({ msg: "User not authorized" });
}
await comment.remove();
// resend the comments that belongs to that post
const postComments = await Comment.find({ post: req.params.id });
res.json(postComments);
} catch (err) {
console.error(err.message);
res.status(500).send("Server Error");
}
});
Now you may ask, how will access the posts from an user? Since we setup virtual populate in our user schema, we can populate the posts like this:
router.get("/users/:id/posts", async (req, res) => {
const result = await User.findById(req.params.id).populate("posts");
res.send(result);
});
You can try this code snipet :
Comment.deleteOne({
_id: comment.id
}, function (err) {
if (err) {
console.log(err);
return res.send(err.message);
}
res.send('success');
});
Data comming from req is store in Comment collection but it is not store in Product collection and i'll get an error message
app.post("/products/:id/comments", function(req, res) {
//lookup campground using ID
Product.findById(req.params.id, function(err, product) {
if (err) {
console.log(err);
res.redirect("/products");
} else {
Comment.create(req.body.comment, function(err, comment) {
if (err) {
console.log(err);
} else {
console.log("aaaaaa");
product.comments.push(comment);
console.log("bbbbbb");
product.save();
console.log("cccccc");
res.redirect('/products/' + product._id);
}
});
}
});});
and following is Product
var mongoose = require("mongoose"),
productSchema = new mongoose.Schema({
name: String,
img: String,
price: Number,
desc: String,
comments: [{
type: mongoose.Schema.Types.ObjectId,
ref: "Comment"
}],
created: {
type: Date,
default: Date.now()
}});module.exports = mongoose.model("Product", productSchema);
and this is Comment Schema
var mongoose = require("mongoose"),
commentSchema = mongoose.Schema({
text: String,
author: String
});module.exports = mongoose.model("Comment", commentSchema);
and i got following output :-
enter image description here
so it's store in commentSchema but it's not store in product.comments and when i run site that also redirect to products/:id, what i'm missing to store comment into database ??
If you don’t pass a callback function to save() then it will return a promise.
Therefore, you are getting the error as you have not added a catch.
Try passing a callback to save():
// ...
product.save(function(err, result) {
if (err) {
// Handle error
}
console.log("cccccc");
res.redirect('/products/' + product._id);
})