Add new column to multi nested array in mongo db - arrays

"ParentType": {
"Food": [
{
"Name": "Burger",
"FoodId": "5e3abe145c1bfb31b4e335de",
"Price": 0,
"Quantity": 1,
"SubCategory": 0
}
],
"Inventory": [
{
"Name": "Small Popcorn",
"InventoryId": "5e3a64245c1bfb31b4e335b7",
"Price": 0,
"Quantity": 1,
"SubCategory": 0
}
]
}
I need to add UOM as new column only for Inventory array.I have used aggregate as below but collection is not getting updated.Pls help me with adding this new Column in mongodb
db.Concession.aggregate([
{
$addFields: { ParentType.Inventory.UOM: "null" }
}
])

add UOM to all eliment in Inventory
db.collection.update(
{},
{ $set: { 'ParentType.Inventory.$[].UOM':''} }
)

Option 1: ( Update/$set 3.6+ )
db.collection.update({},
{
$set: {
"ParentType.Inventory.$[].UOM": null
}
},
{
multi: true
})
Explained:
Use update() operation with positional operator $[] ( mongoDB 3.6+ )
Use option multi to update all documents in collection matching the filter criteria
Playground
Option 2: ( Update/aggregation 4.2+)
db.collection.update({},
[
{
$addFields: {
"ParentType.Inventory": {
"$map": {
input: "$ParentType.Inventory",
as: "i",
in: {
"$mergeObjects": [
"$$i",
{
UOM: null
}
]
}
}
}
}
}
],
{
multi: true
})
Explained:
Use update with aggregation pipeline ( mongoDB 4.2+ )
In aggregation use the addFileds with mergeObjects so you add the new fields to the array element.
Use multi:true to affect all documents in collection matching the filter criteria
Playground 2

Related

adding data to nested document : mongodb

I want to add new record within 'music'. My document looks similar to
{
"username": "erkin",
"email": "erkin-07#hotmail.com",
"password": "b",
"playlists": [
{
"_id": 58,
"name": "asdsa",
"date": "09-01-15",
"musics": {
"song-one":{
"song-one": "INNA - Cola Song (feat. J Balvin)",
"duration": "3.00"
},
"song-two":{
"song-two": "blabla",
"duration": "3.00"
}
}
}
]
}
After navigating to "music" and then using $set to add/update multiple records at once. But new records is getting added in lexicographical manner(behaviour of $set).
I'm using query similar to (so in my case song-four is coming before song-three) :
db.getCollection('myCollection').update({username:"erkin"},{$set:{"playlists.musics.song-three":{...},"playlists.musics.song-four":{...}}})
Is there any way I can add new records to that location in a way my $set query is arranged ?
As playlists is an array:
Option 1: Update the first document in playlists.
Specify the index: 0 for the first document in playlists.
playlists.0.musics.song-three
db.collection.update({
username: "erkin"
},
{
$set: {
"playlists.0.musics.song-three": {
"song-three": "song-three"
},
"playlists.0.musics.song-four": {
"song-four": "song-four"
}
}
})
Sample Demo on Mongo Playground (Option 1)
Option 2: Update all documents in playlists.
With $[] all positional operator.
playlists.$[].musics.song-three
db.collection.update({
username: "erkin"
},
{
$set: {
"playlists.$[].musics.song-three": {
"song-three": "song-three"
},
"playlists.$[].musics.song-four": {
"song-four": "song-four"
}
}
})
Sample Demo on Mongo Playground (Option 2)
Option 3: Update specified document in playlists.
With $[<identifier>] filtered positional operator.
playlists.$[playlist].musics.song-three
db.collection.update({
username: "erkin"
},
{
$set: {
"playlists.$[playlist].musics.song-three": {
"song-three": "song-three"
},
"playlists.$[playlist].musics.song-four": {
"song-four": "song-four"
}
}
},
{
arrayFilters: [
{
"playlist._id": 58
}
]
})
Sample Demo on Mongo Playground (Option 3)

Parsing a difficult MongoDB field (with multi-level array)

Hello Experts,
I am trying to parse a MongoDB collection row, and after using $unwind,
one of the remaining fields looks like that:
[
{
"account_id": "1234",
"cities": {
"cityname1": {
"param1": 1,
"param2": 2
}
}
},
{
"account_id": "2345",
"cities": {
"cityname2": {
"param1": 3,
"param2": 3
}
}
},
{
"account_id": "3456",
"cities": {
"cityname3": {
"param1": 8,
"param2": 6
}
}
}
]
Now, I would like to continue parsing this field, so I can extract the fieldname/value for account_id, for param1 and for param2, hoping then to sum up the param1 and param2 values.
However, when I try to use a second $unwind, I receive those fields with "null" value.
How should I parse this field correctly?
$set change cities into array by using $objectToArray
$unwind unwind cities array
$group group by account_id and cityname then sum up param1 and param2
(you can only group by account_id or cityname, just remove one of them)
aggregate
db.collection.aggregate([
{
"$set": {
cities: {
"$objectToArray": "$cities"
}
}
},
{
"$unwind": "$cities"
},
{
"$group": {
"_id": {
account_id: "$account_id",
"cityname": "$cities.k"
},
"sumOfParam1": {
"$sum": "$cities.v.param1"
},
"sumOfParam2": {
"$sum": "$cities.v.param2"
}
}
}
])
mongoplayground

Sum Quantity in Mongo Subdocument Based on Filter

I have a "shipment" document in MongoDB that has the following basic structure:
shipment {
"id": "asdfasdfasdf",
"shipDate": "2021-04-02",
"packages": [
{
"id": "adfasdfasdfasdf",
"contents": [
{
"product": {
"id": "asdfasdfasdfasd"
},
"quantity": 10
}
]
}
]
}
Please note that "product" is stored as a DBRef.
I want to find the total quantity of a specific product (based on the product ID) that has been shipped since a given date. I believe this is the appropriate logic that should be followed:
Match shipments with "shipDate" greater than the given date.
Find entries where "contents" contains a product with an "id" matching the given product ID
Sum the "quantity" value for each matching entry
Return the sum
So far, this is what I've come up with for the Mongo query so far:
db.shipment.aggregate([
{$match: {"shipDate": {$gt: ISODate("2019-01-01")}}},
{$unwind: "$packages"},
{$unwind: "$packages.contents"},
{$unwind: "$packages.contents.product"},
{
$project: {
matchedProduct: {
$filter: {
input: "$packages.contents.products",
as: "products",
cond: {
"$eq": ["$products.id", ObjectId("5fb55eae3fb1bf783a4fa97f")]
}
}
}
}
}
])
The query works, but appears to just return all entries that meet the $match criteria with a "products" value of null.
I'm pretty new with Mongo queries, so it may be a simple solution. However, I've been unable to figure out just how to return the $sum of the "contents" quantity fields for a matching product ID.
Any help would be much appreciated, thank you.
Query Which Solved The Problem
db.shipment.aggregate([
{
$match: {
"shipDate": {$gte: ISODate("2019-01-01")},
"packages.contents.product.$id": ObjectId("5fb55eae3fb1bf783a4fa98e")
}
},
{ $unwind: "$packages" },
{ $unwind: "$packages.contents" },
{ $unwind: "$packages.contents.product" },
{
$match: {
"packages.contents.product.$id": ObjectId("5fb55eae3fb1bf783a4fa98e")
}
},
{
$group: {
"_id": null,
"total": {
"$sum": "$packages.contents.quantity"
}
}
}
])
Demo - https://mongoplayground.net/p/c3Ia9L47cJS
Use { $match: {"packages.contents.product.id": 1 } }, to filter records by product id.
After that group them back and find the total { $group: {"_id": null,"total": { "$sum": "$packages.contents.quantity" } } }
db.collection.aggregate([
{ $match: {"shipDate": "2021-04-02","packages.contents.product.id": 1 } },
{ $unwind: "$packages" },
{ $unwind: "$packages.contents" },
{ $match: { "packages.contents.product.id": 1 } },
{ $group: { "_id": null,"total": { "$sum": "$packages.contents.quantity" } } }
])
Adding extra check at top { $match: {"shipDate": "2021-04-02","packages.contents.product.id": 1 } } for product id will filter only documents with produce id we need so query will be faster.
Option-2
Demo - https://mongoplayground.net/p/eo521luylsG
db.collection.aggregate([
{ $match: { "shipDate": "2021-04-02", "packages.contents.product.id": 1 }},
{ $unwind: "$packages" },
{ $project: { contents: { $filter: { input: "$packages.contents", as: "contents", cond: {"$eq": [ "$$contents.product.id", 1] }}}}},
{ $unwind: "$contents" },
{ $group: { "_id": null, "total": { "$sum": "$contents.quantity" }}}
])

How can I update a multi level nested array in MongoDB?

How can I update a record in a document with multiple levels of array nesting?
My document structure is the following:
{
"_id": "5bfa09f0a0441f38d45dcc9c",
"nombre": "PROYECTO MAIN",
"area": "Sistemas",
"fecha": "27/01/2018",
"reuniones": [
{
"_id": "5bfa09f0a0441f38d45dcc99",
"objetivo": "Objetivo MODIFICADO",
"fecha": "25/10/2018",
"participantes": [
{
"nomina": 1,
"nombre": "MODIFICADO",
"rol": "rol",
"area": "area",
"firma": "url/imagen.jpg"
},
{
"nomina": 2,
"nombre": "nombre 2",
"rol": "rol",
"area": "area",
"firma": "url/imagen.jpg"
}
]
}
],
"_class": "proyecto"
}
Using the following query, returns me the document mentioned above.
db.proyectos.find({
_id:ObjectId("5bfa09f0a0441f38d45dcc9c"),
"reuniones._id":ObjectId("5bfa09f0a0441f38d45dcc99"),
"reuniones.participantes.nomina":2
})
I want to update the firma field of participant with nomina 2.
Since Mongo 3.6, you can update multi-nested arrays by combining the following operators:
$set (to update a specific field)
$[] (to match any item in an array)
$[<identifier>] (to match specific items in an array)
Example
Here's how you can update a specific proyectos document that has a reuniones array that has a participantes array that has an object with the field nomina equal to 2:
// update a specific proyectos document
// that has a field "reuniones" which is an array
// in which each item is an object with a field "participantes" that is an array
// in which each item is an object that has a field "nomina" equal to 2
db.proyectos.update({
_id: ObjectId("5bfa09f0a0441f38d45dcc9c"),
}, {
$set: {
"reuniones.$[].participantes.$[j].firma": <your update>
},
}, {
arrayFilters: [
{
"j.nomina": 2
}
]
})
If you wanted to limit your query to a specific reunion, you can do:
db.proyectos.update({
_id: ObjectId("5bfa09f0a0441f38d45dcc9c"),
}, {
$set: {
"reuniones.$[i].participantes.$[j].firma": <your update>
},
}, {
arrayFilters: [
{
"i._id": ObjectId("5bfa09f0a0441f38d45dcc99")
},
{
"j.nomina": 2
}
]
})
To update all proyectos satisfying the above condition, just omit the _id query:
// update all proyectos
// that have a field "reuniones" which is an array
// in which each item is an object with a field "participantes" that is an array
// in which each item is an object that has a field "nomina" equal to 2
db.proyectos.update({}, {
$set: {
"reuniones.$[].participantes.$[j].firma": <your update>
},
}, {
arrayFilters: [
{
"j.nomina": 2
}
]
})

Is there a way to push a sub document into mongodb array only if the condition is satisfied?

i am pushing sub documents into an array.
Basically the subdocs are something like this:
{
id:"someId",
date:Date
}
i want to add new subdoc only if the theres no other subdoc with matching id.I found $addToSet modifier on mongodb documentation but i am not sure if it can just check a key inside a sub document rather than comparin entire array item to another one.
Is there any way to do this in single query or anyway should i go with 2 queries?
You can do this in a single atomic update operation. Suppose your collection has the following documents:
Mongo shell
db.test.insert([
{
"_id": 1,
"items": [
{ "id": 1, "date": new Date("2016-05-20") },
{ "id": 2, "date": new Date("2016-05-21") }
]
},
{
"_id": 2,
"items": [
{ "date": new Date("2016-05-22") }
]
}
])
Then the following update statement in mongo shell will update the document that has the items subdoc which does not have the id key by adding the new sub document { "id": 3, "date": new Date() } to the items array:
db.test.update(
{ "items.id": { "$exists": false } },
{
"$push": {
"items": { "id": 3, "date": new Date() }
}
}
)
Querying the test collection
> db.test.find().pretty()
{
"_id" : 1,
"items" : [
{
"id" : 1,
"date" : ISODate("2016-05-20T00:00:00Z")
},
{
"id" : 2,
"date" : ISODate("2016-05-21T00:00:00Z")
}
]
}
{
"_id" : 2,
"items" : [
{
"date" : ISODate("2016-05-22T00:00:00Z")
},
{
"id" : 3,
"date" : ISODate("2016-05-23T09:33:15.913Z")
}
]
}
To add a new subdoc only if the there's no other subdoc with a matching id, you only need one update operation where you can specify the matching condition
in the update query document.
The following update operation will add a subdocument with id = 4 to the array since there is no matching array element in the existing document:
db.test.update(
{ "items.id": { "$ne": 4 } },
{
"$push": {
"items": { "id": 4, "date": new Date() }
}
}
)
For an exclusive update where you just want to add the subdocument if there is an id field AND the given id does not match, then include the condition together with the equality check:
db.test.update(
{
"items.id": {
"$exists": true,
"$ne": 4
}
},
{
"$push": {
"items": { "id": 4, "date": new Date() }
}
}
)

Resources