diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 96519ae8a1..c56b6f1bdd 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 ad98e77aa8..1cba4dd76b 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/README.md b/README.md index b28bed6653..d9208cb963 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/docs/connections.pug b/docs/connections.pug index 1217f64fe2..c2b92aa071 100644 --- a/docs/connections.pug +++ b/docs/connections.pug @@ -180,15 +180,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/index.pug b/docs/index.pug index a1c0ecb06f..abf242b3a4 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/lambda.pug b/docs/lambda.pug index 1b5b952058..0025109431 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/docs/models.pug b/docs/models.pug index 7c4b27ff2e..d04a26938b 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 c85a0feaf5..2cd05d94c7 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 4595c2a29d..2747fa3c5d 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 84a3701f02..3305fe8a8d 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.d.ts b/index.d.ts index e349d0cd65..5df726f4c4 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; @@ -796,7 +796,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; @@ -811,7 +811,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; @@ -852,7 +852,7 @@ declare module 'mongoose' { interface QueryOptions { arrayFilters?: { [key: string]: any }[]; batchSize?: number; - collation?: mongodb.CollationDocument; + collation?: mongodb.CollationOptions; comment?: any; context?: string; explain?: any; @@ -882,7 +882,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`. */ @@ -941,7 +941,7 @@ declare module 'mongoose' { populate?: string | string[] | PopulateOptions | PopulateOptions[]; } - interface InsertManyResult extends mongodb.InsertWriteOpResult { + interface InsertManyResult extends mongodb.InsertManyResult { mongoose?: { validationErrors?: Array } } @@ -1232,7 +1232,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. @@ -1765,22 +1765,10 @@ declare module 'mongoose' { toObject(options?: ToObjectOptions & { flattenMaps?: boolean }): any; } - const ObjectId: ObjectIdConstructor; - - class _ObjectId extends mongodb.ObjectID { - _id?: ObjectId; + class ObjectId extends mongodb.ObjectId { + _id: this; } - // 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 { } - class Subdocument extends Document { $isSingleNested: true; @@ -1834,7 +1822,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; @@ -1916,7 +1904,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; @@ -1924,7 +1912,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; @@ -2058,7 +2046,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; @@ -2072,7 +2060,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 @@ -2186,20 +2174,203 @@ 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; + }; + + type ReadonlyPartial = { + readonly [key in keyof TSchema]?: TSchema[key]; + }; + + type MatchKeysAndValues = ReadonlyPartial & DotAndArrayNotation; + + type AllowRegexpForStrings = T extends string ? T | RegExp : T; + + type Condition = AllowRegexpForStrings | 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> & mongodb.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; @@ -2301,7 +2472,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; @@ -2354,7 +2525,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/index.pug b/index.pug index 75ffbb0dfb..b1daf0034b 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/aggregate.js b/lib/aggregate.js index 919d554c65..d4a65b2d26 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 0f311e44bb..baa70e554e 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; @@ -654,17 +652,15 @@ 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 {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.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. - * @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. * @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. @@ -755,9 +751,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; } @@ -776,23 +769,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,28 +776,14 @@ 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); + 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) { @@ -836,7 +798,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') { @@ -863,6 +825,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 @@ -911,61 +876,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; @@ -1085,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/cursor/QueryCursor.js b/lib/cursor/QueryCursor.js index 32a130f7d7..5b5832e234 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/document.js b/lib/document.js index 91ec4989b7..3d7100128f 100644 --- a/lib/document.js +++ b/lib/document.js @@ -3468,7 +3468,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/lib/drivers/node-mongodb-native/collection.js b/lib/drivers/node-mongodb-native/collection.js index 88eac58dd9..9f2a48a584 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 57e7195448..48560bb6f5 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/index.js b/lib/index.js index 90b20f5ff9..e0172c5d06 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 @@ -253,14 +251,12 @@ 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. * @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.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. @@ -313,16 +309,13 @@ 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. * @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 97bb442ec7..d66e951074 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.matchedCount != null) { + numAffected = result.matchedCount; } else { numAffected = result; } @@ -1312,7 +1310,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); @@ -4054,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/lib/query.js b/lib/query.js index 6d9dadb332..4fc25dce6c 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()`: * @@ -1486,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; }; @@ -2040,20 +2040,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); + } }); /** @@ -3804,10 +3800,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); @@ -3830,7 +3822,7 @@ function _updateThunk(op, callback) { } if (this._update == null || Object.keys(this._update).length === 0) { - callback(null, 0); + callback(null, { acknowledged: false }); return null; } @@ -3962,13 +3954,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 * @@ -3983,16 +3974,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) @@ -4130,7 +4111,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`. @@ -4370,7 +4351,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; @@ -4389,7 +4370,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/lib/validoptions.js b/lib/validoptions.js index a095651325..0f070b4884 100644 --- a/lib/validoptions.js +++ b/lib/validoptions.js @@ -26,9 +26,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 9af7dac12f..afce2d6c5f 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.1", "mongoose-legacy-pluralize": "1.0.2", "mpath": "0.8.3", "mquery": "3.2.3", diff --git a/test/aggregate.test.js b/test/aggregate.test.js index 573afbbb8c..333093a705 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/collection.test.js b/test/collection.test.js index 1afb5741b6..3cedeb681a 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/common.js b/test/common.js index ac646467dc..1de39edd7a 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 e81192fb7e..4237d5f3ec 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,29 +225,6 @@ 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 }); - 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(); @@ -287,6 +259,7 @@ describe('connections:', function() { yield db.openUri('fail connection'); } catch (err) { assert.ok(err); + assert.equal(err.name, 'MongoParseError'); threw = true; } @@ -298,7 +271,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(); }); @@ -307,33 +280,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; @@ -566,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); @@ -898,8 +843,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 +860,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 +896,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 +911,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/docs/date.test.js b/test/docs/date.test.js index 7dad6f7147..e1536dc82e 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/docs/validation.test.js b/test/docs/validation.test.js index ff97d58fe6..91a8b7a40e 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.strict.test.js b/test/document.strict.test.js index fe65e7cca0..9b676f836e 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 62218b35ed..7c838573b7 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; } @@ -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/es-next/cast.test.es6.js b/test/es-next/cast.test.es6.js index fcf5f7eba7..07d8280019 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 c8364ac3fe..19e5626549 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 2d1a8f4452..fca73b323c 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 f9153e8bca..34e962d3e6 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() { @@ -36,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/es-next/virtuals.test.es6.js b/test/es-next/virtuals.test.es6.js index 9acb551f18..b530d80b14 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 322fa4df39..234e19aa38 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() { @@ -755,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 55e1e00271..0000000000 --- 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.setting.test.js b/test/model.populate.setting.test.js index 514dc5681a..7872e1523a 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()); diff --git a/test/model.populate.test.js b/test/model.populate.test.js index 2b670220f0..7f1c3b399e 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 981dfb7249..b1de963f16 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/model.querying.test.js b/test/model.querying.test.js index 79e6bfad05..67e488373c 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 4252fe68a0..28c47ffd32 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/model.update.test.js b/test/model.update.test.js index 0acdf1d809..43aaf626bf 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 a260bac25f..a0c3ec88a2 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 })); diff --git a/test/query.test.js b/test/query.test.js index d3743b42ba..7ec98104b9 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() { @@ -1713,7 +1662,7 @@ describe('Query', function() { _id: { select: false, type: Schema.Types.ObjectId, - default: mongoose.Types.ObjectId + default: () => new mongoose.Types.ObjectId() }, username: String }); @@ -2849,7 +2798,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 +2816,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 +2834,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 +2853,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 +2872,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 +2891,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); }); }); diff --git a/test/schema.onthefly.test.js b/test/schema.onthefly.test.js index 67411e6aa5..f88b2612b3 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'); diff --git a/test/typescript/connectSyntax.ts b/test/typescript/connectSyntax.ts index 414a747eec..7147dc9990 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/connection.ts b/test/typescript/connection.ts index eb3b909523..5f24b49a03 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; }); diff --git a/test/typescript/global.ts b/test/typescript/global.ts index 8887190240..c018aac1e7 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(() => { diff --git a/test/typescript/objectid.ts b/test/typescript/objectid.ts index a7f7d60264..ef593840c0 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