Consider I have this document in my MongoDB collection, Workout:
{
_id: ObjectId("60383b491e2a11272c845749") <--- Workout ID
user: ObjectId("5fc7d6b9bbd9473a24d3ab3e") <--- User ID
exercises: [
{
_id: ObjectId("...") <--- Exercise ID
exerciseName: "Bench Press",
sets: [
{
_id: ObjectId("...") <--- Set ID
},
{
_id: ObjectId("...") <--- Set ID
}
]
}
]
}
The Workout object can include many exercise objects in the exercises array and each exercise object can have many set objects in the sets array. I am trying to implement a delete functionality for a certain set. I need to retrieve the workout that the set I want to delete is stored in. I have access to the user's ID (stored in a context), exercise ID and the set ID that I want to delete as parameters for the .findOne() function. However, I'm not sure whether I can traverse through the different levels of arrays and objects within the workout object. This is what I have tried:
const user = checkAuth(context) // Gets logged in user details (id, username)
const exerciseID, setID // Both of these are passed in already and are set to the appropriate values
const workoutLog = Workout.findOne({
user: user.id,
exercises: { _id: exerciseID }
});
This returns an empty array but I am expecting the whole Workout object that contains the set that I want to delete. I would like to omit the exerciseID from this function's parameters and just use the setID but I'm not sure how to traverse through the array of objects to access it's value. Is this possible or should I be going about this another way? Thanks.
When matching against an array, if you specify the query like this:
{ exercises: { _id: exerciseID } }
MongoDB tries to do an exact match on the document. So in this case, MongoDB would only match documents in the exercises array of the exact form { _id: ObjectId("...") }. Because documents in the exercises have other fields, this will never produce a match, even if the _ids are the same.
What you want to do instead is query a field of the documents in the array. The complete query document would then look like this:
{
user: user.id,
"exercises._id": exerciseID
}
You can perform both find and update in one step. Try this:
db.Workout.updateOne(
{
"user": ObjectId("5fc7d6b9bbd9473a24d3ab3e"),
},
{
$pull: {
"exercises.$[exercise].sets": {
"_id": ObjectId("6039709fe0c7d52970d3fa30") // <--- Set ID
}
}
},
{
arrayFilters: [
{
"exercise._id" : ObjectId("6039709fe0c7d52970d3fa2e") // <--- Exercise ID
}
]
}
);