mongoose 9.10.1 → 9.10.3
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.
- package/lib/collection.js +1 -2
- package/lib/drivers/node-mongodb-native/connection.js +5 -0
- package/lib/helpers/clone.js +9 -0
- package/lib/helpers/model/castBulkWrite.js +25 -10
- package/lib/helpers/query/castUpdate.js +26 -5
- package/lib/model.js +34 -16
- package/lib/schema/map.js +13 -14
- package/lib/schema/union.js +4 -0
- package/lib/schema.js +20 -6
- package/lib/types/documentArray/methods/index.js +20 -0
- package/package.json +1 -1
package/lib/collection.js
CHANGED
|
@@ -5,7 +5,6 @@
|
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
7
|
const EventEmitter = require('events').EventEmitter;
|
|
8
|
-
const STATES = require('./connectionState');
|
|
9
8
|
const immediate = require('./helpers/immediate');
|
|
10
9
|
|
|
11
10
|
/**
|
|
@@ -32,7 +31,7 @@ function Collection(name, conn, opts) {
|
|
|
32
31
|
this.buffer = !conn?._hasOpened;
|
|
33
32
|
this.emitter = new EventEmitter();
|
|
34
33
|
|
|
35
|
-
if (
|
|
34
|
+
if (conn?._hasOpened) {
|
|
36
35
|
this.onOpen();
|
|
37
36
|
}
|
|
38
37
|
}
|
|
@@ -495,6 +495,11 @@ function _setClient(conn, client, options, dbName) {
|
|
|
495
495
|
otherDb._flushQueue();
|
|
496
496
|
}
|
|
497
497
|
}
|
|
498
|
+
for (const collection of Object.values(conn.collections)) {
|
|
499
|
+
if (collection.buffer) {
|
|
500
|
+
collection.onOpen();
|
|
501
|
+
}
|
|
502
|
+
}
|
|
498
503
|
});
|
|
499
504
|
|
|
500
505
|
if (options.monitorCommands) {
|
package/lib/helpers/clone.js
CHANGED
|
@@ -197,6 +197,11 @@ function cloneObject(obj, options, isArrayChild) {
|
|
|
197
197
|
}
|
|
198
198
|
|
|
199
199
|
function cloneArray(arr, options) {
|
|
200
|
+
const seen = options?._seen;
|
|
201
|
+
if (seen && seen.has(arr)) {
|
|
202
|
+
return seen.get(arr);
|
|
203
|
+
}
|
|
204
|
+
|
|
200
205
|
let i = 0;
|
|
201
206
|
const len = arr.length;
|
|
202
207
|
|
|
@@ -218,6 +223,10 @@ function cloneArray(arr, options) {
|
|
|
218
223
|
ret = new Array(len);
|
|
219
224
|
}
|
|
220
225
|
|
|
226
|
+
if (seen) {
|
|
227
|
+
seen.set(arr, ret);
|
|
228
|
+
}
|
|
229
|
+
|
|
221
230
|
arr = isMongooseArray(arr) ? arr.__array : arr;
|
|
222
231
|
if (ret.isMongooseDocumentArray) {
|
|
223
232
|
// Create new options object to avoid mutating the shared options.
|
|
@@ -149,7 +149,7 @@ module.exports.castUpdateOne = function castUpdateOne(originalModel, updateOne,
|
|
|
149
149
|
model.schema.options.versionKey
|
|
150
150
|
);
|
|
151
151
|
|
|
152
|
-
updateOne['filter'] =
|
|
152
|
+
updateOne['filter'] = _castFilter(model, updateOne['filter'], {
|
|
153
153
|
strict: strict,
|
|
154
154
|
upsert: updateOne.upsert
|
|
155
155
|
});
|
|
@@ -176,11 +176,13 @@ module.exports.castUpdateMany = function castUpdateMany(originalModel, updateMan
|
|
|
176
176
|
const schema = model.schema;
|
|
177
177
|
const strict = options.strict ?? model.schema.options.strict;
|
|
178
178
|
|
|
179
|
+
const update = clone(updateMany['update']);
|
|
180
|
+
|
|
179
181
|
const globalSetDefaultsOnInsert = originalModel.base.options.setDefaultsOnInsert;
|
|
180
182
|
const shouldSetDefaultsOnInsert = updateMany.setDefaultsOnInsert ?? globalSetDefaultsOnInsert;
|
|
181
183
|
|
|
182
184
|
if (shouldSetDefaultsOnInsert !== false) {
|
|
183
|
-
setDefaultsOnInsert(updateMany['filter'], model.schema,
|
|
185
|
+
setDefaultsOnInsert(updateMany['filter'], model.schema, update, {
|
|
184
186
|
setDefaultsOnInsert: true,
|
|
185
187
|
upsert: updateMany.upsert
|
|
186
188
|
});
|
|
@@ -191,29 +193,29 @@ module.exports.castUpdateMany = function castUpdateMany(originalModel, updateMan
|
|
|
191
193
|
if (model.schema.$timestamps != null && doInitTimestamps) {
|
|
192
194
|
const createdAt = model.schema.$timestamps.createdAt;
|
|
193
195
|
const updatedAt = model.schema.$timestamps.updatedAt;
|
|
194
|
-
applyTimestampsToUpdate(now, createdAt, updatedAt,
|
|
196
|
+
applyTimestampsToUpdate(now, createdAt, updatedAt, update, {
|
|
195
197
|
timestamps: updateMany.timestamps,
|
|
196
198
|
overwriteImmutable: updateMany.overwriteImmutable
|
|
197
199
|
}, updateMany.upsert);
|
|
198
200
|
}
|
|
199
201
|
if (doInitTimestamps) {
|
|
200
|
-
applyTimestampsToChildren(now,
|
|
202
|
+
applyTimestampsToChildren(now, update, model.schema);
|
|
201
203
|
}
|
|
202
204
|
|
|
203
205
|
_addDiscriminatorToObject(schema, updateMany['filter']);
|
|
204
206
|
|
|
205
207
|
decorateUpdateWithVersionKey(
|
|
206
|
-
|
|
208
|
+
update,
|
|
207
209
|
updateMany,
|
|
208
210
|
model.schema.options.versionKey
|
|
209
211
|
);
|
|
210
212
|
|
|
211
|
-
updateMany['filter'] =
|
|
213
|
+
updateMany['filter'] = _castFilter(model, updateMany['filter'], {
|
|
212
214
|
strict: strict,
|
|
213
215
|
upsert: updateMany.upsert
|
|
214
216
|
});
|
|
215
217
|
|
|
216
|
-
updateMany['update'] = castUpdate(model.schema,
|
|
218
|
+
updateMany['update'] = castUpdate(model.schema, update, {
|
|
217
219
|
strict: strict,
|
|
218
220
|
upsert: updateMany.upsert,
|
|
219
221
|
arrayFilters: updateMany.arrayFilters,
|
|
@@ -228,7 +230,7 @@ module.exports.castReplaceOne = async function castReplaceOne(originalModel, rep
|
|
|
228
230
|
const strict = options.strict ?? model.schema.options.strict;
|
|
229
231
|
|
|
230
232
|
_addDiscriminatorToObject(schema, replaceOne['filter']);
|
|
231
|
-
replaceOne['filter'] =
|
|
233
|
+
replaceOne['filter'] = _castFilter(model, replaceOne['filter'], {
|
|
232
234
|
strict: strict,
|
|
233
235
|
upsert: replaceOne.upsert
|
|
234
236
|
});
|
|
@@ -262,7 +264,7 @@ module.exports.castDeleteOne = function castDeleteOne(originalModel, deleteOne)
|
|
|
262
264
|
|
|
263
265
|
_addDiscriminatorToObject(schema, deleteOne['filter']);
|
|
264
266
|
|
|
265
|
-
deleteOne['filter'] =
|
|
267
|
+
deleteOne['filter'] = _castFilter(model, deleteOne['filter']);
|
|
266
268
|
};
|
|
267
269
|
|
|
268
270
|
module.exports.castDeleteMany = function castDeleteMany(originalModel, deleteMany) {
|
|
@@ -271,7 +273,7 @@ module.exports.castDeleteMany = function castDeleteMany(originalModel, deleteMan
|
|
|
271
273
|
|
|
272
274
|
_addDiscriminatorToObject(schema, deleteMany['filter']);
|
|
273
275
|
|
|
274
|
-
deleteMany['filter'] =
|
|
276
|
+
deleteMany['filter'] = _castFilter(model, deleteMany['filter']);
|
|
275
277
|
};
|
|
276
278
|
|
|
277
279
|
module.exports.cast = {
|
|
@@ -283,6 +285,19 @@ module.exports.cast = {
|
|
|
283
285
|
deleteMany: module.exports.castDeleteMany
|
|
284
286
|
};
|
|
285
287
|
|
|
288
|
+
function _castFilter(model, filter, options) {
|
|
289
|
+
try {
|
|
290
|
+
return cast(model.schema, filter, options);
|
|
291
|
+
} catch (err) {
|
|
292
|
+
// Set the model on cast errors so `{MODEL}` gets replaced, like it is for
|
|
293
|
+
// query filter casting (gh-8300)
|
|
294
|
+
if (typeof err.setModel === 'function') {
|
|
295
|
+
err.setModel(model);
|
|
296
|
+
}
|
|
297
|
+
throw err;
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
|
|
286
301
|
function _addDiscriminatorToObject(schema, obj) {
|
|
287
302
|
if (schema == null) {
|
|
288
303
|
return;
|
|
@@ -262,7 +262,16 @@ function walkUpdatePath(schema, obj, op, options, context, filter, prefix) {
|
|
|
262
262
|
}
|
|
263
263
|
}
|
|
264
264
|
if (schematype?.schema != null) {
|
|
265
|
-
|
|
265
|
+
try {
|
|
266
|
+
obj[key] = cast(schematype.schema, obj[key], options, context);
|
|
267
|
+
} catch (error) {
|
|
268
|
+
if (error instanceof CastError) {
|
|
269
|
+
aggregatedError = _appendError(error, context, key, aggregatedError);
|
|
270
|
+
continue;
|
|
271
|
+
} else {
|
|
272
|
+
throw error;
|
|
273
|
+
}
|
|
274
|
+
}
|
|
266
275
|
hasKeys = true;
|
|
267
276
|
continue;
|
|
268
277
|
}
|
|
@@ -446,11 +455,15 @@ function walkUpdatePath(schema, obj, op, options, context, filter, prefix) {
|
|
|
446
455
|
}
|
|
447
456
|
} else {
|
|
448
457
|
if (op === '$rename') {
|
|
449
|
-
|
|
450
|
-
|
|
458
|
+
try {
|
|
459
|
+
if (obj[key] == null) {
|
|
460
|
+
throw new CastError('String', obj[key], `${prefix}${key}.$rename`);
|
|
461
|
+
}
|
|
462
|
+
const schematype = new SchemaString(`${prefix}${key}.$rename`, null, null, schema);
|
|
463
|
+
obj[key] = schematype.castForQuery(null, obj[key], context);
|
|
464
|
+
} catch (error) {
|
|
465
|
+
aggregatedError = _appendError(error, context, key, aggregatedError);
|
|
451
466
|
}
|
|
452
|
-
const schematype = new SchemaString(`${prefix}${key}.$rename`, null, null, schema);
|
|
453
|
-
obj[key] = schematype.castForQuery(null, obj[key], context);
|
|
454
467
|
continue;
|
|
455
468
|
}
|
|
456
469
|
|
|
@@ -499,6 +512,14 @@ function walkUpdatePath(schema, obj, op, options, context, filter, prefix) {
|
|
|
499
512
|
*/
|
|
500
513
|
|
|
501
514
|
function _appendError(error, query, key, aggregatedError) {
|
|
515
|
+
// Set the model on cast errors so `{MODEL}` gets replaced, like it is for
|
|
516
|
+
// query filter casting and document validation (gh-8300). `query` is a
|
|
517
|
+
// Query, or the model itself when casting a `bulkWrite()` operation.
|
|
518
|
+
const model = query?.modelName != null ? query : query?.model;
|
|
519
|
+
if (model != null && typeof error.setModel === 'function') {
|
|
520
|
+
error.setModel(model);
|
|
521
|
+
}
|
|
522
|
+
|
|
502
523
|
if (typeof query !== 'object' || !query.options.multipleCastError) {
|
|
503
524
|
throw error;
|
|
504
525
|
}
|
package/lib/model.js
CHANGED
|
@@ -3676,7 +3676,8 @@ Model.bulkSave = async function bulkSave(documents, options) {
|
|
|
3676
3676
|
|
|
3677
3677
|
await Promise.all(documents.map(doc => buildPreSavePromise(doc, options)));
|
|
3678
3678
|
|
|
3679
|
-
const
|
|
3679
|
+
const documentIndexes = [];
|
|
3680
|
+
const writeOperations = this.buildBulkWriteOperations(documents, options, documentIndexes);
|
|
3680
3681
|
const opts = { skipValidation: true, _skipCastBulkWrite: true, ...options };
|
|
3681
3682
|
const { bulkWriteResult, bulkWriteError } = await this.bulkWrite(writeOperations, opts).then(
|
|
3682
3683
|
(res) => ({ bulkWriteResult: res, bulkWriteError: null }),
|
|
@@ -3697,20 +3698,22 @@ Model.bulkSave = async function bulkSave(documents, options) {
|
|
|
3697
3698
|
);
|
|
3698
3699
|
}
|
|
3699
3700
|
|
|
3700
|
-
const
|
|
3701
|
-
|
|
3702
|
-
|
|
3703
|
-
|
|
3704
|
-
|
|
3705
|
-
|
|
3706
|
-
|
|
3707
|
-
|
|
3708
|
-
|
|
3709
|
-
|
|
3710
|
-
|
|
3711
|
-
|
|
3712
|
-
|
|
3713
|
-
successfulDocuments.
|
|
3701
|
+
const writeErrors = bulkWriteError?.writeErrors || [];
|
|
3702
|
+
let successfulDocuments = documents;
|
|
3703
|
+
if (writeErrors.length > 0) {
|
|
3704
|
+
if (options.ordered !== false) {
|
|
3705
|
+
// Unchanged documents don't generate operations, so map back to the input before slicing.
|
|
3706
|
+
successfulDocuments = documents.slice(0, documentIndexes[writeErrors[0].index]);
|
|
3707
|
+
} else {
|
|
3708
|
+
const failedDocumentIndexes = documents.length >= 25 ? {} : null;
|
|
3709
|
+
if (failedDocumentIndexes != null) {
|
|
3710
|
+
for (const writeError of writeErrors) {
|
|
3711
|
+
failedDocumentIndexes[documentIndexes[writeError.index]] = true;
|
|
3712
|
+
}
|
|
3713
|
+
}
|
|
3714
|
+
successfulDocuments = documents.filter((document, i) => failedDocumentIndexes == null ?
|
|
3715
|
+
!writeErrors.some(writeError => documentIndexes[writeError.index] === i) :
|
|
3716
|
+
!failedDocumentIndexes[i]);
|
|
3714
3717
|
}
|
|
3715
3718
|
}
|
|
3716
3719
|
await Promise.all(successfulDocuments.map(document => handleSuccessfulWrite(document, options)));
|
|
@@ -3951,6 +3954,18 @@ Model.castObject = function castObject(obj, options) {
|
|
|
3951
3954
|
}
|
|
3952
3955
|
|
|
3953
3956
|
if (error != null) {
|
|
3957
|
+
// `{MODEL}` is replaced for query casting and document validation, so do it
|
|
3958
|
+
// here too. Only the top-level call knows the model: the recursive calls for
|
|
3959
|
+
// subdocuments are invoked on the subdocument constructor, which has no
|
|
3960
|
+
// `modelName`, so this runs once over every collected error. (gh-8300)
|
|
3961
|
+
if (this.modelName != null) {
|
|
3962
|
+
for (const key of Object.keys(error.errors)) {
|
|
3963
|
+
const err = error.errors[key];
|
|
3964
|
+
if (typeof err.setModel === 'function') {
|
|
3965
|
+
err.setModel(this);
|
|
3966
|
+
}
|
|
3967
|
+
}
|
|
3968
|
+
}
|
|
3954
3969
|
throw error;
|
|
3955
3970
|
}
|
|
3956
3971
|
|
|
@@ -3964,11 +3979,12 @@ Model.castObject = function castObject(obj, options) {
|
|
|
3964
3979
|
* @param {object} options
|
|
3965
3980
|
* @param {boolean} options.skipValidation defaults to `false`, when set to true, building the write operations will bypass validating the documents.
|
|
3966
3981
|
* @param {boolean} options.timestamps defaults to `null`, when set to false, mongoose will not add/update timestamps to the documents.
|
|
3982
|
+
* @param {number[]} [documentIndexes] If provided, receives the input document index for each generated operation in order.
|
|
3967
3983
|
* @return {Promise[]} Returns a array of all Promises the function executes to be awaited.
|
|
3968
3984
|
* @api private
|
|
3969
3985
|
*/
|
|
3970
3986
|
|
|
3971
|
-
Model.buildBulkWriteOperations = function buildBulkWriteOperations(documents, options) {
|
|
3987
|
+
Model.buildBulkWriteOperations = function buildBulkWriteOperations(documents, options, documentIndexes) {
|
|
3972
3988
|
if (!Array.isArray(documents)) {
|
|
3973
3989
|
throw new MongooseError(`bulkSave expects an array of documents to be passed, received \`${documents}\` instead`);
|
|
3974
3990
|
}
|
|
@@ -3998,6 +4014,7 @@ Model.buildBulkWriteOperations = function buildBulkWriteOperations(documents, op
|
|
|
3998
4014
|
}
|
|
3999
4015
|
const writeOperation = { insertOne: { document } };
|
|
4000
4016
|
utils.injectTimestampsOption(writeOperation.insertOne, options.timestamps);
|
|
4017
|
+
documentIndexes?.push(i);
|
|
4001
4018
|
return writeOperation;
|
|
4002
4019
|
}
|
|
4003
4020
|
|
|
@@ -4024,6 +4041,7 @@ Model.buildBulkWriteOperations = function buildBulkWriteOperations(documents, op
|
|
|
4024
4041
|
document.$__version(where, delta);
|
|
4025
4042
|
const writeOperation = { updateOne: { filter: where, update: changes } };
|
|
4026
4043
|
utils.injectTimestampsOption(writeOperation.updateOne, options.timestamps);
|
|
4044
|
+
documentIndexes?.push(i);
|
|
4027
4045
|
return writeOperation;
|
|
4028
4046
|
}
|
|
4029
4047
|
|
package/lib/schema/map.js
CHANGED
|
@@ -30,20 +30,6 @@ class SchemaMap extends SchemaType {
|
|
|
30
30
|
this._createNestedSchemaType(parentSchema, key, options, schemaOptions);
|
|
31
31
|
}
|
|
32
32
|
|
|
33
|
-
/**
|
|
34
|
-
* Sets a default option for all Map instances.
|
|
35
|
-
*
|
|
36
|
-
* @param {string} option The option you'd like to set the value for
|
|
37
|
-
* @param {any} value value for option
|
|
38
|
-
* @return {undefined}
|
|
39
|
-
* @function set
|
|
40
|
-
* @api public
|
|
41
|
-
*/
|
|
42
|
-
|
|
43
|
-
set(option, value) {
|
|
44
|
-
return SchemaType.set(option, value);
|
|
45
|
-
}
|
|
46
|
-
|
|
47
33
|
/**
|
|
48
34
|
* Casts to Map
|
|
49
35
|
*
|
|
@@ -143,6 +129,19 @@ class SchemaMap extends SchemaType {
|
|
|
143
129
|
}
|
|
144
130
|
}
|
|
145
131
|
|
|
132
|
+
/**
|
|
133
|
+
* Sets a default option for all Map instances.
|
|
134
|
+
*
|
|
135
|
+
* @param {string} option The option you'd like to set the value for
|
|
136
|
+
* @param {any} value value for option
|
|
137
|
+
* @return {undefined}
|
|
138
|
+
* @function set
|
|
139
|
+
* @static
|
|
140
|
+
* @api public
|
|
141
|
+
*/
|
|
142
|
+
|
|
143
|
+
SchemaMap.set = SchemaType.set;
|
|
144
|
+
|
|
146
145
|
/**
|
|
147
146
|
* This schema type's name, to defend against minifiers that mangle
|
|
148
147
|
* function names.
|
package/lib/schema/union.js
CHANGED
|
@@ -60,6 +60,10 @@ class Union extends SchemaType {
|
|
|
60
60
|
|
|
61
61
|
// Setters also need to be aware of casting - we need to apply the setters of the entry in the union we choose.
|
|
62
62
|
applySetters(val, doc, init, prev, options) {
|
|
63
|
+
// Setters on the union path itself run first, so they can change which entry
|
|
64
|
+
// in the union the value resolves to.
|
|
65
|
+
val = this._applySetters(val, doc, init, prev, options);
|
|
66
|
+
|
|
63
67
|
let firstValue = firstValueSymbol;
|
|
64
68
|
let lastError;
|
|
65
69
|
// Loop through each schema type in the union. If one of the schematypes returns a value that is `=== val`, then
|
package/lib/schema.js
CHANGED
|
@@ -595,12 +595,6 @@ Schema.prototype.omit = function(paths, options) {
|
|
|
595
595
|
|
|
596
596
|
newSchema.remove(paths);
|
|
597
597
|
|
|
598
|
-
for (const nested in newSchema.singleNestedPaths) {
|
|
599
|
-
if (paths.includes(nested)) {
|
|
600
|
-
delete newSchema.singleNestedPaths[nested];
|
|
601
|
-
}
|
|
602
|
-
}
|
|
603
|
-
|
|
604
598
|
return newSchema;
|
|
605
599
|
};
|
|
606
600
|
|
|
@@ -2831,6 +2825,7 @@ Schema.prototype.remove = function(path) {
|
|
|
2831
2825
|
this.mapPaths = this.mapPaths.filter(
|
|
2832
2826
|
schemaType => !schemaType.path.startsWith(name + '.')
|
|
2833
2827
|
);
|
|
2828
|
+
_deleteSubpaths(this, name);
|
|
2834
2829
|
|
|
2835
2830
|
delete this.nested[name];
|
|
2836
2831
|
_deletePath(this, name);
|
|
@@ -2849,6 +2844,11 @@ Schema.prototype.remove = function(path) {
|
|
|
2849
2844
|
this.mapPaths = this.mapPaths.filter(
|
|
2850
2845
|
schemaType => !schemaType.path.startsWith(name + '.')
|
|
2851
2846
|
);
|
|
2847
|
+
// A document array registers its element paths under `subpaths` and a
|
|
2848
|
+
// single nested subdocument registers its paths under `singleNestedPaths`.
|
|
2849
|
+
// Left behind, they keep answering `path()` and `pathType()`, and queries
|
|
2850
|
+
// on the removed path keep being cast.
|
|
2851
|
+
_deleteSubpaths(this, name);
|
|
2852
2852
|
_deletePath(this, name);
|
|
2853
2853
|
|
|
2854
2854
|
this._removeEncryptedField(name);
|
|
@@ -2861,6 +2861,20 @@ Schema.prototype.remove = function(path) {
|
|
|
2861
2861
|
* ignore
|
|
2862
2862
|
*/
|
|
2863
2863
|
|
|
2864
|
+
function _deleteSubpaths(schema, name) {
|
|
2865
|
+
const prefix = name + '.';
|
|
2866
|
+
for (const subpath of Object.keys(schema.subpaths)) {
|
|
2867
|
+
if (subpath === name || subpath.startsWith(prefix)) {
|
|
2868
|
+
delete schema.subpaths[subpath];
|
|
2869
|
+
}
|
|
2870
|
+
}
|
|
2871
|
+
for (const subpath of Object.keys(schema.singleNestedPaths)) {
|
|
2872
|
+
if (subpath === name || subpath.startsWith(prefix)) {
|
|
2873
|
+
delete schema.singleNestedPaths[subpath];
|
|
2874
|
+
}
|
|
2875
|
+
}
|
|
2876
|
+
}
|
|
2877
|
+
|
|
2864
2878
|
function _deletePath(schema, name) {
|
|
2865
2879
|
const pieces = name.split('.');
|
|
2866
2880
|
const last = pieces.pop();
|
|
@@ -211,6 +211,26 @@ const methods = {
|
|
|
211
211
|
return this.constructor.prototype.toObject.apply(this, arguments);
|
|
212
212
|
},
|
|
213
213
|
|
|
214
|
+
/**
|
|
215
|
+
* Wraps [`MongooseArray#addToSet`](https://mongoosejs.com/docs/api/array.html#MongooseArray.prototype.addToSet())
|
|
216
|
+
* with proper change tracking.
|
|
217
|
+
*
|
|
218
|
+
* @param {...object} [args]
|
|
219
|
+
* @api public
|
|
220
|
+
* @method addToSet
|
|
221
|
+
* @memberOf MongooseDocumentArray
|
|
222
|
+
*/
|
|
223
|
+
|
|
224
|
+
addToSet() {
|
|
225
|
+
// Arguments are cast before duplicates are skipped, so any value added
|
|
226
|
+
// after a skipped duplicate is cast with an index that is too high.
|
|
227
|
+
const ret = ArrayMethods.addToSet.apply(this, arguments);
|
|
228
|
+
|
|
229
|
+
_updateSubdocIndexes(this);
|
|
230
|
+
|
|
231
|
+
return ret;
|
|
232
|
+
},
|
|
233
|
+
|
|
214
234
|
/**
|
|
215
235
|
* Wraps [`Array#push`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/push) with proper change tracking.
|
|
216
236
|
*
|