UNPKG

15.5 kBMarkdownView Raw
1### Version Requirements
2
3Mongoose now requires node.js >= 4.0.0 and MongoDB >= 3.0.0. [MongoDB 2.6](https://www.mongodb.com/blog/post/mongodb-2-6-end-of-life) and [Node.js < 4](https://github.com/nodejs/Release) where both EOL-ed in 2016.
4
5### Query Middleware
6
7Query middleware is now compiled when you call `mongoose.model()` or `db.model()`. If you add query middleware after calling `mongoose.model()`, that middleware will **not** get called.
8
9```javascript
10const schema = new Schema({ name: String });
11const MyModel = mongoose.model('Test', schema);
12schema.pre('find', () => { console.log('find!'); });
13
14MyModel.find().exec(function() {
15 // In mongoose 4.x, the above `.find()` will print "find!"
16 // In mongoose 5.x, "find!" will **not** be printed.
17 // Call `pre('find')` **before** calling `mongoose.model()` to make the middleware apply.
18});
19```
20
21### Promises and Callbacks for `mongoose.connect()`
22
23`mongoose.connect()` and `mongoose.disconnect()` now return a promise if no callback specified, or `null` otherwise. It does **not** return the mongoose singleton.
24
25```javascript
26// Worked in mongoose 4. Does **not** work in mongoose 5, `mongoose.connect()`
27// now returns a promise consistently. This is to avoid the horrible things
28// we've done to allow mongoose to be a thenable that resolves to itself.
29mongoose.connect('mongodb://localhost:27017/test').model('Test', new Schema({}));
30
31// Do this instead
32mongoose.connect('mongodb://localhost:27017/test');
33mongoose.model('Test', new Schema({}));
34```
35
36### Connection Logic and `useMongoClient`
37
38The [`useMongoClient` option](http://mongoosejs.com/docs/4.x/docs/connections.html#use-mongo-client) was
39removed in Mongoose 5, it is now always `true`. As a consequence, Mongoose 5
40no longer supports several function signatures for `mongoose.connect()` that
41worked in Mongoose 4.x if the `useMongoClient` option was off. Below are some
42examples of `mongoose.connect()` calls that do **not** work in Mongoose 5.x.
43
44* `mongoose.connect('localhost', 27017);`
45* `mongoose.connect('localhost', 'mydb', 27017);`
46* `mongoose.connect('mongodb://host1:27017,mongodb://host2:27017');`
47
48In Mongoose 5.x, the first parameter to `mongoose.connect()` and `mongoose.createConnection()`, if specified, **must** be a [MongoDB connection string](https://docs.mongodb.com/manual/reference/connection-string/). The
49connection string and options are then passed down to [the MongoDB Node.js driver's `MongoClient.connect()` function](http://mongodb.github.io/node-mongodb-native/3.0/api/MongoClient.html#.connect). Mongoose does not modify the connection string, although `mongoose.connect()` and `mongoose.createConnection()` support a [few additional options in addition to the ones the MongoDB driver supports](http://mongoosejs.com/docs/connections.html#options).
50
51### Setter Order
52
53Setters run in reverse order in 4.x:
54
55```javascript
56const schema = new Schema({ name: String });
57schema.path('name').
58 get(() => console.log('This will print 2nd')).
59 get(() => console.log('This will print first'));
60```
61
62In 5.x, setters run in the order they're declared.
63
64```javascript
65const schema = new Schema({ name: String });
66schema.path('name').
67 get(() => console.log('This will print first')).
68 get(() => console.log('This will print 2nd'));
69```
70
71### Checking if a path is populated
72
73Mongoose 5.1.0 introduced an `_id` getter to ObjectIds that lets you get an ObjectId regardless of whether a path
74is populated.
75
76```javascript
77const blogPostSchema = new Schema({
78 title: String,
79 author: {
80 type: mongoose.Schema.Types.ObjectId,
81 ref: 'Author'
82 }
83});
84const BlogPost = mongoose.model('BlogPost', blogPostSchema);
85
86await BlogPost.create({ title: 'test', author: author._id });
87const blogPost = await BlogPost.findOne();
88
89console.log(blogPost.author); // '5b207f84e8061d1d2711b421'
90// New in Mongoose 5.1.0: this will print '5b207f84e8061d1d2711b421' as well
91console.log(blogPost.author._id);
92
93await blogPost.populate('author');
94console.log(blogPost.author._id); '5b207f84e8061d1d2711b421'
95```
96
97As a consequence, checking whether `blogPost.author._id` is [no longer viable as a way to check whether `author` is populated](https://github.com/Automattic/mongoose/issues/6415#issuecomment-388579185). Use `blogPost.populated('author') != null` or `blogPost.author instanceof mongoose.Types.ObjectId` to check whether `author` is populated instead.
98
99Note that you can call `mongoose.set('objectIdGetter', false)` to change this behavior.
100
101### Return Values for `remove()` and `deleteX()`
102
103`deleteOne()`, `deleteMany()`, and `remove()` now resolve to the result object
104rather than the full [driver `WriteOpResult` object](http://mongodb.github.io/node-mongodb-native/2.2/api/Collection.html#~writeOpCallback).
105
106```javascript
107// In 4.x, this is how you got the number of documents deleted
108MyModel.deleteMany().then(res => console.log(res.result.n));
109// In 5.x this is how you get the number of documents deleted
110MyModel.deleteMany().then(res => res.n);
111```
112
113### Aggregation Cursors
114
115The `useMongooseAggCursor` option from 4.x is now always on. This is the new syntax for aggregation cursors in mongoose 5:
116
117```javascript
118// When you call `.cursor()`, `.exec()` will now return a mongoose aggregation
119// cursor.
120const cursor = MyModel.aggregate([{ $match: { name: 'Val' } }]).cursor().exec();
121// No need to `await` on the cursor or wait for a promise to resolve
122cursor.eachAsync(doc => console.log(doc));
123
124// Can also pass options to `cursor()`
125const cursorWithOptions = MyModel.
126 aggregate([{ $match: { name: 'Val' } }]).
127 cursor({ batchSize: 10 }).
128 exec();
129```
130
131### geoNear
132
133`Model.geoNear()` has been removed because the [MongoDB driver no longer supports it](https://github.com/mongodb/node-mongodb-native/blob/master/CHANGES_3.0.0.md#geonear-command-helper)
134
135### Required URI encoding of connection strings
136
137Due to changes in the MongoDB driver, connection strings must be URI encoded.
138
139If they are not, connections may fail with an illegal character message.
140
141#### Passwords which contain certain characters
142
143See a [full list of affected characters](https://developer.mozilla.org/en-US/docs/Glossary/percent-encoding).
144
145If your app is used by a lot of different connection strings, it's possible
146that your test cases will pass, but production passwords will fail. Encode all your connection
147strings to be safe.
148
149If you want to continue to use unencoded connection strings, the easiest fix is to use
150the `mongodb-uri` module to parse the connection strings, and then produce the properly encoded
151versions. You can use a function like this:
152
153```javascript
154const uriFormat = require('mongodb-uri')
155function encodeMongoURI (urlString) {
156 if (urlString) {
157 let parsed = uriFormat.parse(urlString)
158 urlString = uriFormat.format(parsed);
159 }
160 return urlString;
161 }
162}
163
164// Your un-encoded string.
165const mongodbConnectString = "mongodb://...";
166mongoose.connect(encodeMongoURI(mongodbConnectString))
167```
168
169The function above is safe to use whether the existing string is already encoded or not.
170
171#### Domain sockets
172
173Domain sockets must be URI encoded. For example:
174
175```javascript
176// Works in mongoose 4. Does **not** work in mongoose 5 because of more
177// stringent URI parsing.
178const host = '/tmp/mongodb-27017.sock';
179mongoose.createConnection(`mongodb://aaron:psw@${host}/fake`);
180
181// Do this instead
182const host = encodeURIComponent('/tmp/mongodb-27017.sock');
183mongoose.createConnection(`mongodb://aaron:psw@${host}/fake`);
184```
185
186### `toObject()` Options
187
188The `options` parameter to `toObject()` and `toJSON()` merge defaults rather than overwriting them.
189
190```javascript
191// Note the `toObject` option below
192const schema = new Schema({ name: String }, { toObject: { virtuals: true } });
193schema.virtual('answer').get(() => 42);
194const MyModel = db.model('MyModel', schema);
195
196const doc = new MyModel({ name: 'test' });
197// In mongoose 4.x this prints "undefined", because `{ minimize: false }`
198// overwrites the entire schema-defined options object.
199// In mongoose 5.x this prints "42", because `{ minimize: false }` gets
200// merged with the schema-defined options.
201console.log(doc.toJSON({ minimize: false }).answer);
202```
203
204### Aggregate Parameters
205
206`aggregate()` no longer accepts a spread, you **must** pass your aggregation pipeline as an array. The below code worked in 4.x:
207
208```javascript
209MyModel.aggregate({ $match: { isDeleted: false } }, { $skip: 10 }).exec(cb);
210```
211
212The above code does **not** work in 5.x, you **must** wrap the `$match` and `$skip` stages in an array.
213
214```javascript
215MyModel.aggregate([{ $match: { isDeleted: false } }, { $skip: 10 }]).exec(cb);
216```
217
218### Boolean Casting
219
220By default, mongoose 4 would coerce any value to a boolean without error.
221
222```javascript
223// Fine in mongoose 4, would save a doc with `boolField = true`
224const MyModel = mongoose.model('Test', new Schema({
225 boolField: Boolean
226}));
227
228MyModel.create({ boolField: 'not a boolean' });
229```
230
231Mongoose 5 only casts the following values to `true`:
232
233* `true`
234* `'true'`
235* `1`
236* `'1'`
237* `'yes'`
238
239And the following values to `false`:
240
241* `false`
242* `'false'`
243* `0`
244* `'0'`
245* `'no'`
246
247All other values will cause a `CastError`
248### Query Casting
249
250Casting for `update()`, `updateOne()`, `updateMany()`, `replaceOne()`,
251`remove()`, `deleteOne()`, and `deleteMany()` doesn't happen until `exec()`.
252This makes it easier for hooks and custom query helpers to modify data, because
253mongoose won't restructure the data you passed in until after your hooks and
254query helpers have ran. It also makes it possible to set the `overwrite` option
255_after_ passing in an update.
256
257```javascript
258// In mongoose 4.x, this becomes `{ $set: { name: 'Baz' } }` despite the `overwrite`
259// In mongoose 5.x, this overwrite is respected and the first document with
260// `name = 'Bar'` will be replaced with `{ name: 'Baz' }`
261User.where({ name: 'Bar' }).update({ name: 'Baz' }).setOptions({ overwrite: true });
262```
263
264### Post Save Hooks Get Flow Control
265
266Post hooks now get flow control, which means async post save hooks and child document post save hooks execute **before** your `save()` callback.
267
268```javsscript
269const ChildModelSchema = new mongoose.Schema({
270 text: {
271 type: String
272 }
273});
274ChildModelSchema.post('save', function(doc) {
275 // In mongoose 5.x this will print **before** the `console.log()`
276 // in the `save()` callback. In mongoose 4.x this was reversed.
277 console.log('Child post save');
278});
279const ParentModelSchema = new mongoose.Schema({
280 children: [ChildModelSchema]
281});
282
283const Model = mongoose.model('Parent', ParentModelSchema);
284const m = new Model({ children: [{ text: 'test' }] });
285m.save(function() {
286 // In mongoose 5.xm this prints **after** the "Child post save" message.
287 console.log('Save callback');
288});
289```
290
291### The `$pushAll` Operator
292
293`$pushAll` is no longer supported and no longer used internally for `save()`, since it has been [deprecated since MongoDB 2.4](https://docs.mongodb.com/manual/reference/operator/update/pushAll/). Use `$push` with `$each` instead.
294
295### Always Use Forward Key Order
296
297The `retainKeyOrder` option was removed, mongoose will now always retain the same key position when cloning objects. If you have queries or indexes that rely on reverse key order, you will have to change them.
298
299### Run setters on queries
300
301Setters now run on queries by default, and the old `runSettersOnQuery` option
302has been removed.
303
304```javascript
305const schema = new Schema({
306 email: { type: String, lowercase: true }
307});
308const Model = mongoose.model('Test', schema);
309Model.find({ email: 'FOO@BAR.BAZ' }); // Converted to `find({ email: 'foo@bar.baz' })`
310```
311
312### Pre-compiled Browser Bundle
313
314We no longer have a pre-compiled version of mongoose for the browser. If you want to use mongoose schemas in the browser, you need to build your own bundle with browserify/webpack.
315
316### Save Errors
317
318The `saveErrorIfNotFound` option was removed, mongoose will now always error out from `save()` if the underlying document was not found
319
320### Init hook signatures
321
322`init` hooks are now fully synchronous and do not receive `next()` as a parameter.
323
324`Document.prototype.init()` no longer takes a callback as a parameter. It
325was always synchronous, just had a callback for legacy reasons.
326
327### `numAffected` and `save()`
328
329`doc.save()` no longer passes `numAffected` as a 3rd param to its callback.
330
331### `remove()` and debouncing
332
333`doc.remove()` no longer debounces
334
335### `getPromiseConstructor()`
336
337`getPromiseConstructor()` is gone, just use `mongoose.Promise`.
338
339### Passing Parameters from Pre Hooks
340
341You cannot pass parameters to the next pre middleware in the chain using `next()` in mongoose 5.x. In mongoose 4, `next('Test')` in pre middleware would call the
342next middleware with 'Test' as a parameter. Mongoose 5.x has removed support for this.
343
344### `required` validator for arrays
345
346In mongoose 5 the `required` validator only verifies if the value is an array. That is, it will **not** fail for _empty_ arrays as it would in mongoose 4.
347
348### debug output defaults to stdout instead of stderr
349
350In mongoose 5 the default debug function uses `console.info()` to display messages instead of `console.error()`.
351
352### Overwriting filter properties
353
354In Mongoose 4.x, overwriting a filter property that's a primitive with one that is an object would silently fail. For example, the below code would ignore the `where()` and be equivalent to `Sport.find({ name: 'baseball' })`
355
356```javascript
357Sport.find({ name: 'baseball' }).where({name: {$ne: 'softball'}});
358```
359
360In Mongoose 5.x, the above code will correctly overwrite `'baseball'` with `{ $ne: 'softball' }`
361
362### `bulkWrite()` results
363
364Mongoose 5.x uses version 3.x of the [MongoDB Node.js driver](http://npmjs.com/package/mongodb). MongoDB driver 3.x changed the format of
365the result of [`bulkWrite()` calls](http://localhost:8088/docs/api.html#model_Model.bulkWrite) so there is no longer a top-level `nInserted`, `nModified`, etc. property. The new result object structure is [described here](http://mongodb.github.io/node-mongodb-native/3.1/api/Collection.html#~BulkWriteOpResult).
366
367```javascript
368const Model = mongoose.model('Test', new Schema({ name: String }));
369
370const res = await Model.bulkWrite([{ insertOne: { document: { name: 'test' } } }]);
371
372console.log(res);
373```
374
375In Mongoose 4.x, the above will print:
376
377```
378BulkWriteResult {
379 ok: [Getter],
380 nInserted: [Getter],
381 nUpserted: [Getter],
382 nMatched: [Getter],
383 nModified: [Getter],
384 nRemoved: [Getter],
385 getInsertedIds: [Function],
386 getUpsertedIds: [Function],
387 getUpsertedIdAt: [Function],
388 getRawResponse: [Function],
389 hasWriteErrors: [Function],
390 getWriteErrorCount: [Function],
391 getWriteErrorAt: [Function],
392 getWriteErrors: [Function],
393 getLastOp: [Function],
394 getWriteConcernError: [Function],
395 toJSON: [Function],
396 toString: [Function],
397 isOk: [Function],
398 insertedCount: 1,
399 matchedCount: 0,
400 modifiedCount: 0,
401 deletedCount: 0,
402 upsertedCount: 0,
403 upsertedIds: {},
404 insertedIds: { '0': 5be9a3101638a066702a0d38 },
405 n: 1 }
406```
407
408In Mongoose 5.x, the script will print:
409
410```
411BulkWriteResult {
412 result:
413 { ok: 1,
414 writeErrors: [],
415 writeConcernErrors: [],
416 insertedIds: [ [Object] ],
417 nInserted: 1,
418 nUpserted: 0,
419 nMatched: 0,
420 nModified: 0,
421 nRemoved: 0,
422 upserted: [],
423 lastOp: { ts: [Object], t: 1 } },
424 insertedCount: 1,
425 matchedCount: 0,
426 modifiedCount: 0,
427 deletedCount: 0,
428 upsertedCount: 0,
429 upsertedIds: {},
430 insertedIds: { '0': 5be9a1c87decfc6443dd9f18 },
431 n: 1 }
432```
\No newline at end of file