Skip to content

Commit

Permalink
feat(findCoordinates): findCoordinatesKey now findCoordinates as …
Browse files Browse the repository at this point in the history
…it returns GeoPoint rather than the key of the GeoPoint, can also find embedded keys, fixes #88
  • Loading branch information
MichaelSolati committed Apr 23, 2019
1 parent bd18b7a commit 94161a4
Show file tree
Hide file tree
Showing 5 changed files with 82 additions and 27 deletions.
5 changes: 2 additions & 3 deletions src/GeoCollectionReference.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { GeoFirestoreTypes } from './GeoFirestoreTypes';
import { GeoDocumentReference } from './GeoDocumentReference';
import { GeoQuery } from './GeoQuery';
import { findCoordinatesKey, encodeGeohash, encodeGeoDocument } from './utils';
import { findCoordinates, encodeGeohash, encodeGeoDocument } from './utils';

/**
* A `GeoCollectionReference` object can be used for adding documents, getting document references, and querying for documents (using the
Expand Down Expand Up @@ -48,8 +48,7 @@ export class GeoCollectionReference extends GeoQuery {
customKey?: string
): Promise<GeoDocumentReference> {
if (Object.prototype.toString.call(data) === '[object Object]') {
const locationKey: string = findCoordinatesKey(data, customKey);
const location: GeoFirestoreTypes.cloud.GeoPoint | GeoFirestoreTypes.web.GeoPoint = data[locationKey];
const location = findCoordinates(data, customKey);
const geohash: string = encodeGeohash(location);
return (this._collection as GeoFirestoreTypes.cloud.CollectionReference)
.add(encodeGeoDocument(location, geohash, data)).then(doc => new GeoDocumentReference(doc));
Expand Down
53 changes: 31 additions & 22 deletions src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -257,9 +257,8 @@ export function encodeSetDocument(
if (Object.prototype.toString.call(data) === '[object Object]') {
const customKey = (options) ? options.customKey : null;
const unparsed: GeoFirestoreTypes.DocumentData = ('d' in data) ? data.d : data;
const locationKey: string = findCoordinatesKey(unparsed, customKey, (options && (options.merge || !!options.mergeFields)));
if (locationKey) {
const location: GeoFirestoreTypes.cloud.GeoPoint | GeoFirestoreTypes.web.GeoPoint = unparsed[locationKey];
const location = findCoordinates(unparsed, customKey, (options && (options.merge || !!options.mergeFields)));
if (location) {
const geohash: string = encodeGeohash(location);
return encodeGeoDocument(location, geohash, unparsed);
}
Expand All @@ -279,9 +278,9 @@ export function encodeSetDocument(
export function encodeUpdateDocument(data: GeoFirestoreTypes.UpdateData, customKey?: string): GeoFirestoreTypes.UpdateData {
if (Object.prototype.toString.call(data) === '[object Object]') {
const result = {};
const locationKey: string = findCoordinatesKey(data, customKey, true);
if (locationKey) {
result['l'] = data[locationKey];
const location = findCoordinates(data, customKey, true);
if (location) {
result['l'] = location;
result['g'] = encodeGeohash(result['l']);
}
Object.getOwnPropertyNames(data).forEach((prop: string) => {
Expand All @@ -294,38 +293,48 @@ export function encodeUpdateDocument(data: GeoFirestoreTypes.UpdateData, customK
}

/**
* Returns the key of a document that is a GeoPoint.
* Returns coordinates as GeoPoint from a document.
*
* @param document A Firestore document.
* @param customKey The key of the document to use as the location. Otherwise we default to `coordinates`.
* @param flag Tells function supress errors.
* @return The key for the location field of a document.
* @return The GeoPoint for the location field of a document.
*/
export function findCoordinatesKey(document: GeoFirestoreTypes.DocumentData, customKey?: string, flag = false): string {
export function findCoordinates(
document: GeoFirestoreTypes.DocumentData, customKey?: string, flag = false
): GeoFirestoreTypes.web.GeoPoint | GeoFirestoreTypes.cloud.GeoPoint {
let error: string;
let key: string;
let coordinates;

if (document && typeof document === 'object') {
if (customKey && customKey in document) {
key = customKey;
} else if ('coordinates' in document) {
key = 'coordinates';
} else {
error = 'no valid key exists';
}
if (!customKey) {
coordinates = document['coordinates'];
} else if (customKey in document) {
coordinates = document[customKey];
} else {
error = 'document not an object';
const props = customKey.split('.');
coordinates = document;
for (const prop of props) {
if (!(prop in coordinates)) {
coordinates = document['coordinates'];
break;
}
coordinates = coordinates[prop];
}
}

if (!coordinates) {
error = 'could not find GeoPoint';
}

if (key && !validateLocation(document[key], true)) {
error = key + ' is not a valid GeoPoint';
if (coordinates && !validateLocation(coordinates, true)) {
error = 'invalid GeoPoint';
}

if (error && !flag) {
throw new Error('Invalid GeoFirestore document: ' + error);
}

return key;
return coordinates;
}

/**
Expand Down
20 changes: 20 additions & 0 deletions test/GeoCollectionReference.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,26 @@ describe('GeoCollectionReference Tests:', () => {
expect(() => geocollection.add(invalidObject)).to.throw();
});
});

it('add() adds a new object with a custom key', () => {
return geocollection.add({ geopoint: new firebase.firestore.GeoPoint(0, 0) }, 'geopoint').then(d1 => {
return wait(100).then(() => {
return geocollection.doc(d1.id).get().then(d2 => {
expect(d2.exists).to.equal(true);
});
});
});
});

it('add() adds a new object with an embedded custom key', () => {
return geocollection.add({ geopoint: { coordinates: new firebase.firestore.GeoPoint(0, 0) } }, 'geopoint.coordinates').then(d1 => {
return wait(100).then(() => {
return geocollection.doc(d1.id).get().then(d2 => {
expect(d2.exists).to.equal(true);
});
});
});
});
});

describe('doc():', () => {
Expand Down
29 changes: 28 additions & 1 deletion test/GeoDocumentReference.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { GeoCollectionReference } from '../src/GeoCollectionReference';
import { GeoDocumentReference } from '../src/GeoDocumentReference';
import {
afterEachHelper, beforeEachHelper, collection, dummyData, geocollection,
geofirestore, invalidFirestores, invalidObjects, sortObject, stubDatabase
geofirestore, invalidFirestores, invalidObjects, sortObject, stubDatabase, wait
} from './common';

const expect = chai.expect;
Expand Down Expand Up @@ -192,6 +192,33 @@ describe('GeoDocumentReference Tests:', () => {
expect(() => geocollection.doc(`loc${index}`).set(invalidObject)).to.throw();
});
});

it('set() adds a new object with a custom key', () => {
const documentReference = geocollection.doc('loc1');
return documentReference.set({ geopoint: new firebase.firestore.GeoPoint(0, 0) }, { customKey: 'geopoint' })
.then(() => wait(100))
.then(() => documentReference.get())
.then(d1 => {
return geocollection.doc(d1.id).get().then(d2 => {
expect(d2.exists).to.equal(true);
});
});
});

it('set() adds a new object with an embedded custom key', () => {
const documentReference = geocollection.doc('loc1');
return documentReference.set(
{ geopoint: { coordinates: new firebase.firestore.GeoPoint(0, 0) } },
{ customKey: 'geopoint.coordinates' }
)
.then(() => wait(100))
.then(() => documentReference.get())
.then(d1 => {
return geocollection.doc(d1.id).get().then(d2 => {
expect(d2.exists).to.equal(true);
});
});
});
});

describe('update():', () => {
Expand Down
2 changes: 1 addition & 1 deletion test/utils.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import 'firebase/firestore';

import {
boundingBoxBits, boundingBoxCoordinates, calculateDistance, decodeGeoDocumentData, decodeGeoQueryDocumentSnapshotData,
degreesToRadians, encodeGeohash, encodeGeoDocument, encodeSetDocument, encodeUpdateDocument, findCoordinatesKey,
degreesToRadians, encodeGeohash, encodeGeoDocument, encodeSetDocument, encodeUpdateDocument, findCoordinates,
generateGeoQueryDocumentSnapshot, geohashQueries, GEOHASH_PRECISION, geohashQuery, latitudeBitsForResolution, log2,
longitudeBitsForResolution, metersToLongitudeDegrees, sanitizeSetOptions, toGeoPoint, validateGeoDocument, validateGeohash,
validateLimit, validateLocation, validateQueryCriteria, wrapLongitude
Expand Down

0 comments on commit 94161a4

Please sign in to comment.