从嵌套在 Firestore 文档中的集合中获取数据的最佳方法是什么? [英] What's the best way to get data from collections nested in documents in Firestore?

查看:23
本文介绍了从嵌套在 Firestore 文档中的集合中获取数据的最佳方法是什么?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在阅读 Firestore 文档和指南.我下面的代码示例使用 AngularFire2.

让我们考虑一个类似于此处提供的示例的聊天"集合:https://firebase.google.com/docs/firestore/manage-data/structure-data

他们推荐这种结构,但我看不出他们在哪里讨论有效获取所有数据.

每个聊天文档都有属性、成员集合和消息集合:

  • chatsCollection
    • chatDocument
      • [在此处插入聊天数据字段]
      • membersCollection
        • memberDocument
          • [在此处插入成员数据字段]
      • messagesCollection
        • messageDocument
          • [在此处插入消息数据字段]

Firestore 查询很浅,有时可能很棒.我的理解是没有成熟的方式来查询深度和获取嵌套集合.那么,执行此操作的最佳做​​法和最轻松的方法是什么?

目前,我正在检索快照并将其映射到具有 ID 的对象,并将嵌套的集合数据添加到具有附加查询和映射的父文档数据中,我对我的方法不满意,甚至可以做得更快使用非规范化的 Firebase 结构.

这个代码示例只是映射成员,添加回消息是另一回事......

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

来自服务:

getChatsFromFirestore() {return this.fsd.collection('chats');}getChatFromFirestoreById(id: string) {return this.fsd.doc(`chats/${id}`);}getMembersForChat(chatId) {返回 this.getChatFromFirestoreById(chatId).collection('members');}

解决方案

您发布的方法似乎可行,对于大型聊天应用程序,您可能不希望跟踪每个聊天室中发生的每个事件,因为这可能是大量的数据.相反,最好只订阅需要的内容并使用云功能和云消息处理定期更新.

通过使用辅助函数 observeCollection 以及小代码重组,它将清理服务并为每个聊天室创建可观察对象,这些聊天室在订阅之前将处于非活动状态.

class 服务 {//db is plan firestore/no angularfire数据库:firebase.firestore.Firestore;加载聊天室(){const chatsRef = this.db.collection('chats');返回observeCollection(chatsRef).管道(地图(聊天 => {返回 chats.map(chat => {返回 {聊天,会员$: this.observeCollection(chat.ref.collection('members')),消息$: this.observeCollection(chat.ref.collection('messages')),};})}),);}//获取一个引用并返回一个文档数组//带有 id 和引用私人观察集合(参考){返回 Observable.create((观察者) => {const unsubscribeFn = ref.onSnapshot(快照 =>{观察者.下一个(快照.docs.map(doc => {常量数据 = doc.data();返回 {....doc.data(),id: doc.id,参考: doc.ref};}));},错误 =>观察者.错误(错误),);返回 unsubscribeFn;});}}

在应用程序中,您只能观察当前选择的聊天室成员和消息,这将节省数据.由于这篇文章是用 Angular 异步管道标记的,这将有助于通过自动手动订阅进行切换.

在您的组件中:

this.currentChat$ = combineLatest(service.loadChatrooms(),当前选定的房间 ID).管道(地图(([聊天,selectedRoomId])=> {返回 chats.first(chat => chat.id === selectedRoomId)}));

在您的模板中:

{{ currentChat.name }}<div *ngIf="currentChat.members$ 作为会员"><div *ngIf="let member of members">{{ 成员名字 }}

<div *ngIf="currentChat.messages$ 作为消息"><div *ngIf="让消息的消息">{{ 消息内容 }}

I'm going through the Firestore docs and guide. My code samples below use AngularFire2.

Let's consider a "chats" collection similar to the examples provided here: https://firebase.google.com/docs/firestore/manage-data/structure-data

They recommend this kind of structure but I don't see where they discuss getting all the data efficiently.

Each chat document has properties, a collection of members, and a collection of messages:

Firestore queries are shallow, which could be great sometimes. My understanding is that there's no baked-in way to query deep and get nested collections. So, what are the best practices and most hassle-free ways to do this?

At the moment I'm retrieving and mapping snapshots to objects with IDs and adding the nested collection data onto the parent document data with additional queries and mappings, and I'm not happy with my approach, and could do it quicker even with denormalized Firebase structures.

This code example is just mapping on the members, adding back in messages is a whole other story...

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));
  }

And from the service:

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

解决方案

The approach you posted seems like it would work and for a large chat application you probably do not want to track every single event that happens in every chatroom as that could be a lot of data. Instead it would probably be better to subscribe to only what is needed and handle periodic updates with cloud functions and cloud messaging.

By using a helper function observeCollection as well as small code restructuring it would cleanup the service and create observables for each chatroom that would be inactive until they are subscribed to.

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;
        });
    }
}

In the application you could then only observe the currently selected chatrooms members and messages, which would save data. Since this post is tagged with Angular async pipes would help with switching by automatically handly subscriptisons.

In your component:

this.currentChat$ = combineLatest(
  service.loadChatrooms(),
  currentlySelectedRoomId
).pipe(
    map(([chats, selectedRoomId]) => {
        return chats.first(chat => chat.id === selectedRoomId)
    })
);

In your template:

<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>

这篇关于从嵌套在 Firestore 文档中的集合中获取数据的最佳方法是什么?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
其他开发最新文章
热门教程
热门工具
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆