💣

MongoDBのSchemaなるもの

2021/12/12に公開

この記事は闇の魔術に対する防衛策Advent Calender 2021 12日目の記事です。今年は例年と比べると闇要素が薄いですが、一応今年も書いておきます[1]

MongoDBのSchema

MongoDBは「スキーマレスなNoSQL DB」と紹介されることが多いです。しかしMongoDBにはスキーマがないというわけではありません。

そもそも「スキーマ」って何なのでしょう。MongoDBのドキュメントを見てもあまりぱっとする答えは書いていません。「スキーマレス」なのですから。初級者にはさっぱりです。

あまりはっきり言えませんが、自分の理解を示しておきます。データベースでのスキーマとは、一言でいうとデータの構造のことです。MongoDBはスキーマレスと言っていますが、それは「事前にスキーマを定義する必要がない」ということです。

また、言い換えると「スキーマの異なるデータ(ドキュメント)も格納できる」ということです。ここでのポイントは「各ドキュメントはスキーマを持っている」ということです。すなわちスキーマがないというわけではないのです。

その証拠に、MongoDBのGUIクライアントであるMongoDB Compassには「Analyze Schema」という機能があります。これは各ドキュメントがどういうスキーマを持っているかを統計する機能です。

スキーマを定義しなくて良いということは、「スキーマが統一されない」というデメリットもはらむことになります。適当な構造のデータでもそのまま保存できてしまうわけなので、想定される項目が欠落しているデータや、逆に余計な項目も含んでいるデータが保存されてしまいます。

MongooseのSchema

MongoDBを扱うとき、Mongooseというラッパーを通して使うことがあります。Mongooseの特徴のひとつにSchemaを定義することができるということがあります。

const blogPostSchema = new Schema({
	author: String,
	title: String,
	body: String,
});

「Schemaをわざわざ作らないといけないということは、それなりにスキーマチェックをしてくれたりしてるんだろう」と僕は思いました。標準では作らなくてもいいものを作っているということは何か理由があるはずです。初見だとそう思いませんか?

MongooseのSchemaはスキーマチェックをしているわけではない

ここが超重要なポイントです。MongooseのSchemaは実はスキーマチェックをしているわけではありません。つまりSchemaの項目が抜けていたりしても何も問題なくMongoDBへ投稿されてしまいます。

試しに、上のblogPostSchemaで次のようなデータをsaveしてみましょう。

const doc = new myModel({
	// author: 欠損値
	title: "hello",
	body: "Hi",
});
doc.save((err) => {if(err) {throw err;}});

なんと、このコードはそのまま通って、データは欠損値を持ったまま保存されます。なんでやねん。

Mongooseでスキーマチェックをする方法

とはいえ、スキーマチェックする方法はあります。Schemaの定義をするときにrequiredオプションをつけることで、その項目を含まないデータを排除することができます。

const blogPostSchema = new Schema({
        author: {
            type: String,
            required: true,
        },
	title: String,
	body: String,
});
// errが出る
doc.save((err) => {if(err) {throw err;}});
// => Uncaught ValidationError: myblog validation failed: author: Path `author` is required.

しかし、Schemaを定義する機能は実は生のMongoDB Clientにも搭載されています。

https://docs.mongodb.com/manual/core/schema-validation/

本家のMongoDBがSchema機能を実装したのはver3.2からなので、それまではSchemaを使うためにはMongooseが必要だったのだろうと思います。

というかModelというネーミングがすでに違う気がするし生のMongoDB Client使うのと比べて何のメリットがあるのかわからんし正直Mongooseは使いたくない。

余談

MongoDBはNode.jsを指向しています。Python用のライブラリなどもありますが、超クセがあるのでNode.js+ExpressでやってたことをPython+Flaskに移植したりするとえらい目に遭います。

脚注
  1. 実はとんでもない闇の魔術と今戦っていますが、現在進行系の事案のため控えておきます。 ↩︎

Discussion