Multiple updates in one document using mongoose - node.js

I want to update multiple values in a single document in one mongoose call. Is this possible?
I have something similar to this:
update = {$inc : { numShown : 1 }, $inc : { secondField.subField : 1 }};
options = {};
MyModel.findByIdAndUpdate(req._id, update, options, function(err){
if(err){ return console.error(err);}
}
It runs, but doesn't update anything.

You need to combine the two $inc values into a single object and quote the dotted key:
update = { $inc : { numShown : 1, 'secondField.subField' : 1 } };

Related

Mongoose - Update a nested object in array in MongoDB

Alright, this might have been asked quite a lot of times but none of them gives me a solution.
Here's my schema.
{
"_id" : ObjectId("23453453453453"),
"title": "Item 01"
"checkList" : [
{
"ch_id" : "621eff4e0ed5c751adaa42fb",
"status" : "statu",
"dateMonthYear" : 1646286480139.0,
"val" : "Gopi",
"remarks" : "Good",
"_id" : ObjectId("7555777575")
},
{
"ch_id" : "621eff4e0ed5c751adaa42fb",
"status" : "status",
"dateMonthYear" : 1646286480139.0,
"val" : "Gopi",
"remarks" : "Good",
"_id" : ObjectId("7555777575")
}
]
}
What I want to do is update the status in the 2nd object in the checklist array. I can use the following query to update this just fine.
const itemUpdated = await Item.updateOne(
{_id: id, 'checklist._id': req.params.id},
{$set: { "checklist.$.status": req.body.status }},
);
But, I want to use Mongoose method like save() to update this. Not RAW query. Because with Mongoose methods, I get extra layer of validation and middleware. I checked all over internet but only found ones with raw queries.
How to update a nested object in array with Mongoose ORM ?
Retrieve the Item and loop to its checkList updating the status:
const item = await Item.findOne({ _id: id, 'checklist._id': req.params.id });
if (!item || item.checkList.length === 0) return;
for (let i = 0; i < item.checkList.length; i++) {
item.checkList[i].status = req.body.status;
}
await item.save();
I finally found this solution that works best for this situation. I don't know why Mongoose docs are so poorly written.
If we have an array of subdocuments, we can fetch the one we need with id() method provided by Mongoose.
const item = await Item.findById(id);
checkListItem = item?.checklist.id(req.params.id);
if(checkListItem){
checkListItem.status = req.body.status;
item?.save();
}
This worked for me. Hope someone might find this useful!

Bulk Save/Update lists of data in MongoDB (Nodejs)

I have lists of data which I want to save and if already exist update.
I can do that using loop. But Is there any other way like insertMany which only supports insert but I want to insert and update too in bulk.
You can use the bulk update feature that Mongo driver provides. Instead of invoking the transactions in a loop, you may add them to a bulk transaction and execute as a batch.
First you need to initialize the bulk operation, ordered / unordered:
var bulk = db.collection.initializeUnorderedBulkOp();
or
var bulk = db.collection.initializeOrderedBulkOp();
Then you can go on adding transactions to the bulk object.
bulk.insert( {
// attributes
} ); // insert operation
or
bulk.find( {
// query attributes
} ).update( {
$set: {
// set attributes
} } ); // update operation
In the end you need to call
bulk.execute();
I don't know if this would serve your purpose.
Please refer this link:
https://docs.mongodb.com/manual/reference/method/Bulk/
Use updateMany with option { upsert: true } which update the document if it is already exists otherwise insert the new document.
Find below example with restaurant collection
{ "_id" : 1, "name" : "Central Perk Cafe", "violations" : 3 }
{ "_id" : 2, "name" : "Rock A Feller Bar and Grill", "violations" : 2 }
{ "_id" : 3, "name" : "Empire State Sub", "violations" : 5 }
{ "_id" : 4, "name" : "Pizza Rat's Pizzaria", "violations" : 8 }
The query below update the documents with violations equal to 4 and if the document not exists insert new document.
db.restaurant.updateMany(
{ violations: 4 },
{ $set: { "name" : "Eat and Treat" } },
{ upsert: true }
);
Find more details here:
https://docs.mongodb.com/manual/reference/method/db.collection.updateMany/

Persist rest of the object when updating fields with $set- mongoose

Below is my collection structure :
{
"_id" : ObjectId("59c9e021af4886c9149a28c7"),
"userkey" : "r1Et2ZNdW",
"devicekey" : "12345",
"analog" : {
"4" : 458,
"6" : 448,
"7" : 100
},
"__v" : 0
}
My update code for the collection is :
DeviceModel.update({_id : device.id}, {$set : {"analog" : analog}}, function(err, doc){
if(err) throw err;
console.log("Device switches updated");
});
I want to update the one (or more) value of "analog" object. I may or may not have entire value of the "analog" collection. Which means most of the time I will be having just {"7" : 200} or {"6" : 500}...etc. If I use this in my above update code it works. But the problem is rest of the items in the object will be removed. That is, the "analog" object will become just {"7" : 200}. What change should I do to my update code to persist the rest of the Object.? or do I need to change my entire logic ?
You need to first flatten the analog variable object into another object with a property that uses the dot notation. This enables the $set operator to correctly update the embedded field denoted in the dot notation without updating/removing/affecting the other fields.
So, in your example you can go about this:
let setObject = {};
Object.keys(analog).forEach(function (key) {
setObject[`analog.${key}`] = analog[key];
});
DeviceModel.update(
{ '_id': device.id },
{ '$set': setObject },
function(err, doc) {
if(err) throw err;
console.log("Device switches updated");
}
);

How to update a field using its previous value in MongoDB/Mongoose

For example, I have some documents that look like this:
{
id: 1
name: "foo"
}
And I want to append another string to the current name field value.
I tried the following using Mongoose, but it didn't work:
Model.findOneAndUpdate({ id: 1 }, { $set: { name: +"bar" } }, ...);
Edit:
From Compatibility Changes in MongoDB 3.6:
MongoDB 3.6.1 deprecates the snapshot query option.
For MMAPv1, use hint() on the { _id: 1} index instead to prevent a cursor from returning a document more than once if an intervening write operation results in a move of the document.
For other storage engines, use hint() with { $natural : 1 } instead.
Original 2017 answer:
You can't refer to the values of the document you want to update, so you will need one query to retrieve the document and another one to update it. It looks like there's a feature request for that in OPEN state since 2016.
If you have a collection with documents that look like:
{ "_id" : ObjectId("590a4aa8ff1809c94801ecd0"), "name" : "bar" }
Using the MongoDB shell, you can do something like this:
db.test.find({ name: "bar" }).snapshot().forEach((doc) => {
doc.name = "foo-" + doc.name;
db.test.save(doc);
});
The document will be updated as expected:
{ "_id" : ObjectId("590a4aa8ff1809c94801ecd0"), "name": "foo-bar" }
Note the .snapshot() call.
This ensures that the query will not return a document multiple times because an intervening write operation moves it due to the growth in document size.
Applying this to your Mongoose example, as explained in this official example:
Cat.findById(1, (err, cat) => {
if (err) return handleError(err);
cat.name = cat.name + "bar";
cat.save((err, updatedCat) => {
if (err) return handleError(err);
...
});
});
It's worth mentioning that there's a $concat operator in the aggregation framework, but unfortunately you can't use that in an update query.
Anyway, depending on what you need to do, you can use that together with the $out operator to save the results of the aggregation to a new collection.
With that same example, you will do:
db.test.aggregate([{
$match: { name: "bar" }
}, {
$project: { name: { $concat: ["foo", "-", "$name"] }}
}, {
$out: "prefixedTest"
}]);
And a new collection prefixedTest will be created with documents that look like:
{ "_id" : ObjectId("XXX"), "name": "foo-bar" }
Just as a reference, there's another interesting question about this same topic with a few answers worth reading: Update MongoDB field using value of another field
If this is still relevant, I have a solution for MongoDB 4.2.
I had the same problem where "projectDeadline" fields of my "project" documents were Array type (["2020","12","1"])
Using Robo3T, I connected to my MongoDB Atlas DB using SRV link. Then executed the following code and it worked for me.
Initial document:
{
_id : 'kjnolqnw.KANSasdasd',
someKey : 'someValue',
projectDeadline : ['2020','12','1']
}
CLI Command:
db
.getCollection('mainData')
.find({projectDeadline: {$not: {$eq: "noDeadline"}}})
.forEach((doc) => {
var deadline = doc.projectDeadline;
var deadlineDate = new Date(deadline);
db
.mainData
.updateOne({
_id: doc._id},
{"$set":
{"projectDeadline": deadlineDate}
}
)}
);
Resulting document:
{
_id : 'kjnolqnw.KANSasdasd',
someKey : 'someValue',
projectDeadline : '2020-12-01 21:00:00.000Z'
}

Passing variables into a query in mongoose in the first argument

I am using MEAN stack, i have an entry like this in my mongodb
{ "_id" : ObjectId("5577467683f4716018db19ed"),
"requestMatrix" : { "1698005072" : { "rideId" : "641719948", "status" :"accepted" },"1698005073" : { "rideId" : "641719545", "status" :"rejected" } },
"partners":[ { "customerNumber" : 1698005072 }, { "customerNumber" : 1698072688 } ]}
I want to query the db to return me this entire document based on whether the status is accepted or rejected.
When I run the below query in a command prompt, i get the expected answer
db.joinedrides.find({'requestMatrix.1698005072.status':"accepted"})
But when i want to do the same from nodeJs, I am stuck as the number 1698005072 in the above query is a variable, i am not able to write a query for that.
tried something like this
var criteria = "'requestMatrix.'"+customerNumber+"'.status'";
JoinedRide.find({criteria:"accepted"},function(err,joinedRides){
})
where customerNumber will vary for different requests, in the above mentioned case its value is 1698005072
Any help is appreciated.
You need to do something like this:
var query = {};
var criteria = "requestMatrix." + customerNumber + ".status";
query[criteria] = "accepted"
JoinedRide.find(query,function(err,joinedRides){
})

Resources