How to Update only passed value in mongodb using mongoose? - node.js

I have built an API that updates records in MongoDB using mongoose, but currently what happening is if I am passing only 4 field values in the JSON file of postman and try to update then all the values are updating with value null except that 4 fields which I had passed in JSON so can anyone help me how I can pass dynamic field and value that update only passed values of collection not all the fields of collection.
Passes JSON :
{
"preferance_id" : "60fe9ba1766d10d65c64083c",
"is_active": true,
"price_blur":true,
"affiliate_commission":27,
"language_code": "en"
}
Update call which I have passed in node js
transaction.update(PreferencesMasterName,
{ _id: new mongoose.Types.ObjectId(preferance_id) }, {
subscriptin_vat : subscriptin_vat,
popular_normal : popular_normal,
popular_crawled : popular_crawled,
price_blur : price_blur,
blur_rule : blur_rule,
affiliate_commission : affiliate_commission,
red_lock : red_lock,
automatic_dummy_price : automatic_dummy_price,
...
is_active: is_active
})
I want to pass dynamic field and values here instead of this because due to this other values are set will null value. So, can anyone have an idea how to do this?

You can do something like this:
const data = res.body; // should be an object that needs to updated
transaction.update({_id: PreferanceMasterName._id}, data, {new: true }, ( error, obj ) => {
if( error ) {
console.error( JSON.stringify( error ) );
}
console.log( obj );
});
In certain cases new doesn't work, you can use : { returnOriginal: false };
for more details, you can check this thread there are multiple ways you can do this.
Please check update how to use it.

Related

Update element of any particular index in an array in MongoDb using mongoose in Node.js

Hi I tried to update the element at a particular index in an array but I'm not able to update it. It is updating the entire array. Not able to figure out how to update any particular index. Also tried
{$set:{"Data.1:req.body}}
this is updating at 1st index but I don't want to hardcode the index value. It should take from frontend. Let say I have a schema in which I have Data who's type is array and default value is as shown below or anything in the same format.
Data: {
type: Array,
Default: ["0","1","0"]
}
Whenever I'll create a user then Data field will contain these default values, But now I want to update the value at any index (coming from frontend) of Data array of any user created.
I tried findByIdAndUpdate method but I don't know what to pass in set property. If I'm passing this {$set: req.body} and In postman I'm giving any value of Data then obviously it is updating Data array but I want to update value at any index which I'm passing from frontend, let say the index I'm passing is 2 then it should update the value of array at index 2, similarly I can pass any index from frontend how should I do that. What changes I have to make in {$set : } Thanks in advance.
Waiting for any help or suggestions. Thanks
It appears that you can solve this in backend logic if you are passing the index from the frontend.
You can dynamically specify the index, based on the input from the frontend, before you send a query.
const updateUserData = async (req, res) => {
const { index, user_id, new_value } = req.body;
try {
const update = {};
update[`Data.${index}`] = new_value;
const data = await Users.updateOne(
{ _id: user_id },
{ $set: update }
);
return res.status(200).json({ success: true });
} catch (error) {
return res.status(500).json({ success: false });
}
};

Sequelize query to count and update a data if count value returns 1

I am building a register system in my nodejs application.
I am using Sequelize js as ORM.
This is my controller
exports.activateaccount=function(req,res,next){
var token=req.params.token;
indexmodel.activateemail(token,function(err,result){
if(err) throw err;
else{
res.send('Activation Completed');
}
})
}
i am taking token and sending it to model as :
exports.activateemail=function(token,callback){
partner.count({where:{token:token}}).then((partner)=>{
Sequelize.query("UPDATE partners WHERE token='"+token+"' SET confirmed=1").then(partner=>{
callback();
})
})
}
I want to set 'confirmed' column of that 'token' as 1 if token matches.
Currently the default value is 0.
Does anyone knows code of it.
I tried using Sequelize.query but it is not a function.
Instead of using raw query like :
Sequelize.query("UPDATE partners WHERE token='"+token+"' SET confirmed=1")
You should be using the Sequelize way , this way you can handle it better.
By better means :
In future you can switch to any dialect
you can use hooks
And others ...
But with method you have used you can't to do such things.
Here you go :
partner.update({ 'confirmed' : 1 },{ where : { 'token' : token , 'confirmed' : 0 }})
.then(...) // handle update
.catch(...); // handle error
For delete :
partner.destroy({ where : { 'token' : token , 'confirmed' : 1 }});

How to update a collection document using multiple criteria

I have this document in collection "registosSRS":
{
"_id" : ObjectId("5a3a2b47d04b7e07f8a273dc"),
"sessao" : "5",
"valorRelacao" : "4.89",
"valorObjectivo" : "4.97",
"valorAbordagem" : "4.88",
"valorGeral" : "4.92",
"cliente_id" : "5a1407c8099ca208e48170a5",
"email" : "mgoncalves#psi.uminho.pt",
"data" : 1513761607431
}
and this document in collection sessao. I've created this document in another place, and set dadosSRS to {} because I want to later change this value. Is this possible to add this value without having created it?
"_id" : ObjectId("5a3a2b41d04b7e07f8a273db"),
"cliente" : ObjectId("5a1407c8099ca208e48170a5"),
"data" : 1513761601705,
"numero" : "5",
"dadosORS" : ObjectId("5a3a2b41d04b7e07f8a273da"),
"dadosSRS" : {
}
Then I'm looking in collection sessao for a client and number os session as in registosSRS, to add the registosSRS id.
mongoClient.collection('sessao', function(err,collection){
collection.update(
{cliente:result.cliente_id, numero:dadosSRS.sessao},
{$set: {'dadosSRS':dadosSessao.dadosSRS}},
function(result){
if (err) throw err;
console.log(result);
console.log('encontrou registo do cliente na collection registoSRS: ' + result);
});
but the result is null, although I have the client and the session number. What am I doing wrong?
In your callback you have only one argument which is essentially the error object hence the result is null since there is no error thrown from the update option. You need a second argument which returns the actual result object. You are using the wrong update method which returns the result object if the command was executed successfully, not the actual document.
From the documentation, the result object has the following properties:
Name Type Description
result Object The raw result returned from MongoDB, field will vary depending on server version.
Properties
Name Type Description
ok Number Is 1 if the command executed correctly.
n Number The total count of documents scanned.
nModified Number The total count of documents modified.
connection Object The connection object used for the operation.
matchedCount Number The number of documents that matched the filter.
modifiedCount Number The number of documents that were modified.
upsertedCount Number The number of documents upserted.
upsertedId Object The upserted id.
You instead need to use findOneAndUpdate which will return the updated document if found
const { ObjectId } = require('mongodb'); // or ObjectID
// or var ObjectId = require('mongodb').ObjectId if node version < 6
const safeObjectId = s => ObjectId.isValid(s) ? new ObjectId(s) : null;
collection.findOneAndUpdate(
{ 'cliente': safeObjectId(result.cliente_id), 'numero': dadosSRS.sessao },
{ '$set': { 'dadosSRS': dadosSessao.dadosSRS } },
{ 'returnOriginal': true },
function(err, result) { // <-- add result as a second argument in the callback
if (err) throw err;
console.log(result);
console.log('encontrou registo do cliente na collection registoSRS: ' + result);
});

Updating Mongo DB entries by params

The goal of this program is to add a property called 'userResponse' to the problem objects that reflects the input from the user. The problem object in Mongo DB is held in an array of objects, called 'problems', that is held in an object called 'session':
{
"_id" : ObjectId("59df5ee7adb378237377dbb4"),
"updatedAt" : ISODate("2017-10-12T12:24:07.269Z"),
"createdAt" : ISODate("2017-10-12T12:24:07.269Z"),
"userId" : "59df5edbadb378237377dbb3",
"problems" : [
{
"correctResponse" : 23,
"problem" : "20 + 3",
"secondTerm" : 3,
"firstTerm" : 20
} ]
}
Here is the logic for the endpoint that I have been using:
router.patch( '/session/:sessionId/:index', passport.authenticate( 'jwt', { session: false } ), ( req, res ) => {
Session.findOne({_id: req.params.sessionId})
.then( (item)=>{
item.problems[req.params.index].userResponse = req.body.userResponse;
Session.update({_id: req.params.sessionId}, item).then( (updated)=>{
res.json(updated.problems[req.params.index]);
});
})
})
I looked at some other examples ( one two ) but they do not seem relavant since this is a patch to single elements of the array as identified by the params.
This approach works, in that it successfully updates the database with the new properties on the objects, but during the execution the server logs this error:
(node:10955) UnhandledPromiseRejectionWarning: Unhandled promise rejection (rejection id: 1): TypeError: Cannot read property '0' of undefined
My understanding is that this means the object can not be found in the database at some point, but since the database does include the new properties it must be resolved later. Is that corrrect?
Additionally it seems brittle to have two chained promises with no catch statements...
Can anyone offer suggestions on how to improve this process? This logic does successfully update the database, with errors. Is there a better way?
Thank you for your time.
Well i can see multiple issues in the code that can cause your problem
1- From the structure of your database session document _id is of type ObjectId and when you try to find, you are finding by just the id which is probably string you will need to use ObjectId(req.params.sessionId)
2- from the error, i can tell that the value of item does not contain an array of problems ... so items.problems is undefined so undefined[0] is an error.
3- You don't need to find session document item twice to update the array, i updated the code to update the userResponse value in array by using a single update operation.
4- for a better error handling and code maintainability you need to handle promise rejection so you need to handle catch as well as you are doing with then
So from the above comments i can update your code to be
var mongoose = require('mongoose');
router.patch( '/session/:sessionId/:index', passport.authenticate( 'jwt', { session: false } ), ( req, res ) => {
var index = req.params.index;
Session.update({_id: mongoose.Types.ObjectId(req.params.sessionId) }, {$set : {"problems." + index + ".userResponse" : req.body.userResponse } )
.then( (updated)=>{
console.log(updated);
res.json(updated.problems[req.params.index]);
})
.catch((err) => {
console.log(err.message);
res.json({status: "error" , message:err.message});
});
})

mongoose-encryption and updating objects

I know the mongoose-encryption doc states:
update will work fine on unencrypted and unauthenticated fields, but will not work correctly if encrypted or authenticated fields are involved.
And I've observed that when I use the mongoose create method that my fields are encrypted into the _ct field. However if I then use findByIdAndUpdate to update my object I see the fields are created in plain text (as output from mongodb console via find command).
From save
> db.tenants.find().pretty()
{
"_id" : ObjectId("554b7f8e7806c204e0c7589e"),
"_ac" : BinData(0,"YdJjOUJhzDWuDE5oBU4SH33O4qM2hbotQTsF6NzDnx4hWyJfaWQiLCJfY3QiXQ=="),
"_ct" : BinData(0,"YaU4z/UY3djGCKBcgMaNIFHeNp8NJ9Woyh9ahff0hRas4WD80V80JE2B8tRLUs0Qd9B7IIzHsq6O4pYub5VKJ1PIQA+/dbStZpOH/KfvPoDC6DzR5JdoAu+feU7HyFnFCMY81RZeJF5BKJylhY1+mG4="),
"__v" : 0
}
After findByIdAndUpdate
> db.tenants.find().pretty()
{
"_id" : ObjectId("554b7f8e7806c204e0c7589e"),
"_ac" : BinData(0,"YdJjOUJhzDWuDE5oBU4SH33O4qM2hbotQTsF6NzDnx4hWyJfaWQiLCJfY3QiXQ=="),
"_ct" : BinData(0,"YaU4z/UY3djGCKBcgMaNIFHeNp8NJ9Woyh9ahff0hRas4WD80V80JE2B8tRLUs0Qd9B7IIzHsq6O4pYub5VKJ1PIQA+/dbStZpOH/KfvPoDC6DzR5JdoAu+feU7HyFnFCMY81RZeJF5BKJylhY1+mG4="),
"__v" : 0,
"userId" : ObjectId("55268f43cbfc87be221cd611"),
"social" : "123-45-6789",
"last" : "bar",
"first" : "foo"
}
Is there a recommended strategy for updating objects and maintaining the encryption with mongoose-encryption?
As you quoted, the documentation for mongoose-encryption clearly tells that it does not work for update.
https://github.com/joegoldbeck/mongoose-encryption
Mongoose update hook is little tricky as well.
What you can do potentially is model your collection in such a way that fields which needs to be encrypted are a separate collection altogether and in the paren collection just link them via ids.
Person = {
_id: <ObjectId>
name: Blah
..
..
documents: [
{ 'doc_id': <ObjectId1> },
{ 'doc_id': <ObjectId2> },
]
}
Documents = [
{
"_id" : <ObjectId1>,
"_ac" : BinData(0,"YdJjOUJhzDWuDE5oBU4SH33O4qM2hbotQTsF6NzDnx4hWyJfaWQiLCJfY3QiXQ=="),
"_ct" : BinData(0,"YaU4z/UY3djGCKBcgMaNIFHeNp8NJ9Woyh9ahff0hRas4WD80V80JE2B8tRLUs0Qd9B7IIzHsq6O4pYub5VKJ1PIQA+/dbStZpOH/KfvPoDC6DzR5JdoAu+feU7HyFnFCMY81RZeJF5BKJylhY1+mG4="),
"__v" : 0
}
...
...
]
This will increase code reuse as well.
I have implemented an strategy that i don´t think it is most efficient but it works.
I need to have all my data in database encrypted so i can´t use the above approach.
What i did is to create an update function that finds the document i want to modify, then i construct a new schema object and assing the _id of the found document to the new object.
Then i delete the original document and after that save the new object wich has the original _id. The only problem i found is that mongoose throw an error because duplicated _id that is printed in the console but it still works and _id aren´t duplicated.
I have tried replacing the_id and traking the document with another property but it still throw that error, anyway data is stored as expected.
exports.update= (req, res, next) => {
Solucion.findOne({_id: req.params.id})
.then(document => {
if (!document) {
res.status(404).json({
message: notFoundMessage,
data: null,
error: null
})
} else {
const solucion = new Solucion({
_id: document._id,
identificacion: document.identificacion,
informacion: document.informacion,
estado: req.body
})
Solucion.deleteOne({_id: document._id})
.then(() => {return solucion.save()})
.then(result=> {
return res.status(201).json({
message: editedSavedMessage,
data: result,
error: null
});
})
.catch(err => {
errorHandler.errorHandler(err, res);
})
}
})
};
UPDATE 29/07/2020
I have found that if you use the save method using the same _id, data is stored encrypted but Mongo creates your schema structure but with all values set to null.
Beyond that it seems to work as expected as data is not visible in DB.

Resources