In Firestore, I have seen multiple ways to restrict the deletion of documents with security rules and to prevent the deletion of a collection by using Acess Controls.
But is there a way to get a notification or an alert when a collection is deleted in Firestore?
Deleting a collection is critical and it's surprising that there is no easy way to stay aware of it.
Collections are automatically created when a first document is added to them, and automatically removed when the last document is removed from them. There is no notification for either of these events though.
If you have a use-case that requires knowing when the collection is created/deleted, you'll have to detect adding the first document/removing the last document in your application code with a query.
Related
Background: I am using Firestore as the main database for my (web) application. I also pre-render the data stored in there, which basically means that I collect all data needed for specific requests so I can later fetch them in a single read access, and I store that pre-rendered data in a separate Firestore collection.
When a user changes some data, I want to know when this background rendering is finished, so I can then show updated data. Until rendering is finished, I want to show a loading indicator ("spinner") so the user knows that what he is currently looking at is outdated data.
Until now, I planned to have the application write the changed data into the database and use a cloud funtion to propagate the changed data to the collection of pre-rendered data. This poses a problem because the writing application only knows when the original write access is finished, but not when the re-rendering is finished, so it doesn't know when to update its views. I can hook into the table of rendered views to get an update when the rendering finished, but that callback won't be notified if nothing visibly changes, so I still do not know when to remove the spinner.
My second idea was to have the renderer function publish to a pubsub topic, but this fails because if the user's requests happens to leave the original data unchanged, the onUpdate/renderer is not called, so nothing gets published on the pubsub and again the client does not know when to remove the spinner.
In both cases, I could theoretically first fetch the data and look if something changed, but I feel that this too easily introduces subtle bugs.
My final idea was to disallow direct writes to the database and have all write actions be performed through cloud functions instead, that is, more like a classical backend. These functions could then run the renderer and only send a response (or publish to a pubsub) when the renderer is finished. But this has two new problems: First, these functions have full write access to the whole database and I'm back to checking the user's permissions manually like in a classical backend, not being able to make use of Firestore's rules for permissions. Second, in this approach the renderer won't get before/after snapshots automatically like it would get for onUpdate, so I'm back to fetching each record before updating so the renderer knows what changed and won't re-render huge parts of the database that were not actually affected at all.
Ideally, what (I think) I need is either
(1) a way to know when a write access to the database has finished including the onUpdate trigger, or
(2) a way to have onUpdate called for a write access that didn't actually change the database (all updated fields were updated to the values they already contained).
Is there any way to do this in Firestore / cloud functions?
You could increment a counter in the rendered documents, in such a way a field always changes even if there is no change for the "meaningful" fields
For that, the best is to use FieldValue.increment.
When a collection is deleted from Cloud Firestore, its indexes are deleted along with it. I presume that when a collection goes from one or more documents to zero documents that its indexes are preserved. However, in the Cloud Firestore UI, when a collection goes from one document to zero the collection disappears from the root collections tree. Again, I presume this is an artifact of the Cloud Firestore UI, but it got me wondering whether something more happens when a collection becomes empty (as opposed to the collection being deleted outright).
Can you please help clarify what happens (if anything) when a collection goes from one or more documents to zero in Cloud Firestore? Do I need to be worried about losing any indexes when this occurs?
I'm neither Googler nor Firebaser, BUT...
Firestore indexes documents, not collections - the collection paths are an organizing principle more than physical entities. The "collections" are part of the path to documents, and it's the paths and the document fields that end up indexed.
Case in point: you can actually delete a collection while child documents remain, and they will still be indexed with the collection name/ID as part of their path - you'll see this in the console with the collection (and any interstitial document) names italicized.
When a collection goes from 1 to 0 documents, all that happens is that the document is gone, and nothing else. The UI sees no reason to display a collection when there is nothing to show.
Collections don't really "exist". They are just ways to organize documents for the purpose of making queries. What you see in the console is just there to help you visualize the contents of the database. Collections will apparently spring into "existence" when a document is first created, and just as quickly disappear when there are none. They do not work like directories in a filesystem.
An index is just a way of telling Firestore that you have special query needs for documents in a certain named collection or collection group. The index simply enables the query against the documents in the collection or collection group that you name. The index works without requiring any documents to index, and it will continue working no matter how many documents exist.
Some great answers by LeadDreamer and Doug already, but one more thing you seem to be curious about: deleting all documents from a collection does not affect the index definitions for that collection. So if you later add documents to the collection again, the same index definitions will still apply.
I have a users collection with a bunch of users and their details. I also have a notifications collection, that the users can query on. I expect the amount of notifications to be at least in the thousands, but probably tens of thousands over the years.
I want the users to be able to mark a notification as "seen". How would I go about this?
I have considered following options:
Add an array notificationsSeen with references to notification documents to each user document. I'm scared of hitting size limits here though, if a user has seen e.g. 50k notifications.
Add the same but as a sub-collection in users. I'm not sure how to go about this though, since I only really need one property (notification ID). Do I put the notification ID as the sub-collection doc ID and have no fields on the documents? Do I let Firestore generate a random ID and assign the notification ID as a property on the sub-collection?
Add an array seenBy with references to user documents to each notification document. Although this will allow users to see which notifications other users have seen, and I don't think I want this.
Hope you can help me out, I'm out of ideas and I am not sure how to implement the best idea I have so far (sub-collection in users), which has also been mentioned as a solution here: Firestore storage size limit how to store large arrays (but without implementation details).
The only scalable way to store an arbitrarily large list of data in Firestore is using documents in a collection. Array type fields do not scale for growing lists of data because the items will eventually exceed the 1MB size limit for a single document, which will obviously cause problems at scale.
It's OK to have a document with no fields. If all you need to do is record that a document exists in order to check its existence later in that collection, that's fine. You can use the notification ID as the document ID if you are absolutely certain that ID conforms to valid IDs in Firestore. Otherwise, you should give it a random ID, and also put the notification ID as a field in the document so that you can query for it later.
You will want to familiarize yourself with the documentation on Firestore limits, which talks about the maximum size of a document, and also the valid characters for a Firestore document ID.
1st option is not possible, as document size limitation defined by firebase, 2nd and 3rd option are possible but 2nd option is a better to implement this feature, and in 2nd option i'll prefer to set notification id as the document id in the subcollection, but setting notification id as property in document is also valid(this option is better suited for case where there are multiple documents with same id, something like posts collection, where user has multiple posts posted).
It’s possible to build a collection for each user for their notifications. You may delete a user-specific document after the notification is read. You may also add some on_snapshot targets to send out notifications after it’s been added to the collection.
I am use Firestore and try to remove race condition in Flutter app by use transaction.
I have subcollection where add 2 document maximum.
Race condition mean more than 2 document may be add because client code is use setData. For example:
Firestore.instance.collection(‘collection').document('document').collection('subCollection’).document(subCollectionDocument2).setData({
‘document2’: documentName,
});
I am try use transaction to make sure maximum 2 document are add. So if collection has been change (For example new document add to collection) while transaction run, the transaction will fail.
But I am read docs and it seem transaction use more for race condition where set field in document, not add document in subcollection.
For example if try implement:
Firestore.instance.collection(‘collection').document('document').collection('subCollection').runTransaction((transaction) async {
}),
Give error:
error: The method 'runTransaction' isn't defined for the class 'CollectionReference'.
Can transaction be use for monitor change to subcollection?
Anyone know other solution?
Can transaction be use for monitor change to subcollection?
Transactions in Firestore work by a so-called compare-and-swap operation. In a transaction, you read a document from the database, determine its current state, and then set its new state based on that. When you've done that for the entire transaction, you send the whole package of current-state-and-new-state documents to the server. The server then checks whether the current state in the storage layer still matches what your client started with, and if so it commits the new state that you specified.
Knowing this, the only way it is possible to monitor an entire collection in a transaction is to read all documents in that collection into the transaction. While that is technically possible for small collections, it's likely to be very inefficient, and I've never seen it done in practice. Then again, for just the two documents in your collection it may be totally feasible to simply read them in the transaction.
Keep in mind though that a transaction only ensures consistent data, it doesn't necessarily limit what a malicious user can do. If you want to ensure there are never more than two documents in the collection, you should look at a server-side mechanism.
The simplest mechanism (infrastructure wise) is to use Firestore's server-side security rules, but I don't think those will work to limit the number of documents in a collection, as Doug explained in his answer to Limit a number of documents in a subcollection in firestore rules.
The most likely solution in that case is (as Doug also suggests) to use Cloud Functions to write the documents in the subcollection. That way you can simply reject direct writes from the client, and enforce any business logic you want in your Cloud Functions code, which runs in a trusted environment.
I am trying to limit document creation of my each subscriber for a certain number of documents.
In my cloud function, I create an onCreate trigger and even I return "null" if document count doesn't match with my limits, firestore still creates this document.
I digged into firestore and cloud functions documentations but could not find any example how to cancel/abort a cloud function trigger.
Bonus question;
Do I have any way to alert customer wheter he/she exceeded the limit of this document creation? I thought I can update a seperate alert document in my trigger function and read and display this to customer. Do you know any way to listen onCreate trigger's result and display the error real time?
Any help please?
Many Thanks.
What you're trying to do isn't possible. Cloud Functions respond to events that occur within some product (such as Firestore). The event indicates that some change already happened. All you can do is choose what you want to do in response to that event. You can't prevent the change from taking place. The best you could do is undo the change by performing the opposite of what already happened. So, if a document was created, and that violates whatever rules you want to enforce, then you can simply delete the document.