Solution required for firebase query limit of <=10

205

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
}
Share:
205
Osamah Atif
Author by

Osamah Atif

Updated on January 03, 2023

Comments

  • Osamah Atif
    Osamah Atif over 1 year

    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();