- [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
--- /dev/null
+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
+pub mod community_aggregates;
pub mod site_aggregates;
pub mod user_aggregates;
}
}
+table! {
+ community_aggregates (id) {
+ id -> Int4,
+ community_id -> Int4,
+ subscribers -> Int8,
+ posts -> Int8,
+ comments -> Int8,
+ }
+}
+
table! {
community_aggregates_fast (id) {
id -> Int4,
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));
comment_report,
comment_saved,
community,
+ community_aggregates,
community_aggregates_fast,
community_follower,
community_moderator,
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, *};
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,
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,
})
}
}
--- /dev/null
+-- 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;
--- /dev/null
+-- 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();
+