1

When I query a specific child I get all the results back instead of the ones specific to what I'm looking for:

For eg if my search text begins with the letter "c" I should only get the cat and the cow from below but for some reason I'm also getting the bull.

root
  |
  @---users
        |
        @---uid_1000
        |    |--uid: "uid_1000"
        |    |--username: "bull"
        |
        @---uid_2000
        |    |--uid: "uid_2000"
        |    |--username: "cat"
        |
        @---uid_3000
             |--uid: "uid_3000"
             |--username: "cow"

I was thinking maybe the issue is because I seem to grab everything at the users node (dictionaries plural) and then loop through the dictionaries that might be the issue but then I realized it should still only be the username values beginning with the letter "c" that should appear inside the dictionaries to begin with and bull should get skipped.

Where am I going wrong at?

let searchText = "c"

let ref = Database.database().reference().child("users")
    .queryOrdered(byChild: "username")
    .queryStarting(atValue: searchText)
    .queryEnding(atValue: searchText + "\u{f8ff}")

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

    if !snapshot.exists() { return }

    // dictionaries plural
    guard let dictionaries = snapshot.value as? [String: Any] else { return }

    dictionaries.forEach({ (key, value) in

        guard let dict = value as? [String: Any] else { return }
        let user = User(dict: dict)

        let isContained = self.users.contains(where: { (containedUser) -> Bool in
            return user.uid == containedUser.uid
        })

        if !isContained {
            self.users.append(user)
            self.collectionView.reloadData()
        }
})

Updated with Json per Frank's request:

{
  "users" : {
    "1000" : {
      "userId" : "1000",
      "username" : "bull"
    },
    "2000" : {
      "userId" : "2000",
      "username" : "cat"
    },
    "3000" : {
      "userId" : "3000",
      "username" : "cow"
    }
  }
}

A picture to match. I excluded the other 2 objects from the json because even though they showed up on my end (I actually got 5 results) I wanted to just focus on what's in the question which is the first 3.

enter image description here

Lance Samaria
  • 17,576
  • 18
  • 108
  • 256
  • As far as I can see you're adding the correct filter in the code, and should only be getting child nodes whose `username` property starts with a `c`. Can you try this simplified version: `let ref = Database.database().reference().child("users") .queryOrdered(byChild: "username") .queryStarting(atValue: "c") .queryEnding(atValue: "d")`? – Frank van Puffelen Apr 08 '19 at 14:15
  • I just tried your simplified version and the same thing happened, all 3 of them get returned. – Lance Samaria Apr 08 '19 at 15:04
  • Here are my rules if it makes any difference: "users": { ".read": "auth.uid != null", ".indexOn": ["username"], "$uid": { ".write": "$uid === auth.uid" } – Lance Samaria Apr 08 '19 at 15:07
  • Rules only determine *whether* results are returned, not *what* results are returned. Can you post the literal JSON (as text, no screenshot), so I can give it a try? You can get this by clicking the "Export JSON" link in the overflow menu (⠇) on your [Firebase Database console](https://console.firebase.google.com/project/_/database/data). – Frank van Puffelen Apr 08 '19 at 15:33
  • Do you need all the data from all the other nodes? I have 19 nodes with varying info. A lot of clutter unless you really need it – Lance Samaria Apr 08 '19 at 15:56
  • I need the minimal JSON that is necessary to reproduce the problem. As far as I can see that is only the `username` property, but if you [create a minimal, complete, verifiable example](http://stackoverflow.com/help/mcve) you will be best equipped to answer that question yourself. – Frank van Puffelen Apr 08 '19 at 16:00
  • I added just that node, thanks – Lance Samaria Apr 08 '19 at 16:02
  • I can't reproduce the problem with your data and (simplified) code. I added what I've tried as an answer, so that you can see what I did and what output I got. – Frank van Puffelen Apr 08 '19 at 17:16

2 Answers2

1

I just tried this code:

let ref = Database.database().reference(withPath: "55574954")
ref.child("users") .queryOrdered(byChild: "username") .queryStarting(atValue: "c") .queryEnding(atValue: "d").observeSingleEvent(of: .value, with: { (snapshot) in
    for child in (snapshot.children.allObjects as! [DataSnapshot]) {
        print(child.key)
    }
})

On this data structure:

{
  "users" : {
    "1000" : {
      "userId" : "1000",
      "username" : "bull"
    },
    "2000" : {
      "userId" : "2000",
      "username" : "cat"
    },
    "3000" : {
      "userId" : "3000",
      "username" : "cow"
    }
  }
}

Live JSON is here.

Running this code prints:

2000

3000

Which is exactly what I'd expect with the data structure above.

Community
  • 1
  • 1
Frank van Puffelen
  • 565,676
  • 79
  • 828
  • 807
  • Hey thanks for trying. I don’t know what the heck is going on. I use the code at en entirely different node and it works correctly so maybe there’s something in my vc that’s causing the error. I’ll keep looking and get back to you once I nail it. Thanks again for your help! Btw I have 1 question (maybe it’s better I submit it as a new one). Is using obsereSingleEvent and then iterating over the dictionaries correct? If there was a million nodes with the username beginning with “c” that’s a lot of client side filtering – Lance Samaria Apr 08 '19 at 17:23
  • 1
    If you define an index on `users` then the filtering is done server-side. There *is* a limit on how many nodes you can reasonably have though. Have a look at https://stackoverflow.com/questions/39712833/firebase-performance-how-many-children-per-node. Beyond that consider sharding, for example: put the names in subnodes based on the first character. – Frank van Puffelen Apr 08 '19 at 17:33
  • Ok thanks again I’ll look it over and get back to you about this issue was cleared. Cheers! – Lance Samaria Apr 08 '19 at 17:34
  • I found the problem and it’s really weird. I had a class property named blockedRef and in viewWillAppear I initialized it with a .observe(.value). This ref has no connection to my users ref, I only was checking there to make sure this user isn’t blocked from whomever. Somehow that blockedRef corrupted my query on the users ref. I’m still having problems too. Strange stuff – Lance Samaria Apr 08 '19 at 19:21
  • This is one of the reasons we ask for an MCVE, since it's impossible for us to see this type of interaction in your app. It's also why I provided an MCVE, showing the minimum code+data that works correctly for me. – Frank van Puffelen Apr 08 '19 at 19:41
  • Ahhhh I see. I had no idea that it would have an effect on it otherwise I would’ve included it. I thought it was unnecessary code. Tricky this one was. The reason I knew it was blockedRef was because it went from returning 5 results (I had 5 on my end) to only 3 results. After I deleted the blockedRefs it kept querying incorrectly. I even changed queryOrdered(byChild: “nonsense”) and it still returned the 3 results. I closed the project, logged out, then logged back in as another user and same results. I just deleted the entire file and will rebuild it from scratch. I’ll keep you updated – Lance Samaria Apr 08 '19 at 19:46
  • Frank if you get a chance look over the answer I posted. It was a very odd problem and once I chained everything together the corrects results were returned. I'm baffled – Lance Samaria Apr 08 '19 at 22:49
  • I did see it, but unless you can provide a minimal single standalone snippet that reproduces the problem, it's going to be hard for me to help furthe. – Frank van Puffelen Apr 08 '19 at 22:51
  • Well I fixed thank God for that so I’m good from this point. It was just very odd. Anyhow as usual I appreciate the help!!! Enjoy your day – Lance Samaria Apr 08 '19 at 22:57
0

I'm not sure why this happened but it was a very odd bug.

1st thing what I didn't add in my question was I had another observer inside the same class as the one I was using for queries.

I had a class property named blockedRef and in viewWillAppear I initialized it with a .observe(.value). This ref has no connection to my users ref, I only was checking there to make sure this user isn’t blocked from whomever. Somehow that blockedRef corrupted my query on the users ref.

Once I removed that blockedRef observer 1 problem was resolved (In my actually project I was initially getting 5 results then after removal I was getting 3 but they were still incorrect).

To fix the problem I removed the instance ref initialization

let ref = Database.database().reference().child("users")....

and instead chained everything needed for the query to the Database object itself and everything worked fine.

Database.database().reference().child("users")
    .queryOrdered(byChild: "username")
    .queryStarting(atValue: searchText)
    .queryEnding(atValue: searchText + "\u{f8ff}")
    .observeSingleEvent(of: .value, with: { ...

The only 2 results that showed up was cat and cow. Very strange bug

let searchText = "c"

Database.database().reference().child("users")
    .queryOrdered(byChild: "username")
    .queryStarting(atValue: searchText)
    .queryEnding(atValue: searchText + "\u{f8ff}")
    .observeSingleEvent(of: .value, with: { (snapshot) in

    if !snapshot.exists() { return }

    // dictionaries plural
    guard let dictionaries = snapshot.value as? [String: Any] else { return }

    dictionaries.forEach({ (key, value) in

        guard let dict = value as? [String: Any] else { return }
        let user = User(dict: dict)

        let isContained = self.users.contains(where: { (containedUser) -> Bool in
            return user.uid == containedUser.uid
        })

        if !isContained {
            self.users.append(user)
            self.collectionView.reloadData()
        }
})
Lance Samaria
  • 17,576
  • 18
  • 108
  • 256