Count a Signal's items and unread in one pass (#348)
signal_rows ran the same correlated subquery over signal_verdicts, entries, subscriptions and entry_state twice a Signal, once for items and once for unread. One grouped subquery now gives both: count(*) and a sum of the unread, joined to the person's Signals. Same results on SQLite and Postgres (the existing counts test runs on both). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -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).
|
- 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 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 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
|
### Fixed
|
||||||
|
|
||||||
|
|||||||
@@ -2804,23 +2804,25 @@ impl Db {
|
|||||||
let mut a = Args::default();
|
let mut a = Args::default();
|
||||||
let user = a.p(user_id);
|
let user = a.p(user_id);
|
||||||
let one = id.map(|i| format!("AND g.id = {}", a.p(i))).unwrap_or_default();
|
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.
|
// The items it shows, and of those the unread: what its list counts, by the same rules. One
|
||||||
let hits = |unread: &str| {
|
// pass for both; they were two copies of the same subquery (#348).
|
||||||
format!(
|
let sql = format!(
|
||||||
"(SELECT count(*) FROM signal_verdicts v
|
"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 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
|
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 = g.user_id AND st.feed_id = v.feed_id AND st.guid = v.guid
|
LEFT JOIN entry_state st
|
||||||
WHERE v.signal_id = g.id AND v.hit AND {unread} AND {} AND {})",
|
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),
|
not_hidden(&user),
|
||||||
shown_in("hide_from_signals", "s")
|
shown_in("hide_from_signals", "s")
|
||||||
)
|
|
||||||
};
|
|
||||||
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)")
|
|
||||||
);
|
);
|
||||||
self.rows(&sql, a.0)
|
self.rows(&sql, a.0)
|
||||||
.await?
|
.await?
|
||||||
|
|||||||
Reference in New Issue
Block a user