Я працюю з Observables тут і обгорткою AngularFire, але ось як мені це вдалося.
Це щось божевільне, я все ще дізнаюся про спостереження, і я, можливо, переоцінив це. Але це була приємна вправа.
Деякі пояснення (не експерт RxJS):
- songId $ - це спостереження, яке випромінює ідентифікатори
- dance $ - це спостережуване, яке читає цей ідентифікатор, а потім отримує лише перше значення.
- Потім він запитує collectionGroup всіх пісень, щоб знайти всі його примірники.
- Виходячи з примірників, він переходить до батьківських танців і отримує їхні ідентифікатори.
- Тепер, коли у нас є всі танці танцю, нам потрібно запитати їх, щоб отримати їх дані. Але я хотів, щоб це було добре, тому замість того, щоб запитувати по черзі, я збираю їх у відра з 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();
Я скопіювавши вставлений код і змінив імена змінних на ваш, не впевнений, чи є помилки, але він добре працював для мене. Повідомте мене, якщо ви знайдете якісь помилки чи можете запропонувати кращий спосіб перевірити це, можливо, з якоюсь макетною стрілкою.