Mongoose don't insert object into array - node.js

I have a simple express application that insets comments into posts, the issue is that the comments are never inserted but no errors are shown when post via postman it properly returns the post but with no comments.
Just try: this and this but seems to not working
This is my schema
interface PostAttrs {
userid: mongoose.Schema.Types.ObjectId;
username: string;
date: Date;
text: string;
image: string;
comments: Array<any>;
likes?: number;
}
const postSchema = new Schema<PostAttrs>({
userid: {
type: mongoose.Schema.Types.ObjectId,
required: true,
},
username: {
type: String,
required: true,
},
date: {
type: Date,
required: true,
},
text: {
type: String,
required: true,
},
image: {
type: String,
required: false,
},
comments: [
{
required: false,
date: {
type: String,
required: true,
},
user: {
type: Schema.Types.ObjectId,
ref: 'User',
required: true,
},
text: {
type: String,
required: true,
},
},
],
likes: {
type: Number,
required: true,
},
});
And the API route
export const createComment = async (req: Request, res: Response) => {
try {
const postId = req.params.postId;
const userId = req.params.userId;
const comment = req.body.comment;
var commentObj = {
date: new Date(),
userId: userId,
text: comment
};
await Post.findOneAndUpdate(
{ _id: postId },
{ new: true },
{$push: {
comments: { commentObj }
}},
(err: any, doc: any) => {
if (err) {
console.log("Something wrong when updating data!");
}
console.log(doc);
return res.status(200).send(doc);
}
);
} catch (error) { }
}
What's wrong with my code?

SOLVED: The problem was the order of the parameters in the findOneAndUpdate() sentence, first the search condition, next, the value to update, and finally the statement. So I had to change this
await Post.findOneAndUpdate(
{ _id: postId },
{ new: true },
{$push: {
comments: { commentObj }
}},
(err: any, doc: any) => {
if (err) {
console.log("Something wrong when updating data!");
}
console.log(doc);
return res.status(200).send(doc);
});
to
await Post.findOneAndUpdate(
{ _id: postId },
{$push: {
comments: { commentObj }
}},
{ new: true },
(err: any, doc: any) => {
if (err) {
console.log("Something wrong when updating data!");
}
console.log(doc);
return res.status(200).send(doc);
});

When using 'await' with Mongoose's methods like findOneAnd.... the method is not run unless you explicitly do so.
Try:
await Post.findOneAndUpdate(......).exec();
Also when using the await keyword you can refactor and remove the callbacks

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

Problem with update a single doc in monogdb using express and mongoose

I'm quiet new to mongodb and I'm actually trying to implement a follow-unfollow method in the backend
there are two types of users in the database
Mentors and mentees
only mentees can follow the mentors and mentors can only accept the request
the schema
Mentors
const MentorsSchema = mongoose.Schema({
name: { type: String, required: true },
designation: { type: String, required: true },
yearNdClass: {
type: String,
required: ["true", "year and class must be spciefied"],
},
respondIn: { type: String, required: true },
tags: {
type: [String],
validate: (v) => v == null || v.length > 0,
},
socialLinks: {
github: { type: String, default: "" },
twitter: { type: String, default: "" },
facebook: { type: String, default: "" },
instagram: { type: String, default: "" },
},
watNum: { type: Number, required: true },
email: { type: String, required: true, unique: true },
password: { type: String, required: true },
about: { type: String },
followers: [
{ type: mongoose.Schema.Types.ObjectId, ref: "Mentees", default: "" },
],
pending: [
{ type: mongoose.Schema.Types.ObjectId, ref: "Mentees", default: "" },
],
});
Mentee
const MenteeSchema = mongoose.Schema({
name: { type: String, required: true },
email: { type: String, required: true, unique: true },
password: { type: String, required: true },
yearNdClass: {
type: String,
required: ["true", "year and class must be spciefied"],
},
socialLinks: {
github: { type: String },
twitter: { type: String },
facebook: { type: String },
instagram: { type: String },
},
about: { type: String },
skillLooksFor: { type: String, required: true },
watNum: { type: Number, required: true },
following: [{ type: mongoose.Schema.Types.ObjectId, ref: "Mentors",default:"" },
],
});
you can see that there are two fields for mentors both following and pending arrays which consist of the ids of the mentees who follow the mentors and the ids of the mentees which yet to be accepted as a follower
I planned to create an endpoint where when a mentee gives a follow request it should be reached into the mentor pending array so that he can accept it later
so my logic like this
// #desc follow a mentor
// #route POST /api/mentees/follow-mentor/:id
// #access private
menteeRoute.post(
"/follow-mentor/:id",
isAuthorisedMentee,
expressAsyncHandler(async (req, res) => {
const { id } = req.params;
const mentee = await Mentees.findById(req.mentee.id);
const mentor = await Mentors.findById(id).select("-password");
// console.log(mentor)
if (mentee) {
try {
await Mentees.findOneAndUpdate(
{ _id: mongoose.Types.ObjectId(id) },
{ $addToSet: { "following.0": mentor._id } },
{ new: true }
);
await Mentors.findOneAndUpdate(
{ _id: mongoose.Types.ObjectId(mentor._id) },
{
$addToSet: {
"pending.0": id,
},
},
{ new: true },
);
res.json({
data: {
mentor,
mentee,
},
});
} catch (error) {
console.log(error);
throw new Error(error);
}
}
})
);
but the code didn't work.
can anyone help me to resolve the problem?
basically, when a mentee gives a follow request it should update the following array of mentee with the id of mentor and it should also update the pending array of mentor with the id of the mentee
PS: any alternative ideas are also welcome
Try to remove the .0 index and use the $push method.
Also, you should return the updated objects:
menteeRoute.post(
'/follow-mentor/:id',
isAuthorisedMentee,
expressAsyncHandler(async (req, res) => {
const { id } = req.params;
const mentee = await Mentees.findById(req.mentee.id);
const mentor = await Mentors.findById(id).select('-password');
// console.log(mentor)
if (mentee) {
try {
const updatedMentee = await Mentees.findOneAndUpdate(
{ _id: mongoose.Types.ObjectId(id) },
{ $push: { following: mentor._id } },
{ new: true }
);
const updatedMentor = await Mentors.findOneAndUpdate(
{ _id: mentor._id },
{
$push: {
pending: id,
},
},
{ new: true }
);
res.json({
data: {
mentor: updatedMentor,
mentee: updatedMentee,
},
});
} catch (error) {
console.log(error);
throw new Error(error);
}
}
})
);

Mongodb find specific comment within array of objects in post collection

i store comments in post collection like this:
...
const postSchema = new mongoose.Schema(
{
body: {
type: String,
},
userId: {
type: String,
required: true,
},
likes: {
type: Array,
default: [],
},
img: {
type: String,
default: null,
},
comments: [
{
body: {
type: String,
required: true,
},
userId: {
type: String,
required: true,
},
postId: {
type: String,
required: true,
},
},
{ timestamps: true },
],
},
{ timestamps: true }
);
...
i created this put route to update comment:
app.put("/update-comment", (req, res) => {
posts.updateComment(req, res);
});
updateComment function:
const updateComment = async (req, res) => {
try {
const post = await Post.findById(req.body.postId);
const comment = await post.comments.map((commentObj) => {
return commentObj.find({ _id: req.body.commentId });
});
await comment.updateOne({ $set: req.body });
res.status(200).json(comment);
} catch (err) {
res.status(500).json(err);
}
};
it finds the post by postId and simply loop the comments array to find the comment object with the comment id provided, i tried this route with postman providing the following json body:
{
"postId":"6242a4c75bce78154824fc8f",
"commentId":"6242ac32a61fd275ed13846b",
"body":"my first comment updated"
}
but it doesn't work, it returns 500 internal error, if i replaced the code with this:
try {
const post = await Post.findById(req.body.postId);
const comment = post.comments;
res.status(200).json(comment);
} catch (err) {
res.status(500).json(err);
}
it will indeed return the comments within that post as an array of objects, i don't know what's wrong, i made sure the postId and commentId provided by the json body is correct, what's the problem?

problem when creating one to many data in mongoose

so i'm creating CRUD with relation of two collections, then i got problem, i can't do push from first collection data to second collection. this is my code.
Schema
const CourseSchema = new Schema(
{
title: {
type: String,
required: true,
},
desc: String,
price: Number,
video: String,
category: String,
status: Number,
lessons: [
{
type: Schema.Types.ObjectId,
ref: "Lessons",
},
],
},
{
timestamps: true,
}
);
const course = mongoose.model("Courses", CourseSchema);
const LessonSchema = new Schema(
{
title: {
type: String,
required: true,
},
desc: String,
video: String,
status: Number,
},
{ timestamps: true }
);
const Lessons = mongoose.model("Lessons", LessonSchema);
code to do data push
Lessons.create(req.body)
.then((data) => {
res.status(200).send({
status: 200,
message: "Successfully Create Lessons",
data: data,
});
Course.findByIdAndUpdate(
courseId,
{ $push: { lessons: data._id } },
{ safe: true, upsert: true, new: true }
);
})
is there any solution for my problem? please help me, i'm just learning about one to many relation in nodejs using mongoose
You need a callback function to make it works.
Lessons.create(req.body)
.then((data) => {
res.status(200).send({
status: 200,
message: "Successfully Create Lessons",
data: data,
});
course.findByIdAndUpdate(
courseId,
{ $push: { lessons: data } },
{ safe: true, upsert: true, new: true },
function (err, newdoc) { // callback function
if (err) {
console.log(err);
} else {
console.log("completed");
}
}
);
})

Cast to ObjectId failed for value at path for model error

This is my Profile Schema:
const mongoose = require('mongoose');
const ProfileSchema = new mongoose.Schema({
user: {
// Special field type because
// it will be associated to different user
type: mongoose.Schema.Types.ObjectId,
ref: 'user',
},
company: {
type: String,
},
website: {
type: String,
},
location: {
type: String,
},
status: {
type: String,
required: true,
},
skills: {
type: [String],
required: true,
},
bio: {
type: String,
},
githubusername: {
type: String,
},
experience: [
{
title: {
type: String,
required: true,
},
company: {
type: String,
required: true,
},
location: {
type: String,
},
from: {
type: Date,
required: true,
},
to: {
type: Date,
},
current: {
type: Boolean,
default: false,
},
description: {
type: String,
},
},
],
education: [
{
school: {
type: String,
required: true,
},
degree: {
type: String,
required: true,
},
fieldofstudy: {
type: String,
required: true,
},
from: {
type: Date,
required: true,
},
to: {
type: Date,
},
current: {
type: Boolean,
default: false,
},
description: {
type: String,
},
},
],
social: {
youtube: {
type: String,
},
twitter: {
type: String,
},
facebook: {
type: String,
},
linkedin: {
type: String,
},
instagram: {
type: String,
},
},
date: {
type: Date,
default: Date.now,
},
});
module.exports = Profile = mongoose.model('profile', ProfileSchema);
This is my view api. It doesn't work. it only return Cast to ObjectId failed for value { 'experience._id': '5edcb6933c0bb75b3c90a263' } at path _id for model profile
router.get('/experience/viewing/:viewexp_id', auth, async (req, res) => {
try {
const exp = await Profile.findById({
'experience._id': req.params.viewexp_id,
});
if (!exp) {
return res.status(404).json({ msg: 'Experience not found' });
}
res.json(exp);
} catch (err) {
console.error(err.message);
res.status(500).send(err.message);
}
});
How can I fix this? I tried looking at the stackoverflow of the same errors. still it doesn't seem to work.
and this is what I am trying to hit
The problem is that you have to convert your string _id to mongoose object id using this function mongoose.Types.ObjectId and my suggestion is to use findOne function instead of findById,
var mongoose = require('mongoose');
router.get('/experience/viewing/:viewexp_id', auth, async (req, res) => {
try {
let id = mongoose.Types.ObjectId(req.params.viewexp_id);
const exp = await Profile.findOne(
{ "experience._id": req.params.viewexp_id },
// This will show your sub record only and exclude parent _id
{ "experience.$": 1, "_id": 0 }
);
if (!exp) {
return res.status(404).json({ msg: 'Experience not found' });
}
res.json(exp);
} catch (err) {
console.error(err.message);
res.status(500).send(err.message);
}
});
var mongoose = require('mongoose');
router.get('/experience/viewing/:viewexp_id', auth, async (req, res) => {
try {
const exp = await Profile.findOne({
'experience._id': mongoose.Types.ObjectId(req.params.viewexp_id),
});
if (!exp) {
return res.status(404).json({ msg: 'Experience not found' });
}
res.json(exp);
} catch (err) {
console.error(err.message);
res.status(500).send(err.message);
}
});
You are saving object id . but your param id is string. convert it in ObjectId. Please check my solution.
router.post(
"/",
[
auth,
[
check("status", "status is required").not().isEmpty(),
check("skills", "skills is required").not().isEmpty(),
],
],
async (req, res) => {
const errors = validationResult(req);
if (!errors.isEmpty()) {
return res.status(400).json({ errors: errors.array() });
}
const {
company,
website,
location,
bio,
status,
githubuername,
skills,
youtube,
facebook,
twitter,
instagram,
linkedin,
} = req.body;
const profileFileds = {};
profileFileds.user = req.user.id;
if (company) profileFileds.company = company;
if (website) profileFileds.website = website;
if (location) profileFileds.location = location;
if (bio) profileFileds.bio = bio;
if (status) profileFileds.status = status;
if (githubuername) profileFileds.githubuername = githubuername;
if (skills) {
profileFileds.skills = skills.split(",").map((skill) => skill.trim());
}
//Build profile object
profileFileds.social = {};
if (youtube) profileFileds.social.youtube = youtube;
if (twitter) profileFileds.social.twitter = twitter;
if (facebook) profileFileds.social.facebook = facebook;
if (linkedin) profileFileds.social.linkedin = linkedin;
if (instagram) profileFileds.social.instagram = instagram;
try {
let profile = await Profile.findOne({ user: req.user.id });
if (profile) {
//update
profile = await Profile.findOneAndUpdate(
{ user: req.user.id },
{ $set: profileFileds },
{ new: true }
);
return res.json(profile);
}
//Create profile
profile = new Profile(profileFileds);
await profile.save();
res.json(profile);
} catch (err) {
console.error(err.message);
res.status(500).send("server Error");
}
}
);

Resources