Marco
Marco

Reputation: 5

swift firebase nested children count

groups
--group1(autoid)
---subgroup1(autoid)
----id
----ownerId
----description
--group2(autoid)
---subgroup2(autoid)
----id
----ownerId
----description

In a structure like over here i have to count all occurrencies of ownerId that are equal to my id (currentUserId) in all groups, can somebody help me?

what i've done so far:

root.child("groups").observe(.value, with: {(snapshot) in
    if let result = snapshot.children.allObjects as? [DataSnapshot] {
        var count = 0
        for child in result {
            let orderID = child.key as String //get autoID
            self.root.child("groups/\(orderID)/").queryOrdered(byChild: "ownerId").queryEqual(toValue: self.currentUserId).observe(.value, with: { (snapshot: DataSnapshot!) in
                print(snapshot.childrenCount, "quanti sono")
                count += (Int(snapshot.childrenCount))
                print(count)
            })
        }
    }
})

with this i can get a count but it updates all cycles... i need i need the final value outside

Upvotes: 0

Views: 808

Answers (2)

Jay
Jay

Reputation: 35658

One important aspect of Firebase Structures is denormalizing or flattening the structure. Denormalized data generally makes queries much easier and while conceptually the structure you are using works for some tasks, it makes doing the query you want challenging.

So, I would suggest an alternate structure that would make the query super simple, and not loose other functionality.

A change to the structure like this:

groups
  group1: true
  group2: true

subgroups
  subgroup1(autoid)
     id
     ownerId
     description
     belongs_to_group: "group1"
  subgroup2(autoid)
     id
     ownerId
     description
     belongs_to_group: "group2"

Then if you want to count all of subgroups with a particular ownerId

let subGroupsRef = self.ref.child("subgroups")
let query = subGroupsRef.queryOrdered(byChild: "ownerId").queryEqual(toValue: "their id")
query.observeSingleEvent(of: .value) { snapshot in
    let count = snapshot.childrenCount
    print(count)
}

Edit:

Based on the comment, here's an way to get the count based on your current structure. It's pretty brute force and the code could be reduced considerably but I left it verbose for readability

let groupsRef = self.ref.child("groups")
groupsRef.observeSingleEvent(of: .value, with: { snapshot in
    var count = 0
    for groupChild in snapshot.children {
        let groupSnap = groupChild as! DataSnapshot
        for subGroupChild in groupSnap.children {
            let subGroupSnap = subGroupChild as! DataSnapshot
            let dict = subGroupSnap.value as! [String: Any]
            let uid = dict["owner_id"] as! String
            if uid == "uid_0" {
                count += 1
                print(count)
            }
        }
    }
    print("total found \(count)")
})

Where this fails is if you have a lot of nodes as they are all initially loaded in (by .value) so it could be iterated over in code. If it's a few thousand it works well and is very fast.

Upvotes: 1

Giuseppe Sapienza
Giuseppe Sapienza

Reputation: 4708

You can achieve this step by doing a single observe of .childAdded type. The .childAdded on an sub child, in your example groups, is like a for loop that iterate all nodes.

With this configuration you can append a .queryOrdered(byChild:) and a .queryEqual(toValue:):

ref.child("groups")
    .queryOrdered(byChild: "ownerID")
    .queryEqual(toValue: 123)
    .observe(.childAdded)
{ (snap) in
    print(snap)
}

After that, if you want to count all this child, you need to add a property on your class

This is a test example:

To optimize performance remember to add a .indexOn rule on your firebase app:

"groups" : {
  ".indexOn" : "ownerID"
}

Hope this help you ;)

Upvotes: 0

Related Questions