Bobbbay
Bobbbay

Reputation: 335

Fetch direct descendants from Firebase Database

I have been working on an application lately and have stumbled into yet another problem (that's the life of a programmer, isn't it?). For the record, I am working with the latest versions of Swift, XCode and Firebase. I would like to fetch only the direct descendants of a parent (my reference). Here is my JSON tree:

users
  family-name
    user-1      // I want this...
      pass: abc // but not this ...
    user-2      // ... and this
      pass: def // ... but not this
    events      // I skip this specifically in my code
      event-1
        a: a
        b: b
      event-2
        a: a
        b: b
      event-3
        a: a
        b: b

As shown in the tree, I only want to get user-1, user-2, and user-3.

I have tried to use a normal query but it has not worked Instead of getting the ones that I want I also get children values.

let query = userRef.queryOrdered(byChild: "users")
query.observe(.value, with: { (snapshot) in
  for child in snapshot.children {
    let value = snapshot.value as? NSDictionary
    for anotherChild in value! {
      if "\(key)" == "events" {
        print("Let's skip that, since it's just the events sub-tree!")
      } else {
        // my work goes here
      }

I get six values returned to me but I expect two. I have no idea why! Thanks in advance to anyone who can help.

Upvotes: 2

Views: 174

Answers (2)

Jay
Jay

Reputation: 35667

I will provide a specific answer the question and the a suggested change to the structure with an answer.

First, when a Firebase node is read in any way, it will load the node key (which is what you are after) along with the child data. That's the way Firebase works. As long as the child data is not extensive, then it's pretty straightforward.

Here we load the users family node, which loads all of the data within family and then assigns the child snapshot data, which are key: value pairs to a dictionary. From there we create an array of those keys (user-1, user-2 etc) and then iterate over them, printing all of the ones that are not 'events'

func printUids() {
    let familyRef = self.ref.child("users").child("family-name")
    familyRef.observeSingleEvent(of: .value, with: { snapshot in
        let dict = snapshot.value as! [String: Any]
        let allKeys = dict.keys
        for key in allKeys {
            if key != "events" {
                print(key)
            }
        }
    })
}

However, denormalizing your data can lead to a loss less data being loaded in these cases. I don't know the exact use case but let me present a revised struture where all of your users are stored in a users node, and then family data stored in another.

users
   uid_0
      name: "uid_0 users name"
      fav_food: "pizza"
   uid_1
      name: "uid_1 users name"
      fav_food: "pizza"

and then family data which contains a family name, events for the family and the family members (users) that belong to the family, referenced by the users uid

families
    family_id_0
       family_name = "some family name"
       family_users
          uid_0: true
          uid_1: true
       family_events
          event_0
              event_desc: "family reunion"
              event_data: "some date"

Assume we want to know which familiy members (users) belong to the family_id_0. The following code reads the specific node for that family, and prints out the family members (users)

func printFamilyUsers() {
    let familiesRef = self.ref.child("families")
    let whichFamilyRef = familiesRef.child("family_id_0")
    let familyUsersRef = whichFamilyRef.child("family_users")

    familyUsersRef.observeSingleEvent(of: .value, with: { snapshot in
        let theUsers = snapshot.children.allObjects as! [DataSnapshot]
        theUsers.forEach { print($0.key) }
    })
}

The advantage here is that the users are stored by their reference so when loading them in, it's a very minimal amount of data; just a uid and a bool.

I can update further if needed.

Upvotes: 1

Doug Stevenson
Doug Stevenson

Reputation: 317808

Realtime Database queries are "deep", in that a request for a node always retries all the children under that node. There are no "shallow" queries when use the client libraries, and there is no way to exclude a child from a node. If you want specific children, and not their siblings, you will have to request them each individually.

Upvotes: 1

Related Questions