从 Firestore 的一个集合中获取所有文档

Posted

技术标签:

【中文标题】从 Firestore 的一个集合中获取所有文档【英文标题】:Getting all documents from one collection in Firestore 【发布时间】:2019-02-05 13:55:33 【问题描述】:

您好,我从 javascript 和 react-native 开始,我花了几个小时试图解决这个问题。有人可以解释一下如何从 firestore 集合中获取所有文件吗?

我一直在尝试这个:

async getMarkers() 
  const events = await firebase.firestore().collection('events').get()
    .then(querySnapshot => 
      querySnapshot.docs.map(doc => 
        console.log('LOG 1', doc.data());
        return doc.data();
      );
    );
  console.log('LOG 2', events);
  return events;

日志 1 打印所有对象(一个一个),但日志 2 未定义,为什么?

【问题讨论】:

【参考方案1】:

另一个答案中的示例过于复杂。如果您只想为查询或集合中的每个文档返回原始数据对象,这将更直接:

async getMarker() 
    const snapshot = await firebase.firestore().collection('events').get()
    return snapshot.docs.map(doc => doc.data());

【讨论】:

这对我有用const snapshot = await firestore.collection('events').get() @etoxin 仅当您的导入/要求与 OP 所做的不同时才会起作用。 这将计入一次读取配额,还是将集合中的每个文档都算作一次读取操作? @Mentor 该查询将读取每个匹配的文档,无论您如何处理代码中的快照。 @bermick 一次查询是一次往返。查询的全部结果会在快照中返回。【参考方案2】:

如果你想包含 ID

async getMarkers() 
  const events = await firebase.firestore().collection('events')
  events.get().then((querySnapshot) => 
      const tempDoc = querySnapshot.docs.map((doc) => 
        return  id: doc.id, ...doc.data() 
      )
      console.log(tempDoc)
    )

数组也一样

async getMarkers() 
  const events = await firebase.firestore().collection('events')
  events.get().then((querySnapshot) => 
      const tempDoc = []
      querySnapshot.forEach((doc) => 
         tempDoc.push( id: doc.id, ...doc.data() )
      )
      console.log(tempDoc)
   )
 

【讨论】:

【参考方案3】:

我是这样设计的:

async getMarkers() 
  const markers = [];
  await firebase.firestore().collection('events').get()
    .then(querySnapshot => 
      querySnapshot.docs.forEach(doc => 
      markers.push(doc.data());
    );
  );
  return markers;

【讨论】:

【参考方案4】:

如果您需要在响应中包含文档的键,另一种选择是:

async getMarker() 
    const snapshot = await firebase.firestore().collection('events').get()
    const documents = [];
    snapshot.forEach(doc => 
       documents[doc.id] = doc.data();
    );
    return documents;

【讨论】:

【参考方案5】:

您可以将整个集合作为一个对象,而不是像这样的数组:

async getMarker() 
    const snapshot = await firebase.firestore().collection('events').get()
    const collection = ;
    snapshot.forEach(doc => 
        collection[doc.id] = doc.data();
    );
    return collection;

这会让您更好地了解 Firestore 中的内容。数组没有错,只是另一种选择。

【讨论】:

【参考方案6】:

我更喜欢在我的服务中隐藏所有代码复杂性......所以,我通常使用这样的东西:

在我的 events.service.ts 中

    async getEvents() 
        const snapchot = await this.db.collection('events').ref.get();
        return new Promise <Event[]> (resolve => 
            const v = snapchot.docs.map(x => 
                const obj = x.data();
                obj.id = x.id;
                return obj as Event;
            );
            resolve(v);
        );
    

在我的 sth.page.ts

   myList: Event[];

   construct(private service: EventsService)

   async ngOnInit() 
      this.myList = await this.service.getEvents();
   

享受:)

【讨论】:

【参考方案7】:

晚了两年,但我最近才开始阅读 Firestore 文档以获取乐趣,发现withConverter 没有在上述任何答案中发布。因此:

如果您想包含 id 并且也使用withConverter(Firestore 的 ORM 版本,例如用于 Ruby on Rails 的 ActiveRecord、用于 .NET 的实体框架等),那么这可能对你:

在您的项目中,您可能已经正确定义了 Event 模型。例如,类似:

您的模型(TypeScript): ./models/Event.js

export class Event 
  constructor (
    public id: string,
    public title: string,
    public datetime: Date
  )


export const eventConverter = 
  toFirestore: function (event: Event) 
    return 
      // id: event.id,  // Note! Not in ".data()" of the model!
      title: event.title,
      datetime: event.datetime
    
  ,
  fromFirestore: function (snapshot: any, options: any) 
    const data = snapshot.data(options)
    const id = snapshot.id
    return new Event(id, data.title, data.datetime)
  

然后是你的客户端TypeScript 代码:

import  eventConverter  from './models/Event.js'

...

async function loadEvents () 
  const qs = await firebase.firestore().collection('events')
    .orderBy('datetime').limit(3)  // Remember to limit return sizes!
    .withConverter(eventConverter).get()

  const events = qs.docs.map((doc: any) => doc.data())

  ...


Firestore 的两个有趣的怪癖需要注意(或者至少,我认为很有趣):

    您的event.id 实际上是“上一级”存储在snapshot.id不是 snapshot.data()

    如果您使用的是 TypeScript,遗憾的是,TS linter(或其他任何名称)不够聪明,无法理解:

const events = qs.docs.map((doc: Event) =&gt; doc.data())

即使您在上面明确指出: .withConverter(eventConverter)

这就是为什么它需要doc: any

(但是!你会实际上得到Array&lt;Event&gt;回来!(不是@987654337 @ 返回。)这就是withConverter 的全部意义...这样,如果您有任何对象方法(本示例中未显示),您可以立即使用它们。)

这对我来说是有道理的,但我想我已经变得如此贪婪/被宠坏了,以至于我只是有点期望我的 VS 代码、ESLint 和 TS Watcher 真正为我做一切。 ? 哦,好吧。


正式文档(关于 withConverter 等)在这里:https://firebase.google.com/docs/firestore/query-data/get-data#custom_objects

【讨论】:

【参考方案8】:

这是最佳答案的简单版本,但进入一个带有文档 ID 的对象:

async getMarker() 
    const snapshot = await firebase.firestore().collection('events').get()
    return snapshot.docs.reduce(function (acc, doc, i) 
              acc[doc.id] = doc.data();
              return acc;
            , );

【讨论】:

【参考方案9】:

docs 状态:

import  collection, getDocs  from "firebase/firestore";

const querySnapshot = await getDocs(collection(db, "cities"));
querySnapshot.forEach((doc) => 
  // doc.data() is never undefined for query doc snapshots
  console.log(doc.id, " => ", doc.data());
);

但是我正在使用以下内容(请原谅 TypeScript):

import  collection, Firestore, getDocs, Query, QueryDocumentSnapshot, QuerySnapshot  from 'firebase/firestore'

const q: Query<any> = collection(db, 'videos')
const querySnapshot: QuerySnapshot<IVideoProcessed> = await getDocs(q)
const docs: QueryDocumentSnapshot<IVideoProcessed>[] = querySnapshot.docs
const videos: IVideoProcessed[] = docs.map((doc: QueryDocumentSnapshot<IVideoProcessed>) => doc.data())

其中 db 的类型为 Firestore

【讨论】:

【参考方案10】:

我了解您的查询,这是因为 Javascript 如何处理承诺和变量。所以基本上 events 变量被提升为 undefined 值并打印在 LOG 2 控制台日志上,而负责 promise 调用的事件循环产生一个对象数组作为 events 变量的值,然后是控制台日志 (LOG 1)打印了已解决的承诺响应

【讨论】:

【参考方案11】:

所有答案都是正确的,但是当您有大量数据时,您将面临内存和带宽问题,因此您必须编写一个 [cursor] 函数来部分读取数据。

另外,您可能会遇到带宽耗尽错误,请查看我在 gist 上实施的这个解决方案 https://gist.github.com/navidshad/973e9c594a63838d1ebb8f2c2495cf87

否则,你可以使用我写的这个游标来逐个阅读集合文档:

async function runCursor(
    collection,
    orderBy,
    limit = 1000,
    onDoc,
    onDone,
) 
    let lastDoc;
    let allowGoAhead = true;

    const getDocs = () => 
        let query = admin.firestore().collection(collection).orderBy(orderBy).limit(limit)
        // Start from last part
        if (lastDoc) query = query.startAfter(lastDoc)

        return query.get().then(sp => 
            if (sp.docs.length > 0) 
                for (let i = 0; i < sp.docs.length; i++) 
                    const doc = sp.docs[i];
                    if (onDoc) onDoc(doc);
                
                // define end of this part
                lastDoc = sp.docs[sp.docs.length - 1]
                // continue the cursor
                allowGoAhead = true
             else 
                // stop cursor if there is not more docs
                allowGoAhead = false;
            
        ).catch(error => 
            console.log(error);
        )
    

    // Read part by part
    while (allowGoAhead) 
        await getDocs();
    

    onDone();

【讨论】:

以上是关于从 Firestore 的一个集合中获取所有文档的主要内容,如果未能解决你的问题,请参考以下文章

从 id 数组中获取 firestore 文档列表

从嵌套在Firestore中的文档中的集合中获取数据的最佳方法是什么?

我可以获取从 Firestore 集合组查询中获取的文档的路径或引用吗?

Firestore - 从集合中的文档中获取集合条目

从嵌套在 Firestore 文档中的集合中获取数据的最佳方法是啥?

引用了 Firestore 如何从另一个集合文档 id 中获取集合值