0

I'm just getting started with Firebase Database and am having some troubles wrapping my head around efficiently querying data. My current database structure is similar to the Firebase documentation for recommended data structures, where we see this example:

// An index to track Ada's memberships
{
  "users": {
    "alovelace": {
      "name": "Ada Lovelace",
      // Index Ada's groups in her profile
      "groups": {
         // the value here doesn't matter, just that the key exists
         "techpioneers": true,
         "womentechmakers": true
      }
    },
    ...
  },
  "groups": {
    "techpioneers": {
      "name": "Historical Tech Pioneers",
      "members": {
        "alovelace": true,
        "ghopper": true,
        "eclarke": true
      }
    },
    ...
  }
}

The problem I'm having is that I'm not sure how to efficiently retrieve all of the group names for a specific user. I can retrieve all of the group IDs for a user easily:

usersRef.child("alovelace").child("groups").observeSingleEvent(of: .value, with: { (snapshot) in 
// code to store group IDs 
})

But now the only way I can think to get all the group names is to make a for loop through the IDs, then make a .observeSingleEvent call for each one to get the name. But what if I have a very large set of groups for the user? Ideally I'd prefer not to make so many database calls and just do it in one call. Is that possible with this data structure?

Vlad Pulichev
  • 3,162
  • 2
  • 20
  • 34
  • Firebase will retrieve the items over a single open connection, so the overhead of the connection is small. Retrieving reasonable number of items in such a way (also called a client-side join) is not as slow as you may think, because the requests are pipelined over the existing connection. See http://stackoverflow.com/questions/35931526/speed-up-fetching-posts-for-my-social-network-app-by-using-query-instead-of-obse/35932786#35932786 – Frank van Puffelen May 15 '17 at 01:08

2 Answers2

0

You can do this:

usersRef.child("alovelace").child("groups").observeSingleEvent(of: .value, with: { (snapshot) in 
    let result = snapshot.children.allObjects as? [FIRDataSnapshot]
    for child in result {
        let groupName = child.key
        print(groupName)
    }
})
Farid Al Haddad
  • 833
  • 9
  • 19
  • When I get down to (ds1 as! FIRDataSnapshot), there is no key. From my understanding, this snapshot is only referencing the path I specified (in users), so it doesn't have the group name there. Am I missing something? – Lindsey Isaak May 15 '17 at 00:49
  • Excuse me for the previous answer, this one should work i edited it. – Farid Al Haddad May 15 '17 at 09:08
  • This returns the names of the keys under "alovelace" - "name," "groups." I'm looking to return the actual name of the items under "groups" - in this case, "Historical Tech Pioneers" – Lindsey Isaak May 16 '17 at 02:35
0

This is what I suggest you do. This populates the groups array with all the data inside the groups tree that the user belongs to.

import UIKit
import Firebase

class TableViewController: UITableViewController {

    var groups = Array<Group>()

    override func viewDidLoad() {
        super.viewDidLoad()

        Group.get(userUID: "alovelace") { (groups) in

            for uid in groups {

                Group.get(groupUID: uid, completion: { (group) in

                    if let group = group {

                        print(group)

                        self.groups.append(group)

                    } else {

                        print("There is no group with id: \(uid)")
                    }

                    self.tableView.reloadData()
                })
            }
        }
    }
}

struct Group {

    var uid: String

    var name: String

    var members: Array<String>

    init?(uid:String, dict:Dictionary<String,Any>){

        guard

            let name = dict["name"] as? String,

            let users = dict["members"] as? Dictionary<String,Bool>

        else {

            return nil
        }

        self.uid = uid

        self.name = name

        self.members = Array<String>()

        for (id, _) in users {

            self.members.append(id)
        }
    }

    // Returns a Group, when given a group id.
    static func get(groupUID:String, completion: @escaping (Group?) -> ()) {

        let ref = FIRDatabase.database().reference().child("groups").child(groupUID)

        ref.observeSingleEvent(of: .value, with: { (snapshot) in

            if let value = snapshot.value as? Dictionary<String,Any> {

                if let group = Group(uid: groupUID, dict: value) {

                    completion(group)

                    return

                } else {

                    print("Incomplete Group Data")
                }
            }

            completion(nil)
        })
    }

    // Returns the group ids that a user belongs to
    static func get(userUID:String, completion: @escaping (Array<String>) -> ()) {

        let ref = FIRDatabase.database().reference().child("users").child(userUID).child("groups")

        ref.observeSingleEvent(of: .value, with: { (snapshot) in

            if let value = snapshot.value as? Dictionary<String,Any> {

                completion(value.keys.sorted())

                return
            }

            completion([])
        })
    }
}
Tristan Beaton
  • 1,742
  • 2
  • 14
  • 25