【Angular x Firestore】 필드의 값을 바탕으로 다른 문서를 합성해 취득한다

소개



이번에는 이미지와 같은 데이터베이스를 예로 RxJS를 사용하여 다른 컬렉션에 있는 사용자의 데이터를 합성하면서 기사의 데이터를 취득하는 예를 소개합니다.

구체적으로는, 「articles 컬렉션내의 authorId」와 「users 컬렉션내의 userId」가 일치하는 문서를 합성합니다.



샘플 코드



거친 흐름은 이렇게 되어 있습니다.
  • article 컬렉션 얻기
  • users 컬렉션 얻기
  • 문서 합성
  • getArticles(): Observable<ArticleWithUser[]>  {
        // 記事のデータを保管しておく空配列を用意
        let articles: Article[];
        return this.db.collection<Article>(`articles`).valueChanges().pipe(
          // 流れて来たarticlesコレクションをswitchMapでusersコレクションに差し替える
          switchMap((docs: Article[]) => {
            // 差し替えたらarticlesコレクションのデータは取得出来なくなってしまうので保管する
            articles = docs;
            if (articles.length) {
              // filterを使ってauthorIdの重複を無くす
              const authorIds: string[] = articles.filter((article, index, self) => {
                return self.findIndex(item => article.authorId === item.authorId) === index;
              }).map(article => article.authorId);
    
              // 重複なしのIDリストを使ってusersコレクション内のドキュメントを取得
              // ドキュメントが流れるObserbableが複数ある状態なので、combineLatestで1つにまとめる
              return combineLatest(authorIds.map(userId => {
                return this.db.doc<User>(`users/${userId}`).valueChanges();
              }));
            } else {
              // switchMapはObserbableを返す必要があるのでofを使う
              return of([]);
            }
          }),
          // 流れてきたuserのドキュメントと、保管しておいた記事のドキュメントを合成する
          map((users: User[]) => {
            return articles.map(article => {
              const result: ArticleWithUser = {
                ...article,
                author: users.find(user => user.id === article.authorId),
              };
              return result;
            });
          })
        );
      }
    

    이 코드로 등장하는 RxJS의 Operators를 간이 해설

    switchMap: Observable을 다른 Observable로 바꾸기

    combineLatest: 여러 Observable을 하나로 결합

    map: 흘러온 값에 뭔가 변경을 하고 되돌린다

    모르는 Operators는 이 사이트에서 조사하면 좋다
    htps //w w.ぇ rn rxjs. 이오 / 아 r-rxjs / 오페라와 rs

    switchMap 내에서 authorId의 중복을 filter로 없애는 이유



    쓸데없는 요청을 없애기 위해서입니다.
    중복된 상태라면, 취득한 기사 중 100건이 같은 유저의 기사였을 경우에, 99회도 쓸데없이 유저 데이터를 리퀘스트하게 되어 버립니다.

    여러 방법으로 사용



    Firestore에서는 이러한 방식으로 컬렉션을 좁히거나 정렬할 수 있습니다.
    return this.db.collection<Article>(`articles`, ref => {
          return ref.where('条件').orderBy('条件').limit('');
    }).valueChanges();
    

    실제로 기사를 취득하는 장면에서는 「좋아하는 숫자가 많은 순서로 취득」 「특정 유저의 기사만 취득」 등, 조건부로 취득하는 경우가 많다고 생각합니다.

    그러한 경우 「조건만이 달라, 내용은 거의 상기의 getArticles 메소드와 같다」라고 하는 메소드를 양산해 버리는 것은, DRY 원칙 로부터 해 좋은 코드라고는 할 수 없습니다.

    해결 방법



    개별의 메소드에서는 조건 지정만을 실시합시다.
    정렬 및 필터링이 완료된 컬렉션을 getArticles에 전달합니다.

    여기에서는 favorite의 수를 참조하여 내림차순으로 정렬한 후 6건만 취득하고 있습니다.
    getPopularArticles(): Observable<ArticleWithUser[]> {
        const sorted = this.db.collection<ArticleWithUser>(`articles`, ref => {
          return ref.orderBy('favorite', 'desc').limit(6);
        });
        return this.getArticles(sorted);
      }
    

    getArticles는 컬렉션을 받도록 변경해 봅시다.
    받은 Sorted를 사용하여 기사와 사용자 데이터를 합성하고 반환합니다.
    getArticles(sorted: AngularFirestoreCollection<Article>): Observable<ArticleWithUser[]> {
        let articles: Article[];
        return sorted.valueChanges().pipe(
          switchMap((docs: Article[]) => {
            articles = docs;
    // ...以下省略
    

    참고 사이트



    CAMP
    h tps : // 및. 카 mp / 아보 t

    Angular × Firebase를 이용한 SPA 개발을 가르치고 있는 온라인 스쿨입니다.
    일본어의 정보 자원이 적은 Angular의 해설 동영상이나 기사가 실려 있기 때문에, 편리합니다.

    좋은 웹페이지 즐겨찾기