KingTim
KingTim

Reputation: 1301

Getting a specific child value from nodes only in which user's UID is present

I am working on a group chat application, and I'm running into trouble trying to make a specific query to Firebase DB.

I need to get all of the profile pictures of users in a room. But only the rooms that the current user is a part of.

private func observeRooms() {

    let databaseRef = FIRDatabase.database().reference()
    let groupRef = databaseRef.child("groups")

    guard let uid = FIRAuth.auth()?.currentUser?.uid else {return}
    let queryRef = groupRef.queryOrdered(byChild: "participants/\(uid)").queryEqual(toValue: nil)

    queryRef.observe(.childAdded, with: { snapshot in

        let roomDict = snapshot.value as! [String: AnyObject]
        print("roomDict: \(roomDict)")
        let id = snapshot.key

        let avatar = roomDict["profilePicture"] as! String
    })
}

Obviously setting queryEqual(toValue: nil) returns all the rooms that the current user is NOT a part of - the opposite of what I need. I know the inverse will be to check queryEqual(toValue: true), but the : true is not showing up in the database.

This is how I add a node to "groups" in Firebase:

        // Start group node for new room
        let groupRef: FIRDatabaseReference = FIRDatabase.database().reference().child("groups")
        let roomID = newRoomRef.key
        let groupRoomRef = groupRef.child(roomID)
        let groupDict = ["roomName" : roomName, "participants": [uid : true]] as [String : Any]
        groupRoomRef.setValue(groupDict)

And this is how the data structure ends up:

"groups" : {
  "-KkGzZ7frbnXmImt0JpV" : {
    "participants" : {
      "tzfHgGKWLEPzPU9GvkO4XE1QKy53" : {
        "gender" : "male",
        "handle" : "Testing",
        "name" : "Test User",
        "profilePicture" : "https://graph.facebook.com/*removed*/picture?type=large&return_ssl_resources=1",
        "status" : "F8B016"
      }
    },
    "roomName" : "Test Room"
  },

How can I properly the profilePicture values for all users in a room, only for the groups that the current user's UID is a participant of?

Upvotes: 1

Views: 163

Answers (1)

Travis
Travis

Reputation: 2462

You're on the right track. When using queryEqual your query needs to match the value actually present in the database. Unfortunately that gets complicated when the values you're querying against are complex objects. In fact I'm not sure the function will work even if you did match the entire object value. The issue here is that you're trying to look up objects by the presence of a child key, instead of actually sorting by or matching their values. That doesn't quite fit the use case of any of the orderBy methods.

There are a few ways to work around this, ordered by effort.

First, you could take advantage of the sort order to get only those who have some object with the name of the UID. Since Firebase sorts nulls first, then primitives, and finally objects, you should be able to order by child and start at true (or any primitive) to get all object instances with that key (assuming, of course, you're not storing UID: true anywhere).

groupRef.queryOrdered(byChild: "participants/\(uid)").queryStartingAtValue(true)

Second, duplicate the UID as a key inside the object with the value true so that you can sort on it directly. Notice the extra step in the query.

"groups" : {
  "-KkGzZ7frbnXmImt0JpV" : {
    "participants" : {
      "tzfHgGKWLEPzPU9GvkO4XE1QKy53" : {
        "tzfHgGKWLEPzPU9GvkO4XE1QKy53" : true,
        ...

groupRef.queryOrdered(byChild: "participants/\(uid)/\(uid)").queryEqual(toValue: true)

Finally, and this is what I recommend, reconsider your data structure. You're nesting a lot of data inside this collection of collections that is going to make your queries and updates unwieldy. Firebase tends to work better when you denormalize your data, preferring flat/shallow structures with some duplication of simple data. In your case, instead of storing the user's profile data inside of their group membership, you might have a collection of groups and another collection of users, with each group containing only a list of UIDs to its members and vice versa. The example in the documentation is a very similar case to yours involving membership in chat conversations. At that point your UIDs (and group IDs, if you're querying on users instead) will have a value of true so you can again use a simple value query.

"groups" : {
"-KkGzZ7frbnXmImt0JpV" : {
  "participants" : {
    "tzfHgGKWLEPzPU9GvkO4XE1QKy53" : true
    ...

"users" : {
"tzfHgGKWLEPzPU9GvkO4XE1QKy53" : {
  "groups" : {
    "-KkGzZ7frbnXmImt0JpV" : true
    ...

groupRef.queryOrdered(byChild: "participants/\(uid)").queryEqual(toValue: true)

Upvotes: 1

Related Questions