Home > database >  How to properly delete a record and all its references in MongoDB
How to properly delete a record and all its references in MongoDB

Time:06-14

I have a user collection and I have an API thats delete the user. However, I use the user as a reference in other collections like in Posts has a userId reference, visitRequests also.

Is there a proper way to do this ? or should I just in each collection do a specific query like this ?.

await User.findOneAndDelete({ _id: id });
await visitRequestModel.findOneAndDelete(
  {_userId: id},
);
...

Also I want for example to change the status in Posts collection instead of deleting it.

And, can this be done in aggregate method instead ?

CodePudding user response:

One popular way to go about this would be to use either of MongoDB's pre/post save hooks for a remove query. But a better solution that I recommend is to use MongoDB transactions considering that the number of references to delete might grow as the application does, and also as you're looking to update the Posts' status and not delete. Transactions are very helpful if you look from the ACID perspective which is native to relational databases. To avoid inconsistencies because in your proposed approach above or even with hooks, one query may fail after others have been run and this can be very bad for your application. So transactions solve this problem, hence the way to go would be as follows:

  • Get your Mongodb connection after you must have connected to your DB. const connection = mongoose.connection;
  • You may now implement a transaction as seen below:
const session = await connection.startSession();
console.log('started new session')
// it is advisable to use a try/catch block here to handle failures and rollback
try {
  await session.startTransaction();
  console.log('transaction started'); //just some logging;
  await User.findOneAndDelete({ _id: id }, { session });
  await visitRequestModel.findOneAndDelete({ _id: id }, { session });
  // now to update your post's status
  await Posts.updateOne({ _id: id }, { status: 'newStatus' }, { session });
  await session.commitTransaction();
  console.log('transaction(delete operation) successful');

} catch (error) {
   console.log('transaction failed');
   throw new Error(err);
   await session.abortTransaction();
}
await connection.endSession();
console.log('session ended')

This way, you can guarantee your data consistency as the abortTransaction() is called if an error occurs in the process or a query fails, and everything is rolled back to the way it was before. You may as well add other queries that aren't even related to the session, just keep in mind that queries with the { session } passed as a parameter are the ones associated with the running transaction.

Note: In case you're using a self-hosted MongoDB instance(probably your local installation), you may have to configure a MongoDB replica set/sharded cluster as transactions are not supported on standalone MongoDB installations. But if you're using Atlas, then you're good to go without any more configurations.

  • more about replica sets here
  • more about ACID and MongoDB transactions here

Hope this helps...

  • Related