Estoy trabajando con Observables aquí y el contenedor AngularFire, pero así es como logré hacerlo.
Es un poco loco, todavía estoy aprendiendo sobre los observables y posiblemente exagere. Pero fue un buen ejercicio.
Alguna explicación (no un experto en RxJS):
- songId $ es un observable que emitirá identificadores
- dance $ es un observable que lee ese id y luego obtiene solo el primer valor.
- luego consulta el grupo de colección de todas las canciones para encontrar todas sus instancias.
- Según las instancias, atraviesa las Danzas principales y obtiene sus identificadores.
- Ahora que tenemos todos los ID de Dance, necesitamos consultarlos para obtener sus datos. Pero quería que funcionara bien, así que en lugar de consultar uno por uno, los agrupa en grupos de 10 (el ángulo máximo que se tomará para una
in
consulta.
- Terminamos con N cubos y necesitamos hacer N consultas en firestore para obtener sus valores.
- una vez que hacemos las consultas en firestore, todavía necesitamos analizar los datos de eso.
- y finalmente podemos fusionar todos los resultados de la consulta para obtener una única matriz con todos los Dances en ella.
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();
Copié, pegué mi código y cambié los nombres de las variables a los suyos, no estoy seguro de si hay algún error, pero funcionó bien para mí. Avíseme si encuentra algún error o si puede sugerir una mejor manera de probarlo, tal vez con algún firestore simulado.