How do I insert an element into an existing document? - node.js

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');
}
});

Related

updating nested documents in mongoDB(node.js)

i am trying to update a value in the object of my embedded schema(comments schema) whose value i had previously stored 0 by default. i have tried all the ways to update but none of the stackoverflow answer worked.
my code is
var checkedBox = req.body.checkbox;
User.updateOne({_id: foundUser._id},{$set :{comments:{_id :checkedBox,cpermission:1,}}},function(err,updatec){
if(err){
console.log(err);
}
else{
console.log("successfull");
console.log(updatec);
}
});
i had comment schema nested in user schema,here foundUser._id is the particular users id,and checkedBox id is the embedded objects particular id. previously my cpermission was 0,set by default,but now i want to update it to 1. although this is updating my schema,but deleting the previous images and comments in the schema aswell.
where am i going wrong?
here is my schema
const commentSchema = new mongoose.Schema({
comment: String,
imagename: String,
cpermission:{type:Number,default:0},
});
const Comment = new mongoose.model("Comment", commentSchema);
const userSchema = new mongoose.Schema({
firstname: String,
lastname: String,
email: String,
password: String,
comments: [commentSchema],
upermission:{type:Number,default:0},
});
userSchema.plugin(passportLocalMongoose);
const User = new mongoose.model("User", userSchema);
First, you need to convert checkbox in the array, as it will be a string if you select a single element
Then wrap it with mongoose.Types.ObjectId as a precaution
Then you can use arrayFilters to update multiple matching array elements
var checkedBox = req.body.checkbox;
if (!Array.isArray(checkedBox)) {
checkedBox = [checkedBox]
}
checkedBox = checkedBox.map(id => mongoose.Types.ObjectId(id))
User.updateOne(
{ _id: foundUser._id }, // filter part
{ $set: { 'comments.$[comment].cpermission': 1 } }, // update part
{ arrayFilters: [{ 'comment._id': {$in: checkedBox }}] }, // options part
function (err, updatec) {
if (err) {
console.log(err);
}
else {
console.log("successfull");
console.log(updatec);
}
});
your comment is the array of documents. if you want to update an element of an array must be select it. for that must be added another condition to the first section of updateOne then in seconde section use $ for update selected element of the array.
User.updateOne(
{_id: foundUser._id, 'comments._id': checkedBox},
{
$set: {'comments.$.cpermission': 1}
}
, function (err, updatec) {
if (err) {
console.log(err)
}
else {
console.log('successfull')
console.log(updatec)
}
})
for more information, you can read this document form MongoDB official website.
Array Update Operators
var checkedBox = req.body.checkbox;
User.updateOne(
{ _id: foundUser._id, "comment._id": checkedBox },
{ $set: { "comment.$.cpermission": 1 } },
function (err, update) {
if (err) {
console.log(err);
} else {
console.log("successfull");
console.log(update);
}
}
);

Mongoose document.populate() is not working

I am using a pretty simple Node/Mongo/Express setup and am trying to populate referenced documents. Consider my schemas for "Courses" which contain "Weeks":
// define the schema for our user model
var courseSchema = mongoose.Schema({
teachers : { type: [String], required: true },
description : { type: String },
previous_course : { type: Schema.Types.ObjectId, ref: 'Course'},
next_course : { type: Schema.Types.ObjectId, ref: 'Course'},
weeks : { type: [Schema.Types.ObjectId], ref: 'Week'},
title : { type: String }
});
// create the model for Course and expose it to our app
module.exports = mongoose.model('Course', courseSchema);
I specifically want to populate my array of weeks (though when I changed the schema to be a single week, populate() still didn't work).
Here is my schema for a Week (which a Course has multiple of):
var weekSchema = mongoose.Schema({
ordinal_number : { type: Number, required: true },
description : { type: String },
course : { type: Schema.Types.ObjectId, ref: 'Course', required: true},
title : { type: String }
});
// create the model for Week and expose it to our app
module.exports = mongoose.model('Week', weekSchema);
Here is my controller where I am trying to populate the array of weeks inside of a course. I have followed this documentation:
// Get a single course
exports.show = function(req, res) {
// look up the course for the given id
Course.findById(req.params.id, function (err, course) {
// error checks
if (err) { return res.status(500).json({ error: err }); }
if (!course) { return res.sendStatus(404); }
// my code works until here, I get a valid course which in my DB has weeks (I can confirm in my DB and I can console.log the referenced _id(s))
// populate the document, return it
course.populate('weeks', function(err, course){
// NOTE when this object is returned, the array of weeks is empty
return res.status(200).json(course);
});
};
};
I find it strange that if I remove the .populate() portion from the code, I get the correct array of _ids back. But when I add the .populate() the returned array is suddenly empty. I am very confused!
I have also tried Model population (from: http://mongoosejs.com/docs/api.html#model_Model.populate) but I get the same results.
Thanks for any advice to get my population to work!
below should return course with populated weeks array
exports.show = function(req, res) {
// look up the course for the given id
Course.findById(req.params.id)
.populate({
path:"weeks",
model:"Week"
})
.exec(function (err, course) {
console.log(course);
});
};
### update: you can populate from instance also ###
Course.findById(req.params.id, function (err, course) {
// error checks
if (err) { return res.status(500).json({ error: err }); }
if (!course) { return res.sendStatus(404); }
// populate the document, return it
Course.populate(course, { path:"weeks", model:"Weeks" }, function(err, course){
console.log(course);
});
});
### Update2: Perhaps even more cleanly, this worked: ###
Course.findById(req.params.id, function (err, course) {
// error checks
if (err) { return res.status(500).json({ error: err }); }
if (!course) { return res.sendStatus(404); }
// populate the document, return it
console.log(course);
}).populate(course, { path:"weeks", model:"Weeks" });
here it seems like you are using course.populate() instead of Course.populate()
Use this code instead of yours,I change only one single word course.populate() to Course.populate()
In your case "course" is instance but you need to use Course(Model)
Course.findById(req.params.id, function (err, course) {
if (err) { return res.status(500).json({ error: err }); }
if (!course) { return res.sendStatus(404); }
// Guys in some case below three-line does not work in that case you must comment these lines and uncomments the last three-line
Course.populate('weeks', function(err, course){
return res.status(200).json(course);
});
// Course.populate({ path:"weeks", model:"Weeks" }, function(err, course){
// return res.status(200).json(course);
// });
};

Need to do a many comments belong to one article relation MongoDB

I am using Mongoose/MongoDB and I am trying to associate many comments to one article. My app begins by scraping from a website and then the user has the option to save each article that was scraped into the MongoDB. When the user chooses to save one article, I save it into database. So when a user clicks on one of their saved articles, they can comment on them. Each article has its own comment section I need to retrieve the correct comments.
//My post comment request in JS file
function postComment(){
var articleComment = {
comment: $('#comment').val().trim()
}
$.post('/comments/' + articleID, articleComment).done(function(data){
$('.main-popup').fadeOut();
console.log('DONNE', data);
});
}
//Post route in controller
router.post('/comments/:id', function(req, res){
var newComment = new Comment(req.body);
newComment.save(function(err, doc){
if(err){
console.log(err);
}else{
Comment.findOneAndUpdate({ "_id": doc._id }, { "article": req.params.id }).exec(function(err, doc){
if(err){
console.log(err);
res.send(err);
}else{
res.send(doc);
}
});
}
});
});
//Get request to get correct comments when clicked on specific article
function showCommentBox(){
$('.comments').empty();
$('#comment').val("");
articleID = $(this).attr('data-article-id');
$.get('/comments/' + articleID, function(data){
if(data.article){ //This is undefined*********************
for(var x = 0; x < data.comment.length; x++){
$('.comments').append("<div><h2>" + data.comment[x].comment + "</h2><span><button>×</button></span></div>");
}
}
$('.main-popup').fadeIn();
});
}
//Get route in controller
router.get('/comments/:id', function(req, res){
Comment.findOne({ "article": req.params.id }).populate("article").exec(function(err, doc){
if(err){
console.log(err)
}else{
res.json(doc);
}
});
});
//Article Model
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var ArticleSchema = new Schema({
title: {
type: String
},
link: {
type: String
},
description: {
type: String
},
img: {
type: String
}
});
var Article = mongoose.model("Article", ArticleSchema);
module.exports = Article;
//Comment Model
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var CommentSchema = new Schema({
comment: {
type: String
},
article: {
type: Schema.Types.ObjectId,
ref: 'Article'
}
});
var Comment = mongoose.model('Comment', CommentSchema);
module.exports = Comment;
First, you're missing $set when you do .findOneAndUpdate. Also I think you should convert a string to Mongo ObjectId before setting it.
So it might look likt this:
const ObjectId = mongoose.Types.ObjectId;
Comment.findOneAndUpdate({ "_id": doc._id }, {$set: {"article": new ObjectId(req.params.id) }})
Also you don't need to make 2 database calls. You could article id before saving newComment and then simply send it as a response like this:
//Please notice that mongoose.Schema.Types.ObjectId and mongoose.Types.Object are different types.
//You need this one here:
const ObjectId = mongoose.Types.ObjectId;
router.post('/comments/:id', function(req, res){
var newComment = new Comment(req.body);
newComment.article = new ObjectId(req.params.id);
newComment.save(function(err, doc){
if (err) {
console.error(err);
res.send(err);
return;
}
res.send(doc);
});
});

Mongoose adding new entry to parent

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();

Mongoose populate issue - array object

My schema is as below
Sectionschema
var SectionSchema = new Schema({
name: String,
documents : {
type : [{
type: Schema.ObjectId,
ref: 'Document'
}]
}
}
}
DocumentSchema
var DocumentSchema = new Schema({
name: String,
extension: String,
access: String, //private,public
folderName : String,
bucketName : String,
desc: String
});
Api.js
exports.section = function(req, res, next, id) {
var fieldSelection = {
_id: 1,
name: 1,
documents : 1
};
var populateArray = [];
populateArray.push('documents');
Section.findOne({
_id: id
}, fieldSelection)
.populate(populateArray)
.exec(function(err, section) {
if (err) return next(err);
if (!section) return next(new Error('Failed to load Section ' + id));
// Found the section!! Set it in request context.
req.section = section;
next();
});
}
If I go this way, I have
the 'documents' object is []. However if I remove, "populateArray.push('documents');" then I get documents:['5adfsadf525sdfsdfsdfssdfsd'] -- some object Id (atleast)
Please let me know the way I need to populate.
Thanks.
Change your query to
Section.findOne({
_id: id
}, fieldSelection)
.populate('documents.type')
.exec(function(err, section) {
if (err) return next(err);
if (!section) return next(new Error('Failed to load Section ' + id));
// Found the section!! Set it in request context.
req.section = section;
next();
});
and this works. You need to give the path to populate.
If you just want "documents" in your schema pointing to Array of ObjectID which you will populate later. then you can use this.
var SectionSchema = new Schema({
name: String,
documents : [{
type: Schema.ObjectId,
ref: 'Document'
}]
});
And use the following to populate it
Section.findOne({
_id: id
}, fieldSelection)
.populate('documents')
.exec(function(err, section) {
if (err) return next(err);
if (!section) return next(new Error('Failed to load Section ' + id));
// Found the section!! Set it in request context.
req.section = section;
next();
});

Resources