0

I'm working on an app where a contractor can say that they are "available" on specific days and each contractor has a "location". An employer can search for availability based off of location and availability.

The location is based off of GeoFire. This returns the $key of the contractors that are available.

That looks like this:

geoQueryContractor(radius, lat, lng) {

    const subject = new Subject();

    this.fbGeoRef = firebase.database().ref('geofire')

    this.geoFire = new GeoFire(this.fbGeoRef);

    this.geoFire.ref();

    this.geoQuery = this.geoFire.query({
        center: [lat, lng],
        radius: radius
    });

    this.geoQuery.on("key_entered", function(key, location, distance) {
        subject.next(key);
    });

    return subject.asObservable();

}

Next, I can get the availablity by searching the firebase node that looks like this "/AvailForContractor/${timestamp}/$uid:true"

This is how that works and it returns their profile:

getAvailablitybyContractor(timestamp) {

    const availContractorKeys$ = this.db.list(`/AvailForContractor/${timestamp}`);

    const AvailContractors$ = availContractorKeys$

        //maping each key
        .map(keysPerContractor => keysPerContractor

        //once we have each key, we can map it and create an fb object observable
        .map(keyPerContractor => this.db.object(`/users/${keyPerContractor.$key}`)))

        //now we got back an array of firebase object observables (fbojs) and we need to combine them in to one observable
        .mergeMap(fbojs => Observable.combineLatest(fbojs))
        .do(console.log)

    AvailContractors$.subscribe();
}

I have these 2 working independently of each other. I really need to know out of all the $key's returned in the 2nd function, which ones are available by location in the 1st function. I only need to return the profiles where those 2 criteria are met.

I've been messing around with CombineLatest, mergeMap, withLatestFrom and Filter but I can't figure out how to do this the proper way.

My thought is that once I get the keys from the 2nd function, combine it with the GeoFire observable and filter unique keys, then do this part:

    //once we have each key, we can map it and create an fb object observable
    .map(keyPerContractor => this.db.object(`/users/${keyPerContractor.$key}`)))

    //now we got back an array of firebase object observables (fbojs) and we need to combine them in to one observable
    .mergeMap(fbojs => Observable.combineLatest(fbojs))
    .do(console.log)

This is not complete but a poor attempt...

getAvailablitybyContractor(timestamp, radius, lat, lng) {

    const availContractorKeys$ = this.db.list(`/AvailForContractor/${timestamp}`);

    //when we get back the keys, we are going to switch to another obeservables
    const AvailContractors$ = availContractorKeys$

        //maping each key
        .map(keysPerContractor => keysPerContractor
            .map(keyPerContractor => keyPerContractor.$key))
            .combineLatest(this.geoQueryContractor(radius, lat, lng))

            //  .withLatestFrom(this.geoQueryContractor(radius, lat, lng), (keysPerContractor, geo) => ( [keysPerContractor, geo] ))
            //once we have each key, we can map it and create an fb object observable

        //     .map(keyPerContractor => this.db.object(`/users/${keyPerContractor.$key}`)))
        // //now we got back an array of firebase object observables (fbojs) and we need to combine them in to one observable
        // .mergeMap(fbojs => Observable.combineLatest(fbojs))

        .do(console.log)

    AvailContractors$.subscribe();
}

GeoFire kicks out individual keys like this by the way:

3vAWWHaxHRZ94tc8yY08CH3QNQy3, H74INXgYWIMrUcAtZloFGkwJ6Qd2, etc.

Firebase will kick out an array of keys:

[3vAWWHaxHRZ94tc8yY08CH3QNQy3, H74INXgYWIMrUcAtZloFGkwJ6Qd2, J9DHhg5VQrMpNyAN8ElCWyMWh8i2, fdZYKqqiL0bSVF66zGjBhQVu9Hf1  ]

End result would be the unique combination of those to in an RX way which I would use to get the profiles.

Can anyone help? Thanks!

AL.
  • 36,815
  • 10
  • 142
  • 281
dhndeveloper
  • 311
  • 2
  • 15

1 Answers1

0

This is my solution. I'm sure there is a better way to do this. UPDATE: Better solution below.

static geoArray: Array<string> = [];

constructor(private af: AngularFire, private db: AngularFireDatabase) {

}

getAvailablitybyContractor(timestamp, radius, lat, lng) {

    const availContractorKeys$ = this.db.list(`/AvailForContractor/${timestamp}`);

    const AvailContractors$ = availContractorKeys$

        //maping each key
        .map(keysPerContractor => keysPerContractor.map(keyPerContractor => keyPerContractor.$key)
        .filter(key => ContractorService.geoArray.indexOf(key) > -1)

         //once we have each key, we can map it and create an fb object observable
        .map(keyPerContractor => this.db.object(`/users/${keyPerContractor}`)))

        //now we got back an array of firebase object observables (fbojs) and we need to combine them in to one observable
        .mergeMap(fbojs => Observable.combineLatest(fbojs))
        .do(console.log)

    AvailContractors$.subscribe();
}

geoQueryContractor(radius, lat, lng) {


    this.fbGeoRef = firebase.database().ref('geofire')

    this.geoFire = new GeoFire(this.fbGeoRef);

    this.geoFire.ref();

    this.geoQuery = this.geoFire.query({
        center: [lat, lng],
        radius: radius
    });

    this.geoQuery.on("key_entered", function(key, location, distance) {

        ContractorService.geoArray.push(key);

    });

}


}

This solution is far better. The one above was really buggy. It had to do with clearing the array. At the end of the day, I would have to execute the search 2x to get the right results. Not acceptable.

Here is a better approach, its more reactive and it doesn't have the bugs from above. I'm sure there is a better way to refractor this or improve up on it.

getAvailablitybyContractor(timestamp) {

    let availContractorKeys$ = this.db.list(`/AvailForContractor/${timestamp}`);

    this.AvailContractors$ = availContractorKeys$

        //maping each key
        .map(keysPerContractor => keysPerContractor.map(keyPerContractor => keyPerContractor.$key))
        //Combine observable from GeoQuery
        .combineLatest(this.keys$, (fb, geo) => ([fb, geo]))
        // fb, geo are accessible individually
        // .filter method creates a new array with all elements that pass the test implemented by the provided function
        // key is now iteriable through geo.indexOf
        .map(([fb, geo]) => {
            return fb.filter(key => geo.indexOf(key) > -1)
        })
        .map(filteredKeys => filteredKeys.map(keyPerContractor => this.db.object(`/users/${keyPerContractor}`)))
        //now we got back an array of firebase object observables (fbojs) and we need to combine them in to one observable
        .mergeMap(fbojs => {
            return Observable.combineLatest(fbojs)
        })
        .do(console.log)

}

getGeoQuery(radius, lat, lng) {

    this.geoQuery = this.geoFire.query({
        center: [lat, lng],
        radius: radius
    });

}

geoQueryContractor() {

    return this.keys$ = Observable.create(observer => {

        var keys = new Array();

        this.geoQuery.on("key_entered", (key, location, distance) => {
            keys.push(key);
            observer.next(keys);
        });

    });

}
dhndeveloper
  • 311
  • 2
  • 15