Count the originals on this device from the cache, not from every image

`library::local_original_count` feeds the "On this device" chip and runs
beside the rating counts on every star keystroke. On the reference library
it cost 1.3-1.4 ms best-of-50 (3 ms on a busy machine) to find 254
originals among 19,000 visible images.

It was a correlated EXISTS per visible image:

  SCAN i USING INDEX images_grid_order
  SEARCH ic EXISTS USING INTEGER PRIMARY KEY (rowid=?)

`image_cache` holds a row only for what has been fetched, so the question
is driven from it: `i.id IN (SELECT image_id FROM image_cache WHERE
tier_actual >= Original)`, which SQLite plans as the list first and a probe
of `images` by id for each entry:

  SEARCH i USING INTEGER PRIMARY KEY (rowid=?)
  LIST SUBQUERY 1
    SCAN image_cache

`image_id` is the cache's primary key, so each image is in the list at most
once and the count is the one the EXISTS gave (254). After: 0.05 ms. On a
library whose every original is cached this is as much work as before,
which is the proportion the rule asks for.

The count stays on the keystroke path: dropping it there would leave the
chip stale after a background download until something else refreshed it,
and at this cost there is nothing left to save. catalog_bench spells the
query as dr-ui does, so its copy changes with it.
This commit is contained in:
2026-09-26 13:28:50 -04:00
parent 73059f2656
commit fe6e523443
2 changed files with 12 additions and 4 deletions
+2 -2
View File
@@ -155,8 +155,8 @@ fn local_original_count(conn: &rusqlite::Connection) -> i64 {
conn.query_row( conn.query_row(
"SELECT count(*) FROM images i "SELECT count(*) FROM images i
WHERE i.shadowed_by IS NULL AND i.trashed_at IS NULL WHERE i.shadowed_by IS NULL AND i.trashed_at IS NULL
AND EXISTS (SELECT 1 FROM image_cache ic AND i.id IN (SELECT ic.image_id FROM image_cache ic
WHERE ic.image_id = i.id AND ic.tier_actual >= 2)", WHERE ic.tier_actual >= 2)",
[], [],
|r| r.get(0), |r| r.get(0),
) )
+10 -2
View File
@@ -364,13 +364,21 @@ pub(super) fn total_images_filtered(
/// Whole-library, like the star counts beside it: the chip says what narrowing /// Whole-library, like the star counts beside it: the chip says what narrowing
/// to it would show, so counting only the current window would make it /// to it would show, so counting only the current window would make it
/// describe the view it exists to change. /// describe the view it exists to change.
///
/// Driven from `image_cache`, which holds a row only for what has been
/// fetched: an `IN` list the planner walks, probing `images` by id for each.
/// Spelled as a correlated `EXISTS` it walked every visible image and
/// probed the cache for each -- 19,000 probes to find 254 originals on the
/// reference library, 2 ms paid on every star keystroke beside the rating
/// counts. `image_id` is the cache's key, so the list holds each image once
/// and the count is the one the `EXISTS` gave.
pub fn local_original_count(catalog: &Catalog) -> Result<usize, dr_catalog::CatalogError> { pub fn local_original_count(catalog: &Catalog) -> Result<usize, dr_catalog::CatalogError> {
let n: i64 = catalog.connection().query_row( let n: i64 = catalog.connection().query_row(
&format!( &format!(
"SELECT count(*) FROM images i "SELECT count(*) FROM images i
WHERE {VISIBLE} WHERE {VISIBLE}
AND EXISTS (SELECT 1 FROM image_cache ic AND i.id IN (SELECT ic.image_id FROM image_cache ic
WHERE ic.image_id = i.id AND ic.tier_actual >= {})", WHERE ic.tier_actual >= {})",
dr_types::Tier::Original.stored() dr_types::Tier::Original.stored()
), ),
[], [],