Deleting a Firestore entry based on FIFO - flutter

I'm trying to create a script that deletes a record from a Firestore collection using a FIFO (First In First Out approach).
So if there are three matching results in the collection, the script should take the first one added and just delete that one (leaving the remaining two). My code is:
_firestore
.collection('myCollection')
.where('uid',
isEqualTo: _auth.currentUser.uid)
.where('field',
isEqualTo: widget.field)
.orderBy('Posted', descending: false)
.limit(1)
.get()
.then((querySnapshot) {
querySnapshot.docs
.forEach((documentSnapshot) {
_firestore
.collection('myCollection')
.doc(documentSnapshot.id)
.delete();
});
});
(Just to note: 'Posted' is the date the entry was added) Unfortunately this doesn't work, and all three results remain in the collection.
If though I use this script instead, then all three results are removed from the collection:
_firestore
.collection('myCollection')
.where('uid',
isEqualTo: _auth.currentUser.uid)
.where('field',
isEqualTo: widget.field)
.get()
.then((querySnapshot) {
querySnapshot.docs
.forEach((documentSnapshot) {
_firestore
.collection('myCollection')
.doc(documentSnapshot.id)
.delete();
});
});
An example of an entry in my collection is as follows:
So I know the logic, connection, fields etc... are all correct, but why does the first example not work?

Have you check you logs? I am pretty sure that Firebase is throwing an error saying that your collection need indexes with a link.
Just follow the link and the instruction to build the indexes. Once complete you should be able to do what you are looking for.
More info about Indexes: https://firebase.google.com/docs/firestore/query-data/indexing
and Simple queries / Coumpound queries: https://firebase.google.com/docs/firestore/query-data/queries

Related

Can't get one document from flutter cloud firestore

I'm trying to get one document by user id from firebase cloud firestore using flutter.
I tried firstly to fetch the data then added a condition to it, but i'm not able to display the data or even print it in the console!
Here is what i've tried so far:
database.dart:
Future<DocumentSnapshot?> getFileByUser(String userId) async {
return FirebaseFirestore.instance
.collection('cartesPro')
.where('cartUserId', isEqualTo: FirebaseAuth.instance.currentUser!.uid)
.get()
.then((value) {
value.docs.forEach((element) {
print(element.id);
});
});
}
ui page:
User? user = FirebaseAuth.instance.currentUser;
showFile() {
final files = DatabaseMethods().getFileByUser(user!.uid);
print(files);
}
and then made the call in a button so I can print the result only! it's returning the documents of the actual user, but I couldn't map the result in order to get the latest in timestamp order!
I appreciate any kind of help, thanks in advance!
If you want to get the most recent document for the user, you should order on the field that has the timestamp and the limit to a single result:
FirebaseFirestore.instance
.collection('cartesPro')
.where('cartUserId', isEqualTo: FirebaseAuth.instance.currentUser!.uid)
.orderBy('timestamp', descending: true)
.limit(1)
.get()
See the Firestore documentation on ordering and limiting data for more on this.

FirebaseException: Not able to query a collectionGroup

I am trying to do a pretty simple query in firebase for a collectionGroup. I only want to get all the products that are of the type "Restuarant". The code is below:
QuerySnapshot res = await FirebaseFirestore.instance
.collectionGroup('products')
.where("type", isEqualTo: "Restuarant")
.get();
It keeps throwing a FirebaseException as below:
I have added an exception in the Firebase indexes. It is a single field index.
What is the issue here? Why is this Exception occurring?
So I was able to get it working by adding a composite index. After adding the composite index, I was able to perform orderBy query too, using the rating field.
QuerySnapshot res = await FirebaseFirestore.instance
.collectionGroup('products')
.where("type", isEqualTo: "Restuarant")
.orderBy("rating", descending: true)
.get();

Using fields in a document for another query

I have 2 collections, one called Timeline and one called Posts. The first one is very simple, having 2 fields: 'PostId' and 'OwnerId', while the second one is a little bit more complex but it is not important for the purpose of my question.
Using 'OwnerId' and 'PostId' I can get a specified post in the collection Posts.
What I want to do is getting all the docs in timeline of a specified user, for each doc use it to get the post infos in Posts collection, and order the posts in descending timestamp, but I can't find a smart and effective way to do so.
To get all the docs of a specified user in Timeline I write:
QuerySnapshot snapshot = await timelineRef
.doc(currentUserID)
.collection('timelinePosts')
.get();
And to get a specified post from Posts collection I write:
QuerySnapshot snapshot = await postsRef
.doc(ownerId)
.collection('userPosts')
.doc(postId)
.get();
How can I mix these two to get the result I want? Thank you
There is no concept of a server-side join in Firestore, nor is there a way to filter the documents returned based on information in documents in another collection. All Firestore queries can do is evaluate the literal data in the candidate documents (through an index) and filter based on that.
So you will either have to duplicate the data to filter on in each userPosts document, or perform a so-called client-side join - with the latter being the most reasonable option for this use-case as far as I can see.
You'll end up with individual get() calls for the documents, or a bunch in in queries on the FieldPath.documentId() you get from timelinePosts, and then merge the results in your application code.
At the moment I found a solution that is not very elegant but at least is working:
QuerySnapshot snapshot = await timelineRef
.doc(widget.currentUser.userID)
.collection('timelinePosts')
.orderBy('timestamp', descending: true)
.get();
List<TimelineItem> timelineItems =
snapshot.docs.map((doc) => TimelineItem.fromDocument(doc)).toList();
List<PostWidget> postsTemp = [];
for (var element in timelineItems) {
DocumentSnapshot documentSnapshot = await postsRef
.doc(element.ownerId)
.collection('userPosts')
.doc(element.postId)
.get();
postsTemp.add(PostWidget(Post.fromDocument(documentSnapshot)));
}
I added timestamp field to my timelinePosts, created a class to contain the data from the first query, and then I did a second query based on the parameters I got on the first one for each doc.
Hopefully I'll find a more efficient solution but at the moment I use this

Search for data/values/keyes in firestore documents

I have a search screen in my app and i want to make sure you can search for every value in a firestore document. There are always 4 Keys in one document: title, author, genre and code.
getBookbyTitel(query) async{
return await Firestore.instance
.collection("book")
.where("titel", isEqualTo: query)
.getDocuments();
}
but with this code, I am only able to search for the title. how can I search for the three other keys a well?
Thanks
getBookbyTitel(query) async{
return await Firestore.instance
.collection("book")
.where("titel", isEqualTo: query).where("author", isEqualTo: query2).where("genre", isEqualTo: query3).
where("code", isEqualTo: query4).getDocuments();
}
Firestore has the advantage of having indexes complex and simple.
getBookbyTitel(query,query2,query3) async{
return await Firestore.instance
.collection("book")
.where("genre", isEqualTo: query)
.where("title", isEqualTo: query2)
.where("code", isEqualTo: query3)
.getDocuments();
There is no actual limit to how many .where() you can use, however this is what is called a "complex query" and as such the first time will fail because firestore needs to index first.
So, first run the code above, in the console an error message will pop up with a url that will send you to your firebase project and then on its own it will start indexing so the next time you run that code it will do it lightning fast.
However there is a downside, for each .where() you use, an additonal indexation will be needed for it to work properly. Have in mind that the order is important too, if you query in order [title,author,genre,code] you should always do it this way, otherwise it will detect it as a completely different query and will ask you to index again.
Finally there is also the issue that for example a more specific query is not the same as a query with less attributes. i.e:
getBookbyTitel(query,query2,query3) async{
return await Firestore.instance
.collection("book")
.where("genre", isEqualTo: query)
.where("title", isEqualTo: query2)
.where("code", isEqualTo: query3)
.getDocuments();
If you have this query, and then you try this instead:
getBookbyTitel(query,query2,query3) async{
return await Firestore.instance
.collection("book")
.where("genre", isEqualTo: query)
.where("title", isEqualTo: query2)
.getDocuments();
It won't work, it will ask you for another index.

CollectionReference to DocumentReference

I'm trying to perform a firestore transaction in Flutter.
Therefore I need to pass the documentReference.
There is no problem, when accessing the "lesson" document based on the identifier.
The issue is when fetching the active user pass. The where I'm trying to use returns the Query, and the .reference() returns the CollectionReference (which is not accepted by the transaction method).
How can I get the reference to the document basing on the value of its field.
To spice it up: DB rules don't allow me to read ALL the passes. I've got only access to those, that have my UserID (double checked - working).
DocumentReference lessonRef =
await _db.collection('lessons').document(lesson.identifier);
CollectionReference passRef = await _db
.collection('passes')
.where('userID', isEqualTo: user.identifier).reference();
You can get the DocumentReference of the Queried documents like so:
Firestore.instance
.collection('passes')
.where('userID', isEqualTo: user.identifier)
.snapshots()
.listen((reference) =>
data.documents.forEach((doc) => /*do what you want with doc.reference*/));
(I'm not an expert in Flutter and I could't test the code, but I based the answer in the API reference)