我正在尝试使用pre('findOneAndUpdate')
更新icon
文档的Meeting
属性。此更新基于yearlymeeting
属性的预先存在的值(请参见下文)。
由于pre
上没有post
和save()
update()
个挂钩,我似乎无法访问原始文档。然而,这对我正在尝试的操作至关重要。有没有办法解决?
例如,我可以在pre('save')
上完成我的目的,如下所示:
meetingSchema.pre('save', function(next) {
const yearlymeetingSlug = this.yearlymeeting[0].toLowerCase().replace(/[^A-z0-9]/g, '');
this.icon = `${yearlymeetingSlug}.png`
next();
});
我希望能做的是这样的事情:
meetingSchema.pre('findOneAndUpdate', function(next) {
const yearlymeetingSlug = originalDocument.yearlymeeting[0].toLowerCase().replace(/[^A-z0-9]/g, '');
this.icon = `${yearlymeetingSlug}.png`
next();
});
我理解pre(this
)中的findOneAndUpdate
是指查询,而不是存储的文档本身。有没有办法访问该文档,以便我可以根据icon
的存储值更新yearlymeeting
?
答案 0 :(得分:2)
不可能通过中间件。首先查询doc,然后分别更新doc的特定版本以防止竞争条件。
无法按照this issue on the Mongoose Github (from the main dev)的方式进行操作:
按设计 - 正在更新的文档可能甚至不在服务器的内存中。为了做到这一点,mongoose必须在执行update()之前执行findOne()来加载文档,这是不可接受的。
设计是通过添加或删除过滤器,更新参数,选项等来操作查询对象。例如,使用find()和findOne()自动调用.populate(),设置multi:true默认情况下,选项适用于某些型号,访问控制和其他可能性。
findOneAndUpdate()有点用词不当,它使用底层的mongodb findAndModify命令,它与findOne()+ update()不一样。作为一个单独的操作,它应该有自己的中间件。
在此之后,问题线程中没有其他建议可以访问中间件本身内部的原始文档。
我所看到的(以及我自己必须做多次),只需要在更新之前查询文档(当然,这可能导致竞争条件取决于谁是更新文档,以及何时,但你可以通过查询文档的特定版本来解决这个问题 - 一种“乐观锁定”):
let meeting = yield Meeting.findOne({}).exec()
let update = {}
// ... some conditional logic to figure out which icon to set
update.icon = // whatever
yield Meeting.update({ _id: meeting._id, version: meeting.version }, update)
当然,假设您的架构中有“版本”字段。这种锁定将阻止您更新旧版本的文档。如果您要使用这种版本控制,您可能还需要添加一些中间件,以便在更新/保存文档时更新文档版本。
你也可以使用更天真的实现,你不使用锁定,这在你的特定商业案例中可能没问题,只要你知道竞争条件的可能性和风险。 / p>
答案 1 :(得分:0)
这可能不是最佳解决方案,但我确实找到了一种方法让它发挥作用。我使用控制器而不是架构预挂钩。这是我的更新控制器现在的样子:
exports.updateMeeting = async (req, res) => {
const _id = req.params.id
let meeting = await Meeting.findOneAndUpdate({ _id }, req.body, {
new: true,
runValidators: true
});
/* New Code: */
const yearlymeetingSlug = meeting.yearlymeeting[0].toLowerCase().replace(/[^A-z0-9]/g, '');
meeting.icon = `${yearlymeetingSlug}.png`;
meeting.save();
req.flash('success', 'meeting successfully updated!');
res.redirect(`/meetings/${meeting.slug}`);
};
我欢迎您就此解决方案遇到的任何问题提出反馈意见。