mongo-dart / mongo_dart

Mongo_dart: MongoDB driver for Dart programming language
https://pub.dev/packages/mongo_dart
MIT License
444 stars 98 forks source link

Watch() not working with StreamBuilder #316

Closed JohnF17 closed 1 year ago

JohnF17 commented 1 year ago

Hi, I just want to make it clear that, Before opening this issue I've looked at all the other related issues, tried their approach, checked out both the examples provided on watch, and also tried other methods, i'm still yet to come across a solution for the countless effort I've put forth onto making this work. Now i really need help with it.

Basic overview

This Below is Just For demonstration (extracting messages from documents etc.. has been implemented properly)

StreamBuilder(
  stream: DBConnection.getInstance()
      .getPrivateMessagesStream('63d79cb3412bf151d5436a85',
          '63d79cbf412bf151d5436a86'),
  builder: (context, snapshot) {
    if (snapshot.hasData) {
      return Center(child: Text(snapshot.data.toString()));
    }
    if (snapshot.hasError) {
      return const Center(child: Text('no data'));
    }
    return const Center(child: Text('loading'));
  },
)

And so here is the problem, i don't see any changes, it always displays loading in the streamBuilder, and the print statement listening to changes that is inside the listen method doesn't get called, even though i have put an updating timer inside the method getPrivateMessagesStream() that changes the data regularly (just for testing btw) and that itself prints changing data, (and funny enough i do see the changes happen in the db btw, no connection issues there), i just don't seem to get whats happening here, I really need help with this.

This is the point but Please do feel free to comment and correct any wrong doings here and/or in the db method i'm using Small Disclaimer

Additional info

As of now (1/30/2023), I'm using the latest monog_db package, i'm using atlas shared free, latest flutter stable and other CRUD operations work well.

giorgiofran commented 1 year ago

Hi, I'n not a flutter expert, so I'd have to spend some time in creating a test-case. Anyway, at a fisrt glance, it is not clear to me why you have to create a new stream. The object returned by watch() is a strem itself.

JohnF17 commented 1 year ago

First of all, thank you for your reply and secondly

so I'd have to spend some time in creating a test-case.

You shall not take your time for this, i did some refactoring and few code changes 😁 the issue is now fixed, thanks to this phrase below

The object returned by watch() is a stream itself.

I have absolutely no idea how I let that fly by me😅, maybe its because I was too focused on closing the stream, forgetting StreamBuilders do that by default when they're removed off of the widget tree.

Anyhow, if it helps others, here's what i changed to the method getPrivateMessagesStream()

  Stream getPrivateMessagesStream({
    required String senderId,
    required String recipientId,
  }) async* {
    DbCollection? messages;
    String groupId = _getProperGroupId(senderId, recipientId);

    try {
      messages = (await getConnection()).collection('private-messages');
    } catch (e) {
      rethrow;
    }

    var pipeline = AggregationPipelineBuilder()
        .addStage(
          Match(where.eq('fullDocument._id', groupId).map['\$query']),
        )
        .addStage(
          Match(where.eq('operationType', 'update').map['\$query']),
        );

    // Just for testing purposes to listen to changes
    // Timer.periodic(
    //   const Duration(seconds: 5),
    //   (t) async {
    //     sendPrivateMessage(
    //       message: 'Hello ${t.tick}',
    //       recipientId: recipientId,
    //       senderId: senderId,
    //       reactionId: 3,
    //     );
    //   },
    // );

    yield* messages.watch(
      pipeline,
      changeStreamOptions: ChangeStreamOptions(fullDocument: 'updateLookup'),
    );
  }

And used it in a StreamBuilder like this

StreamBuilder(
  stream: DBConnection.getInstance().getPrivateMessagesStream(
    senderId: '63d79cb3412bf151d5436a85', // Sender
    recipientId: '63d79cbf412bf151d5436a86', // Recipient
  ),
  builder: (context, snapshot) {
    if (snapshot.hasData) {
      return Center(
        child: Text(snapshot.data.fullDocument.toString()),
      );
    }
    if (snapshot.hasError) {
      return Center(
          child: Text('no data, ${snapshot.error}'));
    }
    return const Center(child: Text('loading'));
  },
),

This issue is basically a closed issue and I will close it soon, but if you may, could you answer the following questions for me?

  1. Currently, the StreamBuilder shows data only when an update occurs else shows loading (meaning if there are no updates, not even the initial data show up), how do I make it show the current available data first whether or not an update occurs and then let it do its normal job?
  2. Establishing a connection to the database at first takes about 30 seconds, is that normal or is it probably connection issues?
  3. What other operationTypes are there besides update, insert and delete (is there like an all, or a different type like operationType)?

Thank you in Advance!

giorgiofran commented 1 year ago

Establishing a connection to the database at first takes about 30 seconds, is that normal or is it probably connection issues?

Normally is by far faster. But I have been already told that sometimes it takes a lot. Are you connecting to an Atlas free cluster?

What other operationTypes are there besides update, insert and delete (is there like an all, or a different type like operationType)?

see here

JohnF17 commented 1 year ago

Are you connecting to an Atlas free cluster?

Yes, I am indeed.

see here

I shall look in to it, thanks

giorgiofran commented 1 year ago

Currently, the StreamBuilder shows data only when an update occurs else shows loading (meaning if there are no updates, not even the initial data show up), how do I make it show the current available data first whether or not an update occurs and then let it do its normal job?

StremBuilder has an initialData property.

JohnF17 commented 1 year ago

StremBuilder has an initialData property.

I was hoping to find a different way, but i guess i'll just use that, probably get data from cache (data i'll save locally).

Thanks and have a good one! 🙂