Flutter - print querysnapshot on firebase document does not work - flutter

In the function below, I want to get all the records in a collection named 'contexts'.
But, when I ask to print each record, I get nothing, not even an empty field or a null.
On the console, it is like if I have never ask for print.
I do not understand why.
Future getAllContextInFirebaseV1() async {
CollectionReference ref = FirebaseFirestore.instance.collection('Users')
.doc((FirebaseAuth.instance.currentUser.uid))
.collection('contexts');
QuerySnapshot contextsQuery = await ref
.get();
contextsQuery.docs.forEach((document) {
print(document['context_Name']);
});
}

contextsQuery.docs is a list of snapshots. You actually need to call .data() on each document to access the data ('context_Name').
So instead of
print(document['context_Name']);
you should have
print((document.data() as Map<String, dynamic>)['context_Name']);
Note that the document has other properties on it like exists, id, reference, ...
But wait, are you sure that there are documents inside the contexts subcollection on the current user? Because chances are contextsQuery.size could be zero, so forEach doesn't get called.
If yes, are you sure that the collection names are correct?

Related

Remove referenced document from array Field list type in a document

I have a collection in firebase called "community" and "users". All user records have a field "joinedCommunity" (a list of all joined communities).
I'm trying to figure a code that when a community is deleted, all user records are updated to only remove the community reference from "joinedCommunity" field list.
building this in flutterflow using custom action
onTap on a button in UI, the code is included as one of the actions before community document is deleted.
Future userRecordUpdate(DocumentReference community) async {
final instance = FirebaseFirestore.instance;
final batch = instance.batch();
var collection = instance.collection('users');
batch.update(collection, {
"joinedCommunity": FieldValue.arrayRemove([community])
});
await batch.commit();
}
You're using a CollectionReference, when what you want is a DocumentReference. As per the documentation, WriteBatch.update only works on a DocumentReference.
I have a few suggestions:
Try updating the field without using a WriteBatch. Use a for loop and a regular DocumentReference.update() call.
Then, update your code to use a WriteBatch to update the field. Also, keep in mind a batch is limited to 500 operations.
Finally, consider the security implications of allowing a client to be able to update any User document. You should probably update your security rules so that a user document can only be modified by that user. This code is probably something that should run in a Firebase Cloud Function that gets triggered whenever a community document is deleted.
the following code worked -
Future userRecordUpdate(DocumentReference community) async {
final instance = FirebaseFirestore.instance;
final batch = instance.batch();
var collection = instance.collection('users');
var snapshots =
await collection.where("joinedCommunity", arrayContains:
community).get();
for (var doc in snapshots.docs) {
batch.update(doc.reference, {
"joinedCommunity": FieldValue.arrayRemove([community])
});
}
await batch.commit();
}

flutter/firebase/dart: get data from firestore

How to get docs id from some matching condition?
I was able to get this to work:
Future getChat({required String userIdsArr}) async {
var docId = '';
await chat.where('User ids', isEqualTo: userIdsArr).get().then((value) {
value.docs.forEach((element) {
docId = element.id;
});
});
//print(docId);
return docId
}
this returns the correct record, however, I think this is a terrible way of quering the database because I have to fetch all the records everytime.
Is there a way to write this so that I get the doc Id of the matching condition?
Unfortunately, there is not a better way to accomplish this. When you use the where clause though, it won't fetch everything like you suspect, only records that contain the value you are querying for. I don't believe it's as expensive of a call as you might think.

How can I get a collection inside a QuerySnapshot

On the explore page, I get() the entire users collection to create a user list and search results. Inside each of those user documents is a collection posts that I also need to get to create a GridView of each post. I want to reuse that users collection QuerySnapshot instead of fetching each posts collection again to save money. Is this possible?
Here is my current function:
void fetchUsers() async {
final userRef = FirebaseFirestore.instance.collection('users');
final QuerySnapshot result = await userRef.get();
final docs = result.docs.asMap();
docs.forEach((index, value) {
final profile =
ProfileObject.fromJson(value.data() as Map<String, dynamic>);
usersList.add(UserSearchResult(profile, value.id));
/// Below is the code for getting the posts, not working, need ideas
final QuerySnapshot postsResult = value.get('posts');
final posts = postsResult.docs.asMap();
posts.forEach((index, value) {
final post = Post.fromJson(value.data() as Map<String, dynamic>);
postsList.add(post);
});
});
print(usersList);
print(postsList);
}
Here is the structure of my Firestore:
users
uid (doc)
posts (collection)
info (fields)
uid (doc)
posts (collection)
info (fields)
It is not possible to call a collection to get all sub-collections. You should restructure your database to include sub-collection data in document itself. You can use a map or list for that. But remember, calling everything in one go may end up in slow performance and you might end up losing your customers. So the best way is to include the info in every posts' documents. That way, you won't loss your money and user won't feel lag in performance.
It is not possible. You fetch a document, then fetch the (sub)collection under it.
Subcollection data are not included in the initial document snapshots because Firestore queries are shallow. There shouldn't be any cost savings that you can do there?
See the similar Q&A:
Firestore: Get subcollection of document found with where

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