モード変更


    言語

RDBMS経験者が、Firestoreを使うときに知っておくべきこと

2022/02/03

一昔前は、データの保存といえば MySQL や PostgreSQL といったRDBMS(Relational DataBase Management System)を使うことがメインでしたが、最近は Firestore を利用することも増えてきました。
データを保存するという役割は同じですが、RDBとは使い方・設計思想が大きく異るかと思います。

この記事では、RDBとは異なるFirestoreの制約を確認していきます。

集計関数が無い

RDBでは、インデックスやデータ量には依存しますが、数百・数千レコードを対象としても COUNT(*) や SUM(amount) といった集計が比較的手軽にできます。
ですが、Firestoreでは現実的ではありません。

例えば、1つの伝票にそれぞれ10明細が紐付いており、20伝票をリスト表示する画面があったとします。
そのリストに「合計金額」も表示したい場合、RDBではサブクエリで SUM をするなどで、高速に結果を取得できます。

一方、Firestoreで同様のことをする場合は、クライアントサイドで200ドキュメントを読み込み、処理として集計する必要があります。これでは、コストや性能の面で問題があります。
このような場合、明細データの追加・更新時に伝票データの「合計金額」を更新しておき、画面表示時には伝票データのみの参照に抑えることが一般的かと思います。

他の制約においても、このように「書き込み時点で、読み込みのことを考えてデータを作成・更新しておく」という考え方が必要になってきます。

検索は苦手

2つ以上の条件を指定して検索するためには、インデックスを作成する必要があります。
さらに、インデックスを作成しても実行できないクエリが存在します。

範囲(<、<=、>、>=)または不等値(!=)の比較は 1 つのフィールドに対してのみ実行できます。

Cloud Firestore で単純なクエリと複合クエリを実行する | Firebase Documentation

例えば、「作成日時はA以降で、更新日時がB以降」といった、2つのカラムに対する範囲検索ができません。また、範囲検索と別のカラムでのソートもできません。

画面の仕様を変更してしまうか、別の検索用カラムで完全一致検索にするなど、回避策が必要となります。
例えば上記の例であれば、「実際には作成日が”特定の年月”のデータで、更新日時がB以降」で良いのであれば、作成年月(”yyyyMM”の文字列)という別カラムを追加しておくことで、一方の条件を完全一致に変えることができ、この制約を回避できます。

全文検索はできない

特定の文字列を含む投稿を検索するなど、全文検索が必要な場合には、 Algolia などの外部のサービスを利用することが一般的となります。

Cloud Firestore では、ネイティブ インデックスの作成やドキュメント内のテキスト フィールドの検索をサポートしていません。
さらに、コレクション全体をダウンロードして、クライアントサイドでフィールドを検索することは現実的ではありません。

全文検索 | Firebase Documentation

Search with Algolia という Firebase Extensions を利用することで、FirestoreデータをシームレスにAlgoliaに同期することもできます。

件数指定での読み飛ばしができない

RDBMSでは、offsetを指定することでページングを表現できます。
Firestoreでは、limitは指定できますが、offsetは指定できません。
そのため、「ページ番号単位での読み込み」といった仕様に対応するのは難しくなります。

一方で、無限スクロールのような「連続したデータを読み込む」ことは容易に実現できるようになっています。

複数データの一括更新・削除ができない

「条件に該当したデータの、とあるフラグをすべて更新する」とか、「親データを削除したから、そのIDを持つ子データを一括で削除する」といったことができません。
条件指定で一旦取得して、個別に更新・削除していく必要があります。

トランザクションやバッチといった機能は存在するので、atomicに更新・削除することはできます。
ただし、1トランザクションでは500件の書き込みしか行なえません。

Commit オペレーションに渡すか、トランザクションで実行することができる書き込みの最大数 500

使用量と上限 | Firebase Documentation

security ruleはcollection単位のみで、フィールド単位には指定できない

RDBを利用する場合、クライアントとDBの間にAPIを実装するかと思います。
そのため、読み取り制限などはAPI側に”処理”として実装するのが一般的かと思います。

Firestoreの場合は、security ruleとして読み取り制限を定義するため、クライアントが直接Firestoreを参照することが一般的かと思います。

security ruleを設定することで、collection単位でさまざまな制約を付けることができます。
ただし、1つのデータの中で、「特定のフィールドだけは閲覧できない」といった制約を付けることはできません。
そのため、「ほかユーザーに見せる、公開用のユーザー情報」と「本人だけが見える、個人情報などを含んだユーザー情報」が存在する場合には、別々のcollectionで管理するのが一般的となります。

1つのドキュメントに対して、1秒に何度も書き込めない

ドキュメントへの最大継続書き込み速度: 1 秒あたり 1
1 秒あたり 1 回を超える書き込み速度が持続すると、レイテンシが増加し、競合エラーの原因になります。これはハードリミットではなく、短時間のバーストでは上限を超えても構いません。

使用量と上限 | Firebase Documentation

というソフトリミットがあります。

前述の通り集計ができないので、例えば投稿に対する「いいね数」を表示するためには、整数としてカウントアップしていく必要があります。
ただし、「いいね」はたくさんのユーザーから1つの投稿に集中する場合があるので、上記のソフトリミットに制限される可能性がありそうです。
公式ページに、回答が用意されています。

カウンタをもっと頻繁に更新できるようにするには、分散カウンタを作成します。
各カウンタは「シャード」のサブコレクションを持つドキュメントであり、カウンタの値はシャードの値の合計です。

分散カウンタ | Firebase Documentation

簡単に言うと、10個のカウンタドキュメントに分散して保存することでソフトリミットを1/10程度に引き下げ、読み込むときにはそれを合算することで読み込みコストもある程度で済ませる形です。

まとめ

Firestoreは、比較的低コストで運用できたり、リアルタイムに変更を検知することが簡単に実装できたりと、メリットが大きいです。

しかし、今まで利用してきたRDBMSとは、目指している方向や得意な場面が異なっています。
制約を理解した上で、適材適所で使い分けていくことが重要だと思います。

参考

  • 実践Firestore

Article Photo by Jan Antonin Kolar

firestore

Author

Noboru Ishikura

Noboru Ishikura

Mobileエンジニア

2015年から富山でフルリモートワークしているMobileエンジニア。

その他おすすめ記事

2023/03/03

Jetpack Composeでナビゲーションパラメータ、ダイナミックなstartDestinationを実現

私たちのチームは最近、2つの別々のAndroidアプリケーションを必要とするクライアントとのコラボレーションで、Jetpack Composeと全く新しいプロジェクトを行う機会を得ました。このプロジェクトは無事リリースされ、クライアントは最終製品に非常に満足していました。 このブログでは、Jetpack Compose ナビゲーションのパラメータの扱いについて、特に最初はかなり厄介だったので、その過程で得たいくつかの学びを共有したいと思います。 執筆時点では、Jetpack Compose Navigat...

Diarmaid Lindsay

Diarmaid Lindsay

Android

2022/12/20

モンスターラボ2023年度新卒内定者向けイベントレポート

 こんにちは、モンスターラボで BackEnd の TechLead をしています、国平です。  このブログには、 Docker on Lima 以来の投稿になります。 今回は、モンスターラボの新卒採用における内定者イベントのレポートをしてみたいと思います。普段の技術的な話題から離れて、新卒内定者向けのイベントレポートを通して、会社の雰囲気をお伝えしてみようと思います。  11 月初旬、モンスターラボで内定者向けに実際のプロジェクトに参加したメンバーから、プロジェクトの様子を伝えて、モンスターラボのプロジ...

Kiyotaka Kunihira

Kiyotaka Kunihira

サービス開発実績会社情報
採用情報インサイトお問い合わせ
© 2022 Monstarlab
情報セキュリティ基本方針個人情報の取り扱いについて個人情報保護方針