Firestoreは、いわゆる、Key-Value型のNoSQLであるが、ベストプラクティス的なノウハウが圧倒的に少ないのだ。ネットで検索しても出てこない。とくに、あるコレクションと別のコレクションの関連をどうやって表現すればいいのか、わからない。

そこで、今回は、オンラインのこちらの教材の動画を見てみましたので、簡単に内容をメモしました。

Firestore Queries and Data Modeling

データ設計テクニック 

複数のドキュメントの管理について3種類ある.

Embedding

ドキュメントの下に、またドキュメントを置く方法。

Root Collection(Bucketing)

あるコレクションのドキュメントに別のコレクションのドキュメントのフィールドを保持して、必要なときにフィールドから別のルートコレクションをたどる方法。フィールドがとくにIDのときがBucketing?(情報がすくない)

この場合、あるドキュメントに記録したIDの集合から、別のコレクションのIDの集合を取得するために、以下のような並列処理が使える。

// Helper: Reads an array of IDs from a collection concurrently
const readIds = async (collection, ids) => {
   const reads = ids.map(id => collection.doc(id).get() );
   const result = await Promise.all(reads);
   return result.map(v => v.data());
}

SubCollection

ドキュメントの下にコレクショョンを置く方法。

データ取得テクニック

AND, OR, NOT条件の検索。

// Basic Where
const rangeQuery = db.collection('users').where('age', '>=', 21);

// AND
const andQuery = db.collection('users')
               .where('age', '==', 21)
               .where('sex', '==', 'M')


// OR
const q1 = db.collection('users').where('age', '==', 21);
const q2 = db.collection('users').where('age', '==', 25);


// NOT
const not1 = db.collection('users').where('age', '>', 21);
const not2 = db.collection('users').where('age', '<', 21);

関係データの保持方法

one-to-one

ドキュメントIDを保持することで、2つのドキュメントをつなぐ。このとき、セキュリティを意識して、コレクションを分けることが大事。たとえば、公開データと非公開データなど。

one-to-many

一番カンタンなのは、Embeddingでデータを保持することだけれども、これだとある条件のデータの取得やフィルタが難しい。クエリを意識するときは、Embeddingではなくて、SubCollectionやRoot Collectionを検討する。

many-to-many

よくつかうのがこれ。ふたつのドキュメントを中間コレクションでつなぐ。

たとえば、books, authersをつなぐために、reviewsテーブルを用意して、

- ID: (bookId)_(autherID)
  - bookID: bookID
  - autherID: autherID
  - label: xxx

みたいな構成でreviewドキュメントを保持する。

発展テクニック

Cloud Functionsと組み合わせたテクニック。

Duplication

Embeddingしつつ、Root Collectionする方法。Embeddingしたあとに、Root Collectionにコピーする。このとき、Cloud FunctionsでFirestoreの状態変化を監視して、非同期にコピーする。

Aggregation

たとえば、いいねが追加されたら、その総数をCloud Functionsをつかって非同期に計算して、総数をどこかのドキュメントに記録する。