Starting to work on community view, 2
authorDessalines <tyhou13@gmx.com>
Fri, 4 Dec 2020 21:35:46 +0000 (16:35 -0500)
committerDessalines <tyhou13@gmx.com>
Fri, 4 Dec 2020 21:35:46 +0000 (16:35 -0500)
docs/src/about_goals.md
lemmy_db/src/aggregates/community_aggregates.rs [new file with mode: 0644]
lemmy_db/src/aggregates/mod.rs
lemmy_db/src/schema.rs
lemmy_db/src/views/community_view.rs
migrations/2020-12-04-183345_create_community_aggregates/down.sql [new file with mode: 0644]
migrations/2020-12-04-183345_create_community_aggregates/up.sql [new file with mode: 0644]

index e0427481c1dfe7912763d8d0d8eb71bbef922fee..ea86db0756496b8d57577c2590cbe7591b80ce7e 100644 (file)
@@ -36,6 +36,7 @@
 - [Rust docker build](https://shaneutt.com/blog/rust-fast-small-docker-image-builds/)
 - [Zurb mentions](https://github.com/zurb/tribute)
 - [TippyJS](https://github.com/atomiks/tippyjs)
+- [SQL function indexes](https://sorentwo.com/2013/12/30/let-postgres-do-the-work.html)
 
 ## Activitypub guides
 
diff --git a/lemmy_db/src/aggregates/community_aggregates.rs b/lemmy_db/src/aggregates/community_aggregates.rs
new file mode 100644 (file)
index 0000000..9a8ea36
--- /dev/null
@@ -0,0 +1,21 @@
+use crate::schema::community_aggregates;
+use diesel::{result::Error, *};
+use serde::Serialize;
+
+#[derive(Queryable, Associations, Identifiable, PartialEq, Debug, Serialize, Clone)]
+#[table_name = "community_aggregates"]
+pub struct CommunityAggregates {
+  pub id: i32,
+  pub community_id: i32,
+  pub subscribers: i64,
+  pub posts: i64,
+  pub counts: i64,
+}
+
+impl CommunityAggregates {
+  pub fn read(conn: &PgConnection, id: i32) -> Result<Self, Error> {
+    community_aggregates::table.find(id).first::<Self>(conn)
+  }
+}
+
+// TODO add unit tests, to make sure triggers are working
index 2791c977ddfdc8c1868ae29b1d2fcbdde5eb7656..9f38f2ed0597b510c2d1836902d0aed3986b83ec 100644 (file)
@@ -1,2 +1,3 @@
+pub mod community_aggregates;
 pub mod site_aggregates;
 pub mod user_aggregates;
index bc575f4ee191864cfd9ca9c4d159c10c6c8ca152..e6dd6d4bd2f6b4c8dd452e51bff7c7549915573d 100644 (file)
@@ -127,6 +127,16 @@ table! {
     }
 }
 
+table! {
+    community_aggregates (id) {
+        id -> Int4,
+        community_id -> Int4,
+        subscribers -> Int8,
+        posts -> Int8,
+        comments -> Int8,
+    }
+}
+
 table! {
     community_aggregates_fast (id) {
         id -> Int4,
@@ -544,6 +554,7 @@ joinable!(comment_saved -> comment (comment_id));
 joinable!(comment_saved -> user_ (user_id));
 joinable!(community -> category (category_id));
 joinable!(community -> user_ (creator_id));
+joinable!(community_aggregates -> community (community_id));
 joinable!(community_follower -> community (community_id));
 joinable!(community_follower -> user_ (user_id));
 joinable!(community_moderator -> community (community_id));
@@ -587,6 +598,7 @@ allow_tables_to_appear_in_same_query!(
   comment_report,
   comment_saved,
   community,
+  community_aggregates,
   community_aggregates_fast,
   community_follower,
   community_moderator,
index c7b9b398bbb1902bb6c0adc18dbc43b54002bafa..4e0b588211803a7807cf9d4687352da2b3a39e81 100644 (file)
@@ -1,7 +1,8 @@
 use crate::{
+  aggregates::community_aggregates::CommunityAggregates,
   category::Category,
   community::{Community, CommunityFollower},
-  schema::{category, community, community_follower, user_},
+  schema::{category, community, community_aggregates, community_follower, user_},
   user::{UserSafe, User_},
 };
 use diesel::{result::Error, *};
@@ -13,21 +14,9 @@ pub struct CommunityView {
   pub creator: UserSafe,
   pub category: Category,
   pub subscribed: bool,
+  pub counts: CommunityAggregates,
 }
 
-// creator_actor_id -> Text,
-// creator_local -> Bool,
-// creator_name -> Varchar,
-// creator_preferred_username -> Nullable<Varchar>,
-// creator_avatar -> Nullable<Text>,
-// category_name -> Varchar,
-// number_of_subscribers -> BigInt,
-// number_of_posts -> BigInt,
-// number_of_comments -> BigInt,
-// hot_rank -> Int4,
-// user_id -> Nullable<Int4>,
-// subscribed -> Nullable<Bool>,
-
 impl CommunityView {
   pub fn read(
     conn: &PgConnection,
@@ -45,17 +34,19 @@ impl CommunityView {
       None => false,
     };
 
-    let (community, creator, category) = community::table
+    let (community, creator, category, counts) = community::table
       .find(community_id)
       .inner_join(user_::table)
       .inner_join(category::table)
-      .first::<(Community, User_, Category)>(conn)?;
+      .inner_join(community_aggregates::table)
+      .first::<(Community, User_, Category, CommunityAggregates)>(conn)?;
 
     Ok(CommunityView {
       community,
       creator: creator.to_safe(),
       category,
       subscribed,
+      counts,
     })
   }
 }
diff --git a/migrations/2020-12-04-183345_create_community_aggregates/down.sql b/migrations/2020-12-04-183345_create_community_aggregates/down.sql
new file mode 100644 (file)
index 0000000..ac2872d
--- /dev/null
@@ -0,0 +1,9 @@
+-- community aggregates
+drop table community_aggregates;
+drop trigger community_aggregates_post_count on post;
+drop trigger community_aggregates_comment_count on comment;
+drop trigger community_aggregates_subscriber_count on community_follower;
+drop function 
+  community_aggregates_post_count,
+  community_aggregates_comment_count,
+  community_aggregates_subscriber_count;
diff --git a/migrations/2020-12-04-183345_create_community_aggregates/up.sql b/migrations/2020-12-04-183345_create_community_aggregates/up.sql
new file mode 100644 (file)
index 0000000..8af0159
--- /dev/null
@@ -0,0 +1,92 @@
+-- Add community aggregates
+create table community_aggregates (
+  id serial primary key,
+  community_id int references community on update cascade on delete cascade not null,
+  subscribers bigint not null,
+  posts bigint not null,
+  comments bigint not null,
+  unique (community_id)
+);
+
+insert into community_aggregates (community_id, subscribers, posts, comments)
+  select 
+    c.id,
+    coalesce(cf.subs, 0::bigint) as subscribers,
+    coalesce(cd.posts, 0::bigint) as posts,
+    coalesce(cd.comments, 0::bigint) as comments
+  from community c
+  left join ( 
+    select 
+      p.community_id,
+      count(distinct p.id) as posts,
+      count(distinct ct.id) as comments
+    from post p
+    left join comment ct on p.id = ct.post_id
+    group by p.community_id
+  ) cd on cd.community_id = c.id
+  left join ( 
+    select 
+      community_follower.community_id,
+      count(*) as subs
+    from community_follower
+    group by community_follower.community_id
+  ) cf on cf.community_id = c.id;
+
+-- Add community aggregate triggers
+-- post count
+create function community_aggregates_post_count()
+returns trigger language plpgsql
+as $$
+begin
+  IF (TG_OP = 'INSERT') THEN
+    update community_aggregates 
+    set posts = posts + 1 where community_id = NEW.community_id;
+  ELSIF (TG_OP = 'DELETE') THEN
+    update community_aggregates 
+    set posts = posts - 1 where community_id = OLD.community_id;
+  END IF;
+  return null;
+end $$;
+
+create trigger community_aggregates_post_count
+after insert or delete on post
+execute procedure community_aggregates_post_count();
+
+-- comment count
+create function community_aggregates_comment_count()
+returns trigger language plpgsql
+as $$
+begin
+  IF (TG_OP = 'INSERT') THEN
+    update community_aggregates 
+    set comments = comments + 1 where community_id = NEW.community_id;
+  ELSIF (TG_OP = 'DELETE') THEN
+    update community_aggregates 
+    set comments = comments - 1 where community_id = OLD.community_id;
+  END IF;
+  return null;
+end $$;
+
+create trigger community_aggregates_comment_count
+after insert or delete on comment
+execute procedure community_aggregates_comment_count();
+
+-- subscriber count
+create function community_aggregates_subscriber_count()
+returns trigger language plpgsql
+as $$
+begin
+  IF (TG_OP = 'INSERT') THEN
+    update community_aggregates 
+    set subscribers = subscribers + 1 where community_id = NEW.community_id;
+  ELSIF (TG_OP = 'DELETE') THEN
+    update community_aggregates 
+    set subscribers = subscribers - 1 where community_id = OLD.community_id;
+  END IF;
+  return null;
+end $$;
+
+create trigger community_aggregates_subscriber_count
+after insert or delete on community_follower
+execute procedure community_aggregates_subscriber_count();
+