Stroi
Stroi

Reputation: 1961

Getting all documents from one collection in Firestore

Hi I'm starting with javascript and react-native and I'm trying to figure out this problem for hours now. Can someone explain me how to get all the documents from firestore collection ?

I have been trying this:

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

Log 1 prints all the objects(one by one) but log 2 is undefined, why ?

Upvotes: 152

Views: 288953

Answers (14)

Sanjay Bharwani
Sanjay Bharwani

Reputation: 4819

Below code pulls the data from Firebase collection named messages

Future<void> getMessages() async {
    try {
      await for (var snapshot
          in _fireStore.collection("messages").snapshots()) {
        for (var doc in snapshot.docs) {
          print("Messages :: ${doc.data()}");
        }
      }
    } catch (e) {
      print(e);
    }
  }

Where the _firstore is created in the state class of your stateful widget

class _ChatScreenState extends State<ChatScreen> {
  final _auth = FirebaseAuth.instance;
  final _fireStore = FirebaseFirestore.instance;

It prints output at console

I/flutter (25195): Messages :: {sender: [email protected], text: Hello}
I/flutter (25195): Messages :: {sender: autobot, text: this is from firestore edited}

Dependencies in pubspec.yaml:

  firebase_core: ^2.25.4
  firebase_auth: ^4.17.4
  cloud_firestore: ^4.15.5

Dart version

dart --version   
Dart SDK version: 3.2.6 (stable)

Flutter Version

flutter --version
Flutter 3.16.9 • channel stable •

Upvotes: 0

Shreyash.K
Shreyash.K

Reputation: 1148

In version 9 sdk of firebase you can get all the documents from a collection using following query:

const querySnapshot = await getDocs(collection(db, "cities"));
querySnapshot.docs.forEach((doc) => {
  console.log(doc.id, " => ", doc.data());
});

See Get multiple documents from a collection

Upvotes: 2

OneHoopyFrood
OneHoopyFrood

Reputation: 3969

You could get the whole collection as an object, rather than array like this:

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

That would give you a better representation of what's in firestore. Nothing wrong with an array, just another option.

Upvotes: 7

Rodrigo
Rodrigo

Reputation: 382

if you need to include the key of the document in the response, another alternative is:

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

Upvotes: 11

SwiftiSwift
SwiftiSwift

Reputation: 8767

General example to get products from Cloud Firestore:

  Future<void> getAllProducts() async {
    CollectionReference productsRef =
        FirebaseFirestore.instance.collection('products');
    final snapshot = await productsRef.get();
    List<Map<String, dynamic>> map =
        snapshot.docs.map((doc) => doc.data() as Map<String, dynamic>).toList();
  }

Upvotes: 0

danday74
danday74

Reputation: 57215

The docs state:

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

However I am using the following (excuse the 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())

where db has the type Firestore

Upvotes: 10

Navid Shad
Navid Shad

Reputation: 1016

All answers are true, but when you have heavy data you will face memory and bandwidth problems, so you have to write a [cursor] function to read data part by part.

also, you may face to Bandwidth Exhausted error, please have a look at this solution I have implemented on a gist https://gist.github.com/navidshad/973e9c594a63838d1ebb8f2c2495cf87

Otherwise, you can use this cursor I written to read a collection doc by doc:

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

Upvotes: -1

Sayanta Bhattacharjee
Sayanta Bhattacharjee

Reputation: 17

I understand your query, This is because how Javascript handles promises and variables. So basically events variable is hoisted with the value undefined and printed on the LOG 2 console log, while the Event Loop responsible for the promise call resulted in an array of objects as the value of the events variable and then the console log (LOG 1) was printed with the resolved promise response

Upvotes: -2

Robert Lin
Robert Lin

Reputation: 434

Two years late but I just began reading the Firestore documentation recently cover to cover for fun and found withConverter which I saw wasn't posted in any of the above answers. Thus:

If you want to include ids and also use withConverter (Firestore's version of ORMs, like ActiveRecord for Ruby on Rails, Entity Framework for .NET, etc), then this might be useful for you:

Somewhere in your project, you probably have your Event model properly defined. For example, something like:

Your model (in 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)
  }
}

And then your client-side TypeScript code:

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

  ...
}

Two interesting quirks of Firestore to notice (or at least, I thought were interesting):

  1. Your event.id is actually stored "one-level-up" in snapshot.id and not snapshot.data().

  2. If you're using TypeScript, the TS linter (or whatever it's called) sadly isn't smart enough to understand:

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

even though right above it you explicitly stated: .withConverter(eventConverter)

Which is why it needs to be doc: any.

(But! You will actually get Array<Event> back! (Not Array<Map> back.) That's the entire point of withConverter... That way if you have any object methods (not shown here in this example), you can immediately use them.)

It makes sense to me but I guess I've gotten so greedy/spoiled that I just kinda expect my VS Code, ESLint, and the TS Watcher to literally do everything for me. 😇 Oh well.


Formal docs (about withConverter and more) here: https://firebase.google.com/docs/firestore/query-data/get-data#custom_objects

Upvotes: 3

Josh
Josh

Reputation: 133

Here's a simple version of the top answer, but going into an object with the document ids:

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

Upvotes: 0

IMANULLAH
IMANULLAH

Reputation: 579

if you want include 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)
    })
}

Same way with array

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

Upvotes: 37

Nowdeen
Nowdeen

Reputation: 1450

I prefer to hide all code complexity in my services... so, I generally use something like this:

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

In my sth.page.ts

   myList: Event[];

   construct(private service: EventsService){}

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

Enjoy :)

Upvotes: 2

Doug Stevenson
Doug Stevenson

Reputation: 317808

The example in the other answer is unnecessarily complex. This would be more straightforward, if all you want to do is return the raw data objects for each document in a query or collection:

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

Upvotes: 288

Stroi
Stroi

Reputation: 1961

I made it work this way:

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

Upvotes: 13

Related Questions