Geo Queries and Firebase

Geo Queries and Firebase

·

5 min read

We can’t execute geo queries by simply storing latitude and longitude as distinct variables and querying a bounding box since Cloud Firestore only permits one range clause per compound query.

One solution for this as mentioned in Firebase documents is by using Geohashes. While using this way though, you don’t have an option for offset.

A simple use case where offset is needed can be : Given a location, you need to get user that is nearest to the location. Then the one second nearest and so on until certain distance or a limit is reached.

So to do this we can use combination of Cloud functions, Firestore and two npm libraries.

There are couple of steps.

Overview of the steps :

  1. Setting up User document
  2. Setting up onRequest cloud function
  3. Getting nearby users
  4. Sorting users
  5. Handling Offset

We will go through them one by one.

  1. Setting up User document

To query documents using the package we are going to use , we need to store location information about users like so :

{  
   g: {  
    geohash: string;  
    geopoint: GeoPoint;  
   };  
}

g.geohash is the geohash and is required in order to make the geoqery.

.geopoint is the GeoPoint used to generate the g.geohash field.

Now we can access the latitude and longitude using g.geopoint[0] and g.geopoint[1] ; but since we will need these values often I will store them outside separately as well.

2. Setting up onRequest cloud function

Assuming that you have already setup your Firebase account and Firebase Cloud Functions, lets start by creating a onRequest Cloud Function.

This function will expect latitude & longitude for the location we want nearby users for. How we will use them will be clear as you read on.

3. Getting nearby users

We will use geofirestore and use the ‘near’ function it provides.

npm i geofirestore

We first get instance of firestore and pass it to initializeApp function from geoFirestore and then get the Users collection (since this collection has all user documents). In the ‘near’ function we mention the GeoPoint with lat, lng , the radius and limit for how many users we want. So usersNearby will store users within 20 km radius from the center (35 users max).
Note that these users are not sorted from nearest to farthest like we want. So we need to sort them in the next step.

4. Sorting users

Remember the user document with latitude & longitude and geohash? We will use them for sorting. We will be using geodist to calculate distance from a particular user location to the center location.

npm i geodist

geodist takes two points and returns the distance between them in the specified units. We loop through the users and find distance between each user and the center location. We store this distance as ‘distance’ key in each users map.
Then we use the array sort function to sort the users according to ‘distance’ value. Now we got our users sorted from nearest to farthest.

5. Handling Offset

geofirestore has no way to handle offset so we have to come up with a way.
So far our usersNearby array has sorted users from nearest to farthest.

We need a way to get all the nearby users within the radius when we first call this function and get the nearest user. In the second call we need the second nearest and so on until all the users have been picked. We will store the usersNearby list in a Firebase document. In this case I will use the center locations document itself. So the center locations document will look something like this.

//Location (Collection) -> {LocationDocID}

{  
    usersNearby : [u1,u2,u3],  
    lat : 43.23223,  
    lng : -122.3445
}

So lets tweak our onRequest function to handle this and hopefully the code will make it more clear.

Gist of the gist above (Ha !) is the following :

Check if usersNearby list exists :
- If it doesn’t exist then get nearby users, sort them , get the nearest user, update the list and add it to the document
- If it does exist , grab the existing list, get the nearest user, update the list and add updated list back to the document

Few things to remember :
Cloud Functions should return with a promise : When you return a promise that function keeps running until the promise is resolved or rejected. To indicate that a function has completed its work successfully, the promise should be resolved. If you dont return a promise your function might end execution before work is completed successfully. To know more please read Sync , async and promises for cloud functions.

Add npm packages in the package.json inside functions folder :

Your local development folder structure for cloud function will look something like this. There are two package.json. One outside and one is inside the functions folder. Don’t forget to cd into functions folder then run those npm install commands for your packages otherwise you will get errors.

Users location might be changed until we reach that user in the list: We are grabbing users once and then grabbing them from the list from location document. Note that a particular user’s location can be different until the time we reach that user in the list. That’s a huge drawback with this approach.

That’s all for this post 🌸 Let me know down in the comments what you guys think of this approach and if you know of a better one 👋

Did you find this article valuable?

Support Ash Vic by becoming a sponsor. Any amount is appreciated!