0

I have looked at a couple of StackOverflow posts on how to get a data value by equating a child value that is few nodes deep.

In Firebase Database - How to perform a prefix wildcard query the structure of database is:

and the query ref.child("user_id").orderByChild("name").equalTo("objective-c") works in pulling out child with particular uid.

In Firebase search by child value, the database structure is:

and the query ref.child('users').orderByChild('name').equalTo('John Doe') is able to pull out the appropriate child.

In both these cases, the sorting and equating is on the grandchild. In my case, however, the database structure is a bit different. I want to index by a child property that is a few layers deep. The structure is:

So I tried running the following query: authenticatedUsersReference.queryOrdered(byChild: fcmTokenKey).queryEqual(toValue: fcmToken).observeSingleEvent(of: .value), and my snapshot returned nil. But when I ran authenticatedUsersReference.queryOrdered(byChild: fcmTokenKey).observe(.value), I saw that the snapshot values were ordered by the fcmTokens. authenticatedUsersReference points to Database.database().reference().child("people/authenticated"). I have also updated my rules to do indexing on the server side. As far as I can tell the concept should remain the same even if the depth changes, so what am I missing that is causing my query to return nil?

"people": {
      "users": {
        "authenticated": {
          "$userName": {
            ".indexOn": ["fcmToken"]
          }
        }
      }
    }
Parth
  • 2,682
  • 1
  • 20
  • 39

1 Answers1

1

You're defining an index on $userName for fcmToken. This allows you to query a specific $userName node for its fcmToken property.

But your code instead queries across all users for their fcmToken property, for which no index exists. So the first fix is that you'll need to define the index on the location you want to query, so on authenticated:

"people": {
  "users": {
    "authenticated": {
      ".indexOn": ["fcmToken"]
    }
  }
}

Now the above creates the index in the right place, so let's look what values it puts in the index.

For each child node of authenticated the above gets its fcmToken property and puts the value of that property in the index. But if we look at your JSON, the child nodes of authenticated don't have a direct child property fcmToken. So there's no value to put in the index, which explains why you get no results for your query.

Instead you have a child property at a (fixed) path: stats/fcmToken, so that is what you should use in the index definition too.

So in total:

"people": {
  "users": {
    "authenticated": {
      ".indexOn": ["stats/fcmToken"]
    }
  }
}

With that definition, the right values exist in the index, and you can query it with:

authenticatedUsersReference
  .queryOrdered(byChild: "stats/fcmToken")
  .queryEqual(toValue: fcmToken)

Edit:

Once you get this snapshot, all the methods associated with getting the key will not be useful because the root reference is authenticated/. So snapshot.ref, snapshot.key will all point to that.

Most of these types of questions need the ability to get the UIDs that were associated with the retrieved children. (can be more than one if the firToken was same for multiple users, for example)

The minimal way to achieve this, without going into complex and unnecessary parsing with JSONDecoders will look like this:

{(snapshot) in
    guard let value = snapshot.value as? [String: Any] { return }
    let uids = Array(snapshot.keys)
    print("UIDs for with matching FCMToken are: \(uids)")
    //UIDs for with matching FCMToken are: ["parthv21", "anotherUserName"]
}
Parth
  • 2,682
  • 1
  • 20
  • 39
Frank van Puffelen
  • 565,676
  • 79
  • 828
  • 807
  • Thanks, this gives me the expected snapshot. But how will I get the username. `parthv21` in this case? snapshot.ref.key gives me `authenticated` :/ – Parth Feb 04 '21 at 03:43
  • And snapshot.value has the whole json with first key `parthv21: {}`. Is there a way to access this without decoding snapshot.value using a codable object? – Parth Feb 04 '21 at 04:30
  • "Thanks, this gives me the expected snapshot." -> Upvote appreciated in that case. For the other questions: those depend on your code, which you didn't share. I recommend opening a new question for that, with the [minimal code needed to reproduce where you are stuck](http://stackoverflow.com/help/mcve) (the rules are likely no longer relevant there, since that problem was solved here). – Frank van Puffelen Feb 04 '21 at 04:45
  • Yes wait I have solved it. Will accept your answer and modify it a little bit to include what extra is needed, since in most of these problems people want the uid key associated. – Parth Feb 04 '21 at 04:52
  • Also, Frank, since you know so much about firebase could you please take a look at: [Starting a firebase query trigger at a possibly changing start value to efficiently get data after that point in swift](https://stackoverflow.com/questions/66015549/starting-a-firebase-query-trigger-at-a-possibly-changing-start-value-to-efficien). Thanks a lot! – Parth Feb 04 '21 at 05:02