#MongoDBの制約
MongoDBのコレクションに必須項目などの制約を設けたい時があります。制約の付け方については公式ドキュメントに例があるんですけど、色々とカバーしてある分、初見ではなかなか分かりづらいんですよね。
ここでは最小構成として、例えばオラクルで言うところの「NOT NULL」制約を設けたいと思います。MongoDBの言葉では「required」制約ですね。
#コレクション作成時に設定する場合
DBバージョン3.6からはjson Schemaを使用できます。使うのは$jsonSchemaという演算子ですね。
ここではtestというデータベースを使います。createCollectionメソッドを使って明示的にコレクション作成しますが、その際にvalidatorにて制約を設定してみます。
> use test
switched to db test
> db.createCollection("project_d", {validator: {$jsonSchema: {required: ["name"]}}})
{ "ok" : 1 }
上記の例ではnameを必須項目としています。設定の確認はgetCollectionInfosメソッドで行えます。jsonのツリーが深くて見にくいのですが(笑)、nameがrequiredに含まれているっぽい事が分かります。
> db.getCollectionInfos({name: "project_d"})
[
{
"name" : "project_d",
"type" : "collection",
"options" : {
"validator" : {
"$jsonSchema" : {
"required" : [
"name"
]
}
}
},
"info" : {
"readOnly" : false,
"uuid" : UUID("7fa2aca9-42f7-4660-b276-bce2e75f0f3a")
},
"idIndex" : {
"v" : 2,
"key" : {
"_id" : 1
},
"name" : "_id_",
"ns" : "test.project_d"
}
}
]
次は動作確認です。nameを含まないドキュメントを挿入してみます。
> db.project_d.insertOne({_id: 0})
2018-01-02T18:18:16.287-0800 E QUERY [thread1] WriteError: Document failed validation :
WriteError({
"index" : 0,
"code" : 121,
"errmsg" : "Document failed validation",
"op" : {
"_id" : 0
}
})
...
うまく弾かれてくれました。必須項目を含める事でちゃんと挿入できます。
> db.project_d.insertOne({_id: 0, name: "takumi", role: "downhill"})
{ "acknowledged" : true, "insertedId" : 0 }
> db.project_d.find()
{ "_id" : 0, "name" : "takumi", "role" : "downhill" }
#既存コレクションに追加する場合
後から制約を設ける場合には、runCommandメソッドを使います。チームにおける役割をはっきりさせるのは重要ですので、上の例の「role」を必須項目にしてみましょう。
> db.runCommand({collMod: "project_d", validator: {$jsonSchema: {required: ["name", "role"]}}})
{ "ok" : 1 }
> db.getCollectionInfos({name: "project_d"})
[
{
"name" : "project_d",
"type" : "collection",
"options" : {
"validator" : {
"$jsonSchema" : {
"required" : [
"name",
"role"
]
}
},
"validationLevel" : "strict",
"validationAction" : "error"
},
"info" : {
"readOnly" : false,
"uuid" : UUID("7fa2aca9-42f7-4660-b276-bce2e75f0f3a")
},
"idIndex" : {
"v" : 2,
"key" : {
"_id" : 1
},
"name" : "_id_",
"ns" : "test.project_d"
}
}
]
設定できたようです。そしたら新しいデータを入れてみます。
> db.project_d.insertOne({_id: 1, name: "keisuke"})
2018-01-02T18:29:47.788-0800 E QUERY [thread1] WriteError: Document failed validation :
WriteError({
"index" : 0,
"code" : 121,
"errmsg" : "Document failed validation",
"op" : {
"_id" : 1,
"name" : "keisuke"
}
})
...
> db.project_d.insertOne({_id: 1, name: "keisuke", role: "hillclimb"})
{ "acknowledged" : true, "insertedId" : 1 }
roleを省いた方はちゃんと弾かれておりますね。
今回は最小構成で動かす事を意識してrequired制約だけを実装してみましたが、json Schemaの中では、データ型の指定など、種々のvalidationもかける事ができます。必要に応じてincrementalに実験して行きたい所存です。