从 6.x 迁移到 7.x

从 Mongoose 6.x 迁移到 Mongoose 7.x 时,你应该注意一些向后破坏的更改。

如果你仍在使用 Mongoose 5.x,请先阅读 Mongoose 5.x 到 6.x 迁移指南 并升级到 Mongoose 6.x。

strictQuery

strictQuery 现在默认为 false。

const mySchema = new Schema({ field: Number });
const MyModel = mongoose.model('Test', mySchema);

// Mongoose will not strip out `notInSchema: 1` because `strictQuery` is false by default
const docs = await MyModel.find({ notInSchema: 1 });
// Empty array in Mongoose 7. In Mongoose 6, this would contain all documents in MyModel
docs;

已删除 remove()

文档和模型上的 remove() 方法已被删除。 请改用 deleteOne()deleteMany()

const mySchema = new Schema({ field: Number });
const MyModel = mongoose.model('Test', mySchema);

// Change this:
await MyModel.remove(filter);

// To this:
await MyModel.deleteOne(filter);
// Or this, if you want to delete multiple:
await MyModel.deleteMany(filter);

// For documents, change this:
await doc.remove();

// To this:
await doc.deleteOne();

请记住,deleteOne() 钩子默认被视为查询中间件。 因此对于中间件,请执行以下操作:

// Replace this:
schema.pre('remove', function() {
  /* ... */
});

// With this:
schema.pre('deleteOne', { document: true, query: false }, function() {
  /* ... */
});

放弃回调支持

以下函数不再接受回调。 他们总是返回 promise。

  • Aggregate.prototype.exec
  • Aggregate.prototype.explain
  • AggregationCursor.prototype.close
  • Connection.prototype.startSession
  • Connection.prototype.dropCollection
  • Connection.prototype.createCollection
  • Connection.prototype.dropDatabase
  • Connection.prototype.openUri
  • Connection.prototype.close
  • Connection.prototype.destroy
  • Document.prototype.populate
  • Document.prototype.validate
  • Mongoose.prototype.connect
  • Mongoose.prototype.createConnection
  • Model.prototype.save
  • Model.aggregate
  • Model.bulkWrite
  • Model.cleanIndexes
  • Model.countDocuments
  • Model.create
  • Model.createCollection
  • Model.createIndexes
  • Model.deleteOne
  • Model.deleteMany
  • Model.distinct
  • Model.ensureIndexes
  • Model.estimatedDocumentCount
  • Model.exists
  • Model.find
  • Model.findById
  • Model.findByIdAndUpdate
  • Model.findByIdAndReplace
  • Model.findOne
  • Model.findOneAndDelete
  • Model.findOneAndUpdate
  • Model.findOneAndRemove
  • Model.insertMany
  • Model.listIndexes
  • Model.replaceOne
  • Model.syncIndexes
  • Model.updateMany
  • Model.updateOne
  • Query.prototype.find
  • Query.prototype.findOne
  • Query.prototype.findOneAndDelete
  • Query.prototype.findOneAndUpdate
  • Query.prototype.findOneAndRemove
  • Query.prototype.findOneAndReplace
  • Query.prototype.validate
  • Query.prototype.deleteOne
  • Query.prototype.deleteMany
  • Query.prototype.exec
  • QueryCursor.prototype.close
  • QueryCursor.prototype.next

如果你将上述函数与回调一起使用,我们建议切换到 async/await,或者在异步函数不适合你时使用 Promise。 如果你需要重构遗留代码库的帮助,请使用 ChatGPT 这个工具从掌握 JS 回调到异步等待

// Before
conn.startSession(function(err, session) {
  // ...
});

// After
const session = await conn.startSession();
// Or:
conn.startSession().then(sesson => { /* ... */ });

// With error handling
try {
  await conn.startSession();
} catch (err) { /* ... */ }
// Or:
const [err, session] = await conn.startSession().then(
  session => ([null, session]),
  err => ([err, null])
);

已删除 update()

Model.update()Query.prototype.update()Document.prototype.update() 已被删除。 请改用 updateOne()

// Before
await Model.update(filter, update);
await doc.update(update);

// After
await Model.updateOne(filter, update);
await doc.updateOne(update);

鉴别器结构默认使用基本结构选项

当你使用 Model.discriminator() 时,Mongoose 现在将默认使用鉴别器基本结构的选项。 这意味着你不需要显式设置子结构选项来匹配基本结构。

const baseSchema = Schema({}, { typeKey: '$type' });
const Base = db.model('Base', baseSchema);

// In Mongoose 6.x, the `Base.discriminator()` call would throw because
// no `typeKey` option. In Mongoose 7, Mongoose uses the base schema's
// `typeKey` by default.
const childSchema = new Schema({}, {});
const Test = Base.discriminator('Child', childSchema);

Test.schema.options.typeKey; // '$type'

删除了 castForQueryWrapper,更新了 castForQuery() 签名

Mongoose 现在总是使用 3 个参数调用 SchemaType castForQuery() 方法: $conditionalvaluecontext。 如果你已经实现了定义其自己的 castForQuery() 方法的自定义结构类型,则需要按如下方式更新该方法。

// Mongoose 6.x format:
MySchemaType.prototype.castForQuery = function($conditional, value) {
  if (arguments.length === 2) {
    // Handle casting value with `$conditional` - $eq, $in, $not, etc.
  } else {
    value = $conditional;
    // Handle casting `value` with no conditional
  }
};

// Mongoose 7.x format
MySchemaType.prototype.castForQuery = function($conditional, value, context) {
  if ($conditional != null) {
    // Handle casting value with `$conditional` - $eq, $in, $not, etc.
  } else {
    // Handle casting `value` with no conditional
  }
};

复制 Schema.prototype.add() 中的架构选项

Mongoose 现在在将一个结构添加到另一个结构时复制用户定义的结构选项。 例如,下面的 childSchema 将获得 baseSchemaidtoJSON 选项。

const baseSchema = new Schema({ created: Date }, { id: true, toJSON: { virtuals: true } });
const childSchema = new Schema([baseSchema, { name: String }]);

childSchema.options.toJSON; // { virtuals: true } in Mongoose 7. undefined in Mongoose 6.

这既适用于使用结构数组创建新结构的情况,也适用于如下调用 add() 的情况。

childSchema.add(new Schema({}, { toObject: { virtuals: true } }));

childSchema.options.toObject; // { virtuals: true } in Mongoose 7. undefined in Mongoose 6.

ObjectId bsontype 现在有小写 d

ObjectIds 上的内部 _bsontype 属性等于 Mongoose 7 中的 'ObjectId',而不是 Mongoose 6 中的 'ObjectID'

const oid = new mongoose.Types.ObjectId();

oid._bsontype; // 'ObjectId' in Mongoose 7, 'ObjectID' in older versions of Mongoose

请更新任何使用 _bsontype 检查对象是否为 ObjectId 的地方。 这也可能会影响使用 Mongoose 的库。

删除了 mapReduce

MongoDB 不再支持 mapReduce,因此 Mongoose 7 不再有 Model.mapReduce() 功能。 使用聚合框架作为 mapReduce() 的替代品。

// The following no longer works in Mongoose 7.
const o = {
  map: function() {
    emit(this.author, 1);
  },
  reduce: function(k, vals) {
    return vals.length;
  }
};

await MR.mapReduce(o);

删除了对自定义 promise 库的支持

Mongoose 7 不再支持插入自定义 Promise 库。 因此,以下内容不再使 Mongoose 在 Mongoose 7 中返回 Bluebird promise。

const mongoose = require('mongoose');

// No-op on Mongoose 7
mongoose.Promise = require('bluebird');

如果你想使用 Bluebird 实现全局作用域内的所有 promise,你可以执行以下操作:

global.Promise = require('bluebird');

TypeScript 特定的更改

删除了 LeanDocument 并支持 extends Document

Mongoose 7 不再导出 LeanDocument 类型,也不再支持将 extends Document 的文档类型传递到 Model<>

// No longer supported
interface ITest extends Document {
  name?: string;
}
const Test = model<ITest>('Test', schema);

// Do this instead, no `extends Document`
interface ITest {
  name?: string;
}
const Test = model<ITest>('Test', schema);

// If you need to access the hydrated document type, use the following code
type TestDocument = ReturnType<(typeof Test)['hydrate']>;

HydratedDocument 的新参数

Mongoose 的 HydratedDocument 类型将原始文档接口转换为水合 Mongoose 文档的类型,包括虚拟、方法等。在 Mongoose 7 中,HydratedDocument 的通用参数已更改。 在 Mongoose 6 中,通用参数为:

type HydratedDocument<
  DocType,
  TMethodsAndOverrides = {},
  TVirtuals = {}
> = Document<unknown, any, DocType> &
Require_id<DocType> &
TMethodsAndOverrides &
TVirtuals;

在 Mongoose 7 中,新类型如下。

type HydratedDocument<
  DocType,
  TOverrides = {},
  TQueryHelpers = {}
> = Document<unknown, TQueryHelpers, DocType> &
Require_id<DocType> &
TOverrides;

在 Mongoose 7 中,第一个参数是原始文档接口,第二个参数是任何特定于文档的覆盖(通常是虚函数和方法),第三个参数是与文档模型关联的任何查询辅助程序。

主要区别在于,在 Mongoose 6 中,第三个通用参数是文档的虚拟参数。 在 Mongoose 7 中,第三个通用参数是文档的查询助手。

// Mongoose 6 version:
type UserDocument = HydratedDocument<TUser, TUserMethods, TUserVirtuals>;

// Mongoose 7:
type UserDocument = HydratedDocument<TUser, TUserMethods & TUserVirtuals, TUserQueryHelpers>;