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 havechildren
attribute (an empty array), iffalse
, every child not having children will havechildren
attribute at all, default:true
- objectify:
Boolean|Object
, wheather to runtoObject()
method on every child, can be eitherBoolean
or 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 3
To run the tests install mocha
npm install mocha -g
and then run
mocha