Mongoose - Accessing and removing a subdocument by _ID within an array of objects

76 views Asked by At
party

{
  _id: new ObjectId('6570d8aef19ec1c690cc8d34'),
  name: 'party',
  items: [
    {
      name: 'Welcome to your todo list',
      _id: new ObjectId('6570d8aaf19ec1c690cc8d31')
    },
    {
      name: 'Hit the + button to add a new item.',
      _id: new ObjectId('6570d8aaf19ec1c690cc8d32')
    },
    {
      name: 'Hit this to delete an item.',
      _id: new ObjectId('6570d8aaf19ec1c690cc8d33')
    },
    { name: 'heya', _id: new ObjectId('6570ee634c6ed501ba04974a') },
    { name: 'hoiya', _id: new ObjectId('6570eea34c6ed501ba049778') },
    { name: 'heyty', _id: new ObjectId('6570ef6d934fb47039850682') },
    { name: 'dsfdsf', _id: new ObjectId('6570efcad07077740a80ce5a') },
    { name: 'hey', _id: new ObjectId('6570f193c6b50f3c3d692951') },
    { name: 'hiii', _id: new ObjectId('6570f1bcc6b50f3c3d69299b') }
  ],
}

I'm trying to access the _id of the inner array (items) to apply the delete on that ID. I want to delete the item by _id but unfortunately I cannot wrap my head around the documentation/examples on the Mongoose docs website. I am currently using Mongoose

"mongoose": "^8.0.2"

so I would prefer a solution with the async/await method on NodeJS.

// Equivalent to `parent.children.pull(_id)`
parent.children.id(_id).deleteOne();

Apparently using both of those does not work for me it returns undefined (I am probably not doing it right).

I am expecting to use the _id of items to delete an item by ID. Please suggest Mongoose methods only.

2

There are 2 answers

0
jQueeny On BEST ANSWER

Mongoose has two mongoose-specific ways to handle this kind of operation. The first is the least efficient but easiest to understand.

  1. Using MongooseArray.prototype.pull()

This is a three step process that involves finding the document you want, pulling it from the array and then saving it back to the database. As you are making two calls to the db it is less efficient but logically clear to read:

// 1. Get the document that contains the object you want to delete
const document = await PartyModel.findOne({"items._id": _id});
// 2. Pull that object from the array
document.items.pull({_id: _id});
// 3. Save the changes
await document.save();
  1. Using Model.findOneAndUpdate()

This is more efficient because it is a one step process that involves finding the document you want and passing in an update document. That update document can be a mongodb operation such as the $pull operator in @Yong Shun answer.

The major difference between findOneAndUpdate and updateOne is that findOneAndUpdate will return the actual document back to you in either the state it was before you updated or the state after you updated which is configurable by you. Whereas, updateOne will return an update status document contanining useful properties such as acknowledged and modifiedCount, but not any of the document data fields or values. If you need to return the document back to your front-end then use findOneAndUpdate otherwise it makes no difference:

const document = await PartyModel.findOneAndUpdate({
   'items._id': _id
},{
   $pull: {
      "items": {
         _id: _id
      }
   }
}, { new:true }); //< This option says return me the document after the changes are made
2
Yong Shun On

You need the $pull operator to remove the matched object from the items array.

await <Party model>.updateOne({
  "items._id": _id
},
{
  $pull: {
    "items": {
      _id: _id
    }
  }
})

Demo @ Mongo Playground