Find if User Exist in array mongodb and Nodejs - node.js

I have this opportunity model that has this field likes, which is an array of users. How do I check first if the user exist already in that array and if they do I pull them and if they don't I push them back I am building a like creteria for posts
Here is my opportunity model
const mongoose = require("mongoose");
const OpportunityModel = new mongoose.Schema({
user: {
type: mongoose.Schema.Types.ObjectId,
ref: "User",
},
content: {
type: String,
required: true,
trim: true,
},
likes: [
{
type: mongoose.Schema.Types.ObjectId,
ref: "User",
},
],
image: [
{
type: Object,
},
],
share_with_thoughts: {
type: mongoose.Schema.Types.ObjectId,
ref: "Shares",
},
comments: {
type: mongoose.Schema.Types.ObjectId,
ref: "Comment",
},
is_opportunity_applied: [
{
type: mongoose.Schema.Types.ObjectId,
ref: "User",
},
],
});
const Opportunity = mongoose.model("Opportunity", OpportunityModel);
module.exports = Opportunity;
What I tried doing but did not work
const likeOrUnlikeOpportunity = expressAsyncHandler(async (req, res) => {
let userId = req.user._id;
let opportunityId = req.params.opportunityId;
let isUserExist = await Opportunity.find({
$and: [{ _id: opportunityId }, { likes: { $elemMatch: { $eq: userId } } }],
}).populate("user", "user_id user_name");
if (isUserExist.length > 0) {
const unliked = await Opportunity.findByIdAndUpdate(
opportunityId,
{
$pull: { users: userId },
},
{ new: true }
).populate("user", "user_id user_name");
if (!unliked) {
res.status(500).send({ ErrMessaage: "an error occured" });
} else {
res.status(200).json(unliked);
}
} else {
const added = await Opportunity.findByIdAndUpdate(
opportunityId,
{
$push: { users: userId },
},
{ new: true }
).populate("user", "user_id user_name");
if (!added) {
res.status(500).send({ ErrMessaage: "an error occured" });
} else {
res.status(200).json(added);
}
}
});

In this case, the action should be dynamic [like | unlike] function, You don't have to chain the function since the like and unlike function/api can't be called at the same time.
Make Your Query Conditional;
Just make sure you have a way to identify between like and unlike.
const {like, postId} = req.body;
let query = {[`${'$' + (like ? 'push' : 'pull')}`]: {likes: userId}};
//assumes that you have the post id
// you can decide not to wait for it to update, just to be sure it did update
await post.findByIdAndUpdate(postId, query).exec()
I hope this helps.

Related

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

CastError: Cast to ObjectId failed for value "0" at path "followers"

I am using Mongoose on Node JS server. I created a Follow Route which is working correctly.
router.post("/follow/:id/:followerId", async (req, res) => {
const user = await User.findById(req.params.id);
const follower = await User.findById(req.params.followerId);
try {
await User.findByIdAndUpdate(user, {
$push: { followers: follower },
}).then(
await User.findByIdAndUpdate(follower, {
$push: { followings: user },
})
);
res.send("Done");
} catch (error) {
console.log(error);
}
});
I now created the opposite for unfollow route. But I get this error: CastError: Cast to ObjectId failed for value "0" at path "followers"
reason: Error: Argument passed in must be a single String of 12 bytes or a string of 24 hex characters
This is my unfollow route:
router.post("/unfollow/:id/:followerId", async (req, res) => {
const user = await User.findById(req.params.id);
const follower = await User.findById(req.params.followerId);
try {
await User.findByIdAndUpdate(user, {
$pull: { followers: follower },
}).then(
await User.findByIdAndUpdate(follower, {
$pull: { followings: user },
})
);
res.send("Done");
} catch (error) {
console.log(error);
}
});
How can I make this work please?
This is my schema:
const userSchema = new Schema({
username: {
type: String,
required: true,
},
fullName: {
type: String,
required: true,
},
followers: [
{
type: Schema.Types.ObjectId,
ref: "User",
},
],
followings: [
{
type: Schema.Types.ObjectId,
ref: "User",
},
],
active: {
type: Boolean,
default: true,
},
});
Please help
There is a mismatch between the schema and how you add a new follower/following.
In the schema, followers and following are arrays of ObjectId
followers: [
{
type: Schema.Types.ObjectId,
ref: "User",
},
],
followings: [
{
type: Schema.Types.ObjectId,
ref: "User",
},
],
But, when you add a new follower and following into the array, you use the entire object.
const user = await User.findById(req.params.id); // user object
const follower = await User.findById(req.params.followerId);
try {
await User.findByIdAndUpdate(user, {
$push: { followers: follower }, // you use the user object instead of the id
}).then(
await User.findByIdAndUpdate(follower, {
$push: { followings: user },
})
);
I think we should change to the id of the object :
const user = await User.findById(req.params.id);
const follower = await User.findById(req.params.followerId);
try {
await User.findByIdAndUpdate(user, {
$push: { followers: follower._id }, // here we use the id of follower
}).then(
await User.findByIdAndUpdate(follower, {
$push: { followings: user._id },
})
);
and the same when we remove the follower and the following :
try {
await User.findByIdAndUpdate(user, {
$pull: { followers: follower._id },
}).then(
await User.findByIdAndUpdate(follower, {
$pull: { followings: user._id },
})
);
Note: you may need to check in the database what is in the followers and the followings array, in case they are objects (I think), you should change to id.

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.

Resources