MongoDBでスキーマ検証を使用する方法
序章
リレーショナルデータベースの重要な側面の1つは、データベースを行と列で構成されるテーブルに格納することです。これらは、既知のデータ型のフィールドを持つ固定された厳密なスキーマで動作します。 MongoDBのようなドキュメント指向データベースは、必要に応じてドキュメントの構造を再形成できるため、この点でより柔軟性があります。
ただし、特定の構造に従うか、特定の要件を満たすためにデータドキュメントが必要になる場合があります。 多くのドキュメントデータベースでは、ドキュメントのデータの一部をどのように構造化するかを指示するルールを定義できますが、必要に応じてこの構造を自由に変更できます。
MongoDBには、ドキュメントの構造に制約を適用できるスキーマ検証と呼ばれる機能があります。 スキーマ検証は、JSONドキュメント構造の記述と検証のオープンスタンダードである JSONSchemaを中心に構築されています。 このチュートリアルでは、検証ルールを記述して適用し、MongoDBコレクションの例でドキュメントの構造を制御します。
前提条件
このチュートリアルに従うには、次のものが必要です。
- 通常のroot以外のユーザーがいるサーバー
sudo
特権とUFWで構成されたファイアウォール。 このチュートリアルは、Ubuntu 20.04を実行しているサーバーを使用して検証されており、Ubuntu20.04のこの初期サーバーセットアップチュートリアルに従ってサーバーを準備できます。 - サーバーにインストールされているMongoDB。 これを設定するには、 Ubuntu20.04にMongoDBをインストールする方法に関するチュートリアルに従ってください。
- 認証を有効にして管理ユーザーを作成することにより、サーバーのMongoDBインスタンスを保護します。 このようにMongoDBを保護するには、 Ubuntu20.04でMongoDBを保護する方法に関するチュートリアルに従ってください。
- MongoDBコレクションのクエリと結果のフィルタリングに精通していること。 MongoDBクエリの使用方法については、MongoDBでクエリを作成する方法に関するガイドに従ってください。
注:サーバーの構成方法、MongoDBのインストール方法、およびMongoDBのインストールを保護する方法に関するリンクされたチュートリアルは、Ubuntu20.04を参照しています。 このチュートリアルは、基盤となるオペレーティングシステムではなく、MongoDB自体に焦点を当てています。 通常、認証が有効になっている限り、オペレーティングシステムに関係なく、すべてのMongoDBインストールで機能します。
ステップ1—スキーマ検証を適用せずにドキュメントを挿入する
MongoDBのスキーマ検証機能とそれらが役立つ理由を強調するために、このステップでは、MongoDBシェルを開いて、ローカルにインストールされたMongoDBインスタンスに接続し、その中にサンプルコレクションを作成する方法の概要を説明します。 次に、このコレクションにいくつかのサンプルドキュメントを挿入することで、このステップでは、MongoDBがデフォルトでスキーマ検証を強制しない方法を示します。 後の手順で、このようなルールの作成と適用を自分で開始します。
このガイドで使用するサンプルコレクションを作成するには、管理ユーザーとしてMongoDBシェルに接続します。 このチュートリアルは、前提条件 MongoDBセキュリティチュートリアルの規則に従い、この管理ユーザーの名前が AdminSammy であり、その認証データベースが admin
. 異なる場合は、次のコマンドでこれらの詳細を変更して、独自の設定を反映させてください。
- mongo -u AdminSammy -p --authenticationDatabase admin
インストール中に設定したパスワードを入力して、シェルにアクセスします。 パスワードを入力すると、 >
プロンプトサイン。
スキーマ検証機能を説明するために、このガイドの例では、世界で最も高い山を表すドキュメントを含むサンプルデータベースを使用しています。 エベレストのサンプルドキュメントは次の形式になります。
{
"name": "Everest",
"height": 8848,
"location": ["Nepal", "China"],
"ascents": {
"first": {
"year": 1953,
},
"first_winter": {
"year": 1980,
},
"total": 5656,
}
}
このドキュメントには、次の情報が含まれています。
name
:ピークの名前。height
:ピークの標高(メートル単位)。location
:山が位置する国。 このフィールドには、複数の国にある山を許可するための配列として値が格納されます。ascents
:このフィールドの値は別のドキュメントです。 あるドキュメントがこのように別のドキュメント内に保存されている場合、それは埋め込みまたはネストされたドキュメントと呼ばれます。 各ascents
文書は、与えられた山の成功した登りを説明しています。 具体的には、それぞれascents
ドキュメントに含まれているtotal
与えられた各ピークの成功した上昇の総数をリストするフィールド。 さらに、これらのネストされたドキュメントのそれぞれには、値がネストされたドキュメントでもある2つのフィールドが含まれています。first
:このフィールドの値は、1つのフィールドを含むネストされたドキュメントです。year
、これは最初の全体的に成功した上昇の年を表します。first_winter
:このフィールドの値はネストされたドキュメントであり、year
フィールド。その値は、指定された山の最初の冬の登山に成功した年を表します。
次を実行します insertOne()
名前の付いたコレクションを同時に作成するメソッド peaks
MongoDBインストールで、エベレストを表す前のサンプルドキュメントを挿入します。
- db.peaks.insertOne(
- {
- "name": "Everest",
- "height": 8848,
- "location": ["Nepal", "China"],
- "ascents": {
- "first": {
- "year": 1953
- },
- "first_winter": {
- "year": 1980
- },
- "total": 5656
- }
- }
- )
出力には、成功メッセージと、新しく挿入されたオブジェクトに割り当てられたオブジェクト識別子が含まれます。
Output{
"acknowledged" : true,
"insertedId" : ObjectId("618ffa70bfa69c93a8980443")
}
提供されたを実行してこのドキュメントを挿入しましたが insertOne()
この方法では、このドキュメントの構造を完全に自由に設計できます。 場合によっては、データベース内のドキュメントの構造にある程度の柔軟性が必要になることがあります。 ただし、データの分析や処理を容易にするために、ドキュメントの構造の一部の側面に一貫性を持たせることもできます。
これが重要である理由を説明するために、このデータベースに入力される可能性のある他のいくつかのサンプルドキュメントを検討してください。
次のドキュメントは、エベレストを表す前のドキュメントとほぼ同じですが、 name
分野:
{
"height": 8611,
"location": ["Pakistan", "China"],
"ascents": {
"first": {
"year": 1954
},
"first_winter": {
"year": 1921
},
"total": 306
}
}
世界で最も高い山のリストを含むデータベースの場合、山を表すがその名前を含まないドキュメントを追加すると、重大なエラーになる可能性があります。
この次のサンプルドキュメントでは、山の名前が表示されていますが、山の高さは数字ではなく文字列として表されています。 さらに、場所は配列ではなく単一の値であり、上昇の試行の総数に関する情報はありません。
{
"name": "Manaslu",
"height": "8163m",
"location": "Nepal"
}
この例と同じくらい多くの省略があるドキュメントを解釈することは難しいかもしれません。 たとえば、次の場合、コレクションをピーク高さで正常に並べ替えることはできません。 height
属性値は、ドキュメント間で異なるデータ型として保存されます。
次に、以下を実行します insertMany()
これらのドキュメントをエラーを発生させずにデータベースに挿入できるかどうかをテストする方法:
- db.peaks.insertMany([
- {
- "height": 8611,
- "location": ["Pakistan", "China"],
- "ascents": {
- "first": {
- "year": 1954
- },
- "first_winter": {
- "year": 1921
- },
- "total": 306
- }
- },
- {
- "name": "Manaslu",
- "height": "8163m",
- "location": "Nepal"
- }
- ])
結局のところ、MongoDBはエラーを返さず、両方のドキュメントが正常に挿入されます。
Output{
"acknowledged" : true,
"insertedIds" : [
ObjectId("618ffd0bbfa69c93a8980444"),
ObjectId("618ffd0bbfa69c93a8980445")
]
}
この出力が示すように、これらのドキュメントは両方とも有効なJSONであり、コレクションに挿入するのに十分です。 ただし、これだけではデータベースの論理的な一貫性と意味を維持するのに十分ではありません。 次の手順では、スキーマ検証ルールを作成して、 peaks
コレクションは、いくつかの重要な要件に従います。
ステップ2—文字列フィールドの検証
MongoDBでは、スキーマ検証は、JSONスキーマドキュメントをコレクションに割り当てることで個々のコレクションに対して機能します。 JSON Schema は、JSONドキュメントの構造を定義および検証できるオープンスタンダードです。 これを行うには、特定のコレクション内のドキュメントが有効であると見なされるために従わなければならない一連の要件をリストするスキーマ定義を作成します。
特定のコレクションは単一のJSONスキーマのみを使用できますが、コレクションの作成時またはその後いつでもスキーマを割り当てることができます。 後で元の検証ルールを変更する場合は、元のJSONスキーマドキュメントを新しい要件に一致するものに置き換える必要があります。
JSONスキーマバリデータードキュメントをに割り当てるには peaks
前の手順で作成したコレクションの場合、次のコマンドを実行できます。
- db.runCommand({
- "collMod": "collection_name",
- "validator": {
- $jsonSchema: {JSON_Schema_document}
- }
- })
The runCommand
メソッドはを実行します collMod
コマンド。これは、を適用して指定されたコレクションを変更します。 validator
それに属性します。 The validator
属性はスキーマ検証を担当し、この構文例では、属性は $jsonSchema
オペレーター。 この演算子は、特定のコレクションのスキーマバリデーターとして使用されるJSONスキーマドキュメントを定義します。
警告:実行するために collMod
コマンドを実行するには、MongoDBユーザーに適切な権限を付与する必要があります。 Ubuntu 20.04 でMongoDBを保護する方法の前提条件のチュートリアルに従い、そのガイドで作成した管理ユーザーとしてMongoDBインスタンスに接続している場合は、それに続く追加の役割を付与する必要があります。このガイドの例。
まず、ユーザーの認証データベースに切り替えます。 これは admin
次の例では、異なる場合は自分のユーザーの認証データベースに接続します。
- use admin
Outputswitched to db admin
次に、 grantRolesToUser()
メソッドを使用して、ユーザーに dbAdmin
作成したデータベースに対する役割 peaks
コレクション。 次の例では、 peaks
コレクションは test
データベース:
- db.grantRolesToUser(
- "AdminSammy",
- [ { role : "dbAdmin", db : "test" } ]
- )
または、ユーザーに dbAdminAnyDatabase
役割。 この役割の名前が示すように、それはあなたのユーザーに与えます dbAdmin
MongoDBインスタンス上のすべてのデータベースに対する特権:
- db.grantRolesToUser(
- "AdminSammy",
- [ "dbAdminAnyDatabase" ]
- )
ユーザーに適切な役割を付与した後、データベースに戻って peaks
コレクションが保存されます:
- use test
Outputswitched to db test
コレクションを作成するときに、JSONスキーマバリデーターを割り当てることもできることに注意してください。 これを行うには、次の構文を使用できます。
- db.createCollection(
- "collection_name", {
- "validator": {
- $jsonSchema: {JSON_Schema_document}
- }
- })
前の例とは異なり、この構文には collMod
これは、コレクションがまだ存在していないため、変更できないためです。 ただし、前の例と同様に、 collection_name
バリデータードキュメントを割り当てるコレクションの名前であり、 validator
オプションは、指定されたJSONスキーマドキュメントをコレクションのバリデーターとして割り当てます。
このように最初からJSONスキーマバリデーターを適用するということは、コレクションに追加するすべてのドキュメントがバリデーターによって設定された要件を満たさなければならないことを意味します。 ただし、既存のコレクションに検証ルールを追加すると、それらを変更しようとするまで、新しいルールは既存のドキュメントに影響を与えません。
に渡すJSONスキーマドキュメント validator
属性は、コレクションに適用するすべての検証ルールの概要を示す必要があります。 次のJSONスキーマの例では、 name
フィールドはコレクション内のすべてのドキュメントに存在し、 name
フィールドの値は常に文字列です。
{
"bsonType": "object",
"description": "Document describing a mountain peak",
"required": ["name"],
"properties": {
"name": {
"bsonType": "string",
"description": "Name must be a string and is required"
}
},
}
このスキーマドキュメントは、コレクションに入力されたドキュメントの特定の部分が従わなければならない特定の要件の概要を示しています。 JSONスキーマドキュメントのルート部分(前のフィールド properties
、この場合は bsonType
, description
、 と required
)データベースドキュメント自体を記述します。
The bsonType
プロパティは、検証エンジンが検出することを期待するデータ型を記述します。 データベースドキュメント自体の場合、予想されるタイプは次のとおりです。 object
. これは、オブジェクトのみを追加できることを意味します。つまり、中括弧で囲まれた完全で有効なJSONドキュメント({
と }
)—このコレクションへ。 他の種類のデータ型(スタンドアロンの文字列、整数、配列など)を挿入しようとすると、エラーが発生します。
MongoDBでは、すべてのドキュメントがオブジェクトです。 ただし、JSONスキーマは、あらゆる種類の有効なJSONドキュメントを記述および検証するために使用される標準であり、プレーン配列または文字列も有効なJSONです。 MongoDBスキーマ検証を使用する場合、常にルートドキュメントを設定する必要があることがわかります。 bsonType
としての値 object
JSONスキーマバリデーターで。
次に、 description
プロパティは、このコレクションで見つかったドキュメントの簡単な説明を提供します。 このフィールドは必須ではありませんが、ドキュメントの検証に使用されるだけでなく、JSONスキーマを使用してドキュメントの構造に注釈を付けることもできます。 これは、他のユーザーがドキュメントの目的を理解するのに役立ちます。 description
フィールドは良い習慣になり得ます。
検証ドキュメントの次のプロパティは required
分野。 The required
fieldは、コレクション内のすべてのドキュメントに存在する必要があるドキュメントフィールドのリストを含む配列のみを受け入れることができます。 この例では、 ["name"]
これは、ドキュメントに含まれている必要があるのは name
有効と見なされるフィールド。
続いて properties
ドキュメントフィールドの検証に使用されるルールを説明するオブジェクト。 ルールを定義するフィールドごとに、フィールドにちなんで名付けられた埋め込みJSONスキーマドキュメントを含めます。 にリストされていないフィールドのスキーマルールを定義できることに注意してください。 required
配列。 これは、データに必須ではないフィールドが含まれているが、それらが存在する場合でも特定のルールに従うようにしたい場合に役立ちます。
これらの埋め込みスキーマドキュメントは、メインドキュメントと同様の構文に従います。 この例では、 bsonType
プロパティには、すべてのドキュメントが必要です name
フィールドになる string
. この埋め込みドキュメントには、簡単な説明も含まれています description
分野。
このJSONスキーマをに適用するには peaks
前の手順で作成したコレクションは、次を実行します runCommand()
方法:
- db.runCommand({
- "collMod": "peaks",
- "validator": {
- $jsonSchema: {
- "bsonType": "object",
- "description": "Document describing a mountain peak",
- "required": ["name"],
- "properties": {
- "name": {
- "bsonType": "string",
- "description": "Name must be a string and is required"
- }
- },
- }
- }
- })
MongoDBは、コレクションが正常に変更されたことを示す成功メッセージで応答します。
Output{ "ok" : 1 }
その後、MongoDBではドキュメントをに挿入できなくなります peaks
彼らが持っていない場合はコレクション name
分野。 これをテストするには、前の手順で挿入した、山を完全に説明しているドキュメントを挿入してみてください。 name
分野:
- db.peaks.insertOne(
- {
- "height": 8611,
- "location": ["Pakistan", "China"],
- "ascents": {
- "first": {
- "year": 1954
- },
- "first_winter": {
- "year": 1921
- },
- "total": 306
- }
- }
- )
今回の操作では、ドキュメントの検証に失敗したことを示すエラーメッセージが表示されます。
OutputWriteError({
"index" : 0,
"code" : 121,
"errmsg" : "Document failed validation",
. . .
})
MongoDBは、JSONスキーマで指定された検証ルールに合格しなかったドキュメントを挿入しません。
注: MongoDB 5.0以降、検証が失敗すると、エラーメッセージは失敗した制約を示します。 MongoDB 4.4以前では、データベースは失敗の理由に関する詳細を提供しません。
次のコマンドを実行して、MongoDBがJSONスキーマに含めたデータ型の要件を適用するかどうかをテストすることもできます。 insertOne()
方法。 これは前回の操作と似ていますが、今回は name
分野。 ただし、このフィールドの値は文字列ではなく数値です。
- db.peaks.insertOne(
- {
- "name": 123,
- "height": 8611,
- "location": ["Pakistan", "China"],
- "ascents": {
- "first": {
- "year": 1954
- },
- "first_winter": {
- "year": 1921
- },
- "total": 306
- }
- }
- )
もう一度、検証は失敗します。 にもかかわらず name
フィールドが存在しますが、文字列である必要があるという制約を満たしていません。
OutputWriteError({
"index" : 0,
"code" : 121,
"errmsg" : "Document failed validation",
. . .
})
もう一度試してくださいが、 name
ドキュメントに存在し、その後に文字列値が続くフィールド。 今回、 name
ドキュメント内の唯一のフィールドです:
- db.peaks.insertOne(
- {
- "name": "K2"
- }
- )
操作は成功し、ドキュメントは通常どおりオブジェクト識別子を受け取ります。
Output{
"acknowledged" : true,
"insertedId" : ObjectId("61900965bfa69c93a8980447")
}
スキーマ検証ルールは、 name
分野。 この時点で、 name
フィールドが検証要件を満たしている場合、ドキュメントはエラーなしで挿入されます。 ドキュメントの残りの部分は、任意の形をとることができます。
これで、最初のJSONスキーマドキュメントを作成し、最初のスキーマ検証ルールをに適用しました。 name
フィールド、それが存在し、文字列である必要があります。 ただし、データ型ごとに異なる検証オプションがあります。 次に、各ドキュメントに保存されている数値を検証します height
分野。
ステップ3—数値フィールドの検証
次のドキュメントをに挿入したときに、手順1を思い出してください。 peaks
コレクション:
{
"name": "Manaslu",
"height": "8163m",
"location": "Nepal"
}
このドキュメントは height
値は数値ではなく文字列であり、 insertMany()
このドキュメントを挿入するために使用したメソッドは成功しました。 これが可能だったのは、の検証ルールをまだ追加していないためです。 height
分野。
挿入されたドキュメントが有効なJSON構文で記述されている限り、MongoDBは、このフィールドの任意の値(負の値など、このフィールドに意味をなさない値も含む)を受け入れます。 これを回避するには、前の手順のスキーマ検証ドキュメントを拡張して、 height
分野。
確認することから始めます height
フィールドは、新しく挿入されたドキュメントに常に存在し、常に数値として表されます。 次のコマンドを使用して、スキーマ検証を変更します。
- db.runCommand({
- "collMod": "peaks",
- "validator": {
- $jsonSchema: {
- "bsonType": "object",
- "description": "Document describing a mountain peak",
- "required": ["name", "height"],
- "properties": {
- "name": {
- "bsonType": "string",
- "description": "Name must be a string and is required"
- },
- "height": {
- "bsonType": "number",
- "description": "Height must be a number and is required"
- }
- },
- }
- }
- })
このコマンドのスキーマドキュメントでは、 height
フィールドはに含まれています required
配列。 同様に、 height
内のドキュメント properties
新しいものを必要とするオブジェクト height
である値 number
. 繰り返しますが、 description
フィールドは補助的なものであり、含める説明は、他のユーザーがJSONスキーマの背後にある意図を理解できるようにするためだけのものにする必要があります。
MongoDBは、コレクションが正常に変更されたことを通知する短い成功メッセージで応答します。
Output{ "ok" : 1 }
これで、新しいルールをテストできます。 検証ドキュメントに合格するために必要な最小限のドキュメント構造でドキュメントを挿入してみてください。 次のメソッドは、2つの必須フィールドのみを含むドキュメントを挿入します。 name
と height
:
- db.peaks.insertOne(
- {
- "name": "Test peak",
- "height": 8300
- }
- )
挿入は成功します:
Output{
acknowledged: true,
insertedId: ObjectId("61e0c8c376b24e08f998e371")
}
次に、不足しているドキュメントを挿入してみます height
分野:
- db.peaks.insertOne(
- {
- "name": "Test peak"
- }
- )
次に、を含む別のものを試してください height
フィールドですが、このフィールドには文字列値が含まれています。
- db.peaks.insertOne(
- {
- "name": "Test peak",
- "height": "8300m"
- }
- )
どちらの場合も、操作によってエラーメッセージが表示され、失敗します。
OutputWriteError({
"index" : 0,
"code" : 121,
"errmsg" : "Document failed validation",
. . .
})
ただし、負の高さの山頂を挿入しようとすると、山は適切に保存されます。
- db.peaks.insertOne(
- {
- "name": "Test peak",
- "height": -100
- }
- )
これを防ぐために、スキーマ検証ドキュメントにさらにいくつかのプロパティを追加できます。 次の操作を実行して、現在のスキーマ検証設定を置き換えます。
- db.runCommand({
- "collMod": "peaks",
- "validator": {
- $jsonSchema: {
- "bsonType": "object",
- "description": "Document describing a mountain peak",
- "required": ["name", "height"],
- "properties": {
- "name": {
- "bsonType": "string",
- "description": "Name must be a string and is required"
- },
- "height": {
- "bsonType": "number",
- "description": "Height must be a number between 100 and 10000 and is required",
- "minimum": 100,
- "maximum": 10000
- }
- },
- }
- }
- })
新しい minimum
と maximum
属性は、に含まれる値に制約を設定します height
フィールド。100より小さくしたり10000より大きくしたりできないようにします。 このコレクションは山頂の高さに関する情報を格納するために使用されるため、この範囲は理にかなっていますが、これらの属性には任意の値を選択できます。
さて、ネガティブなピークを挿入しようとすると height
値を再度指定すると、操作は失敗します。
- db.peaks.insertOne(
- {
- "name": "Test peak",
- "height": -100
- }
- )
OutputWriteError({
"index" : 0,
"code" : 121,
"errmsg" : "Document failed validation",
. . .
この出力が示すように、ドキュメントスキーマは、各ドキュメントに保持されている文字列値を検証するようになりました。 name
フィールドと、に保持されている数値 height
田畑。 各ドキュメントに格納されている配列値を検証する方法については、読み続けてください。 location
分野。
ステップ4—配列フィールドの検証
今、各ピークは name
と height
値はスキーマ検証制約によって検証されているので、注意を向けることができます。 location
データの一貫性を保証するフィールド。
山の場所を指定することは、ピークが複数の国にまたがるため、予想よりも難しいです。これは、有名な8000メートル峰の多くに当てはまります。 このため、各ピークを保存するのは理にかなっています location
単なる文字列値ではなく、1つ以上の国名を含む配列としてのデータ。 と同じように height
値、それぞれを確認してください location
フィールドのデータ型はすべてのドキュメントで一貫しているため、集計パイプラインを使用するときにデータを要約するのに役立ちます。
まず、いくつかの例を考えてみましょう location
ユーザーが入力する可能性のある値と、有効または無効になる値を比較検討します。
["Nepal", "China"]
:これは2要素の配列であり、2か国にまたがる山の有効な値になります。["Nepal"]
:この例は単一要素の配列であり、単一の国にある山の有効な値にもなります。"Nepal"
:この例はプレーンな文字列です。 単一の国がリストされていますが、location
フィールドには常に配列が含まれている必要があります[]
:空の配列。この例は有効な値ではありません。 結局のところ、すべての山は少なくとも1つの国に存在する必要があります。["Nepal", "Nepal"]
:この2要素配列も、同じ値が2回出現するため、無効になります。["Nepal", 15]
:最後に、この2要素配列は無効になります。これは、その値の1つが文字列ではなく数値であり、これが正しい場所の名前ではないためです。
MongoDBがこれらの各例を有効または無効として正しく解釈するようにするには、次の操作を実行して、 peaks
コレクション:
- db.runCommand({
- "collMod": "peaks",
- "validator": {
- $jsonSchema: {
- "bsonType": "object",
- "description": "Document describing a mountain peak",
- "required": ["name", "height", "location"],
- "properties": {
- "name": {
- "bsonType": "string",
- "description": "Name must be a string and is required"
- },
- "height": {
- "bsonType": "number",
- "description": "Height must be a number between 100 and 10000 and is required",
- "minimum": 100,
- "maximum": 10000
- },
- "location": {
- "bsonType": "array",
- "description": "Location must be an array of strings",
- "minItems": 1,
- "uniqueItems": true,
- "items": {
- "bsonType": "string"
- }
- }
- },
- }
- }
- })
これで $jsonSchema
オブジェクト、 location
フィールドはに含まれています required
アレイと properties
物体。 そこでは、それはで定義されています bsonType
の array
を確実にするために location
値は、単一の文字列や数値ではなく、常に配列です。
The minItems
プロパティは、配列に少なくとも1つの要素が含まれている必要があること、および uniqueItems
プロパティはに設定されています true
それぞれの中の要素を確実にするために location
配列は一意になります。 これにより、次のような値が防止されます ["Nepal", "Nepal"]
受け入れられないように。 最後に、 items
サブドキュメントは、個々の配列アイテムの検証スキーマを定義します。 ここで、唯一の期待は、 location
配列は文字列である必要があります。
注:使用可能なスキーマドキュメントのプロパティは、それぞれ異なります。 bsonType
また、フィールドタイプに応じて、フィールド値のさまざまな側面を検証できます。 たとえば、 number
許容値の範囲を作成するために、最小および最大許容値を定義できる値。 前の例では、 location
田畑 bsonType
に array
、アレイに固有の機能を検証できます。
可能なすべての検証の選択肢の詳細については、JSONスキーマのドキュメントを参照してください。
コマンドを実行した後、MongoDBは、コレクションが新しいスキーマドキュメントで正常に変更されたことを示す短い成功メッセージで応答します。
Output{ "ok" : 1 }
ここで、前に準備した例に一致するドキュメントを挿入して、新しいルールがどのように動作するかをテストしてみてください。 繰り返しになりますが、最小限のドキュメント構造を使用してみましょう。 name
, height
、 と location
フィールドが存在します。
- db.peaks.insertOne(
- {
- "name": "Test peak",
- "height": 8300,
- "location": ["Nepal", "China"]
- }
- )
定義された検証の期待をすべて満たすため、ドキュメントは正常に挿入されます。 同様に、次のドキュメントはエラーなしで挿入されます。
- db.peaks.insertOne(
- {
- "name": "Test peak",
- "height": 8300,
- "location": ["Nepal"]
- }
- )
ただし、次のいずれかを実行する場合 insertOne()
メソッドの場合、検証エラーが発生して失敗します。
- db.peaks.insertOne(
- {
- "name": "Test peak",
- "height": 8300,
- "location": "Nepal"
- }
- )
- db.peaks.insertOne(
- {
- "name": "Test peak",
- "height": 8300,
- "location": []
- }
- )
- db.peaks.insertOne(
- {
- "name": "Test peak",
- "height": 8300,
- "location": ["Nepal", "Nepal"]
- }
- )
- db.peaks.insertOne(
- {
- "name": "Test peak",
- "height": 8300,
- "location": ["Nepal", 15]
- }
- )
以前に定義した検証ルールに従って、 location
これらの操作で提供された値は無効と見なされます。
この手順を実行した後、山頂を表す3つの主要なフィールドは、MongoDBのスキーマ検証機能によってすでに検証されています。 次のステップでは、を使用してネストされたドキュメントを検証する方法を学習します。 ascents
例としてフィールド。
ステップ5—埋め込みドキュメントの検証
この時点で、 peaks
コレクションには3つのフィールドがあります— name
, height
と location
—スキーマ検証によってチェックされています。 このステップでは、の検証ルールの定義に焦点を当てます。 ascents
フィールド。各ピークをサミットするための成功した試みを説明します。
エベレストを表すステップ1のサンプルドキュメントでは、 ascents
フィールドは次のように構成されました。
{
"name": "Everest",
"height": 8848,
"location": ["Nepal", "China"],
"ascents": {
"first": {
"year": 1953,
},
"first_winter": {
"year": 1980,
},
"total": 5656,
}
}
The ascents
サブドキュメントには total
その値が、指定された山の登山試行の総数を表すフィールド。 また、山の最初の冬の登りと全体的な最初の登りに関する情報も含まれています。 ただし、これらは山の説明に不可欠ではない場合があります。 結局のところ、一部の山はまだ冬に登っていないか、登山日が争われているか不明である可能性があります。 今のところ、各ドキュメントに常に必要な情報は、上昇の試行の総数であると想定してください。
スキーマ検証ドキュメントを変更して、 ascents
フィールドは常に存在する必要があり、その値は常にサブドキュメントである必要があります。 このサブドキュメントには、常に total
ゼロ以上の数値を保持する属性。 The first
と first_winter
このガイドではフィールドは必須ではないため、検証フォームではフィールドは考慮されず、柔軟なフォームを使用できます。
もう一度、スキーマ検証ドキュメントを置き換えます peaks
以下を実行して収集 runCommand()
方法:
- db.runCommand({
- "collMod": "peaks",
- "validator": {
- $jsonSchema: {
- "bsonType": "object",
- "description": "Document describing a mountain peak",
- "required": ["name", "height", "location", "ascents"],
- "properties": {
- "name": {
- "bsonType": "string",
- "description": "Name must be a string and is required"
- },
- "height": {
- "bsonType": "number",
- "description": "Height must be a number between 100 and 10000 and is required",
- "minimum": 100,
- "maximum": 10000
- },
- "location": {
- "bsonType": "array",
- "description": "Location must be an array of strings",
- "minItems": 1,
- "uniqueItems": true,
- "items": {
- "bsonType": "string"
- }
- },
- "ascents": {
- "bsonType": "object",
- "description": "Ascent attempts information",
- "required": ["total"],
- "properties": {
- "total": {
- "bsonType": "number",
- "description": "Total number of ascents must be 0 or higher",
- "minimum": 0
- }
- }
- }
- },
- }
- }
- })
ドキュメントのいずれかのフィールドにサブドキュメントが含まれている場合、そのフィールドのJSONスキーマは、メインドキュメントスキーマとまったく同じ構文に従います。 同じドキュメントを相互にネストする方法と同様に、検証スキーマもドキュメントを相互にネストします。 これにより、階層構造に複数のサブドキュメントを含むドキュメント構造の複雑な検証スキーマを簡単に定義できます。
このJSONスキーマドキュメントでは、 ascents
フィールドはに含まれています required
配列、それを必須にします。 にも表示されます properties
で定義されているオブジェクト bsonType
の object
、ルートドキュメント自体と同じように。
の定義に注意してください ascents
検証は、ルートドキュメントと同様の原則に従います。 それは required
フィールド。サブドキュメントに含める必要のあるプロパティを示します。 また、 properties
同じ構造に従うリスト。 以来 ascents
フィールドはサブドキュメントであり、その値は、より大きなドキュメントの値と同じように検証されます。
内部 ascents
、 あります required
値が total
、つまり ascents
サブドキュメントには、 total
分野。 その後、 total
値は、 properties
オブジェクト。これは常に number
とともに minimum
ゼロの値。
繰り返しますが、どちらも first
また、 first_winter
このガイドでは、フィールドは必須であり、これらの検証ルールには含まれていません。
このスキーマ検証ドキュメントを適用した状態で、最初のステップからサンプルのエベレストマウントドキュメントを挿入して、有効として確立済みのドキュメントを挿入できることを確認してください。
- db.peaks.insertOne(
- {
- "name": "Everest",
- "height": 8848,
- "location": ["Nepal", "China"],
- "ascents": {
- "first": {
- "year": 1953,
- },
- "first_winter": {
- "year": 1980,
- },
- "total": 5656,
- }
- }
- )
ドキュメントは正常に保存され、MongoDBは新しいオブジェクト識別子を返します。
Output{
"acknowledged" : true,
"insertedId" : ObjectId("619100f51292cb2faee531f8")
}
検証の最後の部分が正しく機能することを確認するには、を含まないドキュメントを挿入してみてください ascents
分野:
- db.peaks.insertOne(
- {
- "name": "Everest",
- "height": 8848,
- "location": ["Nepal", "China"]
- }
- )
今回の操作では、ドキュメントの検証に失敗したことを示すエラーメッセージが表示されます。
OutputWriteError({
"index" : 0,
"code" : 121,
"errmsg" : "Document failed validation",
. . .
})
次に、そのドキュメントを挿入してみます ascents
サブドキュメントがありません total
分野:
- db.peaks.insertOne(
- {
- "name": "Everest",
- "height": 8848,
- "location": ["Nepal", "China"],
- "ascents": {
- "first": {
- "year": 1953,
- },
- "first_winter": {
- "year": 1980,
- }
- }
- }
- )
これにより、再びエラーが発生します。
最後のテストとして、を含むドキュメントを入力してみてください ascents
フィールドと total
値ですが、この値は負です:
- db.peaks.insertOne(
- {
- "name": "Everest",
- "height": 8848,
- "location": ["Nepal", "China"],
- "ascents": {
- "first": {
- "year": 1953,
- },
- "first_winter": {
- "year": 1980,
- },
- "total": -100
- }
- }
- )
ネガティブのため total
値の場合、このドキュメントも検証テストに失敗します。
結論
このチュートリアルに従うことで、JSONスキーマドキュメントと、それらをコレクションに保存する前にドキュメント構造を検証するためにそれらを使用する方法に精通しました。 次に、JSONスキーマドキュメントを使用してフィールドタイプを確認し、数値と配列に値の制約を適用しました。 また、ネストされたドキュメント構造でサブドキュメントを検証する方法も学習しました。
MongoDBのスキーマ検証機能は、アプリケーションレベルでのデータ検証の代わりと見なすべきではありませんが、データを意味のあるものに保つために不可欠なデータ制約に違反することをさらに防ぐことができます。 スキーマ検証の使用は、データストレージへのスキーマレスアプローチの柔軟性を維持しながら、データを構造化するための便利なツールになります。 スキーマ検証を使用すると、検証するドキュメント構造の部分と、制限のないままにしておきたい部分を完全に制御できます。
チュートリアルでは、MongoDBのスキーマ検証機能のサブセットのみを説明しました。 さまざまなMongoDBデータ型にさらに制約を適用できます。また、検証動作の厳密さを変更し、JSONスキーマを使用して既存のドキュメントをフィルタリングおよび検証することもできます。 公式の公式のMongoDBドキュメントを調べて、スキーマの検証と、データベースに保存されているデータの操作にどのように役立つかについて学ぶことをお勧めします。