Firestore - Delete Documents without Read first - google-cloud-firestore

Is there a way to mass delete documents in a Collection without getting charged for a 'read' first?
Let's say I have a collection with 1000 documents. I decide I want to delete every document that's older than 1 day. I could use a Query to return a QuerySnapshot that returns [300] documents (DocumentReference). I don't need to read the documents contents (DocumentSnapshot), I just need to delete them.
From what I understand from the pricing documentation, because I returned a QuerySnapshot first, it will charge me for 300 reads, then 300 deletes. It doesn't delineate between "reading" a DocumentReference vs. "reading" data in a DocumentSnapshot.
Is there any way to avoid the 300 reads? I can understand that getting back these 300 documents involves effort on Firestore's end to figure out that appropriate subset of documents. But the arbitrary charge of a read no matter if you actually try to get document data (DocumentSnapshot) or not (e.g. just a DocumentReference to delete) seems like it should be possible to avoid.

To delete a document you must have or create a DocumentReference to that document. This requires that you know its complete and exact path to the document.
If you want to delete documents that match a certain condition without already knowing their paths, you will first need to query for those documents to determine those paths/DocumentReferences. This involves reading them. There is now way to avoid this at the moment.

Related

Difference between .where() and .doc() related to number of reads

If I get a document by .collection('users').where('uid', isEqualTo, widget.uid) or by .collection('users').doc(widget.uid) will I read the database the same number of times?
You are charged one read for every document that matches your query when it is executed. It does not matter how you make that query - what matters is the number of documents returned to the app.
If your where query returns exactly one document, then it is not any different in price or performance than one using doc.
The former returns a QueryFieldFilterConstraint while the latter returns a DocumentReference. The DocumentReference is preferable because it can be used to write, read, or listen to the location.
Naturally, a query must read every id in the referenced collection (this is probably indexed) while a reference points to a single id. In terms of pricing, aggregateQueries documentation is actually returning a page not found error at the moment?
See Understand Cloud Firestore billing
For aggregation queries such as count(), you are charged one document
read for each batch of up to 1000 index entries matched by the query.
For aggregation queries that match 0 index entries, there is a minimum
charge of one document read.
For example, count() operations that match between 0 and 1000 index
entries are billed for one document read. For a count() operation that
matches 1500 index entries, you are billed 2 document reads.

Firestore array-not-contains alternative solution

TL-DR
I have created a Flutter Firestore posts application. I want to present the user only new posts, which they didn't read yet.
How do I achieve that using Firestore query?
The problem
Each time a user sees a post, their id is added to the post views field.
Next time the user opens the app, I want to present only posts they didn't read yet.
The problem is that query array-not-contains is not supported. How do I achive that functionality?
You're going to have a real hard time with this because Firestore can only give you documents where you know something about the contents of that document. That's how indexes work - by recording data present in the document. Indexes don't track data not present in a document because that's basically an infinite amount of data.
If you are trying to track documents seen by the user, you would think to mark the document as "seen" using a boolean per user, or tracking the document ID somewhere. But as you can see, you can only query for documents that the user has seen, because that's the data present in the system.
What you can do is query for all documents, then query for all the documents the user has seen, then subtract the seen documents from all documents in order to get the unseen documents. But this probably doesn't scale in a way you'd like. (It's essentially the same problem with Firestore indexes not being able to surface documents without some known data present. Firestore won't do the equivalent of a SQL table scan, since that would be a lot of reads you'd have to pay for.)
You can kind of fake it by making sure there is a creation timestamp in each document, and record for each user the timestamp of the most recent seen document. If you require that the user must view the documents in chronological order, then you can simply query for documents with a creation timestamp greater than the timestamp of the latest document seen by the user. This is really as good as it's going to get with Firestore, since you can't query for the absence of data.

Check the subcollection exists before query in firestore

I'm implementing a social media app, where I put a subcollection of "following" under each user. I want to check if the subcollection exists before I query the subcollection, or the app will crash for querying a nonexistent collection. Is there a way to check this?
Collections don't really "exist" in the way that you're thinking. They simply appear when the first document is created, and they disappear when the last document is removed. There is no operation to simply create or remove a collection like a folder in a filesystem, and there is not operation to check to see if a collection "exists". A query against a collection with no documents will not fail (unless it was rejected by a security rule).
The only thing you can really do is query the collection to see if it has any documents at all. You can limit the query to 1 document if you want to minimize costs.

Skip specific documents when querying them from Firestore database

In my application user gets to pick specific documents out of the list, for example: 1,5,8 from the list containing documents 1,2,3,4,5,6,7,8,9. When logged into the application next time, I want to first fetch all of the chosen documents (considering pagination, because the number of documents user picked could be very high), and then start fetching the remaining documents as the user finishes viewing picked documents by scrolling down the list.
As it turns out, available Firestore querying methods are not capable of skipping the specific documents.
My current idea:
Make single document references for the user-specific documents and fetch them.
Make single document references for the documents between the range of user-specific documents (From the example that would be documents number: 2,3,4,6,7).
After that start making 'big queries' for the remaining documents.
This looks like a working solution, but I'm sure that there is a better way to accomplish the goal, since what I've done is not asynchronous and very slow. Help is appreciated!
Firestore doesn't have any way to exclude specific documents from queries. You may only include them using some existing field values. If you already know the documents to fetch, you can just get() them individually.
It sounds like you are already able to work around these requirements. I don't believe you have any alternatives.

List of updated documents

Is there a way to update (or delete) many documents matching a certain criteria and get the list of IDs of actually updated/deleted documents (or some other fields of those documents)? I cannot simply query the documents matching my criteria beforehand because I need kinda atomicity for this operation. And I can't use findAndModify because it can only process one document at a time which is too slow because of round-trips. Suggestions?
MongoDB only supports atomic operations on a single document.
http://www.mongodb.org/display/DOCS/Atomic+Operations
The only way to do this is to do what you said you didn't one to:
First query the collection to find id's for our query:
db.things.find({"name":"john"}, {_id:1});
Then, use the same query to remove:
db.things.remove({"name":"john"}, {_id:1});
Not ideal, and not atomic, but it's as good as you're going to get in this scenario.