flutter: Retrieving multiple document id present inside a collection of firebase - firebase

I want to get the id of every document in a collection. i tried this code but it return a single document id unlimited times. Can you please suggest a correction or a alternative
getrequest() {
linkref.document(uuid).
collection("Requests").getDocuments().then((value) async{
value.documents.forEach((doc) {
user.add(doc.documentID);
});},);
return ListView.builder(
itemBuilder: (BuildContext cntxt, int index){
return Text(user[index]);
});
}
there is a collection inside a document and inside this collection i have other document. i want to retrieve all documents id
This is screenshot of my firestore

Currently, you have just one document in firestore. I would suggest you add multiple documents and then test this command.You can use snapshot to get multiple documents as suggested by Sandeep using
Firestore.instance
.collection('Links')
.document(docID)
.collection('Requests')
.snapshots();
You can retrieve multiple documents with one request by querying documents in a collection. By default, Cloud Firestore retrieves all documents that satisfy the query in ascending order by document ID, but you can order and limit the data returned. To retrieve the documents conditionally using where() and then use get
More can be found in the documentation here
To use ListView builder, I would suggest using StreamBuilder something like this:
StreamBuilder<DocumentSnapshot>(
stream: Firestore()
.collection('homepage')
.document(widget.user.uid)
.collection('h')
.document(todaysDate())
.snapshots(),
builder: (context, snapshot) {
if (snapshot.data != null) {
snapshot.data.data.forEach((index, individualDetail) {
cardDetails[index] = individualDetail;
});
}
return cardDetails == null
? CircularProgressIndicator()
: ListView.builder(
itemBuilder: (context, index) {
return HomepageCards(
user: widget.user,
cardDetails:
cardDetails[cardDetails.keys.toList()[index]],
);
},
itemCount: (cardDetailKeys == null
? 0
: cardDetailKeys.length),
);
},
)
This is a snippet from my code but the StreamBuilder would look similar for you too.

Firestore.instance
.collection('Links')
.document(docID)
.collection('Requests')
.snapshots();

Related

Flutter Firebase Cloud Firestore get stream of list of documents by their ids

Hey guys I have two top level collections, a user and a tabs collection. Within each user document I have an array field of document ids with each element in the array representing a document in the tabs collection (sort of like a reference or a pointer to that document) and I would like to listen to real time changes in the users/document with the list of ID's and to listen for changes to the corresponding documents in the tabs collection.
Below is my code so far and there are two issues with it. The stream isn’t working client side - I’m not receiving an updated snapshot when I update the document in cloud firestore. Also in order for me to get a list of tabIds I am currently making a query to cloud firestore which returns a stream however, the whereIn operator only accepts a list of objects so I’m not to sure how to handle that.
data model screenshot
Stream<List<Tab>> get tabs {
var tabIds = _db.collection('users').doc(user.uid).snapshots().where((event) => event.get('tabs'));
return _db
.collection('tabs')
.where(FieldPath.documentId, whereIn: tabIds)
.snapshots()
.map((list) =>
list.docs.map((doc) => Tab.fromJson(doc.data())).toList());
}
You can't directly listen to documents based on their ids, but you can add a field called docId (the value should be the id of the document) to each document then listen to collection with this where condition.
List listOfDocIds = []; // this is the list of your docIds
StreamBuilder<QuerySnapshot<Map<String, dynamic>>>(
stream: FirebaseFirestore.instance
.collection('users')
.where('docId', whereIn: listOfDocIds),
builder: (BuildContext context,
AsyncSnapshot<QuerySnapshot<Map<String, dynamic>>> snapshot) {
if (snapshot.hasError) return Text('Something went wrong');
if (snapshot.connectionState == ConnectionState.waiting)
return CircularProgressIndicator();
List<Map<String, dynamic>> data =
snapshot.data.docs.map((e) => e.data()).toList();
print(data);
// you can build your widget here
return ListView.builder(
itemCount: data.length,
itemBuilder: (context, i) {
return ListTile(
title: Text(data[i]['name']),
// TODO: change 'name' to name of field in users document
);
},
);
},
),

Flutter FirebaseFirestore where condition returning related and unrelated values

I am querying a firestore collection in Flutter using where and arrayContains, for some reason it is not working as expected for me.
StreamBuilder(
stream: (_searchTerm.length >= 3)
? FirebaseFirestore.instance.collection("users").snapshots()
: FirebaseFirestore.instance
.collection('users')
.where('email', arrayContains: _searchTerm)
.snapshots(),
builder: (ctx, snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return Center(
child: CircularProgressIndicator(),
);
}
final results = snapshot.data.docs;
print(results.length);
return ListView.builder(
shrinkWrap: true,
itemCount: results.length,
itemBuilder: (ctx, index) => Text(
results[index].data()['display-name'],
),
);
})
The _searchTerm variable is populated as I type in some values into the textfield and when it hits a length of three characters that's when the above query fires.
For example when I type in test the query should only return the values that contain test in it, but I am getting the whole collection with and without the value test.
Please advice!
EDIT - Posting a screenshot of my firestore data structure
When you do the following:
FirebaseFirestore.instance
.collection('users')
.where('email', arrayContains: _searchTerm)
.snapshots(),
You are looking for documents inside the users collection that have _searchTerm as an item of the email array, property of a user document.
There are two problems:
I don't think the email property of your users is an array.
Firebase does not perform substring searches
I think you will need to use a third-party application for searches on Firestore. A popular one is Algolia that comes with a quite powerful FREE plan.

how to reduce cloud firestore reads?

i have a search bar that receives a query string from the user, if the query string is empty, like i just initiated the search operation, it fetches all documents in posts collection...if user starts typing inside the search bar...it takes the typed text and compare it to a field inside each document in firestore collection and if it matches...its retrieves matched documents and it and previews them in screen, if the user deleted his typed query string...so the query string is empty again...so it fetches all documents again...i use provider package to conduct the query string to database fetch function like so:
Provider:
import 'package:flutter/foundation.dart';
class QueryStringProvider extends ChangeNotifier {
String _queryString = '';
String getQueryString() => _queryString;
updateQueryString(String userQueryString) {
_queryString = userQueryString;
notifyListeners();
}
}
the database function:
Stream<QuerySnapshot> filterAllPosts(String query) {
return query.isEmpty
? _postsCollection.snapshots()
: _postsCollection
.where('postSubtitles', arrayContains: query)
.snapshots();
}
the following is a stream builder wrapped inside a Consumer<QueryStringProvider>, the Streambuilder receives all data retrieved from database fetch function, and populate the data according to my preferred layout structure:
#override
Widget build(BuildContext context) {
return Container(
child: Consumer<QueryStringProvider>(
builder: (context, data, _) {
return StreamBuilder<QuerySnapshot>(
// stream: DatabaseService().listenToAllGigs(),
stream: DatabaseService().filterAllGigs(data.getQueryString()),
builder: (context, snapshot) {
return !snapshot.hasData
? Center(child: Text(''))
: snapshot.data.documents.length > 0
? ListView.builder(
itemCount: snapshot.data.documents.length,
itemBuilder: (context, index) {
DocumentSnapshot data =
snapshot.data.documents[index];
Map getDocData = data.data;
return GestureDetector(
child: PostItem(
appointed: getDocData['appointed'],
appointedUserFullName:
getDocData['appointedUserFullName'],
postId: getDocData['gigId'],
postTitle: getDocData['postTitle'],
postSubtitles: getDocData['postSubtitles'],
postBody: getDocData['postBody'],
// onDeleteItem: () => model.deleteGig(index),
),
);
})
: Center(
child: Text(
'No Posts matching your criteria',
style: TextStyle(fontSize: 16),
));
},
);
},
),
);
}
i have three questions about how to reduce cloud reads with the above code:
1) is there any problems that i fetch all documents when i initiate the search operation and the query string is empty?
2)when users starts typing inside the search bar...does this mean that all the fetched data get lost..and we are making new reads again with filtering?...or it just filters what i've already fetched from initiating the search operation from the start?
3) when user deletes the query string and cancels filtering and and query string is now empty...does this mean that i will make a new full read to the cloud collection again...so everytime the users types or deletes the query string i iterate through all the collection again?*
Execuse me if the question is too long...but it is much closer to theoretical concepts rather than code...
any clarifications would be much appreciated...
many thanks.
What I suggest is querying the result only if the user has entered some specific number of characters and all of them are valid.
Like, in my case, I ensure that the user has entered at least 3 characters and all of them are alphabets or numbers.

Query specific field in Firestore snapshot

I created this stream that gets a snapshot of a specific document. Inside the document theres an array called tripAttractions that I want to build into a list. The question is, how do I access this specific array from the snapshot?
Stream<QuerySnapshot> getAttractions(BuildContext context) async* {
Firestore.instance
.collection('trips')
.document(documentId)
.snapshots(includeMetadataChanges: true);
}
The list shows how I'm trying to access the snapshot tripAttractions data but this doesn't work.
ListView.builder(
itemCount: snapshot.data['tripAttractions'].length,
itemBuilder: (BuildContext context, int index) =>
tripAttractionsCards(
context, index, snapshot.data['tripAttractions']),
);
Array inside the firestore document
Most likely you're just missing the array accessor in the item builder:
ListView.builder(
itemCount: snapshot.data['tripAttractions'].length,
itemBuilder: (BuildContext context, int index) =>
tripAttractionsCards(
context, index, snapshot.data['tripAttractions'][index]),
);
Ok the issue was that incorrectly called QuerySnapshot. I'm supposed to use DocumentSnapshot since I'm calling a document directly. Also I had to add yield* to return the data from firestore.
Stream<DocumentSnapshot> getAttractions(BuildContext context) async* {
yield* Firestore.instance
.collection('trips')
.document(documentId)
.snapshots(includeMetadataChanges: true);
}
I was able to troubleshoot this by checking if the snapshot was sending any data by adding an if statement inside the streamBuilder.
if (!snapshot.hasData) return Text("Loading");

Google Firestore how to query multiple sub-collections in Flutter

I am using Firestore in my project - for each user I have document and in each document I have subcollections (recent, planned).
items
document (uid)
sub-collection (recent)
doc1 (several fields)
doc2 ...
sub-collection (planned)
doc1 ...
doc2 ...
Currently, I am using two StreamBuilders in my app - one for getRecent getter and second for getPlanned, but I think, that this is not a good approach.
Is out there any better way, how to query these two sub collections (in the future, there will be more..) in one stream? (Or how to optimize my program to not use much bandwidth)
Here is example of my code in Flutter
// Get planned (getter in Data service class)
Stream<dynamic> get getPlanned {
return data
.document(uid)
.collection("planned")
.snapshots()
.map(_snapshotToPlanned);
}
// Stream Builder in Home widget
StreamBuilder(
stream: Data(uid: user.uid).getPlanned,
builder: (context, snapshot) {
if (snapshot.hasData) {
plannedTrips = snapshot.data;
if (snapshot.data.length == 0) return NothingWidget();
return PageView.builder(
pageSnapping: true,
controller: _pageController,
scrollDirection: Axis.horizontal,
itemCount: plannedTrips.length,
itemBuilder: (context, i) {
return PlannedTrip(planned: plannedTrips[i]);
},
);
} else ...
Do you have any idea, how to deal with this problem?
Thanks!

Resources