如何在Firestore构建关注系统?Firebase实时数据库迁移方案
迁移到Firestore的关注系统实现
我来帮你梳理下怎么把这个关注系统从实时数据库迁移到Firestore,先从数据结构设计说起,再对应改写代码~
一、Firestore数据结构设计
Firestore是文档型数据库,和实时数据库的键值结构思路略有不同,这里给你两种常用的设计方案,你可以根据业务场景选择:
方案1:用户子集合存储关注关系(推荐,贴合原有逻辑)
这个方案和你之前的实时数据库结构逻辑最接近,只是把节点换成Firestore的子集合,适合关注/粉丝量较大的场景:
-users (如果已有用户集合可直接复用) -[userId] -following (子集合) -[followedUserId]: {} (空文档仅标记关注关系,也可存被关注者昵称、头像等基础信息,减少跨集合查询) -followers (子集合) -[followerUserId]: {}
优势是查询关注/粉丝列表时直接读取对应子集合即可,性能稳定,且不受文档大小限制。
方案2:独立集合存储关注关系(适合复杂查询场景)
如果之后需要做“查询同时关注A和B的用户”这类复杂查询,可用独立的follows集合,每个文档代表一条关注关系:
-follows -[自动生成的文档ID] -followerId: "当前用户UID" -followingId: "被关注用户UID" -createdAt: Timestamp.now() (可选,记录关注时间)
写入时仅需创建一个文档,但查询关注状态需要匹配两个字段。从你原有代码逻辑来看,方案1更贴合习惯,下面的代码示例基于方案1编写。
二、对应代码实现
1. 检查关注状态 checkFollowingStatus
之前实时数据库是监听节点是否存在,Firestore里我们监听对应子集合的文档是否存在:
private fun checkFollowingStatus(uid: String, followButton: Button) { firebaseUser?.uid?.let { currentUserId -> // 查询当前用户的following子集合中是否存在目标用户的文档 val followingDocRef = FirebaseFirestore.getInstance() .collection("users") .document(currentUserId) .collection("following") .document(uid) // 添加快照监听,实时更新按钮状态 followingDocRef.addSnapshotListener { snapshot, error -> if (error != null) { // 可在此添加错误处理逻辑 return@addSnapshotListener } followButton.text = if (snapshot?.exists() == true) "Unfollow" else "Follow" } } }
2. 关注/取消关注按钮点击事件
和原有逻辑一致,判断按钮文本后执行添加或删除操作。这里用Firestore的批量操作保证两个操作(添加/删除following和followers)的原子性:
holder.followButton.setOnClickListener { val currentUserId = firebaseUser?.uid ?: return@setOnClickListener val targetUserId = user.getUID() val firestore = FirebaseFirestore.getInstance() if (holder.followButton.text.toString() == "Follow") { // 创建批量写入任务,保证两个操作要么都成功,要么都失败 val batch = firestore.batch() // 1. 在当前用户的following子集合添加目标用户的文档 val currentFollowingRef = firestore.collection("users") .document(currentUserId) .collection("following") .document(targetUserId) batch.set(currentFollowingRef, emptyMap()) // 空文档仅标记存在,可替换为带用户信息的Map // 2. 在目标用户的followers子集合添加当前用户的文档 val targetFollowersRef = firestore.collection("users") .document(targetUserId) .collection("followers") .document(currentUserId) batch.set(targetFollowersRef, emptyMap()) // 提交批量操作 batch.commit().addOnSuccessListener { // 操作成功,按钮状态会被快照监听器自动更新,可在此添加提示逻辑 }.addOnFailureListener { e -> // 处理失败情况,比如Toast提示 } } else { // 取消关注,同样用批量操作删除两个文档 val batch = firestore.batch() val currentFollowingRef = firestore.collection("users") .document(currentUserId) .collection("following") .document(targetUserId) batch.delete(currentFollowingRef) val targetFollowersRef = firestore.collection("users") .document(targetUserId) .collection("followers") .document(currentUserId) batch.delete(targetFollowersRef) batch.commit().addOnSuccessListener { // 取消关注成功 }.addOnFailureListener { e -> // 处理失败情况 } } }
小提示
- 如果项目里没有
users集合,也可以直接用根级别的following和followers集合,比如following/[currentUserId]/[targetUserId],代码里把collection("users").document(currentUserId).collection("following")换成collection("following").document(currentUserId).collection("userIds")即可。 - 若想在关注时存储更多信息(比如被关注者昵称),可把
emptyMap()替换为hashMapOf("username" to user.getUsername(), "avatarUrl" to user.getAvatarUrl()),后续查询关注列表时无需再拉取用户集合信息,提升性能。
内容的提问来源于stack exchange,提问作者Bryt Brode
相关产品推荐
相关产品推荐

