Moor: Watching a subset of a table doesn't reliably update with results

Created on 2 Jul 2019  路  3Comments  路  Source: simolus3/moor

I'm running a query on a moor database that is intended to watch a single entry, and update whenever that entry updates. My query looks like this:

return (select(favorites)..where((favorite) => favorite.id.equals(id)))
        .watch()
        .map((favoritesList) => favoritesList.length >= 1);

When I run this, I would expect the UI I've hooked up to this to update on any changes. However, for my use case, the entry I'm watching might not exist before I make the first update. The behavior I'm seeing is that when I make that first update (an add to the table), my UI does not rebuild. Then, on all subsequent updates, I do see the expected UI changes. I'm wondering if a query like this won't work if the entry doesn't exist yet. Is this a use case your plugin intends to support?

Thanks for your help!

bug

Most helpful comment

This is rad, @simolus3 ! Thank you so much for the help!
Just as an FYI -- we're going to be featuring your plugin on the Flutter Boring Development Show! https://www.youtube.com/watch?v=vqPG1tU6-c0&list=PLjxrf2q8roU3ahJVrSgAnPjzkpGmL9Czl
(the episode featuring your plugin isn't out yet)

All 3 comments

Thank you for the detailed report! I was able to reproduce this locally and think I know what's going on here. Are you passing that stream to a StreamBuilder? Also, is the widget containing that StreamBuilder rebuilt when you'd expect the query to update (e.g. a button press that calls setState and inserts something into the database)?

What's happening here is that moor tries to be efficient about managing streams. The .watch() method doesn't create a new stream every time it's called - we know that streams are regularly passed to StreamBuilders which are rebuilt often, and there is no need to run another SELECT everytime that happens. However, the .map method does create a new stream every time it's called. So, what's most likely happening here is

  1. The StreamBuilder is created and so is the stream - the select statement is executed.
  2. A new record is added to the table
  3. The widget/state is marked for rebuild
  4. Moor detects that the table was updated, the select statement will re-run and listeners will be notified
  5. The stream builder gets a new stream (because map creates a new stream every time it's called), and re-subscribes, but after the data is already there. This causes the UI to not update.

TLDR: This is indeed a moor bug, which should be fixed by making .watch() act like a BehaviorSubject. As a workaround, it's possible to call the database method only once (say in initState()) and then re-use that same stream across widget rebuilds.

Fixed on develop, I'll release a new version bundled with some more fixes soon. Again, thanks for using moor and taking the time to file an issue.

This is rad, @simolus3 ! Thank you so much for the help!
Just as an FYI -- we're going to be featuring your plugin on the Flutter Boring Development Show! https://www.youtube.com/watch?v=vqPG1tU6-c0&list=PLjxrf2q8roU3ahJVrSgAnPjzkpGmL9Czl
(the episode featuring your plugin isn't out yet)

Was this page helpful?
0 / 5 - 0 ratings

Related issues

angel1st picture angel1st  路  4Comments

felixjunghans picture felixjunghans  路  4Comments

Beloin picture Beloin  路  4Comments

jerryzhoujw picture jerryzhoujw  路  4Comments

Ltei picture Ltei  路  3Comments