背景
あるドキュメントが削除された際に、そのドキュメント配下にあるサブコレクションは残ってしまうという問題に直面した。
原因
Firestoreでは、ドキュメントを削除しても、そのドキュメント配下のサブコレクションは自動的には削除されない。
これは、特にドキュメントがサブコレクションを多く持っている場合、手動で一つずつ削除するのは非効率的で面倒な作業。
やること
ドキュメントが削除された際に、そのドキュメント配下にあるサブコレクションもまとめて削除できるようにする。
解決案
Firebase Admin SDKを使用して、ドキュメントとそのサブコレクションを一括で削除する方法を採用。
Admin SDKにはrecursiveDeleteメソッドがあり、これを使用することで指定したドキュメントとそのサブコレクションを再帰的に削除することができる。
実装 Admin SDKで再帰削除する
最初、firebase-toolsを使った方法で試みたが、tokenの問題などに引っかかったり、その方法では有効期限切れも気にしないといけない。(ドキュメント)
それが嫌だったので、代わりにAdmin SDKを使う方法を試みた。(ドキュメント)
完成したコードが以下。
import * as functions from "firebase-functions/v1";
import * as FirebaseAdmin from "firebase-admin";
import * as logger from "firebase-functions/logger";
FirebaseAdmin.initializeApp();
const db = FirebaseAdmin.firestore();
export const onDeleteDocuments = functions
.region("asia-northeast1") // regionの指定が必要ないなら、この行はいらない
.firestore.document("collections/{collectionId}/subcollections/{subcollectionId}") // トリガーとなるドキュメント
.onDelete(async (snapshot, context) => {
const path = snapshot.ref.path; // 削除されたcollectionのpathを取得
try {
const docRef = db.doc(path);
await db.recursiveDelete(docRef); // Admin SDKで再帰削除できる
logger.log(`Successfully deleted collection and subcollection`);
} catch (err) {
logger.error(`Error deleting collection and subcollection: `, err);
}
});
ポイントとしては、以下があれば再帰削除ができるっぽい。
FirebaseAdmin.initializeApp();
const db = FirebaseAdmin.firestore();
// 中略
const docRef = db.doc(path);
await db.recursiveDelete(docRef);
コード解説
Firebase Admin SDKの初期化
FirebaseAdmin.initializeApp();
はFirebase Admin SDKを初期化し、FirestoreなどのFirebaseのサービスにアクセスする準備みたいな感じ。
Firestoreインスタンスの作成
const db = FirebaseAdmin.firestore();
これでFirestoreのインスタンスを作成。これにより、Firestoreのデータにアクセスできる。
Cloud Functionsでのトリガー設定
特定のFirestoreドキュメントが削除されたときにトリガーされるCloud Functionを設定。
.firestore.document("collections/{collectionId}/subcollections/{subcollectionId}")
.onDelete(async (snapshot, context) => {
// ...
});
この関数は、指定されたドキュメントパス(ここでは "collections/{collectionId}/subcollections/{subcollectionId}"
)にあるドキュメントが削除されると自動的に呼び出される。
削除されたドキュメントのパスの取得と再帰削除
削除されたドキュメントのパスを取得し、そのパスに基づいてドキュメントとそのサブコレクションを再帰的に削除。
const path = snapshot.ref.path; // 削除されたドキュメントのパスを取得
try {
const docRef = db.doc(path);
await db.recursiveDelete(docRef); // 再帰的に削除
logger.log(`Successfully deleted collection and subcollection`);
} catch (err) {
logger.error(`Error deleting collection and subcollection: `, err);
}
recursiveDelete
メソッドは、指定されたドキュメントとその下にあるすべてのサブコレクションを削除する。
お疲れ様でした。
この方法により、Firestoreのデータ構造における親子関係を持つデータの整合性を維持しつつ、不要になったデータを効率的に削除できると思う。
また、この処理はサーバーサイドで実行されるため、クライアント側の負荷を軽減できる利点もある。
わからないところ、間違っているところ、もっといい方法がある場合は、コメントでもDMでも教えてください。