Search code examples
mongodbmongoose

Remove object id from array when referenced document expires


I have the following schema in my App:

const storySchema = new mongoose.Schema({
    createdAt: { type: Date, expires: 10000 , default: Date.now },
    title: {type: String},
    creator: {type: Mongoose.types.ObjectId, ref: "User"},
    tags: [{type: String}],
    ....
})

currentSchema.index({"createdAt": 1 },{ expireAfterSeconds: 10000 });

The code above adds an index to ensure every story document is removed after 10000 ms. This code works correctly.

However I have a user schema designed like so:

const userSchema = new mongoose.Schema({
    stories: [{ type: Mongoose.Schema.Types.ObjectId, ref: "Story" }],
    title: {type: String},
    bio: {type: String},
    ....
})

When a story is automatically removed/expired I would like to automatically remove that story from the stories array of objectids which is stored on the creators document. I tried adding a pre('remove') middleware on the User model, however this isn't fired when a document expires.


Solution

  • To use a virtual property first remove the stories field from your userSchema and then update like so:

    const userSchema = new mongoose.Schema({
        title: {type: String},
        bio: {type: String},
    },{
        toJSON: { virtuals: true },
        toObject: { virtuals: true }
    });
    
    userSchema.virtual('stories', { 
        ref: 'Story', 
        localField: '_id', 
        foreignField: 'creator' 
    });
    

    You will note the use of toJSON and toObject options set to true. This ensures your new virtual stories field will be sent to your front-end if using res.json() etc.

    Now you can use Model.populate() to get all of the documents from the stories collection where the Story.creator field matches the User._id and will ensure no expired documents reside in your user documents. Populating this way will only fetch stories that are live at the moment the query is made.

    const user = await User.findOne({bio: 'something'}).populate('stories');
    const users = await User.find().populate('stories');