Check if the body parameter is not null and update on MongoDB - node.js

I'm trying to update a document in MongoDB using NodeJS (NextJS). My current code is:
import connect from "../../util/mongodb";
async function api(req, res) {
if (req.method === "POST") {
const { id } = req.body;
const { name } = req.body;
const { email} = req.body;
const { anything1 } = req.body;
const { anything2 } = req.body;
if (!id) {
res.status(400).json({ "error": "missing id param" });
return;
}
const { db } = await connect();
const update = await db.collection("records_collection").findOneAndUpdate(
{ id },
{
$set: {
name,
email,
anything1,
anything2
}
},
{ returnOriginal: false }
);
res.status(200).json(update);
} else {
res.status(400).json({ "error": "wrong request method" });
}
}
export default api;
Everything is working. But I would like to request only the ID as mandatory, and for the other information, leave optional.
In this code, passing the id and name for example, the other three fields (email, anything1 and anything2) will be null in the document.
It is possible to implement the update without requiring all document information and ignore when body fields are null? (As a beginner in NodeJS and MongoDB, the only way to do that that comes to my head now is to surround it all by a lot of if...)

If I've understood your question correctly you can achieve your requirement using the body object in $set stage.
If there is a field which not exists in the object, mongo will not update that field.
As an example check this example where only name field is updated and the rest of fields are not set null.
Another example with 2 fields updated and 3 fields.
You can see how only is updated the field into object in the $set.
So you only need to pass the object received into the query. Something like this:
const update = await db.collection("records_collection").findOneAndUpdate(
{ id },
{
$set: req.body
},
{ returnOriginal: false }
);

Related

I have a Joi schema and I want that when I do a POST method the id is included and required and with PUT method the id is not taken into account

I have a schema joi object:
const commonFields = {
id: joi.string().required(),
name: joi.string().required().min(5).max(50),
nif: joi.string().length(9).required(),
diet: joi.bool().required().strict(),
vegetarian: joi.bool().required().strict(),
};
const aluno = joi.object({
...commonFields,
num: joi
.string()
.required()
.regex(/^\d{1,4}\/\d{2}$/),
regime: joi.string().required().valid("externo", "interno"),
});
(...)
When i make a PUT request to update a user i want to ignore the field "id" but when i make a POST request i want to make it required.
I tried the following:
In schema I added alter() to the field "id":
id: joi.string().alter({
post: (schema) => schema.required(),
put: (schema) => schema.forbidden(),
}),
And in my functions i did this:
async function updateUser(req, res, type, db) {
try {
const { error } = requestValidation[type].validate(req.body, {
context: { method: req.method }
});
if (error) {
return res.status(400).send({ error: error.message });
}
const id = req.params.id;
const { name, nif, vegetarian, diet } = req.body;
(..)
But when i call this function in my PUT endpoint to update a user and add the field id into the requesition body it doesn't throw an error like it should throw. The response should be like this when i add the id to the body:
{
"error": "\"id\" is not allowed"
}
I want to ignore the id because I want to receive it by req.params.id.
I may not be doing the best way but I'm open to new suggestions!
The documentation has an example using tailor with alter
const { error } = requestValidation[type].tailor(req.method.toLowerCase()).validate(req.body, {
context: { method: req.method }
});

MongoDb bulkWrite is not working for me in node.js

This is my first time of using bulkWrite to carry out updates via mongoose. I am building a blog application and I am using it to learn MERN stack. I have a Post model. The Post model has object value which is an array. This is an example of it:
const PostSchema = new mongoose.Schema(
{
postLikes:{
type: Array,
default: []
}
}
)
The postLikes contain mongodb object ids of users who liked a post.
I have a logic for deleting selected users or all users by an admin. The like system does not come with a Like Model of it own. I simply used an array system inside the post model. After deleting a user, I would like to update all post models with likes of the selected users. Some users may have multiple likes across different posts.
In my node, I created a variable like this:
const {selectedIds} = req.body;
The selectedIds came from reactjs like this:
const [selectedUsers, setSelectedUsers] = useState([]);
const arrayOfSelectedUserId = (userId) =>{
setSelectedUsers(prevArray => [...prevArray, userId]);
);
}
For the request, I did it like this:
const response = await axiosPrivate.post(`/v1/users/deleteSelected`, selectedIds, { withCredentials: true,
headers:{authorization: `Bearer ${auth.token}`}})
In nodejs, the selectedUsers ids was passed to this variable:
const {selectedIds} = req.body;
I created the logic this way:
const findIntersection = (array1, array2) => {
return array1.filter((elem) => {
return array2.indexOf(elem) !== -1;
});
}
const filteredPost = posts.filter((singleFilter) => {
const intersection = findIntersection(selectedIds, singleFilter.postLikes);
return singleFilter.postLikes.length !== 0 && intersection.length !== 0;
});
const updatedPosts = filteredPost.map((obj)=>{
const intersection = findIntersection(selectedIds, obj.postLikes);
console.log(intersection )
return {
updateOne: {
filter: { _id: obj._id },
update: { $pull: { postLikes: { $in: intersection } } },
},
};
});
Post.bulkWrite(updatedPosts).then((res) => {
console.log("Documents Updated", res.modifiedCount)
})
The console.log shows the text Document updated and showed number of documents updated. However, if I check my database, the update won't reflect. This means that the selected users' ID is still in the array.
Is there a better method? What Am I doing wrong?

Update record based on username given in Request body

I need to update value in Group db Group_name to the value send in Json payload.
Db schema
const mongoose = require('mongoose');
const UserSchema = new mongoose.Schema({
username: String,
Group_name: {
type: String,
default: '',
}
});
mongoose.model('User', UserSchema);
And API request
router.put('/join', async(req, res) => {
try {
const data = await User.updateOne(req.params.username, {
Group_name: req.body.Group_name
});
console.log(data)
res.send({ msg: "Group Updated!!!" })
} catch (err) {
console.error(err.message);
res.sendStatus(400).send('Server Error');
}
});
currently its updating only first record which is incorrect , my requirement is to check for all records based on username given and according to username given in request parameters ,i will update value of Group_name to the value sent in request body.
can anyone help me ?
Modify query condition.
const data = await User.updateOne(
{ username: req.params.username },
{ $set: { Group_name: req.body.Group_name } }
);
First of all, understand the difference between req.body & req.params
req.body means hidden parameters sent in request body like in post or put requests.
req.params means defined paramters in URL. For this, you must have it defined in your route like below
router.put('/join/:username', async (req, res) => {
// ^^^^^^^^ here it is defined, now you can access it like
const username = req.params.username;
//or
const {username} = req.params; // destructuring
}
there is one more thing and that is
req.query means undefined paramters attached to URL with ?/&
If you want to give username without pre defining like /join?username=john then use req.query
router.put('/join', async (req, res) => {
const {username} = req.query;
}
Then you should use updateMany() function instead of updateOne()
try {
const {username} = req.params;
const {Group_name} = req.body;
const data = await User.updateMany(
{username}, // find as many users where username matches
{Group_name} // update group name from body
);
console.log(data);
The consoled data would be like { n: 2, nModified: 2, ...} because the update queries don't return updated documents but status of the query. If you want to get updated record set, you have to query again with find().
// after update
const updatedRecord = await User.find({ username });
console.log(updatedRecord);
::POSTMAN::
Postman has two types of parameters
Params
Body
If you add in Params it will be added in URL /join?username=john#email.com&Group_name=GroupB and you have to access it in code with req.query.username or req.query.Group_name
If you add in Body it will be hidden and can be accessed with req.body.Group_name etc
Hope it helps!

Mongoose Set object property in array of objects

I have a document consisting of a Post. Each Post has an array of Comments, which are an object each. So my document looks like this
Now, I want to be able to update the message property in a given Comment object.
So I'll be using the $set method, but how would I be able to select the specific object. Currently, my unfinished method looks like this
export const editComment = async (req, res) => {
const { id } = req.body;
const post = await Post.findById(req.params.id);
const _id = post.id;
const postComments = post.comments.map((comment) => comment._id);
const commentIndex = postComments.indexOf(id.id);
const message = post.comments[commentIndex].message;
try {
await Post.updateOne(
{ _id },
{
$set: {
// Action to update the comment
},
},
{ new: true }
);
res.status(200).json({ message: post });
} catch (error) {
res.status(400).json({ error: error.message });
}
}
I figured selecting the right index of the comment was a good start, but how would I, in the $set method, select the correct Comment object, and then update the message property?
You have to find the right data in database and update its required property. You can do it by the following method
exports.updateMessage= async (_id,objectId, newMessage) => {
return await TableName.updateOne({_id: _id},{{comments: {$elemMatch: {_id: objectId}$set:{message:newMessage}}});
};
Post.updateOne(_id,
{
$set: {
"comments.$[elem].message": "the value you want to set for message"
}
},
{
arrayFilters: [
{
"elem._id": 1 // _id of comment object you want to edit
}
]
})

How to make sure that even if no data is given in the required field, it will consider is empty?

I am using Node.js and MongoDB. I want to make sure that if the user writes in the specific field, the database should be updated with that value, else it should be null.
Following is the code I am using:
exports.updatingUser = async (user_,request,res)=> {
let result = "";
const updateUserInfo = {
fullName: request.fullName,
userName: request.userName,
email :request.email,
password : request.password,
profileImage:request.profileImage,
backgroundImage:request.backgroundImage
};
await User.updateOne({_id:request._id},{$set:updateUserInfo})
.exec()
.then(docs => {
result = docs;
})
.catch(err => {
res.status(500).json({
error: err
});
});
return result;
}
This works when the user writes in the specific fields but not when any of the field is empty.
Try like this
fullName: request.fullName ? request.fullName : '', // You can put null if you want
Note: Need to put in all

Resources