How to remove object from array in Mongoose using updateMany? - node.js

I am trying to get an object removed from an array in MongoDB using Mongoose/Node and so far I have been unable to get it removed. I have read docs and SO posts to no luck. I'm hoping someone could give me specific advice on a solution not just a link to another post which is vaguely related
Mongo document example
{
managers: [{
userID: value,
// other fields and values
},
// Another object for another manager
]
}
Code I am trying to use
await Model.updateMany(
{'managers.userID': userID}, // This should be the query part
{$pullAll: {'managers': {'userID': userID}}} // This should be the update part
)

Using $pullAll you have to match the whole object.
Apart from the syntax is not correct in your query, check how in this example the id 1 is not removed but it is the id 2. That's because $pullAll expects to match the whole object. And this other example works removing both ids.
Then, to do your query you can use $pull and $in like this:
await Model.updateMany({
"managers.userID": userID
},
{
"$pull": {
"managers": {
"userID": {
"$in": userIDs
}
}
}
})
Example here
Note how $in expects an array. If you only want to remove based on one value you can avoid $in:
await Model.updateMany({
"managers.userID": userID
},
{
"$pull": {
"managers": {
"userID": userID
}
}
})
Example here

Related

How can I mix a populated ObjectId with a string

Actually, in the database I got a job that I request with a GET route:
So when I populate candidates I got this response format :
My problem here is I don't need that "id" object, I just need a "selected_candidates" array with users inside as objects. Actually it's an object, in another object that is in an Array.
Here the code from my controller (the populate is in the jobsService):
If I change the data format of the job like that way:
...It is working great (with a path: "candidates_selected") like expected BUT I don't have that "status" string (Normal because I don't have it anymore in the DataBase. Because of ObjectId):
I would like a solution to have them both, but maybe it's the limit of noSQL?
A solution without populate but with a Loop (I don't think it's a good idea):
I think there is no convenience way to achieve it. However you may try the aggregate framework from the native MongoDB driver.
Let your Mongoose schemas be ASchema and BSchema
const result = await ASchema.aggregate([
{$addFields: {org_doc: '$$ROOT'}}, // save original document to retrieve later
{$unwind: '$candidates_selected'},
{
$lookup: {
from: BSchema.collection.name,
let: {
selected_id: '$candidates_selected.id',
status: '$candidates_selected.status',
},
pipeline: [
{
$match: {$expr: {$eq: ['$$selected_id', '$_id']}}, // find candidate by id
},
{
$addFields: {status: '$$status'} // attach status
}
],
as: 'found_candidate'
}
},
{
$group: { // regroup the very first $unwind stage
_id: '$_id',
org_doc: {$first: '$org_doc'},
found_candidates: {
$push: {$arrayElemAt: ['$found_candidate', 0]} // result of $lookup is an array, concat them to reform the original array
}
}
},
{
$addFields: {'org_doc.candidates_selected': '$found_candidates'} // attach found_candidates to the saved original document
},
{
$replaceRoot: {newRoot: '$org_doc'} // recover the original document
}
])

The date is not updated in MongoDB

When trying to update the date inside the MongoDB document, nothing is updated.
To update, I send a request to NodeJS like this:
collection.updateOne(
{ _id: ObjectId(...) },
{ $set: { 'field1.field2.0.date': new Date('2020-02-01T00:00:00Z') } },
callback
)
The task uses the npm package mongodb#2.3.26 and MongoDB 3.6.20.
I tried to send the '2020-02-01T00:00:00Z' as a value - it is not updated. I tried to update the database version - it didn't help. I tried to update the driver version - it didn't help.
It is interesting that such a request goes through Robo3T and updates the value correctly.
Could you please tell me how to update the date inside the document for MongoDB?
UPD: Structure of document
const document = {
field1: {
exp: 1,
field2: [
{
name: "test",
date: 2019-10-01 00:00:00.000Z
}
]
},
settings: {
sharedTill: 2022-10-01 00:00:00.000Z
},
updatedBy: 'Vadim Prokopchuk'
}
UPD2: Added Response from MongoDB
MongoDB returned data and does not return an error.
the syntax you're using isn't correct.
Refer the query below, it would work
collection.updateOne(
{ _id: ObjectId(...) , 'field1.field2.name':"test"},
{ $set: { 'field1.field2.$.date': new Date('2020-02-01T00:00:00Z') } },
callback
)
Here, we are finding the element of the array which matches our condition and then updating the date of that particular element of the array.

returning matching elements using MongoDB 4.0 find function

Mongodb returns non-matching elements in nested array
Here's my database sample:
const users = [{
'username':'jack',
'songs':[{
'song':'Another Love',
'likes':false
}, {
'song':"i'm into you",
'likes': true
}]
}, {
'username':'Stromae',
'songs':[{
'song':'Take me to church',
'likes':false
}, {
'song':"Habits",
'likes': true
}]
}];
I'm trying to find the following row:
const query = {'username':'Stromae' , 'songs.song':'Take me to church','songs.likes':true};
const result = await usersTable.find(query).project({'songs.$':1}).toArray();
as you see I'm trying to find a user who called "Stromae" and has a "Take me to church" song and he don't like it.
I'm expecting as result to be null, while the result is:
{ _id: 5d302809e734acbc5ffa2a8f,
songs: [ { song: 'Take me to church', likes: false } ] }
as you see from the result it ignores that I need "likes" field to be true.
As per my understanding you are trying to match data from 'songs' array which satisfying both the conditions for the 'song' and 'likes' both fields. But you haven't provided the logic like check both the fields for same array element. That's why it is checking this fields in whole 'songs' array.
To check condition for single array element you can use $elemMatch and for checking both the conditions are satisfying or not use the $and operator.
You can use your Mongo query as:
db.usersTable.find({
"username": "Stromae",
songs: {
$elemMatch: {
$and: [
{
"song": "Take me to church"
},
{
"likes": true
}
]
}
}
})
Just update your 'find' query, you might get your required result.

Mongoose update first five documents

Hello I am trying to update only first five documents from my schema using mongoose. I found a way to update documents by giving condition but cant update only first five.
I got this code
mongoose.model('person').update( {active:false} , {multi: true} , function(err,docs) { ... });
Thank you
The key here is to get the first 5 _id values matching your condition and then pass those using $in to your update selection:
mongoose.model('person').find({ "active": { "$ne": false }}).limit(5)
.exec(function(err,docs) {
var ids = docs.map(function(doc) { return doc._id } );
mongoose.model('person').update(
{ "_id": { "$in": ids } },
{ "$set": { "active": false } },
{ "multi": true },
function(err,numAffected) {
}
);
});
Also notice the correct arguments to the update statement with a "query" and "update" block. You should also be using update operators such as $set, otherwise you are overwriting the existing document content with just the contents of the "update"block.
Also see the .update() method from the core documentation.

Querying a mongo collection for objects without certain attributes using mongoose

I'm looking to write a find clause that will search for objects where a certain attribute exactly matches a condition or if the object doesn't have that attribute at all.
Currently I am using:
Card.find( { $or: [ { "playerClass": req.params.className }, { "playerClass": {'$eq': null } } ] }, function(err, docs) {
res.json(docs);
});
But this yields no results.
I tried $equals before as well to no avail -- as a newcomer to Mongo, what am I doing wrong?
Thanks
null counts for missing, so you could use $in:
Card.find({ "playerClass" : { "$in" : [null, req.params.className] } }, callback)

Resources