A mongoose plugin for the materialized paths.
var mongoose = require('mongoose'),
materializedPlugin = require('mongoose-materialized'),
Schema = mongoose.Schema;
mongoose.connect('mongodb://localhost/materialized');
var CatSchema = new Schema({
name: {type: String}
});
CatSchema.plugin(materializedPlugin);
var Cat= mongoose.model('Cat', CatSchema); // Category
Adding root and child element.
Important: The model verifies the existence of the parent category before they would save. Except for the root element or change the parent id not touch.
// if you have predefined datas with parent id
Cat.Building(function(){
// building materialized path
});
// add root element
cat = new Cat({name: "Foods"});
cat.save(function(err, foods){
// append new sub category
foods.appendChild({name: "Vegetables"}, function(err, vega){
// vega: { name: "Vegetables", parentId: [foods ID], path: ',[foods ID]' }
});
// or make new
var vega = new Cat({name: "Vegetables"});
// saving with append
foods.appendChild(vega, function(err, data){ ... });
// or save traditional way
vega.parentId = foods._id;
vega.save(function(err, data){ ... });
});
Find element and checking the relationship
Cat.findOne({parentId: null}, function(err, doc){
// access to the children
doc.getChildren(function(err, docs){
// ...
});
// access to the children with condition and sort
doc.getChildren({
condition: { name: /^a/ },
sort: { name: 1 }
},function(err, docs){
// ...
});
// access to the siblings
doc.getSiblings(function(err, docs){
// ...
});
// access to the ancestors
doc.getAncestors(function(err, docs){
// ...
});
// check element is root
doc.isRoot(function(err, isOk){ ... });
// check element is leaf
doc.isLeaf(function(err, isLeaf){ ... });
// depth virtual attributes
doc.depth
// use promise
doc.getChildren().then(function(docs){
// ...
});
// get doc array tree
doc.getArrayTree(function(err, tree){
// ... [ {"_id": "...", "children": [ {...} ]}]
});
// get doc tree
doc.getTree(function(err, tree){
// ... { "doc ID": { ..., children: { ... } }
});
// or get tree with condition and sorting
doc.getTree({
condition: { name: /^[a-zA-Z]+$/ },
sort: { name: 1 }
}, function(err, tree){
// ...
});
});
Cat.GetTree('elemt ID', function (err, tree) {
// ...
});
Cat.GetArrayTree('elemt ID', function (err, tree) {
// ...
});
// access for full tree in array
Cat.GetFullArrayTree(function (err, tree) {
});
// access for full tree object
Cat.GetFullTree(function (err, tree) {
});
The different arrayTree and simple Tree methods: arrayTree result:
[
{ _id: 53ee2db76f2d838a07a04e6a,
path: '',
name: 'Foods',
__v: 0,
_w: 0,
parentId: null,
depth: 0,
id: '53ee2db76f2d838a07a04e6a',
children: [ [Object], [Object] ]
}
]
and the Tree result:
{ '53ee2db76f2d838a07a04e6a':
{ _id: 53ee2db76f2d838a07a04e6a,
path: '',
name: 'Foods',
__v: 0,
_w: 0,
parentId: null,
depth: 0,
id: '53ee2db76f2d838a07a04e6a',
children: {
'53ee2db76f2d838a07a04e6b': [Object]
}
}
}
Manipulate child element with static method mongoose-materialized it is possible to use more than one root.
Cat.AppendChild('ID', { 'name': 'Meats'}, function(err, doc){ ... });
Cat.getChildren('ID', function(err, childs){ ... });
Cat.getRoots(function(err, roots){
// root elements
});
// Format tree, sub element stored in children field
Cat.getRoots({ name: "" }).then(function (err, root) {
root.getChildren().then(function (err, children) {
console.log( Cat.toTree(children) );
// or only shown name
console.log( Cat.toTree(children, { name: 1 }) );
});
});
Hierarchical builder for the existing data. Important: This operation is relatively slow. Use only the conversion.
Cat.Building(function(){
// builded materialized path sturcture
});
// This example convert nested set to materialized path. Use this function to migration.
Cat.Building({
remove: { lt: 1, gt: 1, children: 1 } // remove nested fields from existsing data
}, function(){
// building is competted
});
The following methods must be used with a callback. The callback method have two arguments. The first error and the second data object. If all goes well then the error is null.
model.calledFunction( function (error, data) {
if (error)
// handle error
});
The methods with work callback return promise. Mongoose Promise
model.calledFunction().then( function (data) {
}, function (err) {
// handle error
});
Imprtant! Do not use the following methods:
These functions are not triggered by the removal and saving events.
Instead, the following are recommended:
The my query
object is special object for mongo query. This parameter available for functions.
var query = {
// mongo condition
condition: {
name: /^a/
},
// selected fields
fields: {
_id: 1,
name: 1
},
// sorting
sort: {
name: -1
}
};
// Example get chidls with query
doc.getChilds(query, function(err, docs){ ... });
To run the tests:
npm test
Added attributes:
Similar method has the static begins with the first letter capitalized. (IsLeaft is static and isLeaf non static)
IsLeaf(ModelOrId, callback)
IsRoot(ModelOrId, callback)
GetChildren(ModelOrId, [query,] callback)
GetRoots([query,] callback)
GetTree(root condition, [children query,] callback) - get elemets tree with children
GetFullTree(callback)
GetArrayTree(root condition, [children query,] callback) - get elemets tree with children
GetFullArrayTree(callback)
Remove(condition, callback) - use this instead of remove.
AppendChild(ModelOrId, callback)
ToTree(documentArray, selected fields) Return object, no mongoose document (toObject()). Fields: { name: 1, _id: 1 }
ToArrayTree(documentArray, selected fields) Return objects in array, no mongoose document (toObject()). Fields: { name: 1, _id: 1 }
Building([prepare,] callback) - rebuild material path (good for extisting collections - parentId is needed)
isRoot(callback)
isLeaf(callback)
isDescendant(callback)
isParent(ModelOrId, callback)
isSibling(ModelOrID, callback)
getParent(callback)
getDescendants([query,] callback)
getChildren([query,] callback) alias for getDescendants
getAncestors([query,] callback)
getSiblings([query,] callback)
getTree([query,] callback) - get elemets tree with children
getArrayTree([query,] callback) - get elemets tree with children, array version
appendChild(model, callback)
setParent(ModelOrId) - if parameter is ID then check parent existence and set parentId (the model parameter to avoid the query)
getChildCondition()
getAncestorsCondition()
getSiblingsCondition()
Inspired by seamless data management.
parentId=null
bug