Solution required for firebase query limit of <=10

Viewed 573

I have a small function for my application that fetches cart items from firebase and displays them. The application was working fine till Flutter version 2.5.2 but when I moved 2.8.1 the application crashed if the number of items in the cart collection are more than 10 because of the firebase query limit of "<=10"

[Unhandled promise rejection: FirebaseError: Invalid Query. 'in' filters support a maximum of 10 elements in the value array.]

If you can suggest a workaround for this, that would be really helpful.

I am currently using the following functions to get data from firebase.

 ///Get products
  Stream<List<Product>> getProducts(List<CartItem> cartItems) {
    List<String> ids = cartItems.map((e) => e.reference).toList();

    return database.getDataWithArrayCondition('products', ids).map(
        (snapshots) => snapshots.docs
            .map((snapshot) => Product.fromMap(
                snapshot.data() as Map<String, dynamic>, snapshot.id))
            .toList());
  }



  ///Get cart items
  Stream<List<CartItem>> _getCartItems() {
    return database
        .getDataFromCollection("users/${auth.uid}/cart")
        .map((snapshots) => snapshots.docs.map((snapshot) {
              return CartItem.fromMap(
                  snapshot.data() as Map<String, dynamic>, snapshot.id);
            }).toList());
  }

Error

The following assertion was thrown building StreamBuilder<List<CartItem>>(dirty, state: _StreamBuilderBaseState<List<CartItem>, AsyncSnapshot<List<CartItem>>>#978e8):
'in' filters support a maximum of 10 elements in the value [List].
'package:cloud_firestore/src/query.dart':
Failed assertion: line 702 pos 11: '(value as List).length <= 10'

The relevant error-causing widget was: 
  StreamBuilder<List<CartItem>> StreamBuilder:file:///Users/Osamah/Downloads/grocery_user_fixed-master/lib/ui/home/cart/cart.dart:265:12
When the exception was thrown, this was the stack: 
#2      _JsonQuery.where (package:cloud_firestore/src/query.dart:702:11)
#3      FirestoreDatabase.getDataWithArrayCondition (package:grocery/services/database.dart:150:10)
#4      CartBloc.getProducts (package:grocery/blocs/cart_bloc.dart:32:21)
#5      _CartState.build.<anonymous closure> (package:grocery/ui/home/cart/cart.dart:292:37)
#6      StreamBuilder.build (package:flutter/src/widgets/async.dart:442:81)
#7      _StreamBuilderBaseState.build (package:flutter/src/widgets/async.dart:124:48)
#8      StatefulElement.build (package:flutter/src/widgets/framework.dart:4705:27)
#9      ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:4588:15)
#10     StatefulElement.performRebuild (package:flutter/src/widgets/framework.dart:4763:11)
#11     Element.rebuild (package:flutter/src/widgets/framework.dart:4311:5)
#12     BuildOwner.buildScope (package:flutter/src/widgets/framework.dart:2578:33)
#13     WidgetsBinding.drawFrame (package:flutter/src/widgets/binding.dart:882:21)
#14     RendererBinding._handlePersistentFrameCallback (package:flutter/src/rendering/binding.dart:363:5)
#15     SchedulerBinding._invokeFrameCallback (package:flutter/src/scheduler/binding.dart:1145:15)
#16     SchedulerBinding.handleDrawFrame (package:flutter/src/scheduler/binding.dart:1082:9)
#17     SchedulerBinding._handleDrawFrame (package:flutter/src/scheduler/binding.dart:996:5)
#21     _invoke (dart:ui/hooks.dart:150:10)
#22     PlatformDispatcher._drawFrame (dart:ui/platform_dispatcher.dart:270:5)
#23     _drawFrame (dart:ui/hooks.dart:114:31)
(elided 5 frames from class _AssertionError and dart:async)

Here is the relevant code

 return StreamBuilder<List<CartItem>>(
      stream: widget.bloc.cartItems,
      builder: (context, snapshot) {
        if (snapshot.hasData && snapshot.data != null) {
          List<CartItem> cartItems = snapshot.data!;
          if (cartItems.length == 0) {
            return FadeIn(
              child: Column(
                  mainAxisAlignment: MainAxisAlignment.center,
                  crossAxisAlignment: CrossAxisAlignment.center,
                  children: [
                    SvgPicture.asset(
                      'images/state_images/empty_cart.svg',
                      width: width * 0.5,
                      fit: BoxFit.cover,
                    ),
                    Padding(
                      padding: EdgeInsets.only(top: 30),
                      child: Texts.headline3(
                          'Nothing found here\nGo and enjoy shopping!',
                          themeModel.accentColor,
                          alignment: TextAlign.center),
                    )
                  ]),
            );
          } else {
            return StreamBuilder<List<Product>>(
                stream: widget.bloc.getProducts(cartItems),
                builder: (context, snapshot) {
                  if (snapshot.hasData && snapshot.data != null) {
                    List<Product> products = snapshot.data!;
                    cartItems = cartItems.where((cartItem) {
                      if (products.where((product) {
                            if (cartItem.reference == product.reference) {
                              cartItem.product = product;
                              return true;
                            } else {
                              return false;
                            }
                          }).length ==
                          0) {
                        return false;
                      } else {
                        return true;
                      }
                    }).toList();
1 Answers

One solution is to partition your array into sets of 10.

And iterate through each partition and make a firebase in query.

Aggregate and add results back to a list.

Here is some psuedo-code:

function queryMoreThan10(query, list) {
  partitions; // this is an array of arrays
  
  for ( i = 0; i < list.size; i += 10) {
      // this aggregates and transforms the array into 10 elements 
      // for each partition
      partitions.add(list.slice(i, i + partition));
  }

  // Now make the query for each partition and map-reduce to 
  // obtain a unified list of results
}
Related