mangayomi-mirror/lib/modules/more
Mehakdeep Singh 8babb975ab perf(statistics): aggregate inside Isar instead of loading all favourite chapters into memory
Fixes #543. The statistics screen was crashing on Android with a
populated library (~75+ favourites). Repro: open the More -> Statistics
screen on an account with a real-sized library; app hangs and is
killed.

Cause is in `getStatistics()` in `lib/modules/more/statistics/
statistics_provider.dart`. The provider materialised every favourite
manga *and* every chapter of every favourite into a Dart list, then
walked the lists in memory:

    final items = await isar.mangas.filter()...findAll();
    final chapters = await isar.chapters.filter()
        .manga((q) => q.favoriteEqualTo(true)...).findAll();

For a library with 75 favourites at ~100 chapters each that is 7,500+
heavy Isar objects materialised at once just to compute six counts.
The "completed items" loop then re-fetched chapters per completed
manga via the link relation (`item.chapters.toList()`), and the
reading-time aggregation loaded every History row to sum a single
int field.

This commit rewrites the function to do all aggregation inside Isar:

* total / read / completed-items / downloads => `count()` queries on
  indexed filter chains. No object hydration.
* completed favourites => projects only the IDs (`idProperty()`),
  then runs two cheap `count()` queries per item to check
  "has-chapters && unread == 0". Logic is unchanged from the original
  `every(isRead) && isNotEmpty` check.
* reading time => projects the `readingTimeSeconds` field
  (`readingTimeSecondsProperty()`) and folds the resulting List<int?>.
  Avoids hydrating full History rows.

Memory: peak in-memory list shrinks from ~7,500 fully-hydrated chapters
plus all favourite mangas plus all history rows, to (at most) the IDs
of completed favourites (typically <50 ints) and the projected reading-
time list (one int per history row, decoded as a flat list).

DB round-trips: the completed-items branch now does 2 round-trips per
completed favourite instead of 1 link-traversal per completed favourite.
On indexed `mangaId` + `isRead` filters this is sub-millisecond per
query and well below the cost of materialising thousands of rows. The
overall function is faster end-to-end on any non-trivial library.

Behaviour preserved: same six fields, same definitions, same edge
cases (manga with no chapters is not counted as completed).

Verified
- `flutter analyze` clean on the touched file
- `flutter build macos --release` succeeds
- Manual smoke test on macOS with the local-all-fixes build (the bug
  reproduces only with a large mobile library; macOS desktop with a
  small test library returns the same numbers as before)
2026-05-09 23:12:49 -07:00
..
about Remove BuildContext from checkForUpdate Provider 2026-05-09 12:59:32 +02:00
categories Reduce Code Duplication by Adding Platform Helper 2026-04-26 19:39:14 +02:00
data_and_storage New Setting: Mark duplicate chapter as read 2026-04-26 01:09:28 +02:00
download_queue rename files manga.dart and chapter.dart 2026-04-23 18:53:25 +02:00
providers update dependencies & update riverpod provider declarations 2026-01-08 14:27:08 +01:00
settings Merge pull request #714 from NBA2K1/main 2026-04-30 11:19:11 +01:00
statistics perf(statistics): aggregate inside Isar instead of loading all favourite chapters into memory 2026-05-09 23:12:49 -07:00
widgets Merge branch 'main' into enhance/mpv 2025-08-21 16:35:08 +01:00
more_screen.dart Merge branch 'main' into enhance/mpv 2025-08-21 16:35:08 +01:00