Mongoose delete array element in document and save - node.js

I have an array in my model document. I would like to delete elements in that array based on a key I provide and then update MongoDB. Is this possible?
Here's my attempt:
var mongoose = require('mongoose'),
Schema = mongoose.Schema;
var favorite = new Schema({
cn: String,
favorites: Array
});
module.exports = mongoose.model('Favorite', favorite, 'favorite');
exports.deleteFavorite = function (req, res, next) {
if (req.params.callback !== null) {
res.contentType = 'application/javascript';
}
Favorite.find({cn: req.params.name}, function (error, docs) {
var records = {'records': docs};
if (error) {
process.stderr.write(error);
}
docs[0]._doc.favorites.remove({uid: req.params.deleteUid});
Favorite.save(function (error, docs) {
var records = {'records': docs};
if (error) {
process.stderr.write(error);
}
res.send(records);
return next();
});
});
};
So far it finds the document but the remove nor save works.

You can also do the update directly in MongoDB without having to load the document and modify it using code. Use the $pull or $pullAll operators to remove the item from the array :
Favorite.updateOne({ cn: req.params.name }, {
$pullAll: {
favorites: req.params.deleteUid,
},
});
To remove objects from array then
Favorite.updateOne({ cn: req.params.name }, {
$pullAll: {
favorites: [{_id: req.params.deleteUid}],
},
});
(you can also use updateMany for multiple documents)
http://docs.mongodb.org/manual/reference/operator/update/pullAll/

The checked answer does work but officially in MongooseJS latest, you should use pull.
doc.subdocs.push({ _id: 4815162342 }) // added
doc.subdocs.pull({ _id: 4815162342 }) // removed
https://mongoosejs.com/docs/api.html#mongoosearray_MongooseArray-pull
I was just looking that up too.
See Daniel's answer for the correct answer. Much better.

Answers above are shown how to remove an array and here is how to pull an object from an array.
Reference: https://docs.mongodb.com/manual/reference/operator/update/pull/
db.survey.update( // select your doc in moongo
{ }, // your query, usually match by _id
{ $pull: { results: { $elemMatch: { score: 8 , item: "B" } } } }, // item(s) to match from array you want to pull/remove
{ multi: true } // set this to true if you want to remove multiple elements.
)

Since favorites is an array, you just need to splice it off and save the document.
var mongoose = require('mongoose'),
Schema = mongoose.Schema;
var favorite = new Schema({
cn: String,
favorites: Array
});
module.exports = mongoose.model('Favorite', favorite);
exports.deleteFavorite = function (req, res, next) {
if (req.params.callback !== null) {
res.contentType = 'application/javascript';
}
// Changed to findOne instead of find to get a single document with the favorites.
Favorite.findOne({cn: req.params.name}, function (error, doc) {
if (error) {
res.send(null, 500);
} else if (doc) {
var records = {'records': doc};
// find the delete uid in the favorites array
var idx = doc.favorites ? doc.favorites.indexOf(req.params.deleteUid) : -1;
// is it valid?
if (idx !== -1) {
// remove it from the array.
doc.favorites.splice(idx, 1);
// save the doc
doc.save(function(error) {
if (error) {
console.log(error);
res.send(null, 500);
} else {
// send the records
res.send(records);
}
});
// stop here, otherwise 404
return;
}
}
// send 404 not found
res.send(null, 404);
});
};

This is working for me and really very helpful.
SubCategory.update({ _id: { $in:
arrOfSubCategory.map(function (obj) {
return mongoose.Types.ObjectId(obj);
})
} },
{
$pull: {
coupon: couponId,
}
}, { multi: true }, function (err, numberAffected) {
if(err) {
return callback({
error:err
})
}
})
});
I have a model which name is SubCategory and I want to remove Coupon from this category Array. I have an array of categories so I have used arrOfSubCategory. So I fetch each array of object from this array with map function with the help of $in operator.

keywords = [1,2,3,4];
doc.array.pull(1) //this remove one item from a array
doc.array.pull(...keywords) // this remove multiple items in a array
if you want to use ... you should call 'use strict'; at the top of your js file; :)

I used this format for my project and it's worked
router.delete('/dashboard/participant/:id', async (req, res, next) => {
try {
const participant = await Participant.findByIdAndDelete({ _id: req.params.id });
// { $pull: { templates: { _id: templateid } } },
const event = await Event.findOneAndUpdate({ participants: participant._id }, { $pull: { participants: participant._id } }, { new: true });
res.status(200).json({ request: 'Deleted', participant, event });
} catch (error) {
res.json(error)
}
});

Favorite.update({ cn: req.params.name }, { "$pull": { "favorites": { "_id": favoriteId } }}, { safe: true, multi:true }, function(err, obj) {
//do something smart
});

Related

updating nested documents in mongoDB(node.js)

i am trying to update a value in the object of my embedded schema(comments schema) whose value i had previously stored 0 by default. i have tried all the ways to update but none of the stackoverflow answer worked.
my code is
var checkedBox = req.body.checkbox;
User.updateOne({_id: foundUser._id},{$set :{comments:{_id :checkedBox,cpermission:1,}}},function(err,updatec){
if(err){
console.log(err);
}
else{
console.log("successfull");
console.log(updatec);
}
});
i had comment schema nested in user schema,here foundUser._id is the particular users id,and checkedBox id is the embedded objects particular id. previously my cpermission was 0,set by default,but now i want to update it to 1. although this is updating my schema,but deleting the previous images and comments in the schema aswell.
where am i going wrong?
here is my schema
const commentSchema = new mongoose.Schema({
comment: String,
imagename: String,
cpermission:{type:Number,default:0},
});
const Comment = new mongoose.model("Comment", commentSchema);
const userSchema = new mongoose.Schema({
firstname: String,
lastname: String,
email: String,
password: String,
comments: [commentSchema],
upermission:{type:Number,default:0},
});
userSchema.plugin(passportLocalMongoose);
const User = new mongoose.model("User", userSchema);
First, you need to convert checkbox in the array, as it will be a string if you select a single element
Then wrap it with mongoose.Types.ObjectId as a precaution
Then you can use arrayFilters to update multiple matching array elements
var checkedBox = req.body.checkbox;
if (!Array.isArray(checkedBox)) {
checkedBox = [checkedBox]
}
checkedBox = checkedBox.map(id => mongoose.Types.ObjectId(id))
User.updateOne(
{ _id: foundUser._id }, // filter part
{ $set: { 'comments.$[comment].cpermission': 1 } }, // update part
{ arrayFilters: [{ 'comment._id': {$in: checkedBox }}] }, // options part
function (err, updatec) {
if (err) {
console.log(err);
}
else {
console.log("successfull");
console.log(updatec);
}
});
your comment is the array of documents. if you want to update an element of an array must be select it. for that must be added another condition to the first section of updateOne then in seconde section use $ for update selected element of the array.
User.updateOne(
{_id: foundUser._id, 'comments._id': checkedBox},
{
$set: {'comments.$.cpermission': 1}
}
, function (err, updatec) {
if (err) {
console.log(err)
}
else {
console.log('successfull')
console.log(updatec)
}
})
for more information, you can read this document form MongoDB official website.
Array Update Operators
var checkedBox = req.body.checkbox;
User.updateOne(
{ _id: foundUser._id, "comment._id": checkedBox },
{ $set: { "comment.$.cpermission": 1 } },
function (err, update) {
if (err) {
console.log(err);
} else {
console.log("successfull");
console.log(update);
}
}
);

Update field within nested array using mongoose

I'm trying to update the subdocument within the array without success. The new data doesn't get saved.
Express:
router.put('/:id/:bookid', (req, res) => {
library.findOneAndUpdate(
{ "_id": req.params.id, "books._id": req.params.bookid},
{
"$set": {
"title.$": 'new title'
}
}
});
LibraryScema:
const LibarySchema = new Library({
Name: {
type: String,
required: false
},
books: [BookSchema]
});
bookScema:
const BookSchema = new Schema({
title: {
type: String,
required: false
},
Chapters: [
{
chapterTitle: {
type: String,
required: false
}
}
]
});
I only aim to update the sub-document, not parent- and sub-document at same time.
I had a similar issue. I believe there is something wrong with the $set when it comes to nested arrays (There was an entire issue thread on GitHub). This is how I solved my issue.
var p = req.params;
var b = req.body;
Account.findById(req.user._id, function (err, acc) {
if (err) {
console.log(err);
} else {
acc.websites.set(req.params._id, req.body.url); //This solved it for me
acc.save((err, webs) => {
if (err) {
console.log(err);
} else {
console.log('all good');
res.redirect('/websites');
}
});
}
});
I have a user with a nested array.
Try this code
router.put('/:id/:bookid', (req, res) => {
library.findById(
req.params.id, (err, obj) => {
if (err) console.log(err); // Debugging
obj.books.set(req.params.bookid, {
"title": 'new title',
'Chapters': 'your chapters array'
});
obj.save((err,obj)=>{
if(err) console.log(err); // Debugging
else {
console.log(obj); // See if the saved object is what expected;
res.redirect('...') // Do smth here
}
})
})
});
Let me know if it works, and I'll add explanation.
Explanation: You start by finding the right object (library in this case), then you find the correct object in the array called books.
Using .set you set the whole object to the new state. You'll need to take the data that's not changing from a previous instance of the library object.
I believe this way will overwrite and remove any data that's not passed into the .set() method. And then you save() the changed.

Mongoose update is not working

My question here is why isn't the watchlistSchema.update(function (error) { { $push: { watchlist: req.body.stockAdded } }}); line updating the existing schema for the watchlist attribute? When I use this update nothing happens and it returns null. When I change it to watchlistSchema.save it work but it creates and entirely different document. I would like to basically check for the user and watchlist and if they both exist together I would like to push a string into the watchlist array. I am very new to mongoose so it is a bit confusing.
var Schema = mongoose.Schema;
var watchlistSchema = new Schema({
watchlist: [{ }],
user: String
});
var Watchlist = mongoose.model('Watchlist', watchlistSchema, "watchlist");
app.post('/watchlistPost', function (req, res) {
var watchlistSchema = Watchlist({
'watchlist': req.body.stockAdded,
'user': req.user.username
});
Watchlist.findOne({
$and: [{
'watchlist': req.body.stockAdded,
}, {
'user': req.user.username
}]
}, function (err, list) {
if (list) {
res.status(200).send({ "success": "Updated Successfully", "status": 200 });
} else {
if (req.user) {
watchlistSchema.update(function (error) {
{ $push: { watchlist: req.body.stockAdded } }
});
} else {
}
}
})
});
Your update statement needs to contain the "find" query. So it can appy the update condition to all documents matching the specified query.
change your code to something like:
var Schema = mongoose.Schema;
var watchlistSchema = new Schema({
watchlist: [{ }],
user: String
});
var Watchlist = mongoose.model('Watchlist', watchlistSchema, "watchlist");
app.post('/watchlistPost', function (req, res) {
var watchlistSchema = Watchlist({
'watchlist': req.body.stockAdded,
'user': req.user.username
});
var query = {
$and: [{
'watchlist': req.body.stockAdded,
}, {
'user': req.user.username
}]};
Watchlist.update(query, { $push: { watchlist: req.body.stockAdded } }, ==your callback to check stuff==);

Mongoose deleting (pull) a document within an array, does not work with ObjectID

I have the following mongoose schema:
user = {
"userId" : "myId",
"connections":
[{
"dateConnectedUnix": 1334567891,
"isActive": true
}, {
"dateConnectedUnix": 1334567893,
"isActive": false
}]
}
I would like to delete the second item in the connections array, to get the following:
user = {
"userId" : "myId",
"connections":
[{
"dateConnectedUnix": 1334567893,
"isActive": false
}]
}
The following code does the job as expected:
userAccounts.update(
{ 'connections.isActive': false },
{ $pull: { 'connections.isActive':false }},
function (err, val) {
console.log(val)
}
);
But, I need to delete based on ObjectId. And the following goes does not work:
userAccounts.update(
{ 'connections._id': '1234-someId-6789' },
{ $pull: { 'connections._id': '1234-someId-6789' } },
function (err, val) {
console.log(val)
}
);
Any suggestions? I have been banging my head against the screen (aka Google, Stackoverflow, ...) for hours and have had no luck.
It seems that the above code would not work. It should not even have worked for the first example I gave.
In the end I was supported by this answer here: MongoDB, remove object from array
Here is my working code:
userAccounts.update(
{ userId: usr.userId },
{
$pull: {
connections: { _id : connId }
}
},
{ safe: true },
function removeConnectionsCB(err, obj) {
// ...
}
);
I have a document like
I have to delete address from address array
After searching lots on internet I found the solution
Customer.findOneAndUpdate(query, {$pull: {address: addressId}}, (err, data) => {
if (err) {
return res.status(500).json({ error: 'error in deleting address' });
}
res.json(data);
});
user: {
_id: ObjectId('5ccf3fa47a8f8b12b0dce204'),
name: 'Test',
posts: [
ObjectId("5cd07ee05c08f51af8d23b64"),
ObjectId("5cd07ee05c08f51af8d23c52")
]
}
Remove a single post from posts array
user.posts.pull("5cd07ee05c08f51af8d23b64");
user.save();
To use update with ObjectId, you should use ObjectId object instead of string representation :
var ObjectId = require('mongoose').Types.ObjectId;
userAccounts.update(
{ 'connections._id': new ObjectId('1234-someId-6789') },
{ $pull: { 'connections._id': new ObjectId('1234-someId-6789') } },
function (err,val) {
console.log(val)
}
);
use findByIdAndUpdate to remove an item from an array
You can do it in mongoose 5.4.x and above
const result = await User.findByIdAndUpdate(user_id, {
$pull: {
someArrayName: { _id: array_item_id }
}
}, { new: true });
The item from array will be removed based on provided property _id value
If you are using mongoose, no need to use the MongoDB stuff, I mean that's why we're using mongoose in the first place, right?
userAccounts.connections.pull({ _id: '1234-someId-6789'});
await userAccounts.save();
mongoose: 4.11.11
What have worked for me is the following syntax:
const removeTansactionFromUser = (userId, connectionId) => {
return User.findByIdAndUpdate(userId, { $pull: { "connections": connectionId} }, {'new': true} );
};
Mongoose support id in string format or ObjectId format.
Tip: new ObjectId(stringId) to switch from string to ObjectId
In mongoose 5.8.11, this $pull: { ... } didn't work for me, so far not sure why. So I overcame it in my controller this way:
exports.removePost = async (req, res, next) => {
const postId = req.params.postId;
try {
const foundPost = await Post.findById(postId);
const foundUser = await User.findById(req.userId);
if (!foundPost || !foundUser) {
const err = new Error(
'Could not find post / user.',
);
err.statusCode = 404;
throw err;
}
// delete post from posts collection:
await Post.findByIdAndRemove(postId);
// also delete that post from posts array of id's in user's collection:
foundUser.posts.pull({ _id: postId });
await foundUser.save();
res.status(200).json({ message: 'Deleted post.' });
} catch (err) {
// ...
}
};

Update a record where _id = :id with Mongoose

I'm trying to update an existing record with Mongoose. The insert is OK but not the update.
Here is my snippet:
app.post('/submit', function(req, res) {
var my_visit = new models.visits({
date: req.body.visit_date,
type: req.body.visit_type,
agency: req.body.visit_agency,
city: req.body.visit_city,
url: req.body.visit_url,
note: req.body.visit_note
});
// INSERT
if(req.body.id == 0) {
my_visit.save(function(err) {
if(err) { throw err; }
console.log('added visit');
res.redirect('/');
});
} else { // UPDATE
var upsertData = my_visit.toObject();
console.log(req.body.id); // OK
models.visits.update({ _id: req.body.id }, upsertData, { multi: false }, function(err) {
if(err) { throw err; }
console.log('updated visit: '+ req.body.id);
res.redirect('/');
});
}
})
The response is Mod on _id is not allowed.
I just want to update the line such as WHERE id = id in MySQL. I didn't find the right syntax.
According to this question and this other one, the Mod on _id is not allowed occurs when one tries to update an object based on its id without deleting it first.
I also found this github issue which tries to explain a solution. It explicitly states:
Be careful to not use an existing model instance for the update clause
(this won't work and can cause weird behavior like infinite loops).
Also, ensure that the update clause does not have an _id property,
which causes Mongo to return a "Mod on _id not allowed" error.
The solution, it seems, is to do the following:
var upsertData = my_visit.toObject();
console.log(req.body.id); // OK
delete upsertData._id;
models.visits.update({ _id: req.body.id }, upsertData, { multi: false }, function(err) {
if(err) { throw err; }
//...
}
On a side note, you can probably rewrite your route to do both the create and update without the if-else clause. update() takes an extra option upsert, which, according to the docs:
upsert (boolean) whether to create the doc if it doesn't match (false)
Here is my solution:
routes/router.js
router.patch('/user/:id', userController.updateUser)
exports.updateUser = async(req, res) => {
const updates = Object.keys(req.body)
const allowedUpdates = ['name', 'email', 'password', 'age']
const isValidOperation = updates.every((update) => allowedUpdates.includes(update))
if (!isValidOperation) {
return res.status(400).send('Invalid updates!')
}
try {
const user = await UserModel.findByIdAndUpdate(req.params.id, req.body, { new: true, runValidators: true })
if (!user) {
return res.status(404).send()
}
res.status(201).send(user)
} catch (error) {
res.status(400).send(error)
}
}

Resources