在MongoDB中删除时自动删除引用对象

Pet*_*son 27 mongoose mongodb node.js

我们假设我有这样的架构:

var Person = new Schema({
    name: String
});

var Assignment = new Schema({
    name: String,
    person: ObjectID
});
Run Code Online (Sandbox Code Playgroud)

如果我删除一个人,仍然会有一些孤立的分配,引用一个不存在的人,这会在数据库中造成无关紧要的混乱.

是否有一种简单的方法可以确保在删除某个人时,对该人的所有相应引用也将被删除?

Joh*_*yHK 33

您可以在架构上添加自己的'remove'Mongoose 中间件,Person以从引用它的所有其他文档中删除该人员.在您的中间件功能中,thisPerson要删除的文档.

Person.pre('remove', function(next) {
    // Remove all the assignment docs that reference the removed person.
    this.model('Assignment').remove({ person: this._id }, next);
});
Run Code Online (Sandbox Code Playgroud)

  • 如果它包含更深的嵌套对象 id 数组怎么办?像 `data:{persons:[{ObjectID}]}` 吗? (3认同)
  • @cute_ptr 是的,您仍然需要确保您的应用程序可以处理孤立的分配情况,然后定期删除孤立的对象以保持整洁。这就是您对非关系数据库所做的事情。 (2认同)

fre*_*ish 11

如果用"简单"表示"内置",则表示没有.毕竟,MongoDB不是关系数据库.您需要实施自己的清洁机制.


rob*_*nix 8

remove()方法已弃用。

因此,在您的 Mongoose 中间件中使用“删除”可能不再是最佳实践。

Mongoose 已经创建了更新来为deleteMany()和提供钩子deleteOne()。你可以用那些代替。

Person.pre('deleteMany', function(next) {
    var person = this;
    person.model('Assignment').deleteOne({ person: person._id }, next);
});
Run Code Online (Sandbox Code Playgroud)

  • 哈哈,我用 remove 因为我是个懒惰的傻瓜!:) 复制粘贴 Johnny 的版本,只是调整了我在自己的代码中更改的部分。谢谢@PeterOlson 提出这个问题!根据您的经验,您可能已经知道了!我会尽量回答只是为了学习!鉴于钩子似乎与这些函数相关联,我推断在运行 `deleteMany` 时总是调用 `deleteMany`,而不会在通过任何其他函数(例如 `deleteOne`)删除单个人时调用。谢谢你教我!:) (2认同)

小智 6

即使引用的个人文档已被删除,您也可以保留文档不变。Mongodb 会清除指向不存在文档的引用,这不会在删除引用文档后立即发生。相反,当您对文档执行操作(例如更新)时。而且,即使在清除引用之前查询数据库,返回的也是空的,而不是空值。

第二个选项是使用$unset运算符,如下所示。

{ $unset: { person: "<person id>"} }
Run Code Online (Sandbox Code Playgroud)

请注意使用人员 ID 来表示查询中引用的值。


Prz*_*cki 5

如果有人在寻找 pre hook 但 fordeleteOnedeleteManyfunctions,这是一个对我有用的解决方案:

const mongoose = require('mongoose');
... 

const PersonSchema = new mongoose.Schema({
  name: {type: String},
  assignments: [{type: mongoose.Schema.Types.ObjectId, ref: 'Assignment'}]
});

mongoose.model('Person', PersonSchema);

.... 

const AssignmentSchema = new mongoose.Schema({
  name: {type: String},
  person: {type: mongoose.Schema.Types.ObjectId, ref: 'Person'}
});

mongoose.model('Assignment', AssignmentSchema)
...

PersonSchema.pre('deleteOne', function (next) {
  const personId = this.getQuery()["_id"];
  mongoose.model("Assignment").deleteMany({'person': personId}, function (err, result) {
    if (err) {
      console.log(`[error] ${err}`);
      next(err);
    } else {
      console.log('success');
      next();
    }
  });
});
Run Code Online (Sandbox Code Playgroud)

deleteOne在服务中的某处调用函数:

try {
  const deleted = await Person.deleteOne({_id: id});
} catch(e) {
  console.error(`[error] ${e}`);
  throw Error('Error occurred while deleting Person');
}
Run Code Online (Sandbox Code Playgroud)