In my application, I am attempting to update a object nested in an array as a below. When testing in postman, there is a delay causing me to have to make two requests in order to see the updated value.
if (taskStatus) {
const taskStatusNew = await Board.findOneAndUpdate(
{
"columns.tasks._id": req.params.id,
},
{
$set: {
"columns.$[].tasks.$[t]": req.body,
},
},
{
arrayFilters: [
{
"t._id": req.params.id,
},
],
}
);
res.status(200).json(taskStatusNew);
}
By default, findOneAndUpdate() returns the document as it was before the update was applied. So you have to set the new option to true if you are using mongoose.
const taskStatusNew = await Board.findOneAndUpdate(
{
"columns.tasks._id": req.params.id,
},
{
$set: {
"columns.$[].tasks.$[t]": req.body,
},
},
{
arrayFilters: [
{
"t._id": req.params.id,
},
],
new: true
}
);
Documentation article for reference: https://mongoosejs.com/docs/tutorials/findoneandupdate.html
If your question is like to return the updated value then use this,- {returnDocument: 'after'}, you just need to add this in other parameter, then it will give you updated value.
Related
When I update any value on frontend it get update in mongodb except boolen value . when I update boolen value on frontend it create new document for that change in collection. below is my code snippet.
let threats = req.body.threat
let threatss = []
if (threats) {
await Promise.all(threats.map(async (threat) => {
threatss.push(
{
updateOne: {
filter: { "id": threat.id },
update: {
...threat
},
upsert: true,
new: true
}
}
)
}))
}
ProjectThreatModel.bulkWrite(threatss);
Currently using Strapi v3.5.3ce. I have a post model with has relation with category model.
A post already exist, but I want to update the post with multiple categories, so I thought of using $addToSet / $set in order to eliminate duplicates added.
But it doesn't seem to push.
ways I have tried are
// with $set
const up = {
$set: { categories: [ '6073ac8f68f6971f3edfc898', '6073ac8f68f888f3edfc111' ] },
...update
}
// with $addToSet
const up = {
$addToSet: { categories: [ '6073ac8f68f6971f3edfc898', '6073ac8f68f888f3edfc111' ] },
...update
}
// with $addToSet and $each
const up = {
$addToSet: { categories: { $each: [ '6073ac8f68f6971f3edfc898', '6073ac8f68f888f3edfc111' ] } },
...update
}
const post = await strapi.query('post').model
.findOneAndUpdate({_id: 'blahblahblah'}, up, { new: true })
.populate({
path: 'categories',
select: 'name'
});
the above uses findOneAndUpdate but I have also tried using update / updateOne
None of the above worked, anyone has any idea what I have gone wrong?
Thanks in advance for any suggestions.
I have a user with various post ID's inside of my mongodb database, I am using mongoose to talk to it.
this is the user object
[
{
"premium": true,
"max_posts": 55,
"posts_made": 52,
"posts": [
"5e10046c0be4f92228f6f532",
"5e1005a9dceb1344241c74c5",
"5e100753a6cfcb44d8f1fa09",
"5e1007bea6cfcb44d8f1fa0a",
"5e1008149324aa1d002a43be",
"5e1009562826a308a0812e92",
"5e100a625e6fcb2c90a07bec",
"5e157143536d6e04a80651bd",
"5e1e320dc749f23b189ccef7",
"5e1e3273546d55384c3a975c",
"5e1e340183d0b0080816cedd",
"5e1e368bd921f3194c22b3d2",
"5e1e3732d921f3194c22b3d3",
"5e1e3a6f9b3017189cff0fe2",
"5e1e3c1a8c38f11c60354052",
"5e1e9ab208d0a5416828d0a3"
],
"_id": "5e0fe3f33c2edb2f5824ddf2",
"email": "user#gmail.com",
"createdAt": "2020-01-04T01:01:39.840Z",
"updatedAt": "2020-01-15T04:53:08.987Z",
"__v": 16
}
]
So, I make a request to the database using express, and I try to filter the array, using an id of one post, then I ask express to save that modified user model...
router.delete('/testing', (req,res,next) =>{
userModel.findOne({ email: req.body.author }, function(error, user) {
user.posts.filter(item => item != req.body.postid)
user.save((err) => {
console.log(err);
});
res.json(user)
});
});
my request in postman :
As you can see, the item is still there in that array... checking the console.log.
Please Advise as I am having doubts, thanks.
You no need to find & update the document of user, which makes two DB calls, plus .filter(), try this :
router.delete('/testing', (req, res, next) => {
userModel.findOneAndUpdate({ email: req.body.author},
{ $pull: { "posts": req.body.postid } }, { new: true }, function (error, user) {
res.json(user);
});
});
Here we're using .findOneAndUpdate() as it returns the updated user document which you need to send back.
This is how I would do it using $pull operator.
The $pull operator removes from an existing array all instances of a value or values that match a specified condition.
userModel.update(
{email: req.body.author},
{ $pull: { posts: { $in: [ req.body.postid ] } } },
{ multi: true }
)
For now it seems like you are passing single postid in request body. In future if you needed to delete multiple posts at the same time, you can use the same query by just replacing { $in: [ req.body.postid ] } with { $in: [ ...req.body.postid ] }
I'm trying to update one subdocument addresses (works) and then update many subdocuments except the previous one. Basically every time an address change is_preferred to true, it must update the previous address that is_preferred was true to false (i'm trying to update everyone except the address that changed to true).
User document
_id: ObjectId("5b996f0fd5fbf511709f668f");
addresses: [
{
_id: ObjectId("5ba33e0991cd7a3bb85dab7e");
is_preferred:true
},
{
_id: ObjectId("5ba3e9337310c637207b44cb");
is_preferred:false
}
]
Here is my solution:
// model
User = mongoose.model('user', new Schema({
_id: { type: mongoose.Schema.Types.ObjectId, required: true },
addresses: [
{
_id: { type: mongoose.Schema.Types.ObjectId, required: true },
is_preferred: { type: Boolean, required: true }
}
],
}, { collection: 'user' }););
// route
router.put('/updateAddress/:addressId', auth, user.updateAddress);
// user.js
exports.updateAddress = wrap(async(req, res, next) => {
// update one object address `is_preferred` to true and return an array 'addresses' containing it
const user = await User.findOneAndUpdate(
{ addresses: { $elemMatch: { _id: mongoose.Types.ObjectId(req.params.addressId) } } }, { 'addresses.$': req.body },
{ projection: {
addresses: {
$elemMatch: { _id: mongoose.Types.ObjectId(req.params.addressId) }
}
}, new: true }).lean();
if (user) {
// updated object `is_preferred` changed to true, so another objects must be false
if (user.addresses[0].is_preferred) {
// doesnt work
await User.update({ _id: { $ne: mongoose.Types.ObjectId(req.params.addressId) }, is_preferred: true },
{ $set: { addresses: { is_preferred: false } } }, { multi: true });
}
res.status(200).json({success: true, message: 'Saved.', new_object: user.addresses[0]});
} else {
res.status(400).json({success: false, message: 'Error.'});
}
});
I'm able to update the user subdocument addresses is_preferred to true. However updating another addresses is_preferred to false isn't working. What Am I doing wrong?
I would recommend for a scenario like yours to utilize the mongoose middleware pre or post schema hooks. The idea is that instead of dealing with this in your controller you would take care of it in your schema via that middleware.
The only inconvenience is that the pre and post hooks do not fire on findOneAndUpdate and you would need to do first find then update.
So you would do something like this for the post hook:
User.post('save', doc => {
// You can update all the rest of the addresses here.
});
Also for your update to work you need to do something like this:
User.update(
{ "addresses._id": { $ne: mongoose.Types.ObjectId(req.params.addressId) }},
{ $set: { 'addresses.0.is_preferred': false }},
{ multi: true }
)
I have an object that looks like this.
{
_id: '577fe7a842c9b447',
name: 'Jacob\'s Bronze Badges',
competitors: [
{
_id: '577fe7a842c9bd6d',
name: 'Peter\'s Silver Badges',
sites: [
{
_id: '577fe7a842c9bd6d',
name: 'Facebook',
url: 'fb.com/peter'
},
{
_id: '577fe7a842c9bd6d'
name: 'Google',
url: 'google.com/peter'
}
]
},
{
_id: '599fe7a842c9bd6d',
name: 'Paul\'s Gold Badges',
sites: [
{
'_id': '577fe7a842c9bd6d',
name: 'Facebook',
url: 'fb.com/paul'
},
{
_id: '577fe7a842c9bd6d',
name: 'Google',
url: 'google.com/paul'
}
]
}
]
}
My goal is to reference the competitors array and update items inside with all of the values from req.body. I based this code off of this answer, as well as this other one.
Location.update(
{ 'competitors._id': req.params.competitorId, },
{ $set: { 'competitors.$': req.body, }, },
(err, result) => {
if (err) {
res.status(500)
.json({ error: 'Unable to update competitor.', });
} else {
res.status(200)
.json(result);
}
}
);
I send my HTTP PUT to localhost:3000/competitors/577fe7a842c9bd6d to update Peter's Silver Badges. The request body is:
{
"name": "McDonald's"
}
The problem is that when I use $set to set the competitor with _id: req.params.competitorId, I don't know what is in req.body. I want to use the entire req.body to update the object in the array, but when I do, that object is overwritten, so instead of getting a new name, Peter's Silver Badges becomes:
{
name: 'McDonald\'s',
sites: []
}
How can I update an object within an array when I know the object's _id with all of the fields from req.body without removing fields that I want to keep?
I believe that the sites array is empty because the object was reinitialized. In my schema I have sites: [sitesSchema] to initialize it. So I am assuming that the whole competitors[_id] object is getting overwritten with the new name and then the sites: [sitesSchema] from myschema.
You would need to use the $ positional operator in your $set. In order to assign those properties dynamically, based on what is in your req.body, you would need to build up your $set programmatically.
If you want to update the name you would do the following:
Location.update(
{ 'competitors._id': req.params.competitorId },
{ $set: { 'competitors.$.name': req.body.name }},
(err, result) => {
if (err) {
res.status(500)
.json({ error: 'Unable to update competitor.', });
} else {
res.status(200)
.json(result);
}
}
);
One way you might programatically build up the $set using req.body is by doing the following:
let updateObj = {$set: {}};
for(var param in req.body) {
updateObj.$set['competitors.$.'+param] = req.body[param];
}
See this answer for more details.
To update embedded document with $ operator, in most of the cases, you have to use dot notation on the $ operator.
Location.update(
{ _id: '577fe7a842c9b447', 'competitors._id': req.params.competitorId, },
{ $set: { 'competitors.$.name': req.body.name, }, },
(err, result) => {
if (err) {
res.status(500)
.json({ error: 'Unable to update competitor.', });
} else {
res.status(200)
.json(result);
}
}
);