What is the best way to update the array element inside the array in MongoDB? For example, the data looks like this:
{
"_id" : ObjectId("6201396b866ffbf1b84fb8f9"),
"title" : "ironman",
"comments" : [
{
"text" : "nihao",
"replies" : [
{
"text" : "hi"
},
{
"text" : "bonjour"
},
{
"text" : "push replies!!!"
}
]
},
{
"text" : "what??",
"replies" : [
{
"text" : "the"
},
{
"text" : "hey"
}
]
},
{
"text" : "push comments!!!"
}
]
}
I want to change
"comments.replies.text: 'hi'"
to
"comments.replies.text: 'hello'"
What would be the best way to write a query if you want to update the elements inside replies?
You need $[<identifier>] filtered positional operator and arrayFilters to update nested document(s) in the array.
db.collection.update({
title: "ironman"
},
{
$set: {
"comments.$[comment].replies.$[reply].text": "hello"
}
},
{
arrayFilters: [
{
"comment.replies": {
$exists: true
}
},
{
"reply.text": "hi"
}
]
})
Sample Demo on Mongo Playground
Related
How can i update values in array inside an object in an array.
{
"_id" : ObjectId("63c7ca9584535c160ee4aaed"),
"status" : "REJECTED",
"steps" : [
{
"_id" : ObjectId("63c7ca9884535c160ee4ab03"),
"status" : "REJECTED",
"stepUsers" : [
{
"_id" : ObjectId("63c7ca9884535c160ee4ab04"),
"status" : "REJECTED",
}
]
},
]
}
I tried to update using arrayFilters but that didn't work. Mongo throw an error MongoServerError: Found multiple array filters with the same top-level field name steps
Collection.updateOne({
_id: id
}, {
$set: {
"steps.$[steps].stepUsers.$[stepUsers].status": 'PENDING',
}
}, {
arrayFilters: [
{ "steps._id": step._id },
{ "steps.stepUsers._id": stepUser._id }
]
})
I need to update steps.stepUsers.status in the collection.
Try to change the arrayFilters: "steps.stepUsers._id" -> "stepUsers._id"
since arrayFilters is referencing the string inside the [], not the path to it.
Collection.updateOne({
_id: id
},
{
$set: {
"steps.$[steps].stepUsers.$[stepUsers].status": "PENDING",
}
},
{
arrayFilters: [
{
"steps._id": step._id
},
{
"stepUsers._id": stepUser._id
}
]
})
See how it works on the playground example
I have a sample Post collection
[{
"_id" : ObjectId("5da832caeb173112348e509b"),
"body" : "Lorem Ipsu.",
"comments" : [
{
"comment" : "my sample comment",
"_id" : ObjectId("5db06e11d0987d0aa2cd5593"),
"replies" : [
{
"likes" : [
"5d999578aeb073247de4bd6e",
"5da85558886aee13e4e7f044"
],
"_id" : ObjectId("5db6a88f7c6cfb0d0c2b689b"),
"reply" : "my reply to this comment",
"user" : "5da85558886aee13e4e7f044"
}
],
"likes" : [
"5da85558886aee13e4e7f044",
]
},
],
}]
To add or remove element into comments.likes I can do like this,$push or $pull, it works with this below operation.
Post.updateOne(
{_id: req.body.id_post, "comments._id": req.body.id_comment},
{ $push: {"comments.$.likes": req.user._id}});
But if I want to push into replies.likes, could anyone please help to guide me?
I have tried something like this, but replies.likes is not updated.
db.posts.updateOne(
{ "comments.replies._id": Object("5db6a88f7c6cfb0d0c2b689b") },
{ "$push": { "comments.0.replies.$.likes": Object("5db6a88f7c6cfb0d0c2b689a") } },
function(err,numAffected) {
// something with the result in here
}
);
you can use array filter :
db.getCollection("test").updateOne(
{
"comments.replies": {
"$elemMatch": {
"_id": Object("5db6a88f7c6cfb0d0c2b689b")
}
}
},
{ "$set": { "comments.$[outer].reply.$[inner].likes": Object("5db6a88f7c6cfb0d0c2b689a") } },
{
"arrayFilters": [
{ "outer._id": ObjectId("5cd26a886458720f7a66a413") },
{ "inner._id": ObjectId("5cd26a886458720f7a66a415") }
]
}
)
don't mind ids
how to get data in mongoose where last element in array?
I have data looks like this:
[
{
"_id" : ObjectId("5b56eb3deb869312d85a8e69"),
"transactionStatus" : [
{
"status" : "pending",
"createdAt" : ISODate("2018-07-24T09:02:53.347Z")
},
{
"status" : "process",
"createdAt" : ISODate("2018-07-24T09:02:53.347Z")
}
]
},
{
"_id" : ObjectId("5b56eb3deb869312d8589765"),
"transactionStatus" : [
{
"status" : "pending",
"createdAt" : ISODate("2018-07-24T09:02:53.347Z")
},
{
"status" : "process",
"createdAt" : ISODate("2018-07-24T09:03:30.347Z")
},
{
"status" : "done",
"createdAt" : ISODate("2018-07-24T09:04:22.347Z")
}
]
}
]
And, I want to get data above where last object transactionStatus.status = process, so the result should be:
{
"_id" : ObjectId("5b56eb3deb869312d85a8e69"),
"transactionStatus" : [
{
"status" : "pending",
"createdAt" : ISODate("2018-07-24T09:02:53.347Z")
},
{
"status" : "process",
"createdAt" : ISODate("2018-07-24T09:02:53.347Z")
}
]
}
how to do that with mongoose?
You can use $expr (MongoDB 3.6+) inside of match. Using $let and $arrayElemAt passing -1 as second argument you can get the last element as a temporary variable and then you can compare the values:
db.col.aggregate([
{
$match: {
$expr: {
$let: {
vars: { last: { $arrayElemAt: [ "$transactionStatus", -1 ] } },
in: { $eq: [ "$$last.status", "process" ] }
}
}
}
}
])
The same result can be achieved for lower versions of MongoDB using $addFields and $match. You can add $project then to remove that temporary field:
db.col.aggregate([
{
$addFields: {
last: { $arrayElemAt: [ "$transactionStatus", -1 ] }
}
},
{
$match: { "last.status": "process" }
},
{
$project: { last: 0 }
}
])
//Always update new status at Position 0 using $position operator
db.update({
"_id": ObjectId("5b56eb3deb869312d85a8e69")
},
{
"$push": {
"transactionStatus": {
"$each": [
{
"status": "process",
"createdAt": ISODate("2018-07-24T09:02:53.347Z")
}
],
"$position": 0
}
}
}
)
//Your Query for checking first element status is process
db.find(
{
"transactionStatus.0.status": "process"
}
)
refer $position, $each
I have the following document structure in my MongoDB and I am trying to return an array of objects containing all prices for itemID "5a59c587fa9b4a212b0a1312" across all documents using the following query but unfortunately it is always returning an empty array. Can someone please advice what I might be doing wrong here? and how I can get such a result?
Note: I am using promised-mongo in a Node.js app to access my MongoDB
Query I tried:
{ transDetails: { $elemMatch: { itemID: "5a59c587fa9b4a212b0a1312" } } }
DB sample:
{
"_id" : ObjectId("5a688e7ea52deb6d4a6b6663"),
"transactionID" : "1",
"transDetails" : [
{
"itemID" : "5a59c587fa9b4a212b0a1312",
"price" : "22"
},
{
"itemID" : "5a59c95b081c6c612bd17058",
"price" : "24"
}
] }
{
"_id" : ObjectId("5a6aa99a52deb6d4a67714"),
"transactionID" : "2",
"transDetails" : [
{
"itemID" : "5a59c587fa9b4a212b0a1312",
"price" : "35"
},
{
"itemID" : "5a59c95b081c6c612bd17058",
"price" : "24"
}
] }
Find with projection to have only matched items in the transDetails:
.find({"transDetails.itemID": "5a59c587fa9b4a212b0a1312"}, {_id:0, "transDetails.$": 1})
Will return
{
"transDetails" : [
{
"itemID" : "5a59c587fa9b4a212b0a1312",
"price" : "22"
}
]
},
{
"transDetails" : [
{
"itemID" : "5a59c587fa9b4a212b0a1312",
"price" : "35"
}
]
},
....
Wish you re-shape the documents, you can use aggregation:
.aggregate([
{ $match: { "transDetails.itemID": "5a59c587fa9b4a212b0a1312" } },
{ $project: {
_id: 0,
transDetails: {
$filter: {
input: "$transDetails",
as: "item",
cond: { $eq: [ "$$item.itemID", "5a59c587fa9b4a212b0a1312" ] }
}
}
} },
{ $unwind: "$transDetails"},
{ $project: {price: "$transDetails.price"}}
])
Which will give you
{
"price" : "22"
},
{
"price" : "35"
},
...
Here is my User Schema
{
"user_collection":[
{
"_id":"xxx",
"name":"NAME",
"prescription":
{
"doctor_id":
[{
"_id":"xxx",
"medicine_id":"MEDICINE_ID",
}]
},
"meal":{
"meal_name":{
"start_time":"START_TIME",
"end_time":"END_TIME"
}
},
"created_at":"CREATED_AT",
"updted_at":"UPDATED_AT"
}
]
}
Note : _id given just for understanding
I need to insert document inside the prescription array. Here is the condition
If the new doctor_id is given, it should add in the prescription array like
{
"_id" : ObjectId("5813288eaa0f1231de477d92"),
"name" : "andrew",
"prescription" : [
{
"prescription" : [
{
"_id" : ObjectId("58143d484e26a229873b0528"),
"medicine_id" : "10011241343"
}
],
"_id" : ObjectId("58143d484e26a229873b0527"),
"doctor_id" : "5813221ace684e2b3f5f0a6d"
}
]
}
And if i given the doctor_id that already exists it should add like
{
"_id" : ObjectId("5813288eaa0f1231de477d92"),
"name" : "andrew",
"prescription" : [
{
"prescription" : [
{
"_id" : ObjectId("58143d484e26a229873b0528"),
"medicine_id" : "10011241343"
}
],
"prescription" : [
{
"_id" : ObjectId("58143d484e26a229873b0529"),
"medicine_id" : "10011241349"
}
],
"_id" : ObjectId("58143d484e26a229873b0527"),
"doctor_id" : "5813221ace684e2b3f5f0a6d"
}
]
}
What i have tried is
dbModel.user.update({
_id: req.body.user_id
}, {
$set: {
prescription: [ { "doctor_id" : req.body.doctor_id, "prescription" : [
{
"medicine_id" : req.body.medicine_id
}]} ],
}
}, {
upsert: true
}, function(err) {
if (err) {
res.status(202).json({
"success": "0",
"message": err
})
} else {
res.status(200).json({
"success": "1",
"message": "Prescription given successfully"
});
}
})
I don't know how to check whether the doctor_id already exists and if it does not exists it should add a new array, and if it exists it should add inside the existing arrays
Take a look in this answer.
But basically you can use the $ operator which identifies an element in an array.
You can see here some mongodb array operators.