Я работаю с Observables здесь и AngularFire оболочкой но вот как мне это удалось.
Это какое-то безумие, я все еще изучаю наблюдаемые и, возможно, перестарался. Но это было хорошее упражнение.
Некоторое объяснение (не эксперт RxJS):
- songId $ - это наблюдаемое, которое будет выдавать идентификаторы
- dance $ - это наблюдаемое, которое считывает этот идентификатор и затем получает только первое значение.
- Затем он запрашивает collectionGroup всех песен, чтобы найти все ее экземпляры.
- На основе экземпляров он переходит к родительским Dances и получает их идентификаторы.
- Теперь, когда у нас есть все идентификаторы Dance, нам нужно запросить их, чтобы получить их данные. Но я хотел, чтобы он работал хорошо, поэтому вместо того, чтобы запрашивать один за другим, я группирую их в группы по 10 (максимальное угловое значение займет
in
запроса.
- У нас получается N корзин, и нам нужно выполнить N запросов в firestore, чтобы получить их значения.
- после того, как мы выполним запросы в firestore, нам все равно нужно будет анализировать данные из него.
- и, наконец, мы можем объединить все результаты запроса, чтобы получить единый массив со всеми танцами в нем.
type Song = {id: string, name: string};
type Dance = {id: string, name: string, songs: Song[]};
const songId$: Observable<Song> = new Observable();
const dance$ = songId$.pipe(
take(1), // Only take 1 song name
switchMap( v =>
// Query across collectionGroup to get all instances.
this.db.collectionGroup('songs', ref =>
ref.where('id', '==', v.id)).get()
),
switchMap( v => {
// map the Song to the parent Dance, return the Dance ids
const obs: string[] = [];
v.docs.forEach(docRef => {
// We invoke parent twice to go from doc->collection->doc
obs.push(docRef.ref.parent.parent.id);
});
// Because we return an array here this one emit becomes N
return obs;
}),
// Firebase IN support up to 10 values so we partition the data to query the Dances
bufferCount(10),
mergeMap( v => { // query every partition in parallel
return this.db.collection('dances', ref => {
return ref.where( firebase.firestore.FieldPath.documentId(), 'in', v);
}).get();
}),
switchMap( v => {
// Almost there now just need to extract the data from the QuerySnapshots
const obs: Dance[] = [];
v.docs.forEach(docRef => {
obs.push({
...docRef.data(),
id: docRef.id
} as Dance);
});
return of(obs);
}),
// And finally we reduce the docs fetched into a single array.
reduce((acc, value) => acc.concat(value), []),
);
const parentDances = await dance$.toPromise();
Я скопировал, вставил свой код и изменил имена переменных на ваши, не уверен, есть ли какие-либо ошибки, но у меня это сработало. Дайте мне знать, если вы обнаружите какие-либо ошибки, или можете предложить лучший способ проверить это, возможно, с помощью какого-нибудь имитационного хранилища огня.