angular - Kan jag både mappa till Observables, som pipas in i mergeMap -> forkJoin OCH skicka ett objekt till nedströms i RxJs?


Jag vet, det är en ganska komplicerad fråga, men jag är verkligen vilse i RxJs-dokumentationen ...

Jag har en Angular-applikation, och jag använder OpenLibrary API för att skapa en hemläxa. Jag har en tjänst, som heter SearchService, som använder /search-endpointen. Problemet är att denna endpoint returnerar dokument som inte är i önskat format, så för varje returnerat dokument måste jag fråga det faktiska objektet från en annan endpoint, en i taget, eftersom batchförfrågningar verkar omöjliga för tillfället (servern returnerar bara HTTP 500).

Min lösning, som för närvarande fungerar, är som följer:

  1. Fråga /search-endpointen med Angulars HttpClient, denna funktion returnerar en Observable<AuthorSearchResponse>
  2. Skicka detta till en RxJs map-operator, filtrera bort dåliga värden och mappa resultatdokumenten (var och en av dem) till en Observable<Author>, som produceras av en annan tjänst (this.authorService.getAuthorById(id) använder också HttpClient för att fråga och transformera en given författare efter ID)
  3. Skicka denna Observable<Author>[] till en mergeMap RxJs-operator, som använder forkJoin-operatorn. Detta kommer att producera den önskade Author[]-arrayen
  4. Pipa detta resultat till en map-operator, som omvandlar denna array till det önskade QueryResult<Author>-objektet, som innehåller en array av Author-objekt, samt det aktuella antalet returnerade objekt, och det bör innehålla det totala antalet sökresultat.

Så här ser min kod ut:

Gränssnittet AuthorSearchResponse

interface AuthorSearchResponse {
  start: number;
  numFound: number;
  docs: {
    type: string;
    key: string;
  }[];
}

Gränssnittet QueryResult<T>

export interface QueryResult<T> {
  totalCount: number;
  count: number;
  data: T[];
}

Min funktion

// This whole thing produces a QueryResult<Author> instance
return this.httpClient
      .get<AuthorSearchResponse>(`${environment.apiUrl}/search/authors.json`, {
        params: params,
      })
      .pipe(
        map((searchResponse) =>
          searchResponse.docs
            .filter(
              (doc) => doc.type === 'author' && typeof doc.key !== 'undefined'
            )
            .map((doc) =>
              // getAuthorById returns Observable<Author>
              this.authorService.getAuthorById(correctAuthorKey(doc.key))
            )
        )
      )
      .pipe(mergeMap((o) => forkJoin(o)))
      .pipe(
        map((authors) => ({
          data: authors,
          totalCount: -1,
          count: authors.length,
        }))
      );

Mitt problem är att jag för närvarande inte har något sätt att skicka searchResponse-objektet till den slutliga operatorn, vilket jag verkligen skulle behöva för att sätta totalCount-egenskapen i den slutliga mappningen. Finns det något sätt i RxJs att ta både searchResponse OCH Observable<Author>[]-arrayen som produceras i den första map-operatorn, och när forkJoin är klar (alla Authors har laddats och QueryResult kan produceras), använda searchResponse och de producerade Author[] nedströms för att kombinera dem till ett objekt?

Svar #1

Du kommer att behöva nästla ditt rör genom att pipa forkJoin och använda mergeMap innan map:

return this.httpClient.get<AuthorSearchResponse>(
  `${environment.apiUrl}/search/authors.json`,
  { params: params }
).pipe(
  mergeMap((searchReponse) => forkJoin(searchReponse.docs
    .filter((doc) => doc.type === 'author' && doc.key != null)
    .map((doc) => this.authorService.getAuthorById(correctAuthorKey(doc.key)))
  ).pipe(
    map((authors) => ({
      data: authors,
      totalCount: searchReponse.numFound,
      count: authors.length,
    }))
  ))
);