Firebase Firestoreでは、ドキュメントごとに異なるサブコレクションを持つことができます。これにより、階層的なデータ構造を簡単に実現できます。以下では、サブコレクションの基本的な概念から、具体的な実装方法までを説明します。
1. サブコレクションとは?
Firestoreのサブコレクションは、ドキュメントの中に格納されるコレクションのことです。各ドキュメントは独自のサブコレクションを持つことができます。
例えば、下記のような構造を考えてみましょう:
users (コレクション)
└─ userId (ドキュメント)
└─ posts (サブコレクション)
├─ postId1 (ドキュメント)
├─ postId2 (ドキュメント)
ここでは、users コレクションの各 userId ドキュメントに posts サブコレクションを持つという構造です。
2. サブコレクションの作成
Firestoreにサブコレクションを作成する際は、以下の手順で実装できます。
2.1 データの書き込み
以下は、あるユーザーの posts サブコレクションに新しい投稿を追加する例です。
import { doc, collection, addDoc } from "firebase/firestore";
import { db } from "./firebase"; // Firebase初期化ファイル
const addPostToUser = async (userId, postContent) => {
try {
// 'users/userId/posts' のパスにサブコレクションを指定
const postsCollectionRef = collection(db, `users/${userId}/posts`);
await addDoc(postsCollectionRef, {
content: postContent,
createdAt: new Date(),
});
console.log("Post added successfully!");
} catch (error) {
console.error("Error adding post: ", error);
}
};
addPostToUser("user123", "This is a new post!");
2.2 データの読み込み
特定のユーザーの posts サブコレクション内のすべてのドキュメントを取得するには以下のようにします。
import { collection, getDocs } from "firebase/firestore";
const getUserPosts = async (userId) => {
try {
const postsCollectionRef = collection(db, `users/${userId}/posts`);
const querySnapshot = await getDocs(postsCollectionRef);
querySnapshot.forEach((doc) => {
console.log(`${doc.id} =>`, doc.data());
});
} catch (error) {
console.error("Error getting posts: ", error);
}
};
getUserPosts("user123");
3. サブコレクションの監視
Firestoreではリアルタイムでサブコレクションのデータ変更を監視することも可能です。
import { collection, onSnapshot } from "firebase/firestore";
const listenToUserPosts = (userId) => {
const postsCollectionRef = collection(db, `users/${userId}/posts`);
onSnapshot(postsCollectionRef, (snapshot) => {
snapshot.docs.forEach((doc) => {
console.log(`${doc.id} =>`, doc.data());
});
});
};
listenToUserPosts("user123");
4. サブコレクションの削除
Firestoreでは、コレクション全体を直接削除するメソッドはありません。そのため、サブコレクション内のすべてのドキュメントを個別に削除する必要があります。
import { collection, getDocs, deleteDoc, doc } from "firebase/firestore";
const deleteUserPosts = async (userId) => {
const postsCollectionRef = collection(db, `users/${userId}/posts`);
const querySnapshot = await getDocs(postsCollectionRef);
querySnapshot.forEach(async (docSnapshot) => {
await deleteDoc(doc(db, `users/${userId}/posts`, docSnapshot.id));
});
console.log("All posts deleted for user:", userId);
};
deleteUserPosts("user123");
5. セキュリティルールの設定
サブコレクションに対する適切なアクセス制御を行うため、Firestoreのセキュリティルールを設定します。
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
match /users/{userId}/posts/{postId} {
allow read, write: if request.auth != null && request.auth.uid == userId;
}
}
}
このルールでは、認証されたユーザーが自身のデータにのみアクセスできるように制限しています。
6. ベストプラクティス
コレクションの深さを管理サブコレクションの階層を深くしすぎると、データのクエリやパフォーマンスが低下する可能性があります。適切な階層構造を設計してください。
インデックスを活用複雑なクエリを使用する場合は、Firestoreのインデックスを作成し、クエリパフォーマンスを最適化します。
まとめ
Firestoreのサブコレクションは、複雑なデータ構造を簡単に管理できる強力な機能です。このガイドを参考に、サブコレクションを使用した効率的なデータ管理を実現してください。