$push into deeply nested array mongoose - node.js

my userSchema is as follows...
const userSchema = new mongoose.Schema({
firstName: { type: String },
lastName: { type: String },
movies: [
{
name: String,
duration: Number,
actors: [{ name: String, age: Number }]
}
],
});
In my NodeJS app with express I am trying to update my actors array to have another actor with value stroking from req.body.
I thought that I could do something like this...
await User.updateOne(
{
_id: req.user.id
movies._id: req.params.id
},
{
$push: { 'movies.actors': req.body }
}
)
I thought this would push an actor into the specified movie req.params.id but does nothing instead.

Try using positional operator $ in this way:
db.collection.update({
"_id": 1,
"movies._id": 1
},
{
"$push": {
"movies.$.actors": req.body
}
})
Note the only diference is the use of $. But with positional operator you are saying mongo where push the new data.
As docs says:
The positional $ operator identifies an element in an array to update without explicitly specifying the position of the element in the array.
So you can update the element movies.actors pushing new data without knowin the position of the element.
Example here

Try this:
await user.updateOne(
{$and:[{_id: req.user.id},{movie.name: 'I am Legend'}]},
{$set: { movies.actors:req.body}},
);

Related

How can I return all the subdocuments for an array of objects in Mongoose?

I'm trying to search through the document TreatmentPlan for the subdocument in treatments that matches the clientId provided:
This is the TreatmentPlan model:
const treatmentPlanSchema = mongoose.Schema({
id: String,
clientId: String,
startDate: Date,
clientGoals: String,
objectivesOfTreatmentPlan: String,
conclusionOfTreatmentPlan: String,
endDate: Date,
treatments: [treatmentSchema]
})
This is the treatments subdocument:
const treatmentSchema = mongoose.Schema({
id: String,
clientId: String,
date: String,
time: String,
price: String
})
And this is the call I'm trying to make in Node:
export const getTreatmentsByClientId = async (req, res) => {
const { clientId: id } = req.params
try {
const result = await TreatmentPlan.find({clientId: id})
const treatments = result.treatments.find(({clientId})=>clientId === id)
console.log(treatments)
} catch (error) {
console.log(error.message)
}
}
When I run this, the error I get is "cannot read property 'find' of undefined."
I ran a similar function on a different array and it worked, but it doesn't seem to work for this array. Any ideas how to make this work?
You can use this find query:
db.collection.find({
"clientId": 1,
"treatments.clientId": 1
},
{
"_id": 0,
"treatments.$": 1
})
Where in the projection you are telling mongo "give me the treatments where the condition given (i.e. treatments.clientId: 1) is true".
But be careful, this only returns the first match. Check this example where there are two clientId but only one is returned.
If there are more objects you can use $filter in an aggregate query like this:
First $match by "parent" clientId and then filter the treatments array to get only objects where clientId is the desired one.
db.collection.aggregate([
{
"$match": {
"clientId": 1
}
},
{
"$project": {
"_id": 0,
"treatments": {
"$filter": {
"input": "$treatments",
"cond": {"$eq": ["$$this.clientId",1]}
}
}
}
}
])
Example here

MongoDB: add object to subarray if not exists

I searched many questions here and other articles on the web, but they all seem to describe somehow different cases from what I have at hand.
I have User schema:
{
username: { type: String },
lessons: [
{
lesson: { type: String },
result: { type: String }
}
]
}
I want to add new element into lessons or skip, if there is already one with same values, therefore I use addToSet:
const dbUser = await User.findOne({ username })
dbUser.lessons.addToSet({ lesson, result: JSON.stringify(result) })
await dbUser.save()
However it makes what seems to be duplicates:
// first run
[
{
_id: 60c80418f2bcfe5fb8f501c1,
lesson: '60c79d81cf1f57221c05fdac',
result: '{"correct":2,"total":2}'
}
]
// second run
[
{
_id: 60c80418f2bcfe5fb8f501c1,
lesson: '60c79d81cf1f57221c05fdac',
result: '{"correct":2,"total":2}'
},
{
_id: 60c80470f2bcfe5fb8f501c2,
lesson: '60c79d81cf1f57221c05fdac',
result: '{"correct":2,"total":2}'
}
]
At this point I see that it adds _id and thus treats them as different entries (while they are identical).
What is my mistake and what should I do in order to fix it? I can change lessons structure or change query - whatever is easier to implement.
You can create sub-documents avoid _id. Just add _id: false to your subdocument declaration.
const userSchema = new Schema({
username: { type: String },
lessons: [
{
_id: false,
lesson: { type: String },
result: { type: String }
}
]
});
This will prevent the creation of an _id field in your subdoc, and you can add a new element to the lesson or skip it with the addToSet operator as you did.

remove a particular array element from mongodb on clicking the element

I'm using this code for removing a particular array element stored in MongoDB when clicked that from the app. But this code is not working.
schema structure looks like this -
const tagsSchema = new Schema({
category: {
type: String,
required: true
},
tname: { type: Array }
}, { _id: true });
Below is the code I'm using for removing array element from db -
Tags.updateOne({ tname: req.params.name }, { $pull: { _id: [req.params.id] } })
For example - "tname": "technical", "nontechnical"
Now, technical is being clicked in the app to remove, but with the code I'm using it's not getting removed.
you can use directly your element_value without [] after your array field, like below..
Tags.updateOne({tname: req.params.name}, { $pull: { your_array_field: req.params.id } } )
You have to find the specific tag by its "_id" and then remove the particular name from the "tname" array.
Tags.updateOne({ _id: req.params.id }, { $pull: { tname: req.params.name } })

MongoDB - update data in array of objects within object

I have a document in mongoDB structured like that
_id: ObjectId("generatedByMongo"),
name: {
required: true,
type: String,
trim: true
},
last: {
required: true,
type: String,
trim: true
},
grades: [{
grade: {
_id: ObjectId(""),
grade: Number,
date: date
}
}]
And to server I send array of objects containing 3 fields
[
{studentId}, {gradeId}, {newGrade}
]
What I'm trying to accomplish is I want to find in within that user collection grade with given gradeId and update it's value to newGrade. As far as I tried to do that I have done this
router.patch('/students/updateGrade',async(req,res) => {
const studentId = req.body.updateGradeArray[0].studentId;
const gradeId = req.body.updateGradeArray[0].gradeId;
const newGrade = req.body.updateGradeArray[0].newGrade;
try {
const student = await Student.find({_id: studentId})
.select({'grades': {$elemMatch: {_id: gradeId}}});
} catch(e) {
console.log(e);
}
}
);
If you intend to update just grade.grade(the number value), try this:
Student.updateOne(
// Find a document with _id matching the studentId
{ "_id": studentId },
// Update the student grade
{ $set: { "grades.$[selectedGrade].grade": newGrade } },
{ arrayFilters: [{ "selectedGrade._id": gradeId }] },
)
Why this should work:
Since you are trying to update a student document, you should be using one of MongoDB update methods not find. In the query above, I'm using the updateOne method. Inside the updateOne, I am using a combination of $set and $[identifier] update operators to update the student grade.
I hope this helps✌🏾

Can't push items in mongo array

I can't push items into MongoDB array every time that i try to push a new element it creates an empty object and i cant figure out why,
I already used the
Collection.Array.push({element})&
Collection.save()
but i cant figure out a solution
This is My Schema
const Schema = mongoose.Schema;
var ParticipantSchema = new Schema({
nom:{Type:String},
prenom:{Type:String},
email:{Type:String}
})
var CompetitionSchema = new Schema({
nom:String,
date:Date,
place:String,
participant :[ParticipantSchema]
})
module.exports = mongoose.model("Competition",CompetitionSchema);
This is my funtion
exports.addParticipant=function(req,res){
var newParticipant={
"nom":req.body.nom,
"prenom":req.body.prenom,
"email":req.body.email
}
Competition.updateOne(
{ _id:req.body.id},
{ $push: { participant: newParticipant } },
(err,done)=>{
return res.json(done)
}
);
}
the result is always an empty object like below
{
"_id": "5ded0eeb85daa100dc5e57bf",
"nom": "Final",
"date": "2019-01-01T23:00:00.000Z",
"place": "Sousse",
"participant": [
{
"_id": "5ded0eeb85daa100dc5e57c0"
},
{
"_id": "5dee3c1b08474e27ac70672e"
}
],
"__v": 0
}
There is no problem in your code, the only problem is that in schema definition you have Type, but it must be type.
If you update your ParticipantSchema like this, it will work:
var ParticipantSchema = new Schema({
nom: { type: String },
prenom: { type: String },
email: { type: String }
});
You are using another Schema in the Array. This results in so-called subdocuments (https://mongoosejs.com/docs/subdocs.html). Mongoose does not populate subdocuments by default. So all you see is just the _id. You can use the populate method to see all subdocuments in detail. ( https://mongoosejs.com/docs/populate.html ) .
Example :
Competition.
find({}).
populate('participant').
exec(function (err, comps) {
//
});
You can either use populate on the Model or on the Document. For populating a document, take a look at https://mongoosejs.com/docs/api.html#document_Document-populate . There is also a auto-populate plugin available via npm but in most cases it's not necessary : https://www.npmjs.com/package/mongoose-autopopulate .

Resources