Nodejs express populate sub array data - node.js

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.

Related

Nodejs express filter data by query

I need to run the query by find like I pass id and data only with that Id will display but the issue is it's showing all data means it's not filtering.
I am finding data like this
router.get('/getannouncementsbyrestaurant/:id', async (req, res) => {
let getannouncementsbyrestaurant = await Announcements.find({ restaurants: req.params.id }).populate(['announcementRestaurants']);
if (!getannouncementsbyrestaurant) {
return res.status(400).json({ success: false, message: "something went wrong" })
}
res.status(200).json({ success: true, data: getannouncementsbyrestaurant })
})
and here is the model
const mongoose = require('mongoose');
const announcementsschema = new mongoose.Schema({
announcementName: {
type: String,
required: true
},
announcementDescription: {
type: String,
required: true
},
announcementCountry: {
type: String,
required: false
},
announcementCity: {
type: String,
required: false
},
announcementStreet: {
type: String,
default: ''
},
announcementRestaurants: {
type: mongoose.Schema.Types.ObjectId,
required: true,
ref: 'restaurants'
},
announcementCreatedOn: {
type: Date,
default: Date.now
}
})
announcementsschema.virtual('id').get(function () {
return this._id.toHexString();
});
announcementsschema.set('toJSON', {
virtuals: true,
});
exports.Announcements = mongoose.model('Announcements', announcementsschema);
exports.announcementsschema = announcementsschema;
Don't know why its showing all data
here is example json
"data": [
{
"_id": "631a4c9c2c4fca7afc0f23f5",
"announcementName": "Mega Sale 10% off",
"announcementRestaurants": {
"_id": "631a4af62c4fca7afc0f238f",
"restaurantName": "Mega Restaurant",
},
"id": "631a4c9c2c4fca7afc0f23f5"
},
{
"_id": "631a51b72c4fca7afc0f2449",
"announcementName": "Sale upto 90%",
"announcementRestaurants": {
"_id": "631a51752c4fca7afc0f2434",
"restaurantName": "McDonalds",
},
"announcementCreatedOn": "2022-09-08T20:33:59.870Z",
"__v": 0,
"id": "631a51b72c4fca7afc0f2449"
}
]
I am passing announcementRestaurants._id in param and need to filter with that.
Your schema doesn't have the field restaurants. The corresponding field is announcementRestaurants. So the query should be:
let getannouncementsbyrestaurant =
await Announcements.find({ announcementRestaurants: req.params.id }).populate(['announcementRestaurants']);
Or
let getannouncementsbyrestaurant =
await Announcements.find({ announcementRestaurants: mongoose.Types.ObjectId(req.params.id) }).populate(['announcementRestaurants']);

How to add object into an array which is a value of a key within an object?

I have a user model like this:
const userSchema = new mongoose.Schema({
name: {
type: String,
required: true
},
courses: {
type: Object,
required: true
}
})
And a course model like this:
const courseSchema = new mongoose.Schema({
course_code: {
type: String,
required: true
},
course_title: {
type: String,
required: true
}
})
app.js file is here:
app.post('/enroll/:id', async function (req, res) {
const courseWithID = await Course.findById(req.params.id)
const course = {"course_code": courseWithID.course_code, "course_title": courseWithID.course_title}
const userID = req.user.id
await User.findByIdAndUpdate(userID, {"courses": course}, {new: true, runValidators: true, useFindAndModify: false})
res.redirect('/dashboard')
})
After first execution of app.js, I get this result:
{
"name": "Person",
"courses": {
"course_code": "123",
"course_title": "ABC"
}
}
After second execution of app.js, I get this result:
{
"name": "Person",
"courses": {
"course_code": "456",
"course_title": "DEF"
}
}
But after second execution of app.js, I want this result:
{
"name": "Person",
"courses": [
{
"course_code": "123",
"course_title": "ABC"
},
{
"course_code": "456",
"course_title": "DEF"
}
]
}
How Can I do that?
Quick fixes,
change courses type Object to an Array
const userSchema = new mongoose.Schema({
name: {
type: String,
required: true
},
courses: {
type: Array,
required: true
}
})
add $push before courses because we need to push object in array
const courseWithID = await Course.findById(req.params.id);
const course = {
"course_code": courseWithID.course_code,
"course_title": courseWithID.course_title
};
const userID = req.user.id;
await User.findByIdAndUpdate(userID,
{
"$push": { "courses": course }
},
{new: true, runValidators: true, useFindAndModify: false}
)

Update name in nested object array

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.

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

Trying to populate an object inside another object that is inside another object (NODE.JS)

This function gets a specific "process" and shows the following information in json: (POSTMAN)
{
"process": {
"_id": "5c18e8d1d4817811839d43d2",
"name": "Dyeing",
"colour": {
"_id": "5c18c972b39bb20769288e8f",
"name": "azul",
"category": "5c18c09f4c6baf05ea621bca",
"__v": 0
},
"__v": 0
},
"request": {
"type": "GET",
"url": "http://localhost:3000/process"
}
}
Process controller function
exports.process_get_process = (req, res, next) => {
Process.findById(req.params.processId)
.populate("colour")
.populate("category")
.exec()
.then(process => {
if (!process) {
return res.status(404).json({
message: "Process not found"
});
}
res.status(200).json({
process: process,
request: {
type: "GET",
url: "http://localhost:3000/process"
}
});
})
.catch(err => {
res.status(500).json({
error: err
});
});
};
The model for the "process" is the following schema:
const mongoose = require('mongoose');
const processSchema = mongoose.Schema({
_id: mongoose.Schema.Types.ObjectId,
name: { type: String, required: true },
price: { type: Number, required: false },
colour: { type: mongoose.Schema.Types.ObjectId, ref: 'Colour', required: false },
});
module.exports = mongoose.model('Process', processSchema);
This is the Colour model:
As you can see the object "category" is inside "colour" and i want to show him in the "process" object as well.
const mongoose = require('mongoose');
const colourSchema = mongoose.Schema({
_id: mongoose.Schema.Types.ObjectId,
name: { type: String, required: true },
category: { type: mongoose.Schema.Types.ObjectId, ref: 'Category', required: true },
});
module.exports = mongoose.model('Colour', colourSchema);
Problem: Inside the "colour" exist a "category" object, but only shows the category id, and i want him to show all the category information. How can I populate it?
You can specify that as options for the populate function.
Process.findById(req.params.processId)
.populate({
path: 'colour',
populate: { path: 'category' }
})
.exec()

Resources