Swift+Cloud Firestore:如何用单查询获取有聊天记录的用户信息?
解答:Swift + Cloud Firestore 实现聊天联系人列表
嘿,这个需求在聊天应用开发里挺常见的~先直接给你结论:没办法通过单次Firestore查询直接实现,因为Firestore不支持跨集合的JOIN关联操作(类似SQL里的表关联)。不过我们有两种高效的方案来达成目标,下面给你详细拆解:
方案一:两步批量查询(推荐,兼顾性能和数据一致性)
这是行业内最常用的方案,看似两步但实际是两次高效的批量请求,性能表现很好:
- 从
Messages集合中筛选出所有你参与过的聊天,提取出所有对方的userId并去重 - 用这些去重后的
userId,批量查询Users集合获取对应的name和profilePic
Swift 代码示例(基于Async/Await)
import FirebaseFirestore func fetchChatContacts(currentUserId: String) async throws -> [(name: String, profilePic: String)] { let db = Firestore.firestore() // 第一步:获取所有参与过的聊天的对方ID let messagesQuery = db.collection("Messages") .whereFilter( .orFilter([ .whereField("senderId", isEqualTo: currentUserId), .whereField("receiverId", isEqualTo: currentUserId) ]) ) let messagesSnapshot = try await messagesQuery.getDocuments() // 提取并去重对方ID,排除自己 var contactUserIds = Set<String>() for doc in messagesSnapshot.documents { let senderId = doc["senderId"] as! String let receiverId = doc["receiverId"] as! String let otherUserId = senderId == currentUserId ? receiverId : senderId contactUserIds.insert(otherUserId) } guard !contactUserIds.isEmpty else { return [] } // 第二步:批量查询用户信息(只请求需要的字段,减少带宽) let usersQuery = db.collection("Users") .whereField("userId", in: Array(contactUserIds)) .fields(["name", "profilePic"]) let usersSnapshot = try await usersQuery.getDocuments() // 整理结果 var contacts = [(name: String, profilePic: String)]() for doc in usersSnapshot.documents { guard let name = doc["name"] as? String, let profilePic = doc["profilePic"] as? String else { continue } contacts.append((name: name, profilePic: profilePic)) } return contacts }
方案二:反范式设计(适合用户资料极少变更的场景)
如果你的应用中用户的name和profilePic几乎不会修改,可以在Messages集合的每个文档里额外存储对方的name和profilePic字段。这样你只需要查询一次Messages集合,提取去重后的用户信息即可。
注意事项
- 优点:只需一次查询,性能更优
- 缺点:当用户修改头像或名字时,你需要批量更新所有相关的
Messages文档,维护成本较高
示例文档结构(Messages集合)
{ "senderId": "user123", "receiverId": "user456", "text": "Hello!", "date": "2024-05-20T12:00:00Z", "senderName": "张三", "senderProfilePic": "https://example.com/avatar1.jpg", "receiverName": "李四", "receiverProfilePic": "https://example.com/avatar2.jpg" }
额外优化建议
- 给
Messages集合创建复合索引:针对senderId和receiverId的OR查询需要提前创建索引(Firestore控制台会在你运行代码后弹出提示,直接点击链接即可自动创建) - 可以在本地缓存联系人列表,避免频繁重复查询
- 若需要实时更新联系人列表,可以给查询添加快照监听(
addSnapshotListener)
内容的提问来源于stack exchange,提问作者BuFaten
相关产品推荐
相关产品推荐

