Mongoose:用于级联删除的 deleteOne 中间件不起作用

not*_*not 5 middleware mongoose mongodb mongoose-deleteone

随着删除在猫鼬 5.7.13 中被弃用,我想改用 deleteOne。我需要获取已删除文档的 id,以便我可以在级联中删除其他集合中的进一步相关文档。我认为 pre 中间件挂钩上下文中的“this”是指已删除的文档,但它只是一个空对象。是否有一个规范的工作示例?我目前仍在使用 5.7.12 - 这会有所不同吗?

这是我目前正在使用的代码。问题是我无法在开始时获取 projectId,因为引用完全为空。在 post 而不是 pre 上执行此操作,或将选项切换为在查询上运行而不是文档都会产生相同的结果。

ProjectSchema.pre("deleteOne", {document:true}, (next) => {
  const projectId = this._id;
  ListModel.find({parentProject:projectId}, (err, lists) => {
    if(err){
      console.log("error cascading project delete to lists", {err});
    }
    lists.map(list => {
      ListModel.deleteOne({_id:list._id}, (err, result) => {
        if(err) {
          console.log("error on project delete cascade", {err});
        }
      });
    });
  });
});
Run Code Online (Sandbox Code Playgroud)

Ale*_*lex 10

这取决于您是在文档还是模型上调用 deleteOne。后者只是没有文件可以绑定它。

前者按您的预期为您提供文档:

const project = await ProjectModel.findOne();
project.deleteOne();
Run Code Online (Sandbox Code Playgroud)

后者为您提供查询。_id查询中没有,但它有this.op例如,在这个中间件中将是“deleteOne”:

await ProjectModel.deleteOne();
Run Code Online (Sandbox Code Playgroud)

在这种情况下获取文档 ID 的唯一方法是确保在查询中提供它:

await ProjectModel.deleteOne({_id: "alex"});
Run Code Online (Sandbox Code Playgroud)

然后你可以从过滤器的中间件中获取它:

const projectId = this.getFilter()["_id"]
Run Code Online (Sandbox Code Playgroud)

您可以query: false在中间件的第二个参数中指定,以确保在模型上调用 deleteOne 时不会调用它。所以你能做的最好的事情是:

ProjectSchema.pre("deleteOne", {document:true, query: false}, (next) => {
    const projectId = this._id;
    ....
});

ProjectSchema.pre("deleteOne", {document:false, query: true}, (next) => {
    const projectId = this.getFilter()["_id"];
    if (typeof projectId === "undefined") {
        // no way to make cascade deletion since there is no _id
        // in the delete query
        // I would throw an exception, but it's up to you how to deal with it
        // to ensure data integrity
    }
});
Run Code Online (Sandbox Code Playgroud)

请看一下v5.7.12上对应的测试:https : //github.com/Automattic/mongoose/blob/5.7.12/test/model.middleware.test.js#L436


Sul*_*Sah 5

mongoose 文档中,它说“Model.deleteOne() 不会触发 pre('remove') 或 post('remove') 挂钩。”

如果您可以使用findByIdAndDelete重构删除操作,则有解决方案,它会触发findOneAndDelete中间件,

所以我们可以将这个中间件添加到项目架构中。

项目型号:

const mongoose = require("mongoose");
const ProjectChild = require("./projectChild");

const ProjectSchema = new mongoose.Schema({
  name: String
});

ProjectSchema.post("findOneAndDelete", async function(doc) {
  console.log(doc);

  if (doc) {
    const deleteResult = await ProjectChild.deleteMany({
      parentProject: doc._id
    });

    console.log("Child delete result: ", deleteResult);
  }
});

module.exports = mongoose.model("Project", ProjectSchema);
Run Code Online (Sandbox Code Playgroud)

项目子模型:

const mongoose = require("mongoose");

const projectChildSchema = new mongoose.Schema({
  name: String,
  parentProject: {
    type: mongoose.Schema.Types.ObjectId,
    ref: "Project"
  }
});

module.exports = mongoose.model("ProjectChild", projectChildSchema);
Run Code Online (Sandbox Code Playgroud)

我创建了一个这样的项目:

{
    "_id": "5dea699cb10c442260245abf",
    "name": "Project 1",
    "__v": 0
}
Run Code Online (Sandbox Code Playgroud)

并为此项目创建了 2 个项目子项目:

儿童 1

{
    "_id": "5dea69c7b10c442260245ac0",
    "name": "Child 1 (project 1)",
    "parentProject": "5dea699cb10c442260245abf",
    "__v": 0
}
Run Code Online (Sandbox Code Playgroud)

儿童 2

{
    "_id": "5dea69e8b10c442260245ac1",
    "name": "Child 2 (project 1)",
    "parentProject": "5dea699cb10c442260245abf",
    "__v": 0
}
Run Code Online (Sandbox Code Playgroud)

我创建了一个示例路由来按项目 ID 删除项目,如下所示:

router.delete("/project/:id", async (req, res) => {
  const result = await Project.findByIdAndDelete(req.params.id);

  res.send(result);
});
Run Code Online (Sandbox Code Playgroud)

当我向该路由发送 DELETE 请求时,我们在控制台中看到以下信息:

console.log(doc);

{ _id: 5dea699cb10c442260245abf, name: 'Project 1', __v: 0 }
Run Code Online (Sandbox Code Playgroud)

console.log("Child delete result: ", deleteResult);

Child delete result:  { n: 2, ok: 1, deletedCount: 2 }
Run Code Online (Sandbox Code Playgroud)

因此,当我们删除项目时,我们可以删除该项目的 2 个子项目。

作为替代方案,您也可以使用findOneAndRemove,它会触发 findOneAndRemove 后中间件。

因此,在 ProjectSchema 中,我们像这样替换后中间件:

ProjectSchema.post("findOneAndRemove", async function(doc) {
  console.log(doc);

  if (doc) {
    const deleteResult = await ProjectChild.deleteMany({
      parentProject: doc._id
    });

    console.log("Child delete result: ", deleteResult);
  }
});
Run Code Online (Sandbox Code Playgroud)

当我们使用 findOneAndRemove 操作时,结果将与第一个替代方案相同:

const result = await Project.findOneAndRemove({ _id: req.params.id });
Run Code Online (Sandbox Code Playgroud)