Edited object/document not saving properly on MongoDB in MERN app - node.js

I'm building a MERN application where blog posts are created and edited, and comments to blog posts can be added and edited. When a blog post is added or edited everything works as expected, and comments are added as expected as well. But when a comment is edited, it doesn't save properly in the database. It gets saved across the session because of rendering functions I added in the react files, but when the app is reloaded the comment edit is gone and the comment is back to how it was originally. When I look at the database, I notice the edited comment is added by itself, but its original copy inside of the comment is not edited. I'm assuming there is an issue with how I'm configuring the model schemas, and/or I'm not accessing the blog post document directly. But I don't know exactly what the issue is despite what I've tried.
Below are two editComment functions that I've tried in the server side express/mongoose files, the first is successful at adding the edited comment to the database but it's failing to access and edit the original one inside of the blog post. The second one is taking into consideration the object id of the post the comment is in, but this just sends a 500 error and goes nowhere. I also have the blog post and comment schemas provided to give a better idea of where the issue may lie.
try {
let newDate = new Date();
let date = newDate.toDateString();
let post = req.params.postid;
const { title, content } = req.body;
const updatedComment = await Comment.findByIdAndUpdate(req.params.commentid, {title, date, content, post});
await updatedComment.save();
res.status(200).json(updatedComment);
} catch (error) {
res.status(404).json( {message: error.message} );
}
}
try {
const { title, content } = req.body;
let newDate = new Date();
let date = newDate.toDateString();
let post = new mongoose.Types.ObjectId(req.params.postid);
const postDoc = await Post.findById(post).lean().exec();
const comment = await Comment.findById(req.params.commentid).exec();
comment.title = title;
comment.date = date;
comment.content = content;
comment.post = post;
const updatedComment = await comment.save();
res.status(200).json(updatedComment);
} catch (error) {
res.status(500).json( {message: error.message} );
}
}
var PostSchema = new Schema({
title: {type: String, required: true, maxLength: 300},
date: {type: String, required: true},
content: {type: String, required: true},
comments: [{type: Object, required: true}],
})
module.exports = mongoose.model('Post', PostSchema);
var CommentSchema = new Schema({
title: {type: String, required: true, maxLength: 300},
date: {type: String, required: true},
content: {type: String, required: true},
post:{type: Schema.Types.ObjectId, ref:"Post", required: true},
})
module.exports = mongoose.model('Comment', CommentSchema);

Related

Mongoose populate returns empty array with a simple,non-nested population

I am trying to populate a model but I am getting a empty array in return. I went throught every stack overflow answer but nothing solved my issue.
Here's my main schema:
const companySchema = new Schema({
name:{type:String,required:true},
img:{data: Buffer,contentType: String},
users:[{
name:String,
email:String,
allottedWarehouse:String,
}],
brands:[{type:Schema.Types.ObjectId,ref:'Brand'}],
warehouses:{type:Array},
shops:{type:Array}
})
Here's how I am trying to populate
exports.getCompanyDetails = function(req,res){
Company.findById(req.params.id)
.populate('brands')
.exec(function(err,selectedCompany){
if(err){
res.json('Something went wrong!')
}
console.log(selectedCompany.brands)
res.json(selectedCompany);
})
}
But in database it's just empty brands array,no id reference at all.
When I return it in res.json,it's empty too.
Here's my simple brand schema
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
const BrandSchema = new Schema(
{
name: {type: String, required: true},
packSize:{type: Number, required: true},
})
module.exports = mongoose.model('Brand',BrandSchema);
I so badly need your help on this. Please look at it guys!Tell me where I am going wrong.

how can i get data from an db object which is store id of another db in mongoDB

hi there I am not getting data from a DB object which is storing the id of another DBS, actually, this is a blog website so I am posting comments and acting comments but there are problems I am getting the same comments on all posts but I want, every post should have their own comment.
here is post schema
const blogSchema = new mongoose.Schema({
title: String ,
content: String,
image:{data: Buffer,contentType: String},
comment:[
{
type:mongoose.Schema.Types.ObjectId, ref:'Comment'
}
]
});
here is the comment schema
var commentSchema = new mongoose.Schema({
name:{
type:String,
required: "this field is required"
},
comment:{
type:String,
required:"this filed is required"
},
blog:{
type:mongoose.Schema.Types.ObjectId,
ref: 'Blog'
}
},{
timestamps: true
})
node js router which is getting post but not comment
pp.get("/post/:postname",(req,res)=>{
// const requesttitle = _.lowerCase(req.params.postname);
const requesttitle = req.params.postname;
Blog.findOne({_id: requesttitle} ,(err ,got)=>{
if(err){
console.log(err)
}else{
const data= got.comment.find({})
console.log(data)
res.render('post',{post:got });
}
})
})
I believe the problem lays in your Schema. In your blogSchema you have references to many Comment documents, and in your commentSchema you have a reference to a single "Blog" ( I suggest not naming it "blog" but "post" since that is what it is ) . This duplicative referencation is not necessary in most cases.
Since in your setup a single comment can only be a child of one specific post, this would be the reference I would go for. The post document itself doesn't really need to know directly what comments are included since that information is already hold in the Comment document.
For your post I would suggest the following schema :
// Post Schema
const postSchema = new mongoose.Schema({
title: String ,
content: String,
image: { data: Buffer, contentType: String }
});
For your comment I would suggest the following schema :
// Comment Schema
const commentSchema = new mongoose.Schema({
name: {
type: String,
required: "this field is required"
},
comment: {
type: String,
required: "this filed is required"
},
post: {
type: mongoose.Schema.Types.ObjectId,
ref: 'Post'
}
});
Now sure the next step depends on your whole frontend part is setup up, but having schemas like this would let you do something along the lines of :
pp.get("/post/:id", async (req,res) => {
const id = req.params.id;
const post = await Post.findOne({ _id: id });
const comments = await Comment.find({ post: id });
res.render('post', {
post: post,
comments: comments
});
});
Pros
one-directional relation means less work if a comment is created or deleted.
possibility to just get comment and/or post or both in one api call.
Cons
Requires 2 database calls if post and comments both are requested.
Alternative: Subdocuments
As an alternative to using referenced Documents you can use Subdocuments.
For your commentSchema that means you won't need to create a seperate Model out of it. However your postSchema would need to look like this:
const commentSchema = new mongoose.Schema({
message : { type : String }
});
const postSchema = new mongoose.Schema({
comments : [commentSchema]
});
👆 This would by default include all comments of the post if you retrieve the post from the database. However it would also require a different code for interacting with those comments (adding, deleting, ...) but you can read about it in the docs I am sure.

Mongoose pre-save hook fires, but does not persist data

I am encountering a problem where my Mongoose pre.save() hook is firing, but the attribute does not get saved to the database. I have been searching for a long time without finding an answer.I found this thread, and the behaviour I am experiencing is very similiar, but OP's problem is related to the context of this, whereas I seem to have a different problem.
Here is my models.js:
'use strict';
const mongoose = require("mongoose");
const slugify = require("slugify");
let Schema = mongoose.Schema;
let BlogPostSchema = new Schema({
title: {
type: String,
required: true
},
createdAt: {type: Date, default: Date.now},
updatedAt: {type: Date, default: Date.now},
author: String,
post: {
type: String,
required: true
}
});
BlogPostSchema.pre('save', function(next) {
this.slug = slugify(this.title);
console.log(this.slug);
next();
});
// Passed to templates to generate url with slug.
BlogPostSchema.virtual("url").get(function() {
console.log(this.slug);
console.log(this.id);
return this.slug + "/" + this.id;
});
BlogPostSchema.set("toObject", {getters: true});
let BlogPost = mongoose.model("BlogPost", BlogPostSchema);
module.exports.BlogPost = BlogPost;
And here is the relevant lines in the router file index.js:
const express = require('express');
const router = express.Router();
const BlogPost = require("../models").BlogPost;
// Route for accepting new blog post
router.post("/new-blog-post", (req, res, next) => {
let blogPost = new BlogPost(req.body);
blogPost.save((err, blogPost) => {
if(err) return next(err);
res.status(201);
res.json(blogPost);
});
});
I am able to save the blog post to the database, and my console.log's correctly prints out the slug to the console. However, the this.slug in the pre-save hook does not get persisted in the database.
Can anybody see what the problem is here? Thank you so much in advance.
Mongoose will act according to the schema you defined.
Currently, your schema does not contain s filed named slug.
You should add a slug field to your schema.
Changing your current schema to something like this should work:
let BlogPostSchema = new Schema({
slug: String,
title: {
type: String,
required: true
},
createdAt: {type: Date, default: Date.now},
updatedAt: {type: Date, default: Date.now},
author: String,
post: {
type: String,
required: true
}
});

Retrieve Array in Subdocument MongoDB

I have a Users model structure somewhat like this:
const userSchema = new mongoose.Schema({
email: { type: String, unique: true },
password: String,
todosDo: [models.Do.schema],
}
And the child "Do" schema somewhat like this (in a different file):
const doSchema = new mongoose.Schema({
name: {type: String, default : ''},
user: {type: mongoose.Schema.ObjectId, ref: 'User'},
createdAt: {type : Date, default : Date.now}
});
And I'm trying to figure out how to retrieve the todosDo array for the signed in user. This is what I've got so far:
// Get all "Do" todos from DB
// Experimenting to find todos from certain user
User.findById(req.user.id, function(err, user){
if(err){
console.log(err);
} else {
doTodos = user.todosDo, // this obviously doesn't work, just an idea of what I was going for
console.log(doTodos);
finished();
}
});
Am I referencing the child/parent wrong or am I just not retrieving the array right? Any help is greatly appreciated!
As far I guess you may want to edit as raw js objects so you need to use lean() function. without using lean() function user is mongoose object so you can't modify it.
can try this one:
User.findById(req.user.id)
.lean()
.exec(function (err, user) {
if(err){
console.log(err);
return res.status(400).send({msg:'Error occurred'});
}
if(!user) {
return res.status(400).send({msg:'User Not found'});
}
doTodos = user.todosDo;
console.log(user.todosDo); // check original todos
console.log(doTodos);
return res.status(200).send({doTodos : doTodos }); // return doTodos
});
and to refer child schema in parent schema from different model you can access a Model's schema via its schema property.
say in doSchema.js file
const doSchema = new mongoose.Schema({
name: {type: String, default : ''},
user: {type: mongoose.Schema.ObjectId, ref: 'User'},
createdAt: {type : Date, default : Date.now}
});
module.exports = mongoose.model( 'DoSchema', doSchema );
in user.js file
var DoModel = require('./doSchema');// exact path
const userSchema = new mongoose.Schema({
email: { type: String, unique: true },
password: String,
todosDo: [DoModel.schema],
}
Thanks for your help everybody! My problem was that I needed to push all the newly created todos in the post route to todosDo, so then I could retrieve them at the get route. Everything's working now!

Nodejs, Mongoose and Jade get no data from Database

i was searching for my Problem but even don't know where is the problem.
I get the title which is set in my route but no data from the database...
My model:
var mongoose = require('mongoose'),
Schema = mongoose.Schema,
ObjectId = Schema.Types.ObjectId;
var blogSchema = new Schema({
title: { type: String, required: true },
author: { type: String, required: true },
body: { type: String, required: true },
date: { type: String, required: true },
hidden: Boolean
});
module.exports = mongoose.model('Blog', blogSchema);
my router:
var express = require('express'),
Blog = require('../models/blog'),
moment = require('moment');
moment.lang('de');
var router = express.Router();
router.get('/articles', function(req, res) {
Blog.find(function(err, docs){
return res.render('blog/articles', {
title: 'Blog_',
articles: docs
});
});
});
app.use('/blog', router);
my jade
extends ../layouts/default
include ../elements/form-elements
block content
h1= title
each article in articles
.col-md-12
div.title= article.title
the only one i get displayed at the Page is
Blog_
So what iam doing wrong?
At the error file it only says:"Cannot read property 'title' of undefined"
So the articles objects are not set...but why?
Thanks so much
edit 1:
change article.title to article doesn't change anything
in the log files is
GET /blog/articles HTTP/1.1 304 - - 3 ms
edit 2:
it seems that node doesnt get any data from the db...
and yes there is one testdata set ;)
console.log() ->
err: null
docs: []
Solution is posted as answer
got the solution...
the model wasn't right...
var blogSchema = new Schema({
title: { type: String, required: true },
author: { type: String, required: true },
body: { type: String, required: true },
date: { type: String, required: true },
hidden: Boolean
}, {collection : 'blog'});
have to name the collection at the end...cause its written in small letters -.-
What a false - never ever do it again ^^
I know this is a very old question and it's marked by OP as answered, but I think the real problem was in "my router", you're not referencing your "docs" (the data coming back from the database) correctly. Keep in mind "docs" is an array so you would need to reference them like this:
router.get('/articles', function(req, res) {
Blog.find(function(err, docs){
return res.render('blog/articles', {
title: docs[0].title, // Get the title for first entry
articles: docs[0].body // Get body for the first entry
});
});
});
I'm hardcoding the array index but you can use a loop to get every item from the array.
I don't think OPs solution fixes the problem because...
By default, when compiling models with:
const someModel = mongoose.model('someModel', SomeSchema);
mongoose creates a collection using 'someModel' name and adding an "s" at the end, so if you check your database, your collection should
appear as 'someModels'. With OP's solution:
{ collection: 'blog' }
as the second parameter when creating the blog schema
var blogSchema = new Schema();
That default behavior is overwritten and the name of your collection will be whatever you set as the value for collection, in this case, "blog".
You can read more about it in Mongoose official docs
or in the Models section in MDN - Node/Express/Mongoose

Resources