0

I'd like to check if a collection (the users collection) exists in the Firestore database or not. But I cannot find any means of doing that. If the collection exists, I would like to stream its documents, otherwise an empty stream as you see in the following method

- Is there any way to find a collection exists without getting its snapshots?
- Why break; or yield* Stream.empty() hangs the stream, like an endless stream!

Stream<userInfo> getCurrentUserInfos() async* {
  final String usersCollectionPath = "users";
  Stream<QuerySnapshot> snapshots = Firestore.instance.collection(usersCollectionPath).snapshots();
  snapshots.isEmpty.then((hasNoUserCollection) {
    // Although I don't have 'users' collection
    // in my database but I never reach this point!
    // i.e. hasNoUserCollection is always FALSE! 
    if(hasNoUserCollection) {
      print("users collection doesn't exist, create it!");
      // next line (i.e. `break;`) hangs the tool!
      // And sometimes hangs the VSCode's IDE as well, if I have a breakpoint on it!!!
      break;
      // I tried yielding an empty stream instead of break, again another hang!
      // yield* Stream<userInfo>.empty();
    } else {
      // previous stream is dead, so create it again!
      snapshots = Firestore.instance.collection(usersCollectionPath ).snapshots();
      await for (QuerySnapshot snap in snapshots) {
      for (DocumentSnapshot userDoc in snap.documents) {
        yield (new userInfo.fromQuerySnapshot(userDoc));
      }
    }
  });
}

Now even a try-catch block cannot catch what's gone wrong, when the stream is empty!

try{
  getCurrentUserInfos().last.then((userInfolastOne) {
    print("last one: $lastOne.name");
  });

 // the following line (i.e. await ...) at least doesn't hang and
 // `catch` block is able to catch the error `Bad state: No element`,
 // when the stream is empty
 //
 // userInfo lastOne = await stream.last;
} catch (ex) {
  print ("ex: $ex");
}
CloudWindMoonSun
  • 372
  • 1
  • 4
  • 13
  • Answer for Q1 below. Please post a separate question for Q2. See https://meta.stackexchange.com/questions/39223/one-post-with-multiple-questions-or-multiple-posts – Frank van Puffelen Jan 05 '19 at 23:04
  • Thanks for the answer. Although I split the question into two parts that's actually one question so I remove the repeated area. – CloudWindMoonSun Jan 05 '19 at 23:46

4 Answers4

2

There is no API to detect if a collection exists. In fact: a collection in Firestore only exists if there are documents in it.

The cheapest check I can think of is doing a query for a single document, and then checking if that query has any results.

Frank van Puffelen
  • 565,676
  • 79
  • 828
  • 807
  • Can you please show an example for this query check? I tried something like this, but it didn't print anything void checkMessage() async { final messages = await _firestore.collection(fsCollection).getDocuments(); for(var message in messages.documents){ if(message.data.length == 0){ print('no data'); }else{ print('has data'); } } } I tried other methods too but didn't get any results – VipiN Negi Nov 08 '19 at 07:58
  • Please open a new question, and include the code that doesn't work, and what it printed ("didn't get any results" is not something you seem to print anywhere). – Frank van Puffelen Nov 08 '19 at 14:53
  • @FrankvanPuffelen It's mean, there no way to check collection exists or not.right? – BIS Tech Dec 06 '19 at 00:51
  • "The cheapest check I can think of is doing a query for a single document, and then checking [if] that query has any results." – Frank van Puffelen Dec 06 '19 at 00:56
1

Okay, maybe I figured it out

final snapshot = await Firestore.instance.collection(collectionName).getDocuments();
if (snapshot.documents.length == 0) {
   //Doesn't exist
}

This worked for me

Piotr Labunski
  • 1,638
  • 4
  • 19
  • 26
0

As stated by @Frank, a collection in Firestore gets deleted if no Documents exist in it.

However, I understand that there might be cases where you want to keep a history of the collection modification/ creation events, or let's say for some reason prevent Collections from being deleted.

Or for example, you want to know when a collection was created in the first place. Normally, if the Documents are deleted, and then the Collection gets created again, you will not know the initial creation date.

A workaround I can think of is the following:

Initialize each collection you want with a Document that will be specifically for keeping generic info about that collection.

For example: enter image description here

This way, even if all other Documents in the Collection are deleted, you'll still keep the Collection in addition to some info that might be handy if In the future you need to get some history info about the Collection.

So to know if a Collection exists of no, you can run a query that checks for a field in Info Documents (eg CollectionInfo.exists) to know which Collections have been already created.

Waelmas
  • 1,894
  • 1
  • 9
  • 19
  • Keep in mind that this will have an extra cost every extra Read/Write operation required to achieve the above. – Waelmas Dec 06 '19 at 10:55
0

This is a sample from one of my projects. You can use snapshot.data!.docs.isEmpty to check if a collection has data or not.

StreamBuilder(
        stream: _billGroupStream,
        builder: (BuildContext context, AsyncSnapshot<QuerySnapshot> snapshot) {
          if (snapshot.hasError) {
            return const Center(
              child: Text('Something went wrong'),
            );
          } else if (snapshot.connectionState == ConnectionState.waiting) {
            return Center(
              child: Column(
                children: const [
                  LinearProgressIndicator(),
                  Text('Loading data, please wait...'),
                ],
              ),
            );
          } else if (!snapshot.hasData) {
            return const Center(child: CircularProgressIndicator());
          } else if (snapshot.data!.docs.isEmpty) {
            return const Center(
              child: Text(
                'Huh!! Looks like you have no transactions yet!' ,
                textAlign: TextAlign.center,
                style: TextStyle(fontWeight: FontWeight.bold, fontSize: 18),
              ),
            );
          } else if (snapshot.connectionState == ConnectionState.active) {
            final List<DocumentSnapshot> docs = snapshot.data!.docs;
            return ListView.builder(
              shrinkWrap: true,
              restorationId: 'billModeList',
              itemCount: snapshot.data!.docs.length,
              itemBuilder: (context, index) {
                ///This document snapshot is used for determining the unique id for update and delete methods
                final DocumentSnapshot doc = docs[index];
                //final DocumentSnapshot doc = snapshot.data!.docs[index];

                ///This [BillModel] converted data is used to build widgets
                final BillModel billModel = doc.data()! as BillModel;
                return Dismissible(
                  onDismissed: (direction) {
                    _remoteStorageService.deleteItemFromGroup(
                        widget.uri, doc.id);
                    setState(() {
                      docs.removeAt(index);
                    });
                  },
                  background: Container(
                    color: Colors.red,
                    child: const Icon(Icons.delete_forever_sharp),
                  ),
                  key: Key(doc.id),
                  child: Card(
                    elevation: 3,
                    shadowColor: Colors.teal.withOpacity(.5),
                    child: ListTile(
                      leading:
                          const CircleAvatar(child: Icon(Icons.attach_money)),
                      title: Text(billModel.name),
                      subtitle: Text(billModel.category ?? ''),
                      trailing: Text(billModel.amount.toString()),
                    ),
                  ),
                );
              },
            );
          }
          return const CircularProgressIndicator();
        },
      ),
Vivek
  • 437
  • 5
  • 12