Mongoose plugin for tree hierarchy using the materialized path pattern.
- Node.js >= 14
- MongoDB >= 4
- Mongoose >= 6
npm install mongoose-mpath
Important note
This plugins adds
parent
,path
andchildren
fields to the schema. You should not define them in the schema which the plugin is enabled on.
Semantics
MySchema.plugin(MpathPlugin, [PLUGIN OPTIONS]);
Plugin options
const options = {
modelName: 'MyModel', // Name of model
pathSeparator: '#', // (optional) String used to separate ids in path
onDelete: 'REPARENT', // (optional) 'REPARENT' or 'DELETE'
idType: Schema.ObjectId // (optional) Type used for model id
}
Example setup
import Mongoose from 'mongoose';
import MpathPlugin from 'mongoose-mpath';
const LocationSchema = new Mongoose.Schema({name: String});
LocationSchema.plugin(MpathPlugin, {modelName: 'Location'});
const LocationModel = Mongoose.model('Location', LocationSchema);
const europe = new LocationModel({name: 'europe'});
const sweden = new LocationModel({name: 'sweden', parent: europe});
const stockholm = new LocationModel({name: 'stockholm', parent: sweden});
await europe.save();
await sweden.save();
await stockholm.save();
At this point in mongoDB you will have documents similar to
{
"_id" : ObjectId("50136e40c78c4b9403000001"),
"name" : "europe",
"path" : "50136e40c78c4b9403000001"
}
{
"_id" : ObjectId("50136e40c78c4b9403000002"),
"name" : "sweden",
"parent" : ObjectId("50136e40c78c4b9403000001"),
"path" : "50136e40c78c4b9403000001#50136e40c78c4b9403000002"
}
{
"_id" : ObjectId("50136e40c78c4b9403000003"),
"name" : "stockholm",
"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.
All examples below are based on the following document hierarchy:
africa
europe
- norway
- sweden
-- stockholm
--- skansen
Returns ancestors of a document. Returns a promise.
Signature
document.getAncestors(conditions, [fields], [options])
Arguments
- See offical docs on model.find() for description of arguments.
Example
const ancestors = await stockholm.getAncestors({}); // (Array) [europe, sweden]
Returns all children of a document. Returns a promise.
Signature
document.getAllChildren(conditions, [fields], [options])
Arguments
- See offical docs on model.find() for description of arguments.
Example
const children = await sweden.getAllChildren({}); // (Array) [stockholm, skansen]
const children = await stockholm.getAllChildren({}); // (Array) [skansen]
Returns all children of a document formatted as a tree hierarchy. Returns a promise.
Signature
document.getChildrenTree([args]) // as method
model.getChildrenTree([args]) // as static
Arguments
-
(Object)
args
{ (Object) filters: {}, // mongoose query filters (Object|String) fields: null, // mongoose query fields (null equals all fields) (Object) options: {}, // mongoose query options (String) populate: '', // string to passed to populate() (int) minLevel: 1, // minimum level to include (int) maxLevel: 9999, // maximum level to include (Mongoose.document) rootDoc // mongoose document }
Example
const args = { filters: {author: 'vikpe'}, fields: '_id name', options: {sort: 'name'}, populate: 'repos', minLevel: 2, maxLevel: 4 }
Example
const tree = await sweden.getChildrenTree({});
// tree is an array similar to
/*
[
{
'name': 'sthlm',
'children': [
{
'name': 'skansen',
'children': [],
}
],
}
]
*/
Returns immediate children of a document. Returns a promise.
Signature
document.getImmediateChildren(conditions, [fields], [options])
Arguments
- See offical docs on model.find() for description of arguments.
Example
const children = await europe.getImmediateChildren({}); // (Array) [norway, sweden]
const children = await sweden.getImmediateChildren({}); // (Array) [stockholm]
Returns parent of a document.
Signature
document.getParent([fields], [options])
Arguments
- See offical docs on model.find() for description of arguments.
Example
const parent = await sweden.getParent(); // (Object) europe
const parent = await stockholm.getParent(); // (Object) sweden
A Virtual field that equals to the level of a document in the hierarchy.
Signature
(Number) document.level
Example
africa.level // 1
sweden.level // 2
skansen.level // 4
Placeholder variable populated when calling .getChildrenTree()
.
Given the following document hierarchy:
africa
europe
- norway
- sweden
-- stockholm
--- skansen
getAncestors()
europe.getAncestors() // (Array) []
stockholm.getAncestors() // (Array) [europe, sweden]
skansen.getAncestors() // (Array) [europe, sweden, stockholm]
getAllChildren()
europe.getAllChildren() // (Array) [sweden, stockholm, skansen]
stockholm.getAllChildren() // (Array) [skansen]
skansen.getAllChildren() // (Array) []
getImmediateChildren()
europe.getImmediateChildren() // (Array) [norway, sweden]
stockholm.getImmediateChildren() // (Array) [skansen]
skansen.getImmediateChildren() // (Array) []
getChildrenTree()
europe.getChildrenTree()
/*
[
{
'name': 'norway',
'children': []
},
{
'name': 'sweden',
'children': [
{
'name': 'sthlm',
'children': [
{
'name': 'skansen',
'children': []
}
],
}
]
}
]
*/
sweden.getChildrenTree()
/*
[
{
'name': 'sthlm',
'children': [
{
'name': 'skansen',
'children': [],
}
],
}
]
*/
getParent()
europe.getParent() // (null)
stockholm.getParent() // (Object) sweden
skansen.getParent() // (Object) stockholm
level
africa.level // (Number) 1
europe.level // (Number) 1
norway.level // (Number) 2
sweden.level // (Number) 2
stockholm.level // (Number) 3
skansen.level // (Number) 4
Feedback and pull requests are most welcome!
npm install mongoose-mpath
- Install MongoDB (Community Server).
- Start MongoDB:
mongod
- Run tests:
npm run test
This plugin is inspired by mongoose-path-tree
by swayf.