Updating array of sub sub document with matching element - mongodb

Below is my schema:
{
"_id":ObjectId("5c49c783de72ec2ec47b95d1"),
"placement":[
{
"offer":[
{
"sent_by":"John",
"comment":""
},
{
"sent_by":"Mary",
"comment":""
}
]
}
]
}
I want to update placement.offer.comment where placement.offer.sent_by is Mary but it always updates the first record. I don't want to provide a hard coded number like placement.0.offer.1.sent_by.
This should be the resulting document:
{
"_id":ObjectId("5c49c783de72ec2ec47b95d1"),
"placement":[
{
"offer":[
{
"sent_by":"John",
"comment":""
},
{
"sent_by":"Mary",
"comment":"Some comment updated"
}
]
}
]
}

You would need to use array filters to achieve that:
db.collection.update(
{ /* add additional query filters here */ },
{ $set: { "placement.$[].offer.$[o].comment": "Some updated comment" } },
{ arrayFilters: [ { "o.sent_by": "Mary" } ] }
)

Related

mongodb: updating an object inside array using forEach

I'm having a collection called vehicles whose structure is given below.
[
{
"vehicleType":"car",
"parts":[
{
"partName":"engine",
"buyingPrice":145.00,
"sellingPrice":200.00
},
{
"partName":"brake",
"buyingPrice":50.00,
"sellingPrice":70.00
},
{
"partName":"wheel",
"buyingPrice":70.00,
"sellingPrice":75.00
}
]
},
{
"vehicleType":"bike",
"parts":[
{
"partName":"engine",
"buyingPrice":1450.00,
"sellingPrice":2000.00
},
{
"partName":"brake",
"buyingPrice":507.00,
"sellingPrice":170.00
},
{
"partName":"wheel",
"buyingPrice":70.00,
"sellingPrice":75.00
}
]
},
{
"vehicleType":"car",
"parts":[
{
"partName":"engine",
"buyingPrice":1450.00,
"sellingPrice":2000.00
},
{
"partName":"brake",
"buyingPrice":50.00,
"sellingPrice":170.00
},
{
"partName":"wheel",
"buyingPrice":700.00,
"sellingPrice":750.00
}
]
}
]
I want to update the sellingPrice as 500.00 if the partName is brake and sellingPrice is <= 170.00. I excuted the below query, but it is updating the value only for the 1st object.
db.getCollection("vehicles").find({})
.forEach(function(v){
v.parts.forEach(function(p){
if(p.partName=="brake" && p.sellingPrice<=170.00){
p.sellingPrice=500.00;
print(v);
}
})
})
Below is the output of above query
[
{
"vehicleType":"bike",
"parts":[
{
"partName":"engine",
"buyingPrice":1450.00,
"sellingPrice":2000.00
},
{
"partName":"brake",
"buyingPrice":507.00,
"sellingPrice":500.00
},
{
"partName":"wheel",
"buyingPrice":70.00,
"sellingPrice":75.00
}
]
},
{
"vehicleType":"car",
"parts":[
{
"partName":"engine",
"buyingPrice":1450.00,
"sellingPrice":2000.00
},
{
"partName":"brake",
"buyingPrice":50.00,
"sellingPrice":170.00
},
{
"partName":"wheel",
"buyingPrice":700.00,
"sellingPrice":750.00
}
]
}
]
Don't know what went wrong. Any help is appreciated. Thanks in advance.
I think that the easiest way to do that is with the following query using the positional operator $
db.getCollection('vehicles').updateMany({
"parts.partName": "brake",
"parts.sellingPrice": {
$lte: 170.00
}
},
{
"$set": {
"parts.$.sellingPrice": 500
}
})
In case you can use mongodb cursor methods like forEach try something like this.

Sort collection in mongodb based on property in Array

{
'name':'abc',
'records': {
'entries':[
{
'date_created':'2020-01-01T00:00:00',
'some_filed':'value'
},
{
'date_created':'2019-02-03T05:00:00',
'some_filed':'value'
}
]
}
},
{
'name':'xyz',
'records': {
'entries':[
{
'date_created':'2022-03-07T08:00:00',
'some_filed':'value'
},
{
'date_created':'2022-02-08T10:987:00',
'some_filed':'value'
}
]
}
}
I have above collection format. I want to sort this based on date_created from first record in the array , i.e I want to perform sorting based on records.entries[0].date_created.
Also I need to project the name and records.entries[0].date_created fields.
I tried below queries
db['sample_collection'].find({}, {'records.entries.0.date_created':1, 'name':1})
db.collection.aggregate([
{
$set: { firstObject: { $first: "$records.entries.date_created" } }
},
{
$sort: { firstObject: -1 }
}
])
mongoplayground

Conditional check inside nested array in MongoDb

After a few pipeline stages, I came up with following sample result which is one document. If the videos.views.userId contains 10, I need to indicate videos.isWatched = true else false. We can use $unwind, check the condition, then group it.
This is sample output, The original document contains a lot of field, so I just like to do with less code unless I need to unwind Is there any way to do without unwind("videos")?
[
{
"_id":"someId",
"videos":[
{
"_id":"1",
"name":"A",
"views":[
{
"userId":10,
"group":"X"
}
]
},
{
"_id":"2",
"name":"B",
"views":[
{
"userId":20,
"group":"Y"
}
]
}
],
"Assessment":[
{
"_id":"22",
"title": "Hello world"
}
]
}
]
Expected result
[
{
"_id":"someId",
"videos":[
{
_id:"1",
name:"A",
views:[
{
userId:10,
group:"X"
}
],
"isWatched":true
},
{
_id:"2",
name:"B",
views:[
{
userId:20,
group:"Y"
}
],
"isWatched":false
}
],
"Assessment":[
{
_id:"22",
title: "Hello world"
}
]
}
]
You can use $map along with $mergeObjects to add a new field to an existing array. $anyElementTrue can be used to determine whether there's any userId equal to 10:
db.collection.aggregate([
{
$addFields: {
videos: {
$map: {
input: "$videos",
in: {
$mergeObjects: [
"$$this",
{
isWatched: {
$anyElementTrue: {
$map: { input: "$$this.views", in: { $eq: [ "$$this.userId", 10 ] } }
}
}
}
]
}
}
}
}
}
])
Mongo Playground

Sum Embedded Document Fields in Mongo Aggregate Query

Data looks like:
{
"_id":5345345345,
"city": "Detroit",
"people":[
{
"name":"Larry",
"value":1200
},
{
"name":"Steve",
"value":1100
},
{
"name":"Larry",
"value":1000
},
{
"name":"Joe",
"value":800
},
{
"name":"Larry",
"value":500
},
{
"name":"Joe",
"value":700
}
]
}
I have this in the middle of an aggregate query. What can I do from here to sum the value field and have unique entry for each person in "people"? i.e.
{
"_id":5345345345,
"city": "Detroit",
"people":[
{
"name":"Larry",
"value":2700
},
{
"name":"Steve",
"value":1100
},
{
"name":"Joe",
"value":1500
}
]
}
Also, cannot have query that returns empty document in case the "people" array is empty, e.g., cannot do:
{$unwind:{
path:people
}
},
{$group :
{
_id:"$people.name",
total_value:{"$sum":"people.value"}
}
}
which would work fine, if it didn't return an empty document when people array is empty.
Need to use $addToSet and $sum somehow? How to do this keeping value associated with name?
Use
{
$unwind: {
path : "$people",
preserveNullAndEmptyArrays : true // this line!
}
}

Mongodb: How to retrieve element from array?

Consider this mongodb document:
{
"_id:"0,
"firstname":"Tom",
"profiles" : [
{
"profile_name": "tom",
"reward_programs:[
{
'program_name':'American',
'username':'tomdoe',
},
{
'program_name':'Delta',
'username':'tomdoe',
}
]
"settings": {
'auto_update': "False"
}
},
{
"profile_name": "harry",
"reward_programs:[
{
'program_name':'American',
'username':'car',
'account':'train',
},
{
'program_name':'Delta',
'username':'harrydoe',
}
]
"settings": {
'auto_update': "False"
}
}
]
}
How would I retrieve just the 'settings' dictionary for a particular profile name? Let's use 'harry' in the example. I have tried:
result = users.find_one({'_id': request._id, 'profiles.profile_name': 'harry'}, {'_id': 0, 'profiles.$.settings': 1})
But this retrieves the entire dictionary of profile_name:'harry'
{
'profiles':
{
"profile_name": "harry",
"reward_programs:[
{
'program_name':'American',
'username':'car',
'account':'train',
},
{
'program_name':'Delta',
'username':'harrydoe',
}
]
"settings": {
'auto_update': "False"
}
}
}
I would prefer getting the result as
{
"profiles" : [
{
"settings": {
'auto_update': "False"
}
}
]
}
And if simple enough I would even prefer:
{
"settings": {
'auto_update': "False"
}
}
I obviously have my projection messed up but I don't know how to fix it. Suggestions?
You need to use aggregation framework for this. Something like this will give what you want -
result = db.users.aggregate( [
{ $unwind : "$profiles"},
{ $match : { "profiles.profile_name" : "harry"}},
{ $project : { settings : "$profiles.settings" } }
] )
If you don't want '_id' field then you can hide it like this -
result = db.users.aggregate( [
{ $unwind : "$profiles"},
{ $match : { "profiles.profile_name" : "harry"}},
{ $project : { '_id' : 0, settings : "$profiles.settings" } }
] )
The result variable will have -
{ "settings" : { "auto_update" : "False" } }
I guess mongodb has added some functionality since the question was answered. Just adding an answer for being current. In the projection you could just give the key from the embedded document and get the required embedded document alone.
result = users.find_one({'_id': request._id, 'profiles.profile_name': 'harry'},{'_id': 0,'settings': 1})