Mongo DB modify elements in nested arrays - arrays

I am wondering how can I change the element in such array in Mongo DB. Say the collections is as follow:
{
"_id" : ObjectId("xxxxxxxxxxxxxxxx"),
"user_info" : {
"user_name" : "joe",
},
"portfolio" : [
{
"market_symbol" : "NASDAQ:GOOGL",
"details" : [
{
"amount" : 100,
"purchased_price" : 810.25,
"date_of_purchase" : "20170210 212426"
},
{
"amount" : 100,
"purchased_price" : 810.25,
"date_of_purchase" : "20170210 212426"
},
{
"amount" : 200,
"purchased_price" : 900.0,
"date_of_purchase" : "20170210 212426"
}
]
},
{
"market_symbol" : "NYSE:BABA",
"details" : [
{
"amount" : 200,
"purchased_price" : 80.0,
"date_of_purchase" : "20170210 212426"
},
{
"amount" : 333,
"purchased_price" : 86.11,
"date_of_purchase" : "20170210 212426"
}
]
}
]
I am trying to modify the value of
amount in "portfolio:market_symbol":"NASDAQ:GOOGL", where the purchased_price is 900 and I want to set the 200 to 300.
so the segment should look like this after the modification:
"portfolio" : [
{
"market_symbol" : "NASDAQ:GOOGL",
"details" : [
{
"amount" : 100,
"purchased_price" : 810.25,
"date_of_purchase" : "20170210 212426"
},
{
"amount" : 100,
"purchased_price" : 810.25,
"date_of_purchase" : "20170210 212426"
},
{
"amount" : 300,
"purchased_price" : 900.0,
"date_of_purchase" : "20170210 212426"
}
]
},
I tried to use $elemMatch in mongo shell
db.Users.update({"user_info.user_name":"joe","portfolio":{$elemMatch:{"market_symbol":{$eq:"NASDAQ:GOOGL"},"details.purchased_price":{$eq:900}}}},{$set:{"portfolio.$.details.0.amount":300}})
It seems that the query always return the entire section of "portfolio:market_symbol":"NASDAQ:GOOGL", because the 0 in {$set:{"portfolio.$.details.0.amount":300}} modifies the first array in details, whose purchased_price is 810.25, rather than what I expect the $elemMatch to give me (the 3rd element from the array, whose purchased_price is 900).
Is there a way that I can modify this nested nested array, rather than pulling the entire thing off, modify the data in my program, then write the entire thing back?
Please help, thanks.

As far as I know,the positional operator only supports one level deep and only the first matching element.So that means updating all documents in array is not possible now.
There is a MongoDB JIRA ticket: https://jira.mongodb.org/browse/SERVER-831
But you can update specific document manually.
db.test.find(
{ "user_info.user_name":"joe", "portfolio.details.purchased_price" : 900.0 }).forEach(function(doc) {
doc.portfolio.forEach(function(item) {
item.details.forEach(function (amt){
if (amt.purchased_price == 900.0)
amt.amount=300
});
});
db.test.update( { "user_info.user_name":"joe", "portfolio.details.purchased_price" : 900.0 }, { "$set": { "portfolio": doc.portfolio } });
});
It works for me.Hope this helps.

Related

How update my objects in a document's array in mongoDB

i have the following collection, I have one question about:
{
"_id" : ObjectId("123456789"),
"user_id" : 123456,
"total" : 100,
"items" : [
{
"item_name" : "my_item_one",
"price" : 30
},
{
"item_name" : "my_item_two",
"price" : 30
},
{
"item_name" : "my_item_three",
"price" : 30
}
]
}
how i can update all price concurrently, (price=50 in all objects)
thanks for help
You can use positional all operator to update all elements in the array
db.collectionName.update(
{ },
{ $set: { "items.$[].price" : 50 } },
{ multi: true }
)

Navigate thought a lots of array and update value of object with mongo query

Hey guys I'm trying to update a value that is in an array in MongoDB, am trying to use the mongo queries but is not working, am following the next documentation from Mongo doc
this one is the array:
{
"_id" : "605e3d9b9ef219de662113d0",
"distribution" : [
{
"floor" : 1,
"rooms" : [
{
"number" : 301,
"beds" : [
{
"number" : 818,
"status" : "Vacante Sucia"
},
{
"number" : 819,
"status" : "Vacante Sucia"
}
],
"gender" : "M"
},
{
"number" : 302,
"beds" : [
{
"number" : 820,
"status" : "Vacante Sucia"
},
{
"number" : 821,
"status" : "Vacante Sucia"
}
],
"gender" : "M"
},
{
"number" : 303,
"beds" : [
{
"number" : 822,
"status" : "Vacante Sucia"
},
{
"number" : 823,
"status" : "Vacante Sucia"
}
],
"gender" : "M"
}
]
}
],
"name" : "Meteorologia",
"code" : "METEO"
}
this one is the query that is using in mongoDB to update the status from the bed 801, room 301, floor 1:
in the arrayFilters i specified the index 0 to get the first element of the arrays
db.getCollection('establishments_copy').findAndModify({query: { code: "METEO"}, update: { $set: { "distribution.$[i].rooms.$[i].beds.$[i].status": "TEST"}}, arrayFilters: [{"i.rooms": 0, "i.beds": 0, "i.status": 0}]})
they are returning me the collection but without changes, is possible to navigate validating not for the index just with the values.
for example using the next query:
db.getCollection('establishments_copy').findAndModify({query: { code: 'METEO', distribution: { $elemMatch: { floor: 1, 'rooms.number': 301, 'rooms.beds.number': 818}}}, update: { $set: { '...': 'CHANGED'}}})
thanks!
You just need to create separate condition as per sub document's field name,
f for floor field in distribution array
r for number field in rooms array
b for number field in beds array
db.getCollection('establishments_copy').findAndModify({
query: { code: "METEO"},
update: {
$set: {
"distribution.$[f].rooms.$[r].beds.$[b].status": "TEST"
}
},
arrayFilters: [
{ "f.floor": 1 },
{ "r.number": 301 },
{ "b.number": 818 }
]
})
Playground

Mongo Aggregation Get Difference between values of array objects

Good day, I am having difficulty using aggregation in mongodb to get the date difference between two values in an array, my document structure looks as follows:
{
"_id" : ObjectId("5c591420a890362dec70fcf4"),
"tracetag" : "T00005",
"amounts" : [
{
"_id" : ObjectId("5c5915bea890362dec71015a"),
"startdate" : ISODate("2019-02-15T04:49:02.000+0000"),
"enddate" : ISODate("2019-02-15T04:50:05.000+0000"),
"amount" : 18.975946800811833,
},
{
"_id" : ObjectId("5c5915bea890362dec71015b"),
"startdate" : ISODate("2019-02-16T04:49:02.000+0000"),
"enddate" : ISODate("2019-02-16T04:51:52.000+0000"),
"amount" : 3.5755730555836203,
},
{
"_id" : ObjectId("5c5915bea890362dec71015c"),
"startdate" : ISODate("2019-02-17T04:49:02.000+0000"),
"enddate" : ISODate("2019-02-17T04:50:04.000+0000"),
"amount" : 2.3937573313380383,
}
],
}
the number of "amounts" entries are not fixed, so it could be 1 or 50 etc.
for each document, I would like to get the difference between the 'enddate' and 'startdate' (aka the duration) and I would like to sum the duration (lets call this totalduration). I can do the total 'amount' without issues, but getting the duration gives me error ""cant $subtract aarray from a array", my aggregation looks like this:
{
"$project" : {
"tracetag" : 1.0,
"totalamount" : {
"$sum" : "$amounts.amount"
},
"totalduration" : {
"$sum" : {
"$subtract" : [
"$amounts.enddate",
"$amounts.startdate"
]
}
}
}
},
OK so I managed to answer my own question: the 'amounts' need to be split using the $unwind operator, then a field can be added to each record (duration), in my case I wanted the duration in minutes. Afterwards I grouped everything again
{
"$unwind" : { "path" : "$amounts" }
},
{
"$addFields" : {
"duration" : { "$divide" : [ { "$subtract" : [
"$enddate", "$startdate" ]}, 60000.0 ] }
}
},

update nested array element value in node js mongoDB [duplicate]

This question already has answers here:
How to Update Multiple Array Elements in mongodb
(16 answers)
Closed 6 years ago.
Hi i am new in nodejs i need to update a value in nested array using _id of document my database document is look like this..
"complaints" : [
{
"complaint" : "head light is not working",
"complaintid" : ObjectId("57205219a56d2b8c0f9274a4"),
"_id" : ObjectId("57454c9249218eb40c1c0d1f"),
"labour" : 350,
"partCost" : 0,
"part" : [
{
"id" : ObjectId("56f12eaab915bd9800272ed7"),
"estimate" : 450,
"partname" : "clutch",
"_id" : ObjectId("57454cef49218eb40c1c0d25"),
"quantity" : 0,
"qrcodes" : []
},
{
"id" : ObjectId("56f12eaab915bd9800272ed7"),
"estimate" : 450,
"partname" : "rear tyre",
"_id" : ObjectId("57454cef49218eb40c1c0d24"),
"quantity" : 0,
"qrcodes" : []
}
],
"acceptance" : true,
"inspection" : false,
"color" : "#8929A9",
"status" : "APPROVED",
"estimate" : 1200,
"solution" : "HEAD LIGHT CHANGE",
"problems" : "HEAD LIGHT IS NOT WORKING"
},
i need to update quantity value of part array exist inside the part array using _id of part array
i am trying this but its not working what should i do for update this value...
var partdata = req.payload.parts;
for(var k = 0; k< partdata.length ; k++ ){
CPS.update({
'complaints.part._id' : partdata[k].partid
}, {
"$inc" : {
'complaints.part.$.quantity' : partdata[k].quantity
}
}).exec
(function(err,temp) {
if(err){
res(err).code(500);
}else{
console.log(temp);
}
});
}
MongoDB doesn't support matching into more than one level of an array.
Consider altering your document model so each document represents an
operation, with information common to a set of operations duplicated
in the operation documents.
Following is not the solution for your case.
But in-case you know the index then you could do something like this:
Assume a sample document like:
{
"_id" : ObjectId("57454c9249218eb40c1c0d1f"),
"part" : [{ "quantity" : 111 }, { "quantity" : 222 }]
}
Then this query should work.
db.test.update({ "_id" : ObjectId("57454c9249218eb40c1c0d1f") }, { "$set" : { "part.1.quantity" : 999 } })
Document will get modified as follows :
{
"_id" : ObjectId("57454c9249218eb40c1c0d1f"),
"array" : [{ "quantity" : 222 }, { "quantity" : 999 }]
}
Update: You can try following way of doing the update. But its not recommended way of doing probably you need to restructure your schema.
db.test.aggregate([
{ "$unwind": "$complaints" },
{ "$unwind": "$complaints.part" },
{ "$project":
{
_id: "$complaints.part._id",
partqty: "$complaints.part.quantity"
}
},
]);
This should return as follows:
{
"_id" : ObjectId("57454cef49218eb40c1c0d25"),
"partqty" : 111
}
{
"_id" : ObjectId("57454cef49218eb40c1c0d24"),
"partqty" : 222
}
Now you can use this information to update, e.g
var cur = db.test.aggregate([
{ "$unwind": "$complaints" },
{ "$unwind": "$complaints.part" },
{ "$project":
{
_id: "$complaints.part._id",
partqty: "$complaints.part.quantity"
}
},
]);
while (cur.hasNext()) {
var doc = cur.next();
//Note the index should be know again :|
db.test.update({ "complaints.part._id": ObjectId("57454cef49218eb40c1c0d25") },
{ "$set": { "complaints.$.part.1.quantity": 55 }},
{ "multi": true})
}

Conditionally remove Subdocument nested inside array of document MongoDB

I have a collection with document like this:
{
"_id" : "ABC",
"Name" : "Rajesh",
"createstmp" : ISODate("2015-06-22T17:09:16.705Z"),
"updstmp" : ISODate("2015-06-22T19:31:53.527Z"),
"AvgValue" : "65",
"PreValues" : [
{
"Date" : 20150709,
"Rate" : [
{
"Time" : 1566,
"value" : 60
},
{
"Time" : 1500,
"value" : 400
},
{
"Time" : 1400,
"value" : 100
},
{
"Time" : 1500,
"value" : 103
}
]
}
]
}
I want to remove the duplicate doc for a particular Date value
eg If Time value is 1500, I need to pull the document and push it the new value for (Value) in single bulk operation.
Here is my query
bulk.find({ "_id":"ABC" })
.update(
{
"_id": "ABC",
"PreValues": { "Date": 20150709 }
},
{
$pu‌​ll: { "PreValues": { "Rate": { "Time": 1000 } } }
}
);
bulk.find({ "_id":"ABC" })
.update(
{ "_id": "ABC","PreValues": { "Date": 20150709 }},
{ $pu‌​sh : {
"PreValues": { "Rate": { "Time": 1000,"Rating": 100 }}
}}
);
bulk.execute();
It's not a great idea to have nested arrays since the only thing you will ever be able to do atomically is $push or $pull. See the positional $ operator for details on why "nested arrays" are not good here, but basically you can only ever match the position of the "outer" array element.
And that is basically what you are missing here, and of course the proper "dot notation" for accessing the elements:
var bulk = db.ABA.initializeOrderedBulkOp();
bulk.find({ "_id": "ABC", "PreValues.Date": 20150709 })
.updateOne({ "$pull": { "PreValues.$.Rate": { "Time": 1500 } } })
bulk.find({ "_id": "ABC", "PreValues.Date": 20150709 })
.updateOne({ "$push": { "PreValues.$.Rate": { "Time": 1500, "Rating": 100 } } })
bulk.execute();
Which alters the document like so:
{
"_id" : "ABC",
"Name" : "Rajesh",
"createstmp" : ISODate("2015-06-22T17:09:16.705Z"),
"updstmp" : ISODate("2015-06-22T19:31:53.527Z"),
"AvgValue" : "65",
"PreValues" : [
{
"Date" : 20150709,
"Rate" : [
{
"Time" : 1566,
"value" : 60
},
{
"Time" : 1400,
"value" : 100
},
{
"Time" : 1500,
"Rating" : 100
}
]
}
]
}
That is the correct syntax for both statements there and sends both requests to the server at the same time with a single response.
Note that you need to inclide in the .find() query a field from the outer array to match. This is so the positional $ operator is populated with the matched index of that element and the operator knows which array element to act upon.

Resources