Retrieving snapshots from firestore sub-collection to dart/flutter object - firebase

New here. I've googled everything and can't figure this out.
The first two functions below work flawlessly the third always return null immediately without calling my ListFromSnapShot function, which just turns the map into a list. I am assuming
I'm not calling the Firestore correctly but I have no idea how to do this properly.
final CollectionReference customerCollection = Firestore.instance.collection('customers');
final CollectionReference jobCollection = Firestore.instance.collection('jobs');
// get user doc stream
Stream<UserData> get userData {
return customerCollection.document(uid).snapshots()
.map(_userDataFromSnapshot);
}
//Returns only snapshots with matching customer name to StreamProvider object
Stream<List<Job>> get jobQuery {
return jobCollection.where('customerName', isEqualTo: currentCustomer.companyName)
.snapshots()
.map(_jobListFromSnapshot);
}
//The following always returns Null
Stream<List<JobSectionModel>> get jobQuerySections {
return jobCollection.document('Family Dollar').collection('sections')
.snapshots()
.map(_jobSectionListFromSnapshot);
This is my database structure
Base Collection
Subcollection
Any help would be greatly appreciated.

The code above does work, the problem, after several frustrating days, ended up being random spaces in the firestore path. I'm posting this as an answer because after googling this there were many others with a similar problem and I don't want them to go through the same frustration. Firestore will not show this until you literally click on the path name and it will not crash your program, it just returns nothing.
Do yourself a favor and use the .trim() function when adding data and if you do it manually on firestore make sure there's no trailing spaces.
Also I rebuilt the code as follows which also works (without spaces of course)
Stream<QuerySnapshot> getjobQuerySections(BuildContext context) async*{
yield* jobCollection.document('T-MOBILE').collection('whyspaces').snapshots();
Hope this helps someone. Good Luck

Related

Firebase Firestore - Best way to call a bunch of where gets?

I have an array of user emails that I want to then pull from firebase each of the corresponding user documents from a collection where one of the email matches. I don't want to pull the whole collection as it would be expensive. What is the easiest way to do this? I tried for looping over the array with individual gets to firebase but ran into promise issues as I want to do something in Javascript with them right after.
Thanks so much!
based on what i understood from your question i can only think of using await Promise.all() - you can look into this here.
as an example you could pass an array of promises to await Promise.all() so you could do
const res = await Promise.all(
array.map( x => db.collection('users')
.where('email' , '==', x.email).limit(1).get());
mind you that in this example you would still have to process the result as they will return a snapshot not a document ...
Update:
Hey there, i just noticed that you can use in operator in firebase query, which will return the values matching in a given array.
i'm not sure but maybe using it might be suitable in your use-case you can check the documentation here

.once() doesn't seem to work / recognized while working with Flutter & Firebase

I am trying to fetch some data from FireStore and store it in a local list to be displayed later in the form of cards. I have seen many tutorials where people have used .once() to fetch data.
Now, when I am trying to do the same then getting error like the word isn't recognized.
#override
void initState() {
super.initState();
CollectionReference dbRefItem = FirebaseFirestore.instance.collection("items");
***dbRefItem.once().then***
}
I cant seem to find any documentation if it has been deprecated or am I doing something wrong! Hovering cursor on the error says
"The method 'once' isn't defined for the type 'CollectionReference'.
Try correcting the name to the name of an existing method, or defining
a method named 'once'."
.get() did the same job as .once()
However, .once() syntax is not being accepted by Flutter's latest SDK while worked on the previous one. Have raised a ticket with Flutter Dev forum.

Flutter & Firebase: How to populate an array and then later, return all the contents

I have been trying to get arrays working in Firebase, and I am aware that there are a lot of references and discussions about this online, and I have read through all of these and none of it works.
First off, the Firebase side. The structure containing the array and two example strings inside it:
Firebase Structure
collection -> document -> fields
userData profileImages URLs (array)
: https://firebasestorage.googleapis.com/v0/b/app-138804.appspot.com/o/jRwscYWLs1DySLMz7jn5Yo2%2Fprofile%2Fimage_picker4459623138678.jpg?alt=media&token=ec1043b-0120-be3c-8e142417
: https://firebasestorage.googleapis.com/v0/b/app-138804.appspot.com/o/jRwscYWLs3872yhdjn5Yo2%2Fprofile%2Fimage_picker445929873mfd38678.jpg?alt=media&token=ec3213b-0120-be9c-8e112632
The first issue I am facing is writing to this array in the database:
Firestore.instance.collection('userData').document('profileImages').updateData({
'URLs': _uploadedFileURL,
});
Whenever I add data to this array, it just overwrites the existing data. I need to be able to keep all the existing data intact and simply add the current new line to the array.
Once this is working, I then need to be able to return all of the strings in this array without needing to know how many of them there will be.
For this part, I basically have nothing at this point. I could show some of the things I have tried based on suggestions from other articles on this, but none of it is even close to working correctly.
im assuming that _uploadedFileURL is a String, and you are updating the property URLs, that's why your data gets overwritten, because you are changing the URLs value to a single string which is _uploadedFileURL. to solve this issue, simply get the current data inside profileImages before commiting the update. like so
final DocumentSnapshot currentData = await Firestore.instance.collection('userData').document('profileImages').get();
Firestore.instance.collection('userData').document('profileImages').updateData({
'URLs': [
...currentData.data['URLs'],
_uploadedFileURL
],
});
and for the second part of your question, all you need is to query for the profileImages
Future<List<String>> _getProfileImages() {
final document = Firestore.instance.collection('userData').document('profileImages').get();
return document.data['profileImages]
}
the result of the get method will be a DocumentSnapshot, and inside the data property will access the profileImages which is a List<String>.
Ok guys and girls I have worked this out. Part 1: appending data to an array in Firebase.
Firestore.instance.collection('userData').document('profileImages').updateDataupdateData({
'URLs':FieldValue.arrayUnion([_uploadedFileURL]),
});
Where _uploadedFileURL is basically a string, for these purposes. Now I have read that arrayUnion, which is super groovy, is only available in Cloud Firestore, and not the Realtime Database. I use Cloud Firestore so it works for me but if you are having issues this might be why.
Now what is extra groovy about Cloud Firestore is that you can similarly remove an element from the array using:
Firestore.instance.collection('userData').document('profileImages').updateDataupdateData({
'URLs':FieldValue.arrayRemove([_uploadedFileURL]),
});
So how to get this data back out again. A simple way I have found to get that data and chuck it into a local array is like so:
List imageURLlist = [];
DocumentReference document = Firestore.instance.collection('userData').document('profileImages');
DocumentSnapshot snapshot = await document.get();
setState(() {
imageURLlist = snapshot.data['URLs'];
});
From here at least you have the data, can add to it, can remove from it and this can be a platform for you to figure out what you want to do with it.

What is the difference between getDocuments() and snapshots() in Firestore?

I am a little confused about the difference between these two. My understanding is that getDocuments is a type of Future and seems to get the entire documents according to the query. while snapshots, on the other hand, is a type of Stream and, correct me if I'm wrong, I think it represents the results of the query? I need a more specific explanation of this issue. I will include some code snippets as an example for more clarification
getDocuments()
getUserById(String userId) async {
return await _firestore.collection("users").where("userId", isEqualTo: userId).getDocuments();
}
snapshots()
getUserById(String userId) async {
return await _firestore.collection("users").where("userId", isEqualTo: userId).snapshots();
}
So what's the difference?
When you call getDocuments(), the Firestore client gets the documents matching the query from the server once. Since this may take some time it returns a Future<QuerySnapshot>.
When you call snapshots() the Firestore client gets the documents, and then keeps watching the database on the server for changes that affect your query. So if document is written in the users collection that affects your query, your code gets called again. So this returns a stream of QuerySnapshot.
In both cases the results for the entire query are in the QuerySnapshot object.
I highly recommend reading the Firestore documentation on getting data once and on listening realtime updates. While they don't contain Flutter examples, the explanation in there applies equally to the Flutter libraries.
getDocuments():
It's used to provide data once. Cloud Firestore contains collections and inside these collections, you have documents that may contain subcollections or fields mapped to a value. To retrieve any of the doc fields to used it in widget this is used.
snapshots():
It will be called on every data change in your document query. For this StreamBuilder must be used to fetch fields as modified.
In short, it will do the job of setState() where it gives you the response for every modification so that UI can be updated.

Filter Firebase collections on arrays or objects in Firebase Firestore

I am since a few days looking for a solution for my problem. What i try to do is loading objects from Firestore with a filter lower in the structure.
/meetings (collection)
/meetingId
/people (object)
- uid1:1
- uid2:1
Without a where query, it works fine. But since i want to validate on people in the object. I walk against walls.
FirebaseUser user = await FirebaseAuth.instance.currentUser();
Firestore.instance
.collection('meetings')
.where('people.${user.uid}', isEqualTo: '1')
.orderBy('sessionStart', descending: true)
.snapshots()
.listen((data) {print(data);});
Returns zero results. Also when i self implement a array-contains. It works not. Since i want implement rules on my data structure, i need a filtering.
Every time i execute a query i get a request to create a new index. com.google.firebase.firestore.FirebaseFirestoreException: FAILED_PRECONDITION: The query requires an index. You can create it here.
When i follow the url, i get the suggestion to create the following index:
Collection: meetings
Fields: people.QkrzIBRs3TNHcTbYGSHsZjFPvlj2 ASC sessionStart DESC
What will result in a index per user, what is not my wish.
Can someone help me with this, guide me to the correct solution? I will be thankful for it!
Finally after a lot of trial and error I found the possible solution.
The sortBy made it impossible to execute the query. If i remove it and just only use the where it works perfect.
This was the solution:
FirebaseUser user = await FirebaseAuth.instance.currentUser();
Firestore.instance
.collection('meetings')
.where('people.${user.uid}', isEqualTo: '1')
.snapshots()
.listen((data) {print(data);});
The rule i needed to create was:
match /meetings/{document=**} {
allow read, list: if resource.data.people[request.auth.uid];
}
At the end i was still missing sort functionality. So i resolved in the code after creating Meeting objects from it:
meetings.sort((Meeting from, Meeting to) => from.sessionStart
.toIso8601String()
.compareTo(to.sessionStart.toIso8601String()));
There are probably better solutions, so if you know a better solution, reply please.
Hope this will help other people

Resources