0

I would like to retrieve all children within a certain collection except one specific child. I have the database "users" which consists of multiple user id's.

"users"
|- "userId1"
|- "userId2"
|- "userId3"

and so on. I now want to retrieve only "userId1" and "userId3" and exclude "userId2". I already know, how to get just the userId2, because I have also stored it in another database, "blocked-users". This database consists of userIds as well as is build like this:

"blocked-users"
|- "userId1" 
      |- "userId2"

And that is the reason why I want to exclude userId2 from retrieving the users. Say userId1 is the currentUser and has blocked userId2, I want to prevent userId1 from finding userId2. How can I do this?

This is how I am getting the id of userId2:

guard let currentUid = Auth.auth().currentUser?.uid else { return }

BLOCKED_USERS.child(currentUid).observe(.childAdded) { (snapshot) in
                
    print("this is the user, that should not be shown: ", snapshot.key)

How can I now exclude the snapshot.key from being fetched within this function?:

var userCurrentKey: String?
USER_REF.queryLimited(toLast: 10).observeSingleEvent(of: .value) { (snapshot) in
                    
                    guard let first = snapshot.children.allObjects.first as? DataSnapshot else { return }
                    guard let allObjects = snapshot.children.allObjects as? [DataSnapshot] else { return }
                    
                    allObjects.forEach({ (snapshot) in
                        let uid = snapshot.key
                        
                        Database.fetchUser(with: uid, completion: { (user) in
                            self.users.append(user)
                            self.tableView.reloadData()
                        })
                    })
                    self.userCurrentKey = first.key
                }

This right here would be the entire function I call for fetching all the users:

func fetchUsers() {
        
        guard let currentUid = Auth.auth().currentUser?.uid else { return }
        
        if userCurrentKey == nil {
            
            BLOCKED_USERS.child(currentUid).observe(.childAdded) { (snapshot) in
                print("these are the users that have blocked this user: ", snapshot.key)
                
                var dontShowThisUser = snapshot.key
                
                USER_REF.queryLimited(toLast: 10).observeSingleEvent(of: .value) { (snapshot) in
                    
                    guard let first = snapshot.children.allObjects.first as? DataSnapshot else { return }
                    guard let allObjects = snapshot.children.allObjects as? [DataSnapshot] else { return }
                    
                    allObjects.forEach({ (snapshot) in
                        let uid = snapshot.key
                        
                        Database.fetchUser(with: uid, completion: { (user) in
                            self.users.append(user)
                            self.tableView.reloadData()
                        })
                    })
                    self.userCurrentKey = first.key
                }
            }
        } else {
            USER_REF.queryOrderedByKey().queryEnding(atValue: userCurrentKey).queryLimited(toLast: 5).observeSingleEvent(of: .value, with: { (snapshot) in
                
                guard let first = snapshot.children.allObjects.first as? DataSnapshot else { return }
                guard var allObjects = snapshot.children.allObjects as? [DataSnapshot] else { return }
                allObjects.removeAll(where: { $0.key == self.userCurrentKey })
                
                allObjects.forEach({ (snapshot) in
                    let uid = snapshot.key

                    if uid != self.userCurrentKey {
                        Database.fetchUser(with: uid, completion: { (user) in
                            self.users.append(user)
                            if self.users.count == allObjects.count  {
                                self.tableView.reloadData()
                            }
                        })
                    }
                })
                self.userCurrentKey = first.key
            })
        }
    }
JuFa512
  • 119
  • 7

2 Answers2

1

I have found a way. In the Firebase rules I solve this problem like this:

  "$users": {
    "$uid": {
".read": "auth != null && !(root.child('blocked-users').child(auth.uid).hasChild($uid))",
".write": "auth != null"
}
}

This reads like this: When auth is not null and the user is not in the blocked-users collection of the user they want to find, the user is allowed to read the user's data.

JuFa512
  • 119
  • 7
  • This allows you to securely get individual users, but doesn't allow you to get a list of users, which is how I interpreted the "When user2 then searches through all the users within the app" in your [original question](https://stackoverflow.com/questions/69314551/prevent-user-from-finding-users-that-have-blocked-them-firebase). If this implementation matches your use-case it is great to hear you've found something that works though. – Frank van Puffelen Oct 05 '21 at 20:43
0

In general there is no way to exclude one (or some) nodes from a query. You will either have to load the entire users node and filter out the users you don't want in your application code, or you'll have to load the users you do want one by one.

The only variation on this is if you can use a query to slice the child nodes that you want and don't want. For example, if you have 99 users (user01 to user99 for simplicity) and won't read all but one user (say user42), you could do that with two queries:

usersRef.queryOrderedByKey().queryEnding(beforeValue: "user42")

and

usersRef.queryOrderedByKey().queryStarting(afterValue: "user42")

I don't think there's a gain by using this approach here though, as this is likely to be (slightly) less efficient and be more code to handle the two queries, than it'd be to filter the one node in the application code.

Frank van Puffelen
  • 565,676
  • 79
  • 828
  • 807
  • I am now trying to solve this issue via Firebase security rules, because I think that is how I can do it. I have just ran into a minor issue there. Maybe someone in this thread can help with it? [link](https://stackoverflow.com/questions/69314551/prevent-user-from-finding-users-that-have-blocked-them-firebase) – JuFa512 Sep 24 '21 at 11:52
  • Any update here @JuFa512? – Frank van Puffelen Oct 02 '21 at 01:10
  • Yes @FrankvanPuffelen, I have found a way! I'll share in the answer's section – JuFa512 Oct 05 '21 at 20:18