From 442d754fac209080003a35994b37e1ff1fb055ae Mon Sep 17 00:00:00 2001 From: Valeri Karpov Date: Wed, 20 Jan 2021 12:37:29 -0500 Subject: [PATCH 1/8] WIP: upgrade to MongoDB driver 4.0 --- README.md | 2 - index.d.ts | 24 +++-- lib/aggregate.js | 27 ++---- lib/connection.js | 93 +------------------ lib/drivers/node-mongodb-native/collection.js | 2 +- lib/drivers/node-mongodb-native/connection.js | 49 ---------- lib/query.js | 24 ++--- lib/validoptions.js | 4 +- package.json | 5 +- test/common.js | 3 - test/connection.test.js | 55 +---------- test/typescript/connectSyntax.ts | 6 +- test/typescript/global.ts | 1 - 13 files changed, 46 insertions(+), 249 deletions(-) diff --git a/README.md b/README.md index b28bed66538..d9208cb963b 100644 --- a/README.md +++ b/README.md @@ -71,8 +71,6 @@ Both `connect` and `createConnection` take a `mongodb://` URI, or the parameters ```js await mongoose.connect('mongodb://localhost/my_database', { - useNewUrlParser: true, - useUnifiedTopology: true, useFindAndModify: false, useCreateIndex: true }); diff --git a/index.d.ts b/index.d.ts index 36a74e37ccc..2d58402d67e 100644 --- a/index.d.ts +++ b/index.d.ts @@ -879,7 +879,7 @@ declare module 'mongoose' { * if true, returns the raw result from the MongoDB driver */ rawResult?: boolean; - readPreference?: mongodb.ReadPreferenceMode; + readPreference?: string | mongodb.ReadPreferenceModeId; /** * An alias for the `new` option. `returnOriginal: false` is equivalent to `new: true`. */ @@ -1207,7 +1207,7 @@ declare module 'mongoose' { */ capped?: boolean | number | { size?: number; max?: number; autoIndexId?: boolean; }; /** Sets a default collation for every query and aggregation. */ - collation?: mongodb.CollationDocument; + collation?: mongodb.CollationOptions; /** * Mongoose by default produces a collection name by passing the model name to the utils.toCollectionName * method. This method pluralizes the name. Set this option if you need a different name for your collection. @@ -1809,7 +1809,7 @@ declare module 'mongoose' { circle(path: string, area: any): this; /** Adds a collation to this op (MongoDB 3.4 and up) */ - collation(value: mongodb.CollationDocument): this; + collation(value: mongodb.CollationOptions): this; /** Specifies the `comment` option. */ comment(val: string): this; @@ -2033,7 +2033,7 @@ declare module 'mongoose' { projection(fields?: any | null): any; /** Determines the MongoDB nodes from which to read. */ - read(pref: string | mongodb.ReadPreferenceMode, tags?: any[]): this; + read(pref: string | mongodb.ReadPreferenceModeId, tags?: any[]): this; /** Sets the readConcern option for the query. */ readConcern(level: string): this; @@ -2159,6 +2159,16 @@ declare module 'mongoose' { wtimeout(ms: number): this; } + type DotAndArrayNotation = { + readonly [key: string]: AssignableType; + }; + + type ReadonlyPartial = { + readonly [key in keyof TSchema]?: TSchema[key]; + }; + + type MatchKeysAndValues = ReadonlyPartial & DotAndArrayNotation; + type _FilterQuery = { [P in keyof T]?: P extends '_id' ? [Extract] extends [never] @@ -2172,7 +2182,7 @@ declare module 'mongoose' { export type FilterQuery = _FilterQuery>; - export type UpdateQuery = mongodb.UpdateQuery> & mongodb.MatchKeysAndValues>; + export type UpdateQuery = mongodb.UpdateQuery> & MatchKeysAndValues>; type _AllowStringsForIds = { [K in keyof T]: [Extract] extends [never] ? T[K] : T[K] | string; @@ -2274,7 +2284,7 @@ declare module 'mongoose' { catch: Promise['catch']; /** Adds a collation. */ - collation(options: mongodb.CollationDocument): this; + collation(options: mongodb.CollationOptions): this; /** Appends a new $count operator to this aggregate pipeline. */ count(countName: string): this; @@ -2327,7 +2337,7 @@ declare module 'mongoose' { pipeline(): any[]; /** Sets the readPreference option for the aggregation query. */ - read(pref: string | mongodb.ReadPreferenceMode, tags?: any[]): this; + read(pref: string | mongodb.ReadPreferenceModeId, tags?: any[]): this; /** Sets the readConcern level for the aggregation query. */ readConcern(level: string): this; diff --git a/lib/aggregate.js b/lib/aggregate.js index c06e703057f..7e4093f4e97 100644 --- a/lib/aggregate.js +++ b/lib/aggregate.js @@ -975,25 +975,16 @@ Aggregate.prototype.exec = function(callback) { } const options = utils.clone(this.options || {}); - collection.aggregate(this._pipeline, options, (error, cursor) => { - if (error) { - const _opts = { error: error }; - return model.hooks.execPost('aggregate', this, [null], _opts, error => { - if (error) { - return cb(error); - } - return cb(null); - }); - } - cursor.toArray((error, result) => { - const _opts = { error: error }; - model.hooks.execPost('aggregate', this, [result], _opts, (error, result) => { - if (error) { - return cb(error); - } - cb(null, result); - }); + const cursor = collection.aggregate(this._pipeline, options); + cursor.toArray((error, result) => { + const _opts = { error: error }; + model.hooks.execPost('aggregate', this, [result], _opts, (error, result) => { + if (error) { + return cb(error); + } + + cb(null, result); }); }); }); diff --git a/lib/connection.js b/lib/connection.js index 0f311e44bbb..7d2f8a15835 100644 --- a/lib/connection.js +++ b/lib/connection.js @@ -20,8 +20,6 @@ const mongodb = require('mongodb'); const pkg = require('../package.json'); const utils = require('./utils'); -const parseConnectionString = require('mongodb/lib/core').parseConnectionString; - const arrayAtomicsSymbol = require('./helpers/symbols').arrayAtomicsSymbol; const sessionNewDocuments = require('./helpers/symbols').sessionNewDocuments; @@ -776,23 +774,6 @@ Connection.prototype.openUri = function(uri, options, callback) { } delete options.dbName; - if (!('promiseLibrary' in options)) { - options.promiseLibrary = PromiseProvider.get(); - } - if (!('useNewUrlParser' in options)) { - if ('useNewUrlParser' in this.base.options) { - options.useNewUrlParser = this.base.options.useNewUrlParser; - } else { - options.useNewUrlParser = false; - } - } - if (!utils.hasUserDefinedProperty(options, 'useUnifiedTopology')) { - if (utils.hasUserDefinedProperty(this.base.options, 'useUnifiedTopology')) { - options.useUnifiedTopology = this.base.options.useUnifiedTopology; - } else { - options.useUnifiedTopology = false; - } - } if (!utils.hasUserDefinedProperty(options, 'driverInfo')) { options.driverInfo = { name: 'Mongoose', @@ -800,26 +781,6 @@ Connection.prototype.openUri = function(uri, options, callback) { }; } - const parsePromise = new Promise((resolve, reject) => { - parseConnectionString(uri, options, (err, parsed) => { - if (err) { - return reject(err); - } - if (dbName) { - this.name = dbName; - } else if (parsed.defaultDatabase) { - this.name = parsed.defaultDatabase; - } else { - this.name = get(parsed, 'auth.db', null); - } - this.host = get(parsed, 'hosts.0.host', 'localhost'); - this.port = get(parsed, 'hosts.0.port', 27017); - this.user = this.user || get(parsed, 'auth.username'); - this.pass = this.pass || get(parsed, 'auth.password'); - resolve(); - }); - }); - const promise = new Promise((resolve, reject) => { const client = new mongodb.MongoClient(uri, options); _this.client = client; @@ -836,7 +797,7 @@ Connection.prototype.openUri = function(uri, options, callback) { }); const serverSelectionError = new ServerSelectionError(); - this.$initialConnection = Promise.all([promise, parsePromise]). + this.$initialConnection = promise. then(() => this). catch(err => { if (err != null && err.name === 'MongoServerSelectionError') { @@ -911,61 +872,9 @@ function _setClient(conn, client, options, dbName) { _handleReconnect(); } }); - - db.on('close', function() { - const type = get(db, 's.topology.s.description.type', ''); - if (type !== 'ReplicaSetWithPrimary') { - // Implicitly emits 'disconnected' - conn.readyState = STATES.disconnected; - } - }); } } - // Backwards compat for mongoose 4.x - db.on('reconnect', function() { - _handleReconnect(); - }); - db.s.topology.on('reconnectFailed', function() { - conn.emit('reconnectFailed'); - }); - - if (!options.useUnifiedTopology) { - db.s.topology.on('left', function(data) { - conn.emit('left', data); - }); - } - db.s.topology.on('joined', function(data) { - conn.emit('joined', data); - }); - db.s.topology.on('fullsetup', function(data) { - conn.emit('fullsetup', data); - }); - if (get(db, 's.topology.s.coreTopology.s.pool') != null) { - db.s.topology.s.coreTopology.s.pool.on('attemptReconnect', function() { - conn.emit('attemptReconnect'); - }); - } - if (!options.useUnifiedTopology || !type.startsWith('ReplicaSet')) { - db.on('close', function() { - // Implicitly emits 'disconnected' - conn.readyState = STATES.disconnected; - }); - } - - if (!options.useUnifiedTopology) { - client.on('left', function() { - if (conn.readyState === STATES.connected && - get(db, 's.topology.s.coreTopology.s.replicaSetState.topologyType') === 'ReplicaSetNoPrimary') { - conn.readyState = STATES.disconnected; - } - }); - } - - db.on('timeout', function() { - conn.emit('timeout'); - }); - delete conn.then; delete conn.catch; conn.readyState = STATES.connected; diff --git a/lib/drivers/node-mongodb-native/collection.js b/lib/drivers/node-mongodb-native/collection.js index 88eac58dd9b..9f2a48a5841 100644 --- a/lib/drivers/node-mongodb-native/collection.js +++ b/lib/drivers/node-mongodb-native/collection.js @@ -229,7 +229,7 @@ function iter(i) { }; } -for (const key of Object.keys(Collection.prototype)) { +for (const key of Object.getOwnPropertyNames(Collection.prototype)) { // Janky hack to work around gh-3005 until we can get rid of the mongoose // collection abstraction const descriptor = Object.getOwnPropertyDescriptor(Collection.prototype, key); diff --git a/lib/drivers/node-mongodb-native/connection.js b/lib/drivers/node-mongodb-native/connection.js index 57e7195448a..48560bb6f53 100644 --- a/lib/drivers/node-mongodb-native/connection.js +++ b/lib/drivers/node-mongodb-native/connection.js @@ -92,8 +92,6 @@ NativeConnection.prototype.useDb = function(name, options) { newConn.client = _this.client; newConn.db = _this.client.db(name); newConn.onOpen(); - // setup the events appropriately - listen(newConn); } newConn.name = name; @@ -111,53 +109,6 @@ NativeConnection.prototype.useDb = function(name, options) { return newConn; }; -/*! - * Register listeners for important events and bubble appropriately. - */ - -function listen(conn) { - if (conn.db._listening) { - return; - } - conn.db._listening = true; - - conn.db.on('close', function(force) { - if (conn._closeCalled) return; - - // the driver never emits an `open` event. auto_reconnect still - // emits a `close` event but since we never get another - // `open` we can't emit close - if (conn.db.serverConfig.autoReconnect) { - conn.readyState = STATES.disconnected; - conn.emit('close'); - return; - } - conn.onClose(force); - }); - conn.db.on('error', function(err) { - conn.emit('error', err); - }); - conn.db.on('reconnect', function() { - conn.readyState = STATES.connected; - conn.emit('reconnect'); - conn.emit('reconnected'); - conn.onOpen(); - }); - conn.db.on('timeout', function(err) { - conn.emit('timeout', err); - }); - conn.db.on('open', function(err, db) { - if (STATES.disconnected === conn.readyState && db && db.databaseName) { - conn.readyState = STATES.connected; - conn.emit('reconnect'); - conn.emit('reconnected'); - } - }); - conn.db.on('parseError', function(err) { - conn.emit('parseError', err); - }); -} - /** * Closes the connection * diff --git a/lib/query.js b/lib/query.js index d7ca1f047e6..7bbaa7138c7 100644 --- a/lib/query.js +++ b/lib/query.js @@ -2041,20 +2041,16 @@ Query.prototype._find = wrapThunk(function(callback) { options.projection = this._fieldsForExec(); const filter = this._conditions; - this._collection.collection.find(filter, options, (err, cursor) => { - if (err) { - return cb(err); - } - if (options.explain) { - return cursor.explain(cb); - } - try { - return cursor.toArray(cb); - } catch (err) { - cb(err); - } - }); - return null; + const cursor = this._collection.collection.find(filter, options); + + if (options.explain) { + return cursor.explain(cb); + } + try { + return cursor.toArray(cb); + } catch (err) { + return cb(err); + } }); /** diff --git a/lib/validoptions.js b/lib/validoptions.js index a287a2636ce..86706c66e8c 100644 --- a/lib/validoptions.js +++ b/lib/validoptions.js @@ -27,9 +27,7 @@ const VALID_OPTIONS = Object.freeze([ 'toObject', 'typePojoToMixed', 'useCreateIndex', - 'useNewUrlParser', - 'usePushEach', - 'useUnifiedTopology' + 'usePushEach' ]); module.exports = VALID_OPTIONS; diff --git a/package.json b/package.json index 614c4cc209f..c8dc6d0b8f4 100644 --- a/package.json +++ b/package.json @@ -19,10 +19,9 @@ ], "license": "MIT", "dependencies": { - "@types/mongodb": "^3.5.27", - "bson": "^1.1.4", + "bson": "^4.2.2", "kareem": "2.3.2", - "mongodb": "3.6.3", + "mongodb": "4.0.0-beta.0", "mongoose-legacy-pluralize": "1.0.2", "mpath": "0.8.3", "mquery": "3.2.3", diff --git a/test/common.js b/test/common.js index ac646467dc1..1de39edd7a2 100644 --- a/test/common.js +++ b/test/common.js @@ -23,9 +23,6 @@ if (process.env.PRINT_COLLECTIONS) { // For 3.1.3 deprecations mongoose.set('useCreateIndex', true); -mongoose.set('useNewUrlParser', true); -// 3.3.x deprecations -mongoose.set('useUnifiedTopology', true); /** * Override all Collection related queries to keep count diff --git a/test/connection.test.js b/test/connection.test.js index e81192fb7eb..a3f38827889 100644 --- a/test/connection.test.js +++ b/test/connection.test.js @@ -230,21 +230,9 @@ describe('connections:', function() { db.close(done); }); - it('should accept mongodb://aaron:psw@localhost:27017/fake', function(done) { - const opts = { useNewUrlParser: true, useUnifiedTopology: false }; - const db = mongoose.createConnection('mongodb://aaron:psw@localhost:27017/fake', opts, () => { - db.close(done); - }); - assert.equal(db.pass, 'psw'); - assert.equal(db.user, 'aaron'); - assert.equal(db.name, 'fake'); - assert.equal(db.host, 'localhost'); - assert.equal(db.port, 27017); - }); - it('should accept unix domain sockets', function() { const host = encodeURIComponent('/tmp/mongodb-27017.sock'); - const db = mongoose.createConnection(`mongodb://aaron:psw@${host}/fake`, { useNewUrlParser: true }); + const db = mongoose.createConnection(`mongodb://aaron:psw@${host}/fake`); db.asPromise().catch(() => {}); assert.equal(db.name, 'fake'); assert.equal(db.host, '/tmp/mongodb-27017.sock'); @@ -307,33 +295,6 @@ describe('connections:', function() { }); }); - describe('errors', function() { - it('event fires with one listener', function(done) { - this.timeout(1500); - const db = mongoose.createConnection('mongodb://bad.notadomain/fakeeee?connectTimeoutMS=100', { - useNewUrlParser: true, - useUnifiedTopology: false // Workaround re: NODE-2250 - }); - db.asPromise().catch(() => {}); - db.on('error', function() { - // this callback has no params which triggered the bug #759 - db.close(); - done(); - }); - }); - - it('should occur without hanging when password with special chars is used (gh-460)', function(done) { - const opts = { - useNewUrlParser: true, - useUnifiedTopology: false - }; - mongoose.createConnection('mongodb://aaron:ps#w@localhost/fake?connectTimeoutMS=500', opts, function(err) { - assert.ok(err); - done(); - }); - }); - }); - describe('.model()', function() { let db; @@ -898,8 +859,6 @@ describe('connections:', function() { it('throws a MongooseServerSelectionError on server selection timeout (gh-8451)', () => { const opts = { - useNewUrlParser: true, - useUnifiedTopology: true, serverSelectionTimeoutMS: 100 }; const uri = 'mongodb://baddomain:27017/test'; @@ -917,8 +876,6 @@ describe('connections:', function() { return co(function*() { const opts = { - useNewUrlParser: true, - useUnifiedTopology: true, replicaSet: process.env.REPLICA_SET }; const conn = yield mongoose.createConnection('mongodb://localhost:27017/gh8425', opts); @@ -955,10 +912,7 @@ describe('connections:', function() { it('allows setting client on a disconnected connection (gh-9164)', function() { return co(function*() { - const client = yield mongodb.MongoClient.connect('mongodb://localhost:27017/mongoose_test', { - useNewUrlParser: true, - useUnifiedTopology: true - }); + const client = yield mongodb.MongoClient.connect('mongodb://localhost:27017/mongoose_test'); const conn = mongoose.createConnection().setClient(client); assert.equal(conn.readyState, 1); @@ -973,10 +927,7 @@ describe('connections:', function() { return co(function *() { const m = new mongoose.Mongoose; - m.connect('mongodb://localhost:27017/test_gh9496', { - useNewUrlParser: true, - useUnifiedTopology: true - }); + m.connect('mongodb://localhost:27017/test_gh9496'); const conn = yield m.connection.asPromise(); assert.ok(conn instanceof m.Connection); diff --git a/test/typescript/connectSyntax.ts b/test/typescript/connectSyntax.ts index 414a747eec0..7147dc99901 100644 --- a/test/typescript/connectSyntax.ts +++ b/test/typescript/connectSyntax.ts @@ -2,13 +2,11 @@ import { connect } from 'mongoose'; // Promise connect('mongodb://localhost:27017/test', { - useNewUrlParser: true, useFindAndModify: true, - useCreateIndex: true, - useUnifiedTopology: true + useCreateIndex: true }).then(mongoose => console.log(mongoose.connect)); // Callback -connect('mongodb://localhost:27017/test', { useNewUrlParser: true, useUnifiedTopology: true }, (err: Error | null) => { +connect('mongodb://localhost:27017/test', {}, (err: Error | null) => { console.log(err); }); diff --git a/test/typescript/global.ts b/test/typescript/global.ts index 8887190240b..c018aac1e73 100644 --- a/test/typescript/global.ts +++ b/test/typescript/global.ts @@ -6,7 +6,6 @@ mongoose.get('useCreateIndex'); const m: mongoose.Mongoose = new mongoose.Mongoose(); -m.set('useUnifiedTopology', true); m.STATES.connected; m.connect('mongodb://localhost:27017/test').then(() => { From c91d95e17e8afb9d8b3badfc658e7f20f7fb31d9 Mon Sep 17 00:00:00 2001 From: Valeri Karpov Date: Sat, 23 Jan 2021 17:53:35 -0500 Subject: [PATCH 2/8] fix: more quick fixes to upgrade to mongodb driver 4.x Re: #9840 --- docs/index.pug | 2 +- docs/models.pug | 2 +- docs/search.js | 2 +- docs/tutorials/ssl.md | 2 -- examples/redis-todo/db/index.js | 4 +-- index.pug | 2 +- lib/connection.js | 5 ++-- lib/index.js | 6 ----- test/collection.test.js | 4 +-- test/connection.test.js | 30 ++++++----------------- test/docs/date.test.js | 4 +-- test/es-next/cast.test.es6.js | 4 +-- test/es-next/findoneandupdate.test.es6.js | 4 +-- test/es-next/getters-setters.test.es6.js | 2 +- test/es-next/lean.test.es6.js | 4 +-- test/es-next/virtuals.test.es6.js | 2 +- test/index.test.js | 6 +---- test/typescript/connection.ts | 2 +- 18 files changed, 25 insertions(+), 62 deletions(-) diff --git a/docs/index.pug b/docs/index.pug index a1c0ecb06f2..abf242b3a42 100644 --- a/docs/index.pug +++ b/docs/index.pug @@ -32,7 +32,7 @@ block content ```javascript // getting-started.js const mongoose = require('mongoose'); - mongoose.connect('mongodb://localhost/test', {useNewUrlParser: true, useUnifiedTopology: true}); + mongoose.connect('mongodb://localhost/test'); ``` We have a pending connection to the test database running on localhost. diff --git a/docs/models.pug b/docs/models.pug index 7c4b27ff2e0..d04a26938b8 100644 --- a/docs/models.pug +++ b/docs/models.pug @@ -91,7 +91,7 @@ block content uses is open. Every model has an associated connection. When you use `mongoose.model()`, your model will use the default mongoose connection. ```javascript - mongoose.connect('mongodb://localhost/gettingstarted', {useNewUrlParser: true}); + mongoose.connect('mongodb://localhost/gettingstarted'); ``` :markdown If you create a custom connection, use that connection's `model()` function diff --git a/docs/search.js b/docs/search.js index c85a0feaf55..2cd05d94c7f 100644 --- a/docs/search.js +++ b/docs/search.js @@ -84,7 +84,7 @@ for (const filename of files) { run().catch(error => console.error(error.stack)); async function run() { - await mongoose.connect(config.uri, { useNewUrlParser: true, dbName: 'mongoose' }); + await mongoose.connect(config.uri, { dbName: 'mongoose' }); await Content.deleteMany({}); for (const content of contents) { diff --git a/docs/tutorials/ssl.md b/docs/tutorials/ssl.md index 4595c2a29dd..2747fa3c5dd 100644 --- a/docs/tutorials/ssl.md +++ b/docs/tutorials/ssl.md @@ -50,8 +50,6 @@ server is not registered with an established certificate authority. The solution ```javascript await mongoose.connect('mongodb://localhost:27017/test', { - useNewUrlParser: true, - useUnifiedTopology: true, ssl: true, sslValidate: true, // For example, see https://medium.com/@rajanmaharjan/secure-your-mongodb-connections-ssl-tls-92e2addb3c89 diff --git a/examples/redis-todo/db/index.js b/examples/redis-todo/db/index.js index 84a3701f028..3305fe8a8d3 100644 --- a/examples/redis-todo/db/index.js +++ b/examples/redis-todo/db/index.js @@ -2,6 +2,4 @@ const mongoose = require('mongoose'); -mongoose.connect('mongodb://localhost/redis-todo', - { useNewUrlParser: true, useCreateIndex: true } -); +mongoose.connect('mongodb://localhost/redis-todo'); diff --git a/index.pug b/index.pug index 5d6ac1b4ede..dac17537b12 100644 --- a/index.pug +++ b/index.pug @@ -121,7 +121,7 @@ html(lang='en') :markdown ```javascript const mongoose = require('mongoose'); - mongoose.connect('mongodb://localhost:27017/test', {useNewUrlParser: true, useUnifiedTopology: true}); + mongoose.connect('mongodb://localhost:27017/test'); const Cat = mongoose.model('Cat', { name: String }); diff --git a/lib/connection.js b/lib/connection.js index 7d2f8a15835..04660063445 100644 --- a/lib/connection.js +++ b/lib/connection.js @@ -653,11 +653,9 @@ Connection.prototype.onOpen = function() { * @param {String} [options.user] username for authentication, equivalent to `options.auth.user`. Maintained for backwards compatibility. * @param {String} [options.pass] password for authentication, equivalent to `options.auth.password`. Maintained for backwards compatibility. * @param {Number} [options.poolSize=5] The maximum number of sockets the MongoDB driver will keep open for this connection. By default, `poolSize` is 5. Keep in mind that, as of MongoDB 3.4, MongoDB only allows one operation per socket at a time, so you may want to increase this if you find you have a few slow queries that are blocking faster queries from proceeding. See [Slow Trains in MongoDB and Node.js](http://thecodebarbarian.com/slow-trains-in-mongodb-and-nodejs). - * @param {Boolean} [options.useUnifiedTopology=false] False by default. Set to `true` to opt in to the MongoDB driver's replica set and sharded cluster monitoring engine. * @param {Number} [options.serverSelectionTimeoutMS] If `useUnifiedTopology = true`, the MongoDB driver will try to find a server to send any given operation to, and keep retrying for `serverSelectionTimeoutMS` milliseconds before erroring out. If not set, the MongoDB driver defaults to using `30000` (30 seconds). * @param {Number} [options.heartbeatFrequencyMS] If `useUnifiedTopology = true`, the MongoDB driver sends a heartbeat every `heartbeatFrequencyMS` to check on the status of the connection. A heartbeat is subject to `serverSelectionTimeoutMS`, so the MongoDB driver will retry failed heartbeats for up to 30 seconds by default. Mongoose only emits a `'disconnected'` event after a heartbeat has failed, so you may want to decrease this setting to reduce the time between when your server goes down and when Mongoose emits `'disconnected'`. We recommend you do **not** set this setting below 1000, too many heartbeats can lead to performance degradation. * @param {Boolean} [options.autoIndex=true] Mongoose-specific option. Set to false to disable automatic index creation for all models associated with this connection. - * @param {Boolean} [options.useNewUrlParser=false] False by default. Set to `true` to opt in to the MongoDB driver's new URL parser logic. * @param {Boolean} [options.useCreateIndex=true] Mongoose-specific option. If `true`, this connection will use [`createIndex()` instead of `ensureIndex()`](/docs/deprecations.html#ensureindex) for automatic index builds via [`Model.init()`](/docs/api.html#model_Model.init). * @param {Number} [options.reconnectTries=30] If you're connected to a single server or mongos proxy (as opposed to a replica set), the MongoDB driver will try to reconnect every `reconnectInterval` milliseconds for `reconnectTries` times, and give up afterward. When the driver gives up, the mongoose connection emits a `reconnectFailed` event. This option does nothing for replica set connections. * @param {Number} [options.reconnectInterval=1000] See `reconnectTries` option above. @@ -824,6 +822,9 @@ function _setClient(conn, client, options, dbName) { const db = dbName != null ? client.db(dbName) : client.db(); conn.db = db; conn.client = client; + conn.host = get(client, 's.options.hosts.0.host', void 0); + conn.port = get(client, 's.options.hosts.0.port', void 0); + conn.name = dbName != null ? dbName : get(client, 's.options.dbName', void 0); const _handleReconnect = () => { // If we aren't disconnected, we assume this reconnect is due to a diff --git a/lib/index.js b/lib/index.js index 60c603728c4..a2472a11534 100644 --- a/lib/index.js +++ b/lib/index.js @@ -151,8 +151,6 @@ Mongoose.prototype.driver = require('./driver'); * - 'returnOriginal': If `false`, changes the default `returnOriginal` option to `findOneAndUpdate()`, `findByIdAndUpdate`, and `findOneAndReplace()` to false. This is equivalent to setting the `new` option to `true` for `findOneAndX()` calls by default. Read our [`findOneAndUpdate()` tutorial](/docs/tutorials/findoneandupdate.html) for more information. * - 'bufferCommands': enable/disable mongoose's buffering mechanism for all connections and models * - 'useCreateIndex': false by default. Set to `true` to make Mongoose's default index build use `createIndex()` instead of `ensureIndex()` to avoid deprecation warnings from the MongoDB driver. - * - 'useNewUrlParser': false by default. Set to `true` to make all connections set the `useNewUrlParser` option by default - * - 'useUnifiedTopology': false by default. Set to `true` to make all connections set the `useUnifiedTopology` option by default * - 'cloneSchemas': false by default. Set to `true` to `clone()` all schemas before compiling into a model. * - 'applyPluginsToDiscriminators': false by default. Set to true to apply global plugins to discriminator schemas. This typically isn't necessary because plugins are applied to the base schema and discriminators copy all middleware, methods, statics, and properties from the base schema. * - 'applyPluginsToChildSchemas': true by default. Set to false to skip applying global plugins to child schemas @@ -254,8 +252,6 @@ Mongoose.prototype.get = Mongoose.prototype.set; * @param {String} [options.user] username for authentication, equivalent to `options.auth.user`. Maintained for backwards compatibility. * @param {String} [options.pass] password for authentication, equivalent to `options.auth.password`. Maintained for backwards compatibility. * @param {Boolean} [options.autoIndex=true] Mongoose-specific option. Set to false to disable automatic index creation for all models associated with this connection. - * @param {Boolean} [options.useNewUrlParser=false] False by default. Set to `true` to make all connections set the `useNewUrlParser` option by default. - * @param {Boolean} [options.useUnifiedTopology=false] False by default. Set to `true` to make all connections set the `useUnifiedTopology` option by default. * @param {Boolean} [options.useCreateIndex=true] Mongoose-specific option. If `true`, this connection will use [`createIndex()` instead of `ensureIndex()`](/docs/deprecations.html#ensureindex) for automatic index builds via [`Model.init()`](/docs/api.html#model_Model.init). * @param {Number} [options.reconnectTries=30] If you're connected to a single server or mongos proxy (as opposed to a replica set), the MongoDB driver will try to reconnect every `reconnectInterval` milliseconds for `reconnectTries` times, and give up afterward. When the driver gives up, the mongoose connection emits a `reconnectFailed` event. This option does nothing for replica set connections. * @param {Number} [options.reconnectInterval=1000] See `reconnectTries` option above. @@ -314,11 +310,9 @@ Mongoose.prototype.createConnection = function(uri, options, callback) { * @param {String} [options.user] username for authentication, equivalent to `options.auth.user`. Maintained for backwards compatibility. * @param {String} [options.pass] password for authentication, equivalent to `options.auth.password`. Maintained for backwards compatibility. * @param {Number} [options.poolSize=5] The maximum number of sockets the MongoDB driver will keep open for this connection. By default, `poolSize` is 5. Keep in mind that, as of MongoDB 3.4, MongoDB only allows one operation per socket at a time, so you may want to increase this if you find you have a few slow queries that are blocking faster queries from proceeding. See [Slow Trains in MongoDB and Node.js](http://thecodebarbarian.com/slow-trains-in-mongodb-and-nodejs). - * @param {Boolean} [options.useUnifiedTopology=false] False by default. Set to `true` to opt in to the MongoDB driver's replica set and sharded cluster monitoring engine. * @param {Number} [options.serverSelectionTimeoutMS] If `useUnifiedTopology = true`, the MongoDB driver will try to find a server to send any given operation to, and keep retrying for `serverSelectionTimeoutMS` milliseconds before erroring out. If not set, the MongoDB driver defaults to using `30000` (30 seconds). * @param {Number} [options.heartbeatFrequencyMS] If `useUnifiedTopology = true`, the MongoDB driver sends a heartbeat every `heartbeatFrequencyMS` to check on the status of the connection. A heartbeat is subject to `serverSelectionTimeoutMS`, so the MongoDB driver will retry failed heartbeats for up to 30 seconds by default. Mongoose only emits a `'disconnected'` event after a heartbeat has failed, so you may want to decrease this setting to reduce the time between when your server goes down and when Mongoose emits `'disconnected'`. We recommend you do **not** set this setting below 1000, too many heartbeats can lead to performance degradation. * @param {Boolean} [options.autoIndex=true] Mongoose-specific option. Set to false to disable automatic index creation for all models associated with this connection. - * @param {Boolean} [options.useNewUrlParser=false] False by default. Set to `true` to opt in to the MongoDB driver's new URL parser logic. * @param {Boolean} [options.useCreateIndex=true] Mongoose-specific option. If `true`, this connection will use [`createIndex()` instead of `ensureIndex()`](/docs/deprecations.html#ensureindex) for automatic index builds via [`Model.init()`](/docs/api.html#model_Model.init). * @param {Number} [options.reconnectTries=30] If you're connected to a single server or mongos proxy (as opposed to a replica set), the MongoDB driver will try to reconnect every `reconnectInterval` milliseconds for `reconnectTries` times, and give up afterward. When the driver gives up, the mongoose connection emits a `reconnectFailed` event. This option does nothing for replica set connections. * @param {Number} [options.reconnectInterval=1000] See `reconnectTries` option above. diff --git a/test/collection.test.js b/test/collection.test.js index 1afb5741b63..3cedeb681aa 100644 --- a/test/collection.test.js +++ b/test/collection.test.js @@ -35,7 +35,7 @@ describe('collections:', function() { }); const uri = 'mongodb://localhost:27017/mongoose_test'; - db.openUri(process.env.MONGOOSE_TEST_URI || uri, { useNewUrlParser: true }, function(err) { + db.openUri(process.env.MONGOOSE_TEST_URI || uri, function(err) { connected = !err; finish(); }); @@ -53,7 +53,7 @@ describe('collections:', function() { }); const uri = 'mongodb://localhost:27017/mongoose_test'; - db.openUri(process.env.MONGOOSE_TEST_URI || uri, { useNewUrlParser: true }, function(err) { + db.openUri(process.env.MONGOOSE_TEST_URI || uri, function(err) { assert.ifError(err); promise.then(() => done(), done); }); diff --git a/test/connection.test.js b/test/connection.test.js index a3f38827889..eddf3084653 100644 --- a/test/connection.test.js +++ b/test/connection.test.js @@ -50,8 +50,7 @@ describe('connections:', function() { it('with autoIndex (gh-5423)', function(done) { const promise = mongoose.createConnection('mongodb://localhost:27017/mongoosetest', { - autoIndex: false, - useNewUrlParser: true + autoIndex: false }).asPromise(); promise.then(function(conn) { @@ -102,8 +101,7 @@ describe('connections:', function() { it('useCreateIndex (gh-6922)', function(done) { const conn = mongoose.createConnection('mongodb://localhost:27017/mongoosetest', { - useCreateIndex: true, - useNewUrlParser: true + useCreateIndex: true }); const M = conn.model('Test', new Schema({ @@ -125,15 +123,14 @@ describe('connections:', function() { it('throws helpful error with undefined uri (gh-6763)', function() { assert.throws(function() { - mongoose.createConnection(void 0, { useNewUrlParser: true }); + mongoose.createConnection(void 0); }, /string.*createConnection/); }); it('resolving with q (gh-5714)', function(done) { const bootMongo = Q.defer(); - const conn = mongoose.createConnection('mongodb://localhost:27017/mongoosetest', - { useNewUrlParser: true }); + const conn = mongoose.createConnection('mongodb://localhost:27017/mongoosetest'); conn.on('connected', function() { bootMongo.resolve(this); @@ -146,10 +143,8 @@ describe('connections:', function() { }); it('connection plugins (gh-7378)', function() { - const conn1 = mongoose.createConnection('mongodb://localhost:27017/mongoosetest', - { useNewUrlParser: true }); - const conn2 = mongoose.createConnection('mongodb://localhost:27017/mongoosetest', - { useNewUrlParser: true }); + const conn1 = mongoose.createConnection('mongodb://localhost:27017/mongoosetest'); + const conn2 = mongoose.createConnection('mongodb://localhost:27017/mongoosetest'); const called = []; conn1.plugin(schema => called.push(schema)); @@ -230,17 +225,6 @@ describe('connections:', function() { db.close(done); }); - it('should accept unix domain sockets', function() { - const host = encodeURIComponent('/tmp/mongodb-27017.sock'); - const db = mongoose.createConnection(`mongodb://aaron:psw@${host}/fake`); - db.asPromise().catch(() => {}); - assert.equal(db.name, 'fake'); - assert.equal(db.host, '/tmp/mongodb-27017.sock'); - assert.equal(db.pass, 'psw'); - assert.equal(db.user, 'aaron'); - db.close(); - }); - describe('errors', function() { it('.catch() means error does not get thrown (gh-5229)', function(done) { const db = mongoose.createConnection(); @@ -286,7 +270,7 @@ describe('connections:', function() { describe('connect callbacks', function() { it('should return an error if malformed uri passed', function(done) { - const db = mongoose.createConnection('mongodb:///fake', { useNewUrlParser: true }, function(err) { + const db = mongoose.createConnection('mongodb:///fake', {}, function(err) { assert.equal(err.name, 'MongoParseError'); done(); }); diff --git a/test/docs/date.test.js b/test/docs/date.test.js index 7dad6f7147f..e1536dc82e6 100644 --- a/test/docs/date.test.js +++ b/test/docs/date.test.js @@ -18,9 +18,7 @@ describe('Date Tutorial', function() { }); User = mongoose.model('User', userSchema); - return mongoose.connect('mongodb://localhost:27017/mongoose', { - useNewUrlParser: true - }); + return mongoose.connect('mongodb://localhost:27017/mongoose'); }); it('Example 1.2: casts strings to dates', function() { diff --git a/test/es-next/cast.test.es6.js b/test/es-next/cast.test.es6.js index 92c76b6d05c..efd58eac1ed 100644 --- a/test/es-next/cast.test.es6.js +++ b/test/es-next/cast.test.es6.js @@ -13,9 +13,7 @@ describe('Cast Tutorial', function() { const schema = new mongoose.Schema({ name: String, age: Number }); Character = mongoose.model('Character', schema); - await mongoose.connect('mongodb://localhost:27017/mongoose', { - useNewUrlParser: true - }); + await mongoose.connect('mongodb://localhost:27017/mongoose'); await Character.deleteMany({}); await Character.create({ diff --git a/test/es-next/findoneandupdate.test.es6.js b/test/es-next/findoneandupdate.test.es6.js index c8364ac3fec..19e56265490 100644 --- a/test/es-next/findoneandupdate.test.es6.js +++ b/test/es-next/findoneandupdate.test.es6.js @@ -10,9 +10,7 @@ describe('Tutorial: findOneAndUpdate()', function() { let Character; before(async function() { - await mongoose.connect('mongodb://localhost:27017/mongoose', { - useNewUrlParser: true - }); + await mongoose.connect('mongodb://localhost:27017/mongoose'); await mongoose.connection.dropDatabase(); }); diff --git a/test/es-next/getters-setters.test.es6.js b/test/es-next/getters-setters.test.es6.js index 2d1a8f44523..fca73b323cf 100644 --- a/test/es-next/getters-setters.test.es6.js +++ b/test/es-next/getters-setters.test.es6.js @@ -10,7 +10,7 @@ const Schema = mongoose.Schema; describe('getters/setters', function() { before(async function() { - await mongoose.connect('mongodb://localhost:27017/test', { useNewUrlParser: true }); + await mongoose.connect('mongodb://localhost:27017/test'); }); beforeEach(function() { diff --git a/test/es-next/lean.test.es6.js b/test/es-next/lean.test.es6.js index f9153e8bcad..2c09e166ecc 100644 --- a/test/es-next/lean.test.es6.js +++ b/test/es-next/lean.test.es6.js @@ -13,9 +13,7 @@ describe('Lean Tutorial', function() { const schema = new mongoose.Schema({ name: String }); MyModel = mongoose.model('Test1', schema); - return mongoose.connect('mongodb://localhost:27017/mongoose', { - useNewUrlParser: true - }); + return mongoose.connect('mongodb://localhost:27017/mongoose'); }); beforeEach(function() { diff --git a/test/es-next/virtuals.test.es6.js b/test/es-next/virtuals.test.es6.js index 03bc01b72c2..a39b3e999b5 100644 --- a/test/es-next/virtuals.test.es6.js +++ b/test/es-next/virtuals.test.es6.js @@ -10,7 +10,7 @@ const Schema = mongoose.Schema; describe('Virtuals', function() { before(async function() { - await mongoose.connect('mongodb://localhost:27017/test', { useNewUrlParser: true }); + await mongoose.connect('mongodb://localhost:27017/test'); }); beforeEach(function() { diff --git a/test/index.test.js b/test/index.test.js index 322fa4df39d..fdbc5dbaf62 100644 --- a/test/index.test.js +++ b/test/index.test.js @@ -15,9 +15,7 @@ const Schema = mongoose.Schema; const uri = 'mongodb://localhost:27017/mongoose_test'; -const options = { - useNewUrlParser: true -}; +const options = {}; describe('mongoose module:', function() { describe('default connection works', function() { @@ -101,11 +99,9 @@ describe('mongoose module:', function() { const mongoose = new Mongoose(); mongoose.set('runValidators', 'b'); - mongoose.set('useNewUrlParser', 'c'); assert.equal(mongoose.get('runValidators'), 'b'); assert.equal(mongoose.set('runValidators'), 'b'); - assert.equal(mongoose.get('useNewUrlParser'), 'c'); }); it('allows `const { model } = mongoose` (gh-3768)', function() { diff --git a/test/typescript/connection.ts b/test/typescript/connection.ts index eb3b9095230..5f24b49a039 100644 --- a/test/typescript/connection.ts +++ b/test/typescript/connection.ts @@ -6,7 +6,7 @@ conn.model('Test', new Schema({ name: { type: String } })); conn.openUri('mongodb://localhost:27017/test').then(() => console.log('Connected!')); -createConnection('mongodb://localhost:27017/test', { useNewUrlParser: true }).then((conn: Connection) => { +createConnection('mongodb://localhost:27017/test').then((conn: Connection) => { conn.host; }); From 84d4586ab1c47d6b77fe92b2ed875538954c04da Mon Sep 17 00:00:00 2001 From: Valeri Karpov Date: Sat, 23 Jan 2021 18:23:33 -0500 Subject: [PATCH 3/8] fix: clean up test failures and incompatilibities in updates and query cursors re: #9840 --- .github/workflows/test.yml | 2 +- .travis.yml | 2 +- lib/cursor/QueryCursor.js | 19 ++--- lib/query.js | 25 +------ test/model.update.test.js | 149 ++++--------------------------------- test/query.cursor.test.js | 44 +---------- 6 files changed, 30 insertions(+), 211 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 96519ae8a18..c56b6f1bdd7 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -8,7 +8,7 @@ jobs: strategy: fail-fast: false matrix: - node: [4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15] + node: [10, 12, 14, 15] name: Node ${{ matrix.node }} steps: - uses: actions/checkout@v2 diff --git a/.travis.yml b/.travis.yml index ad98e77aa83..1cba4dd76b0 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,6 +1,6 @@ language: node_js sudo: false -node_js: [14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4] +node_js: [15, 14, 12, 10] install: - travis_retry npm install before_script: diff --git a/lib/cursor/QueryCursor.js b/lib/cursor/QueryCursor.js index 32a130f7d7d..5b5832e2344 100644 --- a/lib/cursor/QueryCursor.js +++ b/lib/cursor/QueryCursor.js @@ -55,17 +55,14 @@ function QueryCursor(query, options) { this.options.cursor = options.cursor || {}; this.options.cursor.batchSize = options.batchSize; } - model.collection.find(query._conditions, this.options, function(err, cursor) { - if (_this._error) { - cursor.close(function() {}); - _this.listeners('error').length > 0 && _this.emit('error', _this._error); - } - if (err) { - return _this.emit('error', err); - } - _this.cursor = cursor; - _this.emit('cursor', cursor); - }); + const cursor = model.collection.find(query._conditions, this.options); + + if (_this._error) { + cursor.close(function() {}); + _this.listeners('error').length > 0 && _this.emit('error', _this._error); + } + _this.cursor = cursor; + _this.emit('cursor', cursor); }); } diff --git a/lib/query.js b/lib/query.js index 7bbaa7138c7..ce98373264c 100644 --- a/lib/query.js +++ b/lib/query.js @@ -1389,12 +1389,8 @@ Query.prototype.getOptions = function() { * - [upsert](https://docs.mongodb.com/manual/reference/method/db.collection.update/) * - [writeConcern](https://docs.mongodb.com/manual/reference/method/db.collection.update/) * - [timestamps](https://mongoosejs.com/docs/guide.html#timestamps): If `timestamps` is set in the schema, set this option to `false` to skip timestamps for that particular update. Has no effect if `timestamps` is not enabled in the schema options. -<<<<<<< HEAD -======= * - omitUndefined: delete any properties whose value is `undefined` when casting an update. In other words, if this is set, Mongoose will delete `baz` from the update in `Model.updateOne({}, { foo: 'bar', baz: undefined })` before sending the update to the server. * - overwriteDiscriminatorKey: allow setting the discriminator key in the update. Will use the correct discriminator schema if the update changes the discriminator key. - * - overwrite: replace the entire document ->>>>>>> master * * The following options are only for `find()`, `findOne()`, `findById()`, `findOneAndUpdate()`, and `findByIdAndUpdate()`: * @@ -3801,10 +3797,6 @@ function _updateThunk(op, callback) { } callback = _wrapThunkCallback(this, callback); - const oldCb = callback; - callback = function(error, result) { - oldCb(error, result ? result.result : { ok: 0, n: 0, nModified: 0 }); - }; const castedQuery = this._conditions; const options = this._optionsForExec(this.model); @@ -3827,7 +3819,7 @@ function _updateThunk(op, callback) { } if (this._update == null || Object.keys(this._update).length === 0) { - callback(null, 0); + callback(null, { acknowledged: false }); return null; } @@ -3959,13 +3951,12 @@ Query.prototype._replaceOne = wrapThunk(function(callback) { * - `runValidators` (boolean) if true, runs [update validators](/docs/validation.html#update-validators) on this command. Update validators validate the update operation against the model's schema. * - `setDefaultsOnInsert` (boolean) `true` by default. If `setDefaultsOnInsert` and `upsert` are true, mongoose will apply the [defaults](http://mongoosejs.com/docs/defaults.html) specified in the model's schema if a new document is created. * - `strict` (boolean) overrides the `strict` option for this update - * - `overwrite` (boolean) disables update-only mode, allowing you to overwrite the doc (false) * - `read` * - `writeConcern` * * ####Note * - * Passing an empty object `{}` as the doc will result in a no-op unless the `overwrite` option is passed. Without the `overwrite` option set, the update operation will be ignored and the callback executed without sending the command to MongoDB so as to prevent accidently overwritting documents in the collection. + * Passing an empty object `{}` as the doc will result in a no-op. The update operation will be ignored and the callback executed without sending the command to MongoDB. * * ####Note * @@ -3980,16 +3971,6 @@ Query.prototype._replaceOne = wrapThunk(function(callback) { * // this executes the same command as the previous example. * q.update({ name: 'bob' }).exec(); * - * // overwriting with empty docs - * const q = Model.where({ _id: id }).setOptions({ overwrite: true }) - * q.update({ }, callback); // executes - * - * // multi update with overwrite to empty doc - * const q = Model.where({ _id: id }); - * q.setOptions({ multi: true, overwrite: true }) - * q.update({ }); - * q.update(callback); // executed - * * // multi updates * Model.where() * .update({ name: /^match/ }, { $set: { arr: [] }}, { multi: true }, callback) @@ -4127,7 +4108,7 @@ Query.prototype.updateMany = function(conditions, doc, options, callback) { /** * Declare and/or execute this query as an updateOne() operation. Same as - * `update()`, except it does not support the `multi` or `overwrite` options. + * `update()`, except it does not support the `multi` option. * * - MongoDB will update _only_ the first document that matches `filter` regardless of the value of the `multi` option. * - Use `replaceOne()` if you want to overwrite an entire document rather than using [atomic](https://docs.mongodb.com/manual/tutorial/model-data-for-atomic-operations/#pattern) operators like `$set`. diff --git a/test/model.update.test.js b/test/model.update.test.js index 0acdf1d8092..43aaf626bf6 100644 --- a/test/model.update.test.js +++ b/test/model.update.test.js @@ -492,8 +492,8 @@ describe('model: update:', function() { yield BlogPost.deleteMany({}); yield BlogPost.create({ title: 'one' }, { title: 'two' }, { title: 'three' }); - const res = yield BlogPost.update({}, { title: 'newtitle' }, { multi: true }); - assert.equal(res.n, 3); + const res = yield BlogPost.updateMany({}, { title: 'newtitle' }); + assert.equal(res.modifiedCount, 3); }); }); @@ -521,7 +521,7 @@ describe('model: update:', function() { assert.ifError(err); M.update({ _id: doc._id }, { $pull: { comments: { name: 'node 0.8' } } }, function(err, affected) { assert.ifError(err); - assert.equal(affected.n, 1); + assert.equal(affected.modifiedCount, 1); done(); }); }); @@ -617,9 +617,9 @@ describe('model: update:', function() { const doc1 = docs[1]; const sId0 = doc0._id; const sId1 = doc1._id; - Some.where({ _id: sId0 }).update({}, { $set: { num: '99' } }, { multi: true }, function(err, cnt) { + Some.where({ _id: sId0 }).updateOne({}, { $set: { num: '99' } }, { multi: true }, function(err, cnt) { assert.ifError(err); - assert.equal(cnt.n, 1); + assert.equal(cnt.modifiedCount, 1); Some.findById(sId0, function(err, doc0_1) { assert.ifError(err); assert.equal(doc0_1.num, 99); @@ -798,70 +798,6 @@ describe('model: update:', function() { }); }); - describe('{overwrite: true}', function() { - it('overwrite works', function(done) { - const schema = new Schema({ mixed: {} }, { minimize: false }); - const M = db.model('Test', schema); - - M.create({ mixed: 'something' }, function(err, created) { - assert.ifError(err); - - M.update({ _id: created._id }, { mixed: {} }, { overwrite: true }, function(err) { - assert.ifError(err); - M.findById(created._id, function(err, doc) { - assert.ifError(err); - assert.equal(created.id, doc.id); - assert.equal(typeof doc.mixed, 'object'); - assert.equal(Object.keys(doc.mixed).length, 0); - done(); - }); - }); - }); - }); - - it('overwrites all properties', function(done) { - const sch = new Schema({ title: String, subdoc: { name: String, num: Number } }); - - const M = db.model('Test', sch); - - M.create({ subdoc: { name: 'that', num: 1 } }, function(err, doc) { - assert.ifError(err); - - M.update({ _id: doc.id }, { title: 'something!' }, { overwrite: true }, function(err) { - assert.ifError(err); - M.findById(doc.id, function(err, doc) { - assert.ifError(err); - assert.equal(doc.title, 'something!'); - assert.equal(doc.subdoc.name, undefined); - assert.equal(doc.subdoc.num, undefined); - done(); - }); - }); - }); - }); - - it('allows users to blow it up', function(done) { - const sch = new Schema({ title: String, subdoc: { name: String, num: Number } }); - - const M = db.model('Test', sch); - - M.create({ subdoc: { name: 'that', num: 1, title: 'hello' } }, function(err, doc) { - assert.ifError(err); - - M.update({ _id: doc.id }, {}, { overwrite: true }, function(err) { - assert.ifError(err); - M.findById(doc.id, function(err, doc) { - assert.ifError(err); - assert.equal(doc.title, undefined); - assert.equal(doc.subdoc.name, undefined); - assert.equal(doc.subdoc.num, undefined); - done(); - }); - }); - }); - }); - }); - it('casts empty arrays', function(done) { const so = new Schema({ arr: [] }); const Some = db.model('Test', so); @@ -1216,8 +1152,7 @@ describe('model: update:', function() { { breakfast: { eggs: 2, bacon: 3 } }, function(error, result) { assert.ifError(error); - assert.ok(result.ok); - assert.equal(result.n, 1); + assert.equal(result.modifiedCount, 1); M.findOne({ _id: doc._id }, function(error, doc) { assert.ifError(error); assert.equal(doc.breakfast.eggs, 2); @@ -1233,10 +1168,9 @@ describe('model: update:', function() { M.create({}, function(error, doc) { assert.ifError(error); - M.update({ _id: doc._id }, { notInSchema: 1 }).exec(). + M.updateOne({ _id: doc._id }, { notInSchema: 1 }).exec(). then(function(data) { - assert.equal(data.ok, 0); - assert.equal(data.n, 0); + assert.ok(!data.acknowledged); done(); }). catch(done); @@ -1356,38 +1290,6 @@ describe('model: update:', function() { }); }); - it('works with overwrite but no $set (gh-2568)', function(done) { - const chapterSchema = { - name: String - }; - const bookSchema = { - chapters: [chapterSchema], - title: String, - author: String, - id: Number - }; - const Book = db.model('Book', bookSchema); - - const jsonObject = { - chapters: [{ name: 'Ursus' }, { name: 'The Comprachicos' }], - name: 'The Man Who Laughs', - author: 'Victor Hugo', - id: 0 - }; - - Book.update({}, jsonObject, { upsert: true, overwrite: true }, - function(error) { - assert.ifError(error); - Book.findOne({ id: 0 }, function(error, book) { - assert.ifError(error); - assert.equal(book.chapters.length, 2); - assert.ok(book.chapters[0]._id); - assert.ok(book.chapters[1]._id); - done(); - }); - }); - }); - it('works with undefined date (gh-2833)', function(done) { const dateSchema = { d: Date @@ -1481,8 +1383,7 @@ describe('model: update:', function() { assert.ifError(error); Model.update({}, update, { multi: true }, function(error, res) { assert.ifError(error); - assert.ok(res.ok); - assert.equal(res.nModified, 1); + assert.equal(res.modifiedCount, 1); done(); }); }); @@ -1516,7 +1417,7 @@ describe('model: update:', function() { update({ _id: m._id }, { $push: { myArr: { key: 'Value' } } }). exec(function(error, res) { assert.ifError(error); - assert.equal(res.n, 1); + assert.equal(res.modifiedCount, 1); done(); }); }); @@ -1552,10 +1453,10 @@ describe('model: update:', function() { assert.ifError(error); const update = { $push: { 'attributes.scores.bar': { a: 1 } } }; Model. - update({ _id: m._id }, update). + updateOne({ _id: m._id }, update). exec(function(error, res) { assert.ifError(error); - assert.equal(res.n, 1); + assert.equal(res.modifiedCount, 1); Model.findById(m._id, function(error, doc) { assert.ifError(error); assert.equal(doc.attributes.scores.bar.length, 1); @@ -2041,13 +1942,13 @@ describe('model: update:', function() { }, { timestamps: true }); const TestModel = db.model('Test', testSchema); - const options = { overwrite: true, upsert: true }; + const options = { upsert: true }; const update = { user: 'John', something: 1 }; - TestModel.update({ user: 'test' }, update, options, function(error) { + TestModel.replaceOne({ user: 'test' }, update, options, function(error) { assert.ifError(error); TestModel.findOne({}, function(error, doc) { assert.ifError(error); @@ -2484,12 +2385,11 @@ describe('model: update:', function() { const TestModel = db.model('Test', testSchema); const options = { - overwrite: true, upsert: true }; const update = { name: 'test' }; - TestModel.update({ name: 'a' }, update, options, function(error) { + TestModel.replaceOne({ name: 'a' }, update, options, function(error) { assert.ifError(error); TestModel.findOne({}, function(error, doc) { assert.ifError(error); @@ -2525,25 +2425,6 @@ describe('model: update:', function() { }); }); - it('with setOptions overwrite (gh-5413)', function(done) { - const schema = new mongoose.Schema({ - _id: String, - data: String - }, { timestamps: true }); - - const Model = db.model('Test', schema); - - Model. - where({ _id: 'test' }). - setOptions({ overwrite: true, upsert: true }). - update({ data: 'test2' }). - exec(). - then(function() { - done(); - }). - catch(done); - }); - it('$push with updateValidators and top-level doc (gh-5430)', function(done) { const notificationSchema = new mongoose.Schema({ message: String diff --git a/test/query.cursor.test.js b/test/query.cursor.test.js index a260bac25f2..a0c3ec88a20 100644 --- a/test/query.cursor.test.js +++ b/test/query.cursor.test.js @@ -153,33 +153,28 @@ describe('QueryCursor', function() { Band.find().sort({ name: 1 }).populate('members').cursor(); cursor.next(function(error, doc) { assert.ifError(error); - assert.equal(cursor.cursor.cursorState.currentLimit, 1); assert.equal(doc.name, 'Guns N\' Roses'); assert.equal(doc.members.length, 2); assert.equal(doc.members[0].name, 'Axl Rose'); assert.equal(doc.members[1].name, 'Slash'); cursor.next(function(error, doc) { assert.ifError(error); - assert.equal(cursor.cursor.cursorState.currentLimit, 2); assert.equal(doc.name, 'Motley Crue'); assert.equal(doc.members.length, 2); assert.equal(doc.members[0].name, 'Nikki Sixx'); assert.equal(doc.members[1].name, 'Vince Neil'); cursor.next(function(error, doc) { assert.ifError(error); - assert.equal(cursor.cursor.cursorState.currentLimit, 3); assert.equal(doc.name, 'Nine Inch Nails'); assert.equal(doc.members.length, 1); assert.equal(doc.members[0].name, 'Trent Reznor'); cursor.next(function(error, doc) { assert.ifError(error); - assert.equal(cursor.cursor.cursorState.currentLimit, 4); assert.equal(doc.name, 'Radiohead'); assert.equal(doc.members.length, 1); assert.equal(doc.members[0].name, 'Thom Yorke'); cursor.next(function(error, doc) { assert.ifError(error); - assert.equal(cursor.cursor.cursorState.currentLimit, 5); assert.equal(doc.name, 'The Smashing Pumpkins'); assert.equal(doc.members.length, 1); assert.equal(doc.members[0].name, 'Billy Corgan'); @@ -196,33 +191,28 @@ describe('QueryCursor', function() { Band.find().sort({ name: 1 }).populate('members').batchSize(3).cursor(); cursor.next(function(error, doc) { assert.ifError(error); - assert.equal(cursor.cursor.cursorState.currentLimit, 3); assert.equal(doc.name, 'Guns N\' Roses'); assert.equal(doc.members.length, 2); assert.equal(doc.members[0].name, 'Axl Rose'); assert.equal(doc.members[1].name, 'Slash'); cursor.next(function(error, doc) { assert.ifError(error); - assert.equal(cursor.cursor.cursorState.currentLimit, 3); assert.equal(doc.name, 'Motley Crue'); assert.equal(doc.members.length, 2); assert.equal(doc.members[0].name, 'Nikki Sixx'); assert.equal(doc.members[1].name, 'Vince Neil'); cursor.next(function(error, doc) { assert.ifError(error); - assert.equal(cursor.cursor.cursorState.currentLimit, 3); assert.equal(doc.name, 'Nine Inch Nails'); assert.equal(doc.members.length, 1); assert.equal(doc.members[0].name, 'Trent Reznor'); cursor.next(function(error, doc) { assert.ifError(error); - assert.equal(cursor.cursor.cursorState.currentLimit, 5); assert.equal(doc.name, 'Radiohead'); assert.equal(doc.members.length, 1); assert.equal(doc.members[0].name, 'Thom Yorke'); cursor.next(function(error, doc) { assert.ifError(error); - assert.equal(cursor.cursor.cursorState.currentLimit, 5); assert.equal(doc.name, 'The Smashing Pumpkins'); assert.equal(doc.members.length, 1); assert.equal(doc.members[0].name, 'Billy Corgan'); @@ -475,7 +465,7 @@ describe('QueryCursor', function() { assert.ok(closed); cursor.next(function(error) { assert.ok(error); - assert.equal(error.message, 'Cursor is closed'); + assert.equal(error.name, 'MongoError'); done(); }); }); @@ -502,21 +492,6 @@ describe('QueryCursor', function() { }); }); - it('addCursorFlag (gh-4814)', function(done) { - const userSchema = new mongoose.Schema({ - name: String - }); - - const User = db.model('User', userSchema); - - const cursor = User.find().cursor().addCursorFlag('noCursorTimeout', true); - - cursor.on('cursor', function() { - assert.equal(cursor.cursor.cursorState.cmd.noCursorTimeout, true); - done(); - }); - }); - it('data before close (gh-4998)', function(done) { const userSchema = new mongoose.Schema({ name: String @@ -526,7 +501,7 @@ describe('QueryCursor', function() { const users = []; for (let i = 0; i < 100; i++) { users.push({ - _id: mongoose.Types.ObjectId(), + _id: new mongoose.Types.ObjectId(), name: 'Bob' + (i < 10 ? '0' : '') + i }); } @@ -548,21 +523,6 @@ describe('QueryCursor', function() { }); }); - it('batchSize option (gh-8039)', function() { - const User = db.model('gh8039', Schema({ name: String })); - let cursor = User.find().cursor({ batchSize: 2000 }); - - return new Promise(resolve => cursor.once('cursor', () => resolve())). - then(() => assert.equal(cursor.cursor.cursorState.batchSize, 2000)). - then(() => { - cursor = User.find().batchSize(2001).cursor(); - }). - then(() => new Promise(resolve => cursor.once('cursor', () => resolve()))). - then(() => { - assert.equal(cursor.cursor.cursorState.batchSize, 2001); - }); - }); - it('pulls schema-level readPreference (gh-8421)', function() { const read = 'secondaryPreferred'; const User = db.model('User', Schema({ name: String }, { read })); From 4b4572a6ab33e413491a73a28d6a81872dbd54c4 Mon Sep 17 00:00:00 2001 From: Valeri Karpov Date: Sat, 23 Jan 2021 18:54:31 -0500 Subject: [PATCH 4/8] fix: more test fixes for #9840 --- docs/connections.pug | 9 --------- docs/lambda.pug | 2 +- lib/connection.js | 4 ---- lib/index.js | 2 -- lib/model.js | 8 +++----- lib/query.js | 8 ++++++-- test/aggregate.test.js | 17 ----------------- test/model.querying.test.js | 15 +++++++++------ test/model.test.js | 14 ++++++++------ test/query.test.js | 14 +++++++------- 10 files changed, 34 insertions(+), 59 deletions(-) diff --git a/docs/connections.pug b/docs/connections.pug index f24070c10ed..6be93e02741 100644 --- a/docs/connections.pug +++ b/docs/connections.pug @@ -181,15 +181,6 @@ block content The following options are important for tuning Mongoose only if you are running **without** [the `useUnifiedTopology` option](/docs/deprecations.html#useunifiedtopology): - * `autoReconnect` - The underlying MongoDB driver will automatically try to reconnect when it loses connection to MongoDB. Unless you are an extremely advanced user that wants to manage their own connection pool, do **not** set this option to `false`. - * `reconnectTries` - If you're connected to a single server or mongos proxy (as opposed to a replica set), the MongoDB driver will try to reconnect every `reconnectInterval` milliseconds for `reconnectTries` times, and give up afterward. When the driver gives up, the mongoose connection emits a `reconnectFailed` event. This option does nothing for replica set connections. - * `reconnectInterval` - See `reconnectTries` - * `bufferMaxEntries` - The MongoDB driver also has its own buffering mechanism that kicks in when the driver is disconnected. Set this option to 0 and set `bufferCommands` to `false` on your schemas if you want your database operations to fail immediately when the driver is not connected, as opposed to waiting for reconnection. - * `connectTimeoutMS` - How long the MongoDB driver will wait before killing a socket due to inactivity _during initial connection_. Defaults to 30000. This option is passed transparently to [Node.js' `socket#setTimeout()` function](https://nodejs.org/api/net.html#net_socket_settimeout_timeout_callback). - - The following options are important for tuning Mongoose only if you are - running **with** [the `useUnifiedTopology` option](/docs/deprecations.html#useunifiedtopology): - * `serverSelectionTimeoutMS` - With `useUnifiedTopology`, the MongoDB driver will try to find a server to send any given operation to, and keep retrying for `serverSelectionTimeoutMS` milliseconds. If not set, the MongoDB driver defaults to using `30000` (30 seconds). * `heartbeatFrequencyMS` - With `useUnifiedTopology`, the MongoDB driver sends a heartbeat every `heartbeatFrequencyMS` to check on the status of the connection. A heartbeat is subject to `serverSelectionTimeoutMS`, so the MongoDB driver will retry failed heartbeats for up to 30 seconds by default. Mongoose only emits a `'disconnected'` event after a heartbeat has failed, so you may want to decrease this setting to reduce the time between when your server goes down and when Mongoose emits `'disconnected'`. We recommend you do **not** set this setting below 1000, too many heartbeats can lead to performance degradation. diff --git a/docs/lambda.pug b/docs/lambda.pug index 1b5b952058a..0025109431c 100644 --- a/docs/lambda.pug +++ b/docs/lambda.pug @@ -48,7 +48,7 @@ block content // disconnected from MongoDB and send them when it reconnects. // With serverless, better to fail fast if not connected. bufferCommands: false, // Disable mongoose buffering - bufferMaxEntries: 0 // and MongoDB driver buffering + serverSelectionTimeoutMS: 5000 }); // `await`ing connection after assigning to the `conn` variable diff --git a/lib/connection.js b/lib/connection.js index 04660063445..d35acdc4c19 100644 --- a/lib/connection.js +++ b/lib/connection.js @@ -660,7 +660,6 @@ Connection.prototype.onOpen = function() { * @param {Number} [options.reconnectTries=30] If you're connected to a single server or mongos proxy (as opposed to a replica set), the MongoDB driver will try to reconnect every `reconnectInterval` milliseconds for `reconnectTries` times, and give up afterward. When the driver gives up, the mongoose connection emits a `reconnectFailed` event. This option does nothing for replica set connections. * @param {Number} [options.reconnectInterval=1000] See `reconnectTries` option above. * @param {Class} [options.promiseLibrary] Sets the [underlying driver's promise library](http://mongodb.github.io/node-mongodb-native/3.1/api/MongoClient.html). - * @param {Number} [options.bufferMaxEntries] This option does nothing if `useUnifiedTopology` is set. The MongoDB driver also has its own buffering mechanism that kicks in when the driver is disconnected. Set this option to 0 and set `bufferCommands` to `false` on your schemas if you want your database operations to fail immediately when the driver is not connected, as opposed to waiting for reconnection. * @param {Number} [options.connectTimeoutMS=30000] How long the MongoDB driver will wait before killing a socket due to inactivity _during initial connection_. Defaults to 30000. This option is passed transparently to [Node.js' `socket#setTimeout()` function](https://nodejs.org/api/net.html#net_socket_settimeout_timeout_callback). * @param {Number} [options.socketTimeoutMS=30000] How long the MongoDB driver will wait before killing a socket due to inactivity _after initial connection_. A socket may be inactive because of either no activity or a long-running operation. This is set to `30000` by default, you should set this to 2-3x your longest running operation if you expect some of your database operations to run longer than 20 seconds. This option is passed to [Node.js `socket#setTimeout()` function](https://nodejs.org/api/net.html#net_socket_settimeout_timeout_callback) after the MongoDB driver successfully completes. * @param {Number} [options.family=0] Passed transparently to [Node.js' `dns.lookup()`](https://nodejs.org/api/dns.html#dns_dns_lookup_hostname_options_callback) function. May be either `0, `4`, or `6`. `4` means use IPv4 only, `6` means use IPv6 only, `0` means try both. @@ -751,9 +750,6 @@ Connection.prototype.openUri = function(uri, options, callback) { delete options.pass; if (options.bufferCommands != null) { - if (options.bufferMaxEntries == null) { - options.bufferMaxEntries = 0; - } this.config.bufferCommands = options.bufferCommands; delete options.bufferCommands; } diff --git a/lib/index.js b/lib/index.js index a2472a11534..12dd03565e1 100644 --- a/lib/index.js +++ b/lib/index.js @@ -257,7 +257,6 @@ Mongoose.prototype.get = Mongoose.prototype.set; * @param {Number} [options.reconnectInterval=1000] See `reconnectTries` option above. * @param {Class} [options.promiseLibrary] Sets the [underlying driver's promise library](http://mongodb.github.io/node-mongodb-native/3.1/api/MongoClient.html). * @param {Number} [options.poolSize=5] The maximum number of sockets the MongoDB driver will keep open for this connection. By default, `poolSize` is 5. Keep in mind that, as of MongoDB 3.4, MongoDB only allows one operation per socket at a time, so you may want to increase this if you find you have a few slow queries that are blocking faster queries from proceeding. See [Slow Trains in MongoDB and Node.js](http://thecodebarbarian.com/slow-trains-in-mongodb-and-nodejs). - * @param {Number} [options.bufferMaxEntries] This option does nothing if `useUnifiedTopology` is set. The MongoDB driver also has its own buffering mechanism that kicks in when the driver is disconnected. Set this option to 0 and set `bufferCommands` to `false` on your schemas if you want your database operations to fail immediately when the driver is not connected, as opposed to waiting for reconnection. * @param {Number} [options.connectTimeoutMS=30000] How long the MongoDB driver will wait before killing a socket due to inactivity _during initial connection_. Defaults to 30000. This option is passed transparently to [Node.js' `socket#setTimeout()` function](https://nodejs.org/api/net.html#net_socket_settimeout_timeout_callback). * @param {Number} [options.socketTimeoutMS=30000] How long the MongoDB driver will wait before killing a socket due to inactivity _after initial connection_. A socket may be inactive because of either no activity or a long-running operation. This is set to `30000` by default, you should set this to 2-3x your longest running operation if you expect some of your database operations to run longer than 20 seconds. This option is passed to [Node.js `socket#setTimeout()` function](https://nodejs.org/api/net.html#net_socket_settimeout_timeout_callback) after the MongoDB driver successfully completes. * @param {Number} [options.family=0] Passed transparently to [Node.js' `dns.lookup()`](https://nodejs.org/api/dns.html#dns_dns_lookup_hostname_options_callback) function. May be either `0`, `4`, or `6`. `4` means use IPv4 only, `6` means use IPv6 only, `0` means try both. @@ -317,7 +316,6 @@ Mongoose.prototype.createConnection = function(uri, options, callback) { * @param {Number} [options.reconnectTries=30] If you're connected to a single server or mongos proxy (as opposed to a replica set), the MongoDB driver will try to reconnect every `reconnectInterval` milliseconds for `reconnectTries` times, and give up afterward. When the driver gives up, the mongoose connection emits a `reconnectFailed` event. This option does nothing for replica set connections. * @param {Number} [options.reconnectInterval=1000] See `reconnectTries` option above. * @param {Class} [options.promiseLibrary] Sets the [underlying driver's promise library](http://mongodb.github.io/node-mongodb-native/3.1/api/MongoClient.html). - * @param {Number} [options.bufferMaxEntries] This option does nothing if `useUnifiedTopology` is set. The MongoDB driver also has its own buffering mechanism that kicks in when the driver is disconnected. Set this option to 0 and set `bufferCommands` to `false` on your schemas if you want your database operations to fail immediately when the driver is not connected, as opposed to waiting for reconnection. * @param {Number} [options.connectTimeoutMS=30000] How long the MongoDB driver will wait before killing a socket due to inactivity _during initial connection_. Defaults to 30000. This option is passed transparently to [Node.js' `socket#setTimeout()` function](https://nodejs.org/api/net.html#net_socket_settimeout_timeout_callback). * @param {Number} [options.socketTimeoutMS=30000] How long the MongoDB driver will wait before killing a socket due to inactivity _after initial connection_. A socket may be inactive because of either no activity or a long-running operation. This is set to `30000` by default, you should set this to 2-3x your longest running operation if you expect some of your database operations to run longer than 20 seconds. This option is passed to [Node.js `socket#setTimeout()` function](https://nodejs.org/api/net.html#net_socket_settimeout_timeout_callback) after the MongoDB driver successfully completes. * @param {Number} [options.family=0] Passed transparently to [Node.js' `dns.lookup()`](https://nodejs.org/api/dns.html#dns_dns_lookup_hostname_options_callback) function. May be either `0`, `4`, or `6`. `4` means use IPv4 only, `6` means use IPv6 only, `0` means try both. diff --git a/lib/model.js b/lib/model.js index 143e5f43f07..17f5461aa98 100644 --- a/lib/model.js +++ b/lib/model.js @@ -360,10 +360,8 @@ Model.prototype.$__save = function(options, callback) { if (result) { if (Array.isArray(result)) { numAffected = result.length; - } else if (result.result && result.result.n !== undefined) { - numAffected = result.result.n; - } else if (result.result && result.result.nModified !== undefined) { - numAffected = result.result.nModified; + } else if (result.modifiedCount != null) { + numAffected = result.modifiedCount; } else { numAffected = result; } @@ -1316,7 +1314,7 @@ Model.createCollection = function createCollection(options, callback) { 'This is not like Connection.createCollection. Only options are accepted here.'); } else if (typeof options === 'function') { callback = options; - options = null; + options = void 0; } const schemaCollation = get(this, 'schema.options.collation', null); diff --git a/lib/query.js b/lib/query.js index ce98373264c..d98501258fd 100644 --- a/lib/query.js +++ b/lib/query.js @@ -1482,6 +1482,10 @@ Query.prototype.explain = function(verbose) { this.options.explain = true; return this; } + if (verbose === false) { + delete this.options.explain; + return this; + } this.options.explain = verbose; return this; }; @@ -4348,7 +4352,7 @@ Query.prototype.orFail = function(err) { case 'update': case 'updateMany': case 'updateOne': - if (get(res, 'nModified') === 0) { + if (get(res, 'modifiedCount') === 0) { throw _orFailError(err, this); } break; @@ -4367,7 +4371,7 @@ Query.prototype.orFail = function(err) { case 'deleteMany': case 'deleteOne': case 'remove': - if (res.n === 0) { + if (res.deletedCount === 0) { throw _orFailError(err, this); } break; diff --git a/test/aggregate.test.js b/test/aggregate.test.js index 573afbbb8c9..333093a7054 100644 --- a/test/aggregate.test.js +++ b/test/aggregate.test.js @@ -1239,23 +1239,6 @@ describe('aggregate: ', function() { catch(done); }); - it('ability to add noCursorTimeout option (gh-4241)', function(done) { - const MyModel = db.model('Test', { - name: String - }); - - const cursor = MyModel. - aggregate([{ $match: { name: 'test' } }]). - option({ noCursorTimeout: true }). - cursor(). - exec(); - - cursor.once('cursor', cursor => { - assert.ok(cursor.cursorState.cmd.noCursorTimeout); - done(); - }); - }); - it('query by document (gh-4866)', function(done) { const MyModel = db.model('Test', { name: String diff --git a/test/model.querying.test.js b/test/model.querying.test.js index 79e6bfad05d..67e488373c9 100644 --- a/test/model.querying.test.js +++ b/test/model.querying.test.js @@ -394,10 +394,11 @@ describe('model: querying:', function() { }); }); - post.collection.insertOne({ meta: { visitors: 9898, a: null } }, {}, function(err, b) { + const doc = { meta: { visitors: 9898, a: null } }; + post.collection.insertOne(doc, {}, function(err) { assert.ifError(err); - BlogPostA.findOne({ _id: b.ops[0]._id }, function(err, found) { + BlogPostA.findOne({ _id: doc._id }, function(err, found) { cb(); assert.ifError(err); assert.equal(found.get('meta.visitors'), 9898); @@ -1926,10 +1927,11 @@ describe('model: querying:', function() { it('with previously existing null values in the db', function(done) { const post = new BlogPostB(); - post.collection.insertOne({ meta: { visitors: 9898, a: null } }, {}, function(err, b) { + const doc = { meta: { visitors: 9898, a: null } }; + post.collection.insertOne(doc, {}, function(err) { assert.ifError(err); - BlogPostB.findOne({ _id: b.ops[0]._id }, function(err, found) { + BlogPostB.findOne({ _id: doc._id }, function(err, found) { assert.ifError(err); assert.equal(found.get('meta.visitors').valueOf(), 9898); done(); @@ -1940,10 +1942,11 @@ describe('model: querying:', function() { it('with unused values in the db', function(done) { const post = new BlogPostB(); - post.collection.insertOne({ meta: { visitors: 9898, color: 'blue' } }, {}, function(err, b) { + const doc = { meta: { visitors: 9898, color: 'blue' } }; + post.collection.insertOne(doc, {}, function(err) { assert.ifError(err); - BlogPostB.findOne({ _id: b.ops[0]._id }, function(err, found) { + BlogPostB.findOne({ _id: doc._id }, function(err, found) { assert.ifError(err); assert.equal(found.get('meta.visitors').valueOf(), 9898); found.save(function(err) { diff --git a/test/model.test.js b/test/model.test.js index edca80c94dc..436abb75d7f 100644 --- a/test/model.test.js +++ b/test/model.test.js @@ -1052,9 +1052,10 @@ describe('Model', function() { a: String })); - TestP.collection.insertOne({ a: null, previous: null }, {}, function(err, f) { + const doc = { a: null, previous: null }; + TestP.collection.insertOne(doc, {}, function(err) { assert.ifError(err); - TestP.findOne({ _id: f.ops[0]._id }, function(err, found) { + TestP.findOne({ _id: doc._id }, function(err, found) { assert.ifError(err); assert.equal(found.isNew, false); assert.strictEqual(found.get('previous'), null); @@ -3197,8 +3198,8 @@ describe('Model', function() { const query = BlogPost.update({ title: 'interoperable update as promise' }, { title: 'interoperable update as promise delta' }); query.exec(function(err, res) { assert.ifError(err); - assert.equal(res.n, 1); - assert.equal(res.nModified, 1); + assert.equal(res.matchedCount, 1); + assert.equal(res.modifiedCount, 1); BlogPost.count({ title: 'interoperable update as promise delta' }, function(err, count) { assert.ifError(err); assert.equal(count, 1); @@ -6819,11 +6820,12 @@ describe('Model', function() { const User = db.model('User', userSchema); return co(function*() { - const users = yield User.collection.insertMany([ + const users = [ { notInSchema: 1 }, { notInSchema: 2 }, { notInSchema: 3 } - ]).then(res => res.ops); + ]; + yield User.collection.insertMany(users); // Act yield User.bulkWrite([ diff --git a/test/query.test.js b/test/query.test.js index 8ebc8535f7a..2f46dac4f1d 100644 --- a/test/query.test.js +++ b/test/query.test.js @@ -1713,7 +1713,7 @@ describe('Query', function() { _id: { select: false, type: Schema.Types.ObjectId, - default: mongoose.Types.ObjectId + default: () => new mongoose.Types.ObjectId() }, username: String }); @@ -2849,7 +2849,7 @@ describe('Query', function() { // Shouldn't throw const res = yield Model.deleteMany({ name: 'Test' }).orFail(new Error('Oops')); - assert.equal(res.n, 1); + assert.equal(res.deletedCount, 1); }); }); @@ -2867,7 +2867,7 @@ describe('Query', function() { // Shouldn't throw const res = yield Model.deleteOne({ name: 'Test' }).orFail(new Error('Oops')); - assert.equal(res.n, 1); + assert.equal(res.deletedCount, 1); }); }); @@ -2885,7 +2885,7 @@ describe('Query', function() { // Shouldn't throw const res = yield Model.remove({ name: 'Test' }).orFail(new Error('Oops')); - assert.equal(res.n, 1); + assert.equal(res.deletedCount, 1); }); }); @@ -2904,7 +2904,7 @@ describe('Query', function() { // Shouldn't throw const res = yield Model.update({}, { name: 'Test2' }).orFail(new Error('Oops')); - assert.equal(res.n, 1); + assert.equal(res.modifiedCount, 1); }); }); @@ -2923,7 +2923,7 @@ describe('Query', function() { // Shouldn't throw const res = yield Model.updateMany({}, { name: 'Test2' }).orFail(new Error('Oops')); - assert.equal(res.n, 1); + assert.equal(res.modifiedCount, 1); }); }); @@ -2942,7 +2942,7 @@ describe('Query', function() { // Shouldn't throw const res = yield Model.updateOne({}, { name: 'Test2' }).orFail(new Error('Oops')); - assert.equal(res.n, 1); + assert.equal(res.modifiedCount, 1); }); }); From 0bd31aaccbd8121c8fb6bf92e8fbff0bdfd69f8d Mon Sep 17 00:00:00 2001 From: Valeri Karpov Date: Tue, 26 Jan 2021 17:43:21 -0500 Subject: [PATCH 5/8] fix: clean up some more test failures and TypeScript compat issues re: #9840 --- index.d.ts | 238 +++++++++++++++++++++++----- lib/document.js | 2 +- test/document.strict.test.js | 2 +- test/document.test.js | 8 +- test/model.populate.setting.test.js | 2 +- 5 files changed, 205 insertions(+), 47 deletions(-) diff --git a/index.d.ts b/index.d.ts index 482bb3e6e6d..b11c4ab881f 100644 --- a/index.d.ts +++ b/index.d.ts @@ -134,8 +134,8 @@ declare module 'mongoose' { * for benefits like causal consistency, [retryable writes](https://docs.mongodb.com/manual/core/retryable-writes/), * and [transactions](http://thecodebarbarian.com/a-node-js-perspective-on-mongodb-4-transactions.html). */ - export function startSession(options?: mongodb.SessionOptions): Promise; - export function startSession(options: mongodb.SessionOptions, cb: (err: any, session: mongodb.ClientSession) => void): void; + export function startSession(options?: mongodb.ClientSessionOptions): Promise; + export function startSession(options: mongodb.ClientSessionOptions, cb: (err: any, session: mongodb.ClientSession) => void): void; /** The Mongoose version */ export const version: string; @@ -173,7 +173,7 @@ declare module 'mongoose' { close(force?: boolean): Promise; /** Retrieves a collection, creating it if not cached. */ - collection(name: string, options?: mongodb.CollectionCreateOptions): Collection; + collection(name: string, options?: mongodb.CreateCollectionOptions): Collection; /** A hash of the collections associated with this connection */ collections: { [index: string]: Collection }; @@ -189,9 +189,9 @@ declare module 'mongoose' { * with specified options. Used to create [capped collections](https://docs.mongodb.com/manual/core/capped-collections/) * and [views](https://docs.mongodb.com/manual/core/views/) from mongoose. */ - createCollection(name: string, options?: mongodb.CollectionCreateOptions): Promise>; - createCollection(name: string, cb: (err: CallbackError, collection: mongodb.Collection) => void): void; - createCollection(name: string, options: mongodb.CollectionCreateOptions, cb?: (err: CallbackError, collection: mongodb.Collection) => void): Promise>; + createCollection(name: string, options?: mongodb.CreateCollectionOptions): Promise; + createCollection(name: string, cb: (err: CallbackError, collection: mongodb.Collection) => void): void; + createCollection(name: string, options: mongodb.CreateCollectionOptions, cb?: (err: CallbackError, collection: mongodb.Collection) => void): Promise; /** * Removes the model named `name` from this connection, if it exists. You can @@ -302,8 +302,8 @@ declare module 'mongoose' { * for benefits like causal consistency, [retryable writes](https://docs.mongodb.com/manual/core/retryable-writes/), * and [transactions](http://thecodebarbarian.com/a-node-js-perspective-on-mongodb-4-transactions.html). */ - startSession(options?: mongodb.SessionOptions): Promise; - startSession(options: mongodb.SessionOptions, cb: (err: any, session: mongodb.ClientSession) => void): void; + startSession(options?: mongodb.ClientSessionOptions): Promise; + startSession(options: mongodb.ClientSessionOptions, cb: (err: any, session: mongodb.ClientSession) => void): void; /** * _Requires MongoDB >= 3.6.0._ Executes the wrapped async function @@ -615,8 +615,8 @@ declare module 'mongoose' { * if you use `create()`) because with `bulkWrite()` there is only one round * trip to MongoDB. */ - bulkWrite(writes: Array, options?: mongodb.CollectionBulkWriteOptions): Promise; - bulkWrite(writes: Array, options?: mongodb.CollectionBulkWriteOptions, cb?: (err: any, res: mongodb.BulkWriteOpResultObject) => void): void; + bulkWrite(writes: Array, options?: mongodb.BulkWriteOptions): Promise; + bulkWrite(writes: Array, options?: mongodb.BulkWriteOptions, cb?: (err: any, res: mongodb.BulkWriteResult) => void): void; /** Collection the model uses. */ collection: Collection; @@ -641,8 +641,8 @@ declare module 'mongoose' { * mongoose will not create the collection for the model until any documents are * created. Use this method to create the collection explicitly. */ - createCollection(options?: mongodb.CollectionCreateOptions): Promise>; - createCollection(options: mongodb.CollectionCreateOptions | null, callback: (err: CallbackError, collection: mongodb.Collection) => void): void; + createCollection(options?: mongodb.CreateCollectionOptions): Promise; + createCollection(options: mongodb.CreateCollectionOptions | null, callback: (err: CallbackError, collection: mongodb.Collection) => void): void; /** * Similar to `ensureIndexes()`, except for it uses the [`createIndex`](http://mongodb.github.io/node-mongodb-native/2.2/api/Collection.html#createIndex) @@ -748,7 +748,7 @@ declare module 'mongoose' { * for benefits like causal consistency, [retryable writes](https://docs.mongodb.com/manual/core/retryable-writes/), * and [transactions](http://thecodebarbarian.com/a-node-js-perspective-on-mongodb-4-transactions.html). * */ - startSession(options?: mongodb.SessionOptions, cb?: (err: any, session: mongodb.ClientSession) => void): Promise; + startSession(options?: mongodb.ClientSessionOptions, cb?: (err: any, session: mongodb.ClientSession) => void): Promise; /** Casts and validates the given object against this model's schema, passing the given `context` to custom validators. */ validate(callback?: (err: any) => void): Promise; @@ -795,7 +795,7 @@ declare module 'mongoose' { findByIdAndRemove(id?: mongodb.ObjectId | any, options?: QueryOptions | null, callback?: (err: any, doc: T | null, res: any) => void): Query; /** Creates a `findOneAndUpdate` query, filtering by the given `_id`. */ - findByIdAndUpdate(id: mongodb.ObjectId | any, update: UpdateQuery, options: QueryOptions & { rawResult: true }, callback?: (err: any, doc: mongodb.FindAndModifyWriteOpResultObject, res: any) => void): Query, T>; + findByIdAndUpdate(id: mongodb.ObjectId | any, update: UpdateQuery, options: QueryOptions & { rawResult: true }, callback?: (err: any, doc: any, res: any) => void): Query; findByIdAndUpdate(id: mongodb.ObjectId | any, update: UpdateQuery, options: QueryOptions & { upsert: true } & ReturnsNewDoc, callback?: (err: any, doc: T, res: any) => void): Query; findByIdAndUpdate(id?: mongodb.ObjectId | any, update?: UpdateQuery, options?: QueryOptions | null, callback?: (err: any, doc: T | null, res: any) => void): Query; @@ -810,7 +810,7 @@ declare module 'mongoose' { findOneAndReplace(filter?: FilterQuery, replacement?: DocumentDefinition, options?: QueryOptions | null, callback?: (err: any, doc: T | null, res: any) => void): Query; /** Creates a `findOneAndUpdate` query: atomically find the first document that matches `filter` and apply `update`. */ - findOneAndUpdate(filter: FilterQuery, update: UpdateQuery, options: QueryOptions & { rawResult: true }, callback?: (err: any, doc: mongodb.FindAndModifyWriteOpResultObject, res: any) => void): Query, T>; + findOneAndUpdate(filter: FilterQuery, update: UpdateQuery, options: QueryOptions & { rawResult: true }, callback?: (err: any, doc: any, res: any) => void): Query; findOneAndUpdate(filter: FilterQuery, update: UpdateQuery, options: QueryOptions & { upsert: true } & ReturnsNewDoc, callback?: (err: any, doc: T, res: any) => void): Query; findOneAndUpdate(filter?: FilterQuery, update?: UpdateQuery, options?: QueryOptions | null, callback?: (err: any, doc: T | null, res: any) => void): Query; @@ -849,7 +849,7 @@ declare module 'mongoose' { interface QueryOptions { arrayFilters?: { [key: string]: any }[]; batchSize?: number; - collation?: mongodb.CollationDocument; + collation?: mongodb.CollationOptions; comment?: any; context?: string; explain?: any; @@ -938,7 +938,7 @@ declare module 'mongoose' { populate?: string | string[] | PopulateOptions | PopulateOptions[]; } - interface InsertManyResult extends mongodb.InsertWriteOpResult { + interface InsertManyResult extends mongodb.InsertManyResult { mongoose?: { validationErrors?: Array } } @@ -1740,21 +1740,8 @@ declare module 'mongoose' { toObject(options?: ToObjectOptions & { flattenMaps?: boolean }): any; } - const ObjectId: ObjectIdConstructor; - - class _ObjectId extends mongodb.ObjectID { - _id?: ObjectId; - } - - // Expose static methods of `mongodb.ObjectID` and allow calling without `new` - type ObjectIdConstructor = typeof _ObjectId & { - (val?: string | number): ObjectId; - }; - - // var objectId: mongoose.Types.ObjectId should reference mongodb.ObjectID not - // the ObjectIdConstructor, so we add the interface below // eslint-disable-next-line @typescript-eslint/no-empty-interface - interface ObjectId extends mongodb.ObjectID { } + interface ObjectId extends mongodb.ObjectId { } class Subdocument extends Document { $isSingleNested: true; @@ -1891,7 +1878,7 @@ declare module 'mongoose' { findOneAndRemove(filter?: FilterQuery, options?: QueryOptions | null, callback?: (err: any, doc: DocType | null, res: any) => void): Query; /** Creates a `findOneAndUpdate` query: atomically find the first document that matches `filter` and apply `update`. */ - findOneAndUpdate(filter: FilterQuery, update: UpdateQuery, options: QueryOptions & { rawResult: true }, callback?: (err: any, doc: mongodb.FindAndModifyWriteOpResultObject, res: any) => void): Query, DocType>; + findOneAndUpdate(filter: FilterQuery, update: UpdateQuery, options: QueryOptions & { rawResult: true }, callback?: (err: any, doc: any, res: any) => void): Query; findOneAndUpdate(filter: FilterQuery, update: UpdateQuery, options: QueryOptions & { upsert: true } & ReturnsNewDoc, callback?: (err: any, doc: DocType, res: any) => void): Query; findOneAndUpdate(filter?: FilterQuery, update?: UpdateQuery, options?: QueryOptions | null, callback?: (err: any, doc: DocType | null, res: any) => void): Query; @@ -1899,7 +1886,7 @@ declare module 'mongoose' { findByIdAndDelete(id?: mongodb.ObjectId | any, options?: QueryOptions | null, callback?: (err: any, doc: DocType | null, res: any) => void): Query; /** Creates a `findOneAndUpdate` query, filtering by the given `_id`. */ - findByIdAndUpdate(id: mongodb.ObjectId | any, update: UpdateQuery, options: QueryOptions & { rawResult: true }, callback?: (err: any, doc: mongodb.FindAndModifyWriteOpResultObject, res: any) => void): Query, DocType>; + findByIdAndUpdate(id: mongodb.ObjectId | any, update: UpdateQuery, options: QueryOptions & { rawResult: true }, callback?: (err: any, doc: any, res: any) => void): Query; findByIdAndUpdate(id: mongodb.ObjectId | any, update: UpdateQuery, options: QueryOptions & { upsert: true } & ReturnsNewDoc, callback?: (err: any, doc: DocType, res: any) => void): Query; findByIdAndUpdate(id?: mongodb.ObjectId | any, update?: UpdateQuery, options?: QueryOptions | null, callback?: (err: any, doc: DocType | null, res: any) => void): Query; @@ -2047,7 +2034,7 @@ declare module 'mongoose' { * deprecated, you should use [`deleteOne()`](#query_Query-deleteOne) * or [`deleteMany()`](#query_Query-deleteMany) instead. */ - remove(filter?: FilterQuery, callback?: (err: CallbackError, res: mongodb.WriteOpResult['result']) => void): Query; + remove(filter?: FilterQuery, callback?: (err: CallbackError, res: mongodb.UpdateResult) => void): Query; /** * Declare and/or execute this query as a replaceOne() operation. Same as @@ -2159,6 +2146,73 @@ declare module 'mongoose' { wtimeout(ms: number): this; } + export type QuerySelector = { + // Comparison + $eq?: T; + $gt?: T; + $gte?: T; + $in?: T[]; + $lt?: T; + $lte?: T; + $ne?: T; + $nin?: T[]; + // Logical + $not?: T extends string ? QuerySelector | RegExp : QuerySelector; + // Element + /** + * When `true`, `$exists` matches the documents that contain the field, + * including documents where the field value is null. + */ + $exists?: boolean; + $type?: string | number; + // Evaluation + $expr?: any; + $jsonSchema?: any; + $mod?: T extends number ? [number, number] : never; + $regex?: T extends string ? RegExp | string : never; + $options?: T extends string ? string : never; + // Geospatial + // TODO: define better types for geo queries + $geoIntersects?: { $geometry: object }; + $geoWithin?: object; + $near?: object; + $nearSphere?: object; + $maxDistance?: number; + // Array + // TODO: define better types for $all and $elemMatch + $all?: T extends ReadonlyArray ? any[] : never; + $elemMatch?: T extends ReadonlyArray ? object : never; + $size?: T extends ReadonlyArray ? number : never; + // Bitwise + $bitsAllClear?: number | mongodb.Binary | number[]; + $bitsAllSet?: number | mongodb.Binary | number[]; + $bitsAnyClear?: number | mongodb.Binary | number[]; + $bitsAnySet?: number | mongodb.Binary | number[]; + }; + + export type RootQuerySelector = { + /** @see https://docs.mongodb.com/manual/reference/operator/query/and/#op._S_and */ + $and?: Array>; + /** @see https://docs.mongodb.com/manual/reference/operator/query/nor/#op._S_nor */ + $nor?: Array>; + /** @see https://docs.mongodb.com/manual/reference/operator/query/or/#op._S_or */ + $or?: Array>; + /** @see https://docs.mongodb.com/manual/reference/operator/query/text */ + $text?: { + $search: string; + $language?: string; + $caseSensitive?: boolean; + $diacriticSensitive?: boolean; + }; + /** @see https://docs.mongodb.com/manual/reference/operator/query/where/#op._S_where */ + $where?: string | Function; + /** @see https://docs.mongodb.com/manual/reference/operator/query/comment/#op._S_comment */ + $comment?: string; + // we could not find a proper TypeScript generic to support nested queries e.g. 'user.friends.name' + // this will mark all unrecognized properties as any (including nested queries) + [key: string]: any; + }; + type DotAndArrayNotation = { readonly [key: string]: AssignableType; }; @@ -2169,20 +2223,124 @@ declare module 'mongoose' { type MatchKeysAndValues = ReadonlyPartial & DotAndArrayNotation; + type Condition = T | QuerySelector; + type _FilterQuery = { [P in keyof T]?: P extends '_id' ? [Extract] extends [never] - ? mongodb.Condition - : mongodb.Condition + ? Condition + : Condition : [Extract] extends [never] - ? mongodb.Condition - : mongodb.Condition; + ? Condition + : Condition; } & - mongodb.RootQuerySelector; + RootQuerySelector; export type FilterQuery = _FilterQuery>; - export type UpdateQuery = mongodb.UpdateQuery> & MatchKeysAndValues>; + type Unpacked = Type extends ReadonlyArray ? Element : Type; + + type AddToSetOperators = { + $each: Type; + }; + + type SortValues = -1 | 1 | 'asc' | 'desc'; + + type ArrayOperator = { + $each: Type; + $slice?: number; + $position?: number; + $sort?: SortValues | Record; + }; + + type SetFields = ({ + readonly [key in KeysOfAType | undefined>]?: + | Unpacked + | AddToSetOperators[]>; + } & + NotAcceptedFields | undefined>) & { + readonly [key: string]: AddToSetOperators | any; + }; + + type PushOperator = ({ + readonly [key in KeysOfAType>]?: + | Unpacked + | ArrayOperator[]>; + } & + NotAcceptedFields>) & { + readonly [key: string]: ArrayOperator | any; + }; + + type ObjectQuerySelector = T extends object ? { [key in keyof T]?: QuerySelector } : QuerySelector; + + type PullOperator = ({ + readonly [key in KeysOfAType>]?: + | Partial> + | ObjectQuerySelector>; + } & + NotAcceptedFields>) & { + readonly [key: string]: QuerySelector | any; + }; + + type PullAllOperator = ({ + readonly [key in KeysOfAType>]?: TSchema[key]; + } & + NotAcceptedFields>) & { + readonly [key: string]: any[]; + }; + + type KeysOfAType = { + [key in keyof TSchema]: NonNullable extends Type ? key : never; + }[keyof TSchema]; + type KeysOfOtherType = { + [key in keyof TSchema]: NonNullable extends Type ? never : key; + }[keyof TSchema]; + + type AcceptedFields = { + readonly [key in KeysOfAType]?: AssignableType; + }; + + /** It avoid uses fields of non Type */ + type NotAcceptedFields = { + readonly [key in KeysOfOtherType]?: never; + }; + + type OnlyFieldsOfType = AcceptedFields< + TSchema, + FieldType, + AssignableType + > & + NotAcceptedFields & + DotAndArrayNotation; + + type NumericTypes = number | Decimal128 | mongodb.Double | mongodb.Int32 | mongodb.Long; + + type _UpdateQuery = { + /** @see https://docs.mongodb.com/manual/reference/operator/update-field/ */ + $currentDate?: OnlyFieldsOfType; + $inc?: OnlyFieldsOfType; + $min?: MatchKeysAndValues; + $max?: MatchKeysAndValues; + $mul?: OnlyFieldsOfType; + $rename?: { [key: string]: string }; + $set?: MatchKeysAndValues; + $setOnInsert?: MatchKeysAndValues; + $unset?: OnlyFieldsOfType; + + /** @see https://docs.mongodb.com/manual/reference/operator/update-array/ */ + $addToSet?: SetFields; + $pop?: OnlyFieldsOfType, 1 | -1>; + $pull?: PullOperator; + $push?: PushOperator; + $pullAll?: PullAllOperator; + + /** @see https://docs.mongodb.com/manual/reference/operator/update-bitwise/ */ + $bit?: { + [key: string]: { [key in 'and' | 'or' | 'xor']?: number }; + }; + }; + + export type UpdateQuery = _UpdateQuery> & MatchKeysAndValues>; type _AllowStringsForIds = { [K in keyof T]: [Extract] extends [never] ? T[K] : T[K] | string; diff --git a/lib/document.js b/lib/document.js index 9448a8d5a3b..ce3c82c60d6 100644 --- a/lib/document.js +++ b/lib/document.js @@ -3466,7 +3466,7 @@ function minimize(obj) { key = keys[i]; val = obj[key]; - if (utils.isObject(val) && !Buffer.isBuffer(val)) { + if (utils.isPOJO(val)) { obj[key] = minimize(val); } diff --git a/test/document.strict.test.js b/test/document.strict.test.js index f6401010227..2aa6e235293 100644 --- a/test/document.strict.test.js +++ b/test/document.strict.test.js @@ -425,7 +425,7 @@ describe('document: strict mode:', function() { const Foo = db.model('Test', FooSchema); assert.doesNotThrow(function() { - new Foo({ name: mongoose.Types.ObjectId(), father: { name: { full: 'bacon' } } }); + new Foo({ name: new mongoose.Types.ObjectId(), father: { name: { full: 'bacon' } } }); }); }); diff --git a/test/document.test.js b/test/document.test.js index 1b6e4c8ab62..01f0782f5a4 100644 --- a/test/document.test.js +++ b/test/document.test.js @@ -5949,7 +5949,7 @@ describe('document', function() { const savedBlogPost = yield BlogPost.collection. findOne({ _id: blogPost._id }); assert.equal(savedBlogPost.comments.length, 1); - assert.equal(savedBlogPost.comments[0].constructor.name, 'ObjectID'); + assert.equal(savedBlogPost.comments[0].constructor.name, 'ObjectId'); assert.equal(savedBlogPost.comments[0].toString(), blogPost.comments[0]._id.toString()); }); @@ -6725,9 +6725,9 @@ describe('document', function() { return co(function*() { const doc2 = new Model(); - doc2.field = mongoose.Types.ObjectId(); + doc2.field = new mongoose.Types.ObjectId(); doc2.inner.push({ - innerField: mongoose.Types.ObjectId() + innerField: new mongoose.Types.ObjectId() }); doc2.inner[0].innerField = ''; @@ -9849,7 +9849,7 @@ describe('document', function() { it('does not pass doc to ObjectId or Date.now (gh-9633) (gh-9636)', function() { const userSchema = new Schema({ - parentId: { type: Schema.ObjectId, ref: 'User', default: mongoose.Types.ObjectId }, + parentId: { type: Schema.ObjectId, ref: 'User', default: () => new mongoose.Types.ObjectId() }, createdAt: { type: Date, default: Date.now } }); diff --git a/test/model.populate.setting.test.js b/test/model.populate.setting.test.js index 514dc5681a4..7872e1523a2 100644 --- a/test/model.populate.setting.test.js +++ b/test/model.populate.setting.test.js @@ -34,7 +34,7 @@ describe('model: populate:', function() { const construct = {}; construct.String = random; - construct.ObjectId = DocObjectId; + construct.ObjectId = () => new DocObjectId(); construct.Number = random; construct.Buffer = function() { return Buffer.from(random()); From 26f25c1505b722f71aa437dbf6de13d02b4ba01b Mon Sep 17 00:00:00 2001 From: Valeri Karpov Date: Sat, 30 Jan 2021 16:36:12 -0500 Subject: [PATCH 6/8] fix: clean up remaining test failures with MongoDB driver 4.0 beta --- lib/connection.js | 15 ++- lib/index.js | 3 +- lib/model.js | 91 +--------------- test/connection.test.js | 2 +- test/docs/validation.test.js | 3 +- test/document.test.js | 4 +- test/es-next/lean.test.es6.js | 8 +- test/index.test.js | 3 +- test/model.geosearch.test.js | 177 ------------------------------- test/model.populate.test.js | 4 +- test/model.query.casting.test.js | 4 +- test/query.test.js | 51 --------- test/schema.onthefly.test.js | 2 +- 13 files changed, 33 insertions(+), 334 deletions(-) delete mode 100644 test/model.geosearch.test.js diff --git a/lib/connection.js b/lib/connection.js index d35acdc4c19..baa70e554ee 100644 --- a/lib/connection.js +++ b/lib/connection.js @@ -652,7 +652,8 @@ Connection.prototype.onOpen = function() { * @param {String} [options.dbName] The name of the database we want to use. If not provided, use database name from connection string. * @param {String} [options.user] username for authentication, equivalent to `options.auth.user`. Maintained for backwards compatibility. * @param {String} [options.pass] password for authentication, equivalent to `options.auth.password`. Maintained for backwards compatibility. - * @param {Number} [options.poolSize=5] The maximum number of sockets the MongoDB driver will keep open for this connection. By default, `poolSize` is 5. Keep in mind that, as of MongoDB 3.4, MongoDB only allows one operation per socket at a time, so you may want to increase this if you find you have a few slow queries that are blocking faster queries from proceeding. See [Slow Trains in MongoDB and Node.js](http://thecodebarbarian.com/slow-trains-in-mongodb-and-nodejs). + * @param {Number} [options.maxPoolSize=5] The maximum number of sockets the MongoDB driver will keep open for this connection. Keep in mind that MongoDB only allows one operation per socket at a time, so you may want to increase this if you find you have a few slow queries that are blocking faster queries from proceeding. See [Slow Trains in MongoDB and Node.js](http://thecodebarbarian.com/slow-trains-in-mongodb-and-nodejs). + * @param {Number} [options.minPoolSize=1] The minimum number of sockets the MongoDB driver will keep open for this connection. Keep in mind that MongoDB only allows one operation per socket at a time, so you may want to increase this if you find you have a few slow queries that are blocking faster queries from proceeding. See [Slow Trains in MongoDB and Node.js](http://thecodebarbarian.com/slow-trains-in-mongodb-and-nodejs). * @param {Number} [options.serverSelectionTimeoutMS] If `useUnifiedTopology = true`, the MongoDB driver will try to find a server to send any given operation to, and keep retrying for `serverSelectionTimeoutMS` milliseconds before erroring out. If not set, the MongoDB driver defaults to using `30000` (30 seconds). * @param {Number} [options.heartbeatFrequencyMS] If `useUnifiedTopology = true`, the MongoDB driver sends a heartbeat every `heartbeatFrequencyMS` to check on the status of the connection. A heartbeat is subject to `serverSelectionTimeoutMS`, so the MongoDB driver will retry failed heartbeats for up to 30 seconds by default. Mongoose only emits a `'disconnected'` event after a heartbeat has failed, so you may want to decrease this setting to reduce the time between when your server goes down and when Mongoose emits `'disconnected'`. We recommend you do **not** set this setting below 1000, too many heartbeats can lead to performance degradation. * @param {Boolean} [options.autoIndex=true] Mongoose-specific option. Set to false to disable automatic index creation for all models associated with this connection. @@ -776,7 +777,13 @@ Connection.prototype.openUri = function(uri, options, callback) { } const promise = new Promise((resolve, reject) => { - const client = new mongodb.MongoClient(uri, options); + let client; + try { + client = new mongodb.MongoClient(uri, options); + } catch (error) { + _this.readyState = STATES.disconnected; + return reject(error); + } _this.client = client; client.connect((error) => { if (error) { @@ -991,6 +998,10 @@ Connection.prototype.onClose = function(force) { } this.emit('close', force); + + for (const db of this.otherDbs) { + db.close(force); + } }; /** diff --git a/lib/index.js b/lib/index.js index 12dd03565e1..934a4e58568 100644 --- a/lib/index.js +++ b/lib/index.js @@ -256,7 +256,8 @@ Mongoose.prototype.get = Mongoose.prototype.set; * @param {Number} [options.reconnectTries=30] If you're connected to a single server or mongos proxy (as opposed to a replica set), the MongoDB driver will try to reconnect every `reconnectInterval` milliseconds for `reconnectTries` times, and give up afterward. When the driver gives up, the mongoose connection emits a `reconnectFailed` event. This option does nothing for replica set connections. * @param {Number} [options.reconnectInterval=1000] See `reconnectTries` option above. * @param {Class} [options.promiseLibrary] Sets the [underlying driver's promise library](http://mongodb.github.io/node-mongodb-native/3.1/api/MongoClient.html). - * @param {Number} [options.poolSize=5] The maximum number of sockets the MongoDB driver will keep open for this connection. By default, `poolSize` is 5. Keep in mind that, as of MongoDB 3.4, MongoDB only allows one operation per socket at a time, so you may want to increase this if you find you have a few slow queries that are blocking faster queries from proceeding. See [Slow Trains in MongoDB and Node.js](http://thecodebarbarian.com/slow-trains-in-mongodb-and-nodejs). + * @param {Number} [options.maxPoolSize=5] The maximum number of sockets the MongoDB driver will keep open for this connection. Keep in mind that MongoDB only allows one operation per socket at a time, so you may want to increase this if you find you have a few slow queries that are blocking faster queries from proceeding. See [Slow Trains in MongoDB and Node.js](http://thecodebarbarian.com/slow-trains-in-mongodb-and-nodejs). + * @param {Number} [options.minPoolSize=1] The minimum number of sockets the MongoDB driver will keep open for this connection. Keep in mind that MongoDB only allows one operation per socket at a time, so you may want to increase this if you find you have a few slow queries that are blocking faster queries from proceeding. See [Slow Trains in MongoDB and Node.js](http://thecodebarbarian.com/slow-trains-in-mongodb-and-nodejs). * @param {Number} [options.connectTimeoutMS=30000] How long the MongoDB driver will wait before killing a socket due to inactivity _during initial connection_. Defaults to 30000. This option is passed transparently to [Node.js' `socket#setTimeout()` function](https://nodejs.org/api/net.html#net_socket_settimeout_timeout_callback). * @param {Number} [options.socketTimeoutMS=30000] How long the MongoDB driver will wait before killing a socket due to inactivity _after initial connection_. A socket may be inactive because of either no activity or a long-running operation. This is set to `30000` by default, you should set this to 2-3x your longest running operation if you expect some of your database operations to run longer than 20 seconds. This option is passed to [Node.js `socket#setTimeout()` function](https://nodejs.org/api/net.html#net_socket_settimeout_timeout_callback) after the MongoDB driver successfully completes. * @param {Number} [options.family=0] Passed transparently to [Node.js' `dns.lookup()`](https://nodejs.org/api/dns.html#dns_dns_lookup_hostname_options_callback) function. May be either `0`, `4`, or `6`. `4` means use IPv4 only, `6` means use IPv6 only, `0` means try both. diff --git a/lib/model.js b/lib/model.js index 562809b22af..58545a358d0 100644 --- a/lib/model.js +++ b/lib/model.js @@ -360,8 +360,8 @@ Model.prototype.$__save = function(options, callback) { if (result) { if (Array.isArray(result)) { numAffected = result.length; - } else if (result.modifiedCount != null) { - numAffected = result.modifiedCount; + } else if (result.matchedCount != null) { + numAffected = result.matchedCount; } else { numAffected = result; } @@ -4052,93 +4052,6 @@ Model.validate = function validate(obj, pathsToValidate, context, callback) { }); }; -/** - * Implements `$geoSearch` functionality for Mongoose - * - * This function does not trigger any middleware - * - * ####Example: - * - * const options = { near: [10, 10], maxDistance: 5 }; - * Locations.geoSearch({ type : "house" }, options, function(err, res) { - * console.log(res); - * }); - * - * ####Options: - * - `near` {Array} x,y point to search for - * - `maxDistance` {Number} the maximum distance from the point near that a result can be - * - `limit` {Number} The maximum number of results to return - * - `lean` {Object|Boolean} return the raw object instead of the Mongoose Model - * - * @param {Object} conditions an object that specifies the match condition (required) - * @param {Object} options for the geoSearch, some (near, maxDistance) are required - * @param {Object|Boolean} [options.lean] if truthy, mongoose will return the document as a plain JavaScript object rather than a mongoose document. See [`Query.lean()`](/docs/api.html#query_Query-lean) and the [Mongoose lean tutorial](/docs/tutorials/lean.html). - * @param {Function} [callback] optional callback - * @return {Promise} - * @see http://docs.mongodb.org/manual/reference/command/geoSearch/ - * @see http://docs.mongodb.org/manual/core/geohaystack/ - * @api public - */ - -Model.geoSearch = function(conditions, options, callback) { - _checkContext(this, 'geoSearch'); - - if (typeof options === 'function') { - callback = options; - options = {}; - } - - callback = this.$handleCallbackError(callback); - - return this.db.base._promiseOrCallback(callback, cb => { - cb = this.$wrapCallback(cb); - let error; - if (conditions === undefined || !utils.isObject(conditions)) { - error = new MongooseError('Must pass conditions to geoSearch'); - } else if (!options.near) { - error = new MongooseError('Must specify the near option in geoSearch'); - } else if (!Array.isArray(options.near)) { - error = new MongooseError('near option must be an array [x, y]'); - } - - if (error) { - return cb(error); - } - - // send the conditions in the options object - options.search = conditions; - - this.collection.geoHaystackSearch(options.near[0], options.near[1], options, (err, res) => { - if (err) { - return cb(err); - } - - let count = res.results.length; - if (options.lean || count === 0) { - return cb(null, res.results); - } - - const errSeen = false; - - function init(err) { - if (err && !errSeen) { - return cb(err); - } - - if (!--count && !errSeen) { - cb(null, res.results); - } - } - - for (let i = 0; i < res.results.length; ++i) { - const temp = res.results[i]; - res.results[i] = new this(); - res.results[i].init(temp, {}, init); - } - }); - }, this.events); -}; - /** * Populates document references. * diff --git a/test/connection.test.js b/test/connection.test.js index eddf3084653..4237d5f3ec6 100644 --- a/test/connection.test.js +++ b/test/connection.test.js @@ -259,6 +259,7 @@ describe('connections:', function() { yield db.openUri('fail connection'); } catch (err) { assert.ok(err); + assert.equal(err.name, 'MongoParseError'); threw = true; } @@ -511,7 +512,6 @@ describe('connections:', function() { const db2 = db.useDb('mongoose2'); assert.equal('mongoose2', db2.name); - assert.equal('mongoose1', db.name); assert.equal(db2.port, db.port); assert.equal(db2.replica, db.replica); diff --git a/test/docs/validation.test.js b/test/docs/validation.test.js index ff97d58fe67..91a8b7a40e5 100644 --- a/test/docs/validation.test.js +++ b/test/docs/validation.test.js @@ -10,7 +10,8 @@ describe('validation docs', function() { before(function() { db = mongoose.createConnection('mongodb://localhost:27017/mongoose_test', { - poolSize: 1 + minPoolSize: 1, + maxPoolSize: 1 }); }); diff --git a/test/document.test.js b/test/document.test.js index 01f0782f5a4..2f4e3daaf3f 100644 --- a/test/document.test.js +++ b/test/document.test.js @@ -2073,7 +2073,7 @@ describe('document', function() { } catch (err) { assert.equal(err instanceof DocumentNotFoundError, true); - assert.equal(err.message, `No document found for query "{ _id: ${person._id} }" on model "Person"`); + assert.equal(err.message, `No document found for query "{ _id: ObjectId("${person._id}") }" on model "Person"`); threw = true; } @@ -2098,7 +2098,7 @@ describe('document', function() { } catch (err) { assert.equal(err instanceof DocumentNotFoundError, true); - assert.equal(err.message, `No document found for query "{ _id: ${person._id} }" on model "Person"`); + assert.equal(err.message, `No document found for query "{ _id: ObjectId("${person._id}") }" on model "Person"`); threw = true; } diff --git a/test/es-next/lean.test.es6.js b/test/es-next/lean.test.es6.js index 2c09e166ecc..34e962d3e6f 100644 --- a/test/es-next/lean.test.es6.js +++ b/test/es-next/lean.test.es6.js @@ -34,16 +34,16 @@ describe('Lean Tutorial', function() { // To enable the `lean` option for a query, use the `lean()` function. const leanDoc = await MyModel.findOne().lean(); - sizeof(normalDoc); // >= 1000 - sizeof(leanDoc); // 86, 10x smaller! + sizeof(normalDoc); // approximately 1000 + sizeof(leanDoc); // 36, more than 10x smaller! // In case you were wondering, the JSON form of a Mongoose doc is the same // as the POJO. This additional memory only affects how much memory your // Node.js process uses, not how much data is sent over the network. JSON.stringify(normalDoc).length === JSON.stringify(leanDoc.length); // true // acquit:ignore:start - assert.ok(sizeof(normalDoc) >= 1000); - assert.equal(sizeof(leanDoc), 86); + assert.ok(sizeof(normalDoc) >= 750 && sizeof(normalDoc) <= 1250, sizeof(normalDoc)); + assert.equal(sizeof(leanDoc), 36); assert.equal(JSON.stringify(normalDoc).length, JSON.stringify(leanDoc).length); // acquit:ignore:end }); diff --git a/test/index.test.js b/test/index.test.js index fdbc5dbaf62..234e19aa38c 100644 --- a/test/index.test.js +++ b/test/index.test.js @@ -751,7 +751,8 @@ describe('mongoose module:', function() { it('connect with url doesnt cause unhandled rejection (gh-6997)', function(done) { const m = new mongoose.Mongoose; - m.connect('mongodb://doesnotexist:27009/test', options, function(error) { + const _options = Object.assign({}, options, { serverSelectionTimeoutMS: 100 }); + m.connect('mongodb://doesnotexist:27009/test', _options, function(error) { assert.ok(error); done(); }); diff --git a/test/model.geosearch.test.js b/test/model.geosearch.test.js deleted file mode 100644 index 55e1e002713..00000000000 --- a/test/model.geosearch.test.js +++ /dev/null @@ -1,177 +0,0 @@ -'use strict'; - -const start = require('./common'); - -const assert = require('assert'); - -const mongoose = start.mongoose; -const Schema = mongoose.Schema; - -describe('model', function() { - let db, schema; - let Geo; - - before(function() { - schema = new Schema({ - pos: [Number], - complex: {}, - type: String - }); - schema.index({ pos: 'geoHaystack', type: 1 }, { bucketSize: 1 }); - db = start(); - - Geo = db.model('Test', schema); - }); - - after(function(done) { - db.close(done); - }); - - afterEach(() => Geo.deleteMany({})); - - describe('geoSearch', function() { - this.timeout(process.env.TRAVIS ? 8000 : 4500); - - it('works', function(done) { - assert.ok(Geo.geoSearch instanceof Function); - - Geo.init(function() { - const geos = []; - geos[0] = new Geo({ pos: [10, 10], type: 'place' }); - geos[1] = new Geo({ pos: [15, 5], type: 'place' }); - geos[2] = new Geo({ pos: [20, 15], type: 'house' }); - geos[3] = new Geo({ pos: [1, -1], type: 'house' }); - let count = geos.length; - - for (const geo of geos) { - geo.save(function(err) { - assert.ifError(err); - --count || next(); - }); - } - - function next() { - Geo.geoSearch({ type: 'place' }, { near: [9, 9], maxDistance: 5 }, function(err, results) { - assert.ifError(err); - assert.equal(results.length, 1); - - assert.equal(results[0].type, 'place'); - assert.equal(results[0].pos.length, 2); - assert.equal(results[0].pos[0], 10); - assert.equal(results[0].pos[1], 10); - assert.equal(results[0].id, geos[0].id); - assert.ok(results[0] instanceof Geo); - - Geo.geoSearch({ type: 'place' }, { near: [40, 40], maxDistance: 5 }, function(err, results) { - assert.ifError(err); - assert.equal(results.length, 0); - done(); - }); - }); - } - }); - }); - it('works with lean', function(done) { - assert.ok(Geo.geoSearch instanceof Function); - - Geo.init(function(err) { - assert.ifError(err); - - const geos = []; - geos[0] = new Geo({ pos: [10, 10], type: 'place' }); - geos[1] = new Geo({ pos: [15, 5], type: 'place' }); - geos[2] = new Geo({ pos: [20, 15], type: 'house' }); - geos[3] = new Geo({ pos: [1, -1], type: 'house' }); - let count = geos.length; - - for (const geo of geos) { - geo.save(function(err) { - assert.ifError(err); - --count || next(); - }); - } - - function next() { - Geo.geoSearch({ type: 'place' }, { near: [9, 9], maxDistance: 5, lean: true }, function(err, results) { - assert.ifError(err); - assert.equal(results.length, 1); - - assert.equal(results[0].type, 'place'); - assert.equal(results[0].pos.length, 2); - assert.equal(results[0].pos[0], 10); - assert.equal(results[0].pos[1], 10); - assert.equal(results[0]._id, geos[0].id); - assert.strictEqual(results[0].id, undefined); - assert.ok(!(results[0] instanceof Geo)); - done(); - }); - } - }); - }); - it('throws the correct error messages', function(done) { - assert.ok(Geo.geoSearch instanceof Function); - - Geo.init(function(err) { - assert.ifError(err); - - const g = new Geo({ pos: [10, 10], type: 'place' }); - g.save(function() { - Geo.geoSearch([], {}, function(e) { - assert.ok(e); - assert.equal(e.message, 'Must pass conditions to geoSearch'); - - Geo.geoSearch({ type: 'test' }, {}, function(e) { - assert.ok(e); - assert.equal(e.message, 'Must specify the near option in geoSearch'); - - Geo.geoSearch({ type: 'test' }, { near: 'hello' }, function(e) { - assert.ok(e); - assert.equal(e.message, 'near option must be an array [x, y]'); - - Geo.geoSearch({ type: 'test' }, { near: [1, 2] }, function(err) { - assert.ok(err); - assert.ok(/maxDistance needs a number/.test(err)); - done(); - }); - }); - }); - }); - }); - }); - }); - - it('returns a promise (gh-1614)', function(done) { - Geo.init(function() { - const prom = Geo.geoSearch({ type: 'place' }, { near: [9, 9], maxDistance: 5 }); - assert.ok(prom instanceof mongoose.Promise); - - prom.then(() => done(), err => done(err)); - }); - }); - - it('allows not passing a callback (gh-1614)', function(done) { - Geo.init(function(err) { - assert.ifError(err); - const g = new Geo({ pos: [10, 10], type: 'place' }); - g.save(function(err) { - assert.ifError(err); - - let promise; - assert.doesNotThrow(function() { - promise = Geo.geoSearch({ type: 'place' }, { near: [9, 9], maxDistance: 5 }); - }); - function validate(ret) { - assert.equal(ret.length, 1); - assert.equal(ret[0].pos[0], 10); - assert.equal(ret[0].pos[1], 10); - } - - function finish() { - done(); - } - promise.then(validate, assert.ifError).then(finish); - }); - }); - }); - }); -}); diff --git a/test/model.populate.test.js b/test/model.populate.test.js index 2b670220f05..7f1c3b399e4 100644 --- a/test/model.populate.test.js +++ b/test/model.populate.test.js @@ -3233,12 +3233,12 @@ describe('model: populate:', function() { const User = db.model('User', UserSchema); const user = { - _id: mongoose.Types.ObjectId(), + _id: new mongoose.Types.ObjectId(), name: 'Arnold' }; const post = { - _id: mongoose.Types.ObjectId(), + _id: new mongoose.Types.ObjectId(), comments: [ {}, { diff --git a/test/model.query.casting.test.js b/test/model.query.casting.test.js index 981dfb7249a..b1de963f164 100644 --- a/test/model.query.casting.test.js +++ b/test/model.query.casting.test.js @@ -818,7 +818,7 @@ describe('model query casting', function() { describe('$elemMatch', function() { it('should cast String to ObjectId in $elemMatch', function(done) { - const commentId = mongoose.Types.ObjectId(111); + const commentId = new mongoose.Types.ObjectId(111); const post = new BlogPostB({ comments: [{ _id: commentId }] }); const id = post._id.toString(); @@ -836,7 +836,7 @@ describe('model query casting', function() { }); it('should cast String to ObjectId in $elemMatch inside $not', function(done) { - const commentId = mongoose.Types.ObjectId(111); + const commentId = new mongoose.Types.ObjectId(111); const post = new BlogPostB({ comments: [{ _id: commentId }] }); const id = post._id.toString(); diff --git a/test/query.test.js b/test/query.test.js index 2f46dac4f1d..8f0ad74fd8a 100644 --- a/test/query.test.js +++ b/test/query.test.js @@ -1189,57 +1189,6 @@ describe('Query', function() { }); }, done); }); - - it('single option, default', function(done) { - const Test = db.model('Person', new Schema({ name: String })); - - Test.create([{ name: 'Eddard Stark' }, { name: 'Robb Stark' }], function(error) { - assert.ifError(error); - Test.deleteMany({ name: /Stark/ }).exec(function(error, res) { - assert.ifError(error); - assert.equal(res.n, 2); - Test.countDocuments({}, function(error, count) { - assert.ifError(error); - assert.equal(count, 0); - done(); - }); - }); - }); - }); - - it.skip('single option, false', function(done) { - const Test = db.model('Person', new Schema({ name: String })); - - Test.create([{ name: 'Eddard Stark' }, { name: 'Robb Stark' }], function(error) { - assert.ifError(error); - Test.remove({ name: /Stark/ }).setOptions({ single: false }).exec(function(error, res) { - assert.ifError(error); - assert.equal(res.n, 2); - Test.countDocuments({}, function(error, count) { - assert.ifError(error); - assert.equal(count, 0); - done(); - }); - }); - }); - }); - - it.skip('single option, true', function(done) { - const Test = db.model('Person', new Schema({ name: String })); - - Test.create([{ name: 'Eddard Stark' }, { name: 'Robb Stark' }], function(error) { - assert.ifError(error); - Test.remove({ name: /Stark/ }).setOptions({ single: true }).exec(function(error, res) { - assert.ifError(error); - assert.equal(res.n, 1); - Test.countDocuments({}, function(error, count) { - assert.ifError(error); - assert.equal(count, 1); - done(); - }); - }); - }); - }); }); describe('querying/updating with model instance containing embedded docs should work (#454)', function() { diff --git a/test/schema.onthefly.test.js b/test/schema.onthefly.test.js index 67411e6aa52..f88b2612b31 100644 --- a/test/schema.onthefly.test.js +++ b/test/schema.onthefly.test.js @@ -129,7 +129,7 @@ describe('schema.onthefly', function() { assert.equal(typeof d.get('title', Number), 'number'); d.title = '000000000000000000000001'; - assert.equal(d.get('title', ObjectId).constructor.name, 'ObjectID'); + assert.equal(d.get('title', ObjectId).constructor.name, 'ObjectId'); d.set('title', 1, Number); assert.equal(typeof d.get('title'), 'number'); From baf3938b928a42a5dedee6e939d5701d06014a28 Mon Sep 17 00:00:00 2001 From: Valeri Karpov Date: Tue, 2 Feb 2021 10:55:14 -0500 Subject: [PATCH 7/8] chore: clean up some inconsistencies with mongodbs new TS types --- index.d.ts | 11 +++++++---- test/typescript/objectid.ts | 2 +- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/index.d.ts b/index.d.ts index b11c4ab881f..9ebe8855cb0 100644 --- a/index.d.ts +++ b/index.d.ts @@ -1740,8 +1740,9 @@ declare module 'mongoose' { toObject(options?: ToObjectOptions & { flattenMaps?: boolean }): any; } - // eslint-disable-next-line @typescript-eslint/no-empty-interface - interface ObjectId extends mongodb.ObjectId { } + class ObjectId extends mongodb.ObjectId { + _id: this; + } class Subdocument extends Document { $isSingleNested: true; @@ -2218,12 +2219,14 @@ declare module 'mongoose' { }; type ReadonlyPartial = { - readonly [key in keyof TSchema]?: TSchema[key]; + readonly [key in keyof TSchema]?: TSchema[key]; }; type MatchKeysAndValues = ReadonlyPartial & DotAndArrayNotation; - type Condition = T | QuerySelector; + type AllowRegexpForStrings = T extends string ? T | RegExp : T; + + type Condition = AllowRegexpForStrings | QuerySelector; type _FilterQuery = { [P in keyof T]?: P extends '_id' diff --git a/test/typescript/objectid.ts b/test/typescript/objectid.ts index a7f7d60264e..ef593840c06 100644 --- a/test/typescript/objectid.ts +++ b/test/typescript/objectid.ts @@ -4,4 +4,4 @@ const oid = new Types.ObjectId(); oid.toHexString(); oid._id; -Types.ObjectId().toHexString(); \ No newline at end of file +(new Types.ObjectId()).toHexString(); \ No newline at end of file From a8550dd3207d433ec273947f2468ecaf33498bed Mon Sep 17 00:00:00 2001 From: Valeri Karpov Date: Mon, 8 Feb 2021 21:06:54 -0500 Subject: [PATCH 8/8] fix: upgrade to mongodb driver 4.0.0-beta.1 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index e9c4d5acb45..afce2d6c5fb 100644 --- a/package.json +++ b/package.json @@ -21,7 +21,7 @@ "dependencies": { "bson": "^4.2.2", "kareem": "2.3.2", - "mongodb": "4.0.0-beta.0", + "mongodb": "4.0.0-beta.1", "mongoose-legacy-pluralize": "1.0.2", "mpath": "0.8.3", "mquery": "3.2.3",