Skip to content

Commit 95c6799

Browse files
committed
Merge branch 'release/1.2.1'
2 parents ff7c3d1 + 53ee778 commit 95c6799

File tree

4 files changed

+314
-142
lines changed

4 files changed

+314
-142
lines changed

README.md

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,9 @@ Install via NPM
1414

1515
```javascript
1616
Model.plugin(tree, {
17-
pathSeparator : '#' // Default path separator
17+
pathSeparator : '#' // Default path separator
1818
onDelete : 'REPARENT' // Can be set to 'DELETE' or 'REPARENT'. Default: 'REPARENT'
19+
numWorkers: 5 // Number of stream workers
1920
})
2021
```
2122

@@ -73,7 +74,7 @@ The path is used for recursive methods and is kept up to date by the plugin if t
7374

7475
Signature:
7576

76-
getChildren([args], [recursive], cb);
77+
getChildren([filters], [fields], [options], [recursive], cb);
7778

7879
args are additional filters if needed.
7980
if recursive is supplied and true, subchildren are returned
@@ -171,7 +172,7 @@ adam.getChildrenTree( function(err, users) {
171172

172173
Signature:
173174

174-
getAncestors([args], cb);
175+
getAncestors([filters], [fields], [options], cb);
175176

176177
Based on the above hierarchy:
177178

lib/tree.js

Lines changed: 121 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,24 @@
11
var Schema = require('mongoose').Schema;
2-
2+
var streamWorker = require('stream-worker');
33

44
module.exports = exports = tree;
55

66

77
/**
8-
* @class tree
8+
* @class Tree
99
* Tree Behavior for Mongoose
1010
*
1111
* Implements the materialized path strategy with cascade child re-parenting
12-
* on delete for storing a hierarchy of documents with mongoose
12+
* on delete for storing a hierarchy of documents with Mongoose
1313
*
14-
* @param {mongoose.Schema} schema
14+
* @param {Mongoose.Schema} schema
1515
* @param {Object} options
1616
*/
1717
function tree(schema, options) {
1818

1919
var pathSeparator = options && options.pathSeparator || '#'
20-
, onDelete = options && options.onDelete || 'DELETE'; //'REPARENT'
20+
, onDelete = options && options.onDelete || 'DELETE' //'REPARENT'
21+
, numWorkers = options && options.numWorkers || 5;
2122

2223
/**
2324
* Add parent and path properties
@@ -58,7 +59,9 @@ function tree(schema, options) {
5859
var self = this;
5960
this.collection.findOne({ _id: this.parent }, function (err, doc) {
6061

61-
if (err) return next(err);
62+
if (err) {
63+
return next(err);
64+
}
6265

6366
var previousPath = self.path;
6467
self.path = doc.path + pathSeparator + self._id.toString();
@@ -67,19 +70,16 @@ function tree(schema, options) {
6770
// When the parent is changed we must rewrite all children paths as well
6871
self.collection.find({ path: { '$regex': '^' + previousPath + pathSeparator } }, function (err, cursor) {
6972

70-
if (err) return next(err);
71-
72-
var stream = cursor.stream();
73-
stream.on('data', function (doc) {
74-
var newPath = self.path + doc.path.substr(previousPath.length);
75-
self.collection.update({ _id: doc._id }, { $set: { path: newPath } }, function (err) {
73+
if (err) {
74+
return next(err);
75+
}
7676

77-
if (err) return next(err);
78-
});
79-
});
77+
streamWorker(cursor.stream(), numWorkers, function streamOnData(doc, done) {
8078

81-
stream.on('close', next);
82-
stream.on('error', next);
79+
var newPath = self.path + doc.path.substr(previousPath.length);
80+
self.collection.update({ _id: doc._id }, { $set: { path: newPath } }, done);
81+
},
82+
next);
8383
});
8484
}
8585
else {
@@ -114,75 +114,96 @@ function tree(schema, options) {
114114
// Update parent property from children
115115
this.collection.find({ parent: previousParent }, function (err, cursor) {
116116

117-
if (err) return next(err);
118-
var stream = cursor.stream();
119-
stream.on('data', function streamOnData(doc) {
117+
if (err) {
118+
return next(err);
119+
}
120120

121-
self.collection.update({ _id: doc._id }, { $set: { parent: newParent } }, function (err) {
121+
streamWorker(cursor.stream(), numWorkers, function streamOnData(doc, done) {
122122

123-
if (err) return next(err);
124-
});
125-
});
126-
stream.on('close', function streamOnClose() {
123+
self.collection.update({ _id: doc._id }, { $set: { parent: newParent } }, done);
124+
},
125+
function streamOnClose(err) {
126+
127+
if (err) {
128+
return next(err);
129+
}
127130

128-
// Cascade update Path
129131
self.collection.find({ path: { $regex: previousParent + pathSeparator} }, function (err, cursor) {
130132

131133
var subStream = cursor.stream();
132-
subStream.on('data', function subStreamOnData(doc) {
134+
streamWorker(subStream, numWorkers, function subStreamOnData(doc, done) {
133135

134136
var newPath = doc.path.replace(previousParent + pathSeparator, '');
135-
self.collection.update({ _id: doc._id }, { $set: { path: newPath } }, function (err) {
136-
137-
if (err) return next(err);
138-
});
139-
});
140-
subStream.on('close', next);
141-
subStream.on('error', next);
137+
self.collection.update({ _id: doc._id }, { $set: { path: newPath } }, done);
138+
},
139+
next);
142140
});
143141
});
144-
stream.on('error', next);
145142
});
146-
//this.collection.update({})
147143
}
148144
});
149145

150146

151147
/**
152148
* @method getChildren
153149
*
154-
* @param {Object} args
155-
* @param {Boolean} recursive
156-
* @param {Function} next
150+
* {Object} filters (like for mongo find) (optional)
151+
* {Object} or {String} fields (like for mongo find) (optional)
152+
* {Object} options (like for mongo find) (optional)
153+
* @param {Boolean} recursive, default false (optional)
154+
* @param {Function} next
157155
* @return {Model}
158156
*/
159-
schema.methods.getChildren = function getChildren(args, recursive, next) {
157+
schema.methods.getChildren = function getChildren(filters, fields, options, recursive, next) {
160158

161159
// normalize the arguments
162-
if ("function" === typeof args) {
163-
next = args;
164-
recursive = false;
165-
args = {};
160+
if ('function' === typeof filters) {
161+
next = filters;
162+
filters = {};
163+
}
164+
else if ('function' === typeof fields) {
165+
next = fields;
166+
fields = null;
167+
168+
if ('boolean' === typeof filters) {
169+
recursive = filters;
170+
filters = {}
171+
}
172+
}
173+
else if ('function' === typeof options) {
174+
next = options;
175+
options = {};
176+
177+
if ('boolean' === typeof fields) {
178+
recursive = fields;
179+
fields = null;
180+
}
166181
}
167-
else if ("function" === typeof recursive) {
182+
else if ('function' === typeof recursive) {
168183
next = recursive;
169-
if (args instanceof Object) {
170-
recursive = false;
184+
185+
if ('boolean' === typeof options) {
186+
recursive = options;
187+
options = {}
171188
}
172189
else {
173-
recursive = args;
174-
args = {};
190+
recursive = false
175191
}
176192
}
177193

194+
filters = filters || {};
195+
fields = fields || null;
196+
options = options || {};
197+
recursive = recursive || false;
198+
178199
if (recursive) {
179-
args['path'] = {$regex: '^' + this.path + pathSeparator}
200+
filters['path'] = {$regex: '^' + this.path + pathSeparator}
180201
}
181202
else {
182-
args['parent'] = this._id
203+
filters['parent'] = this._id
183204
}
184205

185-
return this.model(this.constructor.modelName).find(args, next);
206+
return this.model(this.constructor.modelName).find(filters, fields, options, next);
186207
};
187208

188209

@@ -205,12 +226,24 @@ function tree(schema, options) {
205226
* @param {Function} next
206227
* @return {Model}
207228
*/
208-
schema.methods.getAncestors = function getAncestors(args, next) {
229+
schema.methods.getAncestors = function getAncestors(filters, fields, options, next) {
209230

210-
if ("function" === typeof args) {
211-
next = args;
212-
args = {};
231+
if ('function' === typeof filters) {
232+
next = filters;
233+
filters = {};
234+
}
235+
else if ('function' === typeof fields) {
236+
next = fields;
237+
fields = null;
213238
}
239+
else if ('function' === typeof options) {
240+
next = options;
241+
options = {};
242+
}
243+
244+
filters = filters || {};
245+
fields = fields || null;
246+
options = options || {};
214247

215248
var ids = [];
216249

@@ -219,9 +252,9 @@ function tree(schema, options) {
219252
ids.pop();
220253
}
221254

222-
args['_id'] = {$in: ids};
255+
filters['_id'] = {$in: ids};
223256

224-
return this.model(this.constructor.modelName).find(args, next);
257+
return this.model(this.constructor.modelName).find(filters, fields, options, next);
225258
};
226259

227260

@@ -232,7 +265,7 @@ function tree(schema, options) {
232265
* @param {Object} args (optional)
233266
* {Object} .filters (like for mongo find)
234267
* {Object} or {String} .fields (like for mongo find)
235-
* {Object} .options (like for mongo find})
268+
* {Object} .options (like for mongo find)
236269
* {Number} .minLevel, default 1
237270
* {Boolean} .recursive
238271
* {Boolean} .allowEmptyChildren
@@ -272,32 +305,40 @@ function tree(schema, options) {
272305

273306
// filters: Add recursive path filter or not
274307
if (recursive) {
275-
if (root)
308+
if (root) {
276309
filters.path = { $regex: '^' + root.path + pathSeparator };
310+
}
277311

278-
if (filters.parent === null)
312+
if (filters.parent === null) {
279313
delete filters.parent;
314+
}
280315

281316
} else {
282-
if (root)
317+
if (root) {
283318
filters.parent = root._id;
284-
else
319+
}
320+
else {
285321
filters.parent = null
322+
}
286323
}
287324

288325
// fields: Add path and parent in the result if not already specified
289326
if (fields) {
290327
if (fields instanceof Object) {
291-
if (!fields.hasOwnProperty('path'))
328+
if (!fields.hasOwnProperty('path')) {
292329
fields['path'] = 1;
293-
if (!fields.hasOwnProperty('parent'))
330+
}
331+
if (!fields.hasOwnProperty('parent')) {
294332
fields['parent'] = 1;
333+
}
295334
}
296335
else {
297-
if (!fields.match(/path/))
336+
if (!fields.match(/path/)) {
298337
fields += ' path';
299-
if (!fields.match(/parent/))
338+
}
339+
if (!fields.match(/parent/)) {
300340
fields += ' parent';
341+
}
301342
}
302343
}
303344

@@ -310,22 +351,27 @@ function tree(schema, options) {
310351

311352
return this.find(filters, fields, options, function (err, results) {
312353

313-
if (err) throw err;
354+
if (err) {
355+
return next(err);
356+
}
314357

315358
var getLevel = function (path) {
316359

317360
return path ? path.split(pathSeparator).length : 0;
318361
};
319362

320-
var createChildren = function (arr, node, level) {
363+
var createChildren = function createChildren(arr, node, level) {
321364

322365
if (level == minLevel) {
323-
if (allowEmptyChildren)
366+
if (allowEmptyChildren) {
324367
node.children = [];
368+
}
325369
return arr.push(node);
326370
}
371+
327372
var nextIndex = arr.length - 1;
328373
var myNode = arr[nextIndex];
374+
329375
if (!myNode) {
330376
//console.log("Tree node " + node.name + " filtered out. Level: " + level + " minLevel: " + minLevel);
331377
return []
@@ -336,8 +382,10 @@ function tree(schema, options) {
336382

337383
var finalResults = [];
338384
var rootLevel = 1;
339-
if (root)
385+
386+
if (root) {
340387
rootLevel = getLevel(root.path) + 1;
388+
}
341389

342390
if (minLevel < rootLevel) {
343391
minLevel = rootLevel
@@ -354,9 +402,9 @@ function tree(schema, options) {
354402
};
355403

356404

357-
schema.methods.getChildrenTree = function(args, cb) {
405+
schema.methods.getChildrenTree = function(args, next) {
358406

359-
this.constructor.getChildrenTree(this, args, cb)
407+
this.constructor.getChildrenTree(this, args, next)
360408
};
361409

362410

0 commit comments

Comments
 (0)