in packages/geofire/src/GeoQuery.ts [431:503]
private _listenForNewGeohashes(): void {
// Get the list of geohashes to query
let geohashesToQuery: string[] = geohashQueryBounds(this._center, this._radius * 1000).map(this._queryToString);
// Filter out duplicate geohashes
geohashesToQuery = geohashesToQuery.filter((geohash: string, i: number) => geohashesToQuery.indexOf(geohash) === i);
// For all of the geohashes that we are already currently querying, check if they are still
// supposed to be queried. If so, don't re-query them. Otherwise, mark them to be un-queried
// next time we clean up the current geohashes queried dictionary.
const keys: string[] = Object.keys(this._currentGeohashesQueried);
keys.forEach((geohashQueryStr: string) => {
const index: number = geohashesToQuery.indexOf(geohashQueryStr);
if (index === -1) {
this._currentGeohashesQueried[geohashQueryStr].active = false;
} else {
this._currentGeohashesQueried[geohashQueryStr].active = true;
geohashesToQuery.splice(index, 1);
}
});
// If we are not already cleaning up the current geohashes queried and we have more than 25 of them,
// kick off a timeout to clean them up so we don't create an infinite number of unneeded queries.
if (this._geohashCleanupScheduled === false && Object.keys(this._currentGeohashesQueried).length > 25) {
this._geohashCleanupScheduled = true;
this._cleanUpCurrentGeohashesQueriedTimeout = setTimeout(() => {
this._cleanUpCurrentGeohashesQueried();
}, 10);
}
// Keep track of which geohashes have been processed so we know when to fire the 'ready' event
this._outstandingGeohashReadyEvents = geohashesToQuery.slice();
// Loop through each geohash to query for and listen for new geohashes which have the same prefix.
// For every match, attach a value callback which will fire the appropriate events.
// Once every geohash to query is processed, fire the 'ready' event.
geohashesToQuery.forEach((toQueryStr: string) => {
// decode the geohash query string
const query: string[] = this._stringToQuery(toQueryStr);
// Create the Firebase query
const firebaseQuery: DatabaseTypes.Query = this._firebaseRef.orderByChild('g').startAt(query[0]).endAt(query[1]);
// For every new matching geohash, determine if we should fire the 'key_entered' event
const childAddedCallback = firebaseQuery.on('child_added', (a) => this._childAddedCallback(a));
const childRemovedCallback = firebaseQuery.on('child_removed', (a) => this._childRemovedCallback(a));
const childChangedCallback = firebaseQuery.on('child_changed', (a) => this._childChangedCallback(a));
// Once the current geohash to query is processed, see if it is the last one to be processed
// and, if so, mark the value event as fired.
// Note that Firebase fires the 'value' event after every 'child_added' event fires.
const valueCallback = firebaseQuery.on('value', () => {
firebaseQuery.off('value', valueCallback);
this._geohashQueryReadyCallback(toQueryStr);
});
// Add the geohash query to the current geohashes queried dictionary and save its state
this._currentGeohashesQueried[toQueryStr] = {
active: true,
childAddedCallback,
childRemovedCallback,
childChangedCallback,
valueCallback
};
});
// Based upon the algorithm to calculate geohashes, it's possible that no 'new'
// geohashes were queried even if the client updates the radius of the query.
// This results in no 'READY' event being fired after the .updateCriteria() call.
// Check to see if this is the case, and trigger the 'READY' event.
if (geohashesToQuery.length === 0) {
this._geohashQueryReadyCallback();
}
}