【问题标题】:Mongoose: Need .pre hook to recursively delete children in treeMongoose:需要 .pre 钩子来递归删除树中的子项
【发布时间】:2019-12-23 03:07:27
【问题描述】:

我在 Mongoose 中定义了一个构成递归树结构 的模式,该模式运行良好。每个节点都有一组子节点,它们引用同一树和模式中的子节点。树可以通过孩子的递归深入。

我想使用 Mongoose .pre() 中间件挂钩递归删除子节点(及其所有后代),当我与其他节点一起删除他们的父节点时猫鼬叫。

我尝试了以下代码的变体,但由于各种错误而失败 - 最新错误“baustoff(remove) is not a function at runtime”记录在下面代码的 cmets 中。

我的问题:

  • 在 .pre 函数中首先发出哪个 Mongoose 调用?
  • 如何正确处理children数组?

  • 注意:如日志所示,我下面代码中的当前 deleteMany 至少将其放入该函数的内部,但可能仍然是错误的......

  • 如何递归地获取删除...子项的所有子项的调用?

  • 在当前引发错误的 .pre 结尾处调用什么,以使其正常工作?

注意:删除父节点的另外两个 Mongoose 调用(在此模型代码之外)应该触发上面定义的 .pre 钩子。这两个的代码 sn-ps 是:

1) 等待 Baustoff.remove ({}); --> 这是在测试开始时清空我的数据库,并将 .pre 代码引入错误“baustoff(remove) 在运行时不是函数”

2) Baustoff.findOneAndDelete(req.params.baustoffId) --> 还不能测试这个,因为 .pre 钩子已经在 1) 中抛出错误

有猫鼬经验的人可以帮助我正确获取此 .pre 代码吗?我在那个新代码部分肯定有一个或多个错误。非常感谢您处理我的问题!

// baustoff.model.js
  const mongoose        = require('mongoose');                    
  const Baustoffe       = require('./../../baustoffe');           // for some enums, irrelevant for BaustoffSchema.pre('remove', )
  const BaumKnotenTypen = require('./../../baumknotentypen');     // for some enums, irrelevant for BaustoffSchema.pre('remove', )             
  const VerzweigungsTypen = require('./../../verzweigungstypen'); // for some enums, irrelevant for BaustoffSchema.pre('remove', )              
  const MaterialEigenschaftSchema  = require('./materialeigenschaft.model.js').model('MaterialEigenschaft').schema 
  const Schema = mongoose.Schema;
  let BaustoffSchema = new Schema  

  ({
    // First some normal properties in my BaustoffSchema schema:
    kurzBezeichnung: { type: String },                                        
    bezeichnung:     { type: String, enum: Object.values(Baustoffe) },       
    baumKnotenTyp:   { type: String, enum: Object.values(BaumKnotenTypen) }, 
    verzweigungsTyp: { type: String, enum: Object.values(VerzweigungsTypen)}, 
    aktiv:           { type: Boolean, default: true},                        
    produkt:         {type: String},
    materialEigenschaften: [MaterialEigenschaftSchema], // some properties as an array to allow variable number of props
    /*--------------------------------------------------------------------*/
    // Now an array of references to children that make up a tree structure, as they refer recursively to the model 'Baustoff' 
    kinder:          [{ type: Schema.Types.ObjectId, ref: 'Baustoff' }],      
   },
         {}
   );
   Object.assign(BaustoffSchema.statics, {Baustoffe},{BaumKnotenTypen},{VerzweigungsTypen} ); 
    // Above line is to define some statics, irrelevant for the .pre function
    /*--------------------------------------------------------------------*/
    // PROBLEMATIC PART starts here:
    // Goal: Define a  Mongoose BaustoffSchema.pre() hook that recursively deletes all children further down in the tree, when their parent is deleted

    BaustoffSchema.pre('remove', {query: true}, function(next){
    mongoose.models["Baustoff"].deleteMany({ kinder: this._id }, function(err, baustoff) {
         console.log("In Mongoose pre Hook before if");
         if(baustoff) {
             console.log("In Mongoose pre Hook after if");
             baustoff.remove(); // ERROR is thrown HERE with this variant of the code: baustoff.remove is not a function at runtime !!!!!!!!!!
          });
          next();
      }); // end of .pre hook
  let Baustoff = mongoose.model('Baustoff', BaustoffSchema, 'Baustoffe'); // Model Name, Schema Name, Collection Name in Mongo db
  module.exports = Baustoff; // Baustoff is model name hook

【问题讨论】:

  • 我将代码中出现错误标记的行更改为: Baustoff.remove({_id: this._id}).exec(); // 仅适用于模式名称!!这会导致 .pre 钩子中的无限循环!它让我相信一种解决方法可能是让递归代码遍历所有子节点,将所有子节点推送到一个临时数组,最后删除该数组中的所有 id。有人有sn-p的代码吗?

标签: node.js mongodb recursion mongoose tree


【解决方案1】:

我实施了我上一条评论中指出的解决方法。我对此并不完全满意,因为它没有按照最初的要求使用 Mongoose .pre 中间件,而是在删除树节点之前删除所有子级++(=后代)的“常规”代码。

如果有人知道如何使用 .pre 中间件实现相同的目标,我很乐意将其用作首选解决方案!我也希望告知 .pre 是否适合此要求?

我知道有更复杂的解决方案来处理树,例如物化路径模式,但请注意,目前我只是在寻找不会改变我的 Mongoose 架构的解决方案。

这是我的初始代码,用于“定期”删除要在我的树中删除的父节点的所有后代。它使用 _ids 的临时堆栈来处理递归。我相信它可以进一步完善:

async function subTreeDelete() {

var descendants = []; // Array to collect all children++, to delete them jointly after the collection phase
var stack = []; // Auxiliary array to recursely collect children that still need to be traversed
const node_id = req.params.baustoffId; // _id of parent node to be deleted, from REST call
var item = await Baustoff.findOne({ _id: node_id }).exec(); // Using await, therefore everything put into an async function
console.log("In Delete children, find parent: ", item); 
stack.push(item); // The parent needs to be traversed first, put it on stack

while (stack.length > 0) {
  var currentnode = stack.pop(); // Get a node from stack, as long as stack isn't empty
  var children = await Baustoff.find({_id: { $in: currentnode.kinder }}).exec(); // Find node's children
  console.log("In Delete children, children: ", children); // Using await, therefore everything put into an async function
  children.forEach(child => {
    descendants.push(child._id); // Push all _ids of direct children on our descendants array that collects the children++
    if (child.kinder.length > 0) {
      stack.push(child); // child node has children still to be traversed, push it on stack
    }
  });
}
console.log("In Delete children: Collected all children of children: ", descendants);
await Baustoff.deleteMany({ _id: { $in: descendants } }).exec(); // Now delete all children++ as collected in descendants
} // /async function

subTreeDelete(); // Call the above async funktion, to delete all children++
// Now we are finally ready to delete the parent node....code for that not included here

【讨论】:

    猜你喜欢
    • 2015-10-27
    • 2018-11-17
    • 1970-01-01
    • 2019-07-26
    • 1970-01-01
    • 2020-07-20
    • 2010-10-13
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多