1

I have a table called users in firebase, the table looks like this:

enter image description here

The structure of this table is:

users
  |_
    uid1
     |_
       latitude: ...
       longitude: ...
       name: ...
    uid2
     |_
       latitude: ...
       longitude: ...
       name: ...

I want to write a Cloud Function called getUserByLocation, the sample request url would be https://someurl/getUserByLocation?latitude=40.45654&longitude=-86.68468, which takes a pair of latitude and longitude as an original point, then go to the users table to find all users within 10 miles then return them back. It doesn't seem very hard and in fact, I have something that works:

  1. Calculate latLower, latUpper, longLower and longUpper and make a circle like this: enter image description here
  2. Then use query ...orderByChild('latitude').startAt(latLower).endAt(latUpper).once('value')... to select users whose latitudes are in range, I cannot consider longitude at this point because apparently I can only order the results by one key
  3. I check all results selected from step 2, if the longitude is in range too (between longLower and longUpper), then this user should be returned

This approach works for sure, however, I am thinking, what if there are millions of users? This approach is just not efficient. As a matter of fact, I wrote a script to create more than 1 million users and then used this approach to select users, usually, it'll take more than 10 secs to see the results and this is not acceptable.

My question is, is there any way I can improve the search efficiency in this situation? I've googled ElasticSearch but currently have no knowledge of what that is, is that something that can be used, in this case, to improve search?

Frank van Puffelen
  • 565,676
  • 79
  • 828
  • 807
K.Wu
  • 3,553
  • 6
  • 31
  • 55
  • Whoever gave me a downvote .... Any reasons? I don't care about reputations, but downvotes would make this question seen by fewer people, at least let me know what's wrong with it? Come on, this is a well structured question – K.Wu Jan 21 '18 at 00:34
  • 2
    Have you heard of GeoFire? https://github.com/firebase/geofire-js/blob/master/docs/reference.md – Callam Jan 21 '18 at 00:53
  • 1
    @Callam no I have not, I’ll read it first and if I have questions about it I’ll post on this site. But if eventually that’s a feasible solution to my problem, I’ll let you know, thanks! – K.Wu Jan 21 '18 at 00:56

1 Answers1

2

You can do this using GeoFire but you'll have to store your coordinates in a separate root node to the users. Something like user-location, where each child is a user key and location managed by GeoFire. You use it to more efficiently query a radius at a location and retrieve the IDs of the users within the bounds – however, then you will have to get the user data for each user ID found.

const geofire = require('geofire');
const geoFire = new geofire(admin.database().ref('user-location'));
const geoQuery = geoFire.query({ center, radius });

let keyListener = null;
let keyLocations = {};

return new Promise((res, rej) => {
    geoQuery.on('ready', function () {
        console.log(keyLocations);
        keyListener.cancel();
        geoQuery.cancel();
        res();
    });
    keyListener = geoQuery.on('key_entered', (key, coordinates, distance) => {
        keyLocations[key] = { coordinates, distance };
    });
});
Callam
  • 11,409
  • 2
  • 34
  • 32
  • I tried it out, but I don't think I can use it in Cloud Function. I don't think it's a good idea to use it on the client side, I'm writing a mobile device app, so it'll be to expensive – K.Wu Jan 21 '18 at 05:09
  • @K.Wu I have implemented a search engine that use's GeoFire to query a location and then filters the key's individually by requesting each child. You can paginate the result by requesting and filtering batches of children. Where are you having difficulty in using GeoFire in Cloud Functions? – Callam Jan 21 '18 at 07:34