Implements the materialized path strategy with cascade child re-parenting on delete for storing a hierarchy of documents with mongoose Version with all collected features and fixes from mongoose-tree, mongoose-tree-fix, mongoose-tree2, mongoose-reparenting-tree
Install via NPM
$ npm install mongoose-path-tree
Model.plugin(tree, {
pathSeparator : '#', // Path separator. Default: '#'
onDelete : 'REPARENT', // Can be set to 'DELETE' or 'REPARENT'. Default: 'DELETE'
numWorkers: 5, // Number of stream workers. Default: 5
idType: Schema.ObjectId // Type used for _id. Default: This Model Schema's _id type
treeOrdering: false || 'position_field_name' // adds a "position" field for siblings on the same level to be moved and swapped
})Then you can use the plugin on your schemas
var tree = require('mongoose-path-tree');
var UserSchema = new Schema({
name : String
});
UserSchema.plugin(tree);
var User = mongoose.model('User', UserSchema);
var adam = new User({ name : 'Adam' });
var bob = new User({ name : 'Bob' });
var carol = new User({ name : 'Carol' });
// Set the parent relationships
bob.parent = adam;
carol.parent = bob;
adam.save(function() {
bob.save(function() {
carol.save();
});
});At this point in mongoDB you will have documents similar to
{
"_id" : ObjectId("50136e40c78c4b9403000001"),
"name" : "Adam",
"path" : "50136e40c78c4b9403000001"
}
{
"_id" : ObjectId("50136e40c78c4b9403000002"),
"name" : "Bob",
"parent" : ObjectId("50136e40c78c4b9403000001"),
"path" : "50136e40c78c4b9403000001#50136e40c78c4b9403000002"
}
{
"_id" : ObjectId("50136e40c78c4b9403000003"),
"name" : "Carol",
"parent" : ObjectId("50136e40c78c4b9403000002"),
"path" : "50136e40c78c4b9403000001#50136e40c78c4b9403000002#50136e40c78c4b9403000003"
}The path is used for recursive methods and is kept up to date by the plugin if the parent is changed
Signature:
doc.getChildren([filters], [fields], [options], [recursive], cb);args are additional filters if needed. if recursive is supplied and true, subchildren are returned
Based on the above hierarchy:
adam.getChildren(function(err, users) {
// users is an array of with the bob document
});
adam.getChildren(true, function(err, users) {
// users is an array with both bob and carol documents
});Signature as a method:
doc.getChildrenTree([args], cb);Signature as a static:
Model.getChildrenTree([rootDoc], [args], cb);returns (Object): recursive tree of sub-children.
args is an object you can defined with theses properties :
- filters:
Object, mongoose query filter, optional, default:{} - fields:
String|Object, mongoose fields, optional, default:null(all fields) - options:
Object, mongoose query option, optional, default:{} - minLevel:
Number, level at which will start the search, default:1 - recursive:
Boolean, make the search recursive or only fetch children for the specified level, default:true - allowEmptyChildren:
Boolean, iftrue, every child not having children would still havechildrenattribute (an empty array), iffalse, every child not having children will havechildrenattribute at all, default:true - objectify:
Boolean|Object, wheather to runtoObject()method on every child, can be eitherBooleanor antoObject()optionsObject, default:false - populationQuery:
String, populate fields, default:''
Example :
var args = {
filters: {owner:myId},
fields: "_id name owner",
minLevel:2,
recursive:true,
allowEmptyChildren:false
}
getChildrenTree(args,myCallback);Based on the above hierarchy:
adam.getChildrenTree( function(err, users) {
/* if you dump users, you will have something like this :
{
"_id" : ObjectId("50136e40c78c4b9403000001"),
"name" : "Adam",
"path" : "50136e40c78c4b9403000001"
"children" : [{
"_id" : ObjectId("50136e40c78c4b9403000002"),
"name" : "Bob",
"parent" : ObjectId("50136e40c78c4b9403000001"),
"path" : "50136e40c78c4b9403000001#50136e40c78c4b9403000002"
"children" : [{
"_id" : ObjectId("50136e40c78c4b9403000003"),
"name" : "Carol",
"parent" : ObjectId("50136e40c78c4b9403000002"),
"path" : "50136e40c78c4b9403000001#50136e40c78c4b9403000002#50136e40c78c4b9403000003"
}]
}]
}
*/
});Signature:
doc.getAncestors([filters], [fields], [options], cb);Based on the above hierarchy:
carol.getAncestors(function(err, users) {
// users as an array [adam, bob] (older -> younger)
})alias: siblingsAndSelf
doc.getSiblingsAndSelf(function(err, docs) {
});Returns this document's siblings and itself in an array
alias: siblings
doc.getSiblings(function(err, docs) {
});Returns this document's siblings in an array
doc.moveToPosition(positin, function(err, docs) {
});Move this node to the specified position (number, zero-based) on the same level and swaps it with the other doc (also changes the target doc's position field value)
Virtual property (Number, strating at 1), equals to the level in the hierarchy
carol.level; // equals 3To run the tests install mocha
npm install mocha -g
and then run
mocha