You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.11 07:37:53