Skip to content

Commit

Permalink
feat: add functions for onSnapshot and get for geoqueries
Browse files Browse the repository at this point in the history
  • Loading branch information
MichaelSolati committed Jun 21, 2020
1 parent e9b3b7a commit e95be22
Show file tree
Hide file tree
Showing 9 changed files with 1,003 additions and 17 deletions.
6 changes: 3 additions & 3 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@
"README.md"
],
"dependencies": {
"geokit": "^0.1.5"
"geokit": "^1.0.1"
},
"optionalDependencies": {
"@google-cloud/firestore": ">= 2.0.0",
Expand Down
119 changes: 119 additions & 0 deletions src/api/query-get.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
import {GeoQuerySnapshot} from './snapshot';
import {GeoFirestoreTypes} from '../definitions';
import {
calculateDistance,
generateQuery,
validateQueryCriteria,
} from '../utils';

/**
* Executes a query and returns the result(s) as a GeoQuerySnapshot.
*
* WEB CLIENT ONLY
* Note: By default, get() attempts to provide up-to-date data when possible by waiting for data from the server, but it may return
* cached data or fail if you are offline and the server cannot be reached. This behavior can be altered via the `GetOptions` parameter.
*
* @param query The Firestore Query instance.
* @param queryCriteria The query criteria of geo based queries, includes field such as center, radius, and limit.
*/
export function geoQueryGet(
query: GeoFirestoreTypes.cloud.Query | GeoFirestoreTypes.web.Query,
queryCriteria: GeoFirestoreTypes.QueryCriteria,
options: GeoFirestoreTypes.web.GetOptions = {source: 'default'}
): Promise<GeoQuerySnapshot> {
const isWeb =
Object.prototype.toString.call(
(query as GeoFirestoreTypes.web.CollectionReference).firestore
.enablePersistence
) === '[object Function]';

if (queryCriteria.center && typeof queryCriteria.radius === 'number') {
const queries = generateQuery(query, queryCriteria).map(q =>
isWeb ? q.get(options) : q.get()
);

return Promise.all(queries).then(value =>
new GeoQueryGet(value, queryCriteria).getGeoQuerySnapshot()
);
} else {
query = queryCriteria.limit ? query.limit(queryCriteria.limit) : query;
const promise = isWeb
? (query as GeoFirestoreTypes.web.Query).get(options)
: (query as GeoFirestoreTypes.web.Query).get();

return promise.then(snapshot => new GeoQuerySnapshot(snapshot));
}
}
/**
* A `GeoJoinerGet` aggregates multiple `get` results.
*/
export class GeoQueryGet {
private _docs: Map<
string,
GeoFirestoreTypes.web.QueryDocumentSnapshot
> = new Map();

/**
* @param snapshots An array of snpashots from a Firestore Query `get` call.
* @param _queryCriteria The query criteria of geo based queries, includes field such as center, radius, and limit.
*/
constructor(
snapshots: GeoFirestoreTypes.web.QuerySnapshot[],
private _queryCriteria: GeoFirestoreTypes.QueryCriteria
) {
validateQueryCriteria(_queryCriteria);

snapshots.forEach((snapshot: GeoFirestoreTypes.web.QuerySnapshot) => {
snapshot.docs.forEach(doc => {
const distance = calculateDistance(
this._queryCriteria.center,
(doc.data() as GeoFirestoreTypes.GeoDocumentData).g.geopoint
);

if (this._queryCriteria.radius >= distance) {
this._docs.set(doc.id, doc);
}
});
});

if (
this._queryCriteria.limit &&
this._docs.size > this._queryCriteria.limit
) {
const arrayToLimit = Array.from(this._docs.values())
.map(doc => {
return {
distance: calculateDistance(
this._queryCriteria.center,
(doc.data() as GeoFirestoreTypes.GeoDocumentData).g.geopoint
),
id: doc.id,
};
})
.sort((a, b) => a.distance - b.distance);

for (let i = this._queryCriteria.limit; i < arrayToLimit.length; i++) {
this._docs.delete(arrayToLimit[i].id);
}
}
}

/**
* Returns parsed docs as a GeoQuerySnapshot.
*
* @return A new `GeoQuerySnapshot` of the filtered documents from the `get`.
*/
getGeoQuerySnapshot(): GeoQuerySnapshot {
const docs = Array.from(this._docs.values());
return new GeoQuerySnapshot(
{
docs,
docChanges: () =>
docs.map((doc, index) => {
return {doc, newIndex: index, oldIndex: -1, type: 'added'};
}),
} as GeoFirestoreTypes.web.QuerySnapshot,
this._queryCriteria.center
);
}
}
Loading

0 comments on commit e95be22

Please sign in to comment.