Комбинированный и наблюдаемый фильтр для уникальных ключей к QueryFirebase
Я работаю над приложением, в котором подрядчик может сказать, что они "доступны" в определенные дни, и у каждого подрядчика есть "местоположение". Работодатель может искать наличие на основе местоположения и доступности.
Расположение основано на GeoFire. Это возвращает ключ $ доступных подрядчиков.
Это выглядит так:
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();
}
Затем я могу получить доступность, выполнив поиск по узлу firebase, который выглядит следующим образом: "/AvailableForContractor/${timestamp}/$uid:true"
Вот как это работает, и это возвращает их профиль:
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();
}
У меня эти 2 работают независимо друг от друга. Мне действительно нужно знать из всех $ key, возвращаемых во 2-й функции, какие из них доступны по местоположению в 1-й функции. Мне нужно только вернуть профили, где эти два критерия выполнены.
Я возился с CombineLatest, mergeMap,LatestFrom и Filter, но я не могу понять, как сделать это правильно.
Я думаю, что как только я получу ключи от 2-й функции, объединю их с наблюдаемой GeoFire и отфильтрую уникальные ключи, а затем сделаю эту часть:
//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)
Это не полная, но неудачная попытка...
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 выбивает отдельные ключи, например, так:
3vAWWHaxHRZ94tc8yY08CH3QNQy3
, H74INXgYWIMrUcAtZloFGkwJ6Qd2
, так далее.
Firebase выведет массив ключей:
[3vAWWHaxHRZ94tc8yY08CH3QNQy3, H74INXgYWIMrUcAtZloFGkwJ6Qd2, J9DHhg5VQrMpNyAN8ElCWyMWh8i2, fdZYKqqiL0bSVF66zGjBhQVu9Hf1 ]
Конечным результатом была бы уникальная комбинация тех, кто в RX-способе, который я использовал бы для получения профилей.
Кто-нибудь может помочь? Спасибо!
1 ответ
Это моё решение. Я уверен, что есть лучший способ сделать это. ОБНОВЛЕНИЕ: лучшее решение ниже.
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);
});
}
}
Это решение намного лучше. Тот, что был выше, был действительно глючным. Это было связано с очисткой массива. В конце дня мне нужно будет выполнить поиск 2 раза, чтобы получить правильные результаты. Неприемлимо.
Вот лучший подход, он более реактивный, и в нем нет ошибок сверху. Я уверен, что есть лучший способ изменить это или улучшить его.
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);
});
});
}