0

I am trying to make a query to a Firebase collection using swift.

My Collection is like this :

{
  "networks" : {
    "-KF9zQYGA1r_JiFam8gd" : {
      "category" : "friends",
      "members" : {
        "facebook:10154023600052295" : true
      },
      "name" : "My friends",
      "owner" : "facebook:10154023600052295",
      "picture" : "https://my.img/img.png"
    },
    "-KF9zQYZX6p_ra34DTGh" : {
      "category" : "friends",
      "members" : {
        "tototata" : true
      },
      "name" : "My friends2",
      "owner" : "facebook:10154023600052295",
      "picture" : "https://my.img/img.png"
    }
  }

and my query is like that :

let networksRef = ref.childByAppendingPath("networks")
                     .queryOrderedByChild("members")
                     .queryEqualToValue(true, childKey: uid)

Then I am populating a TableView using FirebaseUI.

the thing is that query doesn't return anything, I also tried using this kind of query :

let networksRef = ref.childByAppendingPath("networks")
                     .queryOrderedByChild("members")
                     .queryStartingAtValue(true, childKey: uid)
                     .queryEndingAtValue(true, childKey: uid)

And to be clear "uid" is the node name nested in the "members" node with true as value. (As on the picture)

I can't get all the nodes without filtering because of the amount of data to be downloaded.

I'm stuck on that for a day, and I'm going crazy.. :-)

If someone can help ?

Frank van Puffelen
  • 565,676
  • 79
  • 828
  • 807
poms32
  • 37
  • 4

1 Answers1

3

The correct syntax would be:

let networksRef = ref.childByAppendingPath("networks")
                     .queryOrderedByChild("members/\(uid)")
                     .queryEqualToValue(true)

But note that you'll need an index for each uid for this to work efficiently:

{
  "rules": {
    "networks": {
      ".indexOn": ["facebook:10154023600052295", "tototata"]
    }
  }
}

Without these indexes, the Firebase client will download all data and do the filtering locally. The result will be the same, but it'll consume a lot of unneeded bandwidth.

A proper solution for this requires that you change your data structure around. In Firebase (and most other NoSQL databases) you often end up modeling the data in the way that your application wants to access it. Since you are looking for the networks that a user is part of, you should store the networks per user:

"networks_per_uid": {
  "facebook:10154023600052295": {
    "-KF9zQYGA1r_JiFam8gd": true
  },
  "tototata": {
    "-KF9zQYZX6p_ra34DTGh": true
  },
}

Adding this so-called index to your data structure is called denormalizing and is introduces in this blog post, the documentation on data structuring and this great article about NoSQL data modeling.

Frank van Puffelen
  • 565,676
  • 79
  • 828
  • 807
  • THANK YOU ! not only for the answer, but for the better way to do it ! I'll try to reorder all my data in a proper way ! – poms32 Apr 12 '16 at 18:48
  • @frank-van-puffelen - does the index really need to be ".indexOn": ["facebook:10154023600052295", "tototata"] or would ".indexOn": ['members'] achieve the same result? – Daniel Dimitrov Jul 23 '17 at 20:32
  • It needs to be on the property you want to search. You're searching the property with the user's UID, so that's what you'll need to index. As my answer explains, this typically is the result of using the wrong data structure for your needs. For a longer explanation, see http://stackoverflow.com/questions/40656589/firebase-query-if-child-of-child-contains-a-value – Frank van Puffelen Jul 23 '17 at 23:22