Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

What is the recommended way of updating geolocation while preserving associated data? #40

Closed
stefek99 opened this issue Aug 26, 2014 · 10 comments

Comments

@stefek99
Copy link
Contributor

I have the following structure:

screen shot 2014-08-27 at 00 02 03

I would like to update the location, while preserving the data

There is GeoFire.set(key, location) https://github.com/firebase/geofire-js#geofiresetkey-location

But when I call it the data would be lost.

I think I've found a workaround but it doesn't seem very intuitive to me:

        firebaseRef.child('data').once('value', function(snap) {
          var data = snap.val();
          geofire.set(name, [lat, long]).then(function() {
           firebaseRef.child('data').set(data);
          }, function(error) {
            console.log('Error: ' + error);
          });
        });
  1. Saving data locally
  2. Updating geofire
  3. Storing data back to Firebase

Seem like a lot of work. Is there a better way?

Video explanation: https://www.youtube.com/watch?v=i5u77q7l8dU

You data sets are updating niceely: https://publicdata-transit.firebaseio.com/sf-muni :)

Thanks!

@jwngr
Copy link

jwngr commented Aug 27, 2014

Hey @stefek99 - The issue here is that GeoFire doesn't support storing data inside of its index. Instead, you should store that information elsewhere, for multiple reasons. First, let me talk about what the solution here is. You should structure your data like this:

{
  geofire: {
    "-J9aZZl94Sx6h": { "g": <geohash>, "l": {} }
    "-Jhacf97x4S3h": { "g": <geohash>, "l": {} },
    ...
  },
  "locations": {
    "-J9aZZl94Sx6h": { <location-data> },
    "-Jhacf97x4S3h": { <location-data> }
  }
}

Note that the keys used for GeoFire are just the push IDs from the /locations/ node. This is important because when you receive a key from a key_entered or key_exited event, you can easily use it to look up the data by doing a rootRef.child("locations").child(key).once("value").

Now, what is the benefit of doing it this way? For one, this is the only way that really works with GeoFire. Doing what you are trying to do and hack your own solution into the GeoFire index itself is probably not going to work. You don't have enough control over that.

Also, it's important to realize that there is often a difference in security between your GeoFire index and your data. If you store them all together, you can't separate their security rules. And since the GeoFire needs to be pretty open to work properly, that would mean your secure location data would also need to be open. Separating them allows you to give them separate security rules.

Finally, not including your data in the GeoFire index reduces the amount of data going across the wire. If you stored all that data in the index, every time a location moved would result in a lot of wasted bytes. If you do what I suggested, you would only load data that you actually need, when you need it.

You will notice our transit open data set is structured in exactly this way. The GeoFire index for it can be found here. If you look at that, you'll see that the GeoFire keys (e.g. lametro:7869) are a combination of the transit provider and bus ID. This allows us to easily look up bus info by doing a ref.child(provider).child("vehicles").child(busId).once("value") call. This is exactly what we do in our SF MUNI demo code.

I'm going to close this issue but please feel free to respond if you still have questions.

@jwngr jwngr closed this as completed Aug 27, 2014
@stefek99
Copy link
Contributor Author

Thank you, very good explanation, hopefully it will help others too!

@mikepugh
Copy link
Contributor

I would make an argument that there are some good use cases for storing some data within the GeoFire index. The bar example within the readme file would be one. For a 'Local Search' style app, storing some of the bar data (bar name, address, and price) in the GeoFire index would allow the app to very quickly pull the information needed to display a Search Results listing, but defer pulling in the detailed Bar record (under /bars/[bar-id]/) until the user actually selects the bar to see more. This would actually result in less traffic for this type of app, since they don't have to pull in all of the /bars/ data just to show search results. Bars also tend not to move around so there isn't much concern with location movement within the GeoFire index. Lastly, for security, that sort of app would have very open access rules on that data anyway.

Granted you could always create another index /barsummary/[bar-id] which has that same search summary data, but you'd still be making a 2nd call out to Firebase to get that data vs having it conveniently brought in with the GeoFire keys.

An optional [data] param passed into the geofire.set() method could support both cases where it's a good idea for some data to be stored in the index, as well as the current setup. Some documentation detailing when it's appropriate with some examples would probably be needed though, to discourage people from trying to store everything in the GeoFire index - for the very reasons you discuss above.

@jwngr
Copy link

jwngr commented Sep 3, 2014

Thanks for the feedback @mikepugh! I definitely see where you're coming from and this is something that several people have mentioned. I think we will revisit this again in the future and this sort of feature may find its way back into the library. I think I still just don't trust that people won't abuse it and will end up generating a lot more traffic than they desired or need to.

P.S. I think my comment about security rules is only partially true. You could easily set different security rules for the data portion of the GeoFire index. The rules would be a bit more complex than storing them in a different part of your Firebase, but it certainly could be done.

@santoshgistto
Copy link

so @jwngr what is the best way to load the additional data in list (android), ex: geofire returns 100 bars, are we need to make 100 calls?

@jwngr
Copy link

jwngr commented Jun 12, 2015

Yes, you should look them up individually by ID. This does require 100 calls, but since we use a websocket which has already established an HTTPS connection, it should be fast. It's not like you are making 100 HTTPS requests which are all opening connections to our server.

@santoshgistto
Copy link

got it thank you @jwngr

@jaufgang
Copy link

jaufgang commented Oct 9, 2015

Thanks for the explanation. As a new firebase/geofire user I found myself scratching my head reading the geofire docs trying to figure out how to make geotagged data searchable using geofire, until I found this issue referenced in a StackOverflow page

I think it would be very helpful if the above guidelines were to be included in the README.md. It wasn't clear to me that "some_key" is meant to be a reference to an existing key of an object somewhere else in the db. Perhaps expanding the "Example Usage" paragraph with some detail showing that there would be a separate /bars/ for the bar data and /barLocations/ for the geofire index would make this more clear.

@jwngr
Copy link

jwngr commented Oct 9, 2015

Thanks for the message @jaufgang. I actually have a blog post in flight that goes through a larger example but just haven't had enough time to finish it. I plan on adding it to the README once it is done. Hopefully that will make it more clear for everyone.

@benjaminmbrown
Copy link

This is a great way to do it. Currently I have a leaflet app that uses Geofire. This way I can quickly load marker loc data via Geofire . Then, when a user clicks the marker, they get the marker details, which reference another ref that has all the data. This keeps from grabbing too
Much data initially.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

6 participants