mongoose 6.2.1 → 6.2.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (52) hide show
  1. package/.eslintrc.json +5 -1
  2. package/CHANGELOG.md +19 -0
  3. package/dist/browser.umd.js +146 -139
  4. package/index.js +5 -1
  5. package/lib/aggregate.js +22 -27
  6. package/lib/browserDocument.js +1 -1
  7. package/lib/cast/number.js +2 -3
  8. package/lib/cast.js +7 -4
  9. package/lib/connection.js +43 -21
  10. package/lib/cursor/AggregationCursor.js +12 -7
  11. package/lib/cursor/QueryCursor.js +11 -6
  12. package/lib/document.js +27 -46
  13. package/lib/drivers/node-mongodb-native/collection.js +12 -4
  14. package/lib/drivers/node-mongodb-native/connection.js +11 -0
  15. package/lib/error/cast.js +3 -2
  16. package/lib/helpers/clone.js +11 -2
  17. package/lib/helpers/cursor/eachAsync.js +18 -15
  18. package/lib/helpers/document/compile.js +7 -4
  19. package/lib/helpers/printJestWarning.js +2 -2
  20. package/lib/helpers/projection/applyProjection.js +77 -0
  21. package/lib/helpers/projection/hasIncludedChildren.js +36 -0
  22. package/lib/helpers/projection/isExclusive.js +5 -2
  23. package/lib/helpers/projection/isInclusive.js +5 -1
  24. package/lib/helpers/query/cast$expr.js +14 -19
  25. package/lib/helpers/query/isOperator.js +5 -2
  26. package/lib/index.js +14 -17
  27. package/lib/model.js +124 -104
  28. package/lib/options/SchemaTypeOptions.js +1 -1
  29. package/lib/plugins/trackTransaction.js +1 -1
  30. package/lib/query.js +156 -144
  31. package/lib/queryhelpers.js +9 -9
  32. package/lib/schema/SubdocumentPath.js +4 -3
  33. package/lib/schema/array.js +13 -6
  34. package/lib/schema/buffer.js +1 -1
  35. package/lib/schema/date.js +1 -1
  36. package/lib/schema/decimal128.js +1 -1
  37. package/lib/schema/documentarray.js +4 -3
  38. package/lib/schema/number.js +1 -1
  39. package/lib/schema/objectid.js +1 -1
  40. package/lib/schema/string.js +4 -4
  41. package/lib/schema.js +8 -8
  42. package/lib/schematype.js +3 -4
  43. package/lib/types/DocumentArray/index.js +1 -1
  44. package/lib/types/array/index.js +2 -2
  45. package/lib/types/array/methods/index.js +10 -11
  46. package/lib/types/buffer.js +3 -3
  47. package/lib/types/map.js +2 -3
  48. package/package.json +2 -4
  49. package/tsconfig.json +0 -2
  50. package/types/PipelineStage.d.ts +272 -0
  51. package/types/index.d.ts +10 -271
  52. package/lib/types/array/ArrayWrapper.js +0 -981
@@ -1,981 +0,0 @@
1
- 'use strict';
2
-
3
- const Document = require('../../document');
4
- const ArraySubdocument = require('../ArraySubdocument');
5
- const MongooseError = require('../../error/mongooseError');
6
- const ObjectId = require('../objectid');
7
- const cleanModifiedSubpaths = require('../../helpers/document/cleanModifiedSubpaths');
8
- const get = require('../../helpers/get');
9
- const internalToObjectOptions = require('../../options').internalToObjectOptions;
10
- const utils = require('../../utils');
11
- const util = require('util');
12
-
13
- const arrayAtomicsSymbol = require('../../helpers/symbols').arrayAtomicsSymbol;
14
- const arrayParentSymbol = require('../../helpers/symbols').arrayParentSymbol;
15
- const arrayPathSymbol = require('../../helpers/symbols').arrayPathSymbol;
16
- const arraySchemaSymbol = require('../../helpers/symbols').arraySchemaSymbol;
17
- const populateModelSymbol = require('../../helpers/symbols').populateModelSymbol;
18
- const slicedSymbol = Symbol('mongoose#Array#sliced');
19
-
20
- const _basePush = Array.prototype.push;
21
-
22
- const validatorsSymbol = Symbol('mongoose#MongooseCoreArray#validators');
23
-
24
- /*!
25
- * ignore
26
- */
27
-
28
- class ArrayWrapper extends Array {
29
- get isMongooseArray() {
30
- return true;
31
- }
32
-
33
- get validators() {
34
- return this[validatorsSymbol];
35
- }
36
-
37
- set validators(v) {
38
- this[validatorsSymbol] = v;
39
- }
40
-
41
- /**
42
- * Depopulates stored atomic operation values as necessary for direct insertion to MongoDB.
43
- *
44
- * If no atomics exist, we return all array values after conversion.
45
- *
46
- * @return {Array}
47
- * @method $__getAtomics
48
- * @memberOf MongooseArray
49
- * @instance
50
- * @api private
51
- */
52
-
53
- $__getAtomics() {
54
- const ret = [];
55
- const keys = Object.keys(this[arrayAtomicsSymbol] || {});
56
- let i = keys.length;
57
-
58
- const opts = Object.assign({}, internalToObjectOptions, { _isNested: true });
59
-
60
- if (i === 0) {
61
- ret[0] = ['$set', this.toObject(opts)];
62
- return ret;
63
- }
64
-
65
- while (i--) {
66
- const op = keys[i];
67
- let val = this[arrayAtomicsSymbol][op];
68
-
69
- // the atomic values which are arrays are not MongooseArrays. we
70
- // need to convert their elements as if they were MongooseArrays
71
- // to handle populated arrays versus DocumentArrays properly.
72
- if (utils.isMongooseObject(val)) {
73
- val = val.toObject(opts);
74
- } else if (Array.isArray(val)) {
75
- val = this.toObject.call(val, opts);
76
- } else if (val != null && Array.isArray(val.$each)) {
77
- val.$each = this.toObject.call(val.$each, opts);
78
- } else if (val != null && typeof val.valueOf === 'function') {
79
- val = val.valueOf();
80
- }
81
-
82
- if (op === '$addToSet') {
83
- val = { $each: val };
84
- }
85
-
86
- ret.push([op, val]);
87
- }
88
-
89
- return ret;
90
- }
91
-
92
- /*!
93
- * ignore
94
- */
95
-
96
- $atomics() {
97
- return this[arrayAtomicsSymbol] || {};
98
- }
99
-
100
- /*!
101
- * ignore
102
- */
103
-
104
- $parent() {
105
- return this[arrayParentSymbol];
106
- }
107
-
108
- /*!
109
- * ignore
110
- */
111
-
112
- $path() {
113
- return this[arrayPathSymbol];
114
- }
115
-
116
- /**
117
- * Atomically shifts the array at most one time per document `save()`.
118
- *
119
- * ####NOTE:
120
- *
121
- * _Calling this multiple times on an array before saving sends the same command as calling it once._
122
- * _This update is implemented using the MongoDB [$pop](http://www.mongodb.org/display/DOCS/Updating/#Updating-%24pop) method which enforces this restriction._
123
- *
124
- * doc.array = [1,2,3];
125
- *
126
- * const shifted = doc.array.$shift();
127
- * console.log(shifted); // 1
128
- * console.log(doc.array); // [2,3]
129
- *
130
- * // no affect
131
- * shifted = doc.array.$shift();
132
- * console.log(doc.array); // [2,3]
133
- *
134
- * doc.save(function (err) {
135
- * if (err) return handleError(err);
136
- *
137
- * // we saved, now $shift works again
138
- * shifted = doc.array.$shift();
139
- * console.log(shifted ); // 2
140
- * console.log(doc.array); // [3]
141
- * })
142
- *
143
- * @api public
144
- * @memberOf MongooseArray
145
- * @instance
146
- * @method $shift
147
- * @see mongodb http://www.mongodb.org/display/DOCS/Updating/#Updating-%24pop
148
- */
149
-
150
- $shift() {
151
- this._registerAtomic('$pop', -1);
152
- this._markModified();
153
-
154
- // only allow shifting once
155
- if (this._shifted) {
156
- return;
157
- }
158
- this._shifted = true;
159
-
160
- return [].shift.call(this);
161
- }
162
-
163
- /**
164
- * Pops the array atomically at most one time per document `save()`.
165
- *
166
- * #### NOTE:
167
- *
168
- * _Calling this mulitple times on an array before saving sends the same command as calling it once._
169
- * _This update is implemented using the MongoDB [$pop](http://www.mongodb.org/display/DOCS/Updating/#Updating-%24pop) method which enforces this restriction._
170
- *
171
- * doc.array = [1,2,3];
172
- *
173
- * const popped = doc.array.$pop();
174
- * console.log(popped); // 3
175
- * console.log(doc.array); // [1,2]
176
- *
177
- * // no affect
178
- * popped = doc.array.$pop();
179
- * console.log(doc.array); // [1,2]
180
- *
181
- * doc.save(function (err) {
182
- * if (err) return handleError(err);
183
- *
184
- * // we saved, now $pop works again
185
- * popped = doc.array.$pop();
186
- * console.log(popped); // 2
187
- * console.log(doc.array); // [1]
188
- * })
189
- *
190
- * @api public
191
- * @method $pop
192
- * @memberOf MongooseArray
193
- * @instance
194
- * @see mongodb http://www.mongodb.org/display/DOCS/Updating/#Updating-%24pop
195
- * @method $pop
196
- * @memberOf MongooseArray
197
- */
198
-
199
- $pop() {
200
- this._registerAtomic('$pop', 1);
201
- this._markModified();
202
-
203
- // only allow popping once
204
- if (this._popped) {
205
- return;
206
- }
207
- this._popped = true;
208
-
209
- return [].pop.call(this);
210
- }
211
-
212
- /*!
213
- * ignore
214
- */
215
-
216
- $schema() {
217
- return this[arraySchemaSymbol];
218
- }
219
-
220
- /**
221
- * Casts a member based on this arrays schema.
222
- *
223
- * @param {any} value
224
- * @return value the casted value
225
- * @method _cast
226
- * @api private
227
- * @memberOf MongooseArray
228
- */
229
-
230
- _cast(value) {
231
- let populated = false;
232
- let Model;
233
-
234
- if (this[arrayParentSymbol]) {
235
- populated = this[arrayParentSymbol].$populated(this[arrayPathSymbol], true);
236
- }
237
-
238
- if (populated && value !== null && value !== undefined) {
239
- // cast to the populated Models schema
240
- Model = populated.options[populateModelSymbol];
241
-
242
- // only objects are permitted so we can safely assume that
243
- // non-objects are to be interpreted as _id
244
- if (Buffer.isBuffer(value) ||
245
- value instanceof ObjectId || !utils.isObject(value)) {
246
- value = { _id: value };
247
- }
248
-
249
- // gh-2399
250
- // we should cast model only when it's not a discriminator
251
- const isDisc = value.$__schema && value.$__schema.discriminatorMapping &&
252
- value.$__schema.discriminatorMapping.key !== undefined;
253
- if (!isDisc) {
254
- value = new Model(value);
255
- }
256
- return this[arraySchemaSymbol].caster.applySetters(value, this[arrayParentSymbol], true);
257
- }
258
-
259
- return this[arraySchemaSymbol].caster.applySetters(value, this[arrayParentSymbol], false);
260
- }
261
-
262
- /**
263
- * Internal helper for .map()
264
- *
265
- * @api private
266
- * @return {Number}
267
- * @method _mapCast
268
- * @memberOf MongooseArray
269
- */
270
-
271
- _mapCast(val, index) {
272
- return this._cast(val, this.length + index);
273
- }
274
-
275
- /**
276
- * Marks this array as modified.
277
- *
278
- * If it bubbles up from an embedded document change, then it takes the following arguments (otherwise, takes 0 arguments)
279
- *
280
- * @param {ArraySubdocument} subdoc the embedded doc that invoked this method on the Array
281
- * @param {String} embeddedPath the path which changed in the subdoc
282
- * @method _markModified
283
- * @api private
284
- * @memberOf MongooseArray
285
- */
286
-
287
- _markModified(elem) {
288
- const parent = this[arrayParentSymbol];
289
- let dirtyPath;
290
-
291
- if (parent) {
292
- dirtyPath = this[arrayPathSymbol];
293
-
294
- if (arguments.length) {
295
- dirtyPath = dirtyPath + '.' + elem;
296
- }
297
-
298
- if (dirtyPath != null && dirtyPath.endsWith('.$')) {
299
- return this;
300
- }
301
-
302
- parent.markModified(dirtyPath, arguments.length > 0 ? elem : parent);
303
- }
304
-
305
- return this;
306
- }
307
-
308
- /**
309
- * Register an atomic operation with the parent.
310
- *
311
- * @param {Array} op operation
312
- * @param {any} val
313
- * @method _registerAtomic
314
- * @api private
315
- * @memberOf MongooseArray
316
- */
317
-
318
- _registerAtomic(op, val) {
319
- if (this[slicedSymbol]) {
320
- return;
321
- }
322
- if (op === '$set') {
323
- // $set takes precedence over all other ops.
324
- // mark entire array modified.
325
- this[arrayAtomicsSymbol] = { $set: val };
326
- cleanModifiedSubpaths(this[arrayParentSymbol], this[arrayPathSymbol]);
327
- this._markModified();
328
- return this;
329
- }
330
-
331
- this[arrayAtomicsSymbol] || (this[arrayAtomicsSymbol] = {});
332
-
333
- const atomics = this[arrayAtomicsSymbol];
334
-
335
- // reset pop/shift after save
336
- if (op === '$pop' && !('$pop' in atomics)) {
337
- const _this = this;
338
- this[arrayParentSymbol].once('save', function() {
339
- _this._popped = _this._shifted = null;
340
- });
341
- }
342
-
343
- // check for impossible $atomic combos (Mongo denies more than one
344
- // $atomic op on a single path
345
- if (atomics.$set || Object.keys(atomics).length && !(op in atomics)) {
346
- // a different op was previously registered.
347
- // save the entire thing.
348
- this[arrayAtomicsSymbol] = { $set: this };
349
- return this;
350
- }
351
-
352
- let selector;
353
-
354
- if (op === '$pullAll' || op === '$addToSet') {
355
- atomics[op] || (atomics[op] = []);
356
- atomics[op] = atomics[op].concat(val);
357
- } else if (op === '$pullDocs') {
358
- const pullOp = atomics['$pull'] || (atomics['$pull'] = {});
359
- if (val[0] instanceof ArraySubdocument) {
360
- selector = pullOp['$or'] || (pullOp['$or'] = []);
361
- Array.prototype.push.apply(selector, val.map(function(v) {
362
- return v.toObject({ transform: false, virtuals: false });
363
- }));
364
- } else {
365
- selector = pullOp['_id'] || (pullOp['_id'] = { $in: [] });
366
- selector['$in'] = selector['$in'].concat(val);
367
- }
368
- } else if (op === '$push') {
369
- atomics.$push = atomics.$push || { $each: [] };
370
- if (val != null && utils.hasUserDefinedProperty(val, '$each')) {
371
- atomics.$push = val;
372
- } else {
373
- atomics.$push.$each = atomics.$push.$each.concat(val);
374
- }
375
- } else {
376
- atomics[op] = val;
377
- }
378
-
379
- return this;
380
- }
381
-
382
- /**
383
- * Adds values to the array if not already present.
384
- *
385
- * ####Example:
386
- *
387
- * console.log(doc.array) // [2,3,4]
388
- * const added = doc.array.addToSet(4,5);
389
- * console.log(doc.array) // [2,3,4,5]
390
- * console.log(added) // [5]
391
- *
392
- * @param {any} [args...]
393
- * @return {Array} the values that were added
394
- * @memberOf MongooseArray
395
- * @api public
396
- * @method addToSet
397
- */
398
-
399
- addToSet() {
400
- _checkManualPopulation(this, arguments);
401
-
402
- let values = [].map.call(arguments, this._mapCast, this);
403
- values = this[arraySchemaSymbol].applySetters(values, this[arrayParentSymbol]);
404
- const added = [];
405
- let type = '';
406
- if (values[0] instanceof ArraySubdocument) {
407
- type = 'doc';
408
- } else if (values[0] instanceof Date) {
409
- type = 'date';
410
- }
411
-
412
- const rawValues = values.isMongooseArrayProxy ? values.__array : this;
413
- const rawArray = this.isMongooseArrayProxy ? this.__array : this;
414
-
415
- rawValues.forEach(function(v) {
416
- let found;
417
- const val = +v;
418
- switch (type) {
419
- case 'doc':
420
- found = this.some(function(doc) {
421
- return doc.equals(v);
422
- });
423
- break;
424
- case 'date':
425
- found = this.some(function(d) {
426
- return +d === val;
427
- });
428
- break;
429
- default:
430
- found = ~this.indexOf(v);
431
- }
432
-
433
- if (!found) {
434
- rawArray.push(v);
435
- this._registerAtomic('$addToSet', v);
436
- this._markModified();
437
- [].push.call(added, v);
438
- }
439
- }, this);
440
-
441
- return added;
442
- }
443
-
444
- /**
445
- * Returns the number of pending atomic operations to send to the db for this array.
446
- *
447
- * @api private
448
- * @return {Number}
449
- * @method hasAtomics
450
- * @memberOf MongooseArray
451
- */
452
-
453
- hasAtomics() {
454
- if (!utils.isPOJO(this[arrayAtomicsSymbol])) {
455
- return 0;
456
- }
457
-
458
- return Object.keys(this[arrayAtomicsSymbol]).length;
459
- }
460
-
461
- /**
462
- * Return whether or not the `obj` is included in the array.
463
- *
464
- * @param {Object} obj the item to check
465
- * @return {Boolean}
466
- * @api public
467
- * @method includes
468
- * @memberOf MongooseArray
469
- */
470
-
471
- includes(obj, fromIndex) {
472
- const ret = this.indexOf(obj, fromIndex);
473
- return ret !== -1;
474
- }
475
-
476
- /**
477
- * Return the index of `obj` or `-1` if not found.
478
- *
479
- * @param {Object} obj the item to look for
480
- * @return {Number}
481
- * @api public
482
- * @method indexOf
483
- * @memberOf MongooseArray
484
- */
485
-
486
- indexOf(obj, fromIndex) {
487
- if (obj instanceof ObjectId) {
488
- obj = obj.toString();
489
- }
490
-
491
- fromIndex = fromIndex == null ? 0 : fromIndex;
492
- const len = this.length;
493
- for (let i = fromIndex; i < len; ++i) {
494
- if (obj == this[i]) {
495
- return i;
496
- }
497
- }
498
- return -1;
499
- }
500
-
501
- /**
502
- * Helper for console.log
503
- *
504
- * @api public
505
- * @method inspect
506
- * @memberOf MongooseArray
507
- */
508
-
509
- inspect() {
510
- return JSON.stringify(this);
511
- }
512
-
513
- /**
514
- * Pushes items to the array non-atomically.
515
- *
516
- * ####NOTE:
517
- *
518
- * _marks the entire array as modified, which if saved, will store it as a `$set` operation, potentially overwritting any changes that happen between when you retrieved the object and when you save it._
519
- *
520
- * @param {any} [args...]
521
- * @api public
522
- * @method nonAtomicPush
523
- * @memberOf MongooseArray
524
- */
525
-
526
- nonAtomicPush() {
527
- const values = [].map.call(arguments, this._mapCast, this);
528
- const ret = [].push.apply(this, values);
529
- this._registerAtomic('$set', this);
530
- this._markModified();
531
- return ret;
532
- }
533
-
534
- /**
535
- * Wraps [`Array#pop`](https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array/pop) with proper change tracking.
536
- *
537
- * ####Note:
538
- *
539
- * _marks the entire array as modified which will pass the entire thing to $set potentially overwritting any changes that happen between when you retrieved the object and when you save it._
540
- *
541
- * @see MongooseArray#$pop #types_array_MongooseArray-%24pop
542
- * @api public
543
- * @method pop
544
- * @memberOf MongooseArray
545
- */
546
-
547
- pop() {
548
- const ret = [].pop.call(this);
549
- this._registerAtomic('$set', this);
550
- this._markModified();
551
- return ret;
552
- }
553
-
554
- /**
555
- * Pulls items from the array atomically. Equality is determined by casting
556
- * the provided value to an embedded document and comparing using
557
- * [the `Document.equals()` function.](./api.html#document_Document-equals)
558
- *
559
- * ####Examples:
560
- *
561
- * doc.array.pull(ObjectId)
562
- * doc.array.pull({ _id: 'someId' })
563
- * doc.array.pull(36)
564
- * doc.array.pull('tag 1', 'tag 2')
565
- *
566
- * To remove a document from a subdocument array we may pass an object with a matching `_id`.
567
- *
568
- * doc.subdocs.push({ _id: 4815162342 })
569
- * doc.subdocs.pull({ _id: 4815162342 }) // removed
570
- *
571
- * Or we may passing the _id directly and let mongoose take care of it.
572
- *
573
- * doc.subdocs.push({ _id: 4815162342 })
574
- * doc.subdocs.pull(4815162342); // works
575
- *
576
- * The first pull call will result in a atomic operation on the database, if pull is called repeatedly without saving the document, a $set operation is used on the complete array instead, overwriting possible changes that happened on the database in the meantime.
577
- *
578
- * @param {any} [args...]
579
- * @see mongodb http://www.mongodb.org/display/DOCS/Updating/#Updating-%24pull
580
- * @api public
581
- * @method pull
582
- * @memberOf MongooseArray
583
- */
584
-
585
- pull() {
586
- const values = [].map.call(arguments, this._cast, this);
587
- const cur = this[arrayParentSymbol].get(this[arrayPathSymbol]);
588
- let i = cur.length;
589
- let mem;
590
-
591
- while (i--) {
592
- mem = cur[i];
593
- if (mem instanceof Document) {
594
- const some = values.some(function(v) {
595
- return mem.equals(v);
596
- });
597
- if (some) {
598
- [].splice.call(cur, i, 1);
599
- }
600
- } else if (~cur.indexOf.call(values, mem)) {
601
- [].splice.call(cur, i, 1);
602
- }
603
- }
604
-
605
- if (values[0] instanceof ArraySubdocument) {
606
- this._registerAtomic('$pullDocs', values.map(function(v) {
607
- return v.$__getValue('_id') || v;
608
- }));
609
- } else {
610
- this._registerAtomic('$pullAll', values);
611
- }
612
-
613
- this._markModified();
614
-
615
- // Might have modified child paths and then pulled, like
616
- // `doc.children[1].name = 'test';` followed by
617
- // `doc.children.remove(doc.children[0]);`. In this case we fall back
618
- // to a `$set` on the whole array. See #3511
619
- if (cleanModifiedSubpaths(this[arrayParentSymbol], this[arrayPathSymbol]) > 0) {
620
- this._registerAtomic('$set', this);
621
- }
622
-
623
- return this;
624
- }
625
-
626
- /**
627
- * Wraps [`Array#push`](https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array/push) with proper change tracking.
628
- *
629
- * ####Example:
630
- *
631
- * const schema = Schema({ nums: [Number] });
632
- * const Model = mongoose.model('Test', schema);
633
- *
634
- * const doc = await Model.create({ nums: [3, 4] });
635
- * doc.nums.push(5); // Add 5 to the end of the array
636
- * await doc.save();
637
- *
638
- * // You can also pass an object with `$each` as the
639
- * // first parameter to use MongoDB's `$position`
640
- * doc.nums.push({
641
- * $each: [1, 2],
642
- * $position: 0
643
- * });
644
- * doc.nums; // [1, 2, 3, 4, 5]
645
- *
646
- * @param {Object} [args...]
647
- * @api public
648
- * @method push
649
- * @memberOf MongooseArray
650
- */
651
-
652
- push() {
653
- let values = arguments;
654
- let atomic = values;
655
- const isOverwrite = values[0] != null &&
656
- utils.hasUserDefinedProperty(values[0], '$each');
657
- const arr = this.isMongooseArrayProxy ? this.__array : this;
658
- if (isOverwrite) {
659
- atomic = values[0];
660
- values = values[0].$each;
661
- }
662
-
663
- if (this[arraySchemaSymbol] == null) {
664
- return _basePush.apply(this, values);
665
- }
666
-
667
- _checkManualPopulation(this, values);
668
-
669
- const parent = this[arrayParentSymbol];
670
- values = [].map.call(values, this._mapCast, this);
671
- values = this[arraySchemaSymbol].applySetters(values, parent, undefined,
672
- undefined, { skipDocumentArrayCast: true });
673
- let ret;
674
- const atomics = this[arrayAtomicsSymbol];
675
-
676
- if (isOverwrite) {
677
- atomic.$each = values;
678
-
679
- if (get(atomics, '$push.$each.length', 0) > 0 &&
680
- atomics.$push.$position != atomic.$position) {
681
- throw new MongooseError('Cannot call `Array#push()` multiple times ' +
682
- 'with different `$position`');
683
- }
684
-
685
- if (atomic.$position != null) {
686
- [].splice.apply(arr, [atomic.$position, 0].concat(values));
687
- ret = this.length;
688
- } else {
689
- ret = [].push.apply(arr, values);
690
- }
691
- } else {
692
- if (get(atomics, '$push.$each.length', 0) > 0 &&
693
- atomics.$push.$position != null) {
694
- throw new MongooseError('Cannot call `Array#push()` multiple times ' +
695
- 'with different `$position`');
696
- }
697
- atomic = values;
698
- ret = [].push.apply(arr, values);
699
- }
700
-
701
- this._registerAtomic('$push', atomic);
702
- this._markModified();
703
- return ret;
704
- }
705
-
706
- /**
707
- * Alias of [pull](#mongoosearray_MongooseArray-pull)
708
- *
709
- * @see MongooseArray#pull #types_array_MongooseArray-pull
710
- * @see mongodb http://www.mongodb.org/display/DOCS/Updating/#Updating-%24pull
711
- * @api public
712
- * @memberOf MongooseArray
713
- * @instance
714
- * @method remove
715
- */
716
-
717
- remove() {
718
- return this.pull.apply(this, arguments);
719
- }
720
-
721
- /**
722
- * Sets the casted `val` at index `i` and marks the array modified.
723
- *
724
- * ####Example:
725
- *
726
- * // given documents based on the following
727
- * const Doc = mongoose.model('Doc', new Schema({ array: [Number] }));
728
- *
729
- * const doc = new Doc({ array: [2,3,4] })
730
- *
731
- * console.log(doc.array) // [2,3,4]
732
- *
733
- * doc.array.set(1,"5");
734
- * console.log(doc.array); // [2,5,4] // properly cast to number
735
- * doc.save() // the change is saved
736
- *
737
- * // VS not using array#set
738
- * doc.array[1] = "5";
739
- * console.log(doc.array); // [2,"5",4] // no casting
740
- * doc.save() // change is not saved
741
- *
742
- * @return {Array} this
743
- * @api public
744
- * @method set
745
- * @memberOf MongooseArray
746
- */
747
-
748
- set(i, val, skipModified) {
749
- if (skipModified) {
750
- this[i] = val;
751
- return this;
752
- }
753
- const value = this._cast(val, i);
754
- this[i] = value;
755
- this._markModified(i);
756
- return this;
757
- }
758
-
759
- /**
760
- * Wraps [`Array#shift`](https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array/unshift) with proper change tracking.
761
- *
762
- * ####Example:
763
- *
764
- * doc.array = [2,3];
765
- * const res = doc.array.shift();
766
- * console.log(res) // 2
767
- * console.log(doc.array) // [3]
768
- *
769
- * ####Note:
770
- *
771
- * _marks the entire array as modified, which if saved, will store it as a `$set` operation, potentially overwritting any changes that happen between when you retrieved the object and when you save it._
772
- *
773
- * @api public
774
- * @method shift
775
- * @memberOf MongooseArray
776
- */
777
-
778
- shift() {
779
- const arr = this.isMongooseArrayProxy ? this.__array : this;
780
- const ret = [].shift.call(arr);
781
- this._registerAtomic('$set', this);
782
- this._markModified();
783
- return ret;
784
- }
785
-
786
- /**
787
- * Wraps [`Array#sort`](https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array/sort) with proper change tracking.
788
- *
789
- * ####NOTE:
790
- *
791
- * _marks the entire array as modified, which if saved, will store it as a `$set` operation, potentially overwritting any changes that happen between when you retrieved the object and when you save it._
792
- *
793
- * @api public
794
- * @method sort
795
- * @memberOf MongooseArray
796
- * @see https://masteringjs.io/tutorials/fundamentals/array-sort
797
- */
798
-
799
- sort() {
800
- const arr = this.isMongooseArrayProxy ? this.__array : this;
801
- const ret = [].sort.apply(arr, arguments);
802
- this._registerAtomic('$set', this);
803
- return ret;
804
- }
805
-
806
- /**
807
- * Wraps [`Array#splice`](https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array/splice) with proper change tracking and casting.
808
- *
809
- * ####Note:
810
- *
811
- * _marks the entire array as modified, which if saved, will store it as a `$set` operation, potentially overwritting any changes that happen between when you retrieved the object and when you save it._
812
- *
813
- * @api public
814
- * @method splice
815
- * @memberOf MongooseArray
816
- * @see https://masteringjs.io/tutorials/fundamentals/array-splice
817
- */
818
-
819
- splice() {
820
- let ret;
821
- const arr = this.isMongooseArrayProxy ? this.__array : this;
822
-
823
- _checkManualPopulation(this, Array.prototype.slice.call(arguments, 2));
824
-
825
- if (arguments.length) {
826
- let vals;
827
- if (this[arraySchemaSymbol] == null) {
828
- vals = arguments;
829
- } else {
830
- vals = [];
831
- for (let i = 0; i < arguments.length; ++i) {
832
- vals[i] = i < 2 ?
833
- arguments[i] :
834
- this._cast(arguments[i], arguments[0] + (i - 2));
835
- }
836
- }
837
-
838
- ret = [].splice.apply(arr, vals);
839
- this._registerAtomic('$set', this);
840
- }
841
-
842
- return ret;
843
- }
844
-
845
- /*!
846
- * ignore
847
- */
848
-
849
- toBSON() {
850
- return this.toObject(internalToObjectOptions);
851
- }
852
-
853
- /**
854
- * Returns a native js Array.
855
- *
856
- * @param {Object} options
857
- * @return {Array}
858
- * @api public
859
- * @method toObject
860
- * @memberOf MongooseArray
861
- */
862
-
863
- toObject(options) {
864
- const arr = this.isMongooseArrayProxy ? this.__array : this;
865
- if (options && options.depopulate) {
866
- options = utils.clone(options);
867
- options._isNested = true;
868
- // Ensure return value is a vanilla array, because in Node.js 6+ `map()`
869
- // is smart enough to use the inherited array's constructor.
870
- return [].concat(arr).map(function(doc) {
871
- return doc instanceof Document
872
- ? doc.toObject(options)
873
- : doc;
874
- });
875
- }
876
-
877
- return [].concat(arr);
878
- }
879
-
880
- $toObject() {
881
- return this.constructor.prototype.toObject.apply(this, arguments);
882
- }
883
-
884
- /**
885
- * Wraps [`Array#unshift`](https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array/unshift) with proper change tracking.
886
- *
887
- * ####Note:
888
- *
889
- * _marks the entire array as modified, which if saved, will store it as a `$set` operation, potentially overwriting any changes that happen between when you retrieved the object and when you save it._
890
- *
891
- * @api public
892
- * @method unshift
893
- * @memberOf MongooseArray
894
- */
895
-
896
- unshift() {
897
- _checkManualPopulation(this, arguments);
898
-
899
- let values;
900
- if (this[arraySchemaSymbol] == null) {
901
- values = arguments;
902
- } else {
903
- values = [].map.call(arguments, this._cast, this);
904
- values = this[arraySchemaSymbol].applySetters(values, this[arrayParentSymbol]);
905
- }
906
-
907
- const arr = this.isMongooseArrayProxy ? this.__array : this;
908
- [].unshift.apply(arr, values);
909
- this._registerAtomic('$set', this);
910
- this._markModified();
911
- return this.length;
912
- }
913
- }
914
-
915
- if (util.inspect.custom) {
916
- ArrayWrapper.prototype[util.inspect.custom] =
917
- ArrayWrapper.prototype.inspect;
918
- }
919
-
920
- /*!
921
- * ignore
922
- */
923
-
924
- function _isAllSubdocs(docs, ref) {
925
- if (!ref) {
926
- return false;
927
- }
928
-
929
- for (const arg of docs) {
930
- if (arg == null) {
931
- return false;
932
- }
933
- const model = arg.constructor;
934
- if (!(arg instanceof Document) ||
935
- (model.modelName !== ref && model.baseModelName !== ref)) {
936
- return false;
937
- }
938
- }
939
-
940
- return true;
941
- }
942
-
943
- /*!
944
- * ignore
945
- */
946
-
947
- function _checkManualPopulation(arr, docs) {
948
- const ref = arr == null ?
949
- null :
950
- get(arr[arraySchemaSymbol], 'caster.options.ref', null);
951
- if (arr.length === 0 &&
952
- docs.length > 0) {
953
- if (_isAllSubdocs(docs, ref)) {
954
- arr[arrayParentSymbol].$populated(arr[arrayPathSymbol], [], {
955
- [populateModelSymbol]: docs[0].constructor
956
- });
957
- }
958
- }
959
- }
960
-
961
- const returnVanillaArrayMethods = [
962
- 'filter',
963
- 'flat',
964
- 'flatMap',
965
- 'map',
966
- 'slice'
967
- ];
968
- for (const method of returnVanillaArrayMethods) {
969
- if (Array.prototype[method] == null) {
970
- continue;
971
- }
972
-
973
- ArrayWrapper.prototype[method] = function() {
974
- const _arr = this.isMongooseArrayProxy ? this.__array : this;
975
- const arr = [].concat(_arr);
976
-
977
- return arr[method].apply(arr, arguments);
978
- };
979
- }
980
-
981
- module.exports = ArrayWrapper;