mongoose 8.14.3 → 8.15.0
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/dist/browser.umd.js +1 -1
- package/lib/drivers/node-mongodb-native/connection.js +65 -0
- package/lib/drivers/node-mongodb-native/index.js +1 -0
- package/lib/error/serverSelection.js +1 -0
- package/lib/helpers/model/discriminator.js +47 -0
- package/lib/helpers/query/cast$expr.js +4 -1
- package/lib/model.js +50 -23
- package/lib/query.js +56 -0
- package/lib/schema/array.js +4 -0
- package/lib/schema/bigint.js +4 -0
- package/lib/schema/boolean.js +4 -0
- package/lib/schema/buffer.js +4 -0
- package/lib/schema/date.js +4 -0
- package/lib/schema/decimal128.js +4 -0
- package/lib/schema/double.js +4 -0
- package/lib/schema/int32.js +4 -0
- package/lib/schema/map.js +4 -0
- package/lib/schema/objectId.js +4 -0
- package/lib/schema/string.js +4 -0
- package/lib/schema/uuid.js +4 -0
- package/lib/schema.js +182 -2
- package/lib/schemaType.js +8 -0
- package/package.json +4 -2
- package/types/index.d.ts +31 -3
- package/types/models.d.ts +6 -0
- package/types/query.d.ts +1 -1
- package/types/schemaoptions.d.ts +5 -0
- package/types/schematypes.d.ts +29 -0
- package/types/types.d.ts +2 -2
- package/types/utility.d.ts +31 -7
|
@@ -12,6 +12,7 @@ const pkg = require('../../../package.json');
|
|
|
12
12
|
const processConnectionOptions = require('../../helpers/processConnectionOptions');
|
|
13
13
|
const setTimeout = require('../../helpers/timers').setTimeout;
|
|
14
14
|
const utils = require('../../utils');
|
|
15
|
+
const Schema = require('../../schema');
|
|
15
16
|
|
|
16
17
|
/**
|
|
17
18
|
* A [node-mongodb-native](https://github.com/mongodb/node-mongodb-native) connection implementation.
|
|
@@ -320,6 +321,20 @@ NativeConnection.prototype.createClient = async function createClient(uri, optio
|
|
|
320
321
|
};
|
|
321
322
|
}
|
|
322
323
|
|
|
324
|
+
const { schemaMap, encryptedFieldsMap } = this._buildEncryptionSchemas();
|
|
325
|
+
|
|
326
|
+
if ((Object.keys(schemaMap).length > 0 || Object.keys(encryptedFieldsMap).length) && !options.autoEncryption) {
|
|
327
|
+
throw new Error('Must provide `autoEncryption` when connecting with encrypted schemas.');
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
if (Object.keys(schemaMap).length > 0) {
|
|
331
|
+
options.autoEncryption.schemaMap = schemaMap;
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
if (Object.keys(encryptedFieldsMap).length > 0) {
|
|
335
|
+
options.autoEncryption.encryptedFieldsMap = encryptedFieldsMap;
|
|
336
|
+
}
|
|
337
|
+
|
|
323
338
|
this.readyState = STATES.connecting;
|
|
324
339
|
this._connectionString = uri;
|
|
325
340
|
|
|
@@ -343,6 +358,56 @@ NativeConnection.prototype.createClient = async function createClient(uri, optio
|
|
|
343
358
|
return this;
|
|
344
359
|
};
|
|
345
360
|
|
|
361
|
+
/**
|
|
362
|
+
* Given a connection, which may or may not have encrypted models, build
|
|
363
|
+
* a schemaMap and/or an encryptedFieldsMap for the connection, combining all models
|
|
364
|
+
* into a single schemaMap and encryptedFields map.
|
|
365
|
+
*
|
|
366
|
+
* @returns the generated schemaMap and encryptedFieldsMap
|
|
367
|
+
*/
|
|
368
|
+
NativeConnection.prototype._buildEncryptionSchemas = function() {
|
|
369
|
+
const qeMappings = {};
|
|
370
|
+
const csfleMappings = {};
|
|
371
|
+
|
|
372
|
+
const encryptedModels = Object.values(this.models).filter(model => model.schema._hasEncryptedFields());
|
|
373
|
+
|
|
374
|
+
// If discriminators are configured for the collection, there might be multiple models
|
|
375
|
+
// pointing to the same namespace. For this scenario, we merge all the schemas for each namespace
|
|
376
|
+
// into a single schema and then generate a schemaMap/encryptedFieldsMap for the combined schema.
|
|
377
|
+
for (const model of encryptedModels) {
|
|
378
|
+
const { schema, collection: { collectionName } } = model;
|
|
379
|
+
const namespace = `${this.$dbName}.${collectionName}`;
|
|
380
|
+
const mappings = schema.encryptionType() === 'csfle' ? csfleMappings : qeMappings;
|
|
381
|
+
|
|
382
|
+
mappings[namespace] ??= new Schema({}, { encryptionType: schema.encryptionType() });
|
|
383
|
+
|
|
384
|
+
const isNonRootDiscriminator = schema.discriminatorMapping && !schema.discriminatorMapping.isRoot;
|
|
385
|
+
if (isNonRootDiscriminator) {
|
|
386
|
+
const rootSchema = schema._baseSchema;
|
|
387
|
+
schema.eachPath((pathname) => {
|
|
388
|
+
if (rootSchema.path(pathname)) return;
|
|
389
|
+
if (!mappings[namespace]._hasEncryptedField(pathname)) return;
|
|
390
|
+
|
|
391
|
+
throw new Error(`Cannot have duplicate keys in discriminators with encryption. key=${pathname}`);
|
|
392
|
+
});
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
mappings[namespace].add(schema);
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
const schemaMap = Object.fromEntries(Object.entries(csfleMappings).map(
|
|
399
|
+
([namespace, schema]) => ([namespace, schema._buildSchemaMap()])
|
|
400
|
+
));
|
|
401
|
+
|
|
402
|
+
const encryptedFieldsMap = Object.fromEntries(Object.entries(qeMappings).map(
|
|
403
|
+
([namespace, schema]) => ([namespace, schema._buildEncryptedFields()])
|
|
404
|
+
));
|
|
405
|
+
|
|
406
|
+
return {
|
|
407
|
+
schemaMap, encryptedFieldsMap
|
|
408
|
+
};
|
|
409
|
+
};
|
|
410
|
+
|
|
346
411
|
/*!
|
|
347
412
|
* ignore
|
|
348
413
|
*/
|
|
@@ -17,6 +17,51 @@ const CUSTOMIZABLE_DISCRIMINATOR_OPTIONS = {
|
|
|
17
17
|
methods: true
|
|
18
18
|
};
|
|
19
19
|
|
|
20
|
+
/**
|
|
21
|
+
* Validate fields declared on the child schema when either schema is configured for encryption. Specifically, this function ensures that:
|
|
22
|
+
*
|
|
23
|
+
* - any encrypted fields are declared on exactly one of the schemas (not both)
|
|
24
|
+
* - encrypted fields cannot be declared on either the parent or child schema, where the other schema declares the same field without encryption.
|
|
25
|
+
*
|
|
26
|
+
* @param {Schema} parentSchema
|
|
27
|
+
* @param {Schema} childSchema
|
|
28
|
+
*/
|
|
29
|
+
function validateDiscriminatorSchemasForEncryption(parentSchema, childSchema) {
|
|
30
|
+
if (parentSchema.encryptionType() == null && childSchema.encryptionType() == null) return;
|
|
31
|
+
|
|
32
|
+
const allSharedNestedPaths = setIntersection(
|
|
33
|
+
allNestedPaths(parentSchema),
|
|
34
|
+
allNestedPaths(childSchema)
|
|
35
|
+
);
|
|
36
|
+
|
|
37
|
+
for (const path of allSharedNestedPaths) {
|
|
38
|
+
if (parentSchema._hasEncryptedField(path) && childSchema._hasEncryptedField(path)) {
|
|
39
|
+
throw new Error(`encrypted fields cannot be declared on both the base schema and the child schema in a discriminator. path=${path}`);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
if (parentSchema._hasEncryptedField(path) || childSchema._hasEncryptedField(path)) {
|
|
43
|
+
throw new Error(`encrypted fields cannot have the same path as a non-encrypted field for discriminators. path=${path}`);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function allNestedPaths(schema) {
|
|
48
|
+
return [...Object.keys(schema.paths), ...Object.keys(schema.singleNestedPaths)];
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* @param {Iterable<string>} i1
|
|
53
|
+
* @param {Iterable<string>} i2
|
|
54
|
+
*/
|
|
55
|
+
function* setIntersection(i1, i2) {
|
|
56
|
+
const s1 = new Set(i1);
|
|
57
|
+
for (const item of i2) {
|
|
58
|
+
if (s1.has(item)) {
|
|
59
|
+
yield item;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
20
65
|
/*!
|
|
21
66
|
* ignore
|
|
22
67
|
*/
|
|
@@ -80,6 +125,8 @@ module.exports = function discriminator(model, name, schema, tiedValue, applyPlu
|
|
|
80
125
|
value = tiedValue;
|
|
81
126
|
}
|
|
82
127
|
|
|
128
|
+
validateDiscriminatorSchemasForEncryption(model.schema, schema);
|
|
129
|
+
|
|
83
130
|
function merge(schema, baseSchema) {
|
|
84
131
|
// Retain original schema before merging base schema
|
|
85
132
|
schema._baseSchema = baseSchema;
|
|
@@ -69,8 +69,11 @@ const dateOperators = new Set([
|
|
|
69
69
|
const expressionOperator = new Set(['$not']);
|
|
70
70
|
|
|
71
71
|
module.exports = function cast$expr(val, schema, strictQuery) {
|
|
72
|
+
if (typeof val === 'boolean') {
|
|
73
|
+
return val;
|
|
74
|
+
}
|
|
72
75
|
if (typeof val !== 'object' || val === null) {
|
|
73
|
-
throw new Error('`$expr` must be an object');
|
|
76
|
+
throw new Error('`$expr` must be an object or boolean literal');
|
|
74
77
|
}
|
|
75
78
|
|
|
76
79
|
return _castExpression(val, schema, strictQuery);
|
package/lib/model.js
CHANGED
|
@@ -69,7 +69,6 @@ const minimize = require('./helpers/minimize');
|
|
|
69
69
|
const MongooseBulkSaveIncompleteError = require('./error/bulkSaveIncompleteError');
|
|
70
70
|
const ObjectExpectedError = require('./error/objectExpected');
|
|
71
71
|
const decorateBulkWriteResult = require('./helpers/model/decorateBulkWriteResult');
|
|
72
|
-
|
|
73
72
|
const modelCollectionSymbol = Symbol('mongoose#Model#collection');
|
|
74
73
|
const modelDbSymbol = Symbol('mongoose#Model#db');
|
|
75
74
|
const modelSymbol = require('./helpers/symbols').modelSymbol;
|
|
@@ -3394,7 +3393,7 @@ Model.bulkWrite = async function bulkWrite(ops, options) {
|
|
|
3394
3393
|
return bulkWriteResult;
|
|
3395
3394
|
}
|
|
3396
3395
|
|
|
3397
|
-
const validations = ops.map(op => castBulkWrite(this, op, options));
|
|
3396
|
+
const validations = options?._skipCastBulkWrite ? [] : ops.map(op => castBulkWrite(this, op, options));
|
|
3398
3397
|
const asyncLocalStorage = this.db.base.transactionAsyncLocalStorage?.getStore();
|
|
3399
3398
|
if ((!options || !options.hasOwnProperty('session')) && asyncLocalStorage?.session != null) {
|
|
3400
3399
|
options = { ...options, session: asyncLocalStorage.session };
|
|
@@ -3431,6 +3430,9 @@ Model.bulkWrite = async function bulkWrite(ops, options) {
|
|
|
3431
3430
|
let validationErrors = [];
|
|
3432
3431
|
const results = [];
|
|
3433
3432
|
await new Promise((resolve) => {
|
|
3433
|
+
if (validations.length === 0) {
|
|
3434
|
+
return resolve();
|
|
3435
|
+
}
|
|
3434
3436
|
for (let i = 0; i < validations.length; ++i) {
|
|
3435
3437
|
validations[i]((err) => {
|
|
3436
3438
|
if (err == null) {
|
|
@@ -3568,8 +3570,9 @@ Model.bulkSave = async function bulkSave(documents, options) {
|
|
|
3568
3570
|
|
|
3569
3571
|
await Promise.all(documents.map(doc => buildPreSavePromise(doc, options)));
|
|
3570
3572
|
|
|
3571
|
-
const writeOperations = this.buildBulkWriteOperations(documents,
|
|
3572
|
-
const
|
|
3573
|
+
const writeOperations = this.buildBulkWriteOperations(documents, options);
|
|
3574
|
+
const opts = { skipValidation: true, _skipCastBulkWrite: true, ...options };
|
|
3575
|
+
const { bulkWriteResult, bulkWriteError } = await this.bulkWrite(writeOperations, opts).then(
|
|
3573
3576
|
(res) => ({ bulkWriteResult: res, bulkWriteError: null }),
|
|
3574
3577
|
(err) => ({ bulkWriteResult: null, bulkWriteError: err })
|
|
3575
3578
|
);
|
|
@@ -3868,16 +3871,17 @@ Model.buildBulkWriteOperations = function buildBulkWriteOperations(documents, op
|
|
|
3868
3871
|
}
|
|
3869
3872
|
|
|
3870
3873
|
setDefaultOptions();
|
|
3871
|
-
const discriminatorKey = this.schema.options.discriminatorKey;
|
|
3872
3874
|
|
|
3873
|
-
const writeOperations = documents.
|
|
3875
|
+
const writeOperations = documents.map((document, i) => {
|
|
3874
3876
|
if (!options.skipValidation) {
|
|
3875
3877
|
if (!(document instanceof Document)) {
|
|
3876
3878
|
throw new Error(`documents.${i} was not a mongoose document, documents must be an array of mongoose documents (instanceof mongoose.Document).`);
|
|
3877
3879
|
}
|
|
3878
|
-
|
|
3879
|
-
|
|
3880
|
-
|
|
3880
|
+
if (options.validateBeforeSave == null || options.validateBeforeSave) {
|
|
3881
|
+
const err = document.validateSync();
|
|
3882
|
+
if (err != null) {
|
|
3883
|
+
throw err;
|
|
3884
|
+
}
|
|
3881
3885
|
}
|
|
3882
3886
|
}
|
|
3883
3887
|
|
|
@@ -3885,9 +3889,7 @@ Model.buildBulkWriteOperations = function buildBulkWriteOperations(documents, op
|
|
|
3885
3889
|
if (isANewDocument) {
|
|
3886
3890
|
const writeOperation = { insertOne: { document } };
|
|
3887
3891
|
utils.injectTimestampsOption(writeOperation.insertOne, options.timestamps);
|
|
3888
|
-
|
|
3889
|
-
|
|
3890
|
-
return accumulator;
|
|
3892
|
+
return writeOperation;
|
|
3891
3893
|
}
|
|
3892
3894
|
|
|
3893
3895
|
const delta = document.$__delta();
|
|
@@ -3910,22 +3912,14 @@ Model.buildBulkWriteOperations = function buildBulkWriteOperations(documents, op
|
|
|
3910
3912
|
}
|
|
3911
3913
|
}
|
|
3912
3914
|
|
|
3913
|
-
// Set the discriminator key, so bulk write casting knows which
|
|
3914
|
-
// schema to use re: gh-13907
|
|
3915
|
-
if (document[discriminatorKey] != null && !(discriminatorKey in where)) {
|
|
3916
|
-
where[discriminatorKey] = document[discriminatorKey];
|
|
3917
|
-
}
|
|
3918
|
-
|
|
3919
3915
|
document.$__version(where, delta);
|
|
3920
3916
|
const writeOperation = { updateOne: { filter: where, update: changes } };
|
|
3921
3917
|
utils.injectTimestampsOption(writeOperation.updateOne, options.timestamps);
|
|
3922
|
-
|
|
3923
|
-
|
|
3924
|
-
return accumulator;
|
|
3918
|
+
return writeOperation;
|
|
3925
3919
|
}
|
|
3926
3920
|
|
|
3927
|
-
return
|
|
3928
|
-
}
|
|
3921
|
+
return null;
|
|
3922
|
+
}).filter(op => op !== null);
|
|
3929
3923
|
|
|
3930
3924
|
return writeOperations;
|
|
3931
3925
|
|
|
@@ -4893,6 +4887,39 @@ Model.compile = function compile(name, schema, collectionName, connection, base)
|
|
|
4893
4887
|
return model;
|
|
4894
4888
|
};
|
|
4895
4889
|
|
|
4890
|
+
/**
|
|
4891
|
+
* If auto encryption is enabled, returns a ClientEncryption instance that is configured with the same settings that
|
|
4892
|
+
* Mongoose's underlying MongoClient is using. If the client has not yet been configured, returns null.
|
|
4893
|
+
*
|
|
4894
|
+
* @returns {ClientEncryption | null}
|
|
4895
|
+
*/
|
|
4896
|
+
Model.clientEncryption = function clientEncryption() {
|
|
4897
|
+
const ClientEncryption = this.base.driver.get().ClientEncryption;
|
|
4898
|
+
if (!ClientEncryption) {
|
|
4899
|
+
throw new Error('The mongodb driver must be used to obtain a ClientEncryption object.');
|
|
4900
|
+
}
|
|
4901
|
+
|
|
4902
|
+
const client = this.collection?.conn?.client;
|
|
4903
|
+
|
|
4904
|
+
if (!client) return null;
|
|
4905
|
+
|
|
4906
|
+
const autoEncryptionOptions = client.options.autoEncryption;
|
|
4907
|
+
|
|
4908
|
+
if (!autoEncryptionOptions) return null;
|
|
4909
|
+
|
|
4910
|
+
const {
|
|
4911
|
+
keyVaultNamespace,
|
|
4912
|
+
keyVaultClient,
|
|
4913
|
+
kmsProviders,
|
|
4914
|
+
credentialProviders,
|
|
4915
|
+
proxyOptions,
|
|
4916
|
+
tlsOptions
|
|
4917
|
+
} = autoEncryptionOptions;
|
|
4918
|
+
return new ClientEncryption(keyVaultClient ?? client,
|
|
4919
|
+
{ keyVaultNamespace, kmsProviders, credentialProviders, proxyOptions, tlsOptions }
|
|
4920
|
+
);
|
|
4921
|
+
};
|
|
4922
|
+
|
|
4896
4923
|
/**
|
|
4897
4924
|
* Update this model to use the new connection, including updating all internal
|
|
4898
4925
|
* references and creating a new `Collection` instance using the new connection.
|
package/lib/query.js
CHANGED
|
@@ -165,6 +165,32 @@ function Query(conditions, options, model, collection) {
|
|
|
165
165
|
}
|
|
166
166
|
}
|
|
167
167
|
|
|
168
|
+
// Helper function to check if an object is empty or contains only empty objects/arrays
|
|
169
|
+
function isEmptyFilter(obj) {
|
|
170
|
+
if (obj == null) return true;
|
|
171
|
+
if (typeof obj !== 'object') return true;
|
|
172
|
+
if (Object.keys(obj).length === 0) return true;
|
|
173
|
+
|
|
174
|
+
// Check $and, $or, $nor arrays
|
|
175
|
+
for (const key of ['$and', '$or', '$nor']) {
|
|
176
|
+
if (Array.isArray(obj[key])) {
|
|
177
|
+
// If array is empty or all elements are empty objects, consider it empty
|
|
178
|
+
if (obj[key].length === 0 || obj[key].every(item => isEmptyFilter(item))) {
|
|
179
|
+
return true;
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
return false;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
// Helper function to check for empty/invalid filter
|
|
188
|
+
function checkRequireFilter(filter, options) {
|
|
189
|
+
if (options && options.requireFilter && isEmptyFilter(filter)) {
|
|
190
|
+
throw new Error('Empty or invalid filter not allowed with requireFilter enabled');
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
|
|
168
194
|
/*!
|
|
169
195
|
* inherit mquery
|
|
170
196
|
*/
|
|
@@ -3111,6 +3137,7 @@ function _handleSortValue(val, key) {
|
|
|
3111
3137
|
*
|
|
3112
3138
|
* @param {Object|Query} [filter] mongodb selector
|
|
3113
3139
|
* @param {Object} [options] optional see [`Query.prototype.setOptions()`](https://mongoosejs.com/docs/api/query.html#Query.prototype.setOptions())
|
|
3140
|
+
* @param {Boolean} [options.requireFilter=false] If true, throws an error if the filter is empty (`{}`)
|
|
3114
3141
|
* @return {Query} this
|
|
3115
3142
|
* @see DeleteResult https://mongodb.github.io/node-mongodb-native/6.15/interfaces/DeleteResult.html
|
|
3116
3143
|
* @see deleteOne https://mongodb.github.io/node-mongodb-native/6.15/classes/Collection.html#deleteOne
|
|
@@ -3148,6 +3175,9 @@ Query.prototype._deleteOne = async function _deleteOne() {
|
|
|
3148
3175
|
this._applyTranslateAliases();
|
|
3149
3176
|
this._castConditions();
|
|
3150
3177
|
|
|
3178
|
+
// Check for empty/invalid filter with requireFilter option
|
|
3179
|
+
checkRequireFilter(this._conditions, this.options);
|
|
3180
|
+
|
|
3151
3181
|
if (this.error() != null) {
|
|
3152
3182
|
throw this.error();
|
|
3153
3183
|
}
|
|
@@ -3183,6 +3213,7 @@ Query.prototype._deleteOne = async function _deleteOne() {
|
|
|
3183
3213
|
*
|
|
3184
3214
|
* @param {Object|Query} [filter] mongodb selector
|
|
3185
3215
|
* @param {Object} [options] optional see [`Query.prototype.setOptions()`](https://mongoosejs.com/docs/api/query.html#Query.prototype.setOptions())
|
|
3216
|
+
* @param {Boolean} [options.requireFilter=false] If true, throws an error if the filter is empty (`{}`)
|
|
3186
3217
|
* @return {Query} this
|
|
3187
3218
|
* @see DeleteResult https://mongodb.github.io/node-mongodb-native/6.15/interfaces/DeleteResult.html
|
|
3188
3219
|
* @see deleteMany https://mongodb.github.io/node-mongodb-native/6.15/classes/Collection.html#deleteMany
|
|
@@ -3220,6 +3251,9 @@ Query.prototype._deleteMany = async function _deleteMany() {
|
|
|
3220
3251
|
this._applyTranslateAliases();
|
|
3221
3252
|
this._castConditions();
|
|
3222
3253
|
|
|
3254
|
+
// Check for empty/invalid filter with requireFilter option
|
|
3255
|
+
checkRequireFilter(this._conditions, this.options);
|
|
3256
|
+
|
|
3223
3257
|
if (this.error() != null) {
|
|
3224
3258
|
throw this.error();
|
|
3225
3259
|
}
|
|
@@ -3311,6 +3345,7 @@ function prepareDiscriminatorCriteria(query) {
|
|
|
3311
3345
|
* - `maxTimeMS`: puts a time limit on the query - requires mongodb >= 2.6.0
|
|
3312
3346
|
* - `runValidators`: if true, runs [update validators](https://mongoosejs.com/docs/validation.html#update-validators) on this command. Update validators validate the update operation against the model's schema.
|
|
3313
3347
|
* - `setDefaultsOnInsert`: `true` by default. If `setDefaultsOnInsert` and `upsert` are true, mongoose will apply the [defaults](https://mongoosejs.com/docs/defaults.html) specified in the model's schema if a new document is created.
|
|
3348
|
+
* - `requireFilter`: bool - if true, throws an error if the filter is empty (`{}`). Defaults to false.
|
|
3314
3349
|
*
|
|
3315
3350
|
* #### Example:
|
|
3316
3351
|
*
|
|
@@ -3337,6 +3372,7 @@ function prepareDiscriminatorCriteria(query) {
|
|
|
3337
3372
|
* @param {Boolean} [options.translateAliases=null] If set to `true`, translates any schema-defined aliases in `filter`, `projection`, `update`, and `distinct`. Throws an error if there are any conflicts where both alias and raw property are defined on the same object.
|
|
3338
3373
|
* @param {Boolean} [options.overwriteDiscriminatorKey=false] Mongoose removes discriminator key updates from `update` by default, set `overwriteDiscriminatorKey` to `true` to allow updating the discriminator key
|
|
3339
3374
|
* @param {Boolean} [options.overwriteImmutable=false] Mongoose removes updated immutable properties from `update` by default (excluding $setOnInsert). Set `overwriteImmutable` to `true` to allow updating immutable properties using other update operators.
|
|
3375
|
+
* @param {Boolean} [options.requireFilter=false] If true, throws an error if the filter is empty (`{}`)
|
|
3340
3376
|
* @see Tutorial https://mongoosejs.com/docs/tutorials/findoneandupdate.html
|
|
3341
3377
|
* @see findAndModify command https://www.mongodb.com/docs/manual/reference/command/findAndModify/
|
|
3342
3378
|
* @see ModifyResult https://mongodb.github.io/node-mongodb-native/4.9/interfaces/ModifyResult.html
|
|
@@ -3417,6 +3453,9 @@ Query.prototype._findOneAndUpdate = async function _findOneAndUpdate() {
|
|
|
3417
3453
|
this._applyTranslateAliases();
|
|
3418
3454
|
this._castConditions();
|
|
3419
3455
|
|
|
3456
|
+
// Check for empty/invalid filter with requireFilter option
|
|
3457
|
+
checkRequireFilter(this._conditions, this.options);
|
|
3458
|
+
|
|
3420
3459
|
_castArrayFilters(this);
|
|
3421
3460
|
|
|
3422
3461
|
if (this.error()) {
|
|
@@ -3500,6 +3539,7 @@ Query.prototype._findOneAndUpdate = async function _findOneAndUpdate() {
|
|
|
3500
3539
|
*
|
|
3501
3540
|
* - `sort`: if multiple docs are found by the conditions, sets the sort order to choose which doc to update
|
|
3502
3541
|
* - `maxTimeMS`: puts a time limit on the query - requires mongodb >= 2.6.0
|
|
3542
|
+
* - `requireFilter`: bool - if true, throws an error if the filter is empty (`{}`). Defaults to false.
|
|
3503
3543
|
*
|
|
3504
3544
|
* #### Example:
|
|
3505
3545
|
*
|
|
@@ -3512,6 +3552,7 @@ Query.prototype._findOneAndUpdate = async function _findOneAndUpdate() {
|
|
|
3512
3552
|
* @param {Object} [filter]
|
|
3513
3553
|
* @param {Object} [options]
|
|
3514
3554
|
* @param {Boolean} [options.includeResultMetadata] if true, returns the full [ModifyResult from the MongoDB driver](https://mongodb.github.io/node-mongodb-native/4.9/interfaces/ModifyResult.html) rather than just the document
|
|
3555
|
+
* @param {Boolean} [options.requireFilter=false] If true, throws an error if the filter is empty (`{}`)
|
|
3515
3556
|
* @param {ClientSession} [options.session=null] The session associated with this query. See [transactions docs](https://mongoosejs.com/docs/transactions.html).
|
|
3516
3557
|
* @param {Boolean|String} [options.strict] overwrites the schema's [strict mode option](https://mongoosejs.com/docs/guide.html#strict)
|
|
3517
3558
|
* @return {Query} this
|
|
@@ -3551,6 +3592,9 @@ Query.prototype._findOneAndDelete = async function _findOneAndDelete() {
|
|
|
3551
3592
|
this._applyTranslateAliases();
|
|
3552
3593
|
this._castConditions();
|
|
3553
3594
|
|
|
3595
|
+
// Check for empty/invalid filter with requireFilter option
|
|
3596
|
+
checkRequireFilter(this._conditions, this.options);
|
|
3597
|
+
|
|
3554
3598
|
if (this.error() != null) {
|
|
3555
3599
|
throw this.error();
|
|
3556
3600
|
}
|
|
@@ -3590,6 +3634,7 @@ Query.prototype._findOneAndDelete = async function _findOneAndDelete() {
|
|
|
3590
3634
|
* - `sort`: if multiple docs are found by the conditions, sets the sort order to choose which doc to update
|
|
3591
3635
|
* - `maxTimeMS`: puts a time limit on the query - requires mongodb >= 2.6.0
|
|
3592
3636
|
* - `includeResultMetadata`: if true, returns the full [ModifyResult from the MongoDB driver](https://mongodb.github.io/node-mongodb-native/4.9/interfaces/ModifyResult.html) rather than just the document
|
|
3637
|
+
* - `requireFilter`: bool - if true, throws an error if the filter is empty (`{}`). Defaults to false.
|
|
3593
3638
|
*
|
|
3594
3639
|
* #### Example:
|
|
3595
3640
|
*
|
|
@@ -3612,6 +3657,7 @@ Query.prototype._findOneAndDelete = async function _findOneAndDelete() {
|
|
|
3612
3657
|
* @param {Boolean} [options.timestamps=null] If set to `false` and [schema-level timestamps](https://mongoosejs.com/docs/guide.html#timestamps) are enabled, skip timestamps for this update. Note that this allows you to overwrite timestamps. Does nothing if schema-level timestamps are not set.
|
|
3613
3658
|
* @param {Boolean} [options.returnOriginal=null] An alias for the `new` option. `returnOriginal: false` is equivalent to `new: true`.
|
|
3614
3659
|
* @param {Boolean} [options.translateAliases=null] If set to `true`, translates any schema-defined aliases in `filter`, `projection`, `update`, and `distinct`. Throws an error if there are any conflicts where both alias and raw property are defined on the same object.
|
|
3660
|
+
* @param {Boolean} [options.requireFilter=false] If true, throws an error if the filter is empty (`{}`)
|
|
3615
3661
|
* @return {Query} this
|
|
3616
3662
|
* @api public
|
|
3617
3663
|
*/
|
|
@@ -3667,6 +3713,10 @@ Query.prototype.findOneAndReplace = function(filter, replacement, options) {
|
|
|
3667
3713
|
Query.prototype._findOneAndReplace = async function _findOneAndReplace() {
|
|
3668
3714
|
this._applyTranslateAliases();
|
|
3669
3715
|
this._castConditions();
|
|
3716
|
+
|
|
3717
|
+
// Check for empty/invalid filter with requireFilter option
|
|
3718
|
+
checkRequireFilter(this._conditions, this.options);
|
|
3719
|
+
|
|
3670
3720
|
if (this.error() != null) {
|
|
3671
3721
|
throw this.error();
|
|
3672
3722
|
}
|
|
@@ -3969,6 +4019,9 @@ async function _updateThunk(op) {
|
|
|
3969
4019
|
|
|
3970
4020
|
this._castConditions();
|
|
3971
4021
|
|
|
4022
|
+
// Check for empty/invalid filter with requireFilter option
|
|
4023
|
+
checkRequireFilter(this._conditions, this.options);
|
|
4024
|
+
|
|
3972
4025
|
_castArrayFilters(this);
|
|
3973
4026
|
|
|
3974
4027
|
if (this.error() != null) {
|
|
@@ -4119,6 +4172,7 @@ Query.prototype._replaceOne = async function _replaceOne() {
|
|
|
4119
4172
|
* @param {Boolean} [options.translateAliases=null] If set to `true`, translates any schema-defined aliases in `filter`, `projection`, `update`, and `distinct`. Throws an error if there are any conflicts where both alias and raw property are defined on the same object.
|
|
4120
4173
|
* @param {Boolean} [options.overwriteDiscriminatorKey=false] Mongoose removes discriminator key updates from `update` by default, set `overwriteDiscriminatorKey` to `true` to allow updating the discriminator key
|
|
4121
4174
|
* @param {Boolean} [options.overwriteImmutable=false] Mongoose removes updated immutable properties from `update` by default (excluding $setOnInsert). Set `overwriteImmutable` to `true` to allow updating immutable properties using other update operators.
|
|
4175
|
+
* @param {Boolean} [options.requireFilter=false] If true, throws an error if the filter is empty (`{}`)
|
|
4122
4176
|
* @return {Query} this
|
|
4123
4177
|
* @see Model.update https://mongoosejs.com/docs/api/model.html#Model.update()
|
|
4124
4178
|
* @see Query docs https://mongoosejs.com/docs/queries.html
|
|
@@ -4193,6 +4247,7 @@ Query.prototype.updateMany = function(conditions, doc, options, callback) {
|
|
|
4193
4247
|
* @param {Boolean} [options.translateAliases=null] If set to `true`, translates any schema-defined aliases in `filter`, `projection`, `update`, and `distinct`. Throws an error if there are any conflicts where both alias and raw property are defined on the same object.
|
|
4194
4248
|
* @param {Boolean} [options.overwriteDiscriminatorKey=false] Mongoose removes discriminator key updates from `update` by default, set `overwriteDiscriminatorKey` to `true` to allow updating the discriminator key
|
|
4195
4249
|
* @param {Boolean} [options.overwriteImmutable=false] Mongoose removes updated immutable properties from `update` by default (excluding $setOnInsert). Set `overwriteImmutable` to `true` to allow updating immutable properties using other update operators.
|
|
4250
|
+
* @param {Boolean} [options.requireFilter=false] If true, throws an error if the filter is empty (`{}`)
|
|
4196
4251
|
* @return {Query} this
|
|
4197
4252
|
* @see Model.update https://mongoosejs.com/docs/api/model.html#Model.update()
|
|
4198
4253
|
* @see Query docs https://mongoosejs.com/docs/queries.html
|
|
@@ -4259,6 +4314,7 @@ Query.prototype.updateOne = function(conditions, doc, options, callback) {
|
|
|
4259
4314
|
* @param {Object} [options.writeConcern=null] sets the [write concern](https://www.mongodb.com/docs/manual/reference/write-concern/) for replica sets. Overrides the [schema-level write concern](https://mongoosejs.com/docs/guide.html#writeConcern)
|
|
4260
4315
|
* @param {Boolean} [options.timestamps=null] If set to `false` and [schema-level timestamps](https://mongoosejs.com/docs/guide.html#timestamps) are enabled, skip timestamps for this update. Does nothing if schema-level timestamps are not set.
|
|
4261
4316
|
* @param {Boolean} [options.translateAliases=null] If set to `true`, translates any schema-defined aliases in `filter`, `projection`, `update`, and `distinct`. Throws an error if there are any conflicts where both alias and raw property are defined on the same object.
|
|
4317
|
+
* @param {Boolean} [options.requireFilter=false] If true, throws an error if the filter is empty (`{}`)
|
|
4262
4318
|
* @return {Query} this
|
|
4263
4319
|
* @see Model.update https://mongoosejs.com/docs/api/model.html#Model.update()
|
|
4264
4320
|
* @see Query docs https://mongoosejs.com/docs/queries.html
|
package/lib/schema/array.js
CHANGED
package/lib/schema/bigint.js
CHANGED
|
@@ -254,6 +254,10 @@ SchemaBigInt.prototype.toJSONSchema = function toJSONSchema(options) {
|
|
|
254
254
|
return createJSONSchemaTypeDefinition('string', 'long', options?.useBsonType, isRequired);
|
|
255
255
|
};
|
|
256
256
|
|
|
257
|
+
SchemaBigInt.prototype.autoEncryptionType = function autoEncryptionType() {
|
|
258
|
+
return 'long';
|
|
259
|
+
};
|
|
260
|
+
|
|
257
261
|
/*!
|
|
258
262
|
* Module exports.
|
|
259
263
|
*/
|
package/lib/schema/boolean.js
CHANGED
|
@@ -304,6 +304,10 @@ SchemaBoolean.prototype.toJSONSchema = function toJSONSchema(options) {
|
|
|
304
304
|
return createJSONSchemaTypeDefinition('boolean', 'bool', options?.useBsonType, isRequired);
|
|
305
305
|
};
|
|
306
306
|
|
|
307
|
+
SchemaBoolean.prototype.autoEncryptionType = function autoEncryptionType() {
|
|
308
|
+
return 'bool';
|
|
309
|
+
};
|
|
310
|
+
|
|
307
311
|
/*!
|
|
308
312
|
* Module exports.
|
|
309
313
|
*/
|
package/lib/schema/buffer.js
CHANGED
|
@@ -314,6 +314,10 @@ SchemaBuffer.prototype.toJSONSchema = function toJSONSchema(options) {
|
|
|
314
314
|
return createJSONSchemaTypeDefinition('string', 'binData', options?.useBsonType, isRequired);
|
|
315
315
|
};
|
|
316
316
|
|
|
317
|
+
SchemaBuffer.prototype.autoEncryptionType = function autoEncryptionType() {
|
|
318
|
+
return 'binData';
|
|
319
|
+
};
|
|
320
|
+
|
|
317
321
|
/*!
|
|
318
322
|
* Module exports.
|
|
319
323
|
*/
|
package/lib/schema/date.js
CHANGED
|
@@ -440,6 +440,10 @@ SchemaDate.prototype.toJSONSchema = function toJSONSchema(options) {
|
|
|
440
440
|
return createJSONSchemaTypeDefinition('string', 'date', options?.useBsonType, isRequired);
|
|
441
441
|
};
|
|
442
442
|
|
|
443
|
+
SchemaDate.prototype.autoEncryptionType = function autoEncryptionType() {
|
|
444
|
+
return 'date';
|
|
445
|
+
};
|
|
446
|
+
|
|
443
447
|
/*!
|
|
444
448
|
* Module exports.
|
|
445
449
|
*/
|
package/lib/schema/decimal128.js
CHANGED
|
@@ -235,6 +235,10 @@ SchemaDecimal128.prototype.toJSONSchema = function toJSONSchema(options) {
|
|
|
235
235
|
return createJSONSchemaTypeDefinition('string', 'decimal', options?.useBsonType, isRequired);
|
|
236
236
|
};
|
|
237
237
|
|
|
238
|
+
SchemaDecimal128.prototype.autoEncryptionType = function autoEncryptionType() {
|
|
239
|
+
return 'decimal';
|
|
240
|
+
};
|
|
241
|
+
|
|
238
242
|
/*!
|
|
239
243
|
* Module exports.
|
|
240
244
|
*/
|
package/lib/schema/double.js
CHANGED
|
@@ -218,6 +218,10 @@ SchemaDouble.prototype.toJSONSchema = function toJSONSchema(options) {
|
|
|
218
218
|
return createJSONSchemaTypeDefinition('number', 'double', options?.useBsonType, isRequired);
|
|
219
219
|
};
|
|
220
220
|
|
|
221
|
+
SchemaDouble.prototype.autoEncryptionType = function autoEncryptionType() {
|
|
222
|
+
return 'double';
|
|
223
|
+
};
|
|
224
|
+
|
|
221
225
|
/*!
|
|
222
226
|
* Module exports.
|
|
223
227
|
*/
|
package/lib/schema/int32.js
CHANGED
|
@@ -260,6 +260,10 @@ SchemaInt32.prototype.toJSONSchema = function toJSONSchema(options) {
|
|
|
260
260
|
return createJSONSchemaTypeDefinition('number', 'int', options?.useBsonType, isRequired);
|
|
261
261
|
};
|
|
262
262
|
|
|
263
|
+
SchemaInt32.prototype.autoEncryptionType = function autoEncryptionType() {
|
|
264
|
+
return 'int';
|
|
265
|
+
};
|
|
266
|
+
|
|
263
267
|
|
|
264
268
|
/*!
|
|
265
269
|
* Module exports.
|
package/lib/schema/map.js
CHANGED
package/lib/schema/objectId.js
CHANGED
|
@@ -304,6 +304,10 @@ SchemaObjectId.prototype.toJSONSchema = function toJSONSchema(options) {
|
|
|
304
304
|
return createJSONSchemaTypeDefinition('string', 'objectId', options?.useBsonType, isRequired);
|
|
305
305
|
};
|
|
306
306
|
|
|
307
|
+
SchemaObjectId.prototype.autoEncryptionType = function autoEncryptionType() {
|
|
308
|
+
return 'objectId';
|
|
309
|
+
};
|
|
310
|
+
|
|
307
311
|
/*!
|
|
308
312
|
* Module exports.
|
|
309
313
|
*/
|
package/lib/schema/string.js
CHANGED
|
@@ -712,6 +712,10 @@ SchemaString.prototype.toJSONSchema = function toJSONSchema(options) {
|
|
|
712
712
|
return createJSONSchemaTypeDefinition('string', 'string', options?.useBsonType, isRequired);
|
|
713
713
|
};
|
|
714
714
|
|
|
715
|
+
SchemaString.prototype.autoEncryptionType = function autoEncryptionType() {
|
|
716
|
+
return 'string';
|
|
717
|
+
};
|
|
718
|
+
|
|
715
719
|
/*!
|
|
716
720
|
* Module exports.
|
|
717
721
|
*/
|
package/lib/schema/uuid.js
CHANGED
|
@@ -298,6 +298,10 @@ SchemaUUID.prototype.toJSONSchema = function toJSONSchema(options) {
|
|
|
298
298
|
return createJSONSchemaTypeDefinition('string', 'binData', options?.useBsonType, isRequired);
|
|
299
299
|
};
|
|
300
300
|
|
|
301
|
+
SchemaUUID.prototype.autoEncryptionType = function autoEncryptionType() {
|
|
302
|
+
return 'binData';
|
|
303
|
+
};
|
|
304
|
+
|
|
301
305
|
/*!
|
|
302
306
|
* Module exports.
|
|
303
307
|
*/
|