Using StreamBuilder instead of FeatureBuilder to avoid whereIn 10 Limit in Firestore
Solution 1
First, the "whereIn
limit of 10" is a Firestore hard constraint. It means that whether you use FutureBuilder
or StreamBuilder
, that limit-of-10 constraint still applies.
Now, if you still want to switch to StreamBuilder
StreamBuilder<QuerySnapshot>(
stream: _yourStream,
builder: (BuildContext context, AsyncSnapshot<QuerySnapshot> snapshot) {
And you will need to adjust this to become a Stream
await firestore
.collection('contents')
.where('timestamp', whereIn: d)
.get()
.then((QuerySnapshot snap) {
filteredData = snap.docs;
});
You need to change the .get( )
to .snapshots()
and need to use snapshot.data.docs.map
More details in the Stream example here https://firebase.flutter.dev/docs/firestore/usage
EDIT:
Concerning the 10-items hard constraint: Depending on your specific project you can either:
-
query multiple times then merge locally in your application (while removing duplicates), or
-
another solution is to do some filtering client-side in your application (basically, bring more from FireStore and filter in flutter), or
-
another solution is to create new fields that are a combination of other fields (for example in your case, it could be "month" or "weekNumber" so instead of 7 days you have a week in 1 condition)
Solution 2
Firestore does not allow you to make an array membership query with more that 10 records. If you check this documentation you will see that:
The where() method takes three parameters: a field to filter on, a comparison operator, and a value. The ‘in’ operator out of the many comparison operators of where() method is used to combine upto 10 equality (==) clauses on the same field with a logical OR.
If you use more than 10 records, Firestore will not accept it and you will get an error. I suggest that you break the “d” array into multiple arrays with a max of 10 records to make the query, this will allow Firestore to operate the whereIn within its limits and it will work.
Also a similar stackoverflow thread suggests using chunkSizeCollection which splits the array in chunks of 10 elements.
Comments
-
ARUN BALAJI over 1 year
I want to fetch the particular users' liked posts and show them in Staggered grid view. I can able to do this in FutureBuilder. But in FutureBuilder because of the
whereIn
limit of 10, I cannot fetch more. I know it can be implemented using StreamBuilder.But I am helpless and don't know how to do it.This is my firebase collection.
Below is the code for fetch data:
final FirebaseFirestore firestore = FirebaseFirestore.instance; getData() async { SharedPreferences sp = await SharedPreferences.getInstance(); String _uid = sp.getString('uid'); final DocumentReference ref = firestore.collection('users').doc(_uid); DocumentSnapshot snap = await ref.get(); List d = snap['loved items']; List filteredData = []; if (d.isNotEmpty) { await firestore .collection('contents') .where('timestamp', whereIn: d) .get() .then((QuerySnapshot snap) { filteredData = snap.docs; }); } notifyListeners(); return filteredData; }
This is FutureBuilder Code:
body: sb.guestUser == true ? EmptyPage( icon: FontAwesomeIcons.heart, title: 'No wallpapers found.\n Sign in to access this feature', ) : FutureBuilder( future: context.watch<BookmarkBloc>().getData(), builder: (BuildContext context, AsyncSnapshot snapshot) { if (snapshot.hasData) { if (snapshot.data.length == 0) return EmptyPage( icon: FontAwesomeIcons.heart, title: 'No wallpapers found', ); return _buildList(snapshot); } else if (snapshot.hasError) { return Center( child: Text(snapshot.error), ); } return Center( child: CupertinoActivityIndicator(), ); }, ), ), ); } Widget _buildList(snapshot) { return StaggeredGridView.countBuilder( crossAxisCount: 4, itemCount: snapshot.data.length, itemBuilder: (BuildContext context, int index) { List d = snapshot.data; return InkWell( child: Stack( children: <Widget>[ Hero( tag: 'bookmark$index', child: cachedImage(d[index]['image url'])), Positioned( bottom: 15, left: 12, child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: <Widget>[ Text( d[index]['category'], style: TextStyle(color: Colors.white, fontSize: 18), ) ], ), ), Positioned( right: 10, top: 20, child: Row( children: [ Icon(Icons.favorite, color: Colors.white.withOpacity(0.5), size: 25), Text( d[index]['loves'].toString(), style: TextStyle( color: Colors.white.withOpacity(0.7), fontSize: 16, fontWeight: FontWeight.w600), ), ], ), ), ], ), onTap: () { Navigator.push( context, MaterialPageRoute( builder: (context) => DetailsPage( tag: 'bookmark$index', imageUrl: d[index]['image url'], catagory: d[index]['category'], timestamp: d[index]['timestamp'], ))); }, ); }, staggeredTileBuilder: (int index) => new StaggeredTile.count(2, index.isEven ? 4 : 3), mainAxisSpacing: 10, crossAxisSpacing: 10, padding: EdgeInsets.all(15), ); } }
I referred internet and I don't exactly know how to convert FutureBuilder into StreamBuilder which returns a list of snapshots. Is there any other way to do it ?
-
Canada2000 over 2 yearsLet me know if you need something else
-
ARUN BALAJI over 2 yearsThank you for answering. I tried your suggestion. But as you said even if i use streamBuilder it throws same error
'in' filters support a maximum of 10 elements in the value
Is there other way to solve this ? I want to fetch them all. -
Canada2000 over 2 yearsNo, the 10 items is a hard constraint. I will add some hints now about that in my answer. Please consider "accepting" my answer if you think it responded to your question