Mongoose中的级联样式删除
Posted
技术标签:
【中文标题】Mongoose中的级联样式删除【英文标题】:Cascade style delete in Mongoose 【发布时间】:2012-12-30 04:30:39 【问题描述】:有没有办法在 Mongoose 中删除父级的所有子级,类似于使用 mysql 的外键?
例如,在 MySQL 中,我会分配一个外键并将其设置为删除时级联。因此,如果我要删除客户端,所有应用程序和关联用户也会被删除。
从顶层:
-
删除客户端
删除抽奖活动
删除提交内容
抽奖和提交都有一个 client_id 字段。 Submissions 有一个用于sweepstakes_id 和client_id 的字段。
现在,我正在使用以下代码,我觉得必须有更好的方法。
Client.findById(req.params.client_id, function(err, client)
if (err)
return next(new restify.InternalError(err));
else if (!client)
return next(new restify.ResourceNotFoundError('The resource you requested could not be found.'));
// find and remove all associated sweepstakes
Sweepstakes.find(client_id: client._id).remove();
// find and remove all submissions
Submission.find(client_id: client._id).remove();
client.remove();
res.send(id: req.params.client_id);
);
【问题讨论】:
【参考方案1】:这是 Mongoose 的 'remove'
middleware 的主要用例之一。
clientSchema.pre('remove', function(next)
// 'this' is the client being removed. Provide callbacks here if you want
// to be notified of the calls' result.
Sweepstakes.remove(client_id: this._id).exec();
Submission.remove(client_id: this._id).exec();
next();
);
这样,当您调用client.remove()
时,会自动调用此中间件来清理依赖关系。
【讨论】:
不,在通过mongoose.model
或db.model
调用创建模型之前,您会在用于定义Client
模型的Schema
对象上调用它。
当我将它移到我的架构定义文件(模型)中时效果很好。不过,我必须将 .exec() 添加到删除调用的末尾。没有坏处,对吧?
您是对的,当您不提供回调时,您需要使用remove
执行此操作。我更新了答案。
@JohnnyHK:只有当我们调用 client.remove() 时才会调用这个方法。在我的应用程序中,我使用以下语句将其删除:Client.findByIdAndRemove(client_id,function(err)) 这个 pre hook 不会被调用吗?
@Inquisitive 对,在这种情况下不会调用钩子。仅当您在文档实例上调用 remove
时才会调用它。【参考方案2】:
如果您的引用以其他方式存储,例如,client
有一个 submission_ids
数组,那么以与接受的答案类似的方式,您可以在 submissionSchema
上定义以下内容:
submissionSchema.pre('remove', function(next)
Client.update(
submission_ids : this._id,
$pull: submission_ids: this._id ,
multi: true ) //if reference exists in multiple documents
.exec();
next();
);
这将从submission.remove()
上的clients'引用数组中删除提交的 id。
【讨论】:
请您举一个使用 OneToMany 的例子吗?如果此代码删除所有子级并更新引用和父级,我是不对的。 不确定我是否理解您的评论。但让我用例子来解释我的答案。假设有 3 个客户端文档:C1、C2、C3。并且有许多提交文件,其中一个是S273。现在 S273 的 id 存在于 C1 和 C3 的submission_ids
中(OneToMany,即客户端有很多提交)。我上面的回答解决了您要删除 S273 的情况。当您这样做时,S273 的 id 将自动从 C1 和 C3 的submission_ids
中删除。
嗯.. 我知道submissionSchema
是Client schema
上引用的子集合对吗?
是的。这是“hasMany”的方式。客户端拥有许多提交 ID 的数组。另一种是“belongsTo”方式,每个提交文档都有一个属于它的客户端id。
@FrancisRodrigues 因为它与 NoSql 而非 Mongoose 相关(它只是 mongodb 的一个节点库/包装器,以帮助简化开发)。我不确定 mongodb 有一个部分来解释这一点,但 couchbase(另一个 NoSql db)有一个超级信息文档详细解释它。 developer.couchbase.com/documentation/server/4.6/data-modeling/…, developer.couchbase.com/documentation/server/3.x/developer/…, developer.couchbase.com/documentation/server/3.x/developer/…【参考方案3】:
这是我找到的另一种方式
submissionSchema.pre('remove', function(next)
this.model('Client').remove( submission_ids: this._id , next);
next();
);
【讨论】:
删除回调中的next
是因为您无论如何都会在它下面的行中调用next
对吗?【参考方案4】:
我注意到这里的所有答案都有一个pre
分配给架构,而不是post
。
我的解决方案是:(使用 mongoose 6+)
ClientSchema.post("remove", async function(res, next)
await Sweepstakes.deleteMany( client_id: this._id );
await Submission.deleteMany( client_id: this._id );
next();
);
根据定义,post 在流程结束后执行pre => process => post
。
现在,您可能想知道这与此处提供的其他解决方案有何不同。
如果找不到服务器错误或该客户端的 id 怎么办?
在预先,它将在删除过程开始之前删除所有sweeptakes
和submissions
client
。因此,如果出现错误,最好在client
或主文档被删除后级联删除其他文档。
async 和 await 在这里是可选的。但是,它对大数据很重要。这样如果删除进度仍在进行,用户就不会得到那些“将被删除”的级联文档数据。
最后,我可能是错的,希望这对他们的代码有所帮助。
【讨论】:
userSchema.post('findOneAndDelete', async id => await Comments.deleteMany( user: id ); ); findOneAndDelete 方法在这里也有效,似乎删除方法已被弃用以上是关于Mongoose中的级联样式删除的主要内容,如果未能解决你的问题,请参考以下文章