I am designing a location based restaurant search application on android with firebase. Restaurant class as follows on firebase;
restaurants
-key
address: "NYC"
city: "New York"
description: ""
id: 60000
latitude: 39.895107
longitude: 32.797819
name: "Pizza Store"
Fragment class
mainActivity.mDatabase.child("restaurants").addValueEventListener(new ValueEventListener() {
@Override
public void onDataChange(DataSnapshot snapshot) {
System.out.println("There are " + snapshot.getChildrenCount() + " restaurans");
for (DataSnapshot postSnapshot : snapshot.getChildren()) {
Restaurant post = postSnapshot.getValue(Restaurant.class);
mapmap2.put(post.getName(), postSnapshot.getKey());
restaurants.add(post);
}
adapter = new RestaurantCardViewAdapter(getActivity(), restaurants, MapListFragment.this);
mRecyclerView.setAdapter(adapter);
}
@Override
public void onCancelled(DatabaseError firebaseError) {
System.out.println("The read failed: " + firebaseError.getMessage());
}
});
Right now, I can list all the restaurants. But I want to query on user location. Firstly I want to list 20 restaurants near to user location and when user loads page through I want to get next 20 nearest restaurants. I do some search on internet. I found GeoFire library but as I understand its working with the legacy version of firebase. How can I query with these criterias?
Firebase Queries can only filter/order by a single property. Since a location consists of longitude and latitude, in the format you have right now, you cannot query based on location.
Luckily there is an add-on library called GeoFire. This library combines the longitude and latitude into a single property called a geohash. Based on this property it can filter by distance to a given location.
I recommend you check out Geofire and see if it fits your use-case.
I solved the problem with the help of answers. I push a seperate child for geofire named as "restaurant_locations". I push the locations seperately when I insert restaurants.
GeoFire geoFire;
String itemId = mDatabase.child("restaurants").push().getKey();
mDatabase.child("restaurants").child(itemId).setValue(restaurant);
geoFire = new GeoFire(mDatabase.child("restaurants_location"));
geoFire.setLocation(itemId, new GeoLocation(Double.valueOf(rd.location.latitude), Double.valueOf(rd.location.longitude)));
Then when I need to query through locations I used geofire query.
geoFire = new GeoFire(mDatabase.child("restaurants_location"));
GeoQuery geoQuery = geoFire.queryAtLocation(new GeoLocation(39.896263, 32.799652), 1);
geoQuery.addGeoQueryEventListener(new GeoQueryEventListener() {
@Override
public void onKeyEntered(String key, GeoLocation location) {
System.out.println(String.format("Key %s entered the search area at [%f,%f]", key, location.latitude, location.longitude));
mDatabase.child("restaurants").child(key).addValueEventListener(new ValueEventListener() {
@Override
public void onDataChange(DataSnapshot snapshot) {
Restaurant res= snapshot.getValue(Restaurant.class);
}
@Override
public void onCancelled(DatabaseError firebaseError) {
System.out.println("The read failed: " + firebaseError.getMessage());
}
// ...
});
}
@Override
public void onKeyExited(String key) {
System.out.println(String.format("Key %s is no longer in the search area", key));
}
@Override
public void onKeyMoved(String key, GeoLocation location) {
System.out.println(String.format("Key %s moved within the search area to [%f,%f]", key, location.latitude, location.longitude));
}
@Override
public void onGeoQueryReady() {
System.out.println("All initial data has been loaded and events have been fired!");
}
@Override
public void onGeoQueryError(DatabaseError error) {
System.err.println("There was an error with this query: " + error);
}
});
But I still worry about that is it the most efficient way of getting Restaurants from database, because I run query seperately for every single restaurant key on "restaurants" root. Is there anybody know more efficient way please let us know.