简体   繁体   中英

Mongoose: Populate a tree with parent referencing top-down

As I have stated in another question , I am working on a project involving a tree.

  • The tree uses parent referencing, so every node has the id of its parent
  • I need to load the tree top-down (from root to children) from the db and replace the parent references by children arrays (because the client needs them)
  • I've chosen this method, because I estimate 98% of the operation to be create/update on nodes (and this way I only have to create 1 node on update, instead of also updating the parent to add the child to the array) and only about 2% to be read operations (I only have to read the complete tree, there is no use case for reading parts or subtrees)

The Tree models is:

const mongoose = require("mongoose");
const Promise = require("bluebird");
mongoose.Promise = Promise;
const Node = require("./node-model");

const TreeSchema = new mongoose.Schema({
  root: { type: Schema.Types.ObjectId, ref: 'Node' },
});

And the Node model:

const mongoose = require("mongoose");
const Promise = require("bluebird");
mongoose.Promise = Promise;

const NodeSchema = new mongoose.Schema({
  parent:  Schema.Types.ObjectId,
  children: [], // to be populated on loading the tree
  data: {
    d1: String, 
    //...
  }
});

NodeSchema.methods.populateTree = function() {
  return this.constructor.find({ parent: this._id }).exec()
    .then(function(arrayOfChildren) {
      return Promise.each(arrayOfChildren, function(child){
        this.children.push(child); // PROBLEM: 'this' is undfined here!
        delete child.parent; // delete parent reference because JSON has problems with circular references
        return child.populateTree();
      });
    });
}

Also, there is a tree container:

const TreeContainerSchema = new mongoose.Schema({
  owner: { type: Schema.Types.ObjectId, ref: 'User', required: true },
  tree: { type: Schema.Types.ObjectId, ref: 'Tree' },
});

I'm trying to load the complete tree (in his container) to send it back to the client as JSON as follows:

getTreeContainerById = function(req, res) {
  var promise = TreeContainer.
    findById(req.params.id).
    populate("owner", "name"). // only include name
    populate({
      path: "tree",
      populate: {
        path: "root",
        populate: "data"
      }
    }).exec();

    promise.then(function(treeContainer){
      return treeContainer.tree.root.populateTree()
        .then(function(){ return treeContainer });
    }).then(function(treeContainer) {
      // I need the tree container here to send it back to the client
      res.json(treeContainer);
    });
};

But this implementation isn't working. The problems I face are:

  • In the populateTree schema method, I can't access the current node through " this " (it is undefined) but I need the reference somehow to add the children to the array
  • If I try child.parent.children.push instead, this also isn't working, because I only have the id of the parent (in child.parent ) and not the entity (and I don't think it is the correct approach to load it again from the database)
  • In an earlier version, I had the problem, that the JSON was send back to the client, before the tree was completely populated, but I think i solved this through the use of the schema method
  • In general, I don't know, if this is the correct approach to solve my problem (populate the children references and delete the parent references in my tree) or if there is a more appropriate solution

I hope, I could make my problem clear. Any help is much appreciated!

With populateTree as follows it works:

NodeSchema.methods.populateTree = function() {
  var node = this;
  return this.constructor.find({ parent: this._id }).exec()
    .then(function(arrayOfChildren) {
      return Promise.each(arrayOfChildren, function(child){
        node.children.push(child);
        child.parent = null;
        return child.populateTree();
      });
    });
}

Thanks to @danh who suggested the same!

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM