Deleting relational data in MongoDB - Removing specific element from Array - node.js

I have a collection of users and a collection of articles. A user holds multiple articles in an array. Now I'm trying to delete an article from a User's array of articles in MongoDB. So far I have
exports.delete = function(req, res, next) {
const articleId = req.params.id;
Article.findOneAndRemove({_id: articleId})
.then((deletedArticle)=> {
const authorId = deletedArticle.author;
console.log("AUTHOR:"+authorId);
User.update( { _id: authorId }, { $pull: { articles: [ _id: deletedArticle.id ] } } )
res.status(204).send(deletedArticle)
})
.catch(next);
}
this does delete this article itself, however, not the reference to the article saved in the array the User object holds. What am I doing wrong here?

Try changing the square brackets in your query to curly:
User.update( { _id: authorId }, { $pull: { articles: { _id: deletedArticle.id } } } )
This would obviously require that the _id in the articles array is the same as the article collection, which depends on how you populate the array (I have a feeling you are doing that part right, but just wanted to mention that possibility up front).

Related

How to update a value in a mongoose document?

I am trying to modify a piece of code written by another person.
I need to update a mongodb document rather than removing it.
Here is the code :
const docs = await docsModel.find({});
for (const doc of docs) {
doc.remove();
}
I need to update rather than remove. I've tried this :
const docs = await docsModel.find({});
for (const doc of docs) {
doc.updateOne({field_I_want_to_update : "new_value"});
}
But it doesn't work.
Any suggestion ?
Depends on situation.
If you want to update multiple document with the same value, then better would be:
doc.updateMany(<filter>, { $set: { field_I_want_to_update : "new_value" } });
Where: <filter> would be something like: { _id: <validId> } or { _id: { $in: [<array_of_ids>] } }
If you want to update multiple documents with dynamic values, then this would work:
const docs = await docsModel.find({});
docs.forEach(function(doc) {
await docsModel.updateOne({ _id: doc._id }, { $set: { field_I_want_to_update: "new_dynamic_value" } });
});
Comparing to your case, you're missing <filter> as the first parameter, and in second you need to start with $set: { <field>: <value> } rather with { <field>: <value> }
You can use mongoose updateMany function which takes in a filter and an object of keys and values to update. wrap the object in a $set property. In theory, you could for-loop and use findByIdAndUpdate but that's a resource hog.
So something like
await docsModel.updateMany({}, {$set: { /*insert field name*/: /*insert field value*/ } });

Pull objects from array in embedded MongoDB document

I've been unable to figure out a way to to remove objects from arrays in embedded mongoDB documents.
My schema holds a user and a list of flower objects. It looks like this:
const UserSchema = new Schema({
name : {
type : String,
required : true
},
flowers : [
{
name : {
type : String,
required : true
},
water_freq : {
type : Number,
required : true
}
}
]
});
I've managed to add flower objects like so:
router.post('/:id/flowers/add', (req, res) => {
const { name, water_freq } = req.body;
User.findByIdAndUpdate(
req.params.id,
{
$push : {
flowers : { name, water_freq }
}
},
{ new: true }
).then((user) => res.json(user));
});
I want to delete flowers from users by their id but I'm unable to get it to work.
The code below is my non-working attempt at it.
router.delete('/:id/flowers/delete/:flowerid', (req, res) => {
User.findByIdAndUpdate(req.params.id, {
$pull : {
flowers : { _id: req.params.flowerid }
}
}).then((user) => res.json(user));
});
I would greatly appreciate if someone can help me get this right.
One possible cause is, in your query {$pul: xxxx}, MongoDB is expecting a BSON type object id that was auto generated for each flower entry, while you are giving a string. So you may want to convert it to the proper type before doing the query:
try:
router.delete('/:id/flowers/delete/:flowerid', (req, res) => {
User.findByIdAndUpdate(req.params.id, {
$pull : {
flowers : { _id: ObjectId(req.params.flowerid) }
}
}).then((user) => res.json(user)); });
To see more about the objectId
Thanks for the replies!
I did some more testing with postman and found out that in fact the code I posted in my question did work after all. What set me off was that the response with the user document still displayed the flower I had just deleted, which made me think it didn't work.
I still have no idea why that is, or if there's a way to get a response with the updated user. But the deletion seems to work as intended.

push array in array field in mongodb [duplicate]

This question already has answers here:
Push items into mongo array via mongoose
(11 answers)
Closed 4 years ago.
I have an array called students in a schema called Course. I created a route that allows me to add students to this array using a student's ObjectID like so:
router.put('/addStudent/:courseID', function (req, res) {
Course.findOneAndUpdate({courseID: req.params.courseID}, {$push: {students: req.body.students}})
.populate('students')
.exec(function (err, course) {
if (err) return res.status(500).send("There was a problem adding this information to the database");
res.status(201).send(course);
})
});
When I try making a PUT request to my endpoint with the following JSON body:
{
"students":["5b1f06cafa355c2d187c344f"]
}
Nothing happens at all, it just sends me back the course with the student ID not added. How do I make it so I could add more student IDs to the array? I don't want it to replace the array with a student ID, I want to keep adding more as I make more requests.
Thanks!
You need to use $each with $push
Course.findOneAndUpdate(
{ courseID: req.params.courseID },
{ $push: { students: { $each: req.body.students } } }
)
Course.findOneAndUpdate(
{ courseID: req.params.courseID },
{ $addToSet: { students: { $each: req.body.students } } }
)
put request will update your db and findOneAndUpdate method from mongoose is also for updating you current item, you need to use post request and save method in mongoose instead if you want create a new item.
You can Try this:
Course.findOneAndUpdate(
{ courseID: req.params.courseID },
{ $push: {
students: req.body.students
}}, options, function(err, values){
});

express/mongoose update query

I having problem wrapping my head around updating multiple values in my mongoDB using mongooseJS and ExpressJS.
Let say I submit an array of 2 or more objects from my frontend to "express routing" and there I get the req.body parameters to fetch it. My req.body looks like this:
[articles:
{ article: {
_id: '564209c66c23d5d20c37bd84',
quantity: 25,
},
{ article: {
_id: '564209c66c23d5d20c37bd83',
quantity: 51,
},
}]
I then need to loop? to find the specific article in the db to update and when that article is found I want to update the "quantity" value from the frontend to the correct article in db.
var id = [];
var body = {};
for (var i = req.body.length - 1; i >= 0; i--) {
id.push(req.body[i].article._id);
body[i] = req.body[i].article.quantity;
};
Articles.update(
{ _id: {$in: id} },
{ $set: {quantity: body[0].article.quantity} },
{multi: true},
function(err, response){
if(err)
console.log(err);
console.log(response);
});
The problem with this code is that I put in the first quantity value for all articles and I want it to be the correct one from the frontend. It feels like I'm on the right path but i pretty new to mongoDB and express so if there is a better solution or even a solution let me know.
Grahlie,
If you are having issues with queries, it's sometimes useful to test queries from the mongodb shell itself to workout the logic.
If your article documents are structured as such:
{
_id: ObjectId("564209c66c23d5d20c37bd84"),
quantity: 25
}
{
_id: ObjectId("564209c66c23d5d20c37bd83"),
quantity: 51
}
If you want to update the quantity of a unique document based on it's _id then you could so with this query.
db.articles.update(
{"_id": "564209c66c23d5d20c37bd84"},
{$set : { "quantity" : 25}}
)
If you wanted to update multiple documents with the same quantity you could use $in, but that's not what you want to do. You want to loop through your req.body array and update the quantity of each article.
So your code would be as such:
var articles = req.body;
var updateArticle = function(article) {
Articles.update(
{_id:article._id},
{$set:{ quantity: article.quantity}},
function(err, article){
...
);
}
for(var i = 0, n = articles.length; i < n; i++){
updateArticle(articles.[i].article);
}

mongodb/mongoose findMany - find all documents with IDs listed in array

I have an array of _ids and I want to get all docs accordingly, what's the best way to do it ?
Something like ...
// doesn't work ... of course ...
model.find({
'_id' : [
'4ed3ede8844f0f351100000c',
'4ed3f117a844e0471100000d',
'4ed3f18132f50c491100000e'
]
}, function(err, docs){
console.log(docs);
});
The array might contain hundreds of _ids.
The find function in mongoose is a full query to mongoDB. This means you can use the handy mongoDB $in clause, which works just like the SQL version of the same.
model.find({
'_id': { $in: [
mongoose.Types.ObjectId('4ed3ede8844f0f351100000c'),
mongoose.Types.ObjectId('4ed3f117a844e0471100000d'),
mongoose.Types.ObjectId('4ed3f18132f50c491100000e')
]}
}, function(err, docs){
console.log(docs);
});
This method will work well even for arrays containing tens of thousands of ids. (See Efficiently determine the owner of a record)
I would recommend that anybody working with mongoDB read through the Advanced Queries section of the excellent Official mongoDB Docs
Ids is the array of object ids:
const ids = [
'4ed3ede8844f0f351100000c',
'4ed3f117a844e0471100000d',
'4ed3f18132f50c491100000e',
];
Using Mongoose with callback:
Model.find().where('_id').in(ids).exec((err, records) => {});
Using Mongoose with async function:
const records = await Model.find().where('_id').in(ids).exec();
Or more concise:
const records = await Model.find({ '_id': { $in: ids } });
Don't forget to change Model with your actual model.
Combining Daniel's and snnsnn's answers:
let ids = ['id1', 'id2', 'id3'];
let data = await MyModel.find({
'_id': {
$in: ids
}
});
Simple and clean code. It works and tested against:
"mongodb": "^3.6.0",
"mongoose": "^5.10.0",
Use this format of querying
let arr = _categories.map(ele => new mongoose.Types.ObjectId(ele.id));
Item.find({ vendorId: mongoose.Types.ObjectId(_vendorId) , status:'Active'})
.where('category')
.in(arr)
.exec();
This code works for me just fine as of mongoDB v4.2 and mongoose 5.9.9:
const Ids = ['id1','id2','id3']
const results = await Model.find({ _id: Ids})
and the Ids can be of type ObjectId or String
Both node.js and MongoChef force me to convert to ObjectId. This is what I use to grab a list of users from the DB and fetch a few properties. Mind the type conversion on line 8.
// this will complement the list with userName and userPhotoUrl
// based on userId field in each item
augmentUserInfo = function(list, callback) {
var userIds = [];
var users = []; // shortcut to find them faster afterwards
for (l in list) { // first build the search array
var o = list[l];
if (o.userId) {
userIds.push(new mongoose.Types.ObjectId(o.userId)); // for Mongo query
users[o.userId] = o; // to find the user quickly afterwards
}
}
db.collection("users").find({
_id: {
$in: userIds
}
}).each(function(err, user) {
if (err) {
callback(err, list);
} else {
if (user && user._id) {
users[user._id].userName = user.fName;
users[user._id].userPhotoUrl = user.userPhotoUrl;
} else { // end of list
callback(null, list);
}
}
});
}
if you are using the async-await syntax you can use
const allPerformanceIds = ["id1", "id2", "id3"];
const findPerformances = await Performance.find({
_id: {
$in: allPerformanceIds
}
});
I tried like below and it works for me.
var array_ids = [1, 2, 6, 9]; // your array of ids
model.find({
'_id': {
$in: array_ids
}
}).toArray(function(err, data) {
if (err) {
logger.winston.error(err);
} else {
console.log("data", data);
}
});
I am using this query to find the files in mongo GridFs. I wanted to get the by its Ids.
For me this solution is working: Ids type of ObjectId.
gfs.files
.find({ _id: mongoose.Types.ObjectId('618d1c8176b8df2f99f23ccb') })
.toArray((err, files) => {
if (!files || files.length === 0) {
return res.json('no file exist');
}
return res.json(files);
next();
});
This is not working: Id type of string
gfs.files
.find({ _id: '618d1c8176b8df2f99f23ccb' })
.toArray((err, files) => {
if (!files || files.length === 0) {
return res.json('no file exist');
}
return res.json(files);
next();
});

Resources