Automatically remove referencing objects on deletion in MongoDB

Viewed 32136

Let's suppose I have a schema like this:

var Person = new Schema({
    name: String
});

var Assignment = new Schema({
    name: String,
    person: ObjectID
});

If I delete a person, there can still be orphaned assignments left that reference a person that does not exist, which creates extraneous clutter in the database.

Is there a simple way to ensure that when a person is deleted, all corresponding references to that person will also be deleted?

7 Answers

In case if anyone looking for the pre hook but for deleteOne and deleteMany functions this is a solution that works for me:

const mongoose = require('mongoose');
... 

const PersonSchema = new mongoose.Schema({
  name: {type: String},
  assignments: [{type: mongoose.Schema.Types.ObjectId, ref: 'Assignment'}]
});

mongoose.model('Person', PersonSchema);

.... 

const AssignmentSchema = new mongoose.Schema({
  name: {type: String},
  person: {type: mongoose.Schema.Types.ObjectId, ref: 'Person'}
});

mongoose.model('Assignment', AssignmentSchema)
...

PersonSchema.pre('deleteOne', function (next) {
  const personId = this.getQuery()["_id"];
  mongoose.model("Assignment").deleteMany({'person': personId}, function (err, result) {
    if (err) {
      console.log(`[error] ${err}`);
      next(err);
    } else {
      console.log('success');
      next();
    }
  });
});

Invoking deleteOne function somewhere in service:

try {
  const deleted = await Person.deleteOne({_id: id});
} catch(e) {
  console.error(`[error] ${e}`);
  throw Error('Error occurred while deleting Person');
}

You can leave the document as is, even when the referenced person document is deleted. Mongodb clears references which point to non-existing documents, this doesn't happen immediately after deleting the referenced document. Instead, when you perform action on the document, e.g., update. Moreover, even if you query the database before the references are cleared, the return is empty, instead of null value.

Second option is to use $unset operator as shown below.

{ $unset: { person: "<person id>"} }

Note the use of person id to represent the value of the reference in the query.

you can use soft delete. Do not delete person from Person Collection instead use isDelete boolean flag to true.

you can simply call the model that needs to be deleted and delete that document like this:

PS: This answer is not specific to the question schema.

const Profiles = require('./profile');

userModal.pre('deleteOne', function (next) {
  const userId = this.getQuery()['_id'];
  try {
    Profiles.deleteOne({ user: userId }, next);
  } catch (err) {
    next(err);
  }
});

// in user delete route

exports.deleteParticularUser = async (req, res, next) => {
  try {
    await User.deleteOne({
      _id: req.params.id,
    });

    return res.status(200).json('user deleted');
  } catch (error) {
    console.log(`error`, error);
    return next(error);
  }
};
Related