Mongoose - Update multi objects inside an array by ID - mongodb

Firstly, sorry for my bad English.
Secondly, I want to ask, how to multi-update my document.
I have a row structure like this:
Data Model
[
{
"id": '00001',
"exp": '192',
"items": [
{ "qty": 23, "ID": ObjectId("kieryu35261twerts73940djr") },
{ "qty": 77, "ID": ObjectId("1240a1ffuw33dbcv6ut8888zz") },
{ "qty": 1, "ID": ObjectId("5600r0e0rr67cbd60a1234y5") },
{ "qty": 5, "ID": ObjectId("32bbd0e0rr92cdb60a4386c7") }
],
"stats": [
{ "strenght": 1, "ID": ObjectId("3eruskdjfhrutiwo4059yuol3") },
{ "strenght": 2, "ID": ObjectId("3jdks0d9r2seifldvjmw0km2e") },
{ "strenght": 1, "ID": ObjectId("aslkdnasklnc2112uhnsjssad") },
{ "strenght": 5, "ID": ObjectId("1230msafmlklkmasfmcio3kms") }
]
},
{
"id": '00002',
"exp": '100',
"items": [
{ "strenght": 10, "ID": ObjectId("21312kn3kaklmasklcklasmck") },
{ "strenght": 10, "ID": ObjectId("kldsafklsajdfklmsadlkaskl") }
],
"stats": [
{ "strenght": 9, "ID": ObjectId("aslkclkamiior2oinrkl2adsa") },
{ "strenght": 0, "ID": ObjectId("asdoasjdosmdkl123123kmnsd") }
]
}
]
I want to update one document row by finding the id of the document, and multi-deep elements by ID too. Like this:
{
"id": '00001',
"exp": '555',
"items": [
{ "qty": 44, "ID": ObjectId("kieryu35261twerts73940djr") },
{ "qty": 55, "ID": ObjectId("1240a1ffuw33dbcv6ut8888zz") },
{ "qty": 66, "ID": ObjectId("5600r0e0rr67cbd60a1234y5") },
{ "qty": 77, "ID": ObjectId("32bbd0e0rr92cdb60a4386c7") }
],
"stats": [
{ "strenght": 10, "ID": ObjectId("3eruskdjfhrutiwo4059yuol3") },
{ "strenght": 20, "ID": ObjectId("3jdks0d9r2seifldvjmw0km2e") },
{ "strenght": 12, "ID": ObjectId("aslkdnasklnc2112uhnsjssad") },
{ "strenght": 54, "ID": ObjectId("1230msafmlklkmasfmcio3kms") }
]
}
And last, just for information, I do before this:
await DataModel.findOneAndUpdate(
{
"id" : idvariable // == 00001
},
{
"$set" : {
"exp" : 555,
"items": {
//update qty == 44 where ID == kieryu35261twerts73940djr
//update qty == 55 where ID == 1240a1ffuw33dbcv6ut8888zz
//update qty == 66 where ID == 5600r0e0rr67cbd60a1234y5
//update qty == 77 where ID == 32bbd0e0rr92cdb60a4386c7
},
"stats": {
//update strenght == 10 where ID == 3eruskdjfhrutiwo4059yuol3
//update strenght == 20 where ID == 3jdks0d9r2seifldvjmw0km2e
//update strenght == 12 where ID == aslkdnasklnc2112uhnsjssad
//update strenght == 54 where ID == 1230msafmlklkmasfmcio3kms
}
}
}
)
Please, I don't know how to update it with a single query update, or other technique. Thank you.

Possible, but a bit long query.
Working on the Update with Aggregation Pipeline,
$map - Iterate the items in the array and return a new array.
$switch - Switch-case (statement) to match the ID of the current iterated document and update the document if matched. If all the case conditions are failed to match, remain the existing document.
db.collection.update({
"id": idvariable// == 00001
},
[
{
"$set": {
"exp": 555,
"items": {
$map: {
input: "$items",
in: {
$switch: {
branches: [
{
case: {
$eq: [
"$$this.ID",
"kieryu35261twerts73940djr"
]
},
then: {
$mergeObjects: [
"$$this",
{
qty: 44
}
]
}
},
{
case: {
$eq: [
"$$this.ID",
"1240a1ffuw33dbcv6ut8888zz"
]
},
then: {
$mergeObjects: [
"$$this",
{
qty: 55
}
]
}
},
{
case: {
$eq: [
"$$this.ID",
"5600r0e0rr67cbd60a1234y5"
]
},
then: {
$mergeObjects: [
"$$this",
{
qty: 66
}
]
}
},
{
case: {
$eq: [
"$$this.ID",
"32bbd0e0rr92cdb60a4386c7"
]
},
then: {
$mergeObjects: [
"$$this",
{
qty: 77
}
]
}
}
],
default: "$$this"
}
}
}
},
"stats": {
$map: {
input: "$stats",
in: {
$switch: {
branches: [
{
case: {
$eq: [
"$$this.ID",
"3eruskdjfhrutiwo4059yuol3"
]
},
then: {
$mergeObjects: [
"$$this",
{
strenght: 10
}
]
}
},
{
case: {
$eq: [
"$$this.ID",
"3jdks0d9r2seifldvjmw0km2e"
]
},
then: {
$mergeObjects: [
"$$this",
{
strenght: 20
}
]
}
},
{
case: {
$eq: [
"$$this.ID",
"aslkdnasklnc2112uhnsjssad"
]
},
then: {
$mergeObjects: [
"$$this",
{
strenght: 12
}
]
}
},
{
case: {
$eq: [
"$$this.ID",
"1230msafmlklkmasfmcio3kms"
]
},
then: {
$mergeObjects: [
"$$this",
{
strenght: 54
}
]
}
}
],
default: "$$this"
}
}
}
}
}
}
])
For the items and stats with ID to be updated, make sure that you need to parse as ObjectId [Note that Mongo Playground doesn't recognize your provided IDs, I manually change the dataset for those IDs as string but the concept for the update is the same].
Sample Mongo Playground

Related

Mongodb Aggregate Filter Array Of Array Of Array

We would like to filter SKU's List which has verificationData data and differenceInStock difference greater than or Less than 0
Here is an example Data Set.
[
{
"_id": "636e0beaa13ef73324e613f0",
"status": "ACTIVE",
"inventory": 132,
"parentCategory": [
"Salt"
],
"title": "Aashirvaad MRP: 28Rs Salt 27 kg Bopp Bag (Set of 1 kg x 27)",
"createdAt": "2022-11-11T08:46:34.950Z",
"updatedAt": "2022-11-24T17:43:27.361Z",
"__v": 3,
"verificationData": [
{
"_id": "637c57ebbe783a9a138fc2d3",
"verificationDate": "2022-11-22T05:02:35.155Z",
"items": {
"listingId": "636e0beaa13ef73324e613f0",
"phyiscalVerification": [
{
"verifiedBy": "634534e72ef6462fcb681a39",
"closingStock": 178,
"phyiscalStock": 178,
"differenceInStock": 0,
"verifiedAt": "2022-11-22T10:19:38.388Z",
"_id": "637ca23abe783a9a1394f402"
}
],
"_id": "637ca23abe783a9a1394f401"
},
"yearMonthDayUTC": "2022-11-22"
},
{
"_id": "637d9b65be783a9a13998726",
"verificationDate": "2022-11-23T04:02:45.804Z",
"items": {
"listingId": "636e0beaa13ef73324e613f0",
"phyiscalVerification": [
{
"verifiedBy": "634534e72ef6462fcb681a39",
"closingStock": 161,
"phyiscalStock": 167,
"differenceInStock": 6,
"verifiedAt": "2022-11-23T09:52:36.815Z",
"_id": "637ded64be783a9a13a29d55"
}
],
"_id": "637ded64be783a9a13a29d54"
},
"yearMonthDayUTC": "2022-11-23"
},
{
"_id": "637f0254be783a9a13a94354",
"verificationDate": "2022-11-24T05:34:12.995Z",
"items": {
"listingId": "636e0beaa13ef73324e613f0",
"phyiscalVerification": [
{
"verifiedBy": "634534e72ef6462fcb681a39",
"closingStock": 144,
"phyiscalStock": 146,
"differenceInStock": 2,
"verifiedAt": "2022-11-24T12:02:28.123Z",
"_id": "637f5d54be783a9a13b1039a"
}
],
"_id": "637f5d54be783a9a13b10399"
},
"yearMonthDayUTC": "2022-11-24"
},
{
"_id": "2022-11-25",
"yearMonthDayUTC": "2022-11-25",
"items": null
}
]
},
{
"_id": "62b5c39062ddb963fc64c42d",
"status": "ACTIVE",
"inventory": 10,
"parentCategory": [
"Salt"
],
"finalMeasurementUnit": "kg",
"finalMeasure": "1 kg",
"title": "Marvella Citric Acid Lemon Salt 1 kg Pouch (Set of 500 gm x 2)",
"createdAt": "2022-06-24T14:00:49.052Z",
"updatedAt": "2022-11-21T11:04:21.643Z",
"__v": 2,
"verificationData": [
{
"_id": "2022-11-22",
"yearMonthDayUTC": "2022-11-22",
"items": null
},
{
"_id": "2022-11-23",
"yearMonthDayUTC": "2022-11-23",
"items": null
},
{
"_id": "2022-11-24",
"yearMonthDayUTC": "2022-11-24",
"items": null
},
{
"_id": "2022-11-25",
"yearMonthDayUTC": "2022-11-25",
"items": null
}
]
}
]
This could have array of 100+ SKU's
Our Aggregate Functions is as Follows
let reqData = await userListing.aggregate([
{
$match: {
warehouseId: { $eq: ObjectId(warehouseId) },
parentCategory: { $in: catList },
isWarehouseListing: { $eq: true },
isBlocked: { $ne: true },
isArchived: { $ne: true },
},
},
{ $sort: { whAddedAt: -1 } },
{
$lookup: {
from: "listingstockverifications",
let: { listId: "$_id" },
pipeline: [
{
$match: {
verificationDate: {
$gte: newFromDate,
$lt: newToDate,
},
},
},
{
$project: {
verificationDate: 1,
items: {
$filter: {
input: "$items",
cond: {
$and: [
/* {
"$$this.phyiscalVerification": {
$filter: {
input: "$$this.phyiscalVerification",
as: "psitem",
cond: { $gt: [ "$$psitem.differenceInStock", 0 ] },
},
},
}, */
{
$eq: ["$$this.listingId", "$$listId"],
},
],
},
},
},
yearMonthDayUTC: {
$dateToString: {
format: "%Y-%m-%d",
date: "$verificationDate",
},
},
},
},
{ $unwind: "$items" },
],
as: "stockVerification",
},
},
{
$addFields: {
verificationData: {
$map: {
input: dummyArray,
as: "date",
in: {
$let: {
vars: {
dateIndex: {
$indexOfArray: [
"$stockVerification.yearMonthDayUTC",
"$$date",
],
},
},
in: {
$cond: {
if: { $ne: ["$$dateIndex", -1] },
then: {
$arrayElemAt: ["$stockVerification", "$$dateIndex"],
},
else: {
_id: "$$date",
yearMonthDayUTC: "$$date",
items: null,
},
},
},
},
},
},
},
},
},
{
$project: {
stockVerification: 0,
},
},
]);
At Last now we would like to filter the SKU List the which has following Data
verificationData[].items.phyiscalVerification[].differenceInStock is Greater than or Less than 0
Expected Output in the following Exmaple would be 1st SKUs
as 2nd SKU does not have any Item Data
and even if in 3rd SKU if we got Item Data but should match the following condition
verificationData[].items.phyiscalVerification[].differenceInStock is Greater than or Less than 0
Thank you for taking your time to read and support.
You can add these two following stages to your aggregation, The idea is simple - just filter out all subdocuments that do not match the condition.
Because of the nested structure it's just not the sexiest of pipelines but it will suffice.
db.collection.aggregate([
{
$match: {
$or: [
{
"verificationData.items.phyiscalVerification.differenceInStock": {
$gt: 0
}
},
{
"verificationData.items.phyiscalVerification.differenceInStock": {
$lt: 0
}
}
]
}
},
{
$addFields: {
verificationData: {
$filter: {
input: {
$map: {
input: {
$filter: {
input: "$verificationData",
as: "verification",
cond: {
$ne: [
"$$verification.items",
null
]
}
}
},
as: "top",
in: {
$mergeObjects: [
"$$top",
{
"items": {
"$mergeObjects": [
"$$top.items",
{
phyiscalVerification: {
$filter: {
input: "$$top.items.phyiscalVerification",
as: "pshycical",
cond: {
$ne: [
"$$pshycical.differenceInStock",
0
]
}
}
}
}
]
}
}
]
}
}
},
cond: {
$gt: [
{
$size: "$$this.items.phyiscalVerification"
},
0
]
}
}
}
}
}
])
Mongo Playground

MongoDB : group and count users by gender, civilStatus and professionalCategory

I have a collection of users, each user has a profile. I want to implement a query to make statistics on users.
This is my collection.
[
{
"_id": ObjectId("61d2db0d273a9076d630697b"),
"state": "VALIDATED",
"phone": "xxx",
"civilStatus": "SINGLE",
"gender": "MALE",
"professionalCategory": "STUDENT"
}
]
I want the result to contain an array of all genders of users in the database, and the number of users with each gender. same for civilStatus and professionalCategories
This is the result i am looking for :
{
"total": 2000
"validated": 1800,
"genders": [
{
"value": "MALE",
"count": 1200
},
{
"value": "FEMALE",
"count": 600
}
],
"civilStatus": [
{
"value": "SINGLE",
"count": "300"
}
...
],
"professionalCategories": [
{
"value": "STUDENT",
"count": "250"
}
...
]
}
I implemented the query, but I still have a few things that I don't know how to do.
db.getCollection("users").aggregate([
{
$group: {
_id: null,
validated: {
$sum: {
$cond: {
if: { $eq: ["$state", "VALIDATED"] },
then: 1,
else: 0
}
}
},
genders: {
$push: "$gender"
},
civilStatus: {
$push: "$civilStatus"
},
professionalCategories: {
$push: "$professionalCategory"
}
}
}
])
This is the result of this query :
{
"total": 2000
"validated": 1800,
"genders": [
"MALE",
"MALE",
"FEMALE",
"MALE",
"FEMALE",
"FEMALE"
...
],
"civilStatus": [
"SINGLE",
"MARIED",
"SINGLE",
...
],
"professionalCategories": [
"STUDENT",
"WORKER",
"RETIRED"
...
]
}
I miss how to group each gender, civil Status and professional Category and calculate the number of users for each one.
I also tried this query, but I don't know how to complete the "count" field for each item of the array :
db.getCollection("users").aggregate([
{
$group: {
_id: null,
validated: {
$sum: {
$cond: {
if: { $eq: ["$state", "VALIDATED"] },
then: 1,
else: 0
}
}
},
genders: {
$addToSet: {
value: "$gender",
count: {
//
}
}
},
civilStatus: {
$addToSet: {
value: "$civilStatus",
count: {
//
}
}
},
professionalCategories: {
$addToSet: {
value: "$professionalCategory",
count: {
//
}
}
},
}
}
])
if the query was to treat only one field, for example gender. it would have been easier with "unwind". but here I have 3 fields.
can someone help me please?
You can use following aggregation
Here is the code
db.collection.aggregate([
{
"$facet": {
"genders": [
{
"$group": {
"_id": "$gender",
"total": { $sum: 1 }
}
}
],
"civilStatus": [
{
"$group": {
"_id": "$civilStatus",
"total": { $sum: 1 }
}
}
],
"professionalCategory": [
{
"$group": {
"_id": "$professionalCategory",
"total": { $sum: 1 }
}
}
],
"validated": [
{
"$group": {
"_id": "$state",
"total": { "$sum": 1 }
}
}
]
}
},
{
$set: {
validated: {
"$filter": {
"input": "$validated",
"cond": {
"$eq": [ "$$this._id", "VALIDATED" ]
}
}
}
}
},
{
$set: {
validated: {
"$ifNull": [
{
"$arrayElemAt": [ "$validated", 0 ]
},
0
]
}
}
},
{
$set: { validated: "$validated.total" }
}
])
Working Mongo playground

Mongoose not updating multiple matches

[
{
"id": 1,
"items": [
{
id: 15,
score: 10
},
{
id: 14,
score: 100
},
{
id: 12,
score: 1
}
]
},
{
"id": 2,
"items": []
}
]
Now, I try to update items whose id is 14,15 & used the following query.
db.collection.update({
"items.id": {
$in: [
14,
15
]
}
},
{
$set: {
"items.$.score": 444
}
},
{
multi: true
}
)
but it updated only the first match in items that is that is id with 15, what can be wrong?
[
{
"_id": ObjectId("5a934e000102030405000000"),
"id": 1,
"items": [
{
"id": 15,
"score": 444
},
{
"id": 14,
"score": 100
},
{
"id": 12,
"score": 1
}
]
},
{
"_id": ObjectId("5a934e000102030405000001"),
"id": 2,
"items": []
}
]
Use arrayFilters with targeted array elements:
here is the doc
db.collection.update({
"id": 1
},
{
"$set": {
"items.$[ele].score": 20
}
},
{
arrayFilters: [
{
"ele.id": {
"$in": [
15,
14
]
}
}
]
})
see play ground code https://mongoplayground.net/p/cdgu1aqLwsI
You can do it with Positional identifiers $[]
db.collection.update({
"items.id": {
$in: [
14,
15
]
}
},
{
$set: {
"items.$[element].score": 444
}
},
{
arrayFilters: [
{
"element.id": {
$in: [
14,
15
]
}
}
],
multi: true
})
try it here

Mongo Query to fetch distinct nested documents

I need to fetch distinct nested documents.
Please find the sample document:
{
"propertyId": 1001820437,
"date": ISODate("2020-07-17T00:00:00.000Z"),
"HList":[
{
"productId": 123,
"name": "Dubai",
"tsh": true
}
],
"PList":[
{
"productId": 123,
"name": "Dubai",
"tsh": false
},
{
"productId": 234,
"name": "India",
"tsh": true
}
],
"CList":[
{
"productId": 234,
"name": "India",
"tsh": false
}
]
}
Expected result is:
{
"produts":[
{
"productId": 123,
"name": "Dubai"
},
{
"productId": 234,
"name": "India"
}
]
}
I tried with this query:
db.property.aggregate([
{
$match: {
"propertyId": 1001820437,
"date": ISODate("2020-07-17T00:00:00.000Z")
}
},
{
"$project": {
"_id": 0,
"unique": {
"$filter": {
"input": {
"$setDifference": [
{
"$concatArrays": [
"$HList.productId",
"$PList.productId",
"$CList.productId"
]
},
[]
]
},
"cond": {
"$ne": [ "$$this", "" ]
}
}
}
}
}
]);
Is $setDifference aggregation is correct choice here?
My query returns only unique product ids but i need a productId with name.
Could someone help me to solve this?
Thanks in advance
You can use $projectfirst to get rid of tsh field and then run $setUnion which ignores duplicated entries:
db.collection.aggregate([
{
$project: {
"HList.tsh": 0,
"PList.tsh": 0,
"CList.tsh": 0,
}
},
{
$project: {
products: {
$setUnion: [ "$HList", "$PList", "$CList" ]
}
}
}
])
Mongo Playground
The following two aggregations return the expected and same result (you can use any of the two):
db.collection.aggregate( [
{
$project: {
_id: 0,
products: {
$reduce: {
input: { $setUnion: [ "$HList", "$PList", "$CList" ] },
initialValue: [],
in: {
$setUnion: [ "$$value", [ { productId: "$$this.productId", name: "$$this.name" } ] ]
}
}
}
}
}
] )
This one is little verbose:
db.collection.aggregate( [
{
$project: { list: { $setUnion: [ "$HList", "$PList", "$CList" ] } }
},
{
$unwind: "$list"
},
{
$group: {
_id: null,
products: { $addToSet: { "productId": "$list.productId", "name": "$list.name" } }
}
},
{
$project: { _id: 0 }
}
] )
db.collection.aggregate([
{
$match: {
"propertyId": 1001820437,
"date": ISODate("2020-07-17T00:00:00.000Z")
}
},
{
$project: {
products: {
$filter: {
input: { "$setUnion" : ["$CList", "$HList", "$PList"] },
as: 'product',
cond: {}
}
}
}
},
{
$project: {
"_id":0,
"products.tsh": 1,
"products.name": 1,
}
},
])

Project multiple documents with different key nodes for subdocuments

I have two documents as shown. Their common factor is a node in the subdocument (type,veg_type). I have also added the same common node to each individual document (udf_type, udf_veg_type).One is a legacy data (with key node veggies) and another is new data (with key node vegetables).
How do I project the combined data of vegetables and veggies(in key node vegs) without the type and veg_type nodes? I use user_id for matching.
Intended Output
{
"user_id": 31,
"veggies": [
{
"udf_type": "green_vegetables",
"tot": 28560,
"itms": [
{
"num": 1,
"itm_det": {
"name": "spinach",
"qty": 18
}
}
],
"chksum": "d1583afab3a04f4b32589cfa64392765n78782ff60a0e0dc24b295868083"
},
{
"udf_type": "vegetables",
"tot": 2860,
"itms": [
{
"num": 1,
"itm_det": {
"name": "onion",
"qty": 1
}
}
],
"chksum": "e497c7b288e50e3be4c6bc676e4c849e4n5645n64a2d77748e185d7a1bce8c"
},
{
"udf_veg_type": "green_vegetables",
"tot": 2352000,
"itms": [
{
"num": 1,
"itm_det": {
"name": "kale",
"qty": 18
}
}
],
"chksum": "87b239cd9b39baa48b4564b5754009a131f542622ba018f37cd1fdb5"
}
]
}
{
"_id" : ObjectId("1"),
"user_id": 31,
"veggies": [
{
"type": "green_vegetables",
"desc": [
{
"udf_type": "green_vegetables",
"tot": 28560,
"itms": [
{
"num": 1,
"itm_det": {
"name": "spinach",
"qty": 18
}
}
],
"chksum": "d1583afab3a04f4b32589cfa64392765n78782ff60a0e0dc24b295868083"
}
]
},
{
"type": "vegetables",
"desc": [
{
"udf_type": "vegetables",
"tot": 2860,
"itms": [
{
"num": 1,
"itm_det": {
"name": "onion",
"qty": 1
}
}
],
"chksum": "e497c7b288e50e3be4c6bc676e4c849e4n5645n64a2d77748e185d7a1bce8c"
}
]
}
]
}
{
"_id" : ObjectId("2"),
"user_id": 31,
"vegetables": [
{
"veg_type": "green_vegetables",
"desc": [
{
"udf_veg_type": "green_vegetables",
"tot": 2352000,
"itms": [
{
"num": 1,
"itm_det": {
"name": "kale",
"qty": 18
}
}
],
"chksum": "87b239cd9b39baa48b4564b5754009a131f542622ba018f37cd1fdb5"
}
]
}
]
}
Assuming that the desc arrays always have just one item, try this:
db.collection.aggregate([
{
$match: {
"user_id": 31 // change this into the user_id variable
}
},
{
$group: {
_id: "$user_id",
veggies: {
$max: "$veggies"
},
vegetables: {
$max: "$vegetables"
}
}
},
{
$project: {
"user_id": "$_id",
"veggies": {
$concatArrays: [
{
$cond: [
"$veggies",
{
$map: {
input: "$veggies",
in: {
$arrayElemAt: [
"$$this.desc",
0
]
}
}
},
[]
]
},
{
$cond: [
"$vegetables",
{
$map: {
input: "$vegetables",
in: {
$arrayElemAt: [
"$$this.desc",
0
]
}
}
},
[]
]
}
]
}
}
}
])