Storing Extra Data Under GeoFire Nodes
Asked Answered
F

3

4

I am storing location data via GeoFire in my database, here is the structure:

geofire
  -Ke1uhoT3gpHR_VsehIv
  -Kdrel2Z_xWI280XNfGg
    -g: "dr5regw90s"
    -l
      -0: 40.7127837
      -1: -74.00594130000002

It is avised to store a location's information and geofire data in separate nodes, however I see an advantage to storing some extra data under these geofire nodes, such as the name of a business. This way I'd only have to make only one call to my Firebase to fetch nearby locations as well as their names.

Is this achievable via the key_entered method? Has anyone created a similar solution? Is this truly that bad of an idea even if the location info is consistently updated?

Any input is appreciated!

Furor answered 5/3, 2017 at 12:20 Comment(1)
I would not go against the recommendations made by the people that created both Geofire and the Firebase Database that its based on. The extra call doesn't result much overhead when used for multiple keys. See #35932026.Squally
N
12

Firstly the structure you are using for the app is wrong.

let us take an example of users app

When you use Geofire, you have two lists of data:

a list of user details for each user
a list of latitude and longitude coordinates for each user

You are trying to store both in same structure like this

"users" : {
    <userId> : {
        "userData" : "userData",
         <geofireData> 
        ...
    }
}

Trying to store userdata and geofiredata in one node is a bad idea, since you're mixing mostly static data (the properties of your user) with highly volatile data (the geo-location information).Separating the two out leads to better performance, which is why Geofire enforces it.

This is why when you try to add geolocation data to a user node it overwrites previous user details for that node.

Your database structure should be like this.

"Users" : {
    <UserId> : {
        "UserData" : "UserData",
        ...
    }
}
"Users_location" : {
    <UserId> : {
        <geofireData> ...
    }
}

Hence for the same user Id you create 2 structures one for user details and another for geolocation details of that user.

How to push the user and set the geofire data.

String userId = ref.child("users").push().getKey();

ref.child("users").child(userId).setValue(user);

geoFire = new GeoFire(ref.child("user_location"));
geoFire.setLocation(userId, new GeoLocation(lattitude, longitude));

the userId you use in geolocation is same as the one you got during push().

Hence for each userId you have userdetails in one structure and location details in anotehr structure.

To get the data, first you need to do GeoQuery at users_location node and then get the data on the onKeyEntered method. The parameter key is userId from my example.

geoFire=newGeoFire(FirebaseDatabase.getInstance().getReference().child("users_location");
geoQuery = geoFire.queryAtLocation(geoLocation), radius);
geoQuery.addGeoQueryEventListener(new GeoQueryEventListener() {
    @Override
    public void onKeyEntered(String key, GeoLocation location) {
         //retrieve data
//use this key which is userId to fetch user details from user details structure
    }
};    

Happy coding :)

Nautilus answered 6/3, 2017 at 18:17 Comment(0)
P
1

It might be late but just in case someone else is facing the same issue.

It's very possible to integrate and manipulate GeoFire data with existing data. The main challenge faced by many developers is the fact that they can't update the GeoFire geolocation without deleting the existing data using geoFire.setLocation("id", location); as reported in this issue geofire-java/issues/134.

This is my solution for that.

1. Setting mixed data with GeoFire on FireBase

Let's consider below Structure:

|-userExtra : 
    |-userId1 : 
       |- userId : "userId1",
       |- userName : "Name1",
       |- <geofireData> (i and g)
       |...

    |-userId2 : 
       |- userId : "userId2",
       |- userName : "Name2",
       |- <geofireData> (i and g)
       |...

You can generate the geoHash with GeoHash geoHash = new GeoHash(location) and set the value to the child directly in the firebase.

/*Java Code*/
void setGeoFireMixData(UserExtra user, Location location){
   DatabaseReference ref = FirebaseDatabase.getInstance().getReference();
   ref = ref.child("users").child(user.userId);

   //Setting userData if needed
   ref.child("userId").setValue(user.id);
   ref.child("Name").setValue(user.name);
   // .... other references

   //Setting GeoFire Data
   GeoHash geoHash = new GeoHash(location);
   ref.child("l").setValue(Arrays.asList(location.latitude, location.longitude));
   ref.child("g").setValue(geoHash.getGeoHashString());
}

2. Fetching mixed data with GeoFire

About the question: Is this achievable via the key_entered method? Has anyone created a similar solution? Is this truly that bad of an idea even if the location info is consistently updated?

I'd say, you can't retrieve both extra data and GeoFire using only the GeoQueryEventListener with onKeyEntered. You will need to use GeoQueryDataEventListener. For that, you can define the PoJo of your data considering the structure defined above.

...
GeoQueryDataEventListener geoQueryDataEventListener = new GeoQueryDataEventListener() {
  @Override
  public void onDataEntered(DataSnapshot dataSnapshot, GeoLocation location) {
      UserExtra user = dataSnapshot.getValue(UserExtra.class);
      actionOnUser(user);
  }
  ...
};
...
geoQuery = geoFire.queryAtLocation(new GeoLocation(latitude,longitude), 0.5);
geoQuery.addGeoQueryDataEventListener(geoQueryDataEventListener);
...

The class UserExtra can be defined as below:

public class UserExtra implements Serializable {
   String userId;
   String name;
    ...
   List<Double> l; //GeoLocation
   String g;
}

That will give you all the data in your node including the geoFire data.

Plano answered 13/6, 2020 at 23:36 Comment(1)
This is a better solution.!! Having geofire details and POJO details separate will increase number of database calls. This is pretty straight forward.Aldenalder
K
0

I had this solution I am using now for Javascript:

  1. Save any extra info with key separated by symbol i.e. underscore

  2. Make sure auth id is in the key, which is used in security rules to ensure only the user can write to their node

    "rules": { "geofire": { ".read":"true", "$extrainformation_authid":{ ".write":"$extrainformation_authid.contains(auth.uid)" } } }

  3. Client-side, separate the information by the underscore

Kalagher answered 5/11, 2021 at 4:5 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.