[Firestore]コレクションにするか、MapのArrayにするか
はじめに
データ量がたいして大きくならない見込みの、技術ブログのタグなどのようなデータをFirestoreで管理する場合
- コレクションとして複数のドキュメントに分けて保存する
- Map型(オブジェクト型)の配列として一つのドキュメントに保存する
のどちらの方法を取るべきかを考えました。
以降ではこの2つの保存方法を「コレクション」、「Map型の配列」と呼んでいます。
具体例
具体的なデータの例として、技術ブログなどのタグのデータを考えます。タグの数は100個あるとします。
次項の獲得時間の測定では、このデータを使用して測定しています。
コレクションの例
Map型の配列の例
比較
2つの方法を以下の3項目について比較します。
全件獲得にかかる時間
Firestoreのからタグデータ100件の全件取得をそれぞれ5回行い、データの獲得までにかかる時間を測定しました。
コレクション(ms) | MapのArray(ms) |
---|---|
179.6 | 101.4 |
68.7 | 45.6 |
102.3 | 46.4 |
62.9 | 54.8 |
74.3 | 46.9 |
平均: 97.6 | 平均: 59.0 |
値のばらつきが大きいのが気になりますが、コレクションの方がMap型の配列と比べて、2倍近くの処理時間がかかることがわかりました。
実測するまでは、ドキュメントを1件だけ獲得するMap型の配列に比べて、ドキュメントを100件獲得する必要があるコレクション方式では、100倍近くの時間がかかるのではないかと考えていましたが、そこまで大きな違いはなかったのです。
測定に使ったコードはこちら
// コレクションの全件取得
const subCollection = () => {
const start = performance.now();
return getDocs(collection(db, "コレクションのパス")).then((snaps) => {
const end = performance.now();
console.log(`subCollection: ${end - start} ms`);
console.log(snaps.docs.map((doc) => doc.data()));
});
};
// Map型の配列の全件取得
const mapArray = () => {
const start = performance.now();
return getDoc(doc(db, "ドキュメントのパス")).then((snap) => {
const end = performance.now();
console.log(`mapArray: ${end - start} ms`);
console.log(snap.data()?.tags);
});
};
for (let i = 0; i < 5; i++) {
await subCollection();
await mapArray();
}
Firestoreの使用量
Firestoreの料金の計算に用いられる「ドキュメント読み取り回数」、「ドキュメント書き込み回数」、「下りネットワークの使用量」を比較しました。
全件獲得する場合
コレクションの「ドキュメント読み取り回数」がドキュメントの総数(今回の例では100)になるのに対して、Map型の配列では1回で済むので、Map型の配列の方が適している。
1件だけ獲得する場合
「ドキュメント読み取り回数」はどちらも1回だが、「下りネットワークの使用量」がコレクションではタグ1つ分のデータなのに対して、Map型のArrayでは全件のデータ分加算される。よって、コレクションの方が適している。
新規作成・更新する場合
1つのタグを更新・作成する場合は使用量は同じ。
複数のタグをまとめて、作成・更新する場合は1回の書き込みで済むMap型の配列の方が適している。
その他のメリット・デメリット
-
Map型の配列でクエリをする場合は、全件取得しクライアント側で配列のフィルター、ソートを行うことになる。データの件数や、クエリの計算量、端末のスペックによっては処理時間が大きくかかるかもしれない。
-
コレクションではFirestore ruleで、ドキュメントごとにアクセス制御出来る。
まとめ
これまでの項を踏まえて、データ量が1MiBを超えることがなさそうな、今回例に上げたタグのようなデータは、Map型の配列として保存した方がメリットが多いと考えます。
特に、全件獲得の際の「ドキュメント読み取り回数」が1になることは、Firebaseの課金料を大きく減らす可能性のある、重要なメリットだと思います。
一方、データ・フィールドの量が増えて、Map型の配列にしたら1MiBを超える可能性のある場合や、データの件数が多くクライアント側でのクエリが困難な場合、Firestore Rules細かいアクセス制御が必要な場合は、コレクションにすることが必須になると思われます。
ここまで読んでいただきありがとうございました。この記事に誤っている点・誤解を招く点や、アドバイスなどがありましたらぜひ、コメントなどで教えていただきたいです!
Discussion