问题描述
我有一个模型,我们称之为 Client
,我还有另一个模型,称为 Accounts
。它们来自不同的集合,一个客户可以有许多不同的帐户。我在客户文档中引用帐户,并从帐户文档中引用回客户。
const Client = new mongoose.Schema({
accounts: [{
type:mongoose.ObjectId,ref: 'Accounts',}],other....
})
const Accounts = new mongoose.Schema({
name:String,clientID: mongoose.ObjectId
})
所以我们可以看到它们相互引用。我这样做是为了在请求客户信息时轻松访问填充帐户等。
我想做的是当我创建一个新客户端时,我还想创建一个新的默认帐户并在 accounts
数组中引用它。我在创建新客户时尝试使用 pre hook 来创建新帐户,但是这不会使用新创建的 account
doc _id 更新 Client Account
数组。我试过使用 this.update()
Client.pre('save',async function(next,args){
if(this.isNew){
await Accounts.create({clientID:this._id})
.then(async doc=>{
console.log(doc) // this logs my account doc just fine,which means it got created
await this.update($push:{accounts:doc._id) // this doesnt seem to do anything
})
.catch(err=>next(err)
}
next()
}
所以 pre hook 几乎完成了我想要它做的事情,但是我想不出一种方法来使用新创建的 Account 文档中的信息更新我新创建的 Client 文档。它创建客户文档,并创建帐户文档。它的美妙之处在于,如果我在创建帐户文档时出错,那么由于它被原子化了,所以不会创建客户端。但可惜,没有更新的 accounts
数组...
因此,我尝试将其放入 post hook。
Client.pre('save',async function(doc,next){
await Accounts.create({clientID:doc._id})
.then(async acc=>{
await doc.update({$push:{accounts:[acc._id]}})
}).catch(err=>next(err)
}
嘿!这有效!......有点......我可以创建一个创建帐户文档的客户文档,然后更新客户端以在其accounts
数组中包含帐户_id。
但是!!!我使用这种方法的问题是它似乎没有将操作原子化。因此,如果我故意使帐户创建失败(例如通过向它传递一个非 ObjectID 参数),那么它会调用 next(err)
,它在我的 http 请求中正确返回错误消息,甚至说操作失败。但是在我的数据库中,客户端文档仍然被创建,不像在它停止整个操作的前钩子中,在后钩子中它不会“撤消”客户端的创建。
总结和解决方案
基本上我需要一种方法来更新它的 pre.('save') 钩子内的全新文档,以便它将存储我在钩子内处理的任何更改的数据。
如果我使用 post hook 来更新新文档,或者某种方式来保证操作的原子化。
我尝试过的其他事情:
我还尝试在创建 Account 文档后在 pre 钩子中使用 save() ,但这导致循环最大化文档内存,因为它刚刚成为递归
我尝试在 Accounts 模型上使用 pre-hook,这样它就会引用回 Client 模型并更新它,但这给我带来了两个问题。它不会更新新的客户端文档(因为它在技术上尚不可查询)并且如果帐户创建失败,它仍会创建客户端。
很抱歉问了这么长的问题,我感谢任何反馈或建议来解决这个问题或不同的方法来实现我的目标。如果你做到了这一点,感谢阅读!
解决方法
我的问题由几个问题组成,但我想发布解决方案。
虽然我仍然不知道如何保证 post hook 中的错误会使整个操作以原子方式运行,但解决方案非常简单。
在 pre 钩子内部,要修改帐户数组,我只需要 push() 到其中,无需尝试使用 this.set 或 this.update 或任何其他实际查询,只需直接修改 this
{
//inside Client pre hook
//create account doc
await Accounts.create(...).then(doc=>{
this.accounts.push(doc._id)
}).catch(err=>next(err)
}