4

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?

Frank van Puffelen
  • 565,676
  • 79
  • 828
  • 807
starrystar
  • 668
  • 3
  • 8
  • 22

2 Answers2

4

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.

Frank van Puffelen
  • 565,676
  • 79
  • 828
  • 807
  • Thank you for great answer. I search through the properties of GeoFire. But I have some doubts. It is suggested that, keep location entries seperate from restaurants. Entering like, location info with restaurants ids and query on that location root. Like in this link, http://stackoverflow.com/questions/38340949/how-to-save-geofire-coordinates-along-with-other-items-in-firebase-database. But I dont understand that, when I query on locations, I will get nearest restaurant keys. After that, I must get these restaurant with these keys. How can I do that, isn't that a expensive database get? – starrystar Oct 28 '16 at 22:41
  • It actually isn't. Due to its use of geohashes, GeoFire typically needs to read a lot more location data than it returns. By having you do the lookup of the additional info separately, you're actually likely to use significantly less bandwidth. And it's not as slow as you may think, since Firebase pipelines the requests over the existing connecting. See https://stackoverflow.com/questions/35931526/speed-up-fetching-posts-for-my-social-network-app-by-using-query-instead-of-obse/35932786#35932786 – Frank van Puffelen Jul 10 '19 at 14:06
2

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.

starrystar
  • 668
  • 3
  • 8
  • 22
  • Yeah, would be interesting to get an answer to is this the most efficient way to do it. Frank van Puffelen...Thanks ^^ – Andy Strife Mar 25 '17 at 01:04