要約:迅速な修正
MongoServerError: Document failed validation エラーは、挿入または更新しようとしているデータが、MongoDBコレクションに定義されているスキーマバリデーションルールを満たしていないことを意味します。これを修正する最も迅速な方法は次のとおりです。
- 挿入/更新しようとしているドキュメントを検査します。
db.getCollectionInfos({ name: 'yourCollectionName' })を使用して、コレクションのバリデーションルールを確認します。- ルールに準拠するようにデータを調整するか、(必要に応じて) コレクションのバリデーションルールを変更します。
詳細な根本原因
MongoDBはバージョン3.6からスキーマバリデーションを導入しました。この機能により、コレクション内のドキュメントに対して構造的およびデータ型の制約を強制できます。insertまたはupdate操作を試みると、MongoDBは受信するドキュメントをこれらの事前定義されたルールと照合します。
ドキュメントがバリデーションルールの一部を満たさない場合(例:必須フィールドが欠落している、フィールドのデータ型が間違っている、値が許可された範囲外である、許可されていない余分なフィールドが存在するなど)、MongoDBは操作を拒否し、MongoServerError: Document failed validationをスローします。
このエラーは良いことです!これは、データベースがあなた(または他の開発者)が設定したルールに従ってデータ整合性を積極的に保護していることを意味します。あなたのタスクは、ドキュメントが無効と見なされる理由を理解し、それを準拠させることです。
修正アプローチ
アプローチ1:挿入または更新されるデータを検査する
最初のステップは、常にエラーを引き起こした正確なドキュメントを確認することです。多くの場合、問題はアプリケーションのデータペイロードにおける単純なタイプミス、欠落しているフィールド、または間違ったデータ型にあります。
問題のあるデータの例:
コレクションがname(文字列)、age(整数)、email(文字列、必須)を期待しているとします。次を挿入しようとします。
{
"username": "johndoe",
"age": "twenty-five",
"email": "john.doe@example.com"
}
ここでは、usernameが予期されていない可能性があり、ageが整数ではなく文字列であり、必須のnameフィールドが欠落しています。これらのいずれかがバリデーションの失敗を引き起こす可能性があります。
アプリケーションコードに戻り、MongoDBドライバーのinsertOne、insertMany、updateOne、またはupdateManyメソッドに渡しているオブジェクトまたはディクショナリを検査してください。コンソールまたはデバッガに出力して、その正確な構造と値を確認してください。
アプローチ2:コレクションのスキーマバリデーションルールを確認する
送信しているデータがわかったら、それがどのルールと比較されているかを知る必要があります。mongoshでdb.getCollectionInfos()コマンドを使用して、コレクションのバリデーションルールを取得できます。
db.getCollectionInfos({ name: 'yourCollectionName' });
これにより、コレクションに関する情報を含む配列が返されます。options.validatorフィールドを探してください。以下は表示される可能性のある例です。
[
{
"name": "yourCollectionName",
"type": "collection",
"options": {
"validator": {
"$jsonSchema": {
"bsonType": "object",
"required": ["name", "email", "age"],
"properties": {
"name": {
"bsonType": "string",
"description": "Must be a string and is required"
},
"email": {
"bsonType": "string",
"pattern": "^.+@.+\\..+$",
"description": "Must be a valid email address and is required"
},
"age": {
"bsonType": "int",
"minimum": 18,
"description": "Must be an integer >= 18 and is required"
},
"status": {
"enum": ["active", "inactive", "pending"],
"description": "Can only be one of the enum values"
}
},
"additionalProperties": false
}
},
"validationAction": "error",
"validationLevel": "strict"
}
}
]
この例では:
required: ["name", "email", "age"]: これらのフィールドは必須です。bsonType: "string"fornameandemail,bsonType: "int"forage: データ型が強制されます。minimum: 18forage: 値は18以上である必要があります。pattern: "^.+@.+\\..+$"foremail: メール形式の正規表現です。enum: ["active", "inactive", "pending"]forstatus: 値はこれらのいずれかである必要があります。additionalProperties: false: これは重要です!これは、propertiesに明示的にリストされているフィールド以外は許可されないことを意味します。
あなたのドキュメント(アプローチ1から)をこれらのルールと比較してください。不一致はすぐに見つかるでしょう。
アプローチ3:スキーマに一致するようにデータを調整する
不一致を特定したら、MongoDBに送信する前にアプリケーションのデータを修正します。これは通常、データ整合性を維持するため、推奨される解決策です。
問題のあるデータとアプローチ2のスキーマを使用して、修正方法は次のとおりです。
{
"name": "John Doe", // 'name'フィールドを追加
"age": 25, // 'age'を文字列ではなく整数に変更
"email": "john.doe@example.com" // メールは正しい
// スキーマの'additionalProperties: false'により'username'を削除
}
これで、この修正されたドキュメントを挿入しようとすると、バリデーションに合格するはずです。
db.yourCollectionName.insertOne({
"name": "John Doe",
"age": 25,
"email": "john.doe@example.com"
});
アプローチ4:スキーマバリデーションルールを変更する(必要な場合)
場合によっては、バリデーションルール自体が現在のアプリケーションロジックに対して間違っていたり、厳しすぎたり、古くなっていたりすることがあります。そのような場合は、コレクションのバリデーターを変更する必要があるかもしれません。これは、コレクションに対する今後のすべての操作に影響するため、慎重に行う必要があります。
collModコマンドを使用してバリデーションルールを変更できます。
db.runCommand({
collMod: 'yourCollectionName',
validator: {
// ここに新しい、または変更されたバリデーションルールを記述
// 例:'username'フィールドを許可する場合:
"$jsonSchema": {
"bsonType": "object",
"required": ["name", "email", "age"],
"properties": {
"name": { "bsonType": "string" },
"email": { "bsonType": "string", "pattern": "^.+@.+\\..+$" },
"age": { "bsonType": "int", "minimum": 18 },
"username": { "bsonType": "string", "minLength": 3 } // 新しいフィールドを許可
},
"additionalProperties": false // 他の任意のフィールドは引き続き不可
}
},
validationAction: 'error', // 'error'(デフォルト)または'warn'
validationLevel: 'strict' // 'strict'(デフォルト)または'moderate'
});
validationAction: バリデーション失敗時に何が起こるかを決定します。'error'は操作を拒否し、'warn'は警告をログに記録しますが、操作は許可します。validationLevel: どの操作がバリデーションの対象となるかを決定します。'strict'はすべての挿入と更新に適用されます。'moderate'は既存の有効なドキュメントに対する挿入と更新に適用されます(既存の無効なドキュメントに対する更新はバリデーションされません)。
コレクションのバリデーションを完全に無効にするには、バリデーターを空のオブジェクトに設定します。
db.runCommand({
collMod: 'yourCollectionName',
validator: {},
validationAction: 'warn'
});
強力なアプリケーションレベルのバリデーションがない限り、これは通常、本番環境では推奨されません。
アプローチ5:アプリケーションコードでバリデーションエラーを処理する
堅牢なアプリケーションでは、バリデーションエラーを予測し、適切に処理する必要があります。ほとんどのMongoDBドライバーは例外をスローするか、キャッチできるエラーオブジェクトを返します。
例(Node.js/JavaScript疑似コード):
const { MongoClient } = require('mongodb');
async function insertUser(userData) {
const client = new MongoClient('mongodb://localhost:27017');
try {
await client.connect();
const database = client.db('mydb');
const users = database.collection('users');
const result = await users.insertOne(userData);
console.log(`Successfully inserted user with _id: ${result.insertedId}`);
} catch (error) {
if (error.name === 'MongoServerError' && error.code === 121) { // 121はDocumentValidationFailureのエラーコード
console.error('Document failed validation:', error.message);
console.error('Problematic document:', JSON.stringify(userData, null, 2));
// エラー詳細で利用可能な場合、特定のバリデーションエラーをログに記録
// (MongoDBのエラーメッセージには、どのルールが失敗したかの詳細が含まれる場合があります)
} else {
console.error('An unexpected error occurred:', error);
}
} finally {
await client.close();
}
}
// Example usage with invalid data:
insertUser({
username: 'testuser',
age: 'twenty',
email: 'invalid'
});
特定のMongoServerErrorをキャッチし、その詳細(特にerror.messageまたはerror.errInfo.details)を検査することで、ユーザーにより良いフィードバックを提供したり、より具体的な診断情報をログに記録したりできます。
予防策
-
アプリケーションレベルのバリデーション: MongoDBにデータを送信する前に、アプリケーション層で堅牢なバリデーションを実装します。これにより、ユーザーに即座にフィードバックが提供され、不要なデータベース呼び出しが削減されます。Joi、Yup(Node.js用)、Pydantic(Python用)、Hibernate Validator(Java用)などのライブラリがこれに適しています。
-
明確なスキーマドキュメント: プロジェクトに携わるすべての開発者向けに、MongoDBコレクションのスキーマとバリデーションルールを明確に文書化します。
-
自動テスト: MongoDBスキーマバリデーションルールに対してデータモデルを具体的にテストするユニットテストと統合テストを作成します。
-
JSONバリデーターツールの使用: 複雑なJSON構造をデバッグしたり、データが特定の形式に準拠していることを確認したりする際には、ToolCraftのJSON Formatter & Validatorのようなツールをよく使用します。これは、データベースに送信する前に、構文エラーをチェックしたり、JSONをきれいに表示したりするための迅速なブラウザベースの方法です。特に複雑な
$jsonSchemaルールを使用している場合、バリデーションエラーにつながる可能性のある単純な間違いを特定するのに非常に便利です。
検証手順
修正を適用した後(データの修正またはスキーマルールの変更のいずれか):
-
操作を再試行する: データを再度挿入または更新してみてください。エラーが表示されなくなった場合は、良い兆候です。
-
コレクションをクエリする:
db.yourCollectionName.find({ _id: yourDocumentId })または関連するクエリを使用して、ドキュメントが正常に挿入または更新され、データベースに期待どおりに表示されていることを確認します。 -
エッジケースをテストする: スキーマを変更した場合は、(以前は失敗したが) 今は合格するはずのデータや、引き続き失敗するはずのデータを挿入して、新しいバリデーションルールが意図したとおりに動作していることを確認します。

