Firestore document/subcollection is not existing - firebase

I have this db structure:
Firestore-root
|
--- sites (collection)
| |
| --- common (document)
| |
| --- //templates (collection)
| |
| --- //menus (collection)
| |
| --- //articles (collection) <----
| --- other (document)
| |
| --- //articles (collection)
When I try to add articles to the db (shown by the arrow) the "common" and the "other" - document is in italic and therefore doesn't exist.
My code when i try to add: priority is common, type is articles.
def documentReference = firestoreClient.databaseReference.collection(siteName).document(priority).collection(TYPE).document(key)
documentReference.set(article)
this is the console:
Is this a bad way to structure my db or is there a quick fix?
I have tried to create the sub collections first but without any luck!

The Firebase Console isn't indicating you that the "common" and the "other" documents are deleted. It is telling you that it just does not exist. Those documents do not exist because you didn't create them at all. What you did do, was only to create a subcollection under a document that never existed in the first place. With other words, it merely "reserves" an id for a document in that collection and then creates a subcollection under it. Typically, you should only create subcollections of documents that actually do exist but this is how it looks like when the document doesn't exist.
One thing to remember, in Cloud Firestore documents and subcollections don't work like filesystem files and directories. If you create a subcollection under a document, it doesn't implicitly create any parent documents. Subcollections are not tied in any way to a parent document.
Document ids shown in italics are not necessarily "deleted". They are shown that way because the document does not exist. With other words, there is no physical document at that location but there is other data under the location.
If you want to correct that, you have to write at least a property that can hold a value.
P.S. In Firestore, if you delete a document, its subcollections still exist.

Related

How I can copy One Firebase Collection to Another Firebase Collection in Android

As you can there are 3 collections in my Firestore database:
Plans
UPIs
Users
Upon Successful Signup of a user, I want to copy all the values present in Plans Collection to Users Collection under their mobile number as a document.
There is currently no straightforward solution for that. The single option that you have, is to move each and every document that exists in the "Plans" collection, to any other collection of your choice.
If you want the "Plans" to be a subcollection under the user's mobile number document in the "Users" collection, then you need to have a schema similar to this:
Firestore-root
|
--- Users (collection)
|
--- $phoneNumber (document) //👈
|
--- Plans (Sub-collection)
|
--- $planId
|
--- //fields
This operation can be done on the client, or using a trusted environment you control. The latter is obviously more recommended. So you might consider using Cloud Functions for Firebase, to trigger a function on user creation that does exactly that.

How can I model a many-to-many relationship in Firestore without exceeding the document size limit?

Below is my database schema that stores a many-to-many relationship between a task and tag model. Google state that the maximum size that a document can be stored on Firestore is 1 MiB. If I continuously add tags to a task the document size would exceed that size limit.
Firestore-root
|
--- tasks (collection)
| |
| --- taskID (document)
| |
| --- title: "Go for a cycle"
| |
| --- completed: false
| |
| --- userID: "zaEh95kXJKapyVUqrPws58dyRIC3"
| |
| --- tagIDs: ["rWqTxB01TK9w8KRo2GHD"]
| |
| --- // Other task properties
|
--- tags (collection)
|
--- tagID (document)
|
--- title: "Health"
|
--- userID: "zaEh95kXJKapyVUqrPws58dyRIC3"
|
--- colour: "red"
|
--- // Other tag properties
A solution that I have found to work is to create a junction table, however every time I navigate to the detail view of a task I have to query the database to find those relationships which in return drives up billing costs. Can’t help but feel as though I am caught between a rock and a hard place.
Related / follow-up Q&As
What is the most efficient way to store tags in Firestore?
How to model a many-to-many relationship in Firestore
What is denormalization in Firebase Cloud Firestore?
when I tap to see the details of a task a query is sent to Firestore to retrieve the tags associated with it.
Since you store the data in two different collections, yes, two different queries are needed. One to get the tasks and the second one to get the corresponding tags data. But that's not bad.
However, there are some other options that you can take into consideration. The first one would be, instead of saving the IDs of tags into an array, just save the actual data, meaning the entire "Tag" object. Or at least the important data of the tag. This practice is called denormalization. If you're new to the NoSQL databases, please note that this practice is quite common when it comes to Firebase. Also bear in mind that when you are duplicating data, there is one thing that you should know about. In the same way, you are adding data, you need to maintain it. In other words, if you want to update/delete an item, you need to do it in every place that it exists.
When using the above solution, note that there are some limits when it comes to how much data you can put into a document. According to the official documentation regarding usage and limits:
Maximum size for a document: 1 MiB (1,048,576 bytes)
As you can see, you are limited to 1 MiB total of data in a single document. When we are talking about storing text (tag IDs), you can store pretty much. I doubt you'll reach the limitation but as your arrays get bigger, be careful about this constraint. A workaround for this would be to create another document and another document for storing the tags. But also note, that besides the number of reads, you are also charged with the bandwidth needed to download the documents.
So it's up to you to decide which solution works best for your application.

How to do deep recursion document delete in Firestore properly?

I have a root collection students which has student documents and each of this document has some sub-collections. for eg: marks and each mark document has again sub-collections and so on till depth 4.
Now If i remove a particular marks sub-collection, I want all of its nested sub collections to be deleted completely instead of hanging orphaned.
I read the following open github issue
https://github.com/firebase/firebase-admin-node/issues/361
and find out that deepDeleteCollection can be used only if we know that collection is a leaf sub-collection(reached max depth). but to get this information, we need to separately query by each document inside that sub collection, which doesn't makes sense in terms of performance.
What would be the best way to achieve deepCollectionDelete?
Please let me know In case I seem to miss something here.
Thanks in Advance.
but to get this information, we need to separately query by each document inside that sub collection, which doesn't makes sense in terms of performance.
Unlike in Firebase realtime database where to remove the whole structure within a particular node, you would have taken a reference and call removeValue() method, in Cloud Firestore this is not possible. In order to delete a document that contains a subcollection which in terms contains other documents with other subcollections, you need to find and remove all documents within subcollections from deeper hierarchy to higher hierarchy. For instance, let's assume you have a schema that looks like this:
Firestore-root
|
--- collectionOne
|
--- documentOne
| |
| --- subcollectionOne
| |
| --- documentTwo
| |
| --- subSubCollectionOne
| |
| --- //Documents
|
--- documentThree
|
--- subcollectionThree
|
--- // documents
To delete let's say documentOne, you need to get all documents within subSubCollectionOne and delete them and then find all documents within subcollectionOne and delete them and only at the end you should delete documentOne.
which doesn't makes sense in terms of performance.
This is not true. This process of deleting documents that exist in collections and subcollections works very well. You can delete the documents on client in smaller chunks or using a Cloud Function.

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?
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:
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.
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.
Queries in subcollections are indexed by default, as in the case of top-level collections.
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.
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.
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.
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
}
Surprised this hasn't been mentioned before, but sub-collections can (in some cases) help bypass the orderBy limitations:
You can't order your query by a field included in an equality (==) or in clause.
Suppose you want to get a users most recent 10 logins:
Top-Level:
//We can't use .orderBy after .where('==')
USER_LOGINS.where('userId', '==', {uid}).limit(10)
Sub-Collection:
//With a subcollection we can order and limit properly
USERS.doc({uid}).collection('LOGINS').orderBy('unixCreated', 'desc').limit(10);
Subcollections are also helpful in setting up security rules. Suppose you are building a chat app and have a user collection with a replies subcollection. You want other users to be able to add to the replies collection but want to give the user full rights to the user collection. If you have replies as an array of maps/objects in user collection, it severely limits the rules you can write against the user collection for the collection owner and other users to be able to add to the collection. Whereas, having it as its own subcollection makes writing security rules waaaaay easier.

Firestore data modeling for a booking app for easy availability queries

I wanted to ask for an advice on data structuring best practices for Cloud Firestore for the following scenario.
There's a booking/appointment app. Hotels rent out rooms. Each hotel has multiple rooms. Clients can search the rooms of all hotels by availability on specific days.
What is the best way to structure the availability data in Firestore so I could create a view of all available rooms throughout all hotels.
I thought of creating a separate collections where I would put all the reservations referencing the room ID and date of the reservation. However, it seems like I won't be able to search for available slots this way since Firestore can't perform 'not equals' queries.
So I thought I would create an array field for each room containing all the available dates as timestamps. This creates another problem. Even though I can use 'array_contains' query, users can't check availability for more than one day this way since 'array_contains' can only be used once per query.
What would be the most efficient way to structure the data in this case?
Thank you!
What is the best way to structure the availability data in Firestore so I could create a view of all available rooms throughout all hotels.
A possible database structure that can help you achieve what you want, might be this:
Firestore-root
|
--- hotels (collection)
| |
| --- hotelId (document)
| |
| --- //Hotel properties
|
|
--- rooms (collection)
| |
| --- hotelId (document)
| |
| --- hotelRooms (collection)
| |
| --- roomId (document)
| |
| --- available: true
| |
| --- hotel: "hotelId"
| |
| --- //Other room properties
|
|
--- availabeRooms (collection)
|
--- roomId (document)
|
--- available: true
|
--- hotel: "hotelId"
|
--- //Other room properties
As you can probably see, I have duplicate some data in order to achieve what you want. This practice is called denormalization and is a common practice when it comes to Firebase. For a better understanding, I recommend you see this video, Denormalization is normal with the Firebase Database. It's for Firebase realtime database but same principles apply to Cloud Firestore.
Also, when you are duplicating data, there is one thing that need to keep in mind. In the same way you are adding data, you need to maintain it. With other words, if you want to update/detele an item, you need to do it in every place that it exists.
Using this database schema, you can simply query the database to get all available rooms from all hotels by attaching a listener on availabeRooms reference and get all room objects. If you want to get the details of the hotel from which a particular room is apart, you need to make an extra call to get the hotel details. I have stored within the room object, only a reference of the hotel object which is as you can see, the hotelId. You can also store the entire hotel object but before taking a decision, I recommend you to be aware of some details that can be found in my answer from this post.
Furthermore, if a room becomes unavailable, simply change the value of the available property that exist under rooms -> hotelId -> hotelRooms -> roomId to false and remove the corresponding room from the availabeRooms collection. That's it!
P.S. If you want to get all the available rooms within a single hotel, just attach a listener on rooms -> hotelId -> hotelRooms and get all available rooms using a query that should look like this:
Query query = db.collection("rooms").document(hotelId)
.collection("hotelRooms").whereEqualTo("available", true);
Edit:
According to your comment regarding the date of the reservation, you should create a calendar of reservations for each room separately. Then just simply create a function, in Cloud Function that can be triggered using a cron job. This function can help you check the availability for each room daily. If the room is available, set the available to true otherwise, set the property to false and remove the room from the availabeRooms collection.

Resources