5

在 Mongoose 5.7.13 中不推荐使用 remove ,我想改用 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});
        }
      });
    });
  });
});

4

2 回答 2

10

这取决于您是在文档还是模型上调用 deleteOne。后者只是没有将其绑定到的文档。

前者为您提供您期望的文件:

const project = await ProjectModel.findOne();
project.deleteOne();

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

await ProjectModel.deleteOne();

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

await ProjectModel.deleteOne({_id: "alex"});

然后你可以从过滤器的中间件中得到它:

const projectId = this.getFilter()["_id"]

您可以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
    }
});

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

于 2019-12-06T13:50:22.550 回答
5

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

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

所以我们可以将这个中间件添加到 Project Schema 中。

项目型号:

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);

项目儿童模型:

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);

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

{
    "_id": "5dea699cb10c442260245abf",
    "name": "Project 1",
    "__v": 0
}

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

孩子 1

{
    "_id": "5dea69c7b10c442260245ac0",
    "name": "Child 1 (project 1)",
    "parentProject": "5dea699cb10c442260245abf",
    "__v": 0
}

孩子 2

{
    "_id": "5dea69e8b10c442260245ac1",
    "name": "Child 2 (project 1)",
    "parentProject": "5dea699cb10c442260245abf",
    "__v": 0
}

我创建了一个示例路由来通过它的 id 删除一个项目,如下所示:

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

  res.send(result);
});

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

console.log(doc);

{ _id: 5dea699cb10c442260245abf, name: 'Project 1', __v: 0 }

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

Child delete result:  { n: 2, ok: 1, deletedCount: 2 }

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

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

所以在 ProjectSchema 中,我们像这样替换 post 中间件:

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);
  }
});

当我们使用 findOneAndRemove 操作时,结果将与第一种选择相同:

const result = await Project.findOneAndRemove({ _id: req.params.id });
于 2019-12-06T14:52:35.873 回答