Mongoose updateOne and $switch - node.js

In one of my database collections I want to update an entry of an array in my document depending of a condition. My documents are like this:
{
_id : ...
myarray: [
{
field1: false,
field2: false,
...
},
...
]
}
I want to set field2 dependent of the old value of field1 and then change the value of field1 too. I tried it like this with no success:
Document.updateOne({
person : req.body.person
},
{
$set: {
"myarray.$[elem].field2" : {
$switch : {
branches : [
{ case : { $eq : [ "marry.$[elem].field1", true ]}, then : false }
],
default : req.body.field2
},
"myarray.$[elem].field2" : req.body.field1
}
},{
arrayFilters: [
{ "elem._id" : { $in : req.body.myEntries }}
],
"multi" : true
})
.exec(...)
Can you help me? Thanks in advance!

Related

Mongoose, update values in array inside an object in an array

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

MongoDB - Update array data in array

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

findOneAndUpdate is not working inside array

I'm trying to update the document in the MongoDB collection but it's not working for me. Here is the function all fields come to the backend.
{
"_id" : ObjectId("59a6b381c13a090c70fc21b6"),
"processNumber" : "FEE 082517",
"System" : "abc",
"TaxAmount" : 0,
"TaxPercent" : 0,
"Currency" : "USD",
"ProcessData" : [
{
"_id" : ObjectId("59ee2873b1621419a03fba6c"),
"KDSID" : "1db1d4b8-61bc-45eb-bf6d-15af1e391df5"
},
{
"_id" : ObjectId("59ee2873b1621419a03fba6d"),
"KDSID" : "aa9ccaf3-a638-4013-afdc-ccf0a39361e8"
},
{
"_id" : ObjectId("59ee2873b1621419a03fba6e"),
"KDSID" : "4c5e32a7-e2fb-4fe9-998f-e22602e46dba"
}
{
"Name" : "2017 Calc.xlsx",
"FileID" : "59ee2873b1621419a03fb9b7",
"_id" : ObjectId("59ee2873b1621419a03fba75")
}
]
}
Query:
db.process.findOneAndUpdate(
{ 'ProcessData._id': ObjectId("59ee2873b1621419a03fba75"),'ProcessData.FileID': { '$exists': true, '$ne': null }},
{ $set: { 'ProcessData.$.IsFailed': "Yes" } }
)
When I run the above query IsFailed is not updating. can you please advise?
I have tried in node and MongoDB and it's not working.
If ProcessData._id matches with the given id and ProcessData.FileID exist we have to set IsFailed Yes
here's my version with $elemMatch
db.process.updateOne(
{
ProcessData: {
$elemMatch: {
_id: ObjectId("59ee2873b1621419a03fba75"),
FileID: {
$exists: true,
$ne: null
}
}
}
},
{
$set: { "ProcessData.$.IsFaild": "Yes" }
})
https://mongoplayground.net/p/3LBlw9kA6Gl
You need to use arrayFilter when you modify the array.
db.collectionName.updateOne(
{
'ProcessData._id': ObjectId("59ee2873b1621419a03fba75"),
'ProcessData.FileID': { '$exists': true, '$ne': null }
},
{
$set:{"ProcessData.$[p].isFailed": "Yes"}},
{
arrayFilters:[{"p._id":ObjectId("59ee2873b1621419a03fba75")}]
}
)
You're querying through an array of embedded documents, and hence by using elemMatch to find the specific id and perform needed operation on that document should be implemented for example as below:
db.process.updateOne(
{
ProcessData: {
$elemMatch: {
_id:
ObjectId("59ee2873b1621419a03fba75"),
}
}
},
{
"ProcessData.FileID": {
$exists: true,
$ne: null
}
},
{
$set: {
"ProcessData.$.IsFailed": "Yes"
}
})

Mongoose : Find and Update Multiple Nested Documents

My Documents are as follows.
{
"order_id" : "1",
"payment_status" : false,
"items" : [
{
"item_id" : 1,
"payment_status" : false,
},
{
"item_id" : 2,
"payment_status" : false,
},
{
"item_id" : 3,
"payment_status" : false,
},
]
}
I need to update the fields payment_status for {"order_id":1} and {"item_id" : 1} and {"item_id" : 3}. Also, I need to update the same in bulk for matching conditions. Is this possible in mongoose?
You want to be using $arrayFilters like so:
db.collection.updateMany({
"order_id": "1"
},
{
"$set": {
"items.$[item].payment_status": true
}
},
{
arrayFilters: [
{
"item.item_id": {
$in: [
1,
3
]
}
}
]
})
Mongo Playground

How do I update a field in embedded documents based on another array in MongoDB

I am trying to update an embedded document in MongoDB using mongoose in nodejs. The document is simplified and shown below (The names in friendList is assumed to be unique):
{
"_id" : ObjectId("5eb0617f3aec924ff42249cd"),
"friendList" : [
{
"name" : "Alex",
"flag" : false,
},
{
"name" : "Bob",
"flag" : false,
},
{
"name" : "Caleb",
"flag" : true,
},
{
"name" : "Debbie",
"flag" : false,
}
]
}
I would like to update this collection by:
accepting a Patch API with a request body containing a subset of friendList and
update the nested field flag.
For example, if I were to do a patch call from postman with the request body:
{
"friendList":[
{
"name":"Alex",
"flag":true
},
{
"name":"Caleb",
"flag":false
},
{
"name":"Debbie",
"flag":false
}
]
}
then I should expect my document in MongoDB to look like this:
{
"_id" : ObjectId("5eb0617f3aec924ff42249cd"),
"friendList":[
{
"name":"Alex",
"flag":true
},
{
"name":"Bob",
"flag":false
},
{
"name":"Caleb",
"flag":false
},
{
"name":"Debbie",
"flag":false
}
]
}
What I have tried on nodejs is updating the entire request body:
function updateUser(req){
User.findOneAndUpdate({'_id':req.params._id},req.body,{new:true});
}
which replaces the entire friendList array:
{
"_id" : ObjectId("5eb0617f3aec924ff42249cd"),
"friendList":[
{
"name":"Alex",
"flag":true
},
{
"name":"Caleb",
"flag":false
},
{
"name":"Debbie",
"flag":false
}
]
}
I have also tried using array operators like $:
function updateUser(req){
User.findOneAndUpdate(
{'_id':req.params._id},
{$addToSet:{
"friendList":{
$each:req.body.friendList}
}
},
{new:true}
);
}
which gave me the output:
{
"_id" : ObjectId("5eb0617f3aec924ff42249cd"),
"friendList" : [
{
"name" : "Alex",
"flag" : false,
},
{
"name" : "Bob",
"flag" : false,
},
{
"name" : "Caleb",
"flag" : true,
},
{
"name" : "Debbie",
"flag" : false,
},
{
"name" : "Alex",
"flag" : true,
},
{
"name" : "Caleb",
"flag" : false,
},
]
}
which $addToSet considers both name and flag when making a comparison to check if the values exist in the array. It might work if I am able to intercept at this comparison phase such that only the name field is checked.
I have been exploring concepts like $[<identifier>] and arrayFilter but can't seem to make it work.
Simple $addToSet does not work, because your array is not ["Alex","Caleb","Debbie"]. Your array is
[
{name: "Alex", flag: true},
{name: "Caleb", flag: false},
{name: "Debbie", flag: false}
]
Element {name:"Alex", flag: true} is different to element {name: "Alex", flag: false}, that's the reason why your approach failed. I think you have to use aggregation pipeline, e.g. this one:
db.collection.aggregate([
{ $addFields: { newFriends: friendList } },
{
$set: {
friendList: {
$map: {
input: "$friendList",
in: {
name: "$$this.name",
flag: {
$cond: [
{ $eq: [{ $indexOfArray: ["$newFriends.name", "$$this.name"] }, - 1] },
"$$this.flag",
{ $arrayElemAt: [ "$newFriends.flag", { $indexOfArray: ["$newFriends.name", "$$this.name"] } ] }
]
}
}
}
}
}
},
{ $unset: "newFriends" }
])
Or if you like to work with index variable:
db.collection.aggregate([
{ $addFields: { newFriends: friendList } },
{
$set: {
friendList: {
$map: {
input: "$friendList",
in: {
$let: {
vars: { idx: { $indexOfArray: ["$newFriends.name", "$$this.name"] } },
in: {
name: "$$this.name",
flag: {
$cond: [
{ $eq: ["$$idx", - 1] },
"$$this.flag",
{ $arrayElemAt: ["$newFriends.flag", "$$idx"] }
]
}
}
}
}
}
}
}
},
{ $unset: "newFriends" }
])
Note, this will update only existing names. New names are not added to the array, your question is not clear in this regard. If you like to add also new elements then insert
{
$set: {
friendList: { $setUnion: ["$friendList", "$newFriends"] }
}
},
just before { $unset: "newFriends" }
The aggregation pipeline can be used in an update:
User.findOneAndUpdate(
{'_id':req.params._id},
[
{ $addFields: { newFriends: req.body.friendList } },
{
$set: { ...
}
]
);

Resources