Inesrt data into Nested Array (comment action) MongoDB - node.js

I'm building a simple social platform; I'm currently building out the schema for commenting on a post, but users should be able to comment on other comments as well. So what I'm trying to do is, once a user comments on the post, a condition is being checked whether the postId params matches the post._id in the database, therefore this is a higher order comment action. Now if I were to comment on somebody else's comment, I would be checking whether the post._id matches the post as well as checking whether the comment._id matches as well, but I'm having issues by inserting it into the nested array.
Schema (POST)
onst mongoose = require('mongoose');
const Schema = mongoose.Schema;
const postSchema = new Schema({
uid: {
type: mongoose.Schema.Types.ObjectId,
required: true,
ref: "users",
},
uname: {type: String, required: true},
upic: {type: String, required: true},
message: { type: String,},
media: {type: String,},
likes:[],
comments:[],
commentCount: {type: Number, default: 0},
likeCount: {type: Number, default: 0},
repostCount: {type: Number, default: 0},
postedOn: { type: Date},
});
postSchema.pre('save', function(next){
this.postedOn = Date.now();
next();
});
var PostModel = mongoose.model('posts', postSchema);
module.exports = PostModel;
Controller (Comment)
{/* Higher order comment */}
module.exports.commentPost = async (req, res) => {
try{
await PostModel.findByIdAndUpdate(req.params.postId,
{
$push: {
comments: [{
_id: uuid.v1(),
postId: req.params.postId,
uid: req.user._id,
message: req.body.message,
postedOn: Date.now(),
uname: req.user.name,
upic: req.user.pic,
likes: [],
comments: [],
likeCount: 0,
commentCount: 0,
}]
},
$inc: { commentCount: 1}
}).exec((err,result)=>{
if(err){
return res.status(422).json({error:err})
}else{
res.json(result)
}
});
} catch(err){
throw new ErrorResponse(err, 404);
}
}
{/* Lower order comment */}
module.exports.commentCommentPost = async (req, res) => {
try{
await PostModel.findByIdAndUpdate({"_id": req.params.postId, "comments._id": req.body.commentId},
{
$push: {
comments: [{
comments: [{
_id: uuid.v1(),
postId: req.params.postId,
uid: req.user._id,
message: req.body.message,
postedOn: Date.now(),
uname: req.user.name,
upic: req.user.pic,
likes: [],
comments: [],
likeCount: 0,
commentCount: 0,
}]
}]
},
$inc: { commentCount: 1}
}).exec((err,result)=>{
if(err){
return res.status(422).json({error:err})
}else{
res.json(result)
}
});
} catch(err){
throw new ErrorResponse(err, 404);
}
}
I must be doing something wrong by updating the nested array within a nested array, any help would be appreciated.

I was able to fix the issue, here is the solution on what I've done, which works perfectly fine now.
module.exports.commentCommentPost = async (req, res) => {
try{
await PostModel.update({_id: req.params.postId, 'comments._id': req.body.commentId},
{
$push: {
'comments.$.comments': {
_id: uuid.v1(),
postId: req.params.postId,
uid: req.user._id,
message: req.body.message,
postedOn: Date.now(),
uname: req.user.name,
upic: req.user.pic,
likes: [],
comments: [],
likeCount: 0,
commentCount: 0,
}
}
}).exec((err,result)=>{
if(err){
return res.status(422).json({error:err})
}else{
res.status(200).json(result)
}
});
} catch(err){
throw new ErrorResponse(err, 404);
}
}

Related

NodeJS, Mongoose if ID exists do nothing, if doesn't exist push the new one to the array

I have User model and every user can have an array of ownerId's. I want to make an API which will push a new ownerId to the array, but if this ownerId already exists, do nothing..
I have tried $addToSet but it doesn't work..
However it works with $push, but if ownerId already exists it keeps pushing the same ownerId so i have duplicates which is not OK..
User model
const UserSchema = new mongoose.Schema({
email: { type: String, required: true, min: 6, max: 255 },
password: { type: String, required: true, min: 4, max: 1024 },
role: { type: String, required: true, default: "User" },
owners: [
{
type: Schema.Types.ObjectId,
ref: "Owners",
required: false,
},
],
});
And my NodeJS Mongoose API:
exports.addOwnerToUser = async (req: Request, res: Response) => {
try {
let ObjectID = require("mongodb").ObjectID;
const user = {
email: req.body.email,
ownerId: req.body.ownerId,
};
const updatedUser = await User.findOneAndUpdate(
{ _id: req.params.userId },
{
$push: { owners: req.body.ownerId },
}
);
console.log(updatedUser);
res.status(201).json({ sucess: true, msg: "User updated sucessfully" });
} catch (err) {
res.status(404).json(err);
}
};
Thanks
$push will just push data in array, in your case you should use $addToSet
$addToSet only ensures that there are no duplicate items added to the set and does not affect existing duplicate elements.
const updatedUser = await User.findOneAndUpdate({
{ _id: req.params.userId },
'ownerId.ownerId': {
'$ne': new mongoose.ObjectID(req.body.ownerId)
}
}, {
$addToSet: {
'ownerId.ownerId': new mongoose.ObjectID(req.body.ownerId)
}
}, {
new: true
});
just remove below query
'ownerId.ownerId': {
'$ne': req.body.ownerId
}
Updated code.
const updatedUser = await User.findOneAndUpdate({
_id: req.params.userId,
}, {
$addToSet: {
'ownerId.ownerId': req.body.ownerId
}
}, {
new: true
});
OR
with ownerId Query
const updatedUser = await User.findOneAndUpdate({
_id: req.params.userId,
'ownerId.ownerId': {
'$ne': req.body.ownerId
}
}, {
$push: {
'ownerId': {ownerId: req.body.ownerId }
}
}, {
new: true
});
Try this:
exports.addOwnerToUser = async (req: Request, res: Response) => {
try {
let ObjectID = require("mongodb").ObjectID;
const user = {
email: req.body.email,
ownerId: req.body.ownerId,
};
const updatedUser = await User.findOne({
_id: req.params.userId
})
.then(user => {
if (user.ownerId[0]) {
user.ownerId[0].ownerId = req.body.ownerId;
}
})
console.log(updatedUser);
res.status(201).json({
sucess: true,
msg: "User updated sucessfully"
});
} catch (err) {
res.status(404).json(err);
}
};
Your schema design is not right, that is why $addToSet is not working for you.
so, if you want multiple owners in the user object please change your schema design to this
const UserSchema = new mongoose.Schema({
email: { type: String, required: true, min: 6, max: 255 },
password: { type: String, required: true, min: 4, max: 1024 },
role: { type: String, required: true, default: "User" },
owners: [{
type: Schema.Types.ObjectId,
ref: "Owners",
required: false,
}],
});
After this use $addToSet to add the new owner id in the user object
User.findOneAndUpdate(
{ _id: req.params.userId },
{ $addToSet: { owners: req.body.ownerId } }
)
it will not add the duplicates
For Reference: https://www.mongodb.com/docs/manual/reference/operator/update/addToSet/
Note:
As per my previous experience with this kind of work, it is better if you change the key ownerId to owners
because in general these are the owners array not the ownerId

How to update a field-List in MongoDB using Mongoose and Nodehs?

Hello dear Stackoverflow team.
I am trying to patch a user, which can handle several "devices". I am using nodeJs with Express and Mongoose (MongoDB). My User model is the following:
const userSchema = new Schema({
name: {type: String, required: true},
lastname: {type: String, required: true},
email: {type: String, required: true, trim: true, lowercase: true, unique:
true},
password: {type: String, required: true, minlength: 5},
userTyp: {type: String, required: true,
enum: {values: ['Administrator', 'Doctor','Patient','Optiker'], message:
'{VALUE} is not supported' }},
image: {type: String},
devices: [ {device: {type: Schema.Types.ObjectId, ref: "Device"}} ]
});
and I want to have something like this everytime i do a patch:
{
"user": {
"_id": "6138cd30ffc5239bba72e6c0",
"name": "Fernando",
"lastname": "Gonzalez",
"email": "f.gonzalez#unitransferklinik.de",
"password": "Hol087+/*",
"userTyp": "Administrator",
"image": "sdsadsadsa/asdfasdas",
"devices": [
{
"device": "6138c7587ab4b5fc4d369230"
},
{
"device": "6138c7587ab4b5fc4d365210"
}
],
}
}
How can I implement in my function:
const updateUser = async (req, res, next) => {
const { name, lastname, email, password, userTyp, device } = req.body;
const userID = req.params.userID;
let updatedUser;
try {
updatedUser = await User.findById(userID);
}catch(err){
console.log(err);
return next(new HttpError('Something happend.', 500));
}
updatedUser.name = name;
updatedUser.devices = [device, ...updatedUser.devices];
try{
updatedUser.save();
}catch (err) {
return next(new HttpError('It could not uodate device.', 500));
}
});
res.status(200).json({user: updatedUser.toObject( {getters: true} )});
};
In easy words, I want to updated the list everytime that i do a patch with a new device, and I can fetch later all the device list per user.
Thanks a lot!
regards,
Eliot
You can use findOneAndUpdate function of mongoose library
const dynamicModel = libMongoose.model(collection_name, userSchema);
var filter = { userID: req.params.userID };
var update = { name: name, devices : [...device, ...updatedUser.devices]};
//callback approach
dynamicModel.findOneAndUpdate(filter, update, (err, resp) => {
if(err) {
console.log("Error while updating record " + JSON.stringify(err));
}
if(!resp) {
console.log("Couldn't find record");
} else {
console.log("Updated data to DB");
}
});
You can also refer here for async await, Hope this helps!

How to delete comment that is nested in Post schema with mongoose and nodejs?

I want to be able to delete comment that is inside my Post model.
This is my Schema for Post model:
const PostSchema = new Schema({
userID: {
type: Schema.Types.ObjectId,
ref: 'user'
},
content: {
type: String,
required: true
},
registration_date: {
type: Date,
default: Date.now
},
likes: [
{
type: Schema.Types.ObjectId,
ref: "user"
}
],
comments: [
{
text: String,
userID: {
type: Schema.Types.ObjectId,
ref: 'user'
}
}
]
})
And I have this route:
router.delete('/comment/:id/:comment_id', auth, async (req, res) => {
const postId = req.params.id
const commentId = req.params.comment_id
}
comments in post looks like this:
comments: [
{
_id: 5f1df4cf5fd7d83ec0a8afd8,
text: 'comment 1',
userID: 5efb2296ca33ba3d981398ff
},
{
_id: 5f1df4d35fd7d83ec0a8afd9,
text: 'commnet 2',
userID: 5efb2296ca33ba3d981398ff
}
]
I want to delete comment, and don't know how to do it. Does anyone have idea how to do it?
First we find the post by findByIdAndUpdate then we delete the comment using $pull from the array of comments.
router.delete("/comment/:id/:comment_/id", async function (req, res) {
try {
const post = await Post.findByIdAndUpdate(
req.params.id,
{
$pull: { comments: {_id:req.params.comment_id}},
},
{ new: true }
);
if (!post) {
return res.status(400).send("Post not found");
}
} catch (err) {
console.log(err);
res.status(500).send("Something went wrong");
}
});

mongoose: create new element in X collection, update another in Y collection

I am trying to develop a CRUD app for users to store, add, delete and update recipes. It's built on MEVN stack. As I need to show the user, which recipes they have created, I am trying to create a recipe based on this model:
const RecipeSchema = new Schema({
title: {
type: String,
required: [true, 'Title of the recipe is required'],
},
category: {
type: Array,
required: [true, 'Category is required'],
},
description: {
type: String,
required: [true, 'Description is required'],
},
imgUrl: {
type: String,
required: [true, 'Image is required'],
},
ingredients: {
type: Array,
required: [true, 'Ingredients are required'],
},
timeOfPreparation: {
type: String,
required: true,
},
preparation: {
type: String,
required: true,
},
sourceName: {
type: String,
required: true,
},
sourceUrl: {
type: String,
required: true,
},
author: [{ type: mongoose.Schema.Types.ObjectId, ref: 'User' }],
});
const Recipe = mongoose.model('Recipe', RecipeSchema);
module.exports = Recipe;
And at the same time update User model, based on this:
const UserSchema = Schema({
googleId: String,
name: String,
favorites: [{ type: mongoose.Schema.Types.ObjectId, ref: 'Recipe' }],
authoredRecipes: [{ type: mongoose.Schema.Types.ObjectId, ref: 'Recipe' }],
});
const User = mongoose.model('User', UserSchema);
module.exports = User;
In the controller, I have this method (as per #Stock Overflaw comment):
exports.create_new_recipe = (req, res, next) => {
Recipe.create(req.body)
.then(recipe => {
User.update(
{ _id: req.body.author },
{
$push: { authoredRecipes: recipe.id },
}
);
res.send(res.status);
})
.catch(error => {
res.status(500).json({ error });
});
};
This method is called when I go to /create endpoint. However, even though I do get all the correct ids (req.body.author and recipe.id), I cannot get this to work. In my mLab recipe collection the recipe is displayed correctly (all data that I have inserted with authorId), however in the User collection, the array of authoredRecipes stays empty.
How can I get mongoose to both create an object in one collection as well as update another object based on their ids?
The documentation for findByIdAndUpdate requires the _id field as its value, not an object:
User.findByIdAndUpdate(req.body.author, {
$push: { authoredRecipes: recipe.id }
});
// equivalent to the more general method:
User.findOneAndUpdate({ _id: req.body.author }, {
$push: { authoredRecipes: recipe.id }
});
// and if you don't need the modified document in your callback, this should be faster:
// EDIT: this is advised against (we should use a user object, not the collection)
User.update({ _id: req.body.author }, { // or updateOne
$push: { authoredRecipes: recipe.id }
});
Edit: a working, minimal example
Mind {new: true} maybe? Depending on how you test whether it works...
const mongoose = require('mongoose');
const fs = require('fs');
const userIdFile = './tmp.txt'; // just for this test
mongoose.connect('mongodb://localhost/meuh', {
useNewUrlParser: true, // removes a deprecation warning
useFindAndModify: false // removes another deprecation warning
});
// make schemas/models
const RecipeSchema = mongoose.Schema({
title: { type: mongoose.Schema.Types.String }
});
const UserSchema = mongoose.Schema({
name: { type: mongoose.Schema.Types.String },
data: [{ type: mongoose.Schema.Types.ObjectId, ref: 'Recipe' }]
});
const RecipeModel = mongoose.model('Recipe', RecipeSchema);
const UserModel = mongoose.model('User', UserSchema);
// user precreation
// UserModel.create({
// name: 'me, myself and I'
// }).then((user) => {
// fs.writeFile(userIdFile, user.id, console.log.bind(null, 'error writing file:'));
// mongoose.connection.close();
// });
// return;
// fetch user
const userId = fs.readFileSync(userIdFile);
let pushedRecipeId; // to test everything went smooth
RecipeModel.create({
title: 'pasta solo'
}).then((recipe) => {
console.log('created recipe:', recipe);
pushedRecipeId = recipe.id;
return UserModel.findOneAndUpdate(
{ _id: userId },
{ $push: { data: recipe.id } },
{ new: true } // forces callback to be passed a fresh object
);
}).then((user) => {
console.log('updated user:', user);
console.log('izok:', !!~user.data.indexOf(pushedRecipeId));
mongoose.connection.close();
}).catch((err) => {
console.log('error', err);
mongoose.connection.close();
})
Example output I got:
# creating user (uncommented this part)
ubuntu#ubuntu-VirtualBox:~/web/test$ node .
error writing file: null
# calling for $push (re-commented user creation)
ubuntu#ubuntu-VirtualBox:~/web/test$ node .
created recipe: { _id: 5c72be7032bd2f1acad37c95, title: 'pasta solo', __v: 0 }
updated user: { data: [ 5c72be7032bd2f1acad37c95 ],
_id: 5c72be6a8143fd1aa9416d85,
name: 'me, myself and I',
__v: 0 }
izok: true
# again $push
ubuntu#ubuntu-VirtualBox:~/web/test$ node .
created recipe: { _id: 5c72c020c2ac7a1b8c65fa36, title: 'pasta solo', __v: 0 }
updated user: { data: [ 5c72be7032bd2f1acad37c95, 5c72c020c2ac7a1b8c65fa36 ],
_id: 5c72be6a8143fd1aa9416d85,
name: 'me, myself and I',
__v: 0 }
izok: true
# and again
ubuntu#ubuntu-VirtualBox:~/web/test$ node .
created recipe: { _id: 5c72c023bf62331b97ef096b, title: 'pasta solo', __v: 0 }
updated user: { data:
[ 5c72be7032bd2f1acad37c95,
5c72c020c2ac7a1b8c65fa36,
5c72c023bf62331b97ef096b ],
_id: 5c72be6a8143fd1aa9416d85,
name: 'me, myself and I',
__v: 0 }
izok: true
# end
ubuntu#ubuntu-VirtualBox:~/web/test$
I don't see what's wrong in your code, but at least you have something to compare with... hope this helps!

Mongoose not deleting subdocument

My delete route is
const id = req.body.id;
const postId = req.body.postId;
if (mongoose.Types.ObjectId.isValid(id)) {
Comment.findByIdAndRemove({ _id: id }, (err, cRes) => {
if (err) return err;
Post.findOneAndUpdate({ _id: postId }, {
$pull: {
Comments: {
_id: id
}
}
}, (err, doc, res) => {
if (err) console.log(err);
res.redirect(req.get('referer'));
});
});
}
And the problem is that it does delete the comment from the Comment table but it doesn't delete the comment to the related Post, why is that?
PostSchema
var PostSchema = new mongoose.Schema({
Author: String,
Title: String,
Description: String,
Comments: [{
type: mongoose.Schema.Types.ObjectId, ref: 'Comment'
}],
Tags: [{
type: mongoose.Schema.Types.String, ref: 'Tag'
}],
CreatedOn: Date,
LastEditOn: Date
});
CommentSchema
var CommentSchema = new mongoose.Schema({
_postId: {
type: String,
ref: 'Post'
},
Author: String,
Description: String,
CreatedOn: Date,
LastEditBy: Date
});
no need to put _id during pull because you haven't mentioned any key in Comments of Post collection.
if (mongoose.Types.ObjectId.isValid(id)) {
Comment.findByIdAndRemove({ _id: id }, (err, cRes) => {
if (err) return err;
Post.update({ _id: postId }, {
$pull: {
Comments: id
}
}, (err, doc, res) => {
if (err) console.log(err);
res.redirect(req.get('referer'));
});
});
}
If you define _id in post schema like
Comments: [{
_id: { type: mongoose.Schema.Types.ObjectId, ref: 'Comment' }
}]
then your query could have worked.

Resources