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

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})
}

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 }
)

Query to update an array field by using another array field of same document in mongodb

Scenario
I've the following document from Chat collection with an array of messages and members in the chat.
And for each message, there will be status field which will store the delivered and read timestamp with respect to users.
{
"_id" : ObjectId("60679797b4365465745065b2"),
"members" : [
ObjectId("604e02033f4fc07b6b82771c"),
ObjectId("6056ef4630d7b103d8043abd"),
ObjectId("6031e3dce8934f11f8c9a79c")
],
"isGroup" : true,
"createdAt" : 1617401743720.0,
"updatedAt" : 1617436504453.0,
"messages" : [
{
"createdAt" : 1617401743719.0,
"updatedAt" : 1617401743719.0,
"_id" : ObjectId("60679797b4365465745065b3"),
"body" : "This is test message",
"senderId" : ObjectId("6031e3dce8934f11f8c9a79c"),
"status" : []
}
]
}
So, I want to insert the following data, into messages.status array, to know when the message is received/read by the member.
{
receiverId: <member of chat>
deliveredAt: <timestamp>
readAt: <timestamp>
}
Question
How to write a query to insert the above json for each member (except the sender) in the status array by using the data from existing field?
So that, after query, the document should look like this:
{
"_id" : ObjectId("60679797b4365465745065b2"),
"members" : [
ObjectId("604e02033f4fc07b6b82771c"),
ObjectId("6056ef4630d7b103d8043abd"),
ObjectId("6031e3dce8934f11f8c9a79c")
],
"isGroup" : true,
"createdAt" : 1617401743720.0,
"updatedAt" : 1617436504453.0,
"messages" : [
{
"createdAt" : 1617401743719.0,
"updatedAt" : 1617401743719.0,
"_id" : ObjectId("60679797b4365465745065b3"),
"body" : "This is test message",
"senderId" : ObjectId("6031e3dce8934f11f8c9a79c"),
"status" : [{
"receiverId": ObjectId("604e02033f4fc07b6b82771c")
"deliveredAt": <timestamp>
"readAt": <timestamp>
}, {
"receiverId": ObjectId("6056ef4630d7b103d8043abd")
"deliveredAt": <timestamp>
"readAt": <timestamp>
}]
}
]
}
Edit
I'm able to do this for static data.
Link: https://mongoplayground.net/p/LgVPfRoXL5p
For easy understanding: I've to map the members array and insert it into the status field of the messages
MongoDB Version: 4.0.5
You can use the $function operator to define custom functions to implement behavior not supported by the MongoDB Query Language. So along with updates-with-aggregate-pipeline and $function you can update messages.status array with only receiver's details as shown below:
NOTE: Works only with MongoDB version >= 4.4.
Try this:
let messageId = ObjectId("60679797b4365465745065b3");
db.chats.update(
{ "messages._id": messageId },
[
{
$set: {
"messages": {
$map: {
input: "$messages",
as: "message",
in: {
$cond: {
if: { $eq: ["$$message._id", messageId] },
then: {
$function: {
body: function (message, members) {
message.status = [];
for (let i = 0; i < members.length; i++) {
if (message.senderId.valueOf() != members[i].valueOf()) {
message.status.push({
receiverId: members[i],
deliveredAt: new Date().getTime(),
readAt: new Date().getTime()
})
}
}
return message;
},
args: ["$$message", "$members"],
lang: "js"
}
},
else: "$$message"
}
}
}
}
}
}
]
);
Output:
{
"_id" : ObjectId("60679797b4365465745065b2"),
"members" : [
ObjectId("604e02033f4fc07b6b82771c"),
ObjectId("6056ef4630d7b103d8043abd"),
ObjectId("6031e3dce8934f11f8c9a79c")
],
"isGroup" : true,
"createdAt" : 1617401743720,
"updatedAt" : 1617436504453,
"messages" : [
{
"_id" : ObjectId("60679797b4365465745065b3"),
"createdAt" : 1617401743719,
"updatedAt" : 1617401743719,
"body" : "This is test message",
"senderId" : ObjectId("6031e3dce8934f11f8c9a79c"),
"status" : [
{
"receiverId" : ObjectId("604e02033f4fc07b6b82771c"),
"deliveredAt" : 1617625735318,
"readAt" : 1617625735318
},
{
"receiverId" : ObjectId("6056ef4630d7b103d8043abd"),
"deliveredAt" : 1617625735318,
"readAt" : 1617625735318
}
]
},
{
"_id" : ObjectId("60679797b4365465745065b4"),
"createdAt" : 1617401743719,
"updatedAt" : 1617401743719,
"body" : "This is test message",
"senderId" : ObjectId("6031e3dce8934f11f8c9a79d"),
"status" : [ ]
}
]
}
Demo - https://mongoplayground.net/p/FoOvxXp6nji
https://docs.mongodb.com/manual/reference/operator/update/positional-filtered/
The filtered positional operator $[] identifies the array elements that match the arrayFilters conditions for an update operation, e.g.
db.collection.update({
"messages.senderId": "6031e3dce8934f11f8c9a79c" // query
},
{
"$push": {
"messages.$[m].status": [ // push into the matching element of arrayFilters
{
"receiverId": ObjectId("604e02033f4fc07b6b82771c")
},
{
"receiverId": ObjectId("6056ef4630d7b103d8043abd")
}
]
}
},
{
arrayFilters: [
{
"m.senderId": "6031e3dce8934f11f8c9a79c" // matches array element where senderId is 6031e3dce8934f11f8c9a79c
}
]
})
Note- add index to messages.senderId for performance

Update documents nested multiple levels in an array

I have this JSON structure in MongoDb and want to update changing a specific value of a particular item in a nested array. I would like to change the key targetAreaId from USER_MESSAGE to VISUAL_MESSAGE.
{
"_id" : "5cde9f482f2d5b924f492da2",
"scenario" : "SCENARIOX",
"mediaType" : "VISUAL",
"opCon" : "NORMAL",
"stepConfigs" : [
{
"stepType" : "STEPX",
"enabled" : true,
"configs" : [
{
"contentTemplateId" : "5cde9f472f2d5b924f492973",
"scope" : "STANDARD",
"key" : "CONTENT"
},
{
"priorityId" : "5cde9f472f2d5b924f49224f",
"scope" : "STANDARD",
"key" : "PRIORITY"
},
{
"targetAreaId" : "USER_MESSAGE",
"scope" : "STANDARD",
"key" : "TARGET_AREA"
}
],
"description" : "XPTO"
}
],
"scope" : "STANDARD" }
How can I do it in the same time?
EDIT
I am trying this way:
var cursor = db.getCollection('CollectionX').find({
"scenario": "SCENARIOX",
"stepConfigs.stepType": "STEPX",
"stepConfigs.configs.key": "TARGET_AREA"
});
if (cursor.hasNext()) {
var doc = cursor.next();
doc.stepConfigs.find(function(v,i) {
if (v.stepType == "STEPX") {
doc.stepConfigs[i].configs.find(function(w,j) {
if (w.key == "TARGET_AREA") {
var result = db.getCollection('CollectionX').update(
{ "_id" : doc._id },
{ "$set" : { doc.stepConfigs[i].configs[j].targetAreaId: "VISUAL_MESSAGE" }}
);
}
});
};
});
} else {
print("Step does not exist");
}
But the error below is occurring:
Error: Line 15: Unexpected token .
I don't think that's possible.
You can update the specific element you want using this query:
db.test_array.updateOne({}, {
$set: {
"stepConfigs.$[firstArray].configs.$[secondArray].targetAreaId": "VISUAL_MESSAGE"
}
}, {
arrayFilters: [{
"firstArray.stepType": "STEPX"
}, {
"secondArray.targetAreaId": "USER_MESSAGE"
}]
})
But pushing in the same array at the same time does render this (this was for the original model, but it's still the same problem, you can't $set and $push in the same array this way):
> db.test_array.updateOne({"step.type":"B"},{$push:{"step.$.configs":{"className":"something"}}, $set:{"step.$.configs.$[secondArray].targetAreaId":"TA-1"}},{arrayFilters:[{"secondArray.targetAreaId":"TA-2"}]})
2019-09-06T13:53:44.783+0200 E QUERY [js] WriteError: Updating the path 'step.$.configs.$[secondArray].targetAreaId' would create a conflict at 'step.$.configs' :

Mongo DB modify elements in nested 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.

Adding object to array in array in MongoDB

I have this data structure (classes with comments) and I want to add a 1-level deep reply. This means that I'd like to add another object on a "comentarios" element.
How can I achieve this with mongo?
This means: Match cursada (db) id, match clase (first array) id, match comment (second array) id, then add a new element there.
db.cursada.find({"_id": ObjectId("55444f56e5e154f7638b456a")}).pretty()
{
"_id" : ObjectId("55444f56e5e154f7638b456a"),
"clases" : [
{
"_id" : "554e7f2fe5e154797d8b4578",
"titulo" : "qewewqewq"
},
{
"_id" : "554e8be0e5e154dc698b4582",
"titulo" : "la mejor clase"
},
{
"_id" : "554eb90de5e154dd698b458b",
"comentarios" : [
{
"_id" : ObjectId("55a021afe5e154cf098b4567"),
"nombreUsuario" : "nombre",
"texto" : "432432423"
}
],
"titulo" : "Peeling - cosa"
},
{
"_id" : "554e91a0e5e154797d8b4587",
"titulo" : "fdsfdsa"
},
{
"_id" : "554f8f50e5e154dd698b458f",
"titulo" : "clase2"
},
{
"_id" : "554f99dae5e154797d8b45a7",
"titulo" : "con profesor"
},
{
"_id" : "554fa4a0e5e154797d8b45c4",
"titulo" : "profesor nombre nombre"
},
{
"_id" : "5557b37be5e154e07f8b4567",
"titulo" : "Dermatologia I"
},
{
"_id" : "5557c701e5e154066d8b456c",
"titulo" : "Acido hialuronico"
}
],
"curso" : "552fa5f1e5e1542e628b4567",
"fechaFin" : "2015-05-22T03:00:00.000Z",
"fechaIni" : "2015-05-08T03:00:00.000Z",
"titulo" : "cremotas"
}
Getting this result:
{
"_id" : ObjectId("55444f56e5e154f7638b456a"),
"clases" : [
{
"_id" : ObjectId("554eb90de5e154dd698b458b"),
"comentarios" : [
{
"_id" : ObjectId("55a021afe5e154cf098b4567"),
"nombreUsuario" : "nombre",
"texto" : "432432423",
----------------HERE
"replies": [
{ "_id": ....,
"user": ....,
"text":....,
"date":....
}]
----------------HERE
}
],
"titulo" : "Peeling - cosa"
},
]
}
One twisted example!
Luckily it presents no problem to MongoDB and its $elemMatch query operator:
Model.update({
_id: "55444f56e5e154f7638b456a",
classes: {
$elemMatch: {
_id: "554eb90de5e154dd698b458b",
}
}
}, {
$push: {
'classes.$.comentarios': {
nombreUsuario: 'New comment nombreUsuario',
texto: '111242515'
}
}
}, function(err, count) {
console.log('Updated ' + count + ' document');
});
What happens here?
First, we're specyfing specific course ("cursada") to update:
_id: "55444f56e5e154f7638b456a"
Then, using $elemMatch operator, we're restricting result of the query to contain only one class:
classes: {
$elemMatch: {
_id: "554eb90de5e154dd698b458b",
}
}
Now, having found specific class to update, we can finally add new comment to it. Here's our update query:
$push: {
'classes.$.comentarios': {
nombreUsuario: 'New comment nombreUsuario',
texto: '111242515'
}
}
$push operator tells MongoDB to add new comment to specified array of comments.
'classes.$.comentarios' specifies which nested comentarios array to update, using $ positional operator.
Finally, and this part should be self-explanatory: full object of new comment to add to the specified class.
It's also worth mentioning that if you're running into such deeply nested structure, perhaps it's worth thinking about spreading courses, classes, possibly even comments over separate MongoDB collections.
When nested documents are the way to go and when it's better to create separate collections can be actually tricky question to answer - here's nice presentation discussing this issue.

Resources