Firestore asks me create the index that is already exist - flutter

I'm trying to paginate comments. The first 10 comments is loading ok, but next ones (when query contains startAfterDocument) return error like:
Query(comments where movie_id == 1041047 order by -created, __name__) failed: Status{code=FAILED_PRECONDITION, description=The query requires an index. You can create it here: https://console.firebase.google.com/project/.......
But this index is already exist, I created it before. And if I follow the suggestion link Firebase Console tells me the same: this index is exist.
Future<List<DocumentSnapshot>> _loadPageFrom(
int index, DocumentSnapshot lastDoc) async {
Query query = Firestore.instance
.collection('comments')
.where('movie_id', isEqualTo: movieID)
.orderBy('created', descending: true);
if (lastDoc != null) query = query.startAfterDocument(lastDoc);
final snapshot = await query.limit(10).getDocuments();
return snapshot.documents;
}
What problem is here?

If you had recently deleted your index, you will need to wait a little bit until it's deleted from your project inside GCP, after that you will be able to create it again.

Related

Flutter firestore returns length 0 while there is data in firestore

I have the following code in flutter:
QuerySnapshot querySnapshot =
await _firestore.collection("user1#gmail.com").get();
List Data = querySnapshot.docs.map((doc) => doc.data()).toList();
print("Length: ${Data.length}");
Here is my firestore database:
I get the following output:
I/flutter (11484): Length: 0
The Documents for each user email is variable, so I need the length of the documents. Also I need to get to the details of each document like content and title. How to do it? Thanks.
Could you try this:
int size = await FirebaseFirestore.instance.collection(collectionPath).get(GetOptions(source:Source.server))..size;
I will recommend finding a way to store the length of documents as a field in your Cloud Firestore database because calling the get function on a whole collection means filling up the mobile phone memory. (Say you have 500,000 users at least). This makes your app slow
You could have a field called count such that when you add a document, you can use the firebase transaction to update firebase.
For example:
// Create a reference to the document the transaction will use
DocumentReference documentReference = FirebaseFirestore.instance
.collection('users')
.doc(documentId);
return FirebaseFirestore.instance.runTransaction((transaction) async {
// Get the document
DocumentSnapshot snapshot = await transaction.get(documentReference);
if (!snapshot.exists) {
throw Exception("User does not exist!");
}
// Update the follower count based on the current count
// Note: this could be done without a transaction
// by updating the population using FieldValue.increment()
// Perform an update on the document
transaction.update(documentReference, {'followers': FieldValue.increment(1);});
// Return the new count
return newFollowerCount;
})
.then((value) => print("Follower count updated to $value"))
.catchError((error) => print("Failed to update user followers: $error"));
You can see more documentations here: FlutterFire

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

DocumentID search in Firestore with a List

Can i search a Firestore DocumentID with a List<String>?
I am trying to search through my collection with some selection of documentID in a List. The List will consist of few String. Can I search through the Firestore collection using this?
This is the List:
List<String> _selectedBusStop = List<String>();
This is the code I used in finding the DocumentID based on the list that is in here.
Future <void> saveRoute(_selectedBusStop) async{
Firestore.instance.collection('markers').where('BusstopName', isEqualTo: _selectedBusStop)
.snapshots().listen((location) {
if(location.documents.isNotEmpty){
for (int i = 0; i < location.documents.length; i++){
initRoute(location.documents[i].data, location.documents[i]);
}
}
});
setState(() {
});
}
I am using where and isEqualTo or is this approach wrong? Any idea how to make it work for this part? Thank you in advance for your help.
Update:
This is how my Firestore looks like:
The List have some of the BusstopName but not all of it. I do not want to retrieve all the data from the Firestore just the one that is in the List. Sorry for causing so many misunderstanding.
Use the whereIn operator, like this:
Future <void> saveRoute(_selectedBusStop) async{
Firestore.instance.collection('markers').where('BusstopName', whereIn: _selectedBusStop)
.snapshots().listen((location) {
if(location.documents.isNotEmpty){
for (int i = 0; i < location.documents.length; i++){
initRoute(location.documents[i].data, location.documents[i]);
}
}
});
setState(() {
});
}
Assuming your documents have a unique id stored in the field BusstopName and also the documents actual id matches the content of this field, you have 2 possibilities.
(1) .where query
query data with collection("markers").where("BusstopName", "=", "yourBuststopId").
this returns a querySnapshot Object, on which you can call .size to check if there were any documents with that Id found (could be more than 1 if you have an inconsistent database).
(2) .doc query
query data with collection("markers").doc("yourBuststopId")
this returns a documentSnapshot Object, on which you can call .exist to check if the document actually exsists.
In both cases you need to do 1 query per Id, because Firestore queries only support equality and range operations. See this similar SO question. I would suggest to do the queries asynchronously, otherwise the time to execute will increase with the size of the array.
If you are concerned about costs, you only get billed for the results that actually return documents that exist.
you might also try this:
FirebaseFirestore.instance
.collection('markers')
.where('BusstopName', arrayContainsAny: ['Utar Bus Stop', 'Garden Bus Stop'])
.get()
.then(...);
Taken from the examples documentation

Deleting a Firestore entry based on FIFO

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

How to update a Firestore document after querying in Flutter

I am trying to figure out how to update my document after I query the one I want to update. Here is the code I came up with:
Firestore.instance.collection("Social_Posts").where(data["postTitle"], isEqualTo: "postTitle").where(data["postContent"], isEqualTo: "postContent")
.snapshots()
.listen((event) {print("hh");});
Whenever I perform this query to find the correct document, I would like to update the document by adding a number to it.
It is possible to update the document from inside, The easier way to do that would definitely be to use the documentID and to increment the value Firestore has a special property, FieldValue.increment(1). Using this you can easily increment a field.
Lets say the field in the document you want to increment by 1, is "numberField".
Updated code using async/await style:
onTap: () async {
setState(() {
_title = data["postTitle"];
_content= data["postContent"];
});
print(_title);
print(_content);
QuerySnaphot snapshot = await Firestore.instance.collection("Social_Posts")
.where("postTitle" , isEqualTo: _title)
.where("postContent", isEqualTo: _content)
.getDocuments();
if(snapshot.documents.isNotEmpty)
{
snapshot.documents.forEach((doc){
doc.reference.updateData({"views" : FieldValue.increment(1),});
});
}
}
The numberField will be incremented without needing to make an extra call to know the present value.
EDIT : You were making a mistake in the .where() method
You can do that by using the document id you quired from inside the listen function in your case by using the event.documents[0].documentID assuming that your query returns one document only and then you can call the updateData method from Firestone package
Your code might looks like this:
Firestore.instance.collection("Social_Posts")
.where(data["postTitle"], isEqualTo: "postTitle")
.where(data["postContent"], isEqualTo: "postContent")
.snapshots()
.listen((event) {
Firestore.instance
.collection("Social_Posts")
.document(
event.documents[0].documentID)
.updateData(
updateEvent) //Add here the new object you want to
.whenComplete(() {
// You can add your desire action after the row is updated
}
});
You can check package page for more information https://pub.dev/packages/cloud_firestore
And if you want to check a sample on how to perform CRUD functionalities you can check this repository: https://github.com/sayed3li97/flutter_firestore_crud