During a project, I had to use the IN operator for a Firestore query to filter the documents. From previous projects, I knew that using this with a filter list larger than 30 would result in an error. Since this error comes from Firebase, we have no control over it.
Now, there were two possible ways to solve this problem.
Option 1: Write a custom action that loops through the filter list in a for
loop, reads the matching Firestore document each time, and returns all documents as the output.
The problem here, however, was that I wanted to use FlutterFlow's built-in backend query cache instead of building my own solution via an AppState
.
So, Option 2 had to be implemented.
First, I created a Firestore query in a container that I knew would return no results—just to avoid unnecessarily reading a document. I also set up the corresponding app-level query cache so that it would be available and could be used in a custom action (to see on the image). If desired, this can also be done in a component or page that is never actually used. The Firestore query doesn’t ever need to be executed—the only important part is that FlutterFlow generates the corresponding cache variable in the code.
Now, with the following action code I could leverage FlutterFlow's backend cache logic and call it when loading the page.
Future<List<ProductsRecord>> getUserUsedProducts(
List<SubscriptionsRecord> userSubscriptions) async {
return FFAppState().usersUsedProducts(
uniqueQueryKey: valueOrDefault<String>(currentUserUid, 'user123456'),
overrideCache: false,
requestFn: () async {
final products = <ProductsRecord>[];
for (final sub in userSubscriptions) {
final product = await queryProductsRecordOnce(
queryBuilder: (q) => q.where('product_id', isEqualTo: sub.productId),
).then((list) => list.firstOrNull);
if (product != null) products.add(product);
}
return products;
},
);
}
The parameter requestFn
is a function used to execute the database query. In my case, I could use the previously described for
loop to fetch the individual documents, add them to a list and then return that list. Of course, this part needs to be adapted to your specific requirements.
When the action is executed, it first checks whether the cache is populated. If not, requestFn
is called.
The overrideCache
parameter can be used to force a refresh of the cache, even if it is already filled.