Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Are there any benefits of using subcollections in firestore?

I have a subcollection for each doc in the users collection of my app. This subcollection stores docs that are related to the user, however they could just as well be saved to a master collection, each doc with an associated userId.

I chose this structure as it seemed the most obvious at the time but I can imagine it will make things harder down the road if I need to do database maintenance. E.g. If I wanted to clean up those docs, I would have to query each user and then each users docs, whereas if I had a master collection I could just query all docs.

That lead me to question what is the point of subcollections at all, if you can just associate those docs with an ID. Is it solely there so that you can expand if your doc becomes close to the 1MB limit?

like image 787
H Johnson Avatar asked Jan 19 '19 10:01

H Johnson


People also ask

What is sub collection firestore?

A subcollection is a collection associated with a specific document. Note: You can query across subcollections with the same collection ID by using Collection Group Queries. You can create a subcollection called messages for every room document in your rooms collection: collections_bookmark rooms.

What is the use of reference in firestore?

< T > A DocumentReference refers to a document location in a Firestore database and can be used to write, read, or listen to the location. The document at the referenced location may or may not exist. A DocumentReference can also be used to create a CollectionReference to a subcollection.

What is firestore onSnapshot?

You can listen to a document with the onSnapshot() method. An initial call using the callback you provide creates a document snapshot immediately with the current contents of the single document. Then, each time the contents change, another call updates the document snapshot.

Is firestore key value store?

In Firestore, you typically have a single collection and multiple documents in that collection. To use Firestore as a key/value store for Workflows, one idea is to use the workflow name as the collection name and use a single document to store all the key/value pairs.


2 Answers

Edit: October, 29th 2021:

To be clear about the following sentence that exists in the docs:

If you don't query based on the field with sequential values.

A timestamp just can not be considered consecutive. However, it still can be considered sequential. The same rules apply to alphabetical (Customer1, Customer2, Customer3, ...), or pretty much everything that can be treated as a predictably generated value.

Such sequential data in the Firestore indexes, it's most likely to be written in the physical proximity on the storage media, hence that limitation.

That being said, please note that Firestore uses a mechanism to map the documents to their corresponding locations. This means that if the values are not randomly distributed, the write operations will not be distributed correctly over the locations. That's the reason why that limitation exists.

Also note, that there is a physical limit on how much data you can write to such a location in a specific amount of time. Predictable key/values most likely will end up in the same location, which is actually bad. So there are more changes to reach the limitation.


Edit: July, 16th 2021:

Since this answer sounds a little old, I will try to add a few more advantages of using subcollections that I found over time:

  1. Subcollections will always give you a more structured database schema, as you can always refer to a subcollection that is related only to a specific document. So you can nest only data that is related to a particular document.
  2. As mention before, the maximum depth of a subcollection is 100. So an important feature here is that a Firestore Query is as fast at level 1, as it is at level 100. So there should be no concerns regarding depth. This feature is tested.
  3. Queries in subcollections are indexed by default, as in the case of top-level collections.
  4. In terms of speed, it doesn't really matter if you Query a top-level collection, a subcollection, or a collection group, the speed will always be the same, as long as the Query returns the same number of documents. This is happening because the Query performance depends on the number of documents you request and not on the number of documents you search. So querying a subcollection has the same effect as querying a top-level collection, no downsides at all.
  5. When storing documents in a subcollection, please note that there is no need to storing the document ID as a field, as it is by default part of the reference. This means that you can store less data in the documents that exist in the subcollection. More important, if you would have saved the same data in a top-level collection, and you would have needed to create a Query with two whereEqualTo() calls + an orderBy() call, then an index would be required.
  6. In terms of security, subcollections allow inheritance of security rules, which is useful because we can write less and less code to secure the database.

That's for the moment, if I found other benefits, I'll update the answer.


Let's take an example for that. Let's assume we have a database schema for a quiz app that looks like this:

Firestore-root     |     --- questions (collections)           |           --- questionId (document)                  |                  --- questionId: "LongQuestionIdOne"                  |                  --- title: "Question Title"                  |                  --- tags (collections)                       |                       --- tagIdOne (document)                       |     |                       |     --- tagId: "yR8iLzdBdylFkSzg1k4K"                       |     |                       |     --- tagName: "History"                       |     |                       |     --- //Other tag properties                       |                       --- tagIdTwo (document)                             |                             --- tagId: "tUjKPoq2dylFkSzg9cFg"                             |                             --- tagName: "Geography"                             |                             --- //Other tag properties 

In which tags is a subcollection within questionId object. Let's create now the tags collection as a top-level collection like this:

Firestore-root     |     --- questions (collections)     |     |     |     --- questionId (document)     |            |     |            --- questionId: "LongQuestionIdOne"     |            |     |            --- title: "Question Title"     |     --- tags (collections)           |           --- tagIdOne (document)           |     |           |     --- tagId: "yR8iLzdBdylFkSzg1k4K"           |     |           |     --- tagName: "History"           |     |           |     --- questionId: "LongQuestionIdOne"           |     |           |     --- //Other tag properties           |           --- tagIdTwo (document)                 |                 --- tagId: "tUjKPoq2dylFkSzg9cFg"                 |                 --- tagName: "Geography"                 |                 --- questionId: "LongQuestionIdTwo"                 |                 --- //Other tag properties 

The differences between this two approaches are:

  • If you want to query the database to get all tags of a particular question, using the first schema it's very easy because only a CollectionReference is needed (questions -> questionId -> tags). To achieve the same thing using the second schema, instead of a CollectionReference, a Query is needed, which means that you need to query the entire tags collection to get only the tags that correspond to a single question.
  • Using the first schema everything is more organised. Beside that, in Firestore Maximum depth of subcollections: 100. So you can take advantage of that.
  • As also @RenaudTarnec mentioned in his comment, queries in Cloud Firestore are shallow, they only get documents from the collection that the query is run against. There is no way to get documents from a top-level collection and other collections or subcollections in a single query. Firestore doesn't support queries across different collections in one go. A single query may only use properties of documents in a single collection. So there is no way you can get all the tags of all the questions using the first schema.

This technique is called database flatten and is a quite common practice when it comes to Firebase. So use this technique only if is needed. So in your case, if you only need to display the tags of a single question, use the first schema. If you want somehow to display all the tags of all questions, the second schema is recommended.

Is it solely there so that you can expand if your doc becomes close to the 1MB limit?

If you have a subcollection of objects within a document, please note that size of the subcollection it does not count in that 1 MiB limit. Only the data that is stored in the properties of the document is counted.

Edit Oct 01 2019:

According to @ShahoodulHassan comment:

So there is no way you can get all the tags of all the questions using the first schema?

Actually now there is, we can get all tags of all questions with the use of Firestore collection group query. One thing to note is that all the subcolletions must have the same name, for instance tags.

like image 178
Alex Mamo Avatar answered Sep 18 '22 15:09

Alex Mamo


The single biggest advantage of sub-collections that I've found is that they have their own rate limit for writes because each sub-collection has its own index (assuming you don't have a collection group index). This probably isn't a concern for small applications but for medium/large scale apps it could be very important.

Imagine a chat application where each chat has a series of messages. You'll want to index messages by timestamp to show them in chronological order. The Firestore write limit for sequential values is 500/second, which is definitely within reach of a medium-sized app (especially if you consider the possibility of a rogue user scripting messages -- which is not currently easy to prevent with Security Rules)

// root collection  /messages {   chatId: string   timeSent: timestamp // the entire app would be limited to 500/second } 
// sub-collection  /chat/{chatId}/messages {   timeSent: timestamp // each chat could safely write up to 500/second } 
like image 37
geg Avatar answered Sep 19 '22 15:09

geg