Update name in nested object array - node.js

I'm new to MongoDB using angular as frontend. I'm trying to update a name in nested object array.
My Schema is as follows:
const mongoose = require("mongoose");
const projectDragDropSchema = mongoose.Schema({
_idProject: mongoose.Schema.Types.ObjectId,
projectTitle: { type: String, required: true },
boards: [
{
_idBoard: mongoose.Schema.Types.ObjectId,
boardTitle: { type: String, required: false },
cards: [
{
type: new mongoose.Schema(
{
cardId: { type: mongoose.Schema.Types.ObjectId, required: true },
cardTitle: { type: String, required: false },
}
// { minimize: false }
),
required: false,
},
],
required: false,
},
],
});
module.exports = mongoose.model("ProjectDragDrop", projectDragDropSchema);
I'm trying to update the cardTitle.
I have written the multiple updates to it, but unable to find the correct one.
The Router:
router.patch(
"/updateProjectBoardCardName/:_idProject/:_id",
projectBoardsCards.updateCardName
);
The code:
exports.updateCardName = (req, res) => {
const idProject = req.params._idProject;
const boardID = req.params._id;
projectDragDropSchema
.update(
{ _idProject: idProject, "boards._id": boardID },
{ cards: { $elemMatch: { _id: req.body.params } } },
{ $set: { "cards.$.cardTitle": req.body.params } }
)
.exec()
.then((result) => {
console.log(result);
res.status(200).json(result);
})
.catch((err) => {
console.log(err);
res.status(500).json({
error: err,
});
});
};
Thanks in advance.

Related

user updates using Mongoose mongodb push not working

I am trying to use MongoDB Mongoose MongoDB push to update an entity such that the dcrLocations array gets the dcrlocation entity pushed into it
This is my response from the MongoDB server
{
acknowledged: true,
modifiedCount: 0,
upsertedId: null,
upsertedCount: 0,
matchedCount: 0
}
Heres my model code
import mongoose from "mongoose";
//User Schema
const UserSchema = new mongoose.Schema({
firstName: {
type: String,
required: true,
},
lastName: {
type: String,
required: true,
},
username: {
type: String,
},
email: {
type: String,
required: true,
lowercase: true,
trim: true,
unique: true,
index: true,
},
password: {
type: String,
required: true,
trim: true,
},
hasSubscription: {
type: Boolean,
default: false
},
dcrLocations: [
{
dcrLocationId: {
type: mongoose.Schema.Types.ObjectId,
ref: 'DCRLocation'
}
},
]
},
{
toJSON: {
transform(doc, ret) {
delete ret.password,
delete ret.__v,
delete ret.createdAt,
delete ret.updatedAt
}
}
,
timestamps: true,
}
)
;
UserSchema.index({email: 1, phone: 1})
export default mongoose.model("User", UserSchema);
Heres my controller code
import {User} from "../../path/to/model.js";
import {ObjectID as ObjectId} from "mongodb";
function functionName() {
return {
AssDCRLoToUsr: async (req, res) => {
try {
const {dcrLocationId} = req.body;
console.log(dcrLocationId)
const userToUpdate = await User.updateOne(
{_id: req.params.id},
{
$push: {
dcrLocations: {
dcrLocationId: new ObjectId(dcrLocationId)
}
}
}
);
console.log(userToUpdate)
res.json(userToUpdate)
} catch (error) {
console.error(error)
res.json(false)
}
},
}
}
export {functionName};
Any advice on how I can make this work? I have been battling with it for some time now
try this
$addToSet is used to add unique element in array
import {User} from "../../path/to/model.js";
import {ObjectID as ObjectId} from "mongodb";
function functionName() {
return {
AssDCRLoToUsr: async (req, res) => {
try {
const {dcrLocationId} = req.body;
console.log(dcrLocationId)
const userToUpdate = await User.updateOne(
{_id: req.params.id},
{
$addToSet: {
dcrLocations: {
dcrLocationId: new ObjectId(dcrLocationId)
}
}
}
);
console.log(userToUpdate)
res.json(userToUpdate)
} catch (error) {
console.error(error)
res.json(false)
}
},
}
}
export {functionName};

Nodejs express populate sub array data

I have relative field and in relative I have subRelatives and it continues like nested array. Mean in subRelatives I have subRelatives and some time its 10 times continues process.
My code
router.get(`/userTree/:id`, async (req, res) => {
const userTrees = await Tree.find({createdBy: req.params.id})
.populate(
["createdBy",
{
path: "relatives",
populate: {
path: "subRelatives",
populate: {
path: "subRelatives",
populate: "subRelatives",
},
},
}
]);
if (!userTrees) {
res.status(500).json({success: false});
}
res.send({success: true, data: userTrees});
});
I have added populate but it populate first 2 sub relative and then shows MongooseIds only without populating. So I need to added manually some more populate methods so it will run but its crashing because of lot of data now.
and data look like this.
{
"success": true,
"data": {
"_id": "62dad5c6aff2337dc84d9b40",
"treeName": "test1",
"createdBy": {
"_id": "62d8619cebd6543477c5b7d8",
"userName": "test1",
"userEmail": "test1#gmail.com",
"userFamilyTrees": [
"62d8c713547ba80854d89d59"
]
},
"relatives": [
{
"_id": "62dad5c7aff2337dc84d9b44",
"firstName": "tesads",
"subRelatives": [
{
"_id": "62db1cf186b7012ed9937517",
"firstName": "asdasd",
"subRelatives": []
},
{
"_id": "62db1d0d86b7012ed9937522",
"firstName": "asd",
"subRelatives": []
},
{
"_id": "62dc24c15e6f5ea436cce14b",
"firstName": "julia",
"subRelatives": [
{
"_id": "62dc24c15e6f5ea436cce14b",
"firstName": "julia",
"subRelatives": [
"62dc253bd2119bea52f4f9af"
]
}
]
},
{
"_id": "62dc24fcd2119bea52f4f99d",
"firstName": "julia",
"subRelatives": []
}
]
}
]
}
}
This is my Tree Schema
const mongoose = require('mongoose')
const treeSchema = new mongoose.Schema({
treeName: {
type: String,
required: true
}, image: {
type: String,
default: ''
},
treePrivacy: {
type: Boolean,
required: true
},
treeNote: {
type: String,
default: ""
},
createdBy: {
type: mongoose.Schema.Types.ObjectId,
ref: 'users',
required: true,
},
createDate: {
type: Date,
default: Date.now,
},
relatives: [{
type: mongoose.Schema.Types.ObjectId,
ref: 'relatives',
},],
usersInTree: [{
type: mongoose.Schema.Types.ObjectId,
ref: 'users',
},],
media: [{
type: mongoose.Schema.Types.ObjectId,
ref: 'media',
},]
});
treeSchema.virtual('treeID').get(function () {
return this._id.toHexString();
});
treeSchema.set('toJSON', {
virtuals: true
})
exports.Tree = mongoose.model('trees', treeSchema)
exports.treeSchema = treeSchema;
This is relative Schema
const mongoose = require('mongoose')
const relativeSchema = new mongoose.Schema({
firstName: {
type: String,
},
lastName: {
type: String,
}, image: {
type: String,
},
relativeEmail: {
type: String,
},
relativeType: {
type: Number,
},
// relative grandfather0, father1, mother2, wife3, sister4, brother5, child6
treeID: {
type: mongoose.Schema.Types.ObjectId,
ref: 'users',
required: true,
},
subRelatives: [{
type: mongoose.Schema.Types.ObjectId,
ref: 'relatives',
}],
parentRelative: {
type: mongoose.Schema.Types.ObjectId,
ref: 'relatives',
},
userID: {
type: mongoose.Schema.Types.ObjectId,
ref: 'relatives',
required: false
}
});
relativeSchema.virtual('relativeId').get(function () {
return this._id.toHexString();
});
relativeSchema.set('toJSON', {
virtuals: true
})
exports.Relatives = mongoose.model('relatives', relativeSchema)
exports.relativeSchema = relativeSchema;
This is post api for tree
router.post('/createTree', uploadOptions.single('image'), async (req, res) => {
const file = req.file;
if (!file) return res.status(400).send('No image in the request');
const fileName = file.filename;
const basePath = `${req.protocol}://${req.get('host')}/public/uploads/`;
var userintree = [];
const userExist = await User.findById(req.body.createdBy);
if (!userExist) return res.status(400).send({ success: false, message: 'UserID is not correct' })
userintree.push(req.body.createdBy);
let createtree = new Tree({
treeNote: req.body.treeNote,
treeName: req.body.treeName,
treePrivacy: req.body.treePrivacy,
createdBy: req.body.createdBy,
image: `${basePath}${fileName}`,
usersInTree: userintree
});
createtree = await createtree.save();
if (!createtree) return res.status(400).send({ success: false, message: 'Issue to create a tree' })
userExist.userFamilyTrees.push(createtree._id.toHexString())
const user = await User.findByIdAndUpdate(
req.body.createdBy,
{
userFamilyTrees: userExist.userFamilyTrees,
$push: {
usersInTree: req.body.createdBy
}
},
{ new: true }
)
if (user) res.status(200).send({ success: true, message: 'Tree Created.!,', data: createtree })
});
and post API for relative
router.post('/addRelative', uploadOptions.single('image'), async (req, res) => {
const file = req.file;
if (!file) return res.status(400).send('No image in the request');
const fileName = file.filename;
const basePath = `${req.protocol}://${req.get('host')}/public/uploads/`;
console.log(fileName); console.log(basePath);
console.log(req.body);
let createRelative = new Relatives({
firstName: req.body.firstName,
lastName: req.body.lastName,
relativeEmail: req.body.relativeEmail,
relativeType: req.body.relativeType,
treeID: req.body.treeID,
subRelatives: req.body.subRelatives,
parentRelative: req.body.parentRelative, image: `${basePath}${fileName}`,
});
const treeExist = await Tree.findById(req.body.treeID);
if (!treeExist) return res.status(400).send({ success: false, message: 'TreeID is not correct' })
createRelative = await createRelative.save();
if (!createRelative)
return res.status(400).send({ success: false, message: 'Something Went Wrong.!,' })
treeExist.relatives.push(createRelative._id.toHexString())
const tree = await Tree.findByIdAndUpdate(
req.body.treeID,
{
relatives: treeExist.relatives
},
{ new: true }
)
if (req.body.parentRelative) {
console.log(req.body.parentRelative)
const parent = await Relatives.findById(
req.body.parentRelative
);
// console.log(parent)
// console.log(parent)
parent.subRelatives.push(createRelative)
const user = await Relatives.findByIdAndUpdate(
req.body.parentRelative,
{
subRelatives: parent.subRelatives,
},
{ new: true }
)
// console.log(user)
if (!user) return res.status(400).send({ success: false, message: 'Something Went Wrong.!,' })
// res.send(ser);
}
res.status(200).send({ success: true, message: 'Relative Created Created.!,', data: createRelative })
});
Populate data like this
.populate(
["createdBy",
{
path: "relatives",
populate: {
path: "subRelatives",
model: "SubRelative",
},
}
]);
I've assumed that the model name refaring to subRelative is SubRelative
mongoose does support nested population, it's just that you need to specify the model field as the treeSchema does not have access to all other schema during run time. It looks like this:
const userTrees = await Tree.find({createdBy: req.params.id})
.populate(
[
"createdBy",
{
path: "relatives",
populate: {
path: "subRelatives",
model: 'relatives',
},
}
]);
mongoose does some optimizations to the query, but considering you know the exact structure you can reduce db calls and improve performance if you do this yourself instead of using populate.

query multiple nested objects

I have a problem while querying mongodb with nested multiple objects.
I am trying like this
Project.find()
.then((project) => {
RoadMap.find({
scheduledDate: {
$gte: new Date(req.params.gte), $lt: new
Date(req.params.lt)
}
})
.populate("roadMap", "_id title")
.populate("projectId", "_id title photo ")
.exec((err, roadmap) => {
if (err) {
return res.status(422).json({ error: err });
}
res.json({ project, roadmap });
});
})
.catch((err) => {
return res.status(404).json({ error: "project not found" });
});
I am getting results like this
{
project: {
}
roadmap: [{}{}]
}
but I want to achieve like this
{
project: {
_id:
title:
roadmap: [{},{}]
}
}
this is my schema:
projectShema:
const mongoose = require("mongoose");
const { ObjectId } = mongoose.Schema.Types;
const projectSchema = new mongoose.Schema(
{
title: {
type: String,
required: true,
},
photo: {
type: String,
required: true,
},
caption: {
type: String,
},
postedBy: {
type: ObjectId,
ref: "User",
},
news: [
{
type: ObjectId,
ref: "News",
},
],
roadMap: [
{
type: ObjectId,
ref: "RoadMap",
},
],
},
{ timestamps: true }
);
mongoose.model("Project", projectSchema);
roadMapSchema:
const mongoose = require("mongoose");
const { ObjectId } = mongoose.Schema.Types;
const roadmapSchema = new mongoose.Schema(
{
title: {
type: String,
required: true,
},
postedBy: {
type: ObjectId,
ref: "User",
},
projectId: { type: ObjectId, ref: "Project" },
categoryName: { type: String },
status: {
type: String,
default: "created",
},
},
{ timestamps: true }
);
mongoose.model("RoadMap", roadmapSchema);
I am not sure how to achieve the results, do I need to change schema or it is possible here also?
thank you

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

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