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.52k stars 3.92k forks source link

πŸ› [cloud_firestore] Querysnapshot does not always sending streams when using where statement on timestamp #6654

Closed Hwan-seok closed 2 years ago

Hwan-seok commented 3 years ago

Bug report

I am using cloud firestore on chat room. The chats are sorted by timestamp and it should only appear when the sentDateTime is passed to device's time

So I send(add document) chat message and it should appear right away because I set sentDateTime to DateTime.now()

I did query as follows and when using where query on it, the querysnapshot is not updated. sentDateTime is timestamp

FirebaseFirestore.instance
.collection('messages')
.withConverter<ChatMessage>(
        fromFirestore: (snapshots, _) => ChatMessage.fromJson(snapshots.data()!),
        toFirestore: (chatMessage, _) => chatMessage.toJson()
)
.where(                   <-- snapshot always updated when this where statement is omitted
          "sentDateTime",
          isLessThanOrEqualTo: DateTime.now(), 
        )
.orderBy('sentDateTime', descending: true)
.snapshots();

As I mentioned above, when where statement is omitted, it works as expected(snapshot always updating)

What I tried

  1. changed isLessThanOrEqualTo to isGreaterThan....
    • it updates stream as expected But cannot get LESS THAN CURRENT
  2. changed DateTime.now() to DateTime.now().add(Duration(second:100 and 10 and 3)).
    • when added 100 seconds, it works 100% and when 10s, it works often and 3s, it fewly works .
  3. includeMetaDataChanged : true
    • not worked

Expected behavior

Stream is always updated



Flutter doctor

Run flutter doctor and paste the output below:

Click To Expand ``` [βœ“] Flutter (Channel stable, 2.2.1, on macOS 11.4 20F71 darwin-x64, locale ko-KR) β€’ Flutter version 2.2.1 at /Users/lafity111/flutter β€’ Framework revision 02c026b03c (8 weeks ago), 2021-05-27 12:24:44 -0700 β€’ Engine revision 0fdb562ac8 β€’ Dart version 2.13.1 [βœ“] Android toolchain - develop for Android devices (Android SDK version 30.0.3) β€’ Android SDK at /Users/lafity111/Library/Android/sdk β€’ Platform android-30, build-tools 30.0.3 β€’ Java binary at: /Applications/Android Studio.app/Contents/jre/jdk/Contents/Home/bin/java β€’ Java version OpenJDK Runtime Environment (build 11.0.8+10-b944.6916264) β€’ All Android licenses accepted. [βœ“] Xcode - develop for iOS and macOS β€’ Xcode at /Applications/Xcode.app/Contents/Developer β€’ Xcode 12.5, Build version 12E262 β€’ CocoaPods version 1.10.1 [βœ“] Chrome - develop for the web β€’ Chrome at /Applications/Google Chrome.app/Contents/MacOS/Google Chrome [βœ“] Android Studio (version 4.2) β€’ Android Studio at /Applications/Android Studio.app/Contents β€’ Flutter plugin can be installed from: πŸ”¨ https://plugins.jetbrains.com/plugin/9212-flutter β€’ Dart plugin can be installed from: πŸ”¨ https://plugins.jetbrains.com/plugin/6351-dart β€’ Java version OpenJDK Runtime Environment (build 11.0.8+10-b944.6916264) [βœ“] VS Code (version 1.57.1) β€’ VS Code at /Applications/Visual Studio Code.app/Contents β€’ Flutter extension version 3.24.0 [βœ“] Connected device (3 available) β€’ SM G988N (mobile) β€’ R3CN20B1TQV β€’ android-arm64 β€’ Android 11 (API 30) β€’ μ‘°λΌν”Όν‹°μ˜ iPhone (mobile) β€’ 00008030-001948CC0EBB802E β€’ ios β€’ iOS 14.6 β€’ Chrome (web) β€’ chrome β€’ web-javascript β€’ Google Chrome 91.0.4472.114 β€’ No issues found! ```

Flutter dependencies

Run flutter pub deps -- --style=compact and paste the output below:

Click To Expand ``` Dart SDK 2.13.1 Flutter SDK 2.2.1 lafity_app 2.4.0+135 dependencies: - agora_rtc_engine 4.0.2 [flutter flutter_web_plugins json_annotation] - agora_rtm 1.0.0-rc.1 [flutter] - another_flushbar 1.10.23 [pedantic flutter] - cached_network_image 3.0.0 [flutter flutter_cache_manager octo_image] - camera 0.8.1+3 [camera_platform_interface flutter pedantic quiver] - cloud_firestore 2.4.0 [cloud_firestore_platform_interface cloud_firestore_web firebase_core firebase_core_platform_interface flutter meta] - crypto 3.0.1 [collection typed_data] - device_info 2.0.2 [flutter device_info_platform_interface] - dio 4.0.0 [http_parser path] - easy_debounce 2.0.0 [flutter] - enum_to_string 2.0.1 - equatable 2.0.3 [collection meta] - extended_image 4.1.0 [extended_image_library flutter meta] - firebase_analytics 8.1.2 [firebase_analytics_platform_interface firebase_analytics_web firebase_core flutter meta] - firebase_auth 1.4.1 [firebase_auth_platform_interface firebase_auth_web firebase_core firebase_core_platform_interface flutter meta] - firebase_core 1.4.0 [firebase_core_platform_interface firebase_core_web flutter meta] - firebase_crashlytics 2.0.6 [firebase_core firebase_core_platform_interface firebase_crashlytics_platform_interface flutter stack_trace] - firebase_dynamic_links 2.0.6 [firebase_core flutter] - firebase_messaging 10.0.2 [firebase_core firebase_core_platform_interface firebase_messaging_platform_interface firebase_messaging_web flutter meta] - firebase_remote_config 0.10.0+2 [firebase_core firebase_core_platform_interface firebase_remote_config_platform_interface flutter] - flick_video_player 0.3.1 [flutter video_player provider wakelock] - flutter 0.0.0 [characters collection meta typed_data vector_math sky_engine] - flutter_facebook_auth 3.5.0 [flutter flutter_facebook_auth_platform_interface flutter_facebook_auth_web] - flutter_keyboard_visibility 5.0.2 [meta flutter_keyboard_visibility_platform_interface flutter_keyboard_visibility_web flutter] - flutter_local_notifications 6.0.0 [flutter flutter_local_notifications_platform_interface platform timezone] - flutter_localizations 0.0.0 [flutter intl characters clock collection meta path typed_data vector_math] - flutter_naver_login 1.2.2 [flutter] - flutter_screenutil 5.0.0+2 [flutter] - flutter_spinkit 5.0.0 [flutter] - font_awesome_flutter 9.1.0 [flutter] - get 4.1.4 [flutter] - google_sign_in 5.0.4 [google_sign_in_platform_interface google_sign_in_web flutter meta] - http 0.13.3 [async http_parser meta path pedantic] - image 3.0.2 [archive meta xml] - image_picker 0.8.0+4 [flutter flutter_plugin_android_lifecycle image_picker_platform_interface image_picker_for_web] - intl 0.17.0 [clock path] - json_annotation 4.0.1 - kakao_flutter_sdk 0.6.3 [flutter json_annotation dio shared_preferences platform package_info] - liquid_progress_indicator_ns 1.0.0 [flutter] - lottie 1.1.0 [archive characters flutter path vector_math] - meta 1.3.0 - package_info 2.0.2 [flutter] - permission_handler 8.1.1 [flutter meta permission_handler_platform_interface] - photo_manager 1.2.2 [flutter] - readmore 2.1.0 [flutter] - retrofit 2.0.0 [dio meta] - share 2.0.4 [meta mime flutter] - shared_preferences 2.0.6 [meta flutter shared_preferences_platform_interface shared_preferences_linux shared_preferences_macos shared_preferences_web shared_preferences_windows] - shimmer 2.0.0 [flutter] - sign_in_with_apple 3.0.0 [flutter meta] - simple_animations 3.1.1 [flutter supercharged pedantic] - store_redirect 2.0.0 [flutter] - table_calendar 3.0.0 [flutter intl simple_gesture_detector] - version 2.0.0 - video_player 2.1.6 [flutter flutter_test meta video_player_platform_interface video_player_web] - wakelock 0.5.2 [flutter meta wakelock_macos wakelock_platform_interface wakelock_web wakelock_windows] - webview_flutter 2.0.8 [flutter] dev dependencies: - build_runner 2.0.5 [args async analyzer build build_config build_daemon build_resolvers build_runner_core code_builder collection crypto dart_style frontend_server_client glob graphs http_multi_server io js logging meta mime package_config path pedantic pool pub_semver pubspec_parse shelf shelf_web_socket stack_trace stream_transform timing watcher web_socket_channel yaml] - flutter_test 0.0.0 [flutter test_api path fake_async clock stack_trace vector_math async boolean_selector characters charcode collection matcher meta source_span stream_channel string_scanner term_glyph typed_data] - json_serializable 4.1.3 [analyzer build build_config collection json_annotation meta path source_gen] - retrofit_generator 2.0.0 [dio source_gen built_collection code_builder tuple retrofit analyzer dart_style build] transitive dependencies: - _fe_analyzer_shared 22.0.0 [meta] - analyzer 1.7.1 [_fe_analyzer_shared cli_util collection convert crypto glob meta package_config path pub_semver source_span watcher yaml pedantic] - archive 3.1.2 [crypto path] - args 2.1.1 - async 2.6.1 [meta collection] - boolean_selector 2.1.0 [source_span string_scanner] - build 2.0.2 [analyzer async convert crypto glob logging meta path] - build_config 1.0.0 [checked_yaml json_annotation path pubspec_parse yaml] - build_daemon 3.0.0 [built_collection built_value http_multi_server logging pedantic path pool shelf shelf_web_socket stream_transform watcher web_socket_channel] - build_resolvers 2.0.3 [analyzer async build crypto graphs logging path package_config pool pub_semver stream_transform yaml] - build_runner_core 7.0.0 [async build build_config build_resolvers collection convert crypto glob graphs json_annotation logging meta path package_config pedantic pool timing watcher yaml] - built_collection 5.1.0 - built_value 8.1.0 [built_collection collection fixnum meta] - camera_platform_interface 2.0.1 [flutter meta plugin_platform_interface cross_file stream_transform] - characters 1.1.0 - charcode 1.2.0 - checked_yaml 2.0.1 [json_annotation source_span yaml] - cli_util 0.3.1 [meta path] - clock 1.1.0 - cloud_firestore_platform_interface 5.3.0 [collection firebase_core flutter meta plugin_platform_interface] - cloud_firestore_web 2.3.0 [cloud_firestore_platform_interface firebase_core firebase_core_web flutter flutter_web_plugins js] - code_builder 4.0.0 [built_collection built_value collection matcher meta] - collection 1.15.0 - convert 3.0.0 [typed_data] - cross_file 0.3.1+1 [flutter meta] - dart_style 2.0.1 [analyzer args path pub_semver source_span] - device_info_platform_interface 2.0.1 [flutter meta plugin_platform_interface] - extended_image_library 3.1.0 [crypto flutter http_client_helper path path_provider] - fake_async 1.2.0 [clock collection] - ffi 1.1.2 - file 6.1.2 [meta path] - firebase 9.0.1 [http http_parser js] - firebase_analytics_platform_interface 2.0.1 [flutter meta] - firebase_analytics_web 0.3.0+1 [firebase firebase_analytics_platform_interface flutter flutter_web_plugins meta] - firebase_auth_platform_interface 4.3.1 [firebase_core flutter meta plugin_platform_interface] - firebase_auth_web 1.3.1 [firebase_auth_platform_interface firebase_core firebase_core_web flutter flutter_web_plugins http_parser intl js meta] - firebase_core_platform_interface 4.0.1 [collection flutter meta plugin_platform_interface] - firebase_core_web 1.1.0 [firebase_core_platform_interface flutter flutter_web_plugins js meta] - firebase_crashlytics_platform_interface 3.0.6 [collection firebase_core flutter meta plugin_platform_interface] - firebase_messaging_platform_interface 3.0.2 [firebase_core flutter meta plugin_platform_interface] - firebase_messaging_web 2.0.2 [firebase_core firebase_core_web firebase_messaging_platform_interface flutter flutter_web_plugins js meta] - firebase_remote_config_platform_interface 0.3.0+2 [firebase_core flutter meta plugin_platform_interface] - fixnum 1.0.0 - flutter_blurhash 0.6.0 [flutter meta pedantic] - flutter_cache_manager 3.1.2 [clock collection file flutter http path path_provider pedantic rxdart sqflite uuid] - flutter_facebook_auth_platform_interface 2.7.0 [flutter plugin_platform_interface] - flutter_facebook_auth_web 2.6.0+2 [flutter flutter_web_plugins js flutter_facebook_auth_platform_interface] - flutter_keyboard_visibility_platform_interface 2.0.0 [flutter meta plugin_platform_interface] - flutter_keyboard_visibility_web 2.0.0 [flutter_keyboard_visibility_platform_interface flutter_web_plugins flutter] - flutter_local_notifications_platform_interface 4.0.0 [flutter plugin_platform_interface] - flutter_plugin_android_lifecycle 2.0.2 [flutter] - flutter_web_plugins 0.0.0 [flutter js characters collection meta typed_data vector_math] - frontend_server_client 2.1.0 [async path] - glob 2.0.1 [async collection file path pedantic string_scanner] - google_sign_in_platform_interface 2.0.1 [flutter meta quiver] - google_sign_in_web 0.10.0 [google_sign_in_platform_interface flutter flutter_web_plugins meta js] - graphs 2.0.0 - http_client_helper 2.0.2 [http] - http_multi_server 3.0.1 [async] - http_parser 4.0.0 [charcode collection source_span string_scanner typed_data] - image_picker_for_web 2.0.0 [image_picker_platform_interface meta flutter flutter_web_plugins] - image_picker_platform_interface 2.1.0 [flutter meta http plugin_platform_interface] - io 1.0.0 [meta path string_scanner] - js 0.6.3 - logging 1.0.1 - matcher 0.12.10 [stack_trace] - mime 1.0.0 - nested 1.0.0 [flutter] - octo_image 1.0.0+1 [flutter flutter_blurhash] - package_config 2.0.0 [path] - path 1.8.0 - path_provider 2.0.2 [flutter path_provider_platform_interface path_provider_macos path_provider_linux path_provider_windows] - path_provider_linux 2.0.0 [path xdg_directories path_provider_platform_interface flutter] - path_provider_macos 2.0.0 [flutter] - path_provider_platform_interface 2.0.1 [flutter meta platform plugin_platform_interface] - path_provider_windows 2.0.1 [path_provider_platform_interface meta path flutter ffi win32] - pedantic 1.11.1 - permission_handler_platform_interface 3.6.0 [flutter meta plugin_platform_interface] - petitparser 4.1.0 [meta] - platform 3.0.0 - plugin_platform_interface 2.0.0 [meta] - pool 1.5.0 [async stack_trace] - process 4.2.1 [file path platform] - provider 5.0.0 [collection flutter nested] - pub_semver 2.0.0 [collection] - pubspec_parse 1.0.0 [checked_yaml collection json_annotation pub_semver yaml] - quiver 3.0.1 [matcher] - rxdart 0.27.1 - shared_preferences_linux 2.0.0 [flutter file meta path path_provider_linux shared_preferences_platform_interface] - shared_preferences_macos 2.0.0 [shared_preferences_platform_interface flutter] - shared_preferences_platform_interface 2.0.0 [flutter] - shared_preferences_web 2.0.0 [shared_preferences_platform_interface flutter flutter_web_plugins meta] - shared_preferences_windows 2.0.0 [shared_preferences_platform_interface flutter file meta path path_provider_platform_interface path_provider_windows] - shelf 1.1.4 [async collection http_parser path stack_trace stream_channel] - shelf_web_socket 1.0.1 [shelf stream_channel web_socket_channel] - simple_gesture_detector 0.2.0 [flutter] - sky_engine 0.0.99 - source_gen 1.0.2 [analyzer async build dart_style glob meta path pedantic source_span yaml] - source_span 1.8.1 [collection path term_glyph] - sqflite 2.0.0+3 [flutter sqflite_common path] - sqflite_common 2.0.0+2 [synchronized path meta] - stack_trace 1.10.0 [path] - stream_channel 2.1.0 [async] - stream_transform 2.0.0 - string_scanner 1.1.0 [charcode source_span] - supercharged 2.0.0 [supercharged_dart flutter] - supercharged_dart 2.0.0 - synchronized 3.0.0 - term_glyph 1.2.0 - test_api 0.3.0 [async boolean_selector collection meta path source_span stack_trace stream_channel string_scanner term_glyph matcher] - timezone 0.7.0 [path] - timing 1.0.0 [json_annotation] - tuple 2.0.0 [quiver] - typed_data 1.3.0 [collection] - uuid 3.0.4 [crypto] - vector_math 2.1.0 - video_player_platform_interface 4.1.0 [flutter meta flutter_test] - video_player_web 2.0.1 [flutter flutter_web_plugins meta video_player_platform_interface] - wakelock_macos 0.1.0+1 [flutter flutter_web_plugins wakelock_platform_interface] - wakelock_platform_interface 0.2.1+1 [flutter meta] - wakelock_web 0.2.0+1 [flutter flutter_web_plugins js wakelock_platform_interface] - wakelock_windows 0.1.0 [flutter wakelock_platform_interface win32] - watcher 1.0.0 [async path pedantic] - web_socket_channel 2.1.0 [async crypto stream_channel] - win32 2.2.3 [ffi] - xdg_directories 0.2.0 [meta path process] - xml 5.1.2 [collection meta petitparser] - yaml 3.1.0 [collection source_span string_scanner] ```

markusaksli-nc commented 3 years ago

Hi @Hwan-seok So do you want the value of DateTime.now() in the where of the query to be updated? Because right now you are only passing in the value at the time the stream is created. If you want to run this query on each snapshot you need to do some local filtering. Thank you

Hwan-seok commented 3 years ago

Hi @markusaksli-nc, Thanks for the fast response!

I missed out Why I want to use where clause on my query(Because it is weird behavior). For business purposes, I insert the chat document which timestamp is after a few seconds at that time in the server.

So the user Should see the chat message when the actual time comes.

Back to the main subject, I also tried local filtering on streamBuilder as follows But the stream does not Update when the actual time comes. and also tested by attaching snapshot to listener(streamSubscription), but no stream comes neither...

I carefully predict the time on where clause is wrong?

StreamBuilder<QuerySnapshot<ChatMessage>>(
          stream: DESCRIBED ABOVE SNAPSHOT,
          builder: (context, snapshot) {
            if (snapshot.hasData) {
                   if(...timestamp is before now)
                       return ChatTile(...);               // this is works as expected but not actually showing after time comes 
                  else 
                      return Container();
...
markusaksli-nc commented 2 years ago

Is this on the stream without the where filter?

google-oss-bot commented 2 years ago

Hey @Hwan-seok. We need more information to resolve this issue but there hasn't been an update in 7 weekdays. I'm marking the issue as stale and if there are no new updates in the next 7 days I will close it automatically.

If you have more information that will help us get to the bottom of this, just add a comment!

Hwan-seok commented 2 years ago

@markusaksli-nc Sorry for the very late response..πŸ˜₯πŸ˜₯

Yes, it is. It is on the stream when where is omitted.

markusaksli-nc commented 2 years ago

Could you provide a minimal complete reproducible code sample?

Hwan-seok commented 2 years ago

@markusaksli-nc Sure! I will right back soon

Hwan-seok commented 2 years ago

@markusaksli-nc I am back with minimum reproduction repo. I appreciate it if you check it.

https://github.com/Hwan-seok/firestore_where_statement_mini_repro

Caveat, It has only android settings

markusaksli-nc commented 2 years ago

Does this solve your issue?

Local filtering ```dart import 'package:flutter/material.dart'; import 'package:firebase_core/firebase_core.dart'; import 'package:cloud_firestore/cloud_firestore.dart'; void main() async { WidgetsFlutterBinding.ensureInitialized(); await Firebase.initializeApp(); runApp(MyApp()); } class MyApp extends StatelessWidget { // This widget is the root of your application. @override Widget build(BuildContext context) { return MaterialApp( title: 'Flutter Demo', theme: ThemeData( primarySwatch: Colors.blue, ), home: MyHomePage(), ); } } class MyHomePage extends StatefulWidget { MyHomePage({Key? key}) : super(key: key); @override _MyHomePageState createState() => _MyHomePageState(); } class _MyHomePageState extends State { final textController = TextEditingController(); @override void dispose() { textController.dispose(); super.dispose(); } @override Widget build(BuildContext context) { return Scaffold( body: Column( children: [ SizedBox(height: 30), TextField( controller: this.textController, onSubmitted: (chat) async { await FireStoreHelper.sendMessage( ChatMessage( message: chat, sentDateTime: DateTime.now(), ), ); this.textController.text = ''; }, ), Expanded( child: StreamBuilder>( stream: FireStoreHelper.getMessages(), builder: (context, snapshot) { if (snapshot.hasData) { var filteredMessages = snapshot.data!.docs.where((element) => element.data().sentDateTime.isBefore(DateTime.now())); return ListView.builder( itemCount: filteredMessages.length, itemBuilder: (context, index) { final message = filteredMessages.elementAt(index).data(); return ListTile( title: Text(message.message), ); }, ); } else { return Container(); } }, ), ), ], ), ); } } class FireStoreHelper { static FirebaseFirestore firestore = FirebaseFirestore.instance; static const String collectionName = "messages"; static CollectionReference _collectionReference() { return firestore.collection(collectionName).withConverter( fromFirestore: (snapshots, _) => ChatMessage.fromJson(snapshots.data()!), toFirestore: (chatMessage, _) => chatMessage.toJson()); } static Future sendMessage(ChatMessage message) async { await _collectionReference().add(message); } static Stream> getMessages() { return _collectionReference() .orderBy("sentDateTime", descending: true) //.where("sentDateTime", isLessThanOrEqualTo: DateTime.now()) .limit(1000) .snapshots(); } } class ChatMessage { final String message; final DateTime sentDateTime; ChatMessage({ required this.message, required this.sentDateTime, }); factory ChatMessage.fromJson(Map json) => _$ChatMessageFromJson(json); Map toJson() => _$ChatMessageToJson(this); static dateTimeFromJson(Timestamp timestamp) { return DateTime.fromMillisecondsSinceEpoch(timestamp.millisecondsSinceEpoch); } static dateTimeToTimestamp(DateTime dateTime) { return Timestamp.fromDate(dateTime); } } ChatMessage _$ChatMessageFromJson(Map json) { return ChatMessage( message: json['message'] as String, sentDateTime: ChatMessage.dateTimeFromJson(json['sentDateTime'] as Timestamp), ); } Map _$ChatMessageToJson(ChatMessage instance) => { 'message': instance.message, 'sentDateTime': ChatMessage.dateTimeToTimestamp(instance.sentDateTime), }; ```

This local filtering solution is the only one I could come up with for getting snapshots of a dynamic filtered query. If you only want to use firestore filtering you can do something like querying the collection manually when a new snapshot should be received or recreating the stream every time a new snapshot is received but that seems pretty roundabout.

You could always ask for help on StackOverflow or the firebase community.

Since the plugin itself is working as expected here I'm going to close the issue.