3

I'm trying to figure out how to query with filter with Geofire. Suppose I have restaurants with different category. and I want to add that category to my query. How do I go about this?

One way I have now is querying the key with Geofire, run the for loop through each key and get the restaurant, and insert the appropriate restaurant to the array. These seems so inefficient. Is there any other way to go about this?

Ideally I will have the filtered results, and only load each item when they're about to be shown.

Cheers!

ordinaryman09
  • 2,761
  • 4
  • 26
  • 32
  • Firebase queries can only filter by one condition. Geofire already does quite some "magic" to allow it to filter on both longitude and latitude. Adding another property to that equation might be possible, but is well beyond what Geofire handles by default. See http://stackoverflow.com/questions/34084347/geofire-how-to-add-extra-conditions-within-the-query (which I think is a duplicate). – Frank van Puffelen Dec 18 '15 at 02:37
  • ok, so how would you filter it by other conditions? – ordinaryman09 Dec 18 '15 at 08:10
  • It depends: if you only even want to access one category at a time, you can put the restaurants in a top-level node per category and point Geofire to one category. But more commonly, you simply do the extra filtering in client-side code. If you're worried about the performance of that: measure it, share the code, JSON data and measurements. – Frank van Puffelen Dec 18 '15 at 14:36
  • @FrankvanPuffelen How would you go about point Geofire to one category? Do you mean query the keys from the geofire and check if each one belong to that category? – ordinaryman09 Dec 19 '15 at 03:27
  • Nope. Just have separate top-level keys for each category, under which you have the geodata for items in that category. I.e. `/category1/items...` and `/category2/items...`, etc. The actual item data itself can be kept in a single global/shared `/items`. – Frank van Puffelen Dec 19 '15 at 04:36

2 Answers2

9

Firebase queries can only filter by one condition. Geofire already does quite some "magic" to allow it to filter on both longitude and latitude. Adding another property to that equation might be possible, but is well beyond what Geofire handles by default. See GeoFire: How to add extra conditions within the query?

If you only ever want to access one category at a time, you can put the restaurants in a top-level node per category and point Geofire to one category.

/category1
    item1
        g: "pns0h0mf2u"
        l: [-53.435719, 140.808716]
    item2
        g: "u417k3dwub"
        l: [56.83069, 1.94822]
/category2
    item3
        g: "8m3rz3s480"
        l: [30.902225, -166.66809]
/items
    item1: ...
    item2: ...
    item3: ...

In the above example, we have two categories: category1 with 2 items and category2 with just 1 item. For each item, we see the data that Geofire uses: a geohash and the longitude and latitude. We also keep a single list with the other properties of these 3 items.

But more commonly, you simply do the extra filtering in client-side code. If you're worried about the performance of that: measure it, share the code, JSON data and measurements.

Community
  • 1
  • 1
Frank van Puffelen
  • 565,676
  • 79
  • 828
  • 807
5

This is an old question, but I've seen it in a few places on the web, so I thought I might share one trick I've used.

The Problem

If you have a large collection in your database, maybe containing hundreds of thousands of keys, for example, it might not be feasible to grab them all. If you're trying to filter results based on location in addition to other criteria, you're stuck with something like:

  1. Execute the location query
  2. Loop through each returned geofire key and grab the corresponding data in the database
  3. Check each returned piece of data to see if it matches the other criteria

Unfortunately, that's a lot of network requests, which is quite slow.

More concretely, let's say we want to get all users within e.g. 100 miles of a particular location that are male and between ages 20 and 25. If there are 10,000 users within 100 miles, that means 10,000 network requests to grab the user data and compare their gender and age.

The Workaround:

You can store the data you need for your comparisons in the geofire key itself, separated by a delimiter. Then, you can just split the keys returned by the geofire query to get access to the data. You still have to filter through them, but it's much faster than sending hundreds or thousands of requests.

For instance, you could use the format:

UserID*gender*age, which might look something like facebook:1234567*male*24. The important points are

  1. Separate data points by a delimiter
  2. Use a valid character for the delimiter -- "It can include any unicode characters except for . $ # [ ] / and ASCII control characters 0-31 and 127.)"
  3. Use a character that is not going to be found elsewhere in your database - I used *, but that might not work for you. Do not use any characters from -0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz, since those are fair-game for keys generated by firebase's push()
  4. Choose a consistent order for the data - in this case, UserID first, then gender, then age.

You can store up to 768 bytes of data in firebase keys, which goes a long way.

Hope this helps!

ZenPylon
  • 518
  • 4
  • 11
  • Firebase uses websockets, which eliminates the need for this workaround, yes? The problem is not how many requests you make, as you only make one to create the socket and one to close. The problem is filtering the data, which can be heavy, as you say, when the dataset contains thousands and thousands of entries. The best way, I think, is to structure your data in a way that makes indexing into the nodes and sorting subnodes better – jhm Sep 15 '16 at 08:18
  • @jhm can you elaborate on how to do that? My issue with ZenPylon 's approach is that even if someone on the other side of the planet updates the branch, you'd have to run the filter each time something changed. That is super not ideal, so I'm wondering what the best approach is. – AlxVallejo Jun 21 '17 at 15:41