javascriptfirebasegoogle-cloud-firestoregeohashing

Firestore query timestamp and geohash in one query


I'm attempting to query my Firestore database using both a timestamp and a geohash, but the results keep coming back as an empty array. If I only use the geohash or only use the timestamp then I got results, but no results when I use them together. I don't get any errors in the console when running this query either. I'm not sure exactly why this query isn't working, any help would be appreciated. Also of note, I'm using the method suggested within the Firebase docs for handling geohash queries. Here's the code for the query:

const bounds = geofire.geohashQueryBounds(center, radiusInM);
const promises = [];
for (const b of bounds) {
    const query = firebase
        .firestore()
        .collection('scheduledWorkouts')
        .where('date', '>=', start) // start is a Firebase timestamp variable
        .where('date', '<=', end) // end is a Firebase timestamp variable
        .orderBy('date')
        .orderBy('geohash')
        .startAt(b[0])
        .endAt(b[1]);
    promises.push(query.get());
}

Promise.all(promises)
    .then((snapshots) => {
        const matchingDocs = [];

        for (const snap of snapshots) {
            for (const doc of snap.docs) {
                const lat = doc.get('location.lat');
                const lng = doc.get('location.lng');

                // filtering out false positives
                const distanceInKm = geofire.distanceBetween(
                    [lat, lng],
                    center
                );
                const distanceInM = distanceInKm * 1000;
                if (distanceInM <= radiusInM) {
                    matchingDocs.push(doc.data());
                }
            }
        }

        return matchingDocs;
    })
    .then((matchingDocs) => {
        console.log(matchingDocs);
    })
    .catch((error) => {
        console.log(error);
    });

Solution

  • Firestore has this limit on queries:

    In a compound query, range (<, <=, >, >=) and not equals (!=, not-in) comparisons must all filter on the same field.

    While not explicitly mentioned, start* and end* filters also fall under this limitation.

    By employing a geohash, you already work around this limitation by combining the latitude and longitude in a single value that can be ordered and filtered. But by then adding a range filter on date, you are hitting the same limitation again.


    The only ways to filter on latitude, longitude and date:

    1. Use an equality filter for date, by storing them in a format that allows for that. For example if you want to allow filtering for a specific week, you could store the day in 2021-05-26 format, and do an in query on 7 values.
    2. Find a way to combine the timestamps with the latitude and longitude in your own value type, similar to what a geohash already does for latitude and longitude. This means that you effectively have to be able to express how time relates to distance, so is typically only feasible in very well defined domains.