Firestoreでの多対多 (m:n) 関連

この公式動画の内容の備忘録

www.youtube.com

アプリのシナリオ

  • レストランを見れるアプリ。ユーザーはそれぞれのレストランのレビューを見れる。

登場エンティティ(ドキュメント)

  • Restaurant
  • Review

構成方法の選択肢

  • Reviewを独立したドキュメントにするのをやめる。Restaurantにmapとかmapの配列でReviewを持たせる。
  • RestaurantsとReviewsを共にトップレベルのコレクションにする
  • RestaurantsのサブコレクションとしてReviewsを用意する

どれを選ぶか

Reviewを独立したドキュメントにするのをやめる。Restaurantにmapとかmapの配列でReviewを持たせる。

  • 利点:
    • レストランをタップすると、すぐにレビューが見れる
    • 読み込むドキュメント数を節約できる
  • 制約:
    • 一覧のクエリだと不要なデータが付随する。例: レストランのtop20を閲覧。レストランの一覧を表示したいだけなのにレストランごとの全てのレビューデータまで付随することになる。
    • ドキュメントの制約以上のレビューは持てない。1MBもしくは20,000フィールドを超えるユースケースに対応できない。
    • 「最新のレビュー10件」といったクエリはできない。レストランごと読み込んで、クライアントで処理する必要がある。

考えメモ: この制約が許容できるユースケースならあり。

RestaurantsとReviewsを共にトップレベルのコレクションにする

  • 利点:
    • Reviewsに対するクエリがあれこれできる。
  • 制約:
    • レストランごとのソートされたレビューのクエリとかしたい場合、Composite indexが必要になる。レストランのサブコレクションとしてレビューが構成される場合、不要なもの。
    • 他のドキュメントの内容によって別のドキュメントの操作に制限をかけたい場合、サブコレクションを使うより難しくなりうる。
      • 例: レストランのオーナーならレビューの編集をできる、など。親がレストランで子にレビューがある場合、比較的簡単。

メモ: レストランの関係なくレビューを主体とするクエリがほとんどならこれを選ぶと良さげ

RestaurantsのサブコレクションとしてReviewsを用意する

  • 利点:
    • Composite indexが不要
    • 親ドキュメントの内容に基づく操作の制約が比較的簡単
  • 制約:
    • ドキュメントのread, writeで課金されるため、そのレストランのトップ10のレビューを画面に表示したい場合、レストラン1 + レビュー10の計11の読み込みになる。レストランを表示するたびにそれだけかかってしまう。
  • 対処:
    • レビューのスニペットをレストラン本体に入れる。トップレビュー10の最初の100文字とか。内容はCloud Functionで同期できる。
  • 考慮:
    • 上記を実現するためのコードを書くのと、そのままドキュメントを読ませるのとどちらの費用対効果が良いかは考慮して決める。対して節約にならないならコード書く必要ないかも。

他のシナリオ

省略

まとめ

  • レストランのお気に入りを作るような場合、 FavariteRestaurantsというコレクションを作って、user_idとrestaurant_idを持たせる中間テーブルみたいなドキュメントを作るのがおすすめぽい: 上記動画12:22~ あたりから
  • 料金体系は、ドキュメントのRead, Write単位でかかり、全体のスキャン量などでは決まらない。 料金  |  Firestore  |  Google Cloud

感想

Azure Table Storageの場合はテーブルスキャンになるとコストが高くなる。また、エンティティ間のトランザクションにも同一Partition限定など制約がある。一方Firestoreの場合はスキャンのコストまでは考えなくてのが楽だ。また、Partitionが異なる場合になるようなユースケース、つまりドキュメントが異なる場合でもトランザクションを使えるのが良い。