個人開発でFirestoreを使っているので情報を集めている。なんか日本語だとググっても要領を得ない記事ばっかりだったが、英語でちゃんと調べたらFirebase Blogでしっかり書いてあった
FirestoreなどのNoSQLデータベースは前提として非正規化(denormalization)してサブコレクションをつくることでデータの関連を表現することが推奨されている。これには様々な理由*1があるが、Firestoreではコレクション・グループ(Collection Group)という機能を使うことで正規化したデータのようにサブコレクションを扱うことできる。
実際の例
たとえば「Firestoreで食べログ的なアプリケーションを作る」というケースでは単純に考えてRestaurantコレクション配下に、そのレストランについているレビューのデータを表現するReviewコレクションが存在する、ということは容易に想像できる。 このデータ設計では、ある特定のレストランのレビュー一覧を取得することは簡単であるが、もしレストラン関係なくデータとして存在するすべてのレビューを横断して取得したい、というユースケースの際には困ることになる。もし雑にやるとしたら、まずはレストランの一覧を取得した上で、こんどはレストラン毎にレビューの一覧を取得するという、どう考えてもN+1にしかならない処理をしなくてはいけなくなる。
それどころか「全てのレビューから日付が昨日以前のものだけクエリしたい」や「ある特定のユーザーが書いているレビューのみだけクエリしたい」というケースの場合にも一旦すべてのデータを取得せなばならず、どう考えてもオーバーヘッドと金がかかる処理にならざるをえない。
コレクション・グループによる解決
つまり、これを解決するのがコレクショングループである。非正規化されているデータであっても、インデクスが貼ってあればまるで正規化されたRDBMSでJOINするかのようにデータをクエリすることができる。すごい。
コレクション・グループを使うには事前にインデクスを貼っておく方法と、もしどこにインデクスを貼っていいか分からなければ、とりあえず collectionGroup
メソッドを使ってログに出てくる警告メッセージからオンデマンドでインデクスをFirestoreに作らせる方法があるとのこと。
注意点
最後に、注意点として記載されていたがコレクション・グループでは異なるコレクション配下であるとしても重複したサブコレクション名を許容できないとのこと。たとえばRestraurantコレクションとCouriersコレクションそれぞれにサブコレクションとしてReviewsというものがあると、それらは個別のものとしてみなされないらしい。解決策としてコレクションの名前にユニークなプレフィクスを付ける(たとえばCourierReviews的な)というものがあるらしいが、これは早めに気づいておかないと大きな問題になる気がする。
おもしろかったFAQとして「Firestoreのデータ構造もRDBMSのように正規化してトップレベルにRestraurantやReviewのようなコレクションを配置して毎回クエリで絞りこめばいいのにコレクション・グループを推す意味はなんなのか」というものがあり、コレに対する答えとしてFirestoreはパフォーマンス上の制約からカスタムインデクス*2が最大で200個までしか許容されていないという仕様が上げられている。Firestoreでデータを正規化するのか、しないのかの議論は個別のユースケースによるものであるので、一概に絶対に非正規化することが常に正しい言い切れないが。
- 作者: 河村康爾,北沢匠,佐伯嘉康,佐藤直生,原沢滋,平山毅,李昌桓,渡部徹太郎
- 出版社/メーカー: 秀和システム
- 発売日: 2017/05/09
- メディア: Kindle版
- この商品を含むブログを見る