firebase / flutterfire

🔥 A collection of Firebase plugins for Flutter apps.
https://firebase.google.com/docs/flutter/setup
BSD 3-Clause "New" or "Revised" License
8.68k stars 3.97k forks source link

[cloud_firestore]: invalid-argument when making a query with "where" and "startAfterDocument" #13478

Closed Lukas-Ldc closed 2 weeks ago

Lukas-Ldc commented 2 weeks ago

Is there an existing issue for this?

Which plugins are affected?

Other

Which platforms are affected?

Android

Description

I am trying to make a query with Flutter and Firestore. The goal of the query is to filter with where() and because I paginate my data, I also need to use startAfterDocument(). So I expect to be able to use both in the same query. What actually happens, is that when I am making a query with both it does not work, but when I use them separately, it works.

This query works :

snapshot = await FirebaseFirestore.instance .collection('advertisement') .where("price", isGreaterThanOrEqualTo: critMinimumPrice) .orderBy("title") .limit(limit) .get();

This query works :

snapshot = await FirebaseFirestore.instance .collection('advertisement') .orderBy("title") .startAfterDocument(_lastRequestedDoc!) .limit(limit) .get();

This query does not works :

snapshot = await FirebaseFirestore.instance .collection('advertisement') .where("price", isGreaterThanOrEqualTo: critMinimumPrice) .orderBy("title") .startAfterDocument(_lastRequestedDoc!) .limit(limit) .get(); I get this error : E/flutter ( 5344): [ERROR:flutter/runtime/dart_vm_initializer.cc(41)] Unhandled Exception: [cloud_firestore/invalid-argument] Client specified an invalid argument. Note that this differs from failed-precondition. invalid-argument indicates arguments that are problematic regardless of the state of the system (e.g., an invalid field name).

Reproducing the issue

I have a Flutter project and I configured it with Firestore. I am able to reproduce the error with the two following files:

Expand main.dart snippet
```import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:firebase_core/firebase_core.dart'; import 'package:my_generic_app_name/model/search_ad.dart'; import 'firebase_options.dart'; void main() async { WidgetsFlutterBinding.ensureInitialized(); await SystemChrome.setPreferredOrientations([DeviceOrientation.portraitUp]); await Firebase.initializeApp(options: DefaultFirebaseOptions.currentPlatform); runApp(const MyApp()); } class MyApp extends StatefulWidget { const MyApp({super.key}); @override State createState() => _MyAppState(); } class _MyAppState extends State { @override void initState() { super.initState(); } void test() async { print("test() start"); SearchAdvertisementModel searchAdvertisementModel = SearchAdvertisementModel(); await searchAdvertisementModel.getAdsFromFirestore(null, 10); await searchAdvertisementModel.getAdsFromFirestore(null, 10); print("test() end"); } @override Widget build(BuildContext context) { return MaterialApp(home: ElevatedButton(onPressed: test, child: const Text("test"))); } } ```
Expand search_ad.dart snippet
```import 'dart:math'; import 'package:cloud_firestore/cloud_firestore.dart'; class SearchAdvertisementModel { int critMinimumPrice = 0; DocumentSnapshot? _lastRequestedDoc; Future>> getAdsFromFirestore(DocumentSnapshot? startAfterDoc, int limit) async { //_lastRequestedDoc = startAfterDoc; QuerySnapshot> snapshot; print("lastReqDoc: " + _lastRequestedDoc.toString()); if (_lastRequestedDoc == null) { snapshot = await FirebaseFirestore.instance .collection('advertisement') .where("price", isGreaterThanOrEqualTo: critMinimumPrice) .orderBy("title") .limit(limit) .get(); } else { snapshot = await FirebaseFirestore.instance .collection('advertisement') .where("price", isGreaterThanOrEqualTo: critMinimumPrice) .orderBy("title") .startAfterDocument(_lastRequestedDoc!) .limit(limit) .get(); } print("results size: " + snapshot.size.toString()); _lastRequestedDoc = snapshot.docs[snapshot.size - 1]; await Future.delayed(const Duration(milliseconds: 500)); return snapshot; } Future>> addInFirestore() async { int price = Random().nextInt(1000); Future>> returned = FirebaseFirestore.instance .collection('advertisement') .add({"uid": "oagwmzSOfxcBbdyAciN80XfXrDi2", "title": "title ${price.toString()}", "price": price}); return returned; } } ```

The data in Firestore: db_1

The indexes in Firestore: db_2

Firebase Core version

3.6.0

Flutter Version

3.24.3

Relevant Log Output

Restarted application in 532ms.
D/EGL_emulation( 5344): app_time_stats: avg=3940.52ms min=1.29ms max=153597.62ms count=39
D/EGL_emulation( 5344): app_time_stats: avg=1514.35ms min=1514.35ms max=1514.35ms count=1
I/flutter ( 5344): test() start
I/flutter ( 5344): lastReqDoc: null
W/DynamiteModule( 5344): Local module descriptor class for com.google.android.gms.providerinstaller.dynamite not found.
I/DynamiteModule( 5344): Considering local module com.google.android.gms.providerinstaller.dynamite:0 and remote module com.google.android.gms.providerinstaller.dynamite:0
W/ProviderInstaller( 5344): Failed to load providerinstaller module: No acceptable module com.google.android.gms.providerinstaller.dynamite found. Local version is 0 and remote version is 0.
W/ProviderInstaller( 5344): Failed to report request stats: com.google.android.gms.common.security.ProviderInstallerImpl.reportRequestStats [class android.content.Context, long, long]
D/TrafficStats( 5344): tagSocket(160) with statsTag=0xffffffff, statsUid=-1
I/flutter ( 5344): results size: 10
I/flutter ( 5344): lastReqDoc: Instance of '_JsonQueryDocumentSnapshot'
E/flutter ( 5344): [ERROR:flutter/runtime/dart_vm_initializer.cc(41)] Unhandled Exception: [cloud_firestore/invalid-argument] Client specified an invalid argument. Note that this differs from failed-precondition. invalid-argument indicates arguments that are problematic regardless of the state of the system (e.g., an invalid field name).
E/flutter ( 5344): #0      FirebaseFirestoreHostApi.queryGet (package:cloud_firestore_platform_interface/src/pigeon/messages.pigeon.dart:1153:7)
E/flutter ( 5344): <asynchronous suspension>
E/flutter ( 5344): #1      MethodChannelQuery.get (package:cloud_firestore_platform_interface/src/method_channel/method_channel_query.dart:118:11)
E/flutter ( 5344): <asynchronous suspension>
E/flutter ( 5344): #2      _JsonQuery.get (package:cloud_firestore/src/query.dart:426:9)
E/flutter ( 5344): <asynchronous suspension>
E/flutter ( 5344): #3      SearchAdvertisementModel.getAdsFromFirestore (package:my_generic_app_name/model/search_ad.dart:20:18)
E/flutter ( 5344): <asynchronous suspension>
E/flutter ( 5344): #4      _MyAppState.test (package:my_generic_app_name/main_test.dart:31:5)
E/flutter ( 5344): <asynchronous suspension>
E/flutter ( 5344):

Flutter dependencies

Expand Flutter dependencies snippet
```yaml Dart SDK 3.5.3 Flutter SDK 3.24.3 my_generic_app_name 1.0.0+1 dependencies: - cloud_firestore 5.4.4 [cloud_firestore_platform_interface cloud_firestore_web collection firebase_core firebase_core_platform_interface flutter meta] - cupertino_icons 1.0.8 - firebase_auth 5.3.1 [firebase_auth_platform_interface firebase_auth_web firebase_core firebase_core_platform_interface flutter meta] - firebase_core 3.6.0 [firebase_core_platform_interface firebase_core_web flutter meta] - flutter 0.0.0 [characters collection material_color_utilities meta vector_math sky_engine] - flutter_localizations 0.0.0 [flutter intl characters clock collection material_color_utilities meta path vector_math] - intl 0.19.0 [clock meta path] dev dependencies: - flutter_lints 4.0.0 [lints] - flutter_test 0.0.0 [flutter test_api matcher path fake_async clock stack_trace vector_math leak_tracker_flutter_testing async boolean_selector characters collection leak_tracker leak_tracker_testing material_color_utilities meta source_span stream_channel string_scanner term_glyph vm_service] transitive dependencies: - _flutterfire_internals 1.3.44 [collection firebase_core firebase_core_platform_interface flutter meta] - async 2.11.0 [collection meta] - boolean_selector 2.1.1 [source_span string_scanner] - characters 1.3.0 - clock 1.1.1 - cloud_firestore_platform_interface 6.4.3 [_flutterfire_internals collection firebase_core flutter meta plugin_platform_interface] - cloud_firestore_web 4.3.2 [_flutterfire_internals cloud_firestore_platform_interface collection firebase_core firebase_core_web flutter flutter_web_plugins] - collection 1.18.0 - fake_async 1.3.1 [clock collection] - firebase_auth_platform_interface 7.4.7 [_flutterfire_internals collection firebase_core flutter meta plugin_platform_interface] - firebase_auth_web 5.13.2 [firebase_auth_platform_interface firebase_core firebase_core_web flutter flutter_web_plugins http_parser meta web] - firebase_core_platform_interface 5.3.0 [collection flutter flutter_test meta plugin_platform_interface] - firebase_core_web 2.18.1 [firebase_core_platform_interface flutter flutter_web_plugins meta web] - flutter_web_plugins 0.0.0 [flutter characters collection material_color_utilities meta vector_math] - http_parser 4.0.2 [collection source_span string_scanner typed_data] - leak_tracker 10.0.5 [clock collection meta path vm_service] - leak_tracker_flutter_testing 3.0.5 [flutter leak_tracker leak_tracker_testing matcher meta] - leak_tracker_testing 3.0.1 [leak_tracker matcher meta] - lints 4.0.0 - matcher 0.12.16+1 [async meta stack_trace term_glyph test_api] - material_color_utilities 0.11.1 [collection] - meta 1.15.0 - path 1.9.0 - plugin_platform_interface 2.1.8 [meta] - sky_engine 0.0.99 - source_span 1.10.0 [collection path term_glyph] - stack_trace 1.11.1 [path] - stream_channel 2.1.2 [async] - string_scanner 1.2.0 [source_span] - term_glyph 1.2.1 - test_api 0.7.2 [async boolean_selector collection meta source_span stack_trace stream_channel string_scanner term_glyph] - typed_data 1.3.2 [collection] - vector_math 2.1.4 - vm_service 14.2.5 - web 1.1.0 ```

Additional context and comments

Expand Flutter doctor snippet
```yaml [✓] Flutter (Channel stable, 3.24.3, on Ubuntu 24.04.1 LTS 6.8.0-45-generic, locale fr_FR.UTF-8) [✓] Android toolchain - develop for Android devices (Android SDK version 35.0.0) [✓] Chrome - develop for the web [✓] Linux toolchain - develop for Linux desktop [✓] Android Studio (version 2024.1) [✓] VS Code (version 1.94.0) [✓] Connected device (3 available) [✓] Network resources • No issues found! ```
SelaseKay commented 2 weeks ago

I'm unable to reproduce this issue. Kindly provide a full sample code replicating this issue.

Lukas-Ldc commented 2 weeks ago

I have provided some code in the "Reproducing the issue" part. You have to click on "Expand x.dart snippet". Is this code enough or do you need something else ?

SelaseKay commented 2 weeks ago

Thanks for sharing. To prevent this error, you have to pass the same field name in both where and orderBy. For more clarification as to why this should be the case, kindly visit order-limit-data

This should resolve your issue:

snapshot = await FirebaseFirestore.instance
.collection('advertisement')
.where("foo", isGreaterThanOrEqualTo: critMinimumPrice)
.orderBy("foo")
.startAfterDocument(_lastRequestedDoc!)
.limit(limit)
.get();

where foo represents your field name

Lukas-Ldc commented 2 weeks ago

Thank you for your help @SelaseKay, indeed your solution works. So if I understood correctly, every query with a where filter using one of these < , <= , > , >= on a field must have an orderby in the same query on this same field ?

SelaseKay commented 2 weeks ago

Correct.