Pracuję z Observables tutaj i opakowaniem AngularFire, ale oto jak udało mi się to zrobić.
To trochę szalone, wciąż uczę się o obserwowalnych i prawdopodobnie przesadziłem. Ale to było fajne ćwiczenie.
Kilka wyjaśnień (nie ekspert RxJS):
- songId $ to obserwable, które będą emitować identyfikatory
- dance $ jest obserwowalnym, który odczytuje ten identyfikator, a następnie pobiera tylko pierwszą wartość.
- następnie wysyła zapytanie do collectionGroup wszystkich piosenek, aby znaleźć wszystkie jej wystąpienia.
- Opierając się na instancjach, które przechodzi do tańca rodzica i uzyskać ich identyfikatory.
- Teraz, gdy mamy już wszystkie identyfikatory Dance, potrzebujemy zapytać ich, aby uzyskać ich dane. Ale chciałem, żeby działał dobrze, więc zamiast odpytywania pojedynczo, umieszczam je w pojemnikach po 10 (maksymalny kąt będzie potrzebny dla
in
zapytania.
- Otrzymujemy N zasobników i musimy wykonać N zapytań w Firestore, aby uzyskać ich wartości.
- kiedy wykonamy zapytania w Firestore, nadal musimy faktycznie przeanalizować dane z tego.
- i wreszcie możemy połączyć wszystkie wyniki zapytania, aby uzyskać pojedynczą tablicę ze wszystkimi tańcami.
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();
Skopiowałem, wkleiłem mój kod i zmieniłem nazwy zmiennych na twoje, nie jestem pewien, czy są jakieś błędy, ale dla mnie działało dobrze. Daj mi znać, jeśli znajdziesz jakieś błędy lub możesz zasugerować lepszy sposób przetestowania go przy użyciu fałszywego Firestore.