Clean up dead references with Mongoose populate()

2.6k views Asked by At

If a user has an array called "tags":

var User = new Schema({
    email: {
        type: String,
        unique: true,
        required: true
    },
    tags: [{
        type: mongoose.Schema.Types.ObjectId,
        ref:'Tag',
        required: true
    }],
    created: {
        type: Date,
        default: Date.now
    }
});

and I do a populate('tags') on a query:

User.findById(req.params.id)
    .populate("tags")
    .exec(function(err, user) { ... });

If one of the tags in the list has actually been deleted, is there a way to remove this dead reference in "tags"?

Currently, the returned user object IS returning the desired result -- ie. only tags that actually exist are in the tags array... however, if I look at the underlying document in mongodb, it still contains the dead tag id in the array.

Ideally, I would like to clean these references up lazily. Does anyone know of a good strategy to do this?

3

There are 3 answers

0
Arkadii Berezkin On BEST ANSWER

I've tried to find some built-in way to do that but seems that mongoose doesn't provide such functionality.

So I did something like this

User.findById(userId)
    .populate('tags')
    .exec((err, user) => {
        user.tags = user.tags.filter(tag => tag != null);

        res.send(user); // Return result as soon as you can
        user.save(); // Save user without dead refs to database
    })

This way every time you fetch user you also delete dead refs from the document. Also, you can create isUpdated boolean variable to not call user.save if there was no deleted refs.

const lengthBeforeFilter = user.tags.length;
let isUpdated = user.tags.length;

user.tags = user.tags.filter(tag => tag != null);
isUpdated = lengthBeforeFilter > user.tags.length;

res.send(user);

if (isUpdated) {
    user.save();
}
2
Camo On

Assuming you delete these tags via mongoose, you can use the post middleware.

This will be executed after you've deleted a tag.

tagSchema.post('remove', function(doc) {
     //find all users with referenced tag
     //remove doc._id from array
});
0
Rahman Rezaee On

its sample retainNullValues: true

Example:

 User.findById(req.params.id)
 .populate({
      path: "tag",
      options: {
        retainNullValues: true
      }
    })