Миграция с 5.x на 6.x
Обратите внимание: мы планируем прекратить поддержку Mongoose 5 с 1 марта 2024 года. Пожалуйста, ознакомьтесь с нашим руководством по поддержке версий.
Существуют некоторые несовместимые изменения, о которых следует знать при миграции с Mongoose 5.x на Mongoose 6.x.
Если вы всё ещё используете Mongoose 4.x, пожалуйста, прочитайте руководство по миграции с Mongoose 4.x на 5.x и обновитесь до Mongoose 5.x сначала.
- Требования к версии
- MongoDB Driver 4.0
- Больше нет параметров предупреждения о устаревании
- Метод
asPromise()для соединений mongoose.connect()возвращает промис- Повторное выполнение запроса
Model.exists()возвращает документ lean вместо булевого значенияstrictQueryтеперь по умолчанию равенstrict- MongoError теперь MongoServerError
- Упрощенный
isValidObjectId()и отдельныйisObjectIdOrHexString() - По умолчанию клонировать схемы дискриминаторов
- Порядок ключей документа, определённый схемой
sanitizeFilterиtrusted()- Удалён
omitUndefined: Mongoose теперь удаляет ключиundefinedв обновлениях вместо установки их вnull - Параметр документа для функций по умолчанию
- Массивы являются прокси
typePojoToMixedstrictPopulate()- Контекст функции
refподдокумента - Предупреждение о зарезервированных именах схемы
- Пути поддокументов
- Создание курсоров агрегации
autoCreateпо умолчанию равноtrue- Больше нет
context: 'query' - Пользовательские валидаторы с заполненными путями
- Событие отключения с репликатными наборами
- Удален
execPopulate() create()с пустым массивом- Удалено объединение вложенных путей
- ObjectId
valueOf() - Неизменяемый
createdAt - Удален валидатор
isAsync - Удалён
safe - Параметры SchemaType
setтеперь используютpriorValueв качестве второго параметра вместоself - Нет модели по умолчанию для
Query.prototype.populate() toObject()иtoJSON()используют вложенную схемуminimize- Изменения в TypeScript
- Удалены параметры
reconnectTriesиreconnectInterval
Требования к версии
Mongoose теперь требует Node.js >= 12.0.0. Mongoose всё ещё поддерживает версии MongoDB сервера вплоть до 3.0.0.
MongoDB Driver 4.0
Mongoose теперь использует версию 4.x драйвера MongoDB Node driver. Подробную информацию см. в руководстве по миграции MongoDB Node драйвера. Ниже приведены некоторые из наиболее важных изменений:
- MongoDB Driver 4.x написан на TypeScript и имеет свои определения типов TypeScript. Они могут конфликтовать с
@types/mongodb, поэтому, если у вас возникают ошибки компилятора TypeScript, убедитесь, что вы обновились до последней версии@types/mongodb, которая является пустым заглушкой. - Параметр
poolSizeдля соединений был заменён наminPoolSizeиmaxPoolSize. ПараметрpoolSizeMongoose 5.x эквивалентен параметру Mongoose 6maxPoolSize. Значение по умолчанию дляmaxPoolSizeувеличено до 100. - Результат
updateOne()иupdateMany()теперь отличается. - Результат
deleteOne()иdeleteMany()больше не имеет свойстваn
const res = await TestModel.updateMany({}, { someProperty: 'someValue' });
res.matchedCount; // Number of documents that were found that match the filter. Replaces `res.n`
res.modifiedCount; // Number of documents modified. Replaces `res.nModified`
res.upsertedCount; // Number of documents upserted. Replaces `res.upserted`
const res = await TestModel.deleteMany({});
// In Mongoose 6: `{ acknowledged: true, deletedCount: 2 }`
// In Mongoose 5: `{ n: 2, ok: 1, deletedCount: 2 }`
res;
res.deletedCount; // Number of documents that were deleted. Replaces `res.n`
Больше нет параметров предупреждения о устаревании
useNewUrlParser, useUnifiedTopology, useFindAndModify, и useCreateIndex больше не поддерживаются в качестве параметров. Mongoose 6 всегда ведёт себя так, как будто useNewUrlParser, useUnifiedTopology, и useCreateIndex равны true, а useFindAndModify равно false. Пожалуйста, удалите эти параметры из вашего кода.
// No longer necessary:
mongoose.set('useFindAndModify', false);
await mongoose.connect('mongodb://127.0.0.1:27017/test', {
useNewUrlParser: true, // <-- no longer necessary
useUnifiedTopology: true // <-- no longer necessary
});
Метод asPromise() для соединений
Соединения Mongoose больше не являются thenable. Это означает, что await mongoose.createConnection(uri) больше не ждёт подключения Mongoose. Используйте mongoose.createConnection(uri).asPromise() вместо этого. См. #8810.
// The below no longer works in Mongoose 6
await mongoose.createConnection(uri);
// Do this instead
await mongoose.createConnection(uri).asPromise();
mongoose.connect() возвращает промис
Функция mongoose.connect() теперь всегда возвращает промис, а **не** экземпляр Mongoose.
Повторное выполнение запроса
Mongoose больше не позволяет дважды выполнять один и тот же объект запроса. Если вы это сделаете, вы получите ошибку Query was already executed. Выполнение одного и того же экземпляра запроса дважды обычно указывает на смешение обратных вызовов и промисов. Но если вам нужно выполнить один и тот же запрос дважды, вы можете вызвать Query#clone() для клонирования запроса и повторного выполнения. См. gh-7398
// Results in 'Query was already executed' error, because technically this `find()` query executes twice.
await Model.find({}, function(err, result) {});
const q = Model.find();
await q;
await q.clone(); // Can `clone()` the query to allow executing the query again
Model.exists(...) теперь возвращает документ lean вместо булевого значения
// in Mongoose 5.x, `existingUser` used to be a boolean
// now `existingUser` will be either `{ _id: ObjectId(...) }` or `null`.
const existingUser = await User.exists({ name: 'John' });
if (existingUser) {
console.log(existingUser._id);
}
strictQuery теперь по умолчанию равен strict
Mongoose больше не поддерживает параметр Начиная с Mongoose 6.0.10, мы вернули параметр strictQuery. Теперь вы должны использовать strict.strictQuery. Однако strictQuery привязан к strict по умолчанию. Это означает, что Mongoose по умолчанию будет фильтровать свойства фильтра запроса, которые отсутствуют в схеме.
const userSchema = new Schema({ name: String });
const User = mongoose.model('User', userSchema);
// By default, this is equivalent to `User.find()` because Mongoose filters out `notInSchema`
await User.find({ notInSchema: 1 });
// Set `strictQuery: false` to opt in to filtering by properties that aren't in the schema
await User.find({ notInSchema: 1 }, null, { strictQuery: false });
// equivalent:
await User.find({ notInSchema: 1 }).setOptions({ strictQuery: false });
Вы также можете отключить strictQuery глобально, чтобы переопределить это:
mongoose.set('strictQuery', false);
MongoError теперь MongoServerError
В MongoDB Node.js Driver v4.x, 'MongoError' теперь 'MongoServerError'. Пожалуйста, измените любой код, зависящий от жёстко закодированной строки 'MongoError'.
По умолчанию клонировать схемы дискриминаторов
Mongoose теперь клонирует схемы дискриминаторов по умолчанию. Это означает, что вам нужно передать { clone: false } в discriminator() при использовании рекурсивных вложенных дискриминаторов.
// In Mongoose 6, these two are equivalent:
User.discriminator('author', authorSchema);
User.discriminator('author', authorSchema.clone());
// To opt out if `clone()` is causing issues, pass `clone: false`
User.discriminator('author', authorSchema, { clone: false });
Упрощенный isValidObjectId() и отдельный isObjectIdOrHexString()
В Mongoose 5, mongoose.isValidObjectId() возвращал false для значений типа числа, что было несовместимо с функцией ObjectId.isValid() драйвера MongoDB. Технически, любое число JavaScript может быть преобразовано в MongoDB ObjectId.
В Mongoose 6, mongoose.isValidObjectId() является просто обёрткой для mongoose.Types.ObjectId.isValid() для обеспечения согласованности.
Mongoose 6.2.5 теперь включает функцию mongoose.isObjectIdOrHexString(), которая лучше справляется с более распространённым случаем использования isValidObjectId(): является ли данное значение экземпляром ObjectId или строкой шестнадцатеричного кода длиной 24 символа, представляющей ObjectId?
// `isValidObjectId()` returns `true` for some surprising values, because these
// values are _technically_ ObjectId representations
mongoose.isValidObjectId(new mongoose.Types.ObjectId()); // true
mongoose.isValidObjectId('0123456789ab'); // true
mongoose.isValidObjectId(6); // true
mongoose.isValidObjectId(new User({ name: 'test' })); // true
// `isObjectIdOrHexString()` instead only returns `true` for ObjectIds and 24
// character hex strings.
mongoose.isObjectIdOrHexString(new mongoose.Types.ObjectId()); // true
mongoose.isObjectIdOrHexString('62261a65d66c6be0a63c051f'); // true
mongoose.isObjectIdOrHexString('0123456789ab'); // false
mongoose.isObjectIdOrHexString(6); // false
Порядок ключей документа, определённый схемой
Mongoose теперь сохраняет объекты с ключами в порядке, в котором они указаны в схеме, а не в порядке пользовательского объекта. Таким образом, будет ли Object.keys(new User({ name: String, email: String }).toObject() ['name', 'email'] или ['email', 'name'] зависит от порядка, в котором name и email определены в вашей схеме.
const schema = new Schema({
profile: {
name: {
first: String,
last: String
}
}
});
const Test = db.model('Test', schema);
const doc = new Test({
profile: { name: { last: 'Musashi', first: 'Miyamoto' } }
});
// Note that 'first' comes before 'last', even though the argument to `new Test()` flips the key order.
// Mongoose uses the schema's key order, not the provided objects' key order.
assert.deepEqual(Object.keys(doc.toObject().profile.name), ['first', 'last']);
sanitizeFilter и trusted()
Mongoose 6 вводит новый параметр sanitizeFilter для глобальных параметров и запросов, защищающий от атак на инъекции селекторов запроса. Если вы включите sanitizeFilter, Mongoose обернёт любой объект в фильтре запроса в $eq:
// Mongoose will convert this filter into `{ username: 'val', pwd: { $eq: { $ne: null } } }`, preventing
// a query selector injection.
await Test.find({ username: 'val', pwd: { $ne: null } }).setOptions({ sanitizeFilter: true });
Для явного разрешения селектора запроса используйте mongoose.trusted():
// `mongoose.trusted()` allows query selectors through
await Test.find({ username: 'val', pwd: mongoose.trusted({ $ne: null }) }).setOptions({ sanitizeFilter: true });
Удален omitUndefined: Mongoose теперь удаляет ключи undefined в обновлениях вместо установки их в null
В Mongoose 5.x установка ключа в undefined в операции обновления была эквивалентна установке его в null.
let res = await Test.findOneAndUpdate({}, { $set: { name: undefined } }, { new: true });
res.name; // `null` in Mongoose 5.x
// Equivalent to `findOneAndUpdate({}, {}, { new: true })` because `omitUndefined` will
// remove `name: undefined`
res = await Test.findOneAndUpdate({}, { $set: { name: undefined } }, { new: true, omitUndefined: true });
Mongoose 5.x поддерживал параметр omitUndefined для удаления ключей undefined. В Mongoose 6.x параметр omitUndefined был удалён, и Mongoose всегда удаляет ключи undefined.
// In Mongoose 6, equivalent to `findOneAndUpdate({}, {}, { new: true })` because Mongoose will
// remove `name: undefined`
const res = await Test.findOneAndUpdate({}, { $set: { name: undefined } }, { new: true });
Единственным вариантом является явная установка свойств в null в ваших обновлениях:
const res = await Test.findOneAndUpdate({}, { $set: { name: null } }, { new: true });
Параметр документа для функций по умолчанию
Библиотека Mongoose теперь передает документ в качестве первого параметра функциям default, что полезно для использования стрелочных функций стрелочных функций с параметрами по умолчанию.
Это может повлиять на ваш код, если вы передаете функцию, ожидающую другие параметры в default, например, default: mongoose.Types.ObjectId. См. gh-9633. Если вы передаете функцию по умолчанию, которая не использует документ, измените default: myFunction на default: () => myFunction(), чтобы избежать случайной передачи параметров, которые могут изменить поведение.
const schema = new Schema({
name: String,
age: Number,
canVote: {
type: Boolean,
// Default functions now receive a `doc` parameter, helpful for arrow functions
default: doc => doc.age >= 18
}
});
Массивы как прокси
Массивы Mongoose теперь являются прокси ES6. Вам больше не нужно markModified() после непосредственного задания индекса массива.
const post = await BlogPost.findOne();
post.tags[0] = 'javascript';
await post.save(); // Works, no need for `markModified()`!
typePojoToMixed
Пути схем, объявленные с помощью type: { name: String }, теперь становятся отдельными вложенными поддокументами в Mongoose 6, в отличие от Mixed в Mongoose 5. Это убирает необходимость использования опции typePojoToMixed. См. gh-7181.
// In Mongoose 6, the below makes `foo` into a subdocument with a `name` property.
// In Mongoose 5, the below would make `foo` a `Mixed` type, _unless_ you set `typePojoToMixed: true`.
const schema = new Schema({
foo: { type: { name: String } }
});
strictPopulate()
Теперь Mongoose выдает ошибку, если вы populate() путь, который не определен в вашей схеме. Это относится только к случаям, когда локальная схема может быть определена, например, при использовании Query#populate(), а **не** когда вы вызываете Model.populate() для POJO. См. gh-5124.
Контекст функции ссылки на поддокумент
При популяции поддокумента с помощью функции ref или refPath, this теперь представляет собой поддокумент, который популируется, а не основной документ. См. #8469.
const schema = new Schema({
works: [{
modelId: String,
data: {
type: mongoose.ObjectId,
ref: function(doc) {
// In Mongoose 6, `doc` is the array element, so you can access `modelId`.
// In Mongoose 5, `doc` was the top-level document.
return doc.modelId;
}
}
}]
});
Предупреждение о зарезервированных именах схемы
Использование save, isNew, и других зарезервированных имен Mongoose в качестве имён путей схемы теперь вызывает предупреждение, а не ошибку. Вы можете отключить это предупреждение, установив опцию suppressReservedKeysWarning в ваших опциях схемы: new Schema({ save: String }, { suppressReservedKeysWarning: true }). Имейте в виду, что это может нарушить работу плагинов, которые полагаются на эти зарезервированные имена.
Пути поддокументов
Вложенные поддокументы переименованы в «пути поддокументов». Таким образом, SchemaSingleNestedOptions теперь SchemaSubdocumentOptions, а mongoose.Schema.Types.Embedded теперь mongoose.Schema.Types.Subdocument. См. gh-10419
Создание курсоров агрегации
Aggregate#cursor() теперь возвращает экземпляр AggregationCursor, чтобы быть согласованным с Query#cursor(). Вам больше не нужно Model.aggregate(pipeline).cursor().exec() для получения курсора агрегации, достаточно Model.aggregate(pipeline).cursor().
autoCreate По умолчанию true
autoCreate равно true по умолчанию, **кроме** случаев, когда readPreference является secondary или secondaryPreferred, что означает, что Mongoose попытается создать базу данных каждой модели перед созданием индексов. Если readPreference является secondary или secondaryPreferred, Mongoose по умолчанию установит false как для autoCreate, так и для autoIndex, так как createCollection() и createIndex() не будут работать при подключении к secondary серверу.
Больше нет context: 'query'
Опция context для запросов была удалена. Теперь Mongoose всегда использует context = 'query'.
Пользовательские валидаторы с популированными путями
Mongoose 6 всегда вызывает валидаторы с депопулированными путями (т.е. с идентификатором, а не с самим документом). В Mongoose 5 Mongoose вызывал валидаторы с популированным документом, если путь был популирован. См. #8042
Событие отключения с репликатными наборами
При подключении к репликантному набору соединения теперь генерируют событие «отключено», когда соединение с первичным сервером потеряно. В Mongoose 5 соединения генерировали событие «отключено» только при потере связи со всеми членами репликативного набора.
Однако, Mongoose 6 **не** буферизирует команды во время отключения соединения. Поэтому вы все еще можете успешно выполнять команды, такие как запросы с readPreference = 'secondary', даже если соединение Mongoose находится в состоянии отключения.
Удалена функция execPopulate()
Document#populate() теперь возвращает промис и больше не является цепочечным.
-
Замените
await doc.populate('path1').populate('path2').execPopulate();наawait doc.populate(['path1', 'path2']); -
Замените
await doc.populate('path1', 'select1').populate('path2', 'select2').execPopulate();наawait doc.populate([{path: 'path1', select: 'select1'}, {path: 'path2', select: 'select2'}]);
create() с пустым массивом
await Model.create([]) в версии 6.0 возвращает пустой массив при передаче пустого массива, а в версии 5.0 возвращало undefined. Если ваш код проверяет, является ли результат undefined, вам необходимо изменить его, предполагая, что await Model.create(...) всегда возвращает массив, если ему передается массив.
Удаление слияния вложенных путей
doc.set({ child: { age: 21 } }) теперь работает одинаково, независимо от того, является ли child вложенным путем или поддокументом: Mongoose перезапишет значение child. В Mongoose 5 данная операция объединяла child, если child был вложенным путем.
ObjectId valueOf()
Mongoose теперь добавляет функцию valueOf() в ObjectIds. Это означает, что теперь вы можете использовать == для сравнения ObjectId со строкой.
const a = ObjectId('6143b55ac9a762738b15d4f0');
a == '6143b55ac9a762738b15d4f0'; // true
Неизменяемое свойство createdAt
Если вы установите timestamps: true, Mongoose теперь сделает свойство createdAt immutable. См. gh-10139
Удален валидатор isAsync
isAsync больше не является опцией для validate. Используйте async function вместо этого.
Удалена опция safe
safe больше не является опцией для схем, запросов или save(). Используйте writeConcern вместо этого.
Параметры SchemaType set
Mongoose теперь вызывает функции-сеттеры с priorValue в качестве второго параметра, а не schemaType, как в Mongoose 5.
const userSchema = new Schema({
name: {
type: String,
trimStart: true,
set: trimStartSetter
}
});
// in v5.x the parameters were (value, schemaType), in v6.x the parameters are (value, priorValue, schemaType).
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); // 'robert martin'
toObject() и toJSON() Используют вложенную схему minimize
Это изменение фактически было выпущено с версией 5.10.5, но вызвало проблемы для пользователей, мигрирующих из 5.9.x в 6.x. В Mongoose < 5.10.5, toObject() и toJSON() по умолчанию использовали опцию minimize основной схемы.
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: {} } });
// In v5.10.4, would contain `child.thing` because `toObject()` uses `parent` schema's `minimize` option
// In `>= 5.10.5`, `child.thing` is omitted because `child` schema has `minimize: true`
console.log(p.toObject());
В качестве обходного решения вы можете явно передать minimize в toObject() или toJSON():
console.log(p.toObject({ minimize: false }));
Или определить схему child inline (только в Mongoose 6), чтобы унаследовать опцию minimize родительской схемы.
const parent = new Schema({
// Implicitly creates a new schema with the top-level schema's `minimize` option.
child: { type: { thing: Schema.Types.Mixed } }
}, { minimize: false });
Отсутствует модель по умолчанию для Query.prototype.populate()
В Mongoose 5 вызов populate() для типа Mixed или другого пути без ref использовал модель запроса по умолчанию.
const testSchema = new mongoose.Schema({
data: String,
parents: Array // Array of mixed
});
const Test = mongoose.model('Test', testSchema);
// The below `populate()`...
await Test.findOne().populate('parents');
// Is a shorthand for the following populate in Mongoose 5
await Test.findOne().populate({ path: 'parents', model: Test });
В Mongoose 6 популяция пути без ref, refPath, или model является пустой операцией.
// The below `populate()` does nothing.
await Test.findOne().populate('parents');
Изменения в TypeScript
Класс Schema теперь принимает 3 параметра дженерика вместо 4. Третий параметр дженерика, SchemaDefinitionType, теперь такой же, как и первый параметр дженерика DocType. Замените new Schema<UserDocument, UserModel, User>(schemaDefinition) на new Schema<UserDocument, UserModel>(schemaDefinition)
Types.ObjectId теперь является классом, что означает, что вы больше не можете опускать new при создании нового ObjectId с помощью new mongoose.Types.ObjectId(). В JavaScript вы по-прежнему можете опустить new, но в TypeScript вы **обязательно** должны использовать new
Следующие устаревшие типы были удалены:
ModelUpdateOptionsDocumentQueryHookSyncCallbackHookAsyncCallbackHookErrorCallbackHookNextFunctionHookDoneFunctionSchemaTypeOptsConnectionOptions
Mongoose 6 определяет тип документа для this в виртуальных геттерах и сеттерах. В Mongoose 5.x, this будет any в следующем коде.
schema.virtual('myVirtual').get(function() {
this; // any in Mongoose 5.x
});
В Mongoose 6, this будет установлен на тип документа.
const schema = new Schema({ name: String });
schema.virtual('myVirtual').get(function() {
this.name; // string
});
Удалены опции reconnectTries и reconnectInterval
Опции reconnectTries и reconnectInterval были удалены, так как они больше не нужны.
Драйвер MongoDB для Node всегда будет пытаться повторить любую операцию до serverSelectionTimeoutMS, даже если MongoDB недоступен в течение длительного периода времени. Поэтому он никогда не исчерпает попытки повтора или не попробует переподключиться к MongoDB.
© 2010 LearnBoost
Licensed under the MIT License.
https://mongoosejs.com/docs/migrating_to_6.html