9

私はFirestoreのドキュメントとガイドを調べています。私のコードサンプルは、AngularFire2を使用しています。彼らは、このような構造をお勧めしますが、それらは効率的にすべてのデータを取得話し合うところ、私は表示されませんhttps://firebase.google.com/docs/firestore/manage-data/structure-dataFirestoreのドキュメントにネストされたコレクションからデータを取得する最良の方法は何ですか?

はのは、ここに提供した例と同様の「チャット」コレクションを考えてみましょう。 [ここに挿入チャットデータフィールド]

  • chatsCollection
    • chatDocument
      • 各チャット文書は、プロパティ、メンバーの収集、およびメッセージのコレクションを持っています

      • membersCollection
        • memberDocument
          • [ここで会員データフィールドを挿入]
      • messagesCollection
        • messageDocument
          • [INSERTメッセージデータフィールドここ]時には大きな可能性が

    Firestoreクエリが浅い

、。私の理解では、深く照会してネストされたコレクションを取得するための焼き付け方法はありません。だから、ベストプラクティスとこれを行う最も面倒な方法は何ですか?

現時点では、スナップショットを取得してIDを持つオブジェクトにマッピングし、ネストされたコレクションデータを親のドキュメントデータに追加のクエリとマッピングで追加しています。私のアプローチには満足できず、たとえ非正規化されたFirebase構造であっても。

メッセージに戻って追加の例は、単にメンバーにマッピングしている。このコードは、全体他の物語である...

getChatsFromFirestore() { 
    this.chats = this.dataSvc.getChatsFromFirestore().snapshotChanges() 
     .map(chatSnaps => { 
     return chatSnaps.map(chat => { 
      const chatData = chat.payload.doc.data(); 
      const chatId = chat.payload.doc.id; 
      return this.dataSvc.getMembersForChat(chatId) 
      .snapshotChanges() 
      .map(memberSnaps => { 
       return memberSnaps.map(member => { 
       const data = member.payload.doc.data(); 
       const id = member.payload.doc.id; 
       return { id, ...data } 
       }); 
      }) 
      .map(members => { 
       return { chatId, ...chatData, members: members }; 
      }); 
     }) 
     }) 
     .flatMap(chats => Observable.combineLatest(chats)); 
    } 

とサービスから

getChatsFromFirestore() { 
    return this.fsd.collection<any>('chats'); 
} 
getChatFromFirestoreById(id: string) { 
    return this.fsd.doc(`chats/${id}`); 
} 
getMembersForChat(chatId) { 
    return this.getChatFromFirestoreById(chatId).collection('members'); 
} 
+1

私はリアルタイムデータベース(のようなもの[この回答のためのプロセスごととしてデータを複製&非正規化のためにここに引数はありだと思います](https://stackoverflow.com/a/30699277/2754146))、Firestoreでデータを結合するための別の/より良いプロセスがあることを期待しています。 – Grimthorr

+1

良い点と私はこのケースでは、データを捨ててデノーミングしました。たとえば、各チャットには、そのメンバーのすべてのデータを取得するために使用できるUIDと一致するドキュメントIDがあり、ユーザーの名前も含まれています。個々のメッセージには、そのユーザーのUIDを保持している "authorName"フィールドと "sentBy"フィールドも含まれています... DBへの再帰呼び出しや少なくとも冗長性のないチャットルーム文書の下にあるすべてのネストされたデータコードはクールだ。 – Methodician

+0

'chatDocument'をIDとしてフィールドに保存すると、' .valueChanges() 'を使ってIDを取得できます。これは '.snapshotChanges()'より高速です。 – Hareesh

答えて

0

は、あなたが投稿したアプローチはそうそれがうまくいくように、大きなチャットアプリケーションの場合は、チャットルームごとに発生するすべてのイベントをトラッキングしたくないかもしれません。代わりに、必要なものだけを購読し、クラウド機能とクラウドメッセージングを使用して定期的な更新を処理する方がよいでしょう。

ヘルパー機能observeCollectionを使用することで、小さなコードの再構築だけで、サービスがクリーンアップされ、各チャットルームに対して、購読されるまで非アクティブになるオブザーバブルが作成されます。

class Service { 
    // db is plan firestore/no angularfire 
    db: firebase.firestore.Firestore; 

    loadChatrooms() { 
     const chatsRef = this.db.collection('chats'); 
     return observeCollection(chatsRef) 
      .pipe(
       map(chats => { 
        return chats.map(chat => { 
         return { 
          chat, 
          members$: this.observeCollection(chat.ref.collection('members')), 
          messages$: this.observeCollection(chat.ref.collection('messages')), 
         }; 
        }) 
       }), 
      ); 
    } 

    // Takes a reference and returns an array of documents 
    // with the id and reference 
    private observeCollection(ref) { 
     return Observable.create((observer) => { 
      const unsubscribeFn = ref.onSnapshot(
       snapshot => { 
        observer.next(snapshot.docs.map(doc => { 
         const data = doc.data(); 
         return { 
          ...doc.data(), 
          id: doc.id, 
          ref: doc.ref 
         }; 
        })); 
       }, 
       error => observer.error(error), 
      ); 

      return unsubscribeFn; 
     }); 
    } 
} 

このアプリケーションでは、現在選択されているチャットルームのメンバーとメッセージだけを観察することができ、データを保存できます。 この投稿にはAngular asyncタグが付いているので、自動的に手書きの添字で切り替えるのに役立ちます。あなたのコンポーネントで

テンプレートで
this.currentChat$ = combineLatest(
    service.loadChatrooms(), 
    currentlySelectedRoomId 
).pipe(
    map(([chats, selectedRoomId]) => { 
     return chats.first(chat => chat.id === selectedRoomId) 
    }) 
); 

<div *ngIf="currentChat$ as currentChat"> 
    {{ currentChat.name }} 

    <div *ngIf="currentChat.members$ as members"> 
     <div *ngIf="let member of members"> 
      {{ member.name }} 
     </div> 
    </div> 

    <div *ngIf="currentChat.messages$ as messages"> 
     <div *ngIf="let message of messages"> 
      {{ message.content }} 
     </div> 
    </div> 
</div> 
+0

答えが正しい方向に向いているかどうか、さらに議論してください。 – adamduren

関連する問題