1
1
var Schema = require ( 'mongoose' ) . Schema ;
2
-
2
+ var streamWorker = require ( 'stream-worker' ) ;
3
3
4
4
module . exports = exports = tree ;
5
5
6
6
7
7
/**
8
- * @class tree
8
+ * @class Tree
9
9
* Tree Behavior for Mongoose
10
10
*
11
11
* 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
13
13
*
14
- * @param {mongoose .Schema } schema
14
+ * @param {Mongoose .Schema } schema
15
15
* @param {Object } options
16
16
*/
17
17
function tree ( schema , options ) {
18
18
19
19
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 ;
21
22
22
23
/**
23
24
* Add parent and path properties
@@ -58,7 +59,9 @@ function tree(schema, options) {
58
59
var self = this ;
59
60
this . collection . findOne ( { _id : this . parent } , function ( err , doc ) {
60
61
61
- if ( err ) return next ( err ) ;
62
+ if ( err ) {
63
+ return next ( err ) ;
64
+ }
62
65
63
66
var previousPath = self . path ;
64
67
self . path = doc . path + pathSeparator + self . _id . toString ( ) ;
@@ -67,19 +70,16 @@ function tree(schema, options) {
67
70
// When the parent is changed we must rewrite all children paths as well
68
71
self . collection . find ( { path : { '$regex' : '^' + previousPath + pathSeparator } } , function ( err , cursor ) {
69
72
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
+ }
76
76
77
- if ( err ) return next ( err ) ;
78
- } ) ;
79
- } ) ;
77
+ streamWorker ( cursor . stream ( ) , numWorkers , function streamOnData ( doc , done ) {
80
78
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 ) ;
83
83
} ) ;
84
84
}
85
85
else {
@@ -114,75 +114,96 @@ function tree(schema, options) {
114
114
// Update parent property from children
115
115
this . collection . find ( { parent : previousParent } , function ( err , cursor ) {
116
116
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
+ }
120
120
121
- self . collection . update ( { _id : doc . _id } , { $set : { parent : newParent } } , function ( err ) {
121
+ streamWorker ( cursor . stream ( ) , numWorkers , function streamOnData ( doc , done ) {
122
122
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
+ }
127
130
128
- // Cascade update Path
129
131
self . collection . find ( { path : { $regex : previousParent + pathSeparator } } , function ( err , cursor ) {
130
132
131
133
var subStream = cursor . stream ( ) ;
132
- subStream . on ( 'data' , function subStreamOnData ( doc ) {
134
+ streamWorker ( subStream , numWorkers , function subStreamOnData ( doc , done ) {
133
135
134
136
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 ) ;
142
140
} ) ;
143
141
} ) ;
144
- stream . on ( 'error' , next ) ;
145
142
} ) ;
146
- //this.collection.update({})
147
143
}
148
144
} ) ;
149
145
150
146
151
147
/**
152
148
* @method getChildren
153
149
*
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
157
155
* @return {Model }
158
156
*/
159
- schema . methods . getChildren = function getChildren ( args , recursive , next ) {
157
+ schema . methods . getChildren = function getChildren ( filters , fields , options , recursive , next ) {
160
158
161
159
// 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
+ }
166
181
}
167
- else if ( " function" === typeof recursive ) {
182
+ else if ( ' function' === typeof recursive ) {
168
183
next = recursive ;
169
- if ( args instanceof Object ) {
170
- recursive = false ;
184
+
185
+ if ( 'boolean' === typeof options ) {
186
+ recursive = options ;
187
+ options = { }
171
188
}
172
189
else {
173
- recursive = args ;
174
- args = { } ;
190
+ recursive = false
175
191
}
176
192
}
177
193
194
+ filters = filters || { } ;
195
+ fields = fields || null ;
196
+ options = options || { } ;
197
+ recursive = recursive || false ;
198
+
178
199
if ( recursive ) {
179
- args [ 'path' ] = { $regex : '^' + this . path + pathSeparator }
200
+ filters [ 'path' ] = { $regex : '^' + this . path + pathSeparator }
180
201
}
181
202
else {
182
- args [ 'parent' ] = this . _id
203
+ filters [ 'parent' ] = this . _id
183
204
}
184
205
185
- return this . model ( this . constructor . modelName ) . find ( args , next ) ;
206
+ return this . model ( this . constructor . modelName ) . find ( filters , fields , options , next ) ;
186
207
} ;
187
208
188
209
@@ -205,12 +226,24 @@ function tree(schema, options) {
205
226
* @param {Function } next
206
227
* @return {Model }
207
228
*/
208
- schema . methods . getAncestors = function getAncestors ( args , next ) {
229
+ schema . methods . getAncestors = function getAncestors ( filters , fields , options , next ) {
209
230
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 ;
213
238
}
239
+ else if ( 'function' === typeof options ) {
240
+ next = options ;
241
+ options = { } ;
242
+ }
243
+
244
+ filters = filters || { } ;
245
+ fields = fields || null ;
246
+ options = options || { } ;
214
247
215
248
var ids = [ ] ;
216
249
@@ -219,9 +252,9 @@ function tree(schema, options) {
219
252
ids . pop ( ) ;
220
253
}
221
254
222
- args [ '_id' ] = { $in : ids } ;
255
+ filters [ '_id' ] = { $in : ids } ;
223
256
224
- return this . model ( this . constructor . modelName ) . find ( args , next ) ;
257
+ return this . model ( this . constructor . modelName ) . find ( filters , fields , options , next ) ;
225
258
} ;
226
259
227
260
@@ -232,7 +265,7 @@ function tree(schema, options) {
232
265
* @param {Object } args (optional)
233
266
* {Object} .filters (like for mongo find)
234
267
* {Object} or {String} .fields (like for mongo find)
235
- * {Object} .options (like for mongo find} )
268
+ * {Object} .options (like for mongo find)
236
269
* {Number} .minLevel, default 1
237
270
* {Boolean} .recursive
238
271
* {Boolean} .allowEmptyChildren
@@ -272,32 +305,40 @@ function tree(schema, options) {
272
305
273
306
// filters: Add recursive path filter or not
274
307
if ( recursive ) {
275
- if ( root )
308
+ if ( root ) {
276
309
filters . path = { $regex : '^' + root . path + pathSeparator } ;
310
+ }
277
311
278
- if ( filters . parent === null )
312
+ if ( filters . parent === null ) {
279
313
delete filters . parent ;
314
+ }
280
315
281
316
} else {
282
- if ( root )
317
+ if ( root ) {
283
318
filters . parent = root . _id ;
284
- else
319
+ }
320
+ else {
285
321
filters . parent = null
322
+ }
286
323
}
287
324
288
325
// fields: Add path and parent in the result if not already specified
289
326
if ( fields ) {
290
327
if ( fields instanceof Object ) {
291
- if ( ! fields . hasOwnProperty ( 'path' ) )
328
+ if ( ! fields . hasOwnProperty ( 'path' ) ) {
292
329
fields [ 'path' ] = 1 ;
293
- if ( ! fields . hasOwnProperty ( 'parent' ) )
330
+ }
331
+ if ( ! fields . hasOwnProperty ( 'parent' ) ) {
294
332
fields [ 'parent' ] = 1 ;
333
+ }
295
334
}
296
335
else {
297
- if ( ! fields . match ( / p a t h / ) )
336
+ if ( ! fields . match ( / p a t h / ) ) {
298
337
fields += ' path' ;
299
- if ( ! fields . match ( / p a r e n t / ) )
338
+ }
339
+ if ( ! fields . match ( / p a r e n t / ) ) {
300
340
fields += ' parent' ;
341
+ }
301
342
}
302
343
}
303
344
@@ -310,22 +351,27 @@ function tree(schema, options) {
310
351
311
352
return this . find ( filters , fields , options , function ( err , results ) {
312
353
313
- if ( err ) throw err ;
354
+ if ( err ) {
355
+ return next ( err ) ;
356
+ }
314
357
315
358
var getLevel = function ( path ) {
316
359
317
360
return path ? path . split ( pathSeparator ) . length : 0 ;
318
361
} ;
319
362
320
- var createChildren = function ( arr , node , level ) {
363
+ var createChildren = function createChildren ( arr , node , level ) {
321
364
322
365
if ( level == minLevel ) {
323
- if ( allowEmptyChildren )
366
+ if ( allowEmptyChildren ) {
324
367
node . children = [ ] ;
368
+ }
325
369
return arr . push ( node ) ;
326
370
}
371
+
327
372
var nextIndex = arr . length - 1 ;
328
373
var myNode = arr [ nextIndex ] ;
374
+
329
375
if ( ! myNode ) {
330
376
//console.log("Tree node " + node.name + " filtered out. Level: " + level + " minLevel: " + minLevel);
331
377
return [ ]
@@ -336,8 +382,10 @@ function tree(schema, options) {
336
382
337
383
var finalResults = [ ] ;
338
384
var rootLevel = 1 ;
339
- if ( root )
385
+
386
+ if ( root ) {
340
387
rootLevel = getLevel ( root . path ) + 1 ;
388
+ }
341
389
342
390
if ( minLevel < rootLevel ) {
343
391
minLevel = rootLevel
@@ -354,9 +402,9 @@ function tree(schema, options) {
354
402
} ;
355
403
356
404
357
- schema . methods . getChildrenTree = function ( args , cb ) {
405
+ schema . methods . getChildrenTree = function ( args , next ) {
358
406
359
- this . constructor . getChildrenTree ( this , args , cb )
407
+ this . constructor . getChildrenTree ( this , args , next )
360
408
} ;
361
409
362
410
0 commit comments