Je travaille avec Observables ici et le wrapper AngularFire, mais voici comment j'ai réussi à le faire.
C'est un peu fou, j'apprends toujours les observables et j'en ai peut-être trop fait. Mais c'était un bel exercice.
Quelques explications (pas un expert RxJS):
- songId $ est une observable qui émettra des identifiants
- dance $ est une observable qui lit cet id et n'obtient ensuite que la première valeur.
- il interroge ensuite le collectionGroup de toutes les chansons pour en trouver toutes les instances.
- En fonction des instances, il traverse les danses parentes et récupère leurs identifiants.
- Maintenant que nous avons tous les identifiants de danse, nous devons les interroger pour obtenir leurs données. Mais je voulais que cela fonctionne bien, donc au lieu de les interroger un par un, je les répartis dans des seaux de 10 (l'angulaire maximum prendra pour une
in
requête.
- Nous nous retrouvons avec N buckets et devons faire N requêtes sur Firestore pour obtenir leurs valeurs.
- une fois que nous avons effectué les requêtes sur Firestore, nous devons encore analyser les données à partir de cela.
- et enfin, nous pouvons fusionner tous les résultats de la requête pour obtenir un seul tableau contenant toutes les danses.
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();
J'ai copié-collé mon code et changé les noms de variables pour les vôtres, je ne sais pas s'il y a des erreurs, mais cela a bien fonctionné pour moi. Faites-moi savoir si vous trouvez des erreurs ou si vous pouvez suggérer une meilleure façon de le tester avec peut-être une fausse cheminée.