Assign roles to users in mongoose - node.js

I am trying to update a users role using mongoose . I have been using mongoose findOneAndUpdate but have not been able to update the role. I dont get any error messages but i get the doc before the update. So i did some research and tried to add {new:true} but it did not change anything. Then I saw someone use the aggregate query $push but that did not either work.
I have rewritten this query many times without giving me the expected results
exports.changeRole = async (req, res) => {
User.findOneAndUpdate(
{ id: req.param.userId },
{
$push: {
$set: { "roles[0].name": req.body.name },
},
},
{ new: true }
)
.populate("roles", "-__v")
.exec((err, data) => {
if (err) {
console.log(err);
} else {
console.log(data);
}
});
};
How I send in postman
{
"roles": ["Admin"]
}
This is what i get in my console:
{
roles: [ { _id: 606242fa3bcbc13305bee567, name: 'user' } ],
_id: 606307a839a54f7982f8ff84,
username: 'before',
email: 'before#gmail.com',
password: '$2a$/lvMv80IPZe9FSm',
__v: 1
}
I have one model that is called User
const userSchema = new mongoose.Schema(
{
username: {
type: String,
required: true,
min: 6,
max: 255,
},
email: {
type: String,
required: true,
min: 6,
max: 255,
},
password: {
type: String,
required: true,
min: 6,
max: 15,
},
roles: [
{
type: mongoose.Schema.Types.ObjectId,
ref: "Role",
},
],
},
{ timestamp: true }
);
This User model is referencing to Role.js. An array of objectId:s. So automatically I have four role documents created if they dont exist. Every user is referencing to one of those documents(roles)
const Role = mongoose.model(
"Role",
new mongoose.Schema({
name: String,
})
);

I was not thinking it right. I did not need to change my populated documents only needed to change the objectId it was referencing to in my roles:[] in User.js. Use $set to change a field and $push to to add new value.
exports.changeRole = async (req, res) => {
await User.findOneAndUpdate(
{ id: req.params.userId },
{
$set: { roles: req.body.roles },
},
{ new: true }
)
.populate("roles")
.then((data) => {
if (!data) return res.status(404).send("Not found");
res.send(data);
})
.catch((err) => {
return res.status(500).send({ message: err.message });
});
};

Related

NodeJS, Mongoose prevent Push to write if same data is already in the array

I have app where I have Users. Every user can be an owner of an item or multiple items..
If user is already owner of that item prevent to push the item object into array, if already exists.
I already tried different solutions (I will write what I tried in the end of the question).
User model:
import * as mongoose from "mongoose";
const Schema = mongoose.Schema;
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,
},
],
});
module.exports = mongoose.model("Users", UserSchema);
Add owner to user controller:
exports.addOwnerToUser = async (req: Request, res: Response) => {
try {
console.log("here");
let ObjectID = require("mongodb").ObjectID;
const mongoose = require("mongoose");
const user = {
email: req.body.email,
ownerId: req.body.ownerId,
};
const updatedUser = await User.findOneAndUpdate(
{
_id: req.params.userId,
owners: { $ne: req.body.ownerId },
},
{
$push: { owners: req.body.ownerId },
}
);
res.status(201).json({ sucess: true, msg: "User updated sucessfully" });
} catch (err) {
res.status(404).json(err);
}
};
I already tried solutions like this, but nothing works as expected.. (check the commented code)
exports.addOwnerToUser = async (req: Request, res: Response) => {
try {
console.log("here");
let ObjectID = require("mongodb").ObjectID;
const mongoose = require("mongoose");
// add get user and find if he already has this id.. if has then json 200
// if not i execute line 230
const user = {
email: req.body.email,
ownerId: req.body.ownerId,
};
/* const updatedUser = await User.findOneAndUpdate(
{ _id: req.params.userId },
{
"ownerId.ownerId": {
$ne: ObjectID(req.body.ownerId),
},
},
{
$addToSet: {
"ownerId.ownerId": ObjectID(req.body.ownerId),
},
},
{
new: true,
}
); */
const updatedUser = await User.findOneAndUpdate(
/* {
_id: req.params.userId,
},
{
$addToSet: {
owners: req.body.ownerId,
},
},
{
new: true,
} */
{
_id: req.params.userId,
owners: { $ne: req.body.ownerId },
},
{
$push: { owners: { ownerId: req.body.ownerId } },
}
);
console.log(updatedUser);
/* const updatedUser = await User.findOneAndUpdate(
{ _id: req.params.userId },
{
$push: { ownerId: { ownerId: req.body.ownerId } },
}
);
console.log(updatedUser); */
// $addToSet: { members: { name: 'something', username: 'something' } }
/*
User.findByIdAndUpdate(req.params.user_id,{$set:req.body},{new:true}, function(err, result){
if(err){
console.log(err);
}
console.log("RESULT: " + result);
res.send('Done')
});
};
*/
res.status(201).json({ sucess: true, msg: "User updated sucessfully" });
} catch (err) {
res.status(404).json(err);
}
};

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

updating document inside array in mongoose

I want to update my answer object inside answers array. I am using following schema
const mongoose = require("mongoose");
const { ObjectId } = mongoose.Schema;
const questionSchema = new mongoose.Schema(
{
postedBy: {
type: ObjectId,
required: true,
ref: "User",
},
question: {
type: String,
required: true,
},
photo: {
data: String,
required: false,
},
answers: [
{
userId: { type: ObjectId, ref: "User" },
answer: String,
},
],
questionType: {
data: String,
required: false,
},
},
{ timeStamps: true }
);
module.exports = mongoose.model("Question", questionSchema);
I am using updateOne method to update my answer in my db. Can anyone explain what is missing here. I am been trying to solve this since hours
exports.updateAnswer = (req, res) => {
const questionId = req.body.questionId;
const answerId = req.body.answerId;
Question.findOne({ _id: questionId }).exec((err, question) => {
if (err) {
res.status(400).json({
error: errorHandler(err),
});
return;
}
if (!question) {
res.status(400).json({
error: "question not found",
});
return;
}
});
Question.updateOne(
{ _id: answerId },
{
$set: {
"answers.$.answer": "This is update answer. My name is Ravi Dubey",
},
},
{ new: true },
(err, success) => {
if (err) {
res.status(400).json({
error: errorHandler(err),
});
}
res.json({
msg: "answer updated successfully",
success,
});
}
);
};
My result is coming successful but answer is not updating in db.
I am confused on Question.updateOne method.
Any help appreciated.
If you trying to query based on id of one of the documents in the answers array then instead of {_id: answerId} you need to provide {'answers._id': answerId}. And also if you need the updated document as result then you should use the findOneAndUpdate method.
Question.findOneAndUpdate(
{ "answers._id": answerId },
{ $set: { "answers.$.answer": "some answer" } },
{ new: true },
(err, data) => {
// handle response
}
);

Creating a document and adding to set in same route

I have the following route:
app.post('/accounts', (req, res) => {
obj = new ObjectID()
var account = new Account({
name: req.body.name,
_owner: req.body._owner
}
)
return account.save()
.then((doc) => {
Account.update(
{
"_id": account._id
},
{
$addToSet: {
subscriptions: obj
}
}
)
})
.then((doc) => {
res.send(doc)
}
)
});
I am trying to create a document and then update a field in it (array) with a created objectID. When I call this route the new document is created however the new objectID is not being added to the subscription set.
Here is my model:
var Account = mongoose.model('Account', {
name: {
type: String,
required: true,
minlength: 1,
trim: true
},
_owner: {
type: mongoose.Schema.Types.ObjectId,
required: true
},
subscriptions: [{
type: mongoose.Schema.Types.ObjectId,
required: true
}]
module.exports = {
Account
};
if you have found the id and subscriptions is array then
app.post('/accounts', (req, res) => {
obj = new ObjectID()
var account = new Account({
name: req.body.name,
_owner: req.body._owner
});
return account.save()
.then((doc) => {
return Account.update( //return was missing which was causing the issue because of promise chain
{
"_id": account._id
},
{
$addToSet: {
subscriptions: obj
}
}
)
}).then((doc) => {
res.send(doc)
}
)
});

Updating mongoose schema fields concurrently

I'm trying to update a mongoose schema. Basically I have two api's '/follow/:user_id' and '/unfollow/:user_id'. What I'm trying to achieve is whenever user A follows user B , user B followers field in mongoose will increment as one.
As for now I managed to get only following fields increment by one but not the followers fields.
schema.js
var UserSchema = new Schema({
name: String,
username: { type: String, required: true, index: { unique: true }},
password: { type: String, required: true, select: false },
followers: [{ type: Schema.Types.ObjectId, ref: 'User'}],
following: [{ type: Schema.Types.ObjectId, ref: 'User'}],
followersCount: Number,
followingCount: Number
});
Updated version: I tried my solution, but whenever I post it, it just fetching the data ( I tried the api's on POSTMAN chrome app ).
api.js
// follow a user
apiRouter.post('/follow/:user_id', function(req, res) {
// find a current user that has logged in
User.update(
{
_id: req.decoded.id,
following: { $ne: req.params.user_id }
},
{
$push: { following: req.params.user_id},
$inc: { followingCount: 1}
},
function(err) {
if (err) {
res.send(err);
return;
}
User.update(
{
_id: req.params.user_id,
followers: { $ne: req.decoded.id }
},
{
$push: { followers: req.decoded.id },
$inc: { followersCount: 1}
}
), function(err) {
if(err) return res.send(err);
res.json({ message: "Successfully Followed!" });
}
});
});
These codes only manage to increment the user's following fields, and without duplication. How do I update logged in user's following fields and as well as other user's followers fields at the same time?
Updated version: it keeps fetching the data.
May be this is how you want to. Instead of using update, you can also use findOneAndUpdate from Mongoose queries.
apiRouter.post('/follow/:user_id', function(req, res) {
User.findOneAndUpdate(
{
_id: req.decoded.id
},
{
$push: {following: req.params.user_id},
$inc: {followingCount: 1}
},
function (err, user) {
if (err)
res.send(err);
User.findOneAndUpdate(
{
_id: req.params.user_id
},
{
$push: {followers: req.decoded.id},
$inc: {followersCount: 1}
},
function (err, anotherUser) {
if (err)
res.send(err);
res.json({message: "Successfully Followed!"})
});
});
}
If you are not sure about it is updated or not, you can just use console.log() for both user and anotherUser variables to see changes.

Resources