如何在Mongoose中当超过expires_at时自动将expired字段设为true?
问题描述
我已经实现了投票(Poll)文档中expires_at字段的计算逻辑(基于创建时间和管理员设置的分钟级duration),现在需要实现:当当前时间超过expires_at时,自动将布尔类型的expired字段设为true。目前仅能在保存文档时触发expired的更新逻辑,不清楚如何让Mongoose在时间到达expires_at时自动触发更新。
现有代码如下:
const PollSchema = new mongoose.Schema({ question: { type: String, required: true, }, options: { type: Array, required: true, }, expires_at: { type: Date, }, expired: { type: Boolean, default: false // how I can update this value to true when now > expires_at; }, duration: { type: Number, required: true, }, }); PollSchema.pre('save', function (next) { const poll = this; poll.expired = expired(poll); next(); }); PollSchema.pre('save', function (next) { const poll = this; var now = new Date(); poll.expires_at = now.setMinutes(now.getMinutes() + poll.duration); next(); }); // I am triggering this function on save, but I think it should be triggered when now > poll.expires_at; PollSchema.pre('save', function (next) { const poll = this; const now = new Date(); poll.expired = now > poll.expires_at; next(); });
可行解决方案
1. 查询时动态计算(最简单推荐)
没必要把expired存在数据库里,每次查询时直接通过expires_at判断状态,或者用Mongoose虚拟字段实现:
// 定义虚拟字段,不存储到数据库,每次访问时实时计算 PollSchema.virtual('expired').get(function() { return new Date() > this.expires_at; }); // 确保转换为JSON/对象时返回虚拟字段 PollSchema.set('toJSON', { virtuals: true }); PollSchema.set('toObject', { virtuals: true });
如果一定要把expired存在数据库,可以在查询前先批量更新符合条件的文档:
async function getPolls() { // 更新所有已过期但未标记的投票 await Poll.updateMany( { expires_at: { $lt: new Date() }, expired: false }, { $set: { expired: true } } ); return Poll.find(); }
2. 定时任务主动扫描
用Node.js定时任务库(比如node-schedule)定期扫描数据库,主动更新过期文档:
首先安装依赖:
npm install node-schedule
然后编写定时任务:
const schedule = require('node-schedule'); // 每分钟执行一次扫描更新 schedule.scheduleJob('* * * * *', async () => { await Poll.updateMany( { expires_at: { $lt: new Date() }, expired: false }, { $set: { expired: true } } ); });
这种方式能保证数据库里的expired字段实时同步,适合需要基于该字段做统计或查询过滤的场景。
3. 查询中间件自动触发更新
给find、findOne等查询方法添加预中间件,每次查询前自动更新过期文档:
PollSchema.pre(['find', 'findOne', 'findById'], async function(next) { // 批量更新已过期但未标记的文档 await this.model.updateMany( { expires_at: { $lt: new Date() }, expired: false }, { $set: { expired: true } } ); next(); });
这样每次查询都会先同步过期状态,保证返回结果的准确性。
内容的提问来源于stack exchange,提问作者Yusuf
相关产品推荐
相关产品推荐

