diff --git a/.github/workflows/tsd.yml b/.github/workflows/tsd.yml
index 7a47d134117..68e4ef1e81a 100644
--- a/.github/workflows/tsd.yml
+++ b/.github/workflows/tsd.yml
@@ -45,7 +45,7 @@ jobs:
- name: Setup node
uses: actions/setup-node@64ed1c7eab4cce3362f8c340dee64e5eaeef8f7c # v3.6.0
with:
- node-version: 12
+ node-version: 14
- run: npm install
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 8ee1586f2a2..a592555f122 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,13 @@
+7.2.2 / 2023-05-30
+==================
+ * fix(schema): make bulkWrite updateOne() and updateMany() respect timestamps option when set by merging schemas #13445
+ * fix(schema): recursively copy schemas from different modules when calling new Schema() #13441 #13275
+ * fix(update): allow setting paths with dots under non-strict paths #13450 #13434
+ * types: improve function parameter types for ToObjectOptions transform option #13446 #13421
+ * docs: add nextjs page with link to next starter app and couple FAQs #13444 #13430
+ * docs(connections): add section on multi tenant #13449 #11187
+ * docs(connection+model): expand docs on accessors for underlying collections #13448 #13334
+
7.2.1 / 2023-05-24
==================
* fix(array): track correct changes when setting nested array of primitives #13422 #13372
diff --git a/docs/connections.md b/docs/connections.md
index 44b5dc4a9e6..87c5b9c54b4 100644
--- a/docs/connections.md
+++ b/docs/connections.md
@@ -31,6 +31,7 @@ See the [mongodb connection string spec](http://www.mongodb.com/docs/manual/refe
Multi-mongos support
Multiple connections
Connection Pools
+ Multi Tenant Connections
@@ -449,6 +450,91 @@ const uri = 'mongodb://127.0.0.1:27017/test?maxPoolSize=10';
mongoose.createConnection(uri);
```
+The connection pool size is important because [MongoDB currently can only process one operation per socket](https://thecodebarbarian.com/slow-trains-in-mongodb-and-nodejs).
+So `maxPoolSize` functions as a cap on the number of concurrent operations.
+
+
+
+In the context of Mongoose, a multi-tenant architecture typically means a case where multiple different clients talk to MongoDB through a single Mongoose application.
+This typically means each client makes queries and executes updates through a single Mongoose application, but has a distinct MongoDB database within the same MongoDB cluster.
+
+We recommend reading [this article about multi-tenancy with Mongoose](https://medium.com/brightlab-techblog/multitenant-node-js-application-with-mongoose-mongodb-f8841a285b4f); it has a good description of how we define multi-tenancy and a more detailed overview of our recommended patterns.
+
+There are two patterns we recommend for multi-tenancy in Mongoose:
+
+1. Maintain one connection pool, switch between tenants using the [`Connection.prototype.useDb()` method](https://mongoosejs.com/docs/api/connection.html#Connection.prototype.useDb()).
+2. Maintain a separate connection pool per tenant, store connections in a map or [POJO](https://masteringjs.io/tutorials/fundamentals/pojo).
+
+The following is an example of pattern (1).
+We recommend pattern (1) for cases where you have a small number of tenants, or if each individual tenant's workload is light (approximately < 1 request per second, all requests take < 10ms of database processing time).
+Pattern (1) is simpler to implement and simpler to manage in production, because there is only 1 connection pool.
+But, under high load, you will likely run into issues where some tenants' operations slow down other tenants' operations due to [slow trains](https://thecodebarbarian.com/slow-trains-in-mongodb-and-nodejs).
+
+```javascript
+const express = require('express');
+const mongoose = require('mongoose');
+
+mongoose.connect('mongodb://127.0.0.1:27017/main');
+mongoose.set('debug', true);
+
+mongoose.model('User', mongoose.Schema({ name: String }));
+
+const app = express();
+
+app.get('/users/:tenantId', function(req, res) {
+ const db = mongoose.connection.useDb(`tenant_${req.params.tenantId}`, {
+ // `useCache` tells Mongoose to cache connections by database name, so
+ // `mongoose.connection.useDb('foo', { useCache: true })` returns the
+ // same reference each time.
+ useCache: true
+ });
+ // Need to register models every time a new connection is created
+ if (!db.models['User']) {
+ db.model('User', mongoose.Schema({ name: String }));
+ }
+ console.log('Find users from', db.name);
+ db.model('User').find().
+ then(users => res.json({ users })).
+ catch(err => res.status(500).json({ message: err.message }));
+});
+
+app.listen(3000);
+```
+
+The following is an example of pattern (2).
+Pattern (2) is more flexible and better for use cases with > 10k tenants and > 1 requests/second.
+Because each tenant has a separate connection pool, one tenants' slow operations will have minimal impact on other tenants.
+However, this pattern is harder to implement and manage in production.
+In particular, [MongoDB does have a limit on the number of open connections](https://www.mongodb.com/blog/post/tuning-mongodb--linux-to-allow-for-tens-of-thousands-connections), and [MongoDB Atlas has separate limits on the number of open connections](https://www.mongodb.com/docs/atlas/reference/atlas-limits), so you need to make sure the total number of sockets in your connection pools doesn't go over MongoDB's limits.
+
+```javascript
+const express = require('express');
+const mongoose = require('mongoose');
+
+mongoose.connect('mongodb://127.0.0.1:27017/main');
+
+const tenantIdToConnection = {};
+
+const app = express();
+
+app.get('/users/:tenantId', function(req, res) {
+ let initialConnection = Promise.resolve();
+ const { tenantId } = req.params;
+ if (!tenantIdToConnection[tenantId]) {
+ tenantIdToConnection[tenantId] = mongoose.createConnection(`mongodb://127.0.0.1:27017/tenant_${tenantId}`);
+ tenantIdToConnection[tenantId].model('User', mongoose.Schema({ name: String }));
+ initialConnection = tenantIdToConnection[tenantId].asPromise();
+ }
+ const db = tenantIdToConnection[tenantId];
+ initialConnection.
+ then(() => db.model('User').find()).
+ then(users => res.json({ users })).
+ catch(err => res.status(500).json({ message: err.message }));
+});
+
+app.listen(3000);
+```
+
Next Up
Now that we've covered connections, let's take a look at [models](models.html).
diff --git a/docs/guides.md b/docs/guides.md
index d24aff91797..aac2bed5396 100644
--- a/docs/guides.md
+++ b/docs/guides.md
@@ -35,6 +35,7 @@ integrating Mongoose with external tools and frameworks.
* [Promises](promises.html)
* [Lodash](lodash.html)
* [AWS Lambda](lambda.html)
+* [Next.js](nextjs.html)
* [Browser Library](browser.html)
* [GeoJSON](geojson.html)
* [Transactions](transactions.html)
diff --git a/docs/nextjs.md b/docs/nextjs.md
new file mode 100644
index 00000000000..9699a14967c
--- /dev/null
+++ b/docs/nextjs.md
@@ -0,0 +1,38 @@
+# Using Mongoose With [Next.js](https://nextjs.org/)
+
+Next.js is a popular framework for building full stack applications with React.
+Mongoose works out of the box with Next.js.
+If you're looking to get started, please use [Next.js' official Mongoose sample app](https://github.com/vercel/next.js/tree/canary/examples/with-mongodb-mongoose).
+Furthermore, if you are using Next.js with [Vercel Serverless Functions](https://vercel.com/docs/concepts/functions/serverless-functions), please review [Mongoose's AWS Lambda docs](https://vercel.com/docs/concepts/functions/serverless-functions).
+
+There are a few common issues when working with Next.js that you should be aware of.
+
+### TypeError: Cannot read properties of undefined (reading 'prototype')
+
+You can fix this issue by adding the following to your `next.config.js`:
+
+```
+const nextConfig = {
+ experimental: {
+ esmExternals: "loose", // <-- add this
+ serverComponentsExternalPackages: ["mongoose"] // <-- and this
+ },
+ // and the following to enable top-level await support for Webpack
+ webpack: (config) => {
+ config.experiments = {
+ topLevelAwait: true
+ };
+ return config;
+ },
+}
+```
+
+This issue is caused by [this change in MongoDB's bson parser](https://github.com/mongodb/js-bson/pull/564/files).
+MongoDB's bson parser uses top-level await and dynamic import in ESM mode to avoid some Webpack bundling issues.
+And Next.js forces ESM mode.
+
+### Next.js Edge Runtime
+
+Mongoose does **not** currently support [Next.js Edge Runtime](https://nextjs.org/docs/app/building-your-application/rendering/edge-and-nodejs-runtimes#edge-runtime).
+While you can import Mongoose in Edge Runtime, you'll get [Mongoose's browser library](browser.html).
+There is no way for Mongoose to connect to MongoDB in Edge Runtime, because [Edge Runtime currently doesn't support Node.js `net` API](https://edge-runtime.vercel.app/features/available-apis#unsupported-apis), which is what the MongoDB Node Driver uses to connect to MongoDB.
\ No newline at end of file
diff --git a/docs/source/index.js b/docs/source/index.js
index de0ca191cef..6f2c1e0060b 100644
--- a/docs/source/index.js
+++ b/docs/source/index.js
@@ -72,6 +72,7 @@ docs['docs/transactions.md'] = { guide: true, title: 'Transactions', acquit: tru
docs['docs/deprecations.md'] = { guide: true, title: 'Deprecation Warnings', markdown: true };
docs['docs/further_reading.md'] = { title: 'Further Reading', markdown: true };
docs['docs/jest.md'] = { title: 'Testing Mongoose with Jest', markdown: true };
+docs['docs/nextjs.md'] = { title: 'Using Mongoose With Next.js', markdown: true };
docs['docs/faq.md'] = { guide: true, title: 'FAQ', markdown: true };
docs['docs/typescript.md'] = { guide: true, title: 'Using TypeScript with Mongoose', markdown: true };
docs['docs/compatibility.md'] = {
diff --git a/lib/connection.js b/lib/connection.js
index 31d486d2c61..6a4f268b536 100644
--- a/lib/connection.js
+++ b/lib/connection.js
@@ -1128,9 +1128,10 @@ Connection.prototype.onClose = function(force) {
};
/**
- * Retrieves a collection, creating it if not cached.
- *
- * Not typically needed by applications. Just talk to your collection through your model.
+ * Retrieves a raw collection instance, creating it if not cached.
+ * This method returns a thin wrapper around a [MongoDB Node.js driver collection]([MongoDB Node.js driver collection](https://mongodb.github.io/node-mongodb-native/Next/classes/Collection.html)).
+ * Using a Collection bypasses Mongoose middleware, validation, and casting,
+ * letting you use [MongoDB Node.js driver](https://mongodb.github.io/node-mongodb-native/) functionality directly.
*
* @param {String} name of the collection
* @param {Object} [options] optional collection options
@@ -1582,7 +1583,7 @@ Connection.prototype.syncIndexes = async function syncIndexes(options = {}) {
};
/**
- * Switches to a different database using the same connection pool.
+ * Switches to a different database using the same [connection pool](https://mongoosejs.com/docs/api/connectionshtml#connection_pools).
*
* Returns a new connection object, with the new db.
*
diff --git a/lib/cursor/QueryCursor.js b/lib/cursor/QueryCursor.js
index 59db17ce112..13f5e320a40 100644
--- a/lib/cursor/QueryCursor.js
+++ b/lib/cursor/QueryCursor.js
@@ -42,7 +42,6 @@ function QueryCursor(query, options) {
this.cursor = null;
this.skipped = false;
this.query = query;
- const _this = this;
const model = query.model;
this._mongooseOptions = {};
this._transforms = [];
@@ -59,16 +58,16 @@ function QueryCursor(query, options) {
util.inspect(resultValue) +
'".'
);
- _this._markError(err);
- _this.listeners('error').length > 0 && _this.emit('error', err);
+ this._markError(err);
+ this.listeners('error').length > 0 && this.emit('error', err);
return;
}
this.skipped = true;
- _this.emit('cursor', null);
+ this.emit('cursor', null);
return;
}
- _this._markError(err);
- _this.listeners('error').length > 0 && _this.emit('error', err);
+ this._markError(err);
+ this.listeners('error').length > 0 && this.emit('error', err);
return;
}
this._transforms = this._transforms.concat(query._transforms.slice());
@@ -87,17 +86,17 @@ function QueryCursor(query, options) {
Object.assign(this.options, query._optionsForExec());
model.collection.find(query._conditions, this.options, (err, cursor) => {
if (err != null) {
- _this._markError(err);
- _this.listeners('error').length > 0 && _this.emit('error', _this._error);
+ this._markError(err);
+ this.listeners('error').length > 0 && this.emit('error', this._error);
return;
}
- if (_this._error) {
+ if (this._error) {
cursor.close(function() {});
- _this.listeners('error').length > 0 && _this.emit('error', _this._error);
+ this.listeners('error').length > 0 && this.emit('error', this._error);
}
- _this.cursor = cursor;
- _this.emit('cursor', cursor);
+ this.cursor = cursor;
+ this.emit('cursor', cursor);
});
});
}
@@ -113,21 +112,20 @@ util.inherits(QueryCursor, Readable);
*/
QueryCursor.prototype._read = function() {
- const _this = this;
- _next(this, function(error, doc) {
+ _next(this, (error, doc) => {
if (error) {
- return _this.emit('error', error);
+ return this.emit('error', error);
}
if (!doc) {
- _this.push(null);
- _this.cursor.close(function(error) {
+ this.push(null);
+ this.cursor.close(function(error) {
if (error) {
- return _this.emit('error', error);
+ return this.emit('error', error);
}
});
return;
}
- _this.push(doc);
+ this.push(doc);
});
};
@@ -223,9 +221,8 @@ QueryCursor.prototype.close = async function close() {
*/
QueryCursor.prototype.rewind = function() {
- const _this = this;
- _waitForCursor(this, function() {
- _this.cursor.rewind();
+ _waitForCursor(this, () => {
+ this.cursor.rewind();
});
return this;
};
@@ -281,14 +278,13 @@ QueryCursor.prototype.next = async function next() {
*/
QueryCursor.prototype.eachAsync = function(fn, opts, callback) {
- const _this = this;
if (typeof opts === 'function') {
callback = opts;
opts = {};
}
opts = opts || {};
- return eachAsync(function(cb) { return _next(_this, cb); }, fn, opts, callback);
+ return eachAsync((cb) => _next(this, cb), fn, opts, callback);
};
/**
@@ -312,9 +308,8 @@ QueryCursor.prototype.options;
*/
QueryCursor.prototype.addCursorFlag = function(flag, value) {
- const _this = this;
- _waitForCursor(this, function() {
- _this.cursor.addCursorFlag(flag, value);
+ _waitForCursor(this, () => {
+ this.cursor.addCursorFlag(flag, value);
});
return this;
};
@@ -521,13 +516,12 @@ function _populateBatch() {
if (!this.ctx._batchDocs.length) {
return this.callback(null, null);
}
- const _this = this;
this.ctx.query.model.populate(this.ctx._batchDocs, this.ctx._pop).then(
() => {
- _nextDoc(_this.ctx, _this.ctx._batchDocs.shift(), _this.ctx._pop, _this.callback);
+ _nextDoc(this.ctx, this.ctx._batchDocs.shift(), this.ctx._pop, this.callback);
},
err => {
- _this.callback(err);
+ this.callback(err);
}
);
}
diff --git a/lib/helpers/query/castUpdate.js b/lib/helpers/query/castUpdate.js
index 607cc918041..36cff19e8e4 100644
--- a/lib/helpers/query/castUpdate.js
+++ b/lib/helpers/query/castUpdate.js
@@ -360,7 +360,7 @@ function walkUpdatePath(schema, obj, op, options, context, filter, pref) {
try {
if (prefix.length === 0 || key.indexOf('.') === -1) {
obj[key] = castUpdateVal(schematype, val, op, key, context, prefix + key);
- } else {
+ } else if (isStrict !== false || schematype != null) {
// Setting a nested dotted path that's in the schema. We don't allow paths with '.' in
// a schema, so replace the dotted path with a nested object to avoid ending up with
// dotted properties in the updated object. See (gh-10200)
diff --git a/lib/helpers/schema/merge.js b/lib/helpers/schema/merge.js
index aeb8c453b00..e7bb91f6ae0 100644
--- a/lib/helpers/schema/merge.js
+++ b/lib/helpers/schema/merge.js
@@ -9,7 +9,9 @@ module.exports = function merge(s1, s2, skipConflictingPaths) {
}
pathsToAdd[key] = s2.tree[key];
}
- s1.add(pathsToAdd);
+ s1.options._isMerging = true;
+ s1.add(pathsToAdd, null);
+ delete s1.options._isMerging;
s1.callQueue = s1.callQueue.concat(s2.callQueue);
s1.method(s2.methods);
@@ -17,8 +19,7 @@ module.exports = function merge(s1, s2, skipConflictingPaths) {
for (const [option, value] of Object.entries(s2._userProvidedOptions)) {
if (!(option in s1._userProvidedOptions)) {
- s1._userProvidedOptions[option] = value;
- s1.options[option] = value;
+ s1.set(option, value);
}
}
diff --git a/lib/model.js b/lib/model.js
index 2f5d3a6ec3a..65e0211e715 100644
--- a/lib/model.js
+++ b/lib/model.js
@@ -145,7 +145,9 @@ Model.prototype.$isMongooseModelPrototype = true;
Model.prototype.db;
/**
- * Collection the model uses.
+ * The collection instance this model uses.
+ * A Mongoose collection is a thin wrapper around a [MongoDB Node.js driver collection]([MongoDB Node.js driver collection](https://mongodb.github.io/node-mongodb-native/Next/classes/Collection.html)).
+ * Using `Model.collection` means you bypass Mongoose middleware, validation, and casting.
*
* This property is read-only. Modifying this property is a no-op.
*
diff --git a/lib/schema.js b/lib/schema.js
index e9cb24b0f54..25a5f773639 100644
--- a/lib/schema.js
+++ b/lib/schema.js
@@ -1285,12 +1285,16 @@ Schema.prototype.interpretAsType = function(path, obj, options) {
// new Schema({ path: [new Schema({ ... })] })
if (cast && cast.instanceOfSchema) {
if (!(cast instanceof Schema)) {
- throw new TypeError('Schema for array path `' + path +
- '` is from a different copy of the Mongoose module. ' +
- 'Please make sure you\'re using the same version ' +
- 'of Mongoose everywhere with `npm list mongoose`. If you are still ' +
- 'getting this error, please add `new Schema()` around the path: ' +
- `${path}: new Schema(...)`);
+ if (this.options._isMerging) {
+ cast = new Schema(cast);
+ } else {
+ throw new TypeError('Schema for array path `' + path +
+ '` is from a different copy of the Mongoose module. ' +
+ 'Please make sure you\'re using the same version ' +
+ 'of Mongoose everywhere with `npm list mongoose`. If you are still ' +
+ 'getting this error, please add `new Schema()` around the path: ' +
+ `${path}: new Schema(...)`);
+ }
}
return new MongooseTypes.DocumentArray(path, cast, obj);
}
@@ -1298,12 +1302,16 @@ Schema.prototype.interpretAsType = function(path, obj, options) {
cast[options.typeKey] &&
cast[options.typeKey].instanceOfSchema) {
if (!(cast[options.typeKey] instanceof Schema)) {
- throw new TypeError('Schema for array path `' + path +
- '` is from a different copy of the Mongoose module. ' +
- 'Please make sure you\'re using the same version ' +
- 'of Mongoose everywhere with `npm list mongoose`. If you are still ' +
- 'getting this error, please add `new Schema()` around the path: ' +
- `${path}: new Schema(...)`);
+ if (this.options._isMerging) {
+ cast[options.typeKey] = new Schema(cast[options.typeKey]);
+ } else {
+ throw new TypeError('Schema for array path `' + path +
+ '` is from a different copy of the Mongoose module. ' +
+ 'Please make sure you\'re using the same version ' +
+ 'of Mongoose everywhere with `npm list mongoose`. If you are still ' +
+ 'getting this error, please add `new Schema()` around the path: ' +
+ `${path}: new Schema(...)`);
+ }
}
return new MongooseTypes.DocumentArray(path, cast[options.typeKey], obj, cast);
}
diff --git a/package.json b/package.json
index 068e48412b9..5f7da55b605 100644
--- a/package.json
+++ b/package.json
@@ -1,7 +1,7 @@
{
"name": "mongoose",
"description": "Mongoose MongoDB ODM",
- "version": "7.2.1",
+ "version": "7.2.2",
"author": "Guillermo Rauch ",
"keywords": [
"mongodb",
@@ -21,7 +21,7 @@
"dependencies": {
"bson": "^5.3.0",
"kareem": "2.5.1",
- "mongodb": "5.5.0",
+ "mongodb": "5.6.0",
"mpath": "0.9.0",
"mquery": "5.0.0",
"ms": "2.1.3",
@@ -64,7 +64,7 @@
"q": "1.5.1",
"sinon": "15.0.4",
"stream-browserify": "3.0.0",
- "tsd": "0.25.0",
+ "tsd": "0.28.1",
"typescript": "5.0.4",
"uuid": "9.0.0",
"webpack": "5.81.0",
diff --git a/test/model.findOneAndUpdate.test.js b/test/model.findOneAndUpdate.test.js
index 4441b633c0f..8ab0c67244b 100644
--- a/test/model.findOneAndUpdate.test.js
+++ b/test/model.findOneAndUpdate.test.js
@@ -2111,12 +2111,36 @@ describe('model: findOneAndUpdate:', function() {
{ $set: { name: 'Test' } },
{ projection: '+nickName', returnDocument: 'after' }
);
+ assert.equal(res.name, 'Test');
assert.equal(res.nickName, 'Quiz');
res = await Test.findOneAndDelete(
{ _id: entry._id },
{ projection: '+nickName', returnDocument: 'before' }
);
+ assert.equal(res.name, 'Test');
assert.equal(res.nickName, 'Quiz');
});
+
+ it('allows setting paths with dots in non-strict paths (gh-13434) (gh-10200)', async function() {
+ const testSchema = new mongoose.Schema({
+ name: String,
+ info: Object
+ }, { strict: false });
+ const Test = db.model('Test', testSchema);
+
+ const doc = await Test.findOneAndUpdate(
+ {},
+ {
+ name: 'Test Testerson',
+ info: { 'second.name': 'Quiz' },
+ info2: { 'second.name': 'Quiz' }
+ },
+ { new: true, upsert: true }
+ ).lean();
+
+ assert.ok(doc);
+ assert.equal(doc.info['second.name'], 'Quiz');
+ assert.equal(doc.info2['second.name'], 'Quiz');
+ });
});
diff --git a/test/model.test.js b/test/model.test.js
index 0abf895aa5c..2042b5e8b2b 100644
--- a/test/model.test.js
+++ b/test/model.test.js
@@ -3960,7 +3960,6 @@ describe('Model', function() {
const M = db.model('Test', schema);
-
await M.create({ num: 42 });
await new Promise((resolve) => setTimeout(resolve, 10));
@@ -3978,7 +3977,29 @@ describe('Model', function() {
assert.ok(doc.createdAt.valueOf() >= now.valueOf());
assert.ok(doc.updatedAt);
assert.ok(doc.updatedAt.valueOf() >= now.valueOf());
+ });
+
+ it('with timestamps from merged schema (gh-13409)', async function() {
+ const schema = new Schema({ num: Number });
+ schema.add(new Schema({}, { timestamps: true }));
+
+ const M = db.model('Test', schema);
+
+ await M.create({ num: 42 });
+
+ await new Promise((resolve) => setTimeout(resolve, 10));
+ const now = Date.now();
+ await M.bulkWrite([{
+ updateOne: {
+ filter: { num: 42 },
+ update: { num: 100 }
+ }
+ }]);
+
+ const doc = await M.findOne({ num: 100 });
+ assert.ok(doc.updatedAt);
+ assert.ok(doc.updatedAt.valueOf() >= now.valueOf());
});
it('with child timestamps (gh-7032)', async function() {
diff --git a/test/types/schema.test.ts b/test/types/schema.test.ts
index 00382a48993..f2de7232ea0 100644
--- a/test/types/schema.test.ts
+++ b/test/types/schema.test.ts
@@ -519,30 +519,24 @@ export function autoTypedSchema() {
}, {
statics: {
staticFn() {
- expectType>(this);
+ expectType>>(this);
return 'Returned from staticFn' as const;
}
},
methods: {
instanceFn() {
- expectType>(this);
+ expectType>>(this);
return 'Returned from DocumentInstanceFn' as const;
}
},
query: {
byUserName(userName) {
- expectAssignable>(this);
+ expectAssignable>>(this);
return this.where({ userName });
}
}
});
- type InferredSchemaType = InferSchemaType;
-
- expectType({} as InferredSchemaType);
-
- expectError({} as InferredSchemaType);
-
return AutoTypedSchema;
}
diff --git a/types/index.d.ts b/types/index.d.ts
index 921051536c1..55e2aa2360b 100644
--- a/types/index.d.ts
+++ b/types/index.d.ts
@@ -161,7 +161,7 @@ declare module 'mongoose' {
[k: string]: string;
}
- export interface ToObjectOptions {
+ export interface ToObjectOptions> {
/** apply all getters (path and virtual getters) */
getters?: boolean;
/** apply virtual getters (can override getters option) */
@@ -171,7 +171,11 @@ declare module 'mongoose' {
/** remove empty objects (defaults to true) */
minimize?: boolean;
/** if set, mongoose will call this function to allow you to transform the returned object */
- transform?: boolean | ((doc: any, ret: any, options: any) => any);
+ transform?: boolean | ((
+ doc: THydratedDocumentType,
+ ret: Record,
+ options: ToObjectOptions
+ ) => any);
/** if true, replace any conventionally populated paths with the original id in the output. Has no affect on virtual populated paths. */
depopulate?: boolean;
/** if false, exclude the version key (`__v` by default) from the output */
diff --git a/types/schemaoptions.d.ts b/types/schemaoptions.d.ts
index c2db1180874..997d79054c9 100644
--- a/types/schemaoptions.d.ts
+++ b/types/schemaoptions.d.ts
@@ -133,14 +133,14 @@ declare module 'mongoose' {
*/
strictQuery?: boolean | 'throw';
/** Exactly the same as the toObject option but only applies when the document's toJSON method is called. */
- toJSON?: ToObjectOptions;
+ toJSON?: ToObjectOptions;
/**
* Documents have a toObject method which converts the mongoose document into a plain JavaScript object.
* This method accepts a few options. Instead of applying these options on a per-document basis, we may
* declare the options at the schema level and have them applied to all of the schema's documents by
* default.
*/
- toObject?: ToObjectOptions;
+ toObject?: ToObjectOptions;
/**
* By default, if you have an object with key 'type' in your schema, mongoose will interpret it as a
* type declaration. However, for applications like geoJSON, the 'type' property is important. If you want to
diff --git a/types/types.d.ts b/types/types.d.ts
index 4cc3097574c..f63a0b05ee3 100644
--- a/types/types.d.ts
+++ b/types/types.d.ts
@@ -42,8 +42,8 @@ declare module 'mongoose' {
shift(): T;
/** Returns a native js Array. */
- toObject(options?: ToObjectOptions): any;
- toObject(options?: ToObjectOptions): T;
+ toObject(options?: ToObjectOptions): any;
+ toObject(options?: ToObjectOptions): T;
/** Wraps [`Array#unshift`](https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array/unshift) with proper change tracking. */
unshift(...args: any[]): number;