diff --git a/crates/ipx/CHANGELOG.md b/crates/ipx/CHANGELOG.md index a34c56d..1ce2a56 100644 --- a/crates/ipx/CHANGELOG.md +++ b/crates/ipx/CHANGELOG.md @@ -17,6 +17,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Traces show more of where a request's time goes: a page of items' query and its files apart, artwork's cache, check and fetch, the status counts, and each part of changing or leaving a feed (#350). - A page of items finds its files by feed as well as item: it read every file's row in the database, about a quarter of a second a page (#344). - A page of items asks for its count beside the items, and the feed list asks its five questions at once, where each waited for the one before (#346). +- A Signal's count of items and of unread ones are worked out in one pass, not two (#348). ### Fixed diff --git a/crates/ipx/src/db.rs b/crates/ipx/src/db.rs index 6565dd7..66de3b3 100644 --- a/crates/ipx/src/db.rs +++ b/crates/ipx/src/db.rs @@ -2804,23 +2804,25 @@ impl Db { let mut a = Args::default(); let user = a.p(user_id); let one = id.map(|i| format!("AND g.id = {}", a.p(i))).unwrap_or_default(); - // The items it shows, and of those the unread: what its list counts, by the same rules. - let hits = |unread: &str| { - format!( - "(SELECT count(*) FROM signal_verdicts v - JOIN entries e ON e.feed_id = v.feed_id AND e.guid = v.guid - JOIN subscriptions s ON s.user_id = g.user_id AND s.feed_id = v.feed_id - LEFT JOIN entry_state st ON st.user_id = g.user_id AND st.feed_id = v.feed_id AND st.guid = v.guid - WHERE v.signal_id = g.id AND v.hit AND {unread} AND {} AND {})", - not_hidden(&user), - shown_in("hide_from_signals", "s") - ) - }; + // The items it shows, and of those the unread: what its list counts, by the same rules. One + // pass for both; they were two copies of the same subquery (#348). let sql = format!( - "SELECT g.id, g.name, g.keywords, g.description, g.icon, {} AS items, {} AS unread - FROM signals g WHERE g.user_id = {user} {one} ORDER BY lower(g.name), g.id", - hits("true"), - hits("NOT coalesce(st.read, false)") + "SELECT g.id, g.name, g.keywords, g.description, g.icon, + coalesce(c.items, 0) AS items, coalesce(c.unread, 0) AS unread + FROM signals g + LEFT JOIN (SELECT v.signal_id, count(*) AS items, + sum(CASE WHEN NOT coalesce(st.read, false) THEN 1 ELSE 0 END) AS unread + FROM signal_verdicts v + JOIN signals vg ON vg.id = v.signal_id + JOIN entries e ON e.feed_id = v.feed_id AND e.guid = v.guid + JOIN subscriptions s ON s.user_id = vg.user_id AND s.feed_id = v.feed_id + LEFT JOIN entry_state st + ON st.user_id = vg.user_id AND st.feed_id = v.feed_id AND st.guid = v.guid + WHERE vg.user_id = {user} AND v.hit AND {} AND {} + GROUP BY v.signal_id) c ON c.signal_id = g.id + WHERE g.user_id = {user} {one} ORDER BY lower(g.name), g.id", + not_hidden(&user), + shown_in("hide_from_signals", "s") ); self.rows(&sql, a.0) .await?