如何在Firebase users集合查询中排除已拉黑的指定用户ID
解决方案
因为Firebase Realtime Database原生不支持「排除指定值」的查询条件,所以我们采用「先拉取拉黑名单、再过滤用户查询结果」的方案实现需求,具体修改如下:
前置准备
先在当前页面的类中新增一个属性,用来存储所有已拉黑的用户ID,Set类型的查询效率远高于数组,适合频繁判断包含关系:
// 存储当前登录用户拉黑的所有用户ID private var blockedUids: Set<String> = []
修改后的完整拉取逻辑
func fetchUsers() { guard let currentUid = Auth.auth().currentUser?.uid else { return } // 首次加载时先拉取所有拉黑用户ID,再查询用户列表 if userCurrentKey == nil { BLOCKED_USERS.child(currentUid).observeSingleEvent(of: .value) { [weak self] snapshot in guard let self = self else { return } // 清空旧的拉黑ID,重新存入最新的所有拉黑ID self.blockedUids.removeAll() for child in snapshot.children { guard let childSnap = child as? DataSnapshot else { continue } self.blockedUids.insert(childSnap.key) } // 拿到所有拉黑ID后再执行首次用户查询 USER_REF.queryLimited(toLast: 10).observeSingleEvent(of: .value) { snapshot in guard let first = snapshot.children.allObjects.first as? DataSnapshot, let allObjects = snapshot.children.allObjects as? [DataSnapshot] else { return } // 临时数组存储本次查询到的合法用户,避免频繁刷新列表 var tempUsers: [User] = [] let group = DispatchGroup() allObjects.forEach { snap in let uid = snap.key // 过滤掉当前用户自己、以及已拉黑的用户 guard uid != currentUid, !self.blockedUids.contains(uid) else { return } group.enter() Database.fetchUser(with: uid) { user in tempUsers.append(user) group.leave() } } // 所有用户信息拉取完成后统一刷新列表 group.notify(queue: .main) { self.users.append(contentsOf: tempUsers) self.tableView.reloadData() self.userCurrentKey = first.key } } } } else { // 分页加载逻辑,同样增加过滤规则 USER_REF.queryOrderedByKey().queryEnding(atValue: userCurrentKey).queryLimited(toLast: 5).observeSingleEvent(of: .value) { [weak self] snapshot in guard let self = self, let first = snapshot.children.allObjects.first as? DataSnapshot, var allObjects = snapshot.children.allObjects as? [DataSnapshot] else { return } // 移除分页边界的重复用户 allObjects.removeAll(where: { $0.key == self.userCurrentKey }) var tempUsers: [User] = [] let group = DispatchGroup() allObjects.forEach { snap in let uid = snap.key // 过滤规则和首次加载保持一致 guard uid != currentUid, !self.blockedUids.contains(uid) else { return } group.enter() Database.fetchUser(with: uid) { user in tempUsers.append(user) group.leave() } } group.notify(queue: .main) { self.users.append(contentsOf: tempUsers) self.tableView.reloadData() self.userCurrentKey = first.key } } } }
关键修改说明
- 把原来的
childAdded监听改为单次value监听,一次性拉取所有拉黑ID,避免有多个拉黑用户时重复触发用户列表查询 - 所有用户查询结果都先经过两层过滤:排除当前登录用户自己、排除已拉黑的用户
- 用DispatchGroup统一管理异步用户信息拉取,所有用户数据拉取完成后才刷新列表,避免频繁刷新带来的性能问题
- 首次加载和分页加载的过滤逻辑完全统一,不会出现分页时漏过滤的问题
内容的提问来源于stack exchange,提问作者JuFa512
相关产品推荐
相关产品推荐

