Mongoose - delete subdocument array item - javascript

I have this little schema for users:
{
username: String,
contacts: Array
}
So for example some user's contacts will look like this:
{
username: "user",
contacts: [{'id': ObjectId('525.....etc'), 'approved': false}, {'id':ObjectId('534.....etc'), 'approved': true}]
}
Now I need to delete an item from contacts so I do:
model.findByIdAndUpdate(23, {'$pull': {
'contacts':{'id':'525.....etc'}
}});
but seems not working, no errors but it doesn't gets deleted, I just would like to return this document for the user:
{
username: "user",
contacts: [{'id':ObjectId('534.....etc'), 'approved': false}]
}
how to achieve this?

The $pull operator actually just performs the conditions on the array element on which it is operating. It seems that your question might not actually show that you are probably working with an ObjectId value that mongoose creates by default for all array fields.
So you could to your query like this, after importing the ObjectId creation method:
model.findByIdAndUpdate(23, {
'$pull': {
'contacts':{ '_id': new ObjectId(someStringValue) }
}
});
Or in fact you can actually define your "schema" a little better, and mongoose will actually "autocast" the ObjectId for you based on the "type" defined in the schema:
var contactSchema = new Schema({
approved: Boolean
});
var userSchema = new Schema({
username: String,
contacts: [contactSchema]
});
This allows mongoose to "follow the rules" for strictly typed field definitions. So now it knows that you actually have an _id field for each element of the contacts array, and the "type" of that field is actually an ObjectId so it will automatically re-cast "String" values supplied as a true ObjectId.

finaly!
MongoDB:
"imgs" : {"other" : [ {
"crop" : "../uploads/584251f58148e3150fa5c1a7/photo_2016-11-09_21-38-55.jpg",
"origin" : "../uploads/584251f58148e3150fa5c1a7/o-photo_2016-11-09_21-38-55.jpg",
"_id" : ObjectId("58433bdcf75adf27cb1e8608")
}
]
},
router.get('/obj/:id', function(req, res) {
var id = req.params.id;
Model.findOne({'imgs.other._id': id}, function (err, result) {
result.imgs.other.id(id).remove();
result.save();
});

Related

Using findById to find the id of a schema in an array

Hey I was wondering how do I use findById for a schema inside an array? For example, I have the following Schema:
const GameSchema = new mongoose.Schema({
users: [
{
user: { type: mongoose.Schema.ObjectId, ref: 'User' },
role: {
type: String,
required: true,
enum: ['user', 'moderator', 'creator'],
default: 'user',
},
},
]
}]
I want to find the user with a mongoose function like findById, such as the following:
const user = await game.users.findById({ user: req.user.id })
It doesn't seem to work since users is not a mongodb model. I know I can find the user by using find() like the following:
const user = await game.users.find(
(gameUser) => gameUser.user == req.user.id
)
The only problem is that the type of gameUser and req.user.id is not the same and I can't use '==='. Is there some way to go through the array and use the mongoose function findById?
As docs explains, findById method:
Finds a single document by its _id field
So you have to use findOne() instead of findById().
Also, to return only one field from the entire array you can use projection into find.
Check this example. This query find an object by its id (i.e. user field) and return only the object, not the whole array.
db.collection.find({
"users": { "$elemMatch": { "user": 1 } }
},
{
"users.$": 1
})
Using mongoose you can do:
yourModel.findOne(({
"users": { "$elemMatch": { "user": 1 } }
},
{
"users.$": 1
})).then(result => {
console.log(result)
}).catch(e => {
// error
})

Mongo - Update nested array of objects by _id

How can I access an array of object with their own _id and update it with Mongo/Mongoose?
Take a look to my update query and check if there's something wrong, because this code doesn't return any error, but it doesn't really update the field
modelUser.findOneAndUpdate(
{ userName: body.author, "portfolio._id": body.id },
{ new: true },
{
$set: { //I thing the problem it's over here
"portfolio.$.profitLoss": profitLoss,
"portfolio.$.percentage": percentage
}
},
(err, user) => {
if (err) {
console.log(err);
}
console.log(`Done`);
}
);
This is my User Schema:
const userSchema = new Schema({
...stuff,
portfolio: [
{
coin: String,
amount: String,
price: String,
bought: Date,
profitLoss: String,
percentage: String
}
],
});
Basically i think mongo just don't know which of these sub documents should update, I don't know if there's something like another findOneAndUpdate for sub object/document by id.
Just changed findOneAndUpdate to updateOne and everything works.

How do i tell Mongoose NOT to save a field?

How do i tell Mongoose not to save the age field if it's null or undefined?
Or could i do this in Express somehow?
Express
router.put('/edit/:id', function(req, res) {
Person.findOneAndUpdate({ _id: req.params.id }, {
name: req.body.updateData.name,
age: req.body.updateData.age
}, { new: true });
})
Mongoose Schema
var PersonSchema = new Schema({
name: { type: String},
age: {type: String}
})
An explination (if u ask why i need this)
I'm using the same html template for new person and edit person. When i create a new person, Mongoose will save just the name field if i leave the age field empty. But when i use the edit template, Mongoose will always set the age field as null, even if the field is empty. I can't think of anything to stop this.
You could manage your request data before the db update, like:
router.put('/edit/:id', function(req, res) {
let update = {name: req.body.updateData.name};
if (req.body.updateData.age != "") {
update.age = req.body.updateData.age;
}
Person.findOneAndUpdate({ _id: req.params.id }, update, { new: true });
})
#Aioros, you answer gave me an idea, however i found this solution to match my issue. I'm just deleteing the null or undefined object elements before they are send to Mongoose.
let update = {
name: req.body.updateData.name,
age: req.body.updateData.age
};
if (update.age === null || update.age === undefined) delete update.age
Person.findOneAndUpdate({ _id: req.params.id }, update, { new: true });

Prevent duplicates in an array of objects

I'm learning Node / Mongoose, and trying to figure out how to prevent duplicates in an array of ListItems:
var listitemSchema = new mongoose.Schema({
name: String,
rank: Number
});
...which exist inside a user:
var userSchema = new mongoose.Schema({
username: String,
password: String,
list: [{
type: mongoose.Schema.Types.ObjectId,
ref: "ListItem"
}]
});
...based on whether the user already has the name within his/her list. I was looking into $addtoset but can't really figure out how to put conditions when dealing with an object. The docs weren't very helpful and I can't seem to find similar examples online.
Right now my 'POST' request is a mess and looks like this:
router.post("/editlist", isLoggedIn, function(req,res){
User.findById(req.user._id).populate("list").exec(function(err , user) {
if(err) {
console.log(err);
res.redirect("/editlist");
} else {
//prevent duplicates based on username
User.update(
{ "_id": req.user._id },
{ $addToSet: { "streamName": req.body.listitem.name}
});
res.redirect("/watch");
}
});
});
Can anyone send me on my way? My $addToSet isn't pushing items onto the list array.
According to the $addToSet documentation, the operator is used in this form:
{ $addToSet: { field1: value1, ... } }
where field is an Array.
To add an item to the list array, we do:
User.update({
{ "_id": req.user._id },
{ $addToSet: { list: {"streamName": req.body.listitem.name } }}
});
You'll need to update the userSchema definition:
var userSchema = new mongoose.Schema({
username: String,
password: String,
list: [listitemSchema]
});

Populating an array when creating a record in MongoDB with mongoose

I am writing an application that keeps track of members and their certifications. Each member has some arrays that refer to other models as described in the Mongoose population docs.
Member schema
{
name: {
first: String,
last: String
},
unit: Number,
// more stuff
class_year: Number,
campus_box: String,
campus_address: String,
// more stuff
emails: [{ type: mongoose.Schema.Types.ObjectId, ref: 'Email' }],
certifications: [{ type: mongoose.Schema.Types.ObjectId, ref: 'Certification' }],
service_credits: [{ type: mongoose.Schema.Types.ObjectId, ref: 'ServiceCredit' }]
}
Certification schema
{
type: String,
issue: Date,
expiry: Date,
number: String,
_member: { type: mongoose.Schema.Types.ObjectId, ref: 'Member' }
}
The schemata for Email and ServiceCredit are similar in that they both have the _member field.
When I create an Email, it is added to the Email model and I can subsequently perform
var Email = mongoose.model('Email');
Email.findOne().populate('_member').exec(function (err, email) {
console.log(email._member.name.first);
});
However, it is not added to the array in the Member schema, because when I run
var Member = mongoose.model('Member');
Member.findOne().populate('emails').exec(function (err, member) {
res.json(200, member);
});
I get emails: [] in the JSON, when there is clearly an Email associated with that member.
I have read on the Mongoose API that the document itself needs to be populated, so I'm trying to do what is referenced at http://mongoosejs.com/docs/api.html#document_Document-populate with the following:
var Member = mongoose.model('Member');
Member.findOne({ _id: req.session.member._id }, function (err, member) {
member.populate('emails', function (err) {
console.log('populated emails');
});
});
But I get the error: (member's JSON data) has no method 'populate'. I know I'm trying to run populate on the wrong thing, but I can't figure out what the correct way would be.
I've also encountered issues with populating array, the schema you defined will create a sub-doc for each array element (try dumping the array).
Try changing the schema to:
emails: [ type: mongoose.Schema.Types.ObjectId ]
and use
Member.findOne().populate({ path: 'emails', model: 'Email' }).exec(function (err, member) {
res.json(200, member);
});
to populate the array.

Categories