GithubHelp home page GithubHelp logo

codediodeio / geofirex Goto Github PK

View Code? Open in Web Editor NEW
477.0 20.0 107.0 2.35 MB

:globe_with_meridians: :round_pushpin: Geolocation Queries with Firestore & RxJS

Home Page: https://geo-test-c92e4.firebaseapp.com/

JavaScript 7.33% TypeScript 84.25% HTML 6.02% CSS 2.41%
rxjs firebase firestore geojson geolocation

geofirex's Introduction

GeoFireX

Realtime Geolocation with Firestore & RxJS. Query geographic points within a radius on the web or Node.js.

⚡ QuickStart

npm install geofirex rxjs firebase

Initialize

The library is a lightweight extension for the Firebase Web and Admin JavaScript SDKs to provide tools for wrangling geolocation data in Firestore.

Web:

// Init Firebase
import firebase from 'firebase/app';
firebase.initializeApp(yourConfig);

// Init GeoFireX
import geofirex from 'geofirex';
const geo = geofirex.init(firebase);

Node.js with the Firebase Admin SDK:

const admin = require('firebase-admin');
admin.initializeApp();

const geo = require('geofirex').init(admin);

With Typescript:

import * as geofirex from 'geofirex'; 
const geo = geofirex.init(firebase);

Write Geolocation Data

Next, add some geolocation data in your database using the main Firebase SDK. You can add multiple points to a single doc. Calling geo.point(lat, lng) creates an object with a geohash string and a Firestore GeoPoint. Data must be saved in this format to be queried.

const cities = firestore().collection('cities');

const position = geo.point(40, -119);

cities.add({ name: 'Phoenix', position });

Query Geo Data

Query Firestore for cities.position within 100km radius of a centerpoint.

const center = geo.point(40.1, -119.1);
const radius = 100;
const field = 'position';

const query = geo.query(cities).within(center, radius, field);

Each hit returns a realtime Observable of the document data, plus some useful hitMetadata like distance and bearing from the query centerpoint.

query.subscribe(console.log);
// [{ ...documentData, hitMetadata: { distance: 1.23232, bearing: 230.23 }  }]

You now have a realtime stream of data to visualize on a map.

📓 API

query<T>(ref: CollectionReference | Query | string): GeoFireQuery<T>

Creates reference to a Firestore collection or query that can be used to make geo-queries.

Example:

const geoRef = geo.query('cities');

// OR make a geoquery on top of a firestore query

const firestoreRef = firestore().collection('cities').where('name', '==', 'Phoenix');
const geoRef = geo.query(firestoreRef);

within(center: FirePoint, radius: number, field: string): Observable<T[]>

const query = geoRef.within(center: FirePoint, radius: number, field: string)
        
query.subscribe(hits => console.log(hits))

// OR fetch as a promise

import { get } from 'geofirex';

const hits = await get(query);

Query the parent Firestore collection by geographic distance. It will return documents that exist within X kilometers of the centerpoint.

Each doc also contains returns distance and bearing calculated on the query on the hitMetadata property.

point(latitude: number, longitude: number): FirePoint

Returns an object with the required geohash format to save to Firestore.

Example: const point = geo.point(38, -119)

A point is a plain JS object with two properties.

  • point.geohash Returns a geohash string at precision 9
  • point.geopoint Returns a Firestore GeoPoint

Additional Features

The goal of this package is to facilitate rapid feature development with tools like MapBox, Google Maps, and D3.js. If you have an idea for a useful feature, open an issue.

Logging

Each query runs on a set of geohash squares, so you may read more documents than actually exist inside the radius. Use the log option to examine the total query size and latency.

query.within(center, radius, field, { log: true })

Logging GeoQueries

Geo Calculations

Convenience methods for calculating distance and bearing.

  • geo.distance(geo.point(38, -118), geo.point(40, -115)) Haversine distance
  • geo.bearing(to, from) Haversine bearing

toGeoJSON Operator

A custom RxJS operator that transforms a collection into a GeoJSON FeatureCollection. Very useful for tools like MapBox that can use GeoJSON to update a realtime data source.

import { toGeoJSON } from 'geofirex';

const query = geo.query('cars').within(...)

query.pipe( toGeoJSON() )

// Emits a single object typed as a FeatureCollection<Geometry>
{
  "type": "FeatureCollection",
  "features": [...]
}

Promises with get

Don't need a realtime stream? Convert any query observable to a promise by wrapping it with get.

import { get } from 'geofirex';

async function getCars {
    const query = geo.query('cars').within(...)
    const cars = await get(query)
}

Tips

Compound Queries

The only well-supported type of compound query is where. A geoquery combines multiple smaller queries into a unified radius, so limit and pagination operators will not provide predictable results - a better approach is to search a smaller radius and do your sorting client-side.

Example:

// Make a query like you normally would
const users = firestore().collection('users').where('status', '==', 'online');


const nearbyOnlineUsers = geo.query(users).within(center, radius, field);

Note: This query requires a composite index, which you will be prompted to create with an error from Firestore on the first request.

Usage with RxJS < 6.2, or Ionic v3

This package requires RxJS 6.2, but you can still use it with older versions without blowing up your app by installing rxjs-compat.

Example:

npm i rxjs@latest rxjs-compat

Make Dynamic Queries the RxJS Way

const radius = new BehaviorSubject(1);
const cities = geo.query('cities');

const points = this.radius.pipe(
  switchMap(rad => {
    return cities.within(center, rad, 'point');
  })
);

// Now update your query
radius.next(23);

Always Order by [Latitude, Longitude]

The GeoJSON spec formats coords as [Longitude, Latitude] to represent an X/Y plane. However, the Firebase GeoPoint uses [Latitude, Longitude]. For consistency, this library always requires to use the latter Firebase-style format.

geofirex's People

Contributors

azurechen avatar codediodeio avatar eikooc avatar liamkarlmitchell avatar rtpharry avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

geofirex's Issues

XhrIoPool is not a constructor at new WebChannelConnection

Hello, I'm getting error when request a collection:

firebase.initializeApp(FIREBASE_CONFIG);

export const db = firebase.firestore();
export const geo = geofirex.init(firebase);

const opinions = geo.collection('opinions'); // Here

Firestore (4.10.1) 2019-03-14T06:35:54.962Z: INTERNAL UNHANDLED ERROR: TypeError: _firebase_webchannel_wrapper__WEBPACK_IMPORTED_MODULE_0__.XhrIoPool is not a constructor at new WebChannelConnection (https://ryrapp.herokuapp.com/bundle.js:13340:21) at BrowserPlatform../node_modules/@firebase/firestore/dist/esm/src/platform_browser/browser_platform.js.BrowserPlatform.loadConnection (https://ryrapp.herokuapp.com/bundle.js:13271:32) at FirestoreClient../node_modules/@firebase/firestore/dist/esm/src/core/firestore_client.js.FirestoreClient.initializeRest (https://ryrapp.herokuapp.com/bundle.js:4206:14) at https://ryrapp.herokuapp.com/bundle.js:4097:54

Everything works fine in development. There's nothing else pointing to geofirex, only that line.

Firestore GeoPoint latitude and longitude errors

Hi,
I have noticed that in my app the GeoPoint is stored as: {Wa:number, za:number} instead of latitude and longitude as used in your code.
I have modified the "filter" inside the "within" function as follows:
var _a = val[field].geopoint, latitude = _a.latitude ? _a.latitude : _a.Wa, longitude = _a.longitude ? _a.longitude :_a.za;

With this is works.
Bye
Luca

bug: queryFn not working

When I supply an extra query function on the geo collection, nothing is returned. this.geo.collection('unicorns', ref => ref.where('height', '==', 'tall')) returns nothing.

Range queries

Is there a way to use range queries ('<', '<=', '>', '>=') with a document field?

Private fireabase ref

GeoFireClient have the firebase reference with private accessor which fail the test suite immediately.

setDoc with Merge.

How can I set the point and update the data of a document, without clearing all document fields.

I notice that setPoint updates but keeps the existing properties (Fields).
But setDoc over-writes the document removing existing properties (Fields).

Could have an optional { merge: true } parameter, or have another similar named function for merging.

Such as:

    /**
     * Create or update a document in the collection based on the document ID
     * Merge keys.
     * @param  {string} id
     * @param  {any} data
     * @returns {Promise<void>}
     */
    GeoFireCollectionRef.prototype.setDocMerge = function (id, data) {
        return this.ref.doc(id).set(data, { merge: true });
    };

Current fields should be able to be maintained when wanting to update location and some other fields.

E.g.
setDocMerge('deviceId', { l: geo.point(lat,lng).data, fuel: 1.4, altitude: 100 })

bug: geo query returns every item twice

Hello. When I query a collection and have a radius over ~1500 km the query returns all the items twice so a collection of 9 items would become 18.
it doesn't happen when I query with a radius below ~1500 km

const center = this.geo.point(location.lat, location.lon)
const field = 'position'
const items = this.geo.collection('items')
const query = items.within(center, radius, field)

Unable to query database from Angular application.

Hello,
I was following the tutorial but cannot seem to query the dataset./
It must also be noted that you cannot save the data as an object type anymore in firestore so I saved the data as a geo-point.

I am using the following two options:
Option 1)

I declare the following in the ngOnInit lifecycle hook:

"const center = this.geo.point(51.5249949, -0.7466908);
this.points = this.radius.pipe(
switchMap(r => {
return this.geo.query('positions').within(center, r, field)
}))"

The following error appears in the chrome console log

ERROR Error: Uncaught (in promise): TypeError: Cannot read property 'GeoPoint' of undefined
TypeError: Cannot read property 'GeoPoint' of undefined

Option 2)

I found the following github repo which referenced the error message above:
angular/angularfire#1604

I declare the following in the ngOnInit lifecycle hook:

"const center = new firestore.GeoPoint(51.5249949, -0.7466908)
this.points = this.radius.pipe(
switchMap(r => {
return this.geo.query('positions').within(center, r, field)
}))"

The following error message appears under the center parameter in the within():

"Argument of type 'GeoPoint' is not assignable to parameter of type 'FirePoint'
Type 'GeoPoint' is missing the following properties from type 'FirePoint': geopoint, geohash"

Is paging possible with this library?

Is paging possible with this library?
I got it with redux thunk and firestore but I do not know how I can put limits.
I know that it is not possible with this library to query where 'birthday', '> =', today because it is already ordered by the geopoints, that is not a problem, what I do not know is how to create this function with the geoquery.
Thanks for this wonderful library!

`//--------------------------onSnapshot listener------------------------------------
export const getUsersAction = (lastUsers) =>
async (dispatch, getState) => {

const today = new Date(Date.now());
const usersRef = firestore.collection('users')

try {

        let startAfter = lastUsers && (await firestore.collection('users').doc(lastUsers.id).get());
        
        let query;

        lastEvent
            ? (query = usersRef.where('birthday', '>=', today).startAfter(startAfter).limit(5))
            : (query = usersRef.where('birthday', '>=', today).limit(5))

        query.onSnapshot(async (querySnapshot) => {
            
            
            if (querySnapshot.docs.length === 0) {
                console.log('querySnapshot.docs.length === 0');
             
                return querySnapshot;
            }
        
            let users = [];

            for (let i = 0; i < querySnapshot.docs.length; i++) {
                let user = {...querySnapshot.docs[i].data(), id: querySnapshot.docs[i].id}
                users.push(user)
            }
            
	console.log(users)                

            return querySnapshot;
            
        }, (err) => {
            console.log('error onSnapshor', err);
        })
    }
    catch (error) {
        console.log(error);
        
}

}
// ------------------------or not onSnapshot listener---------------------------------
export const getUsersAction = (lastUsers) =>
async (dispatch, getState) => {

const today = new Date(Date.now());
const usersRef = firestore.collection('users')

try {

        let startAfter = lastUsers && (await firestore.collection('users').doc(lastUsers.id).get());
        
        let query;

        lastEvent
            ? (query = usersRef.where('birthday', '>=', today).startAfter(startAfter).limit(5))
            : (query = usersRef.where('birthday', '>=', today).limit(5))

        
            let querySnap = await query.get();
            
            if (querySnap.docs.length === 0) {
                console.log('querySnap.docs.length === 0');
             
                return querySnap;
            }
        
            let users = [];

            for (let i = 0; i < querySnap.docs.length; i++) {
                let user = {...querySnap.docs[i].data(), id: querySnap.docs[i].id}
                users.push(user)
            }
            
	console.log(users)                

            return querySnap;
            
        
    }
    catch (error) {
        console.log(error);
        
}

}
`

cannot find module geojson

import { Point, Feature } from 'geojson'; inside the dist geohash.ts

Hi Jeff,

Followed the readme and added geofirex in my ionic project .
On ionic serve I get

TypeScript ERROR
Cannot find module 'geojson'.

  import { firestore } from './interfaces';
  import { Point, Feature } from 'geojson';
  export declare type Latitude = number;

Receive only added documents

Hello,

I'd like to be able to receive an update only if a point has entered my radius. In other words, if an item has exited, position has changed, or has been deleted, I don't want to get an update event for that. In addition, I only want to receive those items that have entered, not the whole list of items. Right now, I tried something like the examples:
points.subscribe(x => {console.log(x)})
But here x gives me the entire list every time anything changed. How can I
1-) receive an update only if an item has entered
2-) get a list of only those items that have entered.

Thank you

app.firestore is not a function

I just started using this library with react-native and I am getting this error on geo.collections() method. Please suggest. Thanks,

2018-07-19_0629

How does this differ from GeoFirestore?

I have been utilizing GeoFirestore in my application for some time now, and it works fairly similar to this library. I am curious to see if this was inspired by GeoFirestore, and how it truly differs? The one thing I have noticed so far is that it handles a lot of the logic inside the library, whereas Geofirestore forces the user to handle a lot of the entrances and exits of data.

Enhancement Request: Pagination support

Hi,
It would be great if there will be a pagination to the queries, i know that there are 9 queries on each request, one for geo block so it's harder to pagination but do you have an idea how to make this happened?

And by the way, this libary is amezing, thank you very much.

Ionic 3 : Cannot read property 'apply' of undefined

I am getting this error when trying to retrieve data from firestore, I am able to write to the firestore without any issues but unable to read.

ERROR Error: Uncaught (in promise): TypeError: Cannot read property 'apply' of undefined
TypeError: Cannot read property 'apply' of undefined
at GeoFireCollectionRef.within (index.esm.js:1548)
at home.ts:27
...........
and

InvalidValueError: setCenter: not a LatLng or LatLngLiteral: in property lat: not a number

Code

export class HomePage implements OnInit{
geo = geofirex.init(firebase)
position:number[] =[];
points:Observable< any>
busses:any
constructor(public navCtrl: NavController,
public geolocation: Geolocation
) {
this.busses= this.geo.collection('buslocation')
this.geolocation.getCurrentPosition({ enableHighAccuracy: true }).then(data=>{
this.position[0] = data.coords.latitude
this.position[1] = data.coords.longitude
const center = this.geo.point(this.position[0],this.position[1])
const radius = 0.5
const field = 'pos'
this.points = this.busses.within(center,radius,field)
})
}
writeLation(event){
var point = this.geo.point(event.lat, event.lng);
this.busses.add({ name: 'here', position: point.data })
}
ngOnInit(): void {

}
}

Enhancement Request: optimization for firestore's cost?

As described at https://youtu.be/lO1S-FAcZU8?t=2m2s, there are inherited edge cases with geohash and they can be solved with doing more queries and filtering. However, since firestore's cost structure is based on number of reads, and more queries yield more latency, it would be great if geofirex can allow the user to pick whether they can live with certain edge cases to save the cost. What do you think?

Argument of type 'typeof firebase'is not assignable to parameter of type 'FirebaseApp'

Im getting this error from a fresh project, i have didnt found any answere.
Thank you.
package.json
"dependencies": { "@agm/core": "^1.0.0-beta.3", "@angular/animations": "^5.2.0", "@angular/common": "^5.2.0", "@angular/compiler": "^5.2.0", "@angular/core": "^5.2.0", "@angular/forms": "^5.2.0", "@angular/http": "^5.2.0", "@angular/platform-browser": "^5.2.0", "@angular/platform-browser-dynamic": "^5.2.0", "@angular/router": "^5.2.0", "angularfire2": "^5.0.0-rc.11", "core-js": "^2.4.1", "firebase": "^5.4.0", "geofirex": "0.0.6", "rxjs": "^5.5.11", "zone.js": "^0.8.19" }, "devDependencies": { "@angular/cli": "~1.7.4", "@angular/compiler-cli": "^5.2.0", "@angular/language-service": "^5.2.0", "@types/jasmine": "~2.8.3", "@types/jasminewd2": "~2.0.2", "@types/node": "~6.0.60", "codelyzer": "^4.0.1", "jasmine-core": "~2.8.0", "jasmine-spec-reporter": "~4.2.1", "karma": "~2.0.0", "karma-chrome-launcher": "~2.2.0", "karma-coverage-istanbul-reporter": "^1.2.1", "karma-jasmine": "~1.1.0", "karma-jasmine-html-reporter": "^0.2.2", "protractor": "~5.1.2", "ts-node": "~4.1.0", "tslint": "~5.9.1", "typescript": "~2.5.3" }

app,module.ts

`import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
import { AngularFireModule } from 'angularfire2';
import { AngularFirestoreModule } from 'angularfire2/firestore';
import { AngularFireStorageModule } from 'angularfire2/storage';
import { AngularFireAuthModule } from 'angularfire2/auth';
import { AgmCoreModule } from '@agm/core';
import { environment } from '../environments/environment';
import { GoogleMapComponent } from './google-map/google-map.component';

@NgModule({
  declarations: [
    AppComponent,
    GoogleMapComponent
  ],
  imports: [
    BrowserModule,
    AppRoutingModule,
    AngularFireModule.initializeApp(environment.firebase), // imports firebase/app needed for everything
    AngularFirestoreModule, // imports firebase/firestore, only needed for database features
    AngularFireAuthModule, // imports firebase/auth, only needed for auth features,
    AngularFireStorageModule,
    AgmCoreModule.forRoot({
      apiKey: environment.googleMapsKey
    })
  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule { }
`

webpack: Compiled successfully. ERROR in src/app/google-map/google-map.component.ts(23,31): error TS2345: Argument of type 'typeof firebase'is not assignable to parameter of type 'FirebaseApp'. Types of property 'firestore' are incompatible. Type 'typeof firestore' is not assignable to type '() => FirebaseFirestore'. Type 'Firestore' is not assignable to type 'FirebaseFirestore'. Types of property 'collection' are incompatible. Type '(collectionPath: string) => firebase.firestore.CollectionReference' is not assignable to type '(collectionPath: string) => CollectionReference'. Type 'firebase.firestore.CollectionReference' is not assignable to type 'CollectionReference'. Types of property 'parent' are incompatible. Type 'firebase.firestore.DocumentReference' is not assignable to type 'DocumentReference'. Types of property 'firestore' are incompatible. Type 'Firestore' is not assignable to type 'FirebaseFirestore'.

toGeoJSON returns empty properties

Hi! My query seems to work but, the returned properties are empty. No errors

const cities = geo.collection('cities');

async function getGeoJSON(point) {
  const query = cities.within(point, 10, 'position');
  const obvservable = query.pipe(toGeoJSON('position'));
  const geoj = await get(obvservable);
  return geoj;
}

The database has 9 cities with a name property. this is what i get in console:

features: Array(7)
  0:
      geometry: {type: "Point", coordinates: Array(2)}
      properties:
           __proto__: Object
      type: "Feature"
      __proto__: Object
  1: {type: "Feature", geometry: {…}, properties: {…}}
  2: {type: "Feature", geometry: {…}, properties: {…}}
  3: {type: "Feature", geometry: {…}, properties: {…}}
  4: {type: "Feature", geometry: {…}, properties: {…}}
  5: {type: "Feature", geometry: {…}, properties: {…}}
  6: {type: "Feature", geometry: {…}, properties: {…}}
length: 7
__proto__: Array(0)
type: "FeatureCollection"

firebase: "^4.6.2",
geofirex: "0.0.6",
rxjs: "^6.3.3"

Saving multiple geopoints on a single document

Hi @codediodeio ,

First off thanks for this awesome library. Is it possible to add an example with respect to saving multiple geopoints on a single document to the readme?

Have gone through past issues and didn't find any related to this. Thanks again.

Observable from await get(query) not functioning properly

So here is my code:

getRooms().then((res) => {
    console.log(res);
}).catch((err) => {
    console.log(err);
});

async function getRooms() {
    const query = collection.within(point, 1, 'location')
    const rooms = await get(query)
    console.log(rooms);
}

For some reason I keep getting an error on the Observable subscriber saying this._next is not a function and the top of the stack is Observable.Subscriber, next is listener, pointing to Query.js.

Any idea why this is happening? I have tried this way with a promise and also the other way with a subscribe and I get the same error.

My first thought was that there were no results or the collection was empty but I can add things just fine and they're pointed to the same place. Also, the geo-hashes match perfectly. I have tried bigger and smaller radiuses (radii?) and can't get around it.

Would appreciate some help!

Possibility to use inside cloud functions

I really like the library and how clear and smooth it is. Thank you for your work.
Now I have a use case where I need to use this library inside Google Cloud Functions. The init function of the library expects FirebaseApp instance. Is it possible to initialise it inside cloud functions in order to query firestore database using firebase-admin?

Error: Argument "onNext" is not a valid function.

I am running the following code that then gives me the errors below. Seems like it is happening in rxjs for some reason. It seems to be happening on the await get(query) call.

async function getOffices() {
const center = geofire.point(40.1, -119.1);
const radius = 100;
const field = 'geofirex';

const query = geofire.collection('clients/Mondelez/datasets/default/offices').within(center, radius, field);
const offices = await get(query)
}

Error: Argument "onNext" is not a valid function.
at Validator.(anonymous function).values [as isFunction] (C:\Users\ljcremer\Google Drive\Development\TroopTravel\v2019.1\cloud-functions\functions\node_modules\firebase-admin\node_modules@google-cloud\firestore\build\src\validate.js:99:27)
at Query.onSnapshot (C:\Users\ljcremer\Google Drive\Development\TroopTravel\v2019.1\cloud-functions\functions\node_modules\firebase-admin\node_modules@google-cloud\firestore\build\src\reference.js:1524:25)
at Observable._subscribe (C:\Users\ljcremer\Google Drive\Development\TroopTravel\v2019.1\cloud-functions\functions\node_modules\geofirex\dist\index.cjs.js:1597:33)
at Observable._trySubscribe (C:\Users\ljcremer\Google Drive\Development\TroopTravel\v2019.1\cloud-functions\functions\node_modules\rxjs\internal\Observable.js:44:25)
at Observable.subscribe (C:\Users\ljcremer\Google Drive\Development\TroopTravel\v2019.1\cloud-functions\functions\node_modules\rxjs\internal\Observable.js:30:22)
at MapOperator.call (C:\Users\ljcremer\Google Drive\Development\TroopTravel\v2019.1\cloud-functions\functions\node_modules\rxjs\internal\operators\map.js:32:23)
at Observable.subscribe (C:\Users\ljcremer\Google Drive\Development\TroopTravel\v2019.1\cloud-functions\functions\node_modules\rxjs\internal\Observable.js:25:22)
at C:\Users\ljcremer\Google Drive\Development\TroopTravel\v2019.1\cloud-functions\functions\node_modules\rxjs\internal\util\subscribeTo.js:22:31
at Object.subscribeToResult (C:\Users\ljcremer\Google Drive\Development\TroopTravel\v2019.1\cloud-functions\functions\node_modules\rxjs\internal\util\subscribeToResult.js:10:45)
at CombineLatestSubscriber._complete (C:\Users\ljcremer\Google Drive\Development\TroopTravel\v2019.1\cloud-functions\functions\node_modules\rxjs\internal\observable\combineLatest.js:76:46)
at CombineLatestSubscriber.Subscriber.complete (C:\Users\ljcremer\Google Drive\Development\TroopTravel\v2019.1\cloud-functions\functions\node_modules\rxjs\internal\Subscriber.js:79:18)
at Observable._subscribe (C:\Users\ljcremer\Google Drive\Development\TroopTravel\v2019.1\cloud-functions\functions\node_modules\rxjs\internal\util\subscribeToArray.js:8:20)
at Observable._trySubscribe (C:\Users\ljcremer\Google Drive\Development\TroopTravel\v2019.1\cloud-functions\functions\node_modules\rxjs\internal\Observable.js:44:25)
at Observable.subscribe (C:\Users\ljcremer\Google Drive\Development\TroopTravel\v2019.1\cloud-functions\functions\node_modules\rxjs\internal\Observable.js:30:22)
at CombineLatestOperator.call (C:\Users\ljcremer\Google Drive\Development\TroopTravel\v2019.1\cloud-functions\functions\node_modules\rxjs\internal\observable\combineLatest.js:46:23)
at Observable.subscribe (C:\Users\ljcremer\Google Drive\Development\TroopTravel\v2019.1\cloud-functions\functions\node_modules\rxjs\internal\Observable.js:25:22)
at MapOperator.call (C:\Users\ljcremer\Google Drive\Development\TroopTravel\v2019.1\cloud-functions\functions\node_modules\rxjs\internal\operators\map.js:32:23)
at Observable.subscribe (C:\Users\ljcremer\Google Drive\Development\TroopTravel\v2019.1\cloud-functions\functions\node_modules\rxjs\internal\Observable.js:25:22)
at TakeSubscriber.shareReplayOperation (C:\Users\ljcremer\Google Drive\Development\TroopTravel\v2019.1\cloud-functions\functions\node_modules\rxjs\internal\operators\shareReplay.js:21:35)
at Observable.subscribe (C:\Users\ljcremer\Google Drive\Development\TroopTravel\v2019.1\cloud-functions\functions\node_modules\rxjs\internal\Observable.js:25:22)
at TakeOperator.call (C:\Users\ljcremer\Google Drive\Development\TroopTravel\v2019.1\cloud-functions\functions\node_modules\rxjs\internal\operators\take.js:38:23)
at Observable.subscribe (C:\Users\ljcremer\Google Drive\Development\TroopTravel\v2019.1\cloud-functions\functions\node_modules\rxjs\internal\Observable.js:25:22)

Cannot read property apply of undefined

ERROR Error: Uncaught (in promise): TypeError: Cannot read property 'apply' of undefined
TypeError: Cannot read property 'apply' of undefined
at GeoFireCollectionRef.within (index.esm.js:1549)

this refers to the line within the GeoQueries

GeoFireCollectionRef.prototype.within =
.......
var combo = combineLatest.apply(void 0, queries).pipe(map(function (arr)

My component code;

    const lat = data['position'].geopoint.latitude;
    const lng = data['position'].geopoint.longitude;
   const center = this.geo.point(lat,lng);
   const radius = 2;
    const field = 'position';
   const locRef = this.geo.collection('locations');

my position data
{position: {…}}position:
geohash: "u3bvhj5pc"
geopoint: GeoPoint {_lat: 55.7514828, _long: 12.4846695}
proto: Object

the constant center
image

Performance takes a major hit (presumably when you hit a geohash border)

I've been troubleshooting a nasty performance issue that seemed to occur randomly. That is, until I narrowed it down. Take the following example:

Searching for properties near Fontana, California.
If I search within a 4km radius, I get 408 results in about 2 seconds.
If I search within a 5km radius, I get 555 results in about 40 seconds.

Somewhere between 4km and 5km I'm hitting an invisible wall which makes the query 20x slower.

Here's the relevant code (I had profiling code in there to show the exact performance but I removed it for simplicity:

this.propertiesCollection = this.geo.collection('properties', ref =>
    ref.where('subtype', '==', 'Residential')
);
const center = this.geo.point(34.1087656, -117.5328097); // Fontana, California
const radius = 4;
this.propertiesCollection.within(center, radius, 'position').subscribe((results) => {
    this.properties = results;
});

Orderby Query with Inequality Fails with GeoHash query

FirebaseError: Invalid query. You have a where filter with an inequality (<, <=, >, or >=) on field 'date_time' and so you must also use 'date_time' as your first Query.orderBy(), but your first Query.orderBy() is on field 'position.geohash' instead.

How I built my query

const stores = geo.collection('events', ref =>
        ref.orderBy('date_time')
        .where('date_time', '>=', currentTimeStamp)
        .where('privacy', '==', 'public')
    );

const query = stores.within(center, radius, field);
    let publicEvents = await geofirex.get(query)

Cannot find module '@firebase/firestore-types'.

I am facing this issue in my Ionic project
[15:18:56] typescript: ../node_modules/geofirex/dist/interfaces.d.ts, line: 1
Cannot find module '@firebase/firestore-types'.

"firebase": "^5.3.1",
"geofirex": "0.0.6",
"ionic-angular": "3.9.2",
"rxjs": "^6.2.1",
"rxjs-compat": "^6.2.1",

Query using the map visible area

It would be great to be able to query using the Map Visible Area or Bounds instead of radius only.
With the visible area it's easy to get all 4 LAT/LNG for all the corners and it would be amazing to query that.

Thanks

The query requires an index

Hello, i have an issue...
When i do this query on geofirex
const questionsGeofire = geo.collection('questions', ref =>
ref.where('hidden', '==', false)
);
i have this error:
whatsapp image 2019-01-13 at 00 31 35

On the documentation i red that i have to create an index for this type of query, but when i enter on the link attached on the error screen :"https://console.firebase.google.com/u/2/project/closetome-9bf7a/database/firestore/indexes?create_index=EglxdWVzdGlvbnMaCgoGaGlkZGVuEAlaFAoQcG9zaXRpb24uZ2VvaGFzaBACGgwKCF9fbmFtZV9fEAI" firebase get bugged.. and i can't create the index...
captura de pantalla de 2019-01-13 18-22-28

do you know some about this?

I create manually indexes but they dont work, and i dont know that i do by the correct way...
Thank you

Huge amount of reads for `within`. Is this normal?

Issue

Worried about ongoing costs 😟

Observation

12k READ queries over a few hours

Expected

Maybe an order of magnitude fewer.

Fixes

Either reset expectation and prepare to pay or fix crazy code?


Context

I've developed a graphql API to list 27k Projects stored in Firestore. I'm using geoFirex to find Projects within a certain radius.

As part of development, I ran this function 30 times or so:

export const getProjectsNearPoint = async (
  point: Location,
  searchRadiusKm: number = 10,
  queryFn: QueryFn = ref => ref.limit(3) // Bug: This is not working
) => {
  const center = geo.point(point.latitude, point.longitude);

  const field = "location";

  const query = geo
    .collection(COLLECTION, queryFn)
    .within(center, searchRadiusKm, field);

  const nearbyProjects = await get(query);

  const result = nearbyProjects.map(
    ({ queryMetadata, ...project }: GeoQueryDocument) => ({
      distance: queryMetadata.distance,
      ...project
    })
  );

  return result;
};

Screenshot 2019-09-10 at 23 39 04


Questions

  • Is this normal?
  • Would an index help?
  • does anyone suspect my graphql resolvers are doing anything necessarily recursive?

Thanks.

Error calling 'point.data'

I am following your video about Firestore Geoqueries and when I try to add a point to my collection I get the following error when trying to call 'point.data'. I am using ionic 4

Exception: TypeError: this.app.firestore.GeoPoint is not a constructor at GeoFirePoint.get [as geoPoint] (http://localhost:4200/home-home-module.js:1405:20) at GeoFirePoint.get [as data] (http://localhost:4200/home-home-module.js:1426:32) at GeoFirePoint.remoteFunction (:2:14)]

As a workaround I am doing collection.add({ name: 'test', position: { geohash: p.hash, geopoint: new firebase.firestore.GeoPoint(p.latitude, p.longitude) } });

feat: support rectangular box queries

It would be great if geofirex could search for items within a rectangular area described by a minimum and maximum latitude and longitude.

An example use case of this would be when a user is displayed a map where they can pan/zoom, and you want to search for items that fit on the map. The Google Maps api allows you to query the bounds of the current map view as a LatLngBounds object, and it would be great if we could pass those bounds to geofirex for our search.

Currently, you could search in the map area would be to get the distance from the center to the closest top or side edge, compute the radius and then search using that radius, but that will miss any items in the corners or the map. Alternately, you could search on the radius from the center of the map to one of the corners, but then you would return results that do not appear on the map so then you'd need to do additional client-side filtering. It would be much more efficient and convenient to be able to just search on a rectangular area in the first place.

await get(query) not working in Firebase Functions

I am using geofirex in Firebase Cloud Functions using the following code:

const functions = require('firebase-functions');
 const firebase = require('firebase-admin');
firebase.initializeApp(functions.config.firebase);
const geo = require('geofirex').init(firebase);
var { get } = require('geofirex')
const firestore = firebase.firestore();
const settings = {
  timestampsInSnapshots: true
}
firestore.settings(settings)
const hostels = firestore.collection('hostels');
exports.helloWorld = functions.https.onRequest((request, response) => {
  const position = geo.point(40, -119);
  hostels.add({ name: 'Phoenix', position });
  response.send("Hello from Firebase!");
});
exports.search = functions.https.onRequest((request,response) => {
  // const center = geo.point(request.body.location[0],request.body.location[1]);
  const center = geo.point(40,-119);
  const radius = 1;
  const field  = 'position';
  getRooms(center,radius,field).then((res) => {
      console.log(res);
  }).catch((err) => {
      console.log("err"+err);
  });
  response.send("Atleast this much");
})
async function getRooms(center,radius,field) {
    const query = geo.query(hostels).within(center,radius,field);
    const snap = await get(query);
    console.log(query);
}

When executing, the code to save the data into firestore works perfectly and creates the hash and all, but the value of query when searching is returned as

Observable {
  _isScalar: false,
  source: 
   Observable {
     _isScalar: false,
     source: Observable { _isScalar: false, source: [Object], operator: [Object] },
     operator: [Function: shareReplayOperation] },
  operator: FinallyOperator { callback: [Function] } }

res has value undefined.

Does anyone know how to fix this?

feat: add generic typing

Right now the type is GeoQueryDocument[] but it would be nice to be able to supply a type like in the angularfire2 sdk. E.g.

const items = this.geo.collection<Rainbow>('rainbows')
const query = items.within(center, radius, field)

This would give the type GeoQueryDocument<Rainbow>[]

Returning DocumentReference will be great

...(id ? { [id]: v.id } : null),

@codediodeio, It would be really great if the result document returns ref(DocumentReference) instead of id

function snapToData(id = 'id') {
  return map((querySnapshot: fb.firestore.QuerySnapshot) =>
    querySnapshot.docs.map(v => {
      return {
       ref: v.ref, //lets return document.ref  ...(id ? { [id]: v.id } : null), 
        ...v.data()
      };
    })
  );
}

Thanks

TypeError: app.firestore is not a function at new GeoFireCollectionRef

GeoFireCollectionRef.setDoc causes SSR cloud function to fail. It works perfectly fine when being called from the client.

Error message from Firebase console:
TypeError: app.firestore is not a function
at new GeoFireCollectionRef (/srv/dist/server.js:303314:24)
at GeoFireClient.collection (/srv/dist/server.js:303488:16)
at new ListingService (/srv/dist/server.js:161140:39)
at ListingService_Factory (/srv/dist/server.js:161248:157)
at _callFactory (/srv/dist/server.js:26514:20)
at createProviderInstance (/srv/dist/server.js:26472:26)
at resolveNgModuleDep (/srv/dist/server.js:26447:17)
at NgModuleRef
.get (/srv/dist/server.js:27141:16)
at injectInjectorOnly (/srv/dist/server.js:8502:33)
at Module.ɵɵinject (/srv/dist/server.js:8507:57)

Why do we need to init?

I can't use this lib because of the init function. Init is global (on the index's file) and some large projects don't let you change this.

// Init GeoFireX
import * as geofirex from 'geofirex';
const geo = geofirex.init(firebase);

Why do we need to init?

feat: get key of the document

In firestore you can do

const carsRefs = await firestore.collection('cars').get()
for (let carRef of carsRefs.docs) {
  carRef.data() // document data
  carRef.id // document key
}

I can't find document key in response:

const cars = geo.collection('cars')
const field = 'position'
const center = geo.point(48.1548894, 11.471625)
const radius = 50

const query = cars.within(center, radius, field)
let docs = await get(query)

for (let doc of docs) {
  doc // document data
  // document key?
}

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.