How to bulk update the array of values in MongoDB? - node.js

I have an array of values to update with existing array. I tried with $set it throws a error. If I have an array of values to update it should update matching item in the array.
1.If I have following documents in a collection
table
[
{
"id": 1,
"name": "Fresh Mart",
"products": [
{
"name": "Onion",
"qty": 10,
"price": "85"
},
{
"name": "Tomato",
"qty": 10,
"price": "85"
}
]
},
{
"id": 2,
"name": "Alfred super market",
"products": [
{
"name": "Onion",
"qty": 10,
"price": "85"
},
{
"name": "Tomato",
"qty": 10,
"price": "85"
}
]
}
]
Following query updates one product in first document:
Query
db.collection.update({
"$and": [
{
id: 1
},
{
"products.name": "Onion"
}
]
},
{
"$set": {
"products.name": "Onion Updated"
}
})
I tried to update both products in the first document with following query:
db.collection.update({
"$and": [
{
id: 1
},
{
"products.name": [ "Onion", "Tomato" ]
}
]
},
{
"$set": {
"products.name": "Onion Updated"
}
})
db.collection.update({
id: 1
},
{
"$addToSet": {
"products": {
"$each": [
{
"name": "Onion Updated",
"qty": 10,
"price": "85"
},
{
"name": "Tomato Updated",
"qty": 10,
"price": "85"
}
]
}
}
})
I used $addToSet it will not allow the duplicates. Is possible to replace existing value with $addToSet
but it didn't update the document.

This answer is the dynamic approach of this answer https://stackoverflow.com/a/49870042/8987128
Configure your payload
let id = 1;
let updateName = [
{
oldName: "Onion",
newName: "Onion Updated"
},
{
oldName: "Tomato",
newName: "Tomato Updated"
},
];
Prepare arrayFilters and set conditions
let arrayFilters = [];
let set = {};
updateName.forEach((v, i) => {
arrayFilters.push({ ["p"+i]: v.oldName });
set["products.$[p"+i+"].name"] = v.newName;
});
Update query
db.collection.update(
{ id: id },
{ $set: set },
{ arrayFilters: arrayFilters }
)
Playground

Related

How to bulkWrite decrease quantity in products when order mongodb?

I have table "products" in mongodb example:
{
"_id": "62ab02ebd3e608133c947798",
"status": true,
"name": "Meat",
"type": "62918ab4cab3b0249cbd2de3",
"price": 34400,
"inventory": [
{
"_id": "62af007abb78a63a44e88561",
"locator": "62933b3fe744ac34445c4fc0",
"imports": [
{
"quantity": 150,
"_id": "62aefddcd5b52c1da07521f2",
"date_manufacture": "2022-03-01T10:43:11.842Z",
"date_expiration": "2023-05-20T10:43:20.431Z"
},
{
"quantity": 200,
"_id": "62af007abb78a63a44e88563",
"date_manufacture": "2022-04-01T10:45:01.711Z",
"date_expiration": "2023-05-11T10:45:06.882Z"
}
]
},
{
"_id": "62b3c2545a78fb4414dd718f",
"locator": "62933e07c224b41fc48a1182",
"imports": [
{
"quantity": 120,
"_id": "62b3c2545a78fb4414dd7190",
"date_manufacture": "2022-03-01T01:30:07.053Z",
"date_expiration": "2023-05-01T10:43:20.431Z"
}
]
}
],
}
I want to decrease quantity in one locator by id in imports of inventory with multiple product (bulkWrite). And can I decrease quantity sort by date_expiration?
Example: when customer order product with quantity 300 and locator 62933b3fe744ac34445c4fc0, I want to product update belike:
{
...
"name": "Meat",
"price": 34400,
"inventory": [
{
"_id": "62af007abb78a63a44e88561",
"locator": "62933b3fe744ac34445c4fc0",
"imports": [
{
"quantity": 50,
"_id": "62aefddcd5b52c1da07521f2",
"date_manufacture": "2022-03-01T10:43:11.842Z",
"date_expiration": "2023-05-20T10:43:20.431Z"
}
]
},
{
"_id": "62b3c2545a78fb4414dd718f",
"locator": "62933e07c224b41fc48a1182",
"imports": [
{
"quantity": 120,
"_id": "62b3c2545a78fb4414dd7190",
"date_manufacture": "2022-03-01T01:30:07.053Z",
"date_expiration": "2023-05-01T10:43:20.431Z"
}
]
}
],
}
Thank you so much!
You should refactor your schema as nesting array as it is considered an anti-pattern and introduces unnecessary complexity to query.
One of the options:
db={
"products": [
{
"_id": "62ab02ebd3e608133c947798",
"status": true,
"name": "Meat",
"type": "62918ab4cab3b0249cbd2de3",
"price": 34400,
"inventory": [
"62af007abb78a63a44e88561",
"62b3c2545a78fb4414dd718f"
]
}
],
"inventory": [
{
"_id": "62af007abb78a63a44e88561",
"locator": "62933b3fe744ac34445c4fc0",
"imports": [
{
"quantity": 150,
"_id": "62aefddcd5b52c1da07521f2",
"date_manufacture": ISODate("2022-03-01T10:43:11.842Z"),
"date_expiration": ISODate("2023-05-20T10:43:20.431Z")
},
{
"quantity": 200,
"_id": "62af007abb78a63a44e88563",
"date_manufacture": ISODate("2022-04-01T10:45:01.711Z"),
"date_expiration": ISODate("2023-05-11T10:45:06.882Z")
}
]
},
{
"_id": "62b3c2545a78fb4414dd718f",
"locator": "62933e07c224b41fc48a1182",
"imports": [
{
"quantity": 120,
"_id": "62b3c2545a78fb4414dd7190",
"date_manufacture": ISODate("2022-03-01T01:30:07.053Z"),
"date_expiration": ISODate("2023-05-01T10:43:20.431Z")
}
]
}
]
}
You can then do something relatively simple. Use $sortArray to sort the date_expiration and start to iterate through the arrays using $reduce.
db.inventory.aggregate([
{
$match: {
locator: "62933b3fe744ac34445c4fc0"
}
},
{
"$set": {
"imports": {
$sortArray: {
input: "$imports",
sortBy: {
date_expiration: 1
}
}
}
}
},
{
$set: {
result: {
"$reduce": {
"input": "$imports",
"initialValue": {
"qtyToDecrease": 300,
"arr": []
},
"in": {
"qtyToDecrease": {
$subtract: [
"$$value.qtyToDecrease",
{
$min: [
"$$value.qtyToDecrease",
"$$this.quantity"
]
}
]
},
"arr": {
"$concatArrays": [
"$$value.arr",
[
{
"$mergeObjects": [
"$$this",
{
"quantity": {
$subtract: [
"$$this.quantity",
{
$min: [
"$$value.qtyToDecrease",
"$$this.quantity"
]
}
]
}
}
]
}
]
]
}
}
}
}
}
},
{
$set: {
imports: "$result.arr",
result: "$$REMOVE"
}
},
{
"$merge": {
"into": "inventory",
"on": "_id"
}
}
])
Mongo Playground
Here is another version that keeps your original schema. You can see it is much more complex.

MongoDb findOneAndUpdate does not update specific document

I'm having trouble getting and updating the only document that matches filter in nest array of objects in mongoose, I'm using the findOneAndUpdate query in mongoose.
This is my data:
{
"_id": "62e87e193fe01f5068f9ae11",
"year": "2023",
"month": "1",
"department_id":"62e387d39ffb6ada6c590fbf",
"blocks": [
{
"name": "CEEDO Schedule Block",
"days": [
{
"day": 2,
"employees": [
{
"employee_id":"62cf92fb3a790000170062e3",
"schedule_type": "Day Off"
},
{
"employee_id": "62cf92fb3a790000170062e2",
"schedule_type": "Shifting"
},
{
"employee_id": "62cf92fb3a790000170062e4",
"schedule_type": "Regular"
}
],
"_id": "62e87e193fe01f5068f9ae13"
},
{
"day": 6,
"employees": [
{
"employee_id": "62cf92fb3a790000170062e3",
"schedule_type": "Day Off"
},
{
"employee_id": "62cf92fb3a790000170062e2",
"schedule_type": "Shifting"
},
{
"employee_id":"62cf92fb3a790000170062e4",
"schedule_type": "Regular"
}
],
"_id": "62e87e193fe01f5068f9ae14"
}
],
"_id": "62e87e193fe01f5068f9ae12"
}
]
}
And here is my query:
const update_block = await schedule_model.findOneAndUpdate({'blocks.days._id': '62e87e193fe01f5068f9ae13'},
{
$set: {"days":req.body.days, "employees":req.body.employees}
}
);
Thanks in advance.
try change '62e87e193fe01f5068f9ae13' to mongoose.Types.ObjectId('62e87e193fe01f5068f9ae13')
I finally found the answer by using the arrayFilter function in mongoose:
const update_block = await schedule_model.updateOne({
"_id": mongoose.Types.ObjectId('62e87e193fe01f5068f9ae11')
}, {
"$set": {
"blocks.$[i].days.$[j].day": 31
}
}, {
arrayFilters: [{
"i._id":mongoose.Types.ObjectId('62e87e193fe01f5068f9ae12')
}, {
"j._id": mongoose.Types.ObjectId('62e87e193fe01f5068f9ae14')
}]
})
console.log(update_block)
Thank you.

Use Mongoose aggregate to fetch object inside of an array

Here is my MongoDB schema:
{
"_id": "603f23ff6c1d862e5ced9e35",
"reviews": [
{
"like": 0,
"dislike": 0,
"_id": "603f23ff6c1d862e5ced9e34",
"userID": "5fd864abb53d452e0cbb5ef0",
"comment": "Not so good",
},
{
"like": 0,
"dislike": 0,
"_id": "603f242a6c1d862e5ced9e36",
"userID": "5fd864abb53d452e0cbb5ef0",
"comment": "Not so good",
}
]
productID:"hdy6nch99dndn"
}
I want to use aggregate to get the review object of a particular id. I tried but not with any success.
Here is my code:
ProductReview.aggregate([
{ $match: { productID: productID } }
])
$match
$unwind
db.collection.aggregate([
{
$match: {
productID: 1
}
},
{
$unwind: "$reviews"
},
{
$match: {
"reviews._id": 2
}
}
])
Output:
[
{
"_id": ObjectId("5a934e000102030405000000"),
"productID": 1,
"reviews": {
"_id": 2,
"comment": "second comment",
"dislikes": [
{
"userID": 3
},
{
"userID": 4
}
],
"likes": [
{
"userID": 1
},
{
"userID": 2
}
]
}
}
]
Mongo Playground: https://mongoplayground.net/p/qfWS1rCuMfc

I need to push into nested array in node mongoose

I used node mongoose.
I need to update this array push new item into Breackfast(mealList.foodList.breackfast || any),
I want to add new foodlist by time can you please give me suggestion for how to do,
{
"_id": "5fe43eb44cd6820963c98c32",
"name": "Monday Diet",
"userID": "5f225d7458b48d0fe897662e",
"day": "Monday",
"type": "private",
"mealList": [
{
"_id": "5fe43eb44cd6820963c98c33",
"time": "Breakfast",
"foodList": [
{
"_id": "5fe43eb44cd6820963c98c34",
"foodName": "Eggs",
"Qty": "2",
"calories": "calories",
"category": "category"
}
]
},
{
"_id": "5fe43eb44cd6820963c98c36",
"time": "Lunch",
"foodList": [
{
"_id": "5fe43eb44cd6820963c98c37",
"foodName": "food1",
"Qty": "100g"
},
]
}
],
"createdAt": "2020-12-24T07:09:40.141Z",
"updatedAt": "2020-12-24T07:09:40.141Z",
"__v": 0
}
I tried:
Diet.updateOne(
{ "Diet.mealList._id": req.body.mealId },
// { $push: { "Diet.0.mealList.$.foodList": req.body.foodList } },
{ $push: { foodList: req.body.foodList } }
)
Few Fixes:
convert your string _id to object type using mongoose.Types.ObjectId
remove Diet from first and push object in foodList
Diet.updateOne({
"mealList._id": mongoose.Types.ObjectId(req.body.mealId)
},
{
$push: {
"mealList.$.foodList": req.body.foodList
}
})
Playground

Add a new array in a nested document [duplicate]

I want add new data my nested array
My document is:
{
"username": "erkin",
"email": "erkin-07#hotmail.com",
"password": "b",
"playlists": [
{
"_id": 58,
"name": "asdsa",
"date": "09-01-15",
"musics": [
{
"name": "INNA - Cola Song (feat. J Balvin)",
"duration": "3.00"
},
{
"name": "blabla",
"duration": "3.00"
}
]
}
]
}
I want add music in this playlist section:
{
"username": "erkin",
"email": "erkin-07#hotmail.com",
"password": "b",
"playlists": [
{
"_id": 58,
"name": "asdsa",
"date": "09-01-15",
"musics": [
{
"name": "INNA - Cola Song (feat. J Balvin)",
"duration": "3.00"
},
{
"name": "blabla",
"duration": "3.00"
},
{
"name": "new",
"duration": "3.00"
}
]
}
]
}
Here is what I tried:
$users->update(
array(
'_id' => new MongoId (Session::get('id')),
'playlists._id' => $playlistId
),
array(
'$push' => array('playlists.musics' => array(
'name' => 'newrecord',
'duration' => '3.00'
))
)
);
Probably something like this where ID is your ObjectId. The first {} are necessary to identify your document. It is not required to use an ObjectId as long as you have another unique identifier in your collection.
db.collection.update(
{ "_id": ID, "playlists._id": "58"},
{ "$push":
{"playlists.$.musics":
{
"name": "test name",
"duration": "4.00"
}
}
}
)
This way it worked for me!
"playlists.$[].musics":
db.collection.update(
{ "_id": ID, "playlists._id": "58"},
{ "$push":
{"playlists.$[].musics":
{
"name": "test name",
"duration": "4.00"
}
}
}
)
https://docs.mongodb.com/manual/reference/operator/update/positional-filtered/#position-nested-arrays-filtered
I suggest you using arrayFilters since it supports multiple nested documents and clearer.
db.collection.update(
{ "_id": ID},
{ "$push":
{"playlists.$[i].musics":
{
"name": "test name",
"duration": "4.00"
}
}
},
{
arrayFilters: [
{'i._id': 58,},
],
},
)
2022 update:
Full snippet:
from pymongo import MongoClient
client = MongoClient('mongodb://localhost:27017/', maxPoolSize=50)
db = client.name_of_db
collection = db["name_of_collection"]
To push:
collection.find_one_and_update(
{"_id": 'id_of_the_document'},
{"$push": {"key":"value"}})
To push into nested:
collection.find_one_and_update(
{"_id": 'id_of_the_document'},
{"$push": {"key.nested_key":"value"}})

Resources