7.x から 8.x への移行
Mongoose 7.x から Mongoose 8.x へ移行する際には、いくつかの後方互換性のない変更点に注意する必要があります。
Mongoose 6.x 以前のバージョンを使用している場合は、Mongoose 6.x から 7.x への移行ガイド を読んで、Mongoose 8 にアップグレードする前に Mongoose 7.x にアップグレードしてください。
Mongoose 8 にアップグレードする前に、MongoDB Node.js ドライバーの v6.0.0 リリースノート を確認することをお勧めします。
findOneAndUpdate()
のrawResult
オプションを削除Document.prototype.deleteOne()
はクエリを返すようになりました- MongoDB Node Driver 6.0
findOneAndRemove()
を削除count()
を削除- id セッターを削除
- 必須ではない文字列列挙型には
null
が有効 - 既存のドキュメントを更新する際に
save()
で最小化を適用 - ディスクリミネーターパスより前に基本スキーマパスを適用
findOneAndUpdate()
のoverwrite
オプションを削除orFail()
と upsert を使用したfindOneAndUpdate()
の動作を変更create()
は、すべての保存が完了するまでエラーをスローしませんModel.validate()
はオブジェクトのコピーを返します- TypeScript でオプションフィールドに
null
を許可 - TypeScript ではモデルコンストラクターのプロパティはすべてオプションです
- スキーマから
distinct()
の戻り値の型を推論
findOneAndUpdate()
の rawResult
オプションを削除
findOneAndUpdate()
、findOneAndReplace()
、および findOneAndDelete()
の rawResult
オプションは、includeResultMetadata
オプションに置き換えられました。
const filter = { name: 'Will Riker' };
const update = { age: 29 };
const res = await Character.findOneAndUpdate(filter, update, {
new: true,
upsert: true,
// Replace `rawResult: true` with `includeResultMetadata: true`
includeResultMetadata: true
});
Mongoose 8 の includeResultMetadata
は、rawResult
と同じように動作します。
Document.prototype.deleteOne
はクエリを返すようになりました
Mongoose 7 では、doc.deleteOne()
は doc
を解決する Promise を返していました。Mongoose 8 では、doc.deleteOne()
は、より簡単なチェーン化と doc.updateOne()
との整合性のために、クエリを返します。
const numberOne = await Character.findOne({ name: 'Will Riker' });
// In Mongoose 7, q is a Promise that resolves to `numberOne`
// In Mongoose 8, q is a Query.
const q = numberOne.deleteOne();
// In Mongoose 7, `res === numberOne`
// In Mongoose 8, `res` is a `DeleteResult`.
const res = await q;
MongoDB Node Driver 6
Mongoose 8 は MongoDB Node ドライバーの v6.x を使用しています。MongoDB Node ドライバー v6 には、Mongoose に影響を与えるいくつかの重要な変更点があります。
ObjectId
コンストラクターは、長さ 12 の文字列を受け付けなくなりました。Mongoose 7 では、new mongoose.Types.ObjectId('12charstring')
は完全に有効でした。Mongoose 8 では、new mongoose.Types.ObjectId('12charstring')
はエラーをスローします。非推奨の SSL オプションが削除されました
sslCA
->tlsCAFile
sslCRL
->tlsCRLFile
sslCert
->tlsCertificateKeyFile
sslKey
->tlsCertificateKeyFile
sslPass
->tlsCertificateKeyFilePassword
sslValidate
->tlsAllowInvalidCertificates
tlsCertificateFile
->tlsCertificateKeyFile
findOneAndRemove()
を削除
Mongoose 7 では、findOneAndRemove()
は後方互換性のために Mongoose がサポートしていた findOneAndDelete()
のエイリアスでした。Mongoose 8 は findOneAndRemove()
をサポートしなくなりました。代わりに findOneAndDelete()
を使用してください。
count()
を削除
Model.count()
と Query.prototype.count()
は Mongoose 8 で削除されました。代わりに Model.countDocuments()
と Query.prototype.countDocuments()
を使用してください。
id セッターを削除
Mongoose 7.4 では、Mongoose は doc.id = '0'.repeat(24)
を doc._id = '0'.repeat(24)
と同等にする id
セッターを導入しました。Mongoose 8 では、そのセッターは削除されました。
必須ではない文字列列挙型には null
が有効
Mongoose 8 より前では、文字列パスで enum
を null
に設定すると、そのパスが required
でなくてもバリデーションエラーが発生していました。Mongoose 8 では、required
が設定されていない場合、enum
を使用していても、文字列パスを null
に設定できます。
const schema = new Schema({
status: {
type: String,
enum: ['on', 'off']
}
});
const Test = mongoose.model('Test', schema);
// Works fine in Mongoose 8
// Throws a `ValidationError` in Mongoose 7
await Test.create({ status: null });
既存のドキュメントを更新する際に save()
で最小化を適用
Mongoose 7 では、Mongoose は新しいドキュメントを保存する場合のみ最小化を適用し、既存のドキュメントを更新する場合は適用しませんでしたが、Mongoose 8 では既存のドキュメント更新時にも最小化が適用されます。
const schema = new Schema({
nested: {
field1: Number
}
});
const Test = mongoose.model('Test', schema);
// Both Mongoose 7 and Mongoose 8 strip out empty objects when saving
// a new document in MongoDB by default
const { _id } = await Test.create({ nested: {} });
let rawDoc = await Test.findById(_id).lean();
rawDoc.nested; // undefined
// Mongoose 8 will also strip out empty objects when saving an
// existing document in MongoDB
const doc = await Test.findById(_id);
doc.nested = {};
doc.markModified('nested');
await doc.save();
let rawDoc = await Test.findById(_id).lean();
rawDoc.nested; // undefined in Mongoose 8, {} in Mongoose 7
ディスクリミネーターパスより前に基本スキーマパスを適用
これは、Mongoose 8 では、ディスクリミネーターパスのゲッターとセッターは、基本パスのゲッターとセッターの *後* に実行されることを意味します。Mongoose 7 では、ディスクリミネーターパスのゲッターとセッターは、基本パスのゲッターとセッターの *前* に実行されていました。
const schema = new Schema({
name: {
type: String,
get(v) {
console.log('Base schema getter');
return v;
}
}
});
const Test = mongoose.model('Test', schema);
const D = Test.discriminator('D', new Schema({
otherProp: {
type: String,
get(v) {
console.log('Discriminator schema getter');
return v;
}
}
}));
const doc = new D({ name: 'test', otherProp: 'test' });
// In Mongoose 8, prints "Base schema getter" followed by "Discriminator schema getter"
// In Mongoose 7, prints "Discriminator schema getter" followed by "Base schema getter"
console.log(doc.toObject({ getters: true }));
findOneAndUpdate()
の overwrite
オプションを削除
Mongoose 7 およびそれ以前のバージョンでは、findOneAndUpdate()
、updateOne()
、および update()
に overwrite
オプションがサポートされていました。Mongoose 7 より前では、overwrite
は update
パラメーターを $set
でラップすることをスキップするため、findOneAndUpdate()
と update()
は一致したドキュメントを上書きしていました。Mongoose 7 では、overwrite
を設定すると、後方互換性を維持するために、findOneAndUpdate()
が findOneAndReplace()
に、updateOne()
が replaceOne()
に変換されていました。
Mongoose 8 では、overwrite
オプションはサポートされなくなりました。ドキュメント全体を上書きする場合は、findOneAndReplace()
または replaceOne()
を使用してください。
orFail()
と upsert を使用した findOneAndUpdate()
の動作を変更
Mongoose 7 では、findOneAndUpdate(filter, update, { upsert: true }).orFail()
は、新しいドキュメントが upsert されると DocumentNotFoundError
をスローしていました。つまり、findOneAndUpdate().orFail()
は、新しいドキュメントが upsert された場合でも、ドキュメントが見つからない場合は常にエラーをスローしていました。
Mongoose 8 では、findOneAndUpdate(filter, update, { upsert: true }).orFail()
は常に成功します。findOneAndUpdate().orFail()
は、ドキュメントが見つからない場合ではなく、ドキュメントが返されない場合に DocumentNotFoundError
をスローするようになりました。
作成は、すべての保存が完了するまでエラーをスローしません
Mongoose 7 では、create()
は、デフォルトで save()
がエラーをスローするとすぐにエラーをスローしていました。Mongoose 8 は、代わりにすべての save()
呼び出しが終了するまで待ってから、発生した最初のエラーをスローします。そのため、create()
は Mongoose 7 と Mongoose 8 の両方で同じエラーをスローしますが、Mongoose 8 はエラーをスローするまでに時間がかかる場合があります。
const schema = new Schema({
name: {
type: String,
enum: ['Badger', 'Mushroom']
}
});
schema.pre('save', async function() {
await new Promise(resolve => setTimeout(resolve, 1000));
});
const Test = mongoose.model('Test', schema);
const err = await Test.create([
{ name: 'Badger' },
{ name: 'Mushroom' },
{ name: 'Cow' }
]).then(() => null, err => err);
err; // ValidationError
// In Mongoose 7, there would be 0 documents, because `Test.create()`
// would throw before 'Badger' and 'Mushroom' are inserted
// In Mongoose 8, there will be 2 documents. `Test.create()` waits until
// 'Badger' and 'Mushroom' are inserted before throwing.
await Test.countDocuments();
Model.validate()
はオブジェクトのコピーを返します
Mongoose 7 では、Model.validate()
は渡されたオブジェクトを変更する可能性がありました。Mongoose 8 は代わりに、最初に渡されたオブジェクトをコピーします。
const schema = new Schema({ answer: Number });
const Test = mongoose.model('Test', schema);
const obj = { answer: '42' };
const res = Test.validate(obj);
typeof obj.answer; // 'string' in Mongoose 8, 'number' in Mongoose 7
typeof res.answer; // 'number' in both Mongoose 7 and Mongoose 8
TypeScript でオプションフィールドに null
を許可
Mongoose 8 では、TypeScript で自動的に推論されたスキーマ型は、オプションフィールドに null
を許可します。Mongoose 7 では、オプションフィールドは null
ではなく undefined
のみを許可していました。
const schema = new Schema({ name: String });
const TestModel = model('Test', schema);
const doc = new TestModel();
// In Mongoose 8, this type is `string | null | undefined`.
// In Mongoose 7, this type is `string | undefined`
doc.name;
TypeScript ではモデルコンストラクターのプロパティはすべてオプションです
Mongoose 8 では、デフォルトでモデルコンストラクターにプロパティは必要ありません。
import {Schema, model, Model} from 'mongoose';
interface IDocument {
name: string;
createdAt: Date;
updatedAt: Date;
}
const documentSchema = new Schema<IDocument>(
{ name: { type: String, required: true } },
{ timestamps: true }
);
const TestModel = model<IDocument>('Document', documentSchema);
// Would throw a compile error in Mongoose 7, compiles in Mongoose 8
const newDoc = new TestModel({
name: 'Foo'
});
// Explicitly pass generic param to constructor to specify the expected
// type of the model constructor param. The following will cause TS
// to complain about missing `createdAt` and `updatedAt` in Mongoose 8.
const newDoc2 = new TestModel<IDocument>({
name: 'Foo'
});
スキーマから distinct()
の戻り値の型を推論
interface User {
name: string;
email: string;
avatar?: string;
}
const schema = new Schema<User>({
name: { type: String, required: true },
email: { type: String, required: true },
avatar: String
});
// Works in Mongoose 8. Compile error in Mongoose 7.
const names: string[] = await MyModel.distinct('name');