mangayomi-mirror/lib/modules
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
..
anime Make downloadedChapterIdsProvider async 2026-05-09 00:33:36 +02:00
browse Why are there two searchproviders? 2026-04-15 23:41:47 +02:00
calendar feat: Implement upcoming manga calendar feature 2026-03-05 12:05:29 +01:00
history rename files manga.dart and chapter.dart 2026-04-23 18:53:25 +02:00
library Merge pull request #722 from NBA2K1/fix-exception 2026-05-09 16:06:47 +01:00
main_view Remove BuildContext from checkForUpdate Provider 2026-05-09 12:59:32 +02:00
manga Make downloadedChapterIdsProvider async 2026-05-09 00:33:36 +02:00
mass_migration Use String Extension instead of private function 2026-04-21 22:59:19 +02:00
more perf(statistics): aggregate inside Isar instead of loading all favourite chapters into memory 2026-05-09 23:12:49 -07:00
novel Merge pull request #714 from NBA2K1/main 2026-04-30 11:19:11 +01:00
tracker_library Add Copilot suggested change (2/3) 2026-04-20 18:46:47 +02:00
updates Make _updateLibrary() fully async 2026-05-09 12:59:12 +02:00
webview Revert "fix(http): align extension requests with WebView cookies and harden Cloudflare detection" 2026-04-04 02:29:27 +02:00
widgets Add Copilot suggested change (3/3) 2026-04-20 18:47:05 +02:00