Materialized path hierarchy for mongoose
npm install mongoose-materializedA mongoose plugin for the materialized paths.
---
``javascript
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.
`javascript
// 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
`javascript
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.
`javascript
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.
`javascript
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
});
`
---
#### Instructions
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.
`javascript`
model.calledFunction( function (error, data) {
if (error)
// handle error
});
The methods with work callback return promise. Mongoose Promise
`javascript
model.calledFunction().then( function (data) {
}, function (err) {
// handle error
});
`
Imprtant! Do not use the following methods:
* Model.findByIdAndUpdate()
* Model.findByOneAndUpdate()
* Model.findByIdAndRemove()
* Model.findByOneAndRemove()
* Model.update() - static version
* instance.update()
* Model.remove() - static version
These functions are not triggered by the removal and saving events.
Instead, the following are recommended:
* instance.save() - saving and update (before use findOne, findById)
* instance.remove() - remove document (before use findOne, findById)
* Model.Remove(condition, callback)
The my `query` object is special object for mongo query. This parameter available for functions.`javascript
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
---
#### Attributes
Added attributes:
* parentId: Parent item id.
* path: materialized path. Auto generated
* _w: weight for sort
* depth: (virtual) element depth
---
#### Static methods
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)
---
#### Methods
* 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.
* MongoDB Model Tree Structures with Materialized Paths
* Inspired by mongoose nested set By @groupdock
* MongooseJS Doc
---
#### Jun 14, 2013 - version: 0.1.1
* added ToTree test
* tempory removed Building static method (thown not implemented error if use)
* fixed: ToTree now return json document. (Not mongoose document)
* updated README.md
#### Jun 10, 2013 - version: 0.1.0
* currently under construction
* added test
* static methods
* before save verifies the existence of parent element
* Query supported methods
* added Travis CI build status
* updated README.md
---
* Janos Meszaros: https://github.com/janez89