-User.discriminator('author', authorSchema);
-User.discriminator('author', authorSchema.clone());
+User.discriminator('author', authorSchema);
+User.discriminator('author', authorSchema.clone());
-User.discriminator('author', authorSchema, { clone: false });
+User.discriminator('author', authorSchema, { clone: false });
+
+
+
+In Mongoose 5, mongoose.isValidObjectId()
returned false
for values like numbers, which was inconsistent with the MongoDB driver's ObjectId.isValid()
function.
+Technically, any JavaScript number can be converted to a MongoDB ObjectId.
+In Mongoose 6, mongoose.isValidObjectId()
is just a wrapper for mongoose.Types.ObjectId.isValid()
for consistency.
+Mongoose 6.2.5 now includes a mongoose.isObjectIdOrHexString()
function, which does a better job of capturing the more common use case for isValidObjectId()
: is the given value an ObjectId
instance or a 24 character hex string representing an ObjectId
?
+
+
+mongoose.isValidObjectId(new mongoose.Types.ObjectId());
+mongoose.isValidObjectId('0123456789ab');
+mongoose.isValidObjectId(6);
+mongoose.isValidObjectId(new User({ name: 'test' }));
+
+
+
+mongoose.isObjectIdOrHexString(new mongoose.Types.ObjectId());
+mongoose.isObjectIdOrHexString('62261a65d66c6be0a63c051f');
+mongoose.isObjectIdOrHexString('0123456789ab');
+mongoose.isObjectIdOrHexString(6);
+
Mongoose now saves objects with keys in the order the keys are specified in the schema, not in the user-defined object. So whether Object.keys(new User({ name: String, email: String }).toObject()
is ['name', 'email']
or ['email', 'name']
depends on the order name
and email
are defined in your schema.
-const schema = new Schema({
+const schema = new Schema({
profile: {
name: {
- first: String,
- last: String
+ first: String,
+ last: String
}
}
});
-const Test = db.model('Test', schema);
+const Test = db.model('Test', schema);
-const doc = new Test({
- profile: { name: { last: 'Musashi', first: 'Miyamoto' } }
+const doc = new Test({
+ profile: { name: { last: 'Musashi', first: 'Miyamoto' } }
});
-
-
-assert.deepEqual(Object.keys(doc.toObject().profile.name), ['first', 'last']);
-
+
+
+assert.deepEqual(Object.keys(doc.toObject().profile.name), ['first', 'last']);
+
+
Mongoose 6 introduces a new sanitizeFilter
option to globals and queries that defends against query selector injection attacks. If you enable sanitizeFilter
, Mongoose will wrap any object in the query filter in a $eq
:
-
+
-await Test.find({ username: 'val', pwd: { $ne: null } }).setOptions({ sanitizeFilter: true });
+await Test.find({ username: 'val', pwd: { $ne: null } }).setOptions({ sanitizeFilter: true });
+
To explicitly allow a query selector, use mongoose.trusted()
:
-await Test.find({ username: 'val', pwd: mongoose.trusted({ $ne: null }) }).setOptions({ sanitizeFilter: true });
+await Test.find({ username: 'val', pwd: mongoose.trusted({ $ne: null }) }).setOptions({ sanitizeFilter: true });
+
+
+
+In Mongoose 5.x, setting a key to undefined
in an update operation was equivalent to setting it to null
.
+let res = await Test.findOneAndUpdate({}, { $set: { name: undefined } }, { new: true });
+
+res.name;
+
+
+
+res = await Test.findOneAndUpdate({}, { $set: { name: undefined } }, { new: true, omitUndefined: true });
+
+Mongoose 5.x supported an omitUndefined
option to strip out undefined
keys.
+In Mongoose 6.x, the omitUndefined
option has been removed, and Mongoose will always strip out undefined keys.
+
+
+const res = await Test.findOneAndUpdate({}, { $set: { name: undefined } }, { new: true });
+
+The only workaround is to explicitly set properties to null
in your updates:
+const res = await Test.findOneAndUpdate({}, { $set: { name: null } }, { new: true });
+
-Mongoose now passes the document as the first parameter to default
functions, which is helpful for using arrow functions with defaults. This may affect you if you pass a function that expects different parameters to default
, like default: mongoose.Types.ObjectId
. See gh-9633
-const schema = new Schema({
- name: String,
- age: Number,
+Mongoose now passes the document as the first parameter to default
functions, which is helpful for using arrow functions with defaults.
+This may affect you if you pass a function that expects different parameters to default
, like default: mongoose.Types.ObjectId
. See gh-9633. If you're passing a default function that does not utilize the document, change default: myFunction
to default: () => myFunction()
to avoid accidentally passing parameters that potentially change the behavior.
+const schema = new Schema({
+ name: String,
+ age: Number,
canVote: {
- type: Boolean,
+ type: Boolean,
- default: doc => doc.age >= 18
+ default: doc => doc.age >= 18
}
-});
+});
+
Mongoose arrays are now ES6 proxies. You no longer need to markModified()
after setting an array index directly.
-const post = await BlogPost.findOne();
+const post = await BlogPost.findOne();
-post.tags[0] = 'javascript';
-await post.save();
-
+post.tags[0] = 'javascript';
+await post.save();
+
+
Schema paths declared with type: { name: String }
become single nested subdocs in Mongoose 6, as opposed to Mixed in Mongoose 5. This removes the need for the typePojoToMixed
option. See gh-7181.
-const schema = new Schema({
- foo: { type: { name: String } }
-});
-
+const schema = new Schema({
+ foo: { type: { name: String } }
+});
+
+
Mongoose now throws an error if you populate()
a path that isn't defined in your schema. This is only for cases when we can infer the local schema, like when you use Query#populate()
, not when you call Model.populate()
on a POJO. See gh-5124.
-
+
When populating a subdocument with a function ref
or refPath
, this
is now the subdocument being populated, not the top-level document. See #8469.
-const schema = new Schema({
+const schema = new Schema({
works: [{
- modelId: String,
+ modelId: String,
data: {
- type: mongoose.ObjectId,
- ref: function(doc) {
+ type: mongoose.ObjectId,
+ ref: function(doc) {
- return doc.modelId;
+ return doc.modelId;
}
}
}]
-});
+});
+
Using save
, isNew
, and other Mongoose reserved names as schema path names now triggers a warning, not an error. You can suppress the warning by setting the supressReservedKeysWarning
in your schema options: new Schema({ save: String }, { supressReservedKeysWarning: true })
. Keep in mind that this may break plugins that rely on these reserved names.
@@ -216,10 +299,10 @@
Aggregate#cursor()
now returns an AggregationCursor instance to be consistent with Query#cursor()
. You no longer need to do Model.aggregate(pipeline).cursor().exec()
to get an aggregation cursor, just Model.aggregate(pipeline).cursor()
.
-
+
autoCreate
is true
by default unless readPreference is secondary or secondaryPreferred, which means Mongoose will attempt to create every model's underlying collection before creating indexes. If readPreference is secondary or secondaryPreferred, Mongoose will default to false
for both autoCreate
and autoIndex
because both createCollection()
and createIndex()
will fail when connected to a secondary.
-
+
The context
option for queries has been removed. Now Mongoose always uses context = 'query'
.
@@ -229,41 +312,110 @@
+
Document#populate()
now returns a promise and is now no longer chainable.
- Replace
await doc.populate('path1').populate('path2').execPopulate();
with await doc.populate(['path1', 'path2']);
-- Replace
await doc.populate('path1', 'select1').populate('path2', 'select2').execPopulate();
withawait doc.populate([{path: 'path1', select: 'select1'}, {path: 'path2', select: 'select2'}]);
+ - Replace
await doc.populate('path1', 'select1').populate('path2', 'select2').execPopulate();
withawait doc.populate([{path: 'path1', select: 'select1'}, {path: 'path2', select: 'select2'}]);
+
-
+
await Model.create([])
in v6.0 returns an empty array when provided an empty array, in v5.0 it used to return undefined
. If any of your code is checking whether the output is undefined
or not, you need to modify it with the assumption that await Model.create(...)
will always return an array if provided an array.
doc.set({ child: { age: 21 } })
now works the same whether child
is a nested path or a subdocument: Mongoose will overwrite the value of child
. In Mongoose 5, this operation would merge child
if child
was a nested path.
-
+
Mongoose now adds a valueOf()
function to ObjectIds. This means you can now use ==
to compare an ObjectId against a string.
-const a = ObjectId('6143b55ac9a762738b15d4f0');
+const a = ObjectId('6143b55ac9a762738b15d4f0');
-a == '6143b55ac9a762738b15d4f0';
-
+a == '6143b55ac9a762738b15d4f0';
+
+
If you set timestamps: true
, Mongoose will now make the createdAt
property immutable
. See gh-10139
-
+
isAsync
is no longer an option for validate
. Use an async function
instead.
-
+
safe
is no longer an option for schemas, queries, or save()
. Use writeConcern
instead.
+
+
+Mongoose now calls setter functions with priorValue
as the 2nd parameter, rather than schemaType
in Mongoose 5.
+const userSchema = new Schema({
+ name: {
+ type: String,
+ trimStart: true,
+ set: trimStartSetter
+ }
+});
+
+
+function trimStartSetter(val, priorValue, schemaType) {
+ if (schemaType.options.trimStart && typeof val === 'string') {
+ return val.trimStart();
+ }
+ return val;
+}
+
+const User = mongoose.model('User', userSchema);
+
+const user = new User({ name: 'Robert Martin' });
+console.log(user.name);
+
+
+
+This change was technically released with 5.10.5, but caused issues for users migrating from 5.9.x to 6.x.
+In Mongoose < 5.10.5
, toObject()
and toJSON()
would use the top-level schema's minimize
option by default.
+const child = new Schema({ thing: Schema.Types.Mixed });
+const parent = new Schema({ child }, { minimize: false });
+const Parent = model('Parent', parent);
+const p = new Parent({ child: { thing: {} } });
+
+
+
+console.log(p.toObject());
+
+As a workaround, you can either explicitly pass minimize
to toObject()
or toJSON()
:
+console.log(p.toObject({ minimize: false }));
+
+Or define the child
schema inline (Mongoose 6 only) to inherit the parent's minimize
option.
+const parent = new Schema({
+
+ child: { type: { thing: Schema.Types.Mixed } }
+}, { minimize: false });
+
+
+
+In Mongoose 5, calling populate()
on a mixed type or other path with no ref
would fall back to using the query's model.
+const testSchema = new mongoose.Schema({
+ data: String,
+ parents: Array
+});
+
+const Test = mongoose.model('Test', testSchema);
+
+
+await Test.findOne().populate('parents');
+
+await Test.findOne().populate({ path: 'parents', model: Test });
+
+In Mongoose 6, populating a path with no ref
, refPath
, or model
is a no-op.
+
+await Test.findOne().populate('parents');
+
The Schema
class now takes 3 generic params instead of 4. The 3rd generic param, SchemaDefinitionType
, is now the same as the 1st generic param DocType
. Replace new Schema<UserDocument, UserModel, User>(schemaDefinition)
with new Schema<UserDocument, UserModel>(schemaDefinition)
+Types.ObjectId
is now a class, which means you can no longer omit new
when creating a new ObjectId using new mongoose.Types.ObjectId()
.
+Currently, you can still omit new
in JavaScript, but you must put new
in TypeScript.
The following legacy types have been removed:
ModelUpdateOptions
@@ -276,56 +428,4 @@
SchemaTypeOpts
ConnectionOptions
-