Mongoose pull nested array - mongodb

My user schema is
{
widgets: [
{
commands: [
{
name: 'delete'
}
]
}
],
name: 'John',
}
and I want to delete widgets.commands by id. I use mongoose. I know the id but when I make the pull request it doesn't delete it.
$pull: {widgets.$.commands: {_id: req.params.id}} Any suggestions?

Here you go
update({}, {
$pull: {
'widgets.commands._id': req.params.id,
},
});
$pull mongoDB documentation
#example from doc
db.profiles.update( { _id: 1 }, { $pull: { votes: { $gte: 6 } } } )

Related

mongoose updateOne function: don't update if $pull didn't work

I'm using updateOne method like this:
Photo.updateOne(
{
"_id": photoId
},
{
"$pull": {
comments: {
_id: ObjectID(commentId),
"user.id": user.id
}
},
"$inc": { "commentCount": -1 },
},
)
Photo model which contains comments as a array and commentCount as a number. When I run the code it's working but if the photo doesn't have the comment (which I'm trying to pull) it's still incrementing commentCount by -1. What I want is, if the code does not pull any comment in photo comments, don't update the commentCount too. How can I add this rule to my code?
Thanks for help.
You can also add both fields comments._id and comments.use.id conditions in query part, if comment is not available then it will skip update and pull part.
Photo.updateOne(
{
_id: photoId,
comments: {
$elemMatch: {
_id: ObjectID(commentId),
"user.id": user.id
}
}
},
{
"$pull": {
comments: {
_id: ObjectID(commentId),
"user.id": user.id
}
},
"$inc": { "commentCount": -1 }
}
)
There is no such feature existing in Mongo, What you can do if you're using Mongo v4.2+ is use pipelined update, as the name suggests this gives you the power to use a pipeline within an update, hence allowing us to have conditions based on previous results.
Photo.updateOne(
{ "_id": photoId },
[
{
$set: {
comments: {
$filter: {
input: "$comments",
as: "comment",
cond: {
$and: [
{$ne: ["$$comment._id", ObjectID(commentId)]},
{$ne: ["$$comment.user.id", user.id]} //really necessary?
]
}
}
}
}
},
{
$set: {
commentCount: {$size: "$comments"}
}
}
]
)
For lesser versions you'll have to split it into 2 calls. no way around it.
-------------- EDIT ---------------
You can update the query to find the document using $elemMatch, if it's not found then it means the comment belonged to someone else and you can throw an error in that case.
Photo.updateOne(
{
_id: photoId,
comments: {
$elemMatch: {
_id: objectID(commentId),
"user.id": user.id
}
}
},
{
"$pull": {
comments: {
_id: ObjectID(commentId),
"user.id": user.id
}
},
"$inc": { "commentCount": -1 }
}
)

delete element out of array with $pull and $cond operators

I want to pull elements out of the array only if some condition is met
This is my document structure:
{
_id: "userId",
posts: [{
_id: "postId",
comments:[{
_id: "commentId",
userid: "some id of an user" // USER
},{
_id: "commentId2",
userid: "some id of an user2"
}]
}]
}
I want to delete the element from the comments array with the given commentId. This should be done only if userid is USER. If that condition isn't met, that means that comment doesn't belongs to the user that wants to delete it so I decline it.
Tried Attempt :
Post.findOneAndUpdate(
{
_id: mongoose.Types.ObjectId(userId)
},
{
$pull: {
$cond: [
{
"posts.$[post].comments.$[comment].userid": {
$eq: USER
}
},
{
$pull: {
comments: {
_id: mongoose.Types.ObjectId(commentId)
}
}
}
]
}
},
{
arrayFilters: [
{
"comment._id": mongoose.Types.ObjectId(commentId)
},
{
"post._id": mongoose.Types.ObjectId(postId)
}
]
}
)
That code above doesn't work, I'm stuck there & I don't know how to continue. maybe somebody knows how to fix this.
You can try below query :
Post.findOneAndUpdate(
{
_id: mongoose.Types.ObjectId(userId) // Fetches actual document
},
// Any matching object that has these fields/values in comments array will be pulled out
{
$pull: {"posts.$[post].comments": { _id : mongoose.Types.ObjectId(commentId), "userid": USER }}},
{
arrayFilters: [
{
"post._id": mongoose.Types.ObjectId(postId) // Checks which object inside `posts` array needs to be updated
}
]
}
)
Note : Use an option { new : true } in mongoose to return updated document, or in shell use { returnNewDocument : true }

Unable to add a object as a Sub field for another Existing JSON object

I am unable to add the following object:
[
{ 'option1':'opt1','option2':'opt2','option3':'opt3'},
]
as a sub filed to:
const question_list=await Questions.find({ $and: [{categoryid:categoryId},{ isDeleted: false }, { status: 0 }] }, { name: 1 });
question_list=[{"_id":"5eb167fb222a6e11fc6fe579","name":"q1"},{"_id":"5eb1680abb913f2810774c2a","name":"q2"},{"_id":"5eb16b5686068831f07c65c3","name":"q5"}]
I want the final Object to be as:
[{"_id":"5eb167fb222a6e11fc6fe579","name":"q1","options":[
{ 'option1':'opt1','option2':'opt2','option3':'opt3'},
]},{"_id":"5eb1680abb913f2810774c2a","name":"q2","options":[
{ 'option1':'opt1','option2':'opt2','option3':'opt3'},
]},{"_id":"5eb16b5686068831f07c65c3","name":"q5","options":[
{ 'option1':'opt1','option2':'opt2','option3':'opt3'},
]}]
what is the best possible solution?
You need to use aggregation-pipeline to do this, instead of .find(). As projection in .find() can only accept $elemMatch, $slice, and $ on existing fields : project-fields-from-query-results. So to add a new field with new data to documents, use $project in aggregation framework.
const question_list = await Questions.aggregate([
{
$match: {
$and: [{ categoryid: categoryId }, { isDeleted: false }, { status: 0 }]
}
},
{
$project: {
_id: 0,
name: 1,
options: [{ option1: "opt1", option2: "opt2", option3: "opt3" }]
}
}
]);
Test : mongoplayground

MongoDb remove element from array as sub property

I am trying to remove an entry in an array that is a sub property of a document field.
The data for a document looks like this:
{
_id: 'user1',
feature: {
enabled: true,
history: [
{
_id: 'abc123'
...
}
]
},
...
}
For some reason I have not been able to remove the element using $pull and I'm not sure what is wrong.
I've looked at the official docs for $pull, this well-known answer, as well this one and another.
I have tried the following query
db.getCollection('userData').update({ _id:'user1' }, {
$pull: {
'feature.history': { _id: 'abc123' }
}
})
and it has no effect. I've double-checked _id and it is a proper match. I've also tried filtering based on the same entry, thinking I need to target the data I'm trying to remove:
db.getCollection('userData')
.update({ _id: 'user1', 'feature.history': { _id: 'abc123' }, { ... })
So far no luck
You need to cast your id to mongoose ObjectId
db.getCollection('userData').update(
{ "_id": "user1" },
{ "$pull": { "feature.history": { "_id": mongoose.Types.ObjectId(your_id) } }
})
db.getCollection('userData').update({ _id:'user1', "feature.history._id" : "abc123" }, {
$pull: {
'feature.history.$._id': 'abc123'
}
})

Check if document exists in mongodb

This is how I check if a document exists:
var query = {};
if (req.body.id) {
query._id = {
$ne: new require('mongodb').ObjectID.createFromHexString(req.body.id)
};
}
Creditor.native(function(err, collection) {
collection.find({
$or: [{
name: req.body.name
}, {
sapId: req.body.sapId
},
query
]
}).limit(-1).toArray(function(err, creditors) {
if (creditors.length > 0) {
return res.send(JSON.stringify({
'message': 'creditor_exists'
}), 409);
} else {
return next();
}
})
});
To avoid that multiple documents exist with the same name or/and the same sapID I do this check on every creation/update of a document.
E.g. I want to update this document and give it a different name
{
name: 'Foo',
sapId: 123456,
id: '541ab60f07a955f447a315e4'
}
But when I log the creditors variable I get this:
[{
_id: 541a89a9bcf55f5a45c6b648,
name: 'Bar',
sapId: 3454345
}]
But the query should only match the same sapID/name. However there totally not the same. Is my query wrong?
You're currently finding docs where name matches OR sapId matches OR _id doesn't match. So that last clause is what's pulling in the doc you're seeing.
You probably mean to find docs where (name matches OR sapId matches) AND _id doesn't match.
collection.find({ $and: [
query,
{ $or: [{
name: req.body.name
}, {
sapId: req.body.sapId
}
] } ]
})
Or more simply:
collection.find({
_id: { $ne: require('mongodb').ObjectID.createFromHexString(req.body.id) },
$or: [{
name: req.body.name
}, {
sapId: req.body.sapId
}
]
})