mongoose 7.3.3 → 7.4.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/lib/cast.js CHANGED
@@ -10,12 +10,12 @@ const Types = require('./schema/index');
10
10
  const cast$expr = require('./helpers/query/cast$expr');
11
11
  const castTextSearch = require('./schema/operators/text');
12
12
  const get = require('./helpers/get');
13
- const getConstructorName = require('./helpers/getConstructorName');
14
13
  const getSchemaDiscriminatorByValue = require('./helpers/discriminator/getSchemaDiscriminatorByValue');
15
14
  const isOperator = require('./helpers/query/isOperator');
16
15
  const util = require('util');
17
16
  const isObject = require('./helpers/isObject');
18
17
  const isMongooseObject = require('./helpers/isMongooseObject');
18
+ const utils = require('./utils');
19
19
 
20
20
  const ALLOWED_GEOWITHIN_GEOJSON_TYPES = ['Polygon', 'MultiPolygon'];
21
21
 
@@ -291,7 +291,7 @@ module.exports = function cast(schema, obj, options, context) {
291
291
  }
292
292
  } else if (val == null) {
293
293
  continue;
294
- } else if (getConstructorName(val) === 'Object') {
294
+ } else if (utils.isPOJO(val)) {
295
295
  any$conditionals = Object.keys(val).some(isOperator);
296
296
 
297
297
  if (!any$conditionals) {
package/lib/connection.js CHANGED
@@ -16,10 +16,7 @@ const clone = require('./helpers/clone');
16
16
  const driver = require('./driver');
17
17
  const get = require('./helpers/get');
18
18
  const immediate = require('./helpers/immediate');
19
- const mongodb = require('mongodb');
20
- const pkg = require('../package.json');
21
19
  const utils = require('./utils');
22
- const processConnectionOptions = require('./helpers/processConnectionOptions');
23
20
  const CreateCollectionsError = require('./error/createCollectionsError');
24
21
 
25
22
  const arrayAtomicsSymbol = require('./helpers/symbols').arrayAtomicsSymbol;
@@ -738,7 +735,7 @@ Connection.prototype.openUri = async function openUri(uri, options) {
738
735
  throw err;
739
736
  }
740
737
 
741
- this.$initialConnection = _createMongoClient(this, uri, options).
738
+ this.$initialConnection = this.createClient(uri, options).
742
739
  then(() => this).
743
740
  catch(err => {
744
741
  this.readyState = STATES.disconnected;
@@ -795,184 +792,6 @@ function _handleConnectionErrors(err) {
795
792
  return err;
796
793
  }
797
794
 
798
- /*!
799
- * ignore
800
- */
801
-
802
- async function _createMongoClient(conn, uri, options) {
803
- if (typeof uri !== 'string') {
804
- throw new MongooseError('The `uri` parameter to `openUri()` must be a ' +
805
- `string, got "${typeof uri}". Make sure the first parameter to ` +
806
- '`mongoose.connect()` or `mongoose.createConnection()` is a string.');
807
- }
808
-
809
- if (conn._destroyCalled) {
810
- throw new MongooseError(
811
- 'Connection has been closed and destroyed, and cannot be used for re-opening the connection. ' +
812
- 'Please create a new connection with `mongoose.createConnection()` or `mongoose.connect()`.'
813
- );
814
- }
815
-
816
- if (conn.readyState === STATES.connecting || conn.readyState === STATES.connected) {
817
- if (conn._connectionString !== uri) {
818
- throw new MongooseError('Can\'t call `openUri()` on an active connection with ' +
819
- 'different connection strings. Make sure you aren\'t calling `mongoose.connect()` ' +
820
- 'multiple times. See: https://mongoosejs.com/docs/connections.html#multiple_connections');
821
- }
822
- }
823
-
824
- options = processConnectionOptions(uri, options);
825
-
826
- if (options) {
827
-
828
- const autoIndex = options.config && options.config.autoIndex != null ?
829
- options.config.autoIndex :
830
- options.autoIndex;
831
- if (autoIndex != null) {
832
- conn.config.autoIndex = autoIndex !== false;
833
- delete options.config;
834
- delete options.autoIndex;
835
- }
836
-
837
- if ('autoCreate' in options) {
838
- conn.config.autoCreate = !!options.autoCreate;
839
- delete options.autoCreate;
840
- }
841
-
842
- if ('sanitizeFilter' in options) {
843
- conn.config.sanitizeFilter = options.sanitizeFilter;
844
- delete options.sanitizeFilter;
845
- }
846
-
847
- // Backwards compat
848
- if (options.user || options.pass) {
849
- options.auth = options.auth || {};
850
- options.auth.username = options.user;
851
- options.auth.password = options.pass;
852
-
853
- conn.user = options.user;
854
- conn.pass = options.pass;
855
- }
856
- delete options.user;
857
- delete options.pass;
858
-
859
- if (options.bufferCommands != null) {
860
- conn.config.bufferCommands = options.bufferCommands;
861
- delete options.bufferCommands;
862
- }
863
- } else {
864
- options = {};
865
- }
866
-
867
- conn._connectionOptions = options;
868
- const dbName = options.dbName;
869
- if (dbName != null) {
870
- conn.$dbName = dbName;
871
- }
872
- delete options.dbName;
873
-
874
- if (!utils.hasUserDefinedProperty(options, 'driverInfo')) {
875
- options.driverInfo = {
876
- name: 'Mongoose',
877
- version: pkg.version
878
- };
879
- }
880
-
881
- conn.readyState = STATES.connecting;
882
- conn._connectionString = uri;
883
-
884
- let client;
885
- try {
886
- client = new mongodb.MongoClient(uri, options);
887
- } catch (error) {
888
- conn.readyState = STATES.disconnected;
889
- throw error;
890
- }
891
- conn.client = client;
892
-
893
- client.setMaxListeners(0);
894
- await client.connect();
895
-
896
- _setClient(conn, client, options, dbName);
897
-
898
- for (const db of conn.otherDbs) {
899
- _setClient(db, client, {}, db.name);
900
- }
901
- return conn;
902
- }
903
-
904
- /*!
905
- * ignore
906
- */
907
-
908
- function _setClient(conn, client, options, dbName) {
909
- const db = dbName != null ? client.db(dbName) : client.db();
910
- conn.db = db;
911
- conn.client = client;
912
- conn.host = client &&
913
- client.s &&
914
- client.s.options &&
915
- client.s.options.hosts &&
916
- client.s.options.hosts[0] &&
917
- client.s.options.hosts[0].host || void 0;
918
- conn.port = client &&
919
- client.s &&
920
- client.s.options &&
921
- client.s.options.hosts &&
922
- client.s.options.hosts[0] &&
923
- client.s.options.hosts[0].port || void 0;
924
- conn.name = dbName != null ? dbName : client && client.s && client.s.options && client.s.options.dbName || void 0;
925
- conn._closeCalled = client._closeCalled;
926
-
927
- const _handleReconnect = () => {
928
- // If we aren't disconnected, we assume this reconnect is due to a
929
- // socket timeout. If there's no activity on a socket for
930
- // `socketTimeoutMS`, the driver will attempt to reconnect and emit
931
- // this event.
932
- if (conn.readyState !== STATES.connected) {
933
- conn.readyState = STATES.connected;
934
- conn.emit('reconnect');
935
- conn.emit('reconnected');
936
- conn.onOpen();
937
- }
938
- };
939
-
940
- const type = client &&
941
- client.topology &&
942
- client.topology.description &&
943
- client.topology.description.type || '';
944
-
945
- if (type === 'Single') {
946
- client.on('serverDescriptionChanged', ev => {
947
- const newDescription = ev.newDescription;
948
- if (newDescription.type === 'Unknown') {
949
- conn.readyState = STATES.disconnected;
950
- } else {
951
- _handleReconnect();
952
- }
953
- });
954
- } else if (type.startsWith('ReplicaSet')) {
955
- client.on('topologyDescriptionChanged', ev => {
956
- // Emit disconnected if we've lost connectivity to the primary
957
- const description = ev.newDescription;
958
- if (conn.readyState === STATES.connected && description.type !== 'ReplicaSetWithPrimary') {
959
- // Implicitly emits 'disconnected'
960
- conn.readyState = STATES.disconnected;
961
- } else if (conn.readyState === STATES.disconnected && description.type === 'ReplicaSetWithPrimary') {
962
- _handleReconnect();
963
- }
964
- });
965
- }
966
-
967
- conn.onOpen();
968
-
969
- for (const i in conn.collections) {
970
- if (utils.object.hasOwnProperty(conn.collections, i)) {
971
- conn.collections[i].onOpen();
972
- }
973
- }
974
- }
975
-
976
795
  /**
977
796
  * Destroy the connection. Similar to [`.close`](https://mongoosejs.com/docs/api/connection.html#Connection.prototype.close()),
978
797
  * but also removes the connection from Mongoose's `connections` list and prevents the
@@ -1525,26 +1344,16 @@ Connection.prototype.getClient = function getClient() {
1525
1344
  * @return {Connection} this
1526
1345
  */
1527
1346
 
1528
- Connection.prototype.setClient = function setClient(client) {
1529
- if (!(client instanceof mongodb.MongoClient)) {
1530
- throw new MongooseError('Must call `setClient()` with an instance of MongoClient');
1531
- }
1532
- if (this.readyState !== STATES.disconnected) {
1533
- throw new MongooseError('Cannot call `setClient()` on a connection that is already connected.');
1534
- }
1535
- if (client.topology == null) {
1536
- throw new MongooseError('Cannot call `setClient()` with a MongoClient that you have not called `connect()` on yet.');
1537
- }
1538
-
1539
- this._connectionString = client.s.url;
1540
- _setClient(this, client, {}, client.s.options.dbName);
1347
+ Connection.prototype.setClient = function setClient() {
1348
+ throw new MongooseError('Connection#setClient not implemented by driver');
1349
+ };
1541
1350
 
1542
- for (const model of Object.values(this.models)) {
1543
- // Errors handled internally, so safe to ignore error
1544
- model.init().catch(function $modelInitNoop() {});
1545
- }
1351
+ /*!
1352
+ * Called internally by `openUri()` to create a MongoClient instance.
1353
+ */
1546
1354
 
1547
- return this;
1355
+ Connection.prototype.createClient = function createClient() {
1356
+ throw new MongooseError('Connection#createClient not implemented by driver');
1548
1357
  };
1549
1358
 
1550
1359
  /**
@@ -1609,6 +1418,29 @@ Connection.prototype.syncIndexes = async function syncIndexes(options = {}) {
1609
1418
  * @api public
1610
1419
  */
1611
1420
 
1421
+ /**
1422
+ * Removes the database connection with the given name created with with `useDb()`.
1423
+ *
1424
+ * Throws an error if the database connection was not found.
1425
+ *
1426
+ * #### Example:
1427
+ *
1428
+ * // Connect to `initialdb` first
1429
+ * const conn = await mongoose.createConnection('mongodb://127.0.0.1:27017/initialdb').asPromise();
1430
+ *
1431
+ * // Creates an un-cached connection to `mydb`
1432
+ * const db = conn.useDb('mydb');
1433
+ *
1434
+ * // Closes `db`, and removes `db` from `conn.relatedDbs` and `conn.otherDbs`
1435
+ * await conn.removeDb('mydb');
1436
+ *
1437
+ * @method removeDb
1438
+ * @memberOf Connection
1439
+ * @param {String} name The database name
1440
+ * @return {Connection} this
1441
+ * @api public
1442
+ */
1443
+
1612
1444
  /*!
1613
1445
  * Module exports.
1614
1446
  */
package/lib/document.js CHANGED
@@ -22,7 +22,6 @@ const clone = require('./helpers/clone');
22
22
  const compile = require('./helpers/document/compile').compile;
23
23
  const defineKey = require('./helpers/document/compile').defineKey;
24
24
  const flatten = require('./helpers/common').flatten;
25
- const flattenObjectWithDottedPaths = require('./helpers/path/flattenObjectWithDottedPaths');
26
25
  const get = require('./helpers/get');
27
26
  const getEmbeddedDiscriminatorPath = require('./helpers/document/getEmbeddedDiscriminatorPath');
28
27
  const getKeysInSchemaOrder = require('./helpers/schema/getKeysInSchemaOrder');
@@ -473,8 +472,6 @@ function $applyDefaultsToNested(val, path, doc) {
473
472
  return;
474
473
  }
475
474
 
476
- flattenObjectWithDottedPaths(val);
477
-
478
475
  const paths = Object.keys(doc.$__schema.paths);
479
476
  const plen = paths.length;
480
477
 
@@ -1079,9 +1076,11 @@ Document.prototype.$set = function $set(path, val, type, options) {
1079
1076
  return this;
1080
1077
  }
1081
1078
 
1079
+ options = Object.assign({}, options, { _skipMinimizeTopLevel: false });
1080
+
1082
1081
  for (let i = 0; i < len; ++i) {
1083
1082
  key = keys[i];
1084
- const pathName = prefix + key;
1083
+ const pathName = prefix ? prefix + key : key;
1085
1084
  pathtype = this.$__schema.pathType(pathName);
1086
1085
  const valForKey = path[key];
1087
1086
 
@@ -1093,20 +1092,15 @@ Document.prototype.$set = function $set(path, val, type, options) {
1093
1092
  pathtype === 'nested' &&
1094
1093
  this._doc[key] != null) {
1095
1094
  delete this._doc[key];
1096
- // Make sure we set `{}` back even if we minimize re: gh-8565
1097
- options = Object.assign({}, options, { _skipMinimizeTopLevel: true });
1098
- } else {
1099
- // Make sure we set `{_skipMinimizeTopLevel: false}` if don't have overwrite: gh-10441
1100
- options = Object.assign({}, options, { _skipMinimizeTopLevel: false });
1101
1095
  }
1102
1096
 
1103
1097
  if (utils.isNonBuiltinObject(valForKey) && pathtype === 'nested') {
1104
- this.$set(prefix + key, path[key], constructing, Object.assign({}, options, { _skipMarkModified: true }));
1105
- $applyDefaultsToNested(this.$get(prefix + key), prefix + key, this);
1098
+ this.$set(pathName, valForKey, constructing, Object.assign({}, options, { _skipMarkModified: true }));
1099
+ $applyDefaultsToNested(this.$get(pathName), pathName, this);
1106
1100
  continue;
1107
1101
  } else if (strict) {
1108
1102
  // Don't overwrite defaults with undefined keys (gh-3981) (gh-9039)
1109
- if (constructing && path[key] === void 0 &&
1103
+ if (constructing && valForKey === void 0 &&
1110
1104
  this.$get(pathName) !== void 0) {
1111
1105
  continue;
1112
1106
  }
@@ -1116,20 +1110,19 @@ Document.prototype.$set = function $set(path, val, type, options) {
1116
1110
  }
1117
1111
 
1118
1112
  if (pathtype === 'real' || pathtype === 'virtual') {
1119
- const p = path[key];
1120
- this.$set(prefix + key, p, constructing, options);
1121
- } else if (pathtype === 'nested' && path[key] instanceof Document) {
1122
- this.$set(prefix + key,
1123
- path[key].toObject({ transform: false }), constructing, options);
1113
+ this.$set(pathName, valForKey, constructing, options);
1114
+ } else if (pathtype === 'nested' && valForKey instanceof Document) {
1115
+ this.$set(pathName,
1116
+ valForKey.toObject({ transform: false }), constructing, options);
1124
1117
  } else if (strict === 'throw') {
1125
1118
  if (pathtype === 'nested') {
1126
- throw new ObjectExpectedError(key, path[key]);
1119
+ throw new ObjectExpectedError(key, valForKey);
1127
1120
  } else {
1128
1121
  throw new StrictModeError(key);
1129
1122
  }
1130
1123
  }
1131
- } else if (path[key] !== void 0) {
1132
- this.$set(prefix + key, path[key], constructing, options);
1124
+ } else if (valForKey !== void 0) {
1125
+ this.$set(pathName, valForKey, constructing, options);
1133
1126
  }
1134
1127
  }
1135
1128
 
@@ -2217,16 +2210,17 @@ Document.prototype[documentModifiedPaths] = Document.prototype.modifiedPaths;
2217
2210
 
2218
2211
  Document.prototype.isModified = function(paths, modifiedPaths) {
2219
2212
  if (paths) {
2220
- if (!Array.isArray(paths)) {
2221
- paths = paths.indexOf(' ') === -1 ? [paths] : paths.split(' ');
2222
- }
2223
-
2224
2213
  const directModifiedPathsObj = this.$__.activePaths.states.modify;
2225
2214
  if (directModifiedPathsObj == null) {
2226
2215
  return false;
2227
2216
  }
2217
+
2218
+ if (typeof paths === 'string') {
2219
+ paths = [paths];
2220
+ }
2221
+
2228
2222
  for (const path of paths) {
2229
- if (Object.prototype.hasOwnProperty.call(directModifiedPathsObj, path)) {
2223
+ if (directModifiedPathsObj[path] != null) {
2230
2224
  return true;
2231
2225
  }
2232
2226
  }
@@ -2652,14 +2646,14 @@ function _getPathsToValidate(doc, pathsToValidate, pathsToSkip) {
2652
2646
  const modifiedPaths = doc.modifiedPaths();
2653
2647
  for (const subdoc of subdocs) {
2654
2648
  if (subdoc.$basePath) {
2655
- // Remove child paths for now, because we'll be validating the whole
2656
- // subdoc
2657
2649
  const fullPathToSubdoc = subdoc.$__fullPathWithIndexes();
2658
2650
 
2659
- for (const p of paths) {
2660
- if (p == null || p.startsWith(fullPathToSubdoc + '.')) {
2661
- paths.delete(p);
2662
- }
2651
+ // Remove child paths for now, because we'll be validating the whole
2652
+ // subdoc.
2653
+ // The following is a faster take on looping through every path in `paths`
2654
+ // and checking if the path starts with `fullPathToSubdoc` re: gh-13191
2655
+ for (const modifiedPath of subdoc.modifiedPaths()) {
2656
+ paths.delete(fullPathToSubdoc + '.' + modifiedPath);
2663
2657
  }
2664
2658
 
2665
2659
  if (doc.$isModified(fullPathToSubdoc, modifiedPaths) &&
@@ -3336,12 +3330,13 @@ Document.prototype.$__reset = function reset() {
3336
3330
  if (subdoc.$isDocumentArrayElement) {
3337
3331
  resetArrays.add(subdoc.parentArray());
3338
3332
  } else {
3339
- if (subdoc.$parent() === this) {
3333
+ const parent = subdoc.$parent();
3334
+ if (parent === this) {
3340
3335
  this.$__.activePaths.clearPath(subdoc.$basePath);
3341
- } else if (subdoc.$parent() != null && subdoc.$parent().$isSubdocument) {
3336
+ } else if (parent != null && parent.$isSubdocument) {
3342
3337
  // If map path underneath subdocument, may end up with a case where
3343
3338
  // map path is modified but parent still needs to be reset. See gh-10295
3344
- subdoc.$parent().$__reset();
3339
+ parent.$__reset();
3345
3340
  }
3346
3341
  }
3347
3342
  }
@@ -4074,6 +4069,7 @@ function applyGetters(self, json, options) {
4074
4069
  path = paths[i];
4075
4070
 
4076
4071
  const parts = path.split('.');
4072
+
4077
4073
  const plen = parts.length;
4078
4074
  const last = plen - 1;
4079
4075
  let branch = json;