diff --git a/CHANGELOG.md b/CHANGELOG.md index 0ef05b3..8a18068 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,6 +18,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Fixed +- The feed list loads faster: it counts the items and files of your own feeds, not of every feed on the server. - Downloading or deleting a file the server no longer has answers "not found" instead of a server error. - A feed's page in the Directory, and subscribing from it, no longer wait while the whole Directory is put together to check that the one feed is in it. - The feed list, and each live update of it, no longer asks the database for a group's title once for every feed in the group. diff --git a/src/db.rs b/src/db.rs index cec95e9..15dbbb6 100644 --- a/src/db.rs +++ b/src/db.rs @@ -398,18 +398,23 @@ impl Db { .map(|r| Ok((r.try_get::("", "feed_id")?, r.try_get::("", "n")?))) .collect::>>() }; + // Of this person's feeds only, as the unread count is: counting every item of every feed on + // the server, 1534 feeds for someone reading 146, was 184-316 ms, 70% of the feed list's + // time (#165). The Directory reads this for every feed's title and artwork, not a count. let entries = counts( "entries", - "SELECT feed_id, count(*) AS n FROM entries - WHERE (CAST($1 AS TEXT) IS NULL OR feed_id = $1) GROUP BY feed_id", - vec![one()], + "SELECT e.feed_id, count(*) AS n FROM entries e + JOIN subscriptions sub ON sub.user_id = $1 AND sub.feed_id = e.feed_id + WHERE (CAST($2 AS TEXT) IS NULL OR e.feed_id = $2) GROUP BY e.feed_id", + vec![user_id.into(), one()], ) .await?; let downloaded = counts( "downloaded", - "SELECT feed_id, count(*) AS n FROM enclosures - WHERE path IS NOT NULL AND (CAST($1 AS TEXT) IS NULL OR feed_id = $1) GROUP BY feed_id", - vec![one()], + "SELECT x.feed_id, count(*) AS n FROM enclosures x + JOIN subscriptions sub ON sub.user_id = $1 AND sub.feed_id = x.feed_id + WHERE x.path IS NOT NULL AND (CAST($2 AS TEXT) IS NULL OR x.feed_id = $2) GROUP BY x.feed_id", + vec![user_id.into(), one()], ) .await?; let unread = counts( @@ -2301,9 +2306,9 @@ mod tests { let db = Db::memory().await.unwrap(); db.exec_for_test( "INSERT INTO users (id, name, is_admin) VALUES (1,'ray',true),(2,'sam',false); - INSERT INTO subscriptions (user_id, feed_id) VALUES (1,'f'),(1,'g'),(2,'f'); - INSERT INTO feeds (id, url, title, ttl_mins) VALUES ('f','u','F',30),('g','v','G',null); - INSERT INTO entries (feed_id, guid, first_seen) VALUES ('f','a',0),('f','b',0),('f','c',0),('g','d',0); + INSERT INTO subscriptions (user_id, feed_id) VALUES (1,'f'),(1,'g'),(2,'f'),(2,'h'); + INSERT INTO feeds (id, url, title, ttl_mins) VALUES ('f','u','F',30),('g','v','G',null),('h','w','H',null); + INSERT INTO entries (feed_id, guid, first_seen) VALUES ('f','a',0),('f','b',0),('f','c',0),('g','d',0),('h','e',0); INSERT INTO entry_state (user_id, feed_id, guid, read) VALUES (1,'f','a',true),(2,'f','b',true); INSERT INTO hidden (user_id, feed_id, guid) VALUES (1,'f','c'); INSERT INTO enclosures (id, feed_id, guid, url, path, state) VALUES @@ -2324,6 +2329,8 @@ mod tests { assert_eq!(one.ttl_mins, db.http_state(id).await.unwrap().ttl_mins, "{id}"); } assert_eq!((list["f"].unread, list["g"].unread), (1, 1)); // a read, c hidden; sam's read is sam's + // Only sam reads h: still listed, for its title, but its items are not counted for ray (#165). + assert_eq!((list["h"].summary.title.as_deref(), list["h"].summary.entries), (Some("H"), 0)); } #[tokio::test]