Repository: LemmyNet/lemmy (language: rust)

Issue:
Rename `when_` fields to `published`
Some of the modlog items have timestamps stored in fields called `when_`. This should be changed to `published` to be consistent with other tables. A simple search and replace should be enough.


Discussion / hints:

Fix patch:
diff --git a/Cargo.lock b/Cargo.lock
index 79896704ab..3f9badf0a2 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -1280,15 +1280,6 @@ dependencies = [
  "tokio-postgres",
 ]
 
-[[package]]
-name = "diesel-bind-if-some"
-version = "0.1.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1ed8ce9db476124d2eaf4c9db45dc6581b8e8c4c4d47d5e0f39de1fb55dfb2a7"
-dependencies = [
- "diesel",
-]
-
 [[package]]
 name = "diesel-derive-enum"
 version = "2.1.0"
@@ -2701,7 +2692,6 @@ dependencies = [
  "derive-new",
  "diesel",
  "diesel-async",
- "diesel-bind-if-some",
  "diesel-derive-enum",
  "diesel-derive-newtype",
  "diesel_ltree",
@@ -2746,7 +2736,6 @@ dependencies = [
  "serde_json",
  "serde_with",
  "serial_test",
- "strum",
  "test-context",
  "tokio",
  "tracing",
diff --git a/Cargo.toml b/Cargo.toml
index 6a33825c2e..cc01fc2a58 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -157,7 +157,6 @@ i-love-jesus = { version = "0.1.0" }
 clap = { version = "4.5.27", features = ["derive", "env"] }
 pretty_assertions = "1.4.1"
 derive-new = "0.7.0"
-diesel-bind-if-some = "0.1.0"
 tuplex = "0.1.2"
 
 [dependencies]
diff --git a/crates/db_schema/Cargo.toml b/crates/db_schema/Cargo.toml
index cf260f3261..b2b1dbb1b1 100644
--- a/crates/db_schema/Cargo.toml
+++ b/crates/db_schema/Cargo.toml
@@ -38,7 +38,6 @@ full = [
   "rustls",
   "i-love-jesus",
   "tuplex",
-  "diesel-bind-if-some",
 ]
 
 [dependencies]
@@ -78,7 +77,6 @@ rustls = { workspace = true, optional = true }
 uuid.workspace = true
 i-love-jesus = { workspace = true, optional = true }
 anyhow = { workspace = true }
-diesel-bind-if-some = { workspace = true, optional = true }
 derive-new.workspace = true
 tuplex = { workspace = true, optional = true }
 
diff --git a/crates/db_schema/src/aggregates/person_post_aggregates.rs b/crates/db_schema/src/aggregates/person_post_aggregates.rs
index 63a50af9c8..9f40b71b8f 100644
--- a/crates/db_schema/src/aggregates/person_post_aggregates.rs
+++ b/crates/db_schema/src/aggregates/person_post_aggregates.rs
@@ -3,7 +3,7 @@ use crate::{
   diesel::OptionalExtension,
   newtypes::{PersonId, PostId},
   schema::post_actions,
-  utils::{find_action, get_conn, now, DbPool},
+  utils::{get_conn, now, DbPool},
 };
 use diesel::{
   expression::SelectableHelper,
@@ -37,7 +37,9 @@ impl PersonPostAggregates {
     post_id_: PostId,
   ) -> Result<Option<Self>, Error> {
     let conn = &mut get_conn(pool).await?;
-    find_action(post_actions::read_comments, (person_id_, post_id_))
+    post_actions::table
+      .find((person_id_, post_id_))
+      .filter(post_actions::read_comments.is_not_null())
       .select(Self::as_select())
       .first(conn)
       .await
diff --git a/crates/db_schema/src/impls/community.rs b/crates/db_schema/src/impls/community.rs
index 059565d6ba..b4748d1964 100644
--- a/crates/db_schema/src/impls/community.rs
+++ b/crates/db_schema/src/impls/community.rs
@@ -20,8 +20,6 @@ use crate::{
   },
   traits::{ApubActor, Bannable, Crud, Followable, Joinable},
   utils::{
-    action_query,
-    find_action,
     functions::{coalesce, coalesce_2_nullable, lower, random_smallint},
     get_conn,
     now,
@@ -268,6 +266,13 @@ impl Community {
       .get_result::<CommunityId>(conn)
       .await
   }
+
+  #[diesel::dsl::auto_type(no_type_alias)]
+  pub fn hide_removed_and_deleted() -> _ {
+    community::removed
+      .eq(false)
+      .and(community::deleted.eq(false))
+  }
 }
 
 impl CommunityModerator {
@@ -301,7 +306,8 @@ impl CommunityModerator {
     for_person_id: PersonId,
   ) -> Result<Vec<CommunityId>, Error> {
     let conn = &mut get_conn(pool).await?;
-    action_query(community_actions::became_moderator)
+    community_actions::table
+      .filter(community_actions::became_moderator.is_not_null())
       .filter(community_actions::person_id.eq(for_person_id))
       .select(community_actions::community_id)
       .load::<CommunityId>(conn)
@@ -322,7 +328,8 @@ impl CommunityModerator {
     persons.push(mod_person_id);
     persons.dedup();
 
-    let res = action_query(community_actions::became_moderator)
+    let res = community_actions::table
+      .filter(community_actions::became_moderator.is_not_null())
       .filter(community_actions::community_id.eq(for_community_id))
       .filter(community_actions::person_id.eq_any(persons))
       .order_by(community_actions::became_moderator)
@@ -393,14 +400,14 @@ impl CommunityFollower {
     remote_community_id: CommunityId,
   ) -> LemmyResult<()> {
     let conn = &mut get_conn(pool).await?;
-    select(exists(
-      action_query(community_actions::followed)
-        .filter(community_actions::community_id.eq(remote_community_id)),
-    ))
-    .get_result::<bool>(conn)
-    .await?
-    .then_some(())
-    .ok_or(LemmyErrorType::CommunityHasNoFollowers.into())
+    let find_action = community_actions::table
+      .filter(community_actions::followed.is_not_null())
+      .filter(community_actions::community_id.eq(remote_community_id));
+    select(exists(find_action))
+      .get_result::<bool>(conn)
+      .await?
+      .then_some(())
+      .ok_or(LemmyErrorType::CommunityHasNoFollowers.into())
   }
 
   pub async fn approve(
@@ -410,16 +417,16 @@ impl CommunityFollower {
     approver_id: PersonId,
   ) -> LemmyResult<()> {
     let conn = &mut get_conn(pool).await?;
-    diesel::update(find_action(
-      community_actions::followed,
-      (follower_id, community_id),
-    ))
-    .set((
-      community_actions::follow_state.eq(CommunityFollowerState::Accepted),
-      community_actions::follow_approver_id.eq(approver_id),
-    ))
-    .execute(conn)
-    .await?;
+    let find_action = community_actions::table
+      .find((follower_id, community_id))
+      .filter(community_actions::followed.is_not_null());
+    diesel::update(find_action)
+      .set((
+        community_actions::follow_state.eq(CommunityFollowerState::Accepted),
+        community_actions::follow_approver_id.eq(approver_id),
+      ))
+      .execute(conn)
+      .await?;
     Ok(())
   }
 }
@@ -462,14 +469,14 @@ impl Followable for CommunityFollower {
     person_id: PersonId,
   ) -> Result<Self, Error> {
     let conn = &mut get_conn(pool).await?;
-    diesel::update(find_action(
-      community_actions::follow_state,
-      (person_id, community_id),
-    ))
-    .set(community_actions::follow_state.eq(Some(CommunityFollowerState::Accepted)))
-    .returning(Self::as_select())
-    .get_result::<Self>(conn)
-    .await
+    let find_action = community_actions::table
+      .find((person_id, community_id))
+      .filter(community_actions::follow_state.is_not_null());
+    diesel::update(find_action)
+      .set(community_actions::follow_state.eq(Some(CommunityFollowerState::Accepted)))
+      .returning(Self::as_select())
+      .get_result::<Self>(conn)
+      .await
   }
   async fn unfollow(
     pool: &mut DbPool<'_>,
@@ -510,9 +517,7 @@ impl ApubActor for Community {
       .filter(community::local.eq(true))
       .filter(lower(community::name).eq(community_name.to_lowercase()));
     if !include_deleted {
-      q = q
-        .filter(community::deleted.eq(false))
-        .filter(community::removed.eq(false));
+      q = q.filter(Self::hide_removed_and_deleted())
     }
     q.first(conn).await.optional()
   }
diff --git a/crates/db_schema/src/impls/community_block.rs b/crates/db_schema/src/impls/community_block.rs
index c520e43e80..9217e21055 100644
--- a/crates/db_schema/src/impls/community_block.rs
+++ b/crates/db_schema/src/impls/community_block.rs
@@ -6,7 +6,7 @@ use crate::{
     community_block::{CommunityBlock, CommunityBlockForm},
   },
   traits::Blockable,
-  utils::{action_query, find_action, get_conn, now, uplete, DbPool},
+  utils::{get_conn, now, uplete, DbPool},
 };
 use diesel::{
   dsl::{exists, insert_into, not},
@@ -27,14 +27,14 @@ impl CommunityBlock {
     for_community_id: CommunityId,
   ) -> LemmyResult<()> {
     let conn = &mut get_conn(pool).await?;
-    select(not(exists(find_action(
-      community_actions::blocked,
-      (for_person_id, for_community_id),
-    ))))
-    .get_result::<bool>(conn)
-    .await?
-    .then_some(())
-    .ok_or(LemmyErrorType::CommunityIsBlocked.into())
+    let find_action = community_actions::table
+      .find((for_person_id, for_community_id))
+      .filter(community_actions::blocked.is_not_null());
+    select(not(exists(find_action)))
+      .get_result::<bool>(conn)
+      .await?
+      .then_some(())
+      .ok_or(LemmyErrorType::CommunityIsBlocked.into())
   }
 
   pub async fn for_person(
@@ -42,7 +42,8 @@ impl CommunityBlock {
     person_id: PersonId,
   ) -> Result<Vec<Community>, Error> {
     let conn = &mut get_conn(pool).await?;
-    action_query(community_actions::blocked)
+    community_actions::table
+      .filter(community_actions::blocked.is_not_null())
       .inner_join(community::table)
       .select(community::all_columns)
       .filter(community_actions::person_id.eq(person_id))
diff --git a/crates/db_schema/src/impls/instance_block.rs b/crates/db_schema/src/impls/instance_block.rs
index 1722e8318a..1ec6d22013 100644
--- a/crates/db_schema/src/impls/instance_block.rs
+++ b/crates/db_schema/src/impls/instance_block.rs
@@ -6,7 +6,7 @@ use crate::{
     instance_block::{InstanceBlock, InstanceBlockForm},
   },
   traits::Blockable,
-  utils::{action_query, find_action, get_conn, now, uplete, DbPool},
+  utils::{get_conn, now, uplete, DbPool},
 };
 use diesel::{
   dsl::{exists, insert_into, not},
@@ -27,14 +27,14 @@ impl InstanceBlock {
     for_instance_id: InstanceId,
   ) -> LemmyResult<()> {
     let conn = &mut get_conn(pool).await?;
-    select(not(exists(find_action(
-      instance_actions::blocked,
-      (for_person_id, for_instance_id),
-    ))))
-    .get_result::<bool>(conn)
-    .await?
-    .then_some(())
-    .ok_or(LemmyErrorType::InstanceIsBlocked.into())
+    let find_action = instance_actions::table
+      .find((for_person_id, for_instance_id))
+      .filter(instance_actions::blocked.is_not_null());
+    select(not(exists(find_action)))
+      .get_result::<bool>(conn)
+      .await?
+      .then_some(())
+      .ok_or(LemmyErrorType::InstanceIsBlocked.into())
   }
 
   pub async fn for_person(
@@ -42,7 +42,8 @@ impl InstanceBlock {
     person_id: PersonId,
   ) -> Result<Vec<Instance>, Error> {
     let conn = &mut get_conn(pool).await?;
-    action_query(instance_actions::blocked)
+    instance_actions::table
+      .filter(instance_actions::blocked.is_not_null())
       .inner_join(instance::table)
       .select(instance::all_columns)
       .filter(instance_actions::person_id.eq(person_id))
diff --git a/crates/db_schema/src/impls/local_user.rs b/crates/db_schema/src/impls/local_user.rs
index 5c184113e3..e6c02ced13 100644
--- a/crates/db_schema/src/impls/local_user.rs
+++ b/crates/db_schema/src/impls/local_user.rs
@@ -8,7 +8,6 @@ use crate::{
     site::Site,
   },
   utils::{
-    action_query,
     functions::{coalesce, lower},
     get_conn,
     now,
@@ -168,42 +167,48 @@ impl LocalUser {
     };
     let conn = &mut get_conn(pool).await?;
 
-    let followed_communities = action_query(community_actions::followed)
+    let followed_communities = community_actions::table
+      .filter(community_actions::followed.is_not_null())
       .filter(community_actions::person_id.eq(person_id_))
       .inner_join(community::table)
       .select(community::actor_id)
       .get_results(conn)
       .await?;
 
-    let saved_posts = action_query(post_actions::saved)
+    let saved_posts = post_actions::table
+      .filter(post_actions::saved.is_not_null())
       .filter(post_actions::person_id.eq(person_id_))
       .inner_join(post::table)
       .select(post::ap_id)
       .get_results(conn)
       .await?;
 
-    let saved_comments = action_query(comment_actions::saved)
+    let saved_comments = comment_actions::table
+      .filter(comment_actions::saved.is_not_null())
       .filter(comment_actions::person_id.eq(person_id_))
       .inner_join(comment::table)
       .select(comment::ap_id)
       .get_results(conn)
       .await?;
 
-    let blocked_communities = action_query(community_actions::blocked)
+    let blocked_communities = community_actions::table
+      .filter(community_actions::blocked.is_not_null())
       .filter(community_actions::person_id.eq(person_id_))
       .inner_join(community::table)
       .select(community::actor_id)
       .get_results(conn)
       .await?;
 
-    let blocked_users = action_query(person_actions::blocked)
+    let blocked_users = person_actions::table
+      .filter(person_actions::blocked.is_not_null())
       .filter(person_actions::person_id.eq(person_id_))
       .inner_join(person::table.on(person_actions::target_id.eq(person::id)))
       .select(person::actor_id)
       .get_results(conn)
       .await?;
 
-    let blocked_instances = action_query(instance_actions::blocked)
+    let blocked_instances = instance_actions::table
+      .filter(instance_actions::blocked.is_not_null())
       .filter(instance_actions::person_id.eq(person_id_))
       .inner_join(instance::table)
       .select(instance::domain)
@@ -271,7 +276,8 @@ impl LocalUser {
       .order_by(local_user::id)
       .select(local_user::person_id);
 
-    let mods = action_query(community_actions::became_moderator)
+    let mods = community_actions::table
+      .filter(community_actions::became_moderator.is_not_null())
       .filter(community_actions::community_id.eq(for_community_id))
       .filter(community_actions::person_id.eq_any(&persons))
       .order_by(community_actions::became_moderator)
diff --git a/crates/db_schema/src/impls/person.rs b/crates/db_schema/src/impls/person.rs
index fb8c96f040..70e2f7f8db 100644
--- a/crates/db_schema/src/impls/person.rs
+++ b/crates/db_schema/src/impls/person.rs
@@ -10,7 +10,7 @@ use crate::{
     PersonUpdateForm,
   },
   traits::{ApubActor, Crud, Followable},
-  utils::{action_query, functions::lower, get_conn, now, uplete, DbPool},
+  utils::{functions::lower, get_conn, now, uplete, DbPool},
 };
 use chrono::Utc;
 use diesel::{
@@ -235,7 +235,8 @@ impl PersonFollower {
     for_person_id: PersonId,
   ) -> Result<Vec<Person>, Error> {
     let conn = &mut get_conn(pool).await?;
-    action_query(person_actions::followed)
+    person_actions::table
+      .filter(person_actions::followed.is_not_null())
       .inner_join(person::table.on(person_actions::person_id.eq(person::id)))
       .filter(person_actions::target_id.eq(for_person_id))
       .select(person::all_columns)
diff --git a/crates/db_schema/src/impls/person_block.rs b/crates/db_schema/src/impls/person_block.rs
index 363a2d3d1a..91f45494c0 100644
--- a/crates/db_schema/src/impls/person_block.rs
+++ b/crates/db_schema/src/impls/person_block.rs
@@ -6,7 +6,7 @@ use crate::{
     person_block::{PersonBlock, PersonBlockForm},
   },
   traits::Blockable,
-  utils::{action_query, find_action, get_conn, now, uplete, DbPool},
+  utils::{get_conn, now, uplete, DbPool},
 };
 use diesel::{
   dsl::{exists, insert_into, not},
@@ -28,14 +28,14 @@ impl PersonBlock {
     for_recipient_id: PersonId,
   ) -> LemmyResult<()> {
     let conn = &mut get_conn(pool).await?;
-    select(not(exists(find_action(
-      person_actions::blocked,
-      (for_person_id, for_recipient_id),
-    ))))
-    .get_result::<bool>(conn)
-    .await?
-    .then_some(())
-    .ok_or(LemmyErrorType::PersonIsBlocked.into())
+    let find_action = person_actions::table
+      .find((for_person_id, for_recipient_id))
+      .filter(person_actions::blocked.is_not_null());
+    select(not(exists(find_action)))
+      .get_result::<bool>(conn)
+      .await?
+      .then_some(())
+      .ok_or(LemmyErrorType::PersonIsBlocked.into())
   }
 
   pub async fn for_person(
@@ -45,7 +45,8 @@ impl PersonBlock {
     let conn = &mut get_conn(pool).await?;
     let target_person_alias = diesel::alias!(person as person1);
 
-    action_query(person_actions::blocked)
+    person_actions::table
+      .filter(person_actions::blocked.is_not_null())
       .inner_join(person::table.on(person_actions::person_id.eq(person::id)))
       .inner_join(
         target_person_alias.on(person_actions::target_id.eq(target_person_alias.field(person::id))),
diff --git a/crates/db_schema/src/impls/registration_application.rs b/crates/db_schema/src/impls/registration_application.rs
index d9777919db..9faa1235dc 100644
--- a/crates/db_schema/src/impls/registration_application.rs
+++ b/crates/db_schema/src/impls/registration_application.rs
@@ -1,6 +1,6 @@
 use crate::{
   newtypes::{LocalUserId, RegistrationApplicationId},
-  schema::registration_application::dsl::{local_user_id, registration_application},
+  schema::registration_application,
   source::registration_application::{
     RegistrationApplication,
     RegistrationApplicationInsertForm,
@@ -20,7 +20,7 @@ impl Crud for RegistrationApplication {
 
   async fn create(pool: &mut DbPool<'_>, form: &Self::InsertForm) -> Result<Self, Error> {
     let conn = &mut get_conn(pool).await?;
-    insert_into(registration_application)
+    insert_into(registration_application::table)
       .values(form)
       .get_result::<Self>(conn)
       .await
@@ -32,7 +32,7 @@ impl Crud for RegistrationApplication {
     form: &Self::UpdateForm,
   ) -> Result<Self, Error> {
     let conn = &mut get_conn(pool).await?;
-    diesel::update(registration_application.find(id_))
+    diesel::update(registration_application::table.find(id_))
       .set(form)
       .get_result::<Self>(conn)
       .await
@@ -45,9 +45,15 @@ impl RegistrationApplication {
     local_user_id_: LocalUserId,
   ) -> Result<Self, Error> {
     let conn = &mut get_conn(pool).await?;
-    registration_application
-      .filter(local_user_id.eq(local_user_id_))
+    registration_application::table
+      .filter(registration_application::local_user_id.eq(local_user_id_))
       .first(conn)
       .await
   }
+
+  /// A missing admin id, means the application is unread
+  #[diesel::dsl::auto_type(no_type_alias)]
+  pub fn is_unread() -> _ {
+    registration_application::admin_id.is_null()
+  }
 }
diff --git a/crates/db_schema/src/lib.rs b/crates/db_schema/src/lib.rs
index 980e6ac8d6..c5fa14f5ab 100644
--- a/crates/db_schema/src/lib.rs
+++ b/crates/db_schema/src/lib.rs
@@ -1,5 +1,3 @@
-#![recursion_limit = "256"]
-
 #[cfg(feature = "full")]
 #[macro_use]
 extern crate diesel;
@@ -41,6 +39,10 @@ pub mod utils;
 #[cfg(feature = "full")]
 pub mod schema_setup;
 
+#[cfg(feature = "full")]
+use diesel::query_source::AliasedField;
+#[cfg(feature = "full")]
+use schema::person;
 use serde::{Deserialize, Serialize};
 use strum::{Display, EnumString};
 #[cfg(feature = "full")]
@@ -328,3 +330,28 @@ macro_rules! assert_length {
     assert_eq!($len, $vec.len(), "Vec has wrong length: {:?}", $vec)
   }};
 }
+
+#[cfg(feature = "full")]
+/// A helper tuple for person alias columns
+pub type Person1AliasAllColumnsTuple = (
+  AliasedField<aliases::Person1, person::id>,
+  AliasedField<aliases::Person1, person::name>,
+  AliasedField<aliases::Person1, person::display_name>,
+  AliasedField<aliases::Person1, person::avatar>,
+  AliasedField<aliases::Person1, person::banned>,
+  AliasedField<aliases::Person1, person::published>,
+  AliasedField<aliases::Person1, person::updated>,
+  AliasedField<aliases::Person1, person::actor_id>,
+  AliasedField<aliases::Person1, person::bio>,
+  AliasedField<aliases::Person1, person::local>,
+  AliasedField<aliases::Person1, person::private_key>,
+  AliasedField<aliases::Person1, person::public_key>,
+  AliasedField<aliases::Person1, person::last_refreshed_at>,
+  AliasedField<aliases::Person1, person::banner>,
+  AliasedField<aliases::Person1, person::deleted>,
+  AliasedField<aliases::Person1, person::inbox_url>,
+  AliasedField<aliases::Person1, person::matrix_user_id>,
+  AliasedField<aliases::Person1, person::bot_account>,
+  AliasedField<aliases::Person1, person::ban_expires>,
+  AliasedField<aliases::Person1, person::instance_id>,
+);
diff --git a/crates/db_schema/src/source/local_site.rs b/crates/db_schema/src/source/local_site.rs
index 25ec40ca92..46b92d3403 100644
--- a/crates/db_schema/src/source/local_site.rs
+++ b/crates/db_schema/src/source/local_site.rs
@@ -17,7 +17,7 @@ use ts_rs::TS;
 
 #[skip_serializing_none]
 #[derive(PartialEq, Eq, Debug, Clone, Serialize, Deserialize, Default)]
-#[cfg_attr(feature = "full", derive(Queryable, Identifiable, TS))]
+#[cfg_attr(feature = "full", derive(Queryable, Selectable, Identifiable, TS))]
 #[cfg_attr(feature = "full", diesel(table_name = local_site))]
 #[cfg_attr(feature = "full", diesel(belongs_to(crate::source::site::Site)))]
 #[cfg_attr(feature = "full", diesel(check_for_backend(diesel::pg::Pg)))]
diff --git a/crates/db_schema/src/utils.rs b/crates/db_schema/src/utils.rs
index 312f219e1d..7597c5bad5 100644
--- a/crates/db_schema/src/utils.rs
+++ b/crates/db_schema/src/utils.rs
@@ -9,22 +9,15 @@ use diesel::{
   helper_types::AsExprOf,
   pg::Pg,
   query_builder::{Query, QueryFragment},
-  query_dsl::methods::{FilterDsl, FindDsl, LimitDsl},
-  query_source::{Alias, AliasSource, AliasedField},
+  query_dsl::methods::LimitDsl,
   result::{
     ConnectionError,
     ConnectionResult,
     Error::{self as DieselError, QueryBuilderError},
   },
-  sql_types::{self, SingleValue, Timestamptz},
-  Column,
+  sql_types::{self, Timestamptz},
   Expression,
-  ExpressionMethods,
   IntoSql,
-  JoinOnDsl,
-  NullableExpressionMethods,
-  QuerySource,
-  Table,
 };
 use diesel_async::{
   pg::AsyncPgConnection,
@@ -35,8 +28,7 @@ use diesel_async::{
   },
   AsyncConnection,
 };
-use diesel_bind_if_some::BindIfSome;
-use futures_util::{future::BoxFuture, Future, FutureExt};
+use futures_util::{future::BoxFuture, FutureExt};
 use i_love_jesus::{CursorKey, PaginatedQueryBuilder};
 use lemmy_utils::{
   error::{LemmyErrorExt, LemmyErrorType, LemmyResult},
@@ -580,102 +572,6 @@ impl<T: Expression + AsExpression<sql_types::Record<T::SqlType>>> AsRecord for T
 /// Output of `IntoSql::into_sql` for a type that implements `AsRecord`
 pub type AsRecordOutput<T> = dsl::AsExprOf<T, sql_types::Record<<T as Expression>::SqlType>>;
 
-/// Output of `t.on((l0, l1).into_sql().eq((r0, r1)))`
-type OnTupleEq<T, L0, L1, R0, R1> = dsl::On<T, dsl::Eq<AsRecordOutput<(L0, L1)>, (R0, R1)>>;
-
-/// Creates an `ON` clause for a table where a person ID and another column are used as the
-/// primary key. Use with the `QueryDsl::left_join` method.
-///
-/// This example modifies a query to make columns in `community_actions` available:
-///
-/// ```
-/// community::table
-///   .left_join(actions(
-///     community_actions::table,
-///     my_person_id,
-///     community::id,
-///   ))
-/// ```
-pub fn actions<T, P, C, K0, K1>(
-  actions_table: T,
-  person_id: Option<P>,
-  target_id: C,
-) -> OnTupleEq<T, dsl::Nullable<K0>, K1, BindIfSome<dsl::AsExprOf<P, sql_types::Integer>>, C>
-where
-  T: Table<PrimaryKey = (K0, K1)> + Copy,
-  K0: Expression,
-  P: AsExpression<sql_types::Integer>,
-  (dsl::Nullable<K0>, K1): AsRecord,
-  (BindIfSome<dsl::AsExprOf<P, sql_types::Integer>>, C):
-    AsExpression<<AsRecordOutput<(dsl::Nullable<K0>, K1)> as Expression>::SqlType>,
-{
-  let (k0, k1) = actions_table.primary_key();
-  actions_table.on((k0.nullable(), k1).into_sql().eq((
-    BindIfSome(person_id.map(diesel::IntoSql::into_sql)),
-    target_id,
-  )))
-}
-
-/// Like `actions` but `actions_table` is an alias and person id is not nullable
-#[allow(clippy::type_complexity)]
-pub fn actions_alias<T, P, C, K0, K1>(
-  actions_table: Alias<T>,
-  person_id: P,
-  target_id: C,
-) -> OnTupleEq<Alias<T>, AliasedField<T, K0>, AliasedField<T, K1>, P, C>
-where
-  Alias<T>: QuerySource + Copy,
-  T: AliasSource<Target: Table<PrimaryKey = (K0, K1)>> + Default,
-  K0: Column<Table = T::Target>,
-  K1: Column<Table = T::Target>,
-  (AliasedField<T, K0>, AliasedField<T, K1>): AsRecord,
-  (P, C): AsExpression<
-    <AsRecordOutput<(AliasedField<T, K0>, AliasedField<T, K1>)> as Expression>::SqlType,
-  >,
-{
-  let (k0, k1) = T::default().target().primary_key();
-  actions_table.on(
-    (actions_table.field(k0), actions_table.field(k1))
-      .into_sql()
-      .eq((person_id, target_id)),
-  )
-}
-
-/// `action_query(table_name::action_name)` is the same as
-/// `table_name::table.filter(table_name::action_name.is_not_null())`.
-pub fn action_query<C>(column: C) -> dsl::Filter<C::Table, dsl::IsNotNull<C>>
-where
-  C: Column<Table: Default + FilterDsl<dsl::IsNotNull<C>>, SqlType: SingleValue>,
-{
-  action_query_with_fn(column, |t| t)
-}
-
-/// `find_action(table_name::action_name, key)` is the same as
-/// `table_name::table.find(key).filter(table_name::action_name.is_not_null())`.
-pub fn find_action<C, K>(
-  column: C,
-  key: K,
-) -> dsl::Filter<dsl::Find<C::Table, K>, dsl::IsNotNull<C>>
-where
-  C:
-    Column<Table: Default + FindDsl<K, Output: FilterDsl<dsl::IsNotNull<C>>>, SqlType: SingleValue>,
-{
-  action_query_with_fn(column, |t| t.find(key))
-}
-
-/// `action_query_with_fn(table_name::action_name, f)` is the same as
-/// `f(table_name::table).filter(table_name::action_name.is_not_null())`.
-fn action_query_with_fn<C, Q>(
-  column: C,
-  f: impl FnOnce(C::Table) -> Q,
-) -> dsl::Filter<Q, dsl::IsNotNull<C>>
-where
-  C: Column<Table: Default, SqlType: SingleValue>,
-  Q: FilterDsl<dsl::IsNotNull<C>>,
-{
-  f(C::Table::default()).filter(column.is_not_null())
-}
-
 pub type ResultFuture<'a, T> = BoxFuture<'a, Result<T, DieselError>>;
 
 pub trait ReadFn<'a, T, Args>: Fn(DbConn<'a>, Args) -> ResultFuture<'a, T> {}
@@ -686,58 +582,6 @@ pub trait ListFn<'a, T, Args>: Fn(DbConn<'a>, Args) -> ResultFuture<'a, Vec<T>>
 
 impl<'a, T, Args, F: Fn(DbConn<'a>, Args) -> ResultFuture<'a, Vec<T>>> ListFn<'a, T, Args> for F {}
 
-/// Allows read and list functions to capture a shared closure that has an inferred return type,
-/// which is useful for join logic
-pub struct Queries<RF, LF> {
-  pub read_fn: RF,
-  pub list_fn: LF,
-}
-
-// `()` is used to prevent type inference error
-impl Queries<(), ()> {
-  pub fn new<'a, RFut, LFut, RT, LT, RA, LA, RF2, LF2>(
-    read_fn: RF2,
-    list_fn: LF2,
-  ) -> Queries<impl ReadFn<'a, RT, RA>, impl ListFn<'a, LT, LA>>
-  where
-    RFut: Future<Output = Result<RT, DieselError>> + Sized + Send + 'a,
-    LFut: Future<Output = Result<Vec<LT>, DieselError>> + Sized + Send + 'a,
-    RF2: Fn(DbConn<'a>, RA) -> RFut,
-    LF2: Fn(DbConn<'a>, LA) -> LFut,
-  {
-    Queries {
-      read_fn: move |conn, args| read_fn(conn, args).boxed(),
-      list_fn: move |conn, args| list_fn(conn, args).boxed(),
-    }
-  }
-}
-
-impl<RF, LF> Queries<RF, LF> {
-  pub async fn read<'a, T, Args>(
-    self,
-    pool: &'a mut DbPool<'_>,
-    args: Args,
-  ) -> Result<T, DieselError>
-  where
-    RF: ReadFn<'a, T, Args>,
-  {
-    let conn = get_conn(pool).await?;
-    (self.read_fn)(conn, args).await
-  }
-
-  pub async fn list<'a, T, Args>(
-    self,
-    pool: &'a mut DbPool<'_>,
-    args: Args,
-  ) -> Result<Vec<T>, DieselError>
-  where
-    LF: ListFn<'a, T, Args>,
-  {
-    let conn = get_conn(pool).await?;
-    (self.list_fn)(conn, args).await
-  }
-}
-
 pub fn paginate<Q, C>(
   query: Q,
   page_after: Option<C>,
diff --git a/crates/db_views/Cargo.toml b/crates/db_views/Cargo.toml
index 1e1f3315db..79b7a80f62 100644
--- a/crates/db_views/Cargo.toml
+++ b/crates/db_views/Cargo.toml
@@ -41,8 +41,7 @@ ts-rs = { workspace = true, optional = true }
 actix-web = { workspace = true, optional = true }
 i-love-jesus = { workspace = true, optional = true }
 chrono = { workspace = true }
-derive-new.workspace = true
-strum = { workspace = true }
+derive-new = { workspace = true }
 
 [dev-dependencies]
 serial_test = { workspace = true }
diff --git a/crates/db_views/src/combined/inbox_combined_view.rs b/crates/db_views/src/combined/inbox_combined_view.rs
index 0e51c4ab35..7e7bc1f206 100644
--- a/crates/db_views/src/combined/inbox_combined_view.rs
+++ b/crates/db_views/src/combined/inbox_combined_view.rs
@@ -49,35 +49,17 @@ use lemmy_db_schema::{
     community::CommunityFollower,
   },
   traits::InternalToCombinedView,
-  utils::{actions, actions_alias, functions::coalesce, get_conn, DbPool},
+  utils::{functions::coalesce, get_conn, DbPool},
   InboxDataType,
 };
 use lemmy_utils::error::LemmyResult;
 
 impl InboxCombinedViewInternal {
-  /// Gets the number of unread mentions
-  pub async fn get_unread_count(
-    pool: &mut DbPool<'_>,
-    my_person_id: PersonId,
-    show_bot_accounts: bool,
-  ) -> Result<i64, Error> {
-    use diesel::dsl::count;
-    let conn = &mut get_conn(pool).await?;
-
+  #[diesel::dsl::auto_type(no_type_alias)]
+  fn joins(my_person_id: PersonId) -> _ {
     let item_creator = person::id;
     let recipient_person = aliases::person1.field(person::id);
 
-    let unread_filter = comment_reply::read
-      .eq(false)
-      .or(person_comment_mention::read.eq(false))
-      .or(person_post_mention::read.eq(false))
-      // If its unread, I only want the messages to me
-      .or(
-        private_message::read
-          .eq(false)
-          .and(private_message::recipient_id.eq(my_person_id)),
-      );
-
     let item_creator_join = comment::creator_id
       .eq(item_creator)
       .or(
@@ -87,11 +69,13 @@ impl InboxCombinedViewInternal {
       )
       .or(private_message::creator_id.eq(item_creator));
 
-    let recipient_join = comment_reply::recipient_id
-      .eq(recipient_person)
-      .or(person_comment_mention::recipient_id.eq(recipient_person))
-      .or(person_post_mention::recipient_id.eq(recipient_person))
-      .or(private_message::recipient_id.eq(recipient_person));
+    let recipient_join = aliases::person1.on(
+      comment_reply::recipient_id
+        .eq(recipient_person)
+        .or(person_comment_mention::recipient_id.eq(recipient_person))
+        .or(person_post_mention::recipient_id.eq(recipient_person))
+        .or(private_message::recipient_id.eq(recipient_person)),
+    );
 
     let comment_join = comment_reply::comment_id
       .eq(comment::id)
@@ -112,27 +96,107 @@ impl InboxCombinedViewInternal {
       .eq(private_message::id.nullable())
       .and(not(private_message::deleted));
 
-    let mut query = inbox_combined::table
+    let community_join = post::community_id.eq(community::id);
+
+    let local_user_join = local_user::table.on(
+      item_creator
+        .eq(local_user::person_id)
+        .and(local_user::admin.eq(true)),
+    );
+
+    let post_aggregates_join = post_aggregates::table.on(post::id.eq(post_aggregates::post_id));
+    let comment_aggregates_join =
+      comment_aggregates::table.on(comment::id.eq(comment_aggregates::comment_id));
+
+    let image_details_join =
+      image_details::table.on(post::thumbnail_url.eq(image_details::link.nullable()));
+
+    let creator_community_actions_join = creator_community_actions.on(
+      creator_community_actions
+        .field(community_actions::community_id)
+        .eq(post::community_id)
+        .and(
+          creator_community_actions
+            .field(community_actions::person_id)
+            .eq(item_creator),
+        ),
+    );
+
+    let community_actions_join = community_actions::table.on(
+      community_actions::community_id
+        .eq(post::community_id)
+        .and(community_actions::person_id.eq(my_person_id)),
+    );
+
+    let instance_actions_join = instance_actions::table.on(
+      instance_actions::instance_id
+        .eq(person::instance_id)
+        .and(instance_actions::person_id.eq(my_person_id)),
+    );
+
+    let post_actions_join = post_actions::table.on(
+      post_actions::post_id
+        .eq(post::id)
+        .and(post_actions::person_id.eq(my_person_id)),
+    );
+
+    let person_actions_join = person_actions::table.on(
+      person_actions::target_id
+        .eq(item_creator)
+        .and(person_actions::person_id.eq(my_person_id)),
+    );
+
+    let comment_actions_join = comment_actions::table.on(
+      comment_actions::comment_id
+        .eq(comment::id)
+        .and(comment_actions::person_id.eq(my_person_id)),
+    );
+
+    inbox_combined::table
       .left_join(comment_reply::table)
       .left_join(person_comment_mention::table)
       .left_join(person_post_mention::table)
       .left_join(private_message::table.on(private_message_join))
       .left_join(comment::table.on(comment_join))
       .left_join(post::table.on(post_join))
-      // The item creator
+      .left_join(community::table.on(community_join))
       .inner_join(person::table.on(item_creator_join))
-      // The recipient
-      .inner_join(aliases::person1.on(recipient_join))
-      .left_join(actions(
-        instance_actions::table,
-        Some(my_person_id),
-        person::instance_id,
-      ))
-      .left_join(actions(
-        person_actions::table,
-        Some(my_person_id),
-        item_creator,
-      ))
+      .inner_join(recipient_join)
+      .left_join(image_details_join)
+      .left_join(post_aggregates_join)
+      .left_join(comment_aggregates_join)
+      .left_join(creator_community_actions_join)
+      .left_join(local_user_join)
+      .left_join(community_actions_join)
+      .left_join(instance_actions_join)
+      .left_join(post_actions_join)
+      .left_join(person_actions_join)
+      .left_join(comment_actions_join)
+  }
+
+  /// Gets the number of unread mentions
+  pub async fn get_unread_count(
+    pool: &mut DbPool<'_>,
+    my_person_id: PersonId,
+    show_bot_accounts: bool,
+  ) -> Result<i64, Error> {
+    use diesel::dsl::count;
+    let conn = &mut get_conn(pool).await?;
+
+    let recipient_person = aliases::person1.field(person::id);
+
+    let unread_filter = comment_reply::read
+      .eq(false)
+      .or(person_comment_mention::read.eq(false))
+      .or(person_post_mention::read.eq(false))
+      // If its unread, I only want the messages to me
+      .or(
+        private_message::read
+          .eq(false)
+          .and(private_message::recipient_id.eq(my_person_id)),
+      );
+
+    let mut query = Self::joins(my_person_id)
       // Filter for your user
       .filter(recipient_person.eq(my_person_id))
       // Filter unreads
@@ -140,6 +204,7 @@ impl InboxCombinedViewInternal {
       // Don't count replies from blocked users
       .filter(person_actions::blocked.is_null())
       .filter(instance_actions::blocked.is_null())
+      .select(count(inbox_combined::id))
       .into_boxed();
 
     // These filters need to be kept in sync with the filters in queries().list()
@@ -147,10 +212,7 @@ impl InboxCombinedViewInternal {
       query = query.filter(not(person::bot_account));
     }
 
-    query
-      .select(count(inbox_combined::id))
-      .first::<i64>(conn)
-      .await
+    query.first::<i64>(conn).await
   }
 }
 
@@ -210,42 +272,6 @@ impl InboxCombinedQuery {
     let item_creator = person::id;
     let recipient_person = aliases::person1.field(person::id);
 
-    let item_creator_join = comment::creator_id
-      .eq(item_creator)
-      .or(
-        inbox_combined::person_post_mention_id
-          .is_not_null()
-          .and(post::creator_id.eq(item_creator)),
-      )
-      .or(private_message::creator_id.eq(item_creator));
-
-    let recipient_join = comment_reply::recipient_id
-      .eq(recipient_person)
-      .or(person_comment_mention::recipient_id.eq(recipient_person))
-      .or(person_post_mention::recipient_id.eq(recipient_person))
-      .or(private_message::recipient_id.eq(recipient_person));
-
-    let comment_join = comment_reply::comment_id
-      .eq(comment::id)
-      .or(person_comment_mention::comment_id.eq(comment::id))
-      // Filter out the deleted / removed
-      .and(not(comment::deleted))
-      .and(not(comment::removed));
-
-    let post_join = person_post_mention::post_id
-      .eq(post::id)
-      .or(comment::post_id.eq(post::id))
-      // Filter out the deleted / removed
-      .and(not(post::deleted))
-      .and(not(post::removed));
-
-    // This could be a simple join, but you need to check for deleted here
-    let private_message_join = inbox_combined::private_message_id
-      .eq(private_message::id.nullable())
-      .and(not(private_message::deleted));
-
-    let community_join = post::community_id.eq(community::id);
-
     let post_tags = post_tag::table
       .inner_join(tag::table)
       .select(diesel::dsl::sql::<diesel::sql_types::Json>(
@@ -255,54 +281,7 @@ impl InboxCombinedQuery {
       .filter(tag::deleted.eq(false))
       .single_value();
 
-    let mut query = inbox_combined::table
-      .left_join(comment_reply::table)
-      .left_join(person_comment_mention::table)
-      .left_join(person_post_mention::table)
-      .left_join(private_message::table.on(private_message_join))
-      .left_join(comment::table.on(comment_join))
-      .left_join(post::table.on(post_join))
-      .left_join(community::table.on(community_join))
-      // The item creator
-      .inner_join(person::table.on(item_creator_join))
-      // The recipient
-      .inner_join(aliases::person1.on(recipient_join))
-      .left_join(actions_alias(
-        creator_community_actions,
-        item_creator,
-        post::community_id,
-      ))
-      .left_join(
-        local_user::table.on(
-          item_creator
-            .eq(local_user::person_id)
-            .and(local_user::admin.eq(true)),
-        ),
-      )
-      .left_join(actions(
-        community_actions::table,
-        Some(my_person_id),
-        post::community_id,
-      ))
-      .left_join(actions(
-        instance_actions::table,
-        Some(my_person_id),
-        person::instance_id,
-      ))
-      .left_join(actions(post_actions::table, Some(my_person_id), post::id))
-      .left_join(actions(
-        person_actions::table,
-        Some(my_person_id),
-        item_creator,
-      ))
-      .left_join(post_aggregates::table.on(post::id.eq(post_aggregates::post_id)))
-      .left_join(comment_aggregates::table.on(comment::id.eq(comment_aggregates::comment_id)))
-      .left_join(actions(
-        comment_actions::table,
-        Some(my_person_id),
-        comment::id,
-      ))
-      .left_join(image_details::table.on(post::thumbnail_url.eq(image_details::link.nullable())))
+    let mut query = InboxCombinedViewInternal::joins(my_person_id)
       .select((
         // Specific
         comment_reply::all_columns.nullable(),
diff --git a/crates/db_views/src/combined/modlog_combined_view.rs b/crates/db_views/src/combined/modlog_combined_view.rs
index a492be07ce..af2b655cc6 100644
--- a/crates/db_views/src/combined/modlog_combined_view.rs
+++ b/crates/db_views/src/combined/modlog_combined_view.rs
@@ -66,130 +66,40 @@ use lemmy_db_schema::{
   ModlogActionType,
 };
 use lemmy_utils::error::LemmyResult;
-
-impl ModlogCombinedPaginationCursor {
-  // get cursor for page that starts immediately after the given post
-  pub fn after_post(view: &ModlogCombinedView) -> ModlogCombinedPaginationCursor {
-    let (prefix, id) = match view {
-      ModlogCombinedView::AdminAllowInstance(v) => {
-        ("AdminAllowInstance", v.admin_allow_instance.id.0)
-      }
-      ModlogCombinedView::AdminBlockInstance(v) => {
-        ("AdminBlockInstance", v.admin_block_instance.id.0)
-      }
-      ModlogCombinedView::AdminPurgeComment(v) => ("AdminPurgeComment", v.admin_purge_comment.id.0),
-      ModlogCombinedView::AdminPurgeCommunity(v) => {
-        ("AdminPurgeCommunity", v.admin_purge_community.id.0)
-      }
-      ModlogCombinedView::AdminPurgePerson(v) => ("AdminPurgePerson", v.admin_purge_person.id.0),
-      ModlogCombinedView::AdminPurgePost(v) => ("AdminPurgePost", v.admin_purge_post.id.0),
-      ModlogCombinedView::ModAdd(v) => ("ModAdd", v.mod_add.id.0),
-      ModlogCombinedView::ModAddCommunity(v) => ("ModAddCommunity", v.mod_add_community.id.0),
-      ModlogCombinedView::ModBan(v) => ("ModBan", v.mod_ban.id.0),
-      ModlogCombinedView::ModBanFromCommunity(v) => {
-        ("ModBanFromCommunity", v.mod_ban_from_community.id.0)
-      }
-      ModlogCombinedView::ModFeaturePost(v) => ("ModFeaturePost", v.mod_feature_post.id.0),
-      ModlogCombinedView::ModHideCommunity(v) => ("ModHideCommunity", v.mod_hide_community.id.0),
-      ModlogCombinedView::ModLockPost(v) => ("ModLockPost", v.mod_lock_post.id.0),
-      ModlogCombinedView::ModRemoveComment(v) => ("ModRemoveComment", v.mod_remove_comment.id.0),
-      ModlogCombinedView::ModRemoveCommunity(v) => {
-        ("ModRemoveCommunity", v.mod_remove_community.id.0)
-      }
-      ModlogCombinedView::ModRemovePost(v) => ("ModRemovePost", v.mod_remove_post.id.0),
-      ModlogCombinedView::ModTransferCommunity(v) => {
-        ("ModTransferCommunity", v.mod_transfer_community.id.0)
-      }
-    };
-    // hex encoding to prevent ossification
-    ModlogCombinedPaginationCursor(format!("{prefix}-{id:x}"))
-  }
-
-  pub async fn read(&self, pool: &mut DbPool<'_>) -> Result<PaginationCursorData, Error> {
-    let err_msg = || Error::QueryBuilderError("Could not parse pagination token".into());
-    let mut query = modlog_combined::table
-      .select(ModlogCombined::as_select())
-      .into_boxed();
-    let (prefix, id_str) = self.0.split_once('-').ok_or_else(err_msg)?;
-    let id = i32::from_str_radix(id_str, 16).map_err(|_err| err_msg())?;
-    query = match prefix {
-      "AdminAllowInstance" => query.filter(modlog_combined::admin_allow_instance_id.eq(id)),
-      "AdminBlockInstance" => query.filter(modlog_combined::admin_block_instance_id.eq(id)),
-      "AdminPurgeComment" => query.filter(modlog_combined::admin_purge_comment_id.eq(id)),
-      "AdminPurgeCommunity" => query.filter(modlog_combined::admin_purge_community_id.eq(id)),
-      "AdminPurgePerson" => query.filter(modlog_combined::admin_purge_person_id.eq(id)),
-      "AdminPurgePost" => query.filter(modlog_combined::admin_purge_post_id.eq(id)),
-      "ModAdd" => query.filter(modlog_combined::mod_add_id.eq(id)),
-      "ModAddCommunity" => query.filter(modlog_combined::mod_add_community_id.eq(id)),
-      "ModBan" => query.filter(modlog_combined::mod_ban_id.eq(id)),
-      "ModBanFromCommunity" => query.filter(modlog_combined::mod_ban_from_community_id.eq(id)),
-      "ModFeaturePost" => query.filter(modlog_combined::mod_feature_post_id.eq(id)),
-      "ModHideCommunity" => query.filter(modlog_combined::mod_hide_community_id.eq(id)),
-      "ModLockPost" => query.filter(modlog_combined::mod_lock_post_id.eq(id)),
-      "ModRemoveComment" => query.filter(modlog_combined::mod_remove_comment_id.eq(id)),
-      "ModRemoveCommunity" => query.filter(modlog_combined::mod_remove_community_id.eq(id)),
-      "ModRemovePost" => query.filter(modlog_combined::mod_remove_post_id.eq(id)),
-      "ModTransferCommunity" => query.filter(modlog_combined::mod_transfer_community_id.eq(id)),
-
-      _ => return Err(err_msg()),
-    };
-    let token = query.first(&mut get_conn(pool).await?).await?;
-
-    Ok(PaginationCursorData(token))
-  }
-}
-
-#[derive(Clone)]
-pub struct PaginationCursorData(ModlogCombined);
-
-#[derive(Default)]
-/// Querying / filtering the modlog.
-pub struct ModlogCombinedQuery {
-  pub type_: Option<ModlogActionType>,
-  pub comment_id: Option<CommentId>,
-  pub post_id: Option<PostId>,
-  pub community_id: Option<CommunityId>,
-  pub hide_modlog_names: Option<bool>,
-  pub mod_person_id: Option<PersonId>,
-  pub other_person_id: Option<PersonId>,
-  pub page_after: Option<PaginationCursorData>,
-  pub page_back: Option<bool>,
-}
-
-impl ModlogCombinedQuery {
-  pub async fn list(self, pool: &mut DbPool<'_>) -> LemmyResult<Vec<ModlogCombinedView>> {
-    let conn = &mut get_conn(pool).await?;
-
-    let mod_person = self.mod_person_id.unwrap_or(PersonId(-1));
-    let show_mod_names = !(self.hide_modlog_names.unwrap_or_default());
-    let show_mod_names_expr = show_mod_names.as_sql::<diesel::sql_types::Bool>();
-
+impl ModlogCombinedViewInternal {
+  #[diesel::dsl::auto_type(no_type_alias)]
+  fn joins(mod_person_id: Option<PersonId>, hide_modlog_names: Option<bool>) -> _ {
     // The modded / other person
     let other_person = aliases::person1.field(person::id);
 
+    let show_mod_names: bool = !(hide_modlog_names.unwrap_or_default());
+    let show_mod_names_expr = show_mod_names.into_sql::<diesel::sql_types::Bool>();
+
     // The query for the admin / mod person
     // It needs an OR condition to every mod table
     // After this you can use person::id to refer to the moderator
-    let moderator_names_join = show_mod_names_expr.or(person::id.eq(mod_person)).and(
-      admin_allow_instance::admin_person_id
-        .eq(person::id)
-        .or(admin_block_instance::admin_person_id.eq(person::id))
-        .or(admin_purge_comment::admin_person_id.eq(person::id))
-        .or(admin_purge_community::admin_person_id.eq(person::id))
-        .or(admin_purge_person::admin_person_id.eq(person::id))
-        .or(admin_purge_post::admin_person_id.eq(person::id))
-        .or(mod_add::mod_person_id.eq(person::id))
-        .or(mod_add_community::mod_person_id.eq(person::id))
-        .or(mod_ban::mod_person_id.eq(person::id))
-        .or(mod_ban_from_community::mod_person_id.eq(person::id))
-        .or(mod_feature_post::mod_person_id.eq(person::id))
-        .or(mod_hide_community::mod_person_id.eq(person::id))
-        .or(mod_lock_post::mod_person_id.eq(person::id))
-        .or(mod_remove_comment::mod_person_id.eq(person::id))
-        .or(mod_remove_community::mod_person_id.eq(person::id))
-        .or(mod_remove_post::mod_person_id.eq(person::id))
-        .or(mod_transfer_community::mod_person_id.eq(person::id)),
-    );
+    let moderator_names_join = show_mod_names_expr
+      .or(person::id.nullable().eq(mod_person_id))
+      .and(
+        admin_allow_instance::admin_person_id
+          .eq(person::id)
+          .or(admin_block_instance::admin_person_id.eq(person::id))
+          .or(admin_purge_comment::admin_person_id.eq(person::id))
+          .or(admin_purge_community::admin_person_id.eq(person::id))
+          .or(admin_purge_person::admin_person_id.eq(person::id))
+          .or(admin_purge_post::admin_person_id.eq(person::id))
+          .or(mod_add::mod_person_id.eq(person::id))
+          .or(mod_add_community::mod_person_id.eq(person::id))
+          .or(mod_ban::mod_person_id.eq(person::id))
+          .or(mod_ban_from_community::mod_person_id.eq(person::id))
+          .or(mod_feature_post::mod_person_id.eq(person::id))
+          .or(mod_hide_community::mod_person_id.eq(person::id))
+          .or(mod_lock_post::mod_person_id.eq(person::id))
+          .or(mod_remove_comment::mod_person_id.eq(person::id))
+          .or(mod_remove_community::mod_person_id.eq(person::id))
+          .or(mod_remove_post::mod_person_id.eq(person::id))
+          .or(mod_transfer_community::mod_person_id.eq(person::id)),
+      );
 
     let other_person_join = mod_add::other_person_id
       .eq(other_person)
@@ -264,7 +174,7 @@ impl ModlogCombinedQuery {
       .eq(instance::id)
       .or(admin_block_instance::instance_id.eq(instance::id));
 
-    let mut query = modlog_combined::table
+    modlog_combined::table
       .left_join(admin_allow_instance::table)
       .left_join(admin_block_instance::table)
       .left_join(admin_purge_comment::table)
@@ -294,32 +204,105 @@ impl ModlogCombinedQuery {
       .left_join(instance::table.on(instance_join))
       // The other / modded person
       .left_join(aliases::person1.on(other_person_join))
-      .select((
-        admin_allow_instance::all_columns.nullable(),
-        admin_block_instance::all_columns.nullable(),
-        admin_purge_comment::all_columns.nullable(),
-        admin_purge_community::all_columns.nullable(),
-        admin_purge_person::all_columns.nullable(),
-        admin_purge_post::all_columns.nullable(),
-        mod_add::all_columns.nullable(),
-        mod_add_community::all_columns.nullable(),
-        mod_ban::all_columns.nullable(),
-        mod_ban_from_community::all_columns.nullable(),
-        mod_feature_post::all_columns.nullable(),
-        mod_hide_community::all_columns.nullable(),
-        mod_lock_post::all_columns.nullable(),
-        mod_remove_comment::all_columns.nullable(),
-        mod_remove_community::all_columns.nullable(),
-        mod_remove_post::all_columns.nullable(),
-        mod_transfer_community::all_columns.nullable(),
-        // Shared
-        person::all_columns.nullable(),
-        aliases::person1.fields(person::all_columns).nullable(),
-        instance::all_columns.nullable(),
-        community::all_columns.nullable(),
-        post::all_columns.nullable(),
-        comment::all_columns.nullable(),
-      ))
+  }
+}
+
+impl ModlogCombinedPaginationCursor {
+  // get cursor for page that starts immediately after the given post
+  pub fn after_post(view: &ModlogCombinedView) -> ModlogCombinedPaginationCursor {
+    let (prefix, id) = match view {
+      ModlogCombinedView::AdminAllowInstance(v) => {
+        ("AdminAllowInstance", v.admin_allow_instance.id.0)
+      }
+      ModlogCombinedView::AdminBlockInstance(v) => {
+        ("AdminBlockInstance", v.admin_block_instance.id.0)
+      }
+      ModlogCombinedView::AdminPurgeComment(v) => ("AdminPurgeComment", v.admin_purge_comment.id.0),
+      ModlogCombinedView::AdminPurgeCommunity(v) => {
+        ("AdminPurgeCommunity", v.admin_purge_community.id.0)
+      }
+      ModlogCombinedView::AdminPurgePerson(v) => ("AdminPurgePerson", v.admin_purge_person.id.0),
+      ModlogCombinedView::AdminPurgePost(v) => ("AdminPurgePost", v.admin_purge_post.id.0),
+      ModlogCombinedView::ModAdd(v) => ("ModAdd", v.mod_add.id.0),
+      ModlogCombinedView::ModAddCommunity(v) => ("ModAddCommunity", v.mod_add_community.id.0),
+      ModlogCombinedView::ModBan(v) => ("ModBan", v.mod_ban.id.0),
+      ModlogCombinedView::ModBanFromCommunity(v) => {
+        ("ModBanFromCommunity", v.mod_ban_from_community.id.0)
+      }
+      ModlogCombinedView::ModFeaturePost(v) => ("ModFeaturePost", v.mod_feature_post.id.0),
+      ModlogCombinedView::ModHideCommunity(v) => ("ModHideCommunity", v.mod_hide_community.id.0),
+      ModlogCombinedView::ModLockPost(v) => ("ModLockPost", v.mod_lock_post.id.0),
+      ModlogCombinedView::ModRemoveComment(v) => ("ModRemoveComment", v.mod_remove_comment.id.0),
+      ModlogCombinedView::ModRemoveCommunity(v) => {
+        ("ModRemoveCommunity", v.mod_remove_community.id.0)
+      }
+      ModlogCombinedView::ModRemovePost(v) => ("ModRemovePost", v.mod_remove_post.id.0),
+      ModlogCombinedView::ModTransferCommunity(v) => {
+        ("ModTransferCommunity", v.mod_transfer_community.id.0)
+      }
+    };
+    // hex encoding to prevent ossification
+    ModlogCombinedPaginationCursor(format!("{prefix}-{id:x}"))
+  }
+
+  pub async fn read(&self, pool: &mut DbPool<'_>) -> Result<PaginationCursorData, Error> {
+    let err_msg = || Error::QueryBuilderError("Could not parse pagination token".into());
+    let mut query = modlog_combined::table
+      .select(ModlogCombined::as_select())
+      .into_boxed();
+    let (prefix, id_str) = self.0.split_once('-').ok_or_else(err_msg)?;
+    let id = i32::from_str_radix(id_str, 16).map_err(|_err| err_msg())?;
+    query = match prefix {
+      "AdminAllowInstance" => query.filter(modlog_combined::admin_allow_instance_id.eq(id)),
+      "AdminBlockInstance" => query.filter(modlog_combined::admin_block_instance_id.eq(id)),
+      "AdminPurgeComment" => query.filter(modlog_combined::admin_purge_comment_id.eq(id)),
+      "AdminPurgeCommunity" => query.filter(modlog_combined::admin_purge_community_id.eq(id)),
+      "AdminPurgePerson" => query.filter(modlog_combined::admin_purge_person_id.eq(id)),
+      "AdminPurgePost" => query.filter(modlog_combined::admin_purge_post_id.eq(id)),
+      "ModAdd" => query.filter(modlog_combined::mod_add_id.eq(id)),
+      "ModAddCommunity" => query.filter(modlog_combined::mod_add_community_id.eq(id)),
+      "ModBan" => query.filter(modlog_combined::mod_ban_id.eq(id)),
+      "ModBanFromCommunity" => query.filter(modlog_combined::mod_ban_from_community_id.eq(id)),
+      "ModFeaturePost" => query.filter(modlog_combined::mod_feature_post_id.eq(id)),
+      "ModHideCommunity" => query.filter(modlog_combined::mod_hide_community_id.eq(id)),
+      "ModLockPost" => query.filter(modlog_combined::mod_lock_post_id.eq(id)),
+      "ModRemoveComment" => query.filter(modlog_combined::mod_remove_comment_id.eq(id)),
+      "ModRemoveCommunity" => query.filter(modlog_combined::mod_remove_community_id.eq(id)),
+      "ModRemovePost" => query.filter(modlog_combined::mod_remove_post_id.eq(id)),
+      "ModTransferCommunity" => query.filter(modlog_combined::mod_transfer_community_id.eq(id)),
+
+      _ => return Err(err_msg()),
+    };
+    let token = query.first(&mut get_conn(pool).await?).await?;
+
+    Ok(PaginationCursorData(token))
+  }
+}
+
+#[derive(Clone)]
+pub struct PaginationCursorData(ModlogCombined);
+
+#[derive(Default)]
+/// Querying / filtering the modlog.
+pub struct ModlogCombinedQuery {
+  pub type_: Option<ModlogActionType>,
+  pub comment_id: Option<CommentId>,
+  pub post_id: Option<PostId>,
+  pub community_id: Option<CommunityId>,
+  pub hide_modlog_names: Option<bool>,
+  pub mod_person_id: Option<PersonId>,
+  pub other_person_id: Option<PersonId>,
+  pub page_after: Option<PaginationCursorData>,
+  pub page_back: Option<bool>,
+}
+
+impl ModlogCombinedQuery {
+  pub async fn list(self, pool: &mut DbPool<'_>) -> LemmyResult<Vec<ModlogCombinedView>> {
+    let conn = &mut get_conn(pool).await?;
+    let other_person = aliases::person1.field(person::id);
+
+    let mut query = ModlogCombinedViewInternal::joins(self.mod_person_id, self.hide_modlog_names)
+      .select(ModlogCombinedViewInternal::as_select())
       .into_boxed();
 
     if let Some(mod_person_id) = self.mod_person_id {
diff --git a/crates/db_views/src/combined/person_content_combined_view.rs b/crates/db_views/src/combined/person_content_combined_view.rs
index 65b4d43e69..898ece8b7b 100644
--- a/crates/db_views/src/combined/person_content_combined_view.rs
+++ b/crates/db_views/src/combined/person_content_combined_view.rs
@@ -31,6 +31,7 @@ use lemmy_db_schema::{
     person,
     person_actions,
     person_content_combined,
+    person_saved_combined,
     post,
     post_actions,
     post_aggregates,
@@ -42,11 +43,197 @@ use lemmy_db_schema::{
     community::CommunityFollower,
   },
   traits::InternalToCombinedView,
-  utils::{actions, actions_alias, functions::coalesce, get_conn, DbPool},
+  utils::{functions::coalesce, get_conn, DbPool},
   PersonContentType,
 };
 use lemmy_utils::error::LemmyResult;
 
+impl PersonContentCombinedViewInternal {
+  #[diesel::dsl::auto_type(no_type_alias)]
+  fn joins(my_person_id: Option<PersonId>) -> _ {
+    let item_creator = person::id;
+
+    let comment_join =
+      comment::table.on(person_content_combined::comment_id.eq(comment::id.nullable()));
+
+    let post_join = post::table.on(
+      person_content_combined::post_id
+        .eq(post::id.nullable())
+        .or(comment::post_id.eq(post::id)),
+    );
+
+    let item_creator_join = person::table.on(
+      comment::creator_id
+        .eq(item_creator)
+        // Need to filter out the post rows where the post_id given is null
+        // Otherwise you'll get duped post rows
+        .or(
+          post::creator_id
+            .eq(item_creator)
+            .and(person_content_combined::post_id.is_not_null()),
+        ),
+    );
+
+    let community_join = community::table.on(post::community_id.eq(community::id));
+
+    let creator_community_actions_join = creator_community_actions.on(
+      creator_community_actions
+        .field(community_actions::community_id)
+        .eq(post::community_id)
+        .and(
+          creator_community_actions
+            .field(community_actions::person_id)
+            .eq(item_creator),
+        ),
+    );
+
+    let local_user_join = local_user::table.on(
+      item_creator
+        .eq(local_user::person_id)
+        .and(local_user::admin.eq(true)),
+    );
+
+    let community_actions_join = community_actions::table.on(
+      community_actions::community_id
+        .eq(post::community_id)
+        .and(community_actions::person_id.nullable().eq(my_person_id)),
+    );
+
+    let post_actions_join = post_actions::table.on(
+      post_actions::post_id
+        .eq(post::id)
+        .and(post_actions::person_id.nullable().eq(my_person_id)),
+    );
+
+    let person_actions_join = person_actions::table.on(
+      person_actions::target_id
+        .eq(item_creator)
+        .and(person_actions::person_id.nullable().eq(my_person_id)),
+    );
+
+    let comment_actions_join = comment_actions::table.on(
+      comment_actions::comment_id
+        .eq(comment::id)
+        .and(comment_actions::person_id.nullable().eq(my_person_id)),
+    );
+
+    let post_aggregates_join = post_aggregates::table.on(post::id.eq(post_aggregates::post_id));
+
+    let comment_aggregates_join = comment_aggregates::table
+      .on(person_content_combined::comment_id.eq(comment_aggregates::comment_id.nullable()));
+
+    let image_details_join =
+      image_details::table.on(post::thumbnail_url.eq(image_details::link.nullable()));
+
+    person_content_combined::table
+      .left_join(comment_join)
+      .inner_join(post_join)
+      .inner_join(item_creator_join)
+      .inner_join(community_join)
+      .left_join(creator_community_actions_join)
+      .left_join(local_user_join)
+      .left_join(community_actions_join)
+      .left_join(post_actions_join)
+      .left_join(person_actions_join)
+      .inner_join(post_aggregates_join)
+      .left_join(comment_aggregates_join)
+      .left_join(comment_actions_join)
+      .left_join(image_details_join)
+  }
+
+  #[diesel::dsl::auto_type(no_type_alias)]
+  pub(crate) fn joins_saved(my_person_id: PersonId) -> _ {
+    let item_creator = person::id;
+
+    let comment_join =
+      comment::table.on(person_saved_combined::comment_id.eq(comment::id.nullable()));
+
+    let post_join = post::table.on(
+      person_saved_combined::post_id
+        .eq(post::id.nullable())
+        .or(comment::post_id.eq(post::id)),
+    );
+
+    let item_creator_join = person::table.on(
+      comment::creator_id
+        .eq(item_creator)
+        // Need to filter out the post rows where the post_id given is null
+        // Otherwise you'll get duped post rows
+        .or(
+          post::creator_id
+            .eq(item_creator)
+            .and(person_saved_combined::post_id.is_not_null()),
+        ),
+    );
+
+    let community_join = community::table.on(post::community_id.eq(community::id));
+
+    let creator_community_actions_join = creator_community_actions.on(
+      creator_community_actions
+        .field(community_actions::community_id)
+        .eq(post::community_id)
+        .and(
+          creator_community_actions
+            .field(community_actions::person_id)
+            .eq(item_creator),
+        ),
+    );
+
+    let local_user_join = local_user::table.on(
+      item_creator
+        .eq(local_user::person_id)
+        .and(local_user::admin.eq(true)),
+    );
+
+    let community_actions_join = community_actions::table.on(
+      community_actions::community_id
+        .eq(post::community_id)
+        .and(community_actions::person_id.eq(my_person_id)),
+    );
+
+    let post_actions_join = post_actions::table.on(
+      post_actions::post_id
+        .eq(post::id)
+        .and(post_actions::person_id.eq(my_person_id)),
+    );
+
+    let person_actions_join = person_actions::table.on(
+      person_actions::target_id
+        .eq(item_creator)
+        .and(person_actions::person_id.eq(my_person_id)),
+    );
+
+    let comment_actions_join = comment_actions::table.on(
+      comment_actions::comment_id
+        .eq(comment::id)
+        .and(comment_actions::person_id.eq(my_person_id)),
+    );
+
+    let post_aggregates_join = post_aggregates::table.on(post::id.eq(post_aggregates::post_id));
+
+    let comment_aggregates_join = comment_aggregates::table
+      .on(person_saved_combined::comment_id.eq(comment_aggregates::comment_id.nullable()));
+
+    let image_details_join =
+      image_details::table.on(post::thumbnail_url.eq(image_details::link.nullable()));
+
+    person_saved_combined::table
+      .left_join(comment_join)
+      .inner_join(post_join)
+      .inner_join(item_creator_join)
+      .inner_join(community_join)
+      .left_join(creator_community_actions_join)
+      .left_join(local_user_join)
+      .left_join(community_actions_join)
+      .left_join(post_actions_join)
+      .left_join(person_actions_join)
+      .inner_join(post_aggregates_join)
+      .left_join(comment_aggregates_join)
+      .left_join(comment_actions_join)
+      .left_join(image_details_join)
+  }
+}
+
 impl PersonContentCombinedPaginationCursor {
   // get cursor for page that starts immediately after the given post
   pub fn after_post(view: &PersonContentCombinedView) -> PersonContentCombinedPaginationCursor {
@@ -115,61 +302,7 @@ impl PersonContentCombinedQuery {
     // For example, the creator must be the person table joined to either:
     // - post.creator_id
     // - comment.creator_id
-    let query = person_content_combined::table
-      // The comment
-      .left_join(comment::table.on(person_content_combined::comment_id.eq(comment::id.nullable())))
-      // The post
-      // It gets a bit complicated here, because since both comments and post combined have a post
-      // attached, you can do an inner join.
-      .inner_join(
-        post::table.on(
-          person_content_combined::post_id
-            .eq(post::id.nullable())
-            .or(comment::post_id.eq(post::id)),
-        ),
-      )
-      // The item creator
-      .inner_join(
-        person::table.on(
-          comment::creator_id
-            .eq(item_creator)
-            // Need to filter out the post rows where the post_id given is null
-            // Otherwise you'll get duped post rows
-            .or(
-              post::creator_id
-                .eq(item_creator)
-                .and(person_content_combined::post_id.is_not_null()),
-            ),
-        ),
-      )
-      // The community
-      .inner_join(community::table.on(post::community_id.eq(community::id)))
-      .left_join(actions_alias(
-        creator_community_actions,
-        item_creator,
-        post::community_id,
-      ))
-      .left_join(
-        local_user::table.on(
-          item_creator
-            .eq(local_user::person_id)
-            .and(local_user::admin.eq(true)),
-        ),
-      )
-      .left_join(actions(
-        community_actions::table,
-        my_person_id,
-        post::community_id,
-      ))
-      .left_join(actions(post_actions::table, my_person_id, post::id))
-      .left_join(actions(person_actions::table, my_person_id, item_creator))
-      .inner_join(post_aggregates::table.on(post::id.eq(post_aggregates::post_id)))
-      .left_join(
-        comment_aggregates::table
-          .on(person_content_combined::comment_id.eq(comment_aggregates::comment_id.nullable())),
-      )
-      .left_join(actions(comment_actions::table, my_person_id, comment::id))
-      .left_join(image_details::table.on(post::thumbnail_url.eq(image_details::link.nullable())))
+    let mut query = PersonContentCombinedViewInternal::joins(my_person_id)
       // The creator id filter
       .filter(item_creator.eq(self.creator_id))
       .select((
@@ -209,8 +342,6 @@ impl PersonContentCombinedQuery {
       ))
       .into_boxed();
 
-    let mut query = PaginatedQueryBuilder::new(query);
-
     if let Some(type_) = self.type_ {
       query = match type_ {
         PersonContentType::All => query,
@@ -221,6 +352,8 @@ impl PersonContentCombinedQuery {
       }
     }
 
+    let mut query = PaginatedQueryBuilder::new(query);
+
     let page_after = self.page_after.map(|c| c.0);
 
     if self.page_back.unwrap_or_default() {
diff --git a/crates/db_views/src/combined/person_saved_combined_view.rs b/crates/db_views/src/combined/person_saved_combined_view.rs
index 4ac7ee3fe7..326c5de72f 100644
--- a/crates/db_views/src/combined/person_saved_combined_view.rs
+++ b/crates/db_views/src/combined/person_saved_combined_view.rs
@@ -6,9 +6,7 @@ use crate::structs::{
 };
 use diesel::{
   result::Error,
-  BoolExpressionMethods,
   ExpressionMethods,
-  JoinOnDsl,
   NullableExpressionMethods,
   QueryDsl,
   SelectableHelper,
@@ -39,7 +37,7 @@ use lemmy_db_schema::{
     community::CommunityFollower,
   },
   traits::InternalToCombinedView,
-  utils::{actions, actions_alias, functions::coalesce, get_conn, DbPool},
+  utils::{functions::coalesce, get_conn, DbPool},
   PersonContentType,
 };
 use lemmy_utils::error::LemmyResult;
@@ -90,7 +88,6 @@ impl PersonSavedCombinedQuery {
     user: &LocalUserView,
   ) -> LemmyResult<Vec<PersonContentCombinedView>> {
     let my_person_id = user.local_user.person_id;
-    let item_creator = person::id;
 
     let conn = &mut get_conn(pool).await?;
 
@@ -103,75 +100,7 @@ impl PersonSavedCombinedQuery {
       .filter(tag::deleted.eq(false))
       .single_value();
 
-    // Notes: since the post_id and comment_id are optional columns,
-    // many joins must use an OR condition.
-    // For example, the creator must be the person table joined to either:
-    // - post.creator_id
-    // - comment.creator_id
-    let query = person_saved_combined::table
-      // The comment
-      .left_join(comment::table.on(person_saved_combined::comment_id.eq(comment::id.nullable())))
-      // The post
-      // It gets a bit complicated here, because since both comments and post combined have a post
-      // attached, you can do an inner join.
-      .inner_join(
-        post::table.on(
-          person_saved_combined::post_id
-            .eq(post::id.nullable())
-            .or(comment::post_id.eq(post::id)),
-        ),
-      )
-      // The item creator
-      .inner_join(
-        person::table.on(
-          comment::creator_id
-            .eq(item_creator)
-            // Need to filter out the post rows where the post_id given is null
-            // Otherwise you'll get duped post rows
-            .or(
-              post::creator_id
-                .eq(item_creator)
-                .and(person_saved_combined::post_id.is_not_null()),
-            ),
-        ),
-      )
-      // The community
-      .inner_join(community::table.on(post::community_id.eq(community::id)))
-      .left_join(actions_alias(
-        creator_community_actions,
-        item_creator,
-        post::community_id,
-      ))
-      .left_join(
-        local_user::table.on(
-          item_creator
-            .eq(local_user::person_id)
-            .and(local_user::admin.eq(true)),
-        ),
-      )
-      .left_join(actions(
-        community_actions::table,
-        Some(my_person_id),
-        post::community_id,
-      ))
-      .left_join(actions(post_actions::table, Some(my_person_id), post::id))
-      .left_join(actions(
-        person_actions::table,
-        Some(my_person_id),
-        item_creator,
-      ))
-      .inner_join(post_aggregates::table.on(post::id.eq(post_aggregates::post_id)))
-      .left_join(
-        comment_aggregates::table
-          .on(person_saved_combined::comment_id.eq(comment_aggregates::comment_id.nullable())),
-      )
-      .left_join(actions(
-        comment_actions::table,
-        Some(my_person_id),
-        comment::id,
-      ))
-      .left_join(image_details::table.on(post::thumbnail_url.eq(image_details::link.nullable())))
-      // The person id filter
+    let mut query = PersonContentCombinedViewInternal::joins_saved(my_person_id)
       .filter(person_saved_combined::person_id.eq(my_person_id))
       .select((
         // Post-specific
@@ -210,8 +139,6 @@ impl PersonSavedCombinedQuery {
       ))
       .into_boxed();
 
-    let mut query = PaginatedQueryBuilder::new(query);
-
     if let Some(type_) = self.type_ {
       query = match type_ {
         PersonContentType::All => query,
@@ -222,6 +149,8 @@ impl PersonSavedCombinedQuery {
       }
     }
 
+    let mut query = PaginatedQueryBuilder::new(query);
+
     let page_after = self.page_after.map(|c| c.0);
 
     if self.page_back.unwrap_or_default() {
diff --git a/crates/db_views/src/combined/report_combined_view.rs b/crates/db_views/src/combined/report_combined_view.rs
index 0fce1afcb5..115c8413a5 100644
--- a/crates/db_views/src/combined/report_combined_view.rs
+++ b/crates/db_views/src/combined/report_combined_view.rs
@@ -22,7 +22,7 @@ use diesel_async::RunQueryDsl;
 use i_love_jesus::PaginatedQueryBuilder;
 use lemmy_db_schema::{
   aliases::{self, creator_community_actions},
-  newtypes::{CommunityId, PostId},
+  newtypes::{CommunityId, PersonId, PostId},
   schema::{
     comment,
     comment_actions,
@@ -48,12 +48,130 @@ use lemmy_db_schema::{
     community::CommunityFollower,
   },
   traits::InternalToCombinedView,
-  utils::{actions, actions_alias, functions::coalesce, get_conn, DbPool, ReverseTimestampKey},
+  utils::{functions::coalesce, get_conn, DbPool, ReverseTimestampKey},
   ReportType,
 };
 use lemmy_utils::error::LemmyResult;
 
 impl ReportCombinedViewInternal {
+  #[diesel::dsl::auto_type(no_type_alias)]
+  fn joins(my_person_id: PersonId) -> _ {
+    let report_creator = person::id;
+    let item_creator = aliases::person1.field(person::id);
+    let resolver = aliases::person2.field(person::id).nullable();
+
+    let comment_join = comment::table.on(comment_report::comment_id.eq(comment::id));
+    let private_message_join =
+      private_message::table.on(private_message_report::private_message_id.eq(private_message::id));
+
+    let post_join = post::table.on(
+      post_report::post_id
+        .eq(post::id)
+        .or(comment::post_id.eq(post::id)),
+    );
+
+    let community_actions_join = community_actions::table.on(
+      community_actions::community_id
+        .eq(community::id)
+        .and(community_actions::person_id.eq(my_person_id)),
+    );
+
+    let report_creator_join = person::table.on(
+      post_report::creator_id
+        .eq(report_creator)
+        .or(comment_report::creator_id.eq(report_creator))
+        .or(private_message_report::creator_id.eq(report_creator))
+        .or(community_report::creator_id.eq(report_creator)),
+    );
+
+    let item_creator_join = aliases::person1.on(
+      post::creator_id
+        .eq(item_creator)
+        .or(comment::creator_id.eq(item_creator))
+        .or(private_message::creator_id.eq(item_creator)),
+    );
+
+    let resolver_join = aliases::person2.on(
+      private_message_report::resolver_id
+        .eq(resolver)
+        .or(post_report::resolver_id.eq(resolver))
+        .or(comment_report::resolver_id.eq(resolver))
+        .or(community_report::resolver_id.eq(resolver)),
+    );
+
+    let community_join = community::table.on(
+      community_report::community_id
+        .eq(community::id)
+        .or(post::community_id.eq(community::id)),
+    );
+
+    let local_user_join = local_user::table.on(
+      item_creator
+        .eq(local_user::person_id)
+        .and(local_user::admin.eq(true)),
+    );
+
+    let creator_community_actions_join = creator_community_actions.on(
+      creator_community_actions
+        .field(community_actions::community_id)
+        .eq(post::community_id)
+        .and(
+          creator_community_actions
+            .field(community_actions::person_id)
+            .eq(item_creator),
+        ),
+    );
+
+    let post_actions_join = post_actions::table.on(
+      post_actions::post_id
+        .eq(post::id)
+        .and(post_actions::person_id.eq(my_person_id)),
+    );
+
+    let person_actions_join = person_actions::table.on(
+      person_actions::target_id
+        .eq(item_creator)
+        .and(person_actions::person_id.eq(my_person_id)),
+    );
+
+    let comment_actions_join = comment_actions::table.on(
+      comment_actions::comment_id
+        .eq(comment::id)
+        .and(comment_actions::person_id.eq(my_person_id)),
+    );
+
+    let post_aggregates_join =
+      post_aggregates::table.on(post_report::post_id.eq(post_aggregates::post_id));
+
+    let comment_aggregates_join =
+      comment_aggregates::table.on(comment_report::comment_id.eq(comment_aggregates::comment_id));
+
+    let community_aggregates_join = community_aggregates::table
+      .on(community_report::community_id.eq(community_aggregates::community_id));
+
+    report_combined::table
+      .left_join(post_report::table)
+      .left_join(comment_report::table)
+      .left_join(private_message_report::table)
+      .left_join(community_report::table)
+      .inner_join(report_creator_join)
+      .left_join(comment_join)
+      .left_join(private_message_join)
+      .left_join(post_join)
+      .left_join(item_creator_join)
+      .left_join(resolver_join)
+      .left_join(community_join)
+      .left_join(creator_community_actions_join)
+      .left_join(local_user_join)
+      .left_join(community_actions_join)
+      .left_join(post_actions_join)
+      .left_join(person_actions_join)
+      .left_join(post_aggregates_join)
+      .left_join(comment_aggregates_join)
+      .left_join(community_aggregates_join)
+      .left_join(comment_actions_join)
+  }
+
   /// returns the current unresolved report count for the communities you mod
   pub async fn get_report_count(
     pool: &mut DbPool<'_>,
@@ -65,27 +183,7 @@ impl ReportCombinedViewInternal {
     let conn = &mut get_conn(pool).await?;
     let my_person_id = user.local_user.person_id;
 
-    let mut query = report_combined::table
-      .left_join(post_report::table)
-      .left_join(comment_report::table)
-      .left_join(private_message_report::table)
-      .left_join(community_report::table)
-      // Need to join to comment and post to get the community
-      .left_join(comment::table.on(comment_report::comment_id.eq(comment::id)))
-      // The post
-      .left_join(
-        post::table.on(
-          post_report::post_id
-            .eq(post::id)
-            .or(comment::post_id.eq(post::id)),
-        ),
-      )
-      .left_join(community::table.on(post::community_id.eq(community::id)))
-      .left_join(actions(
-        community_actions::table,
-        Some(my_person_id),
-        post::community_id,
-      ))
+    let mut query = Self::joins(my_person_id)
       .filter(
         post_report::resolved
           .or(comment_report::resolved)
@@ -93,21 +191,27 @@ impl ReportCombinedViewInternal {
           .or(community_report::resolved)
           .is_distinct_from(true),
       )
+      .select(count(report_combined::id))
       .into_boxed();
 
     if let Some(community_id) = community_id {
-      query = query.filter(post::community_id.eq(community_id))
+      query = query.filter(
+        community::id
+          .eq(community_id)
+          .and(report_combined::community_report_id.is_null()),
+      );
     }
 
     // If its not an admin, get only the ones you mod
     if !user.local_user.admin {
-      query = query.filter(community_actions::became_moderator.is_not_null());
+      query = query.filter(
+        community_actions::became_moderator
+          .is_not_null()
+          .and(report_combined::community_report_id.is_null()),
+      );
     }
 
-    query
-      .select(count(report_combined::id))
-      .first::<i64>(conn)
-      .await
+    query.first::<i64>(conn).await
   }
 }
 
@@ -164,102 +268,9 @@ impl ReportCombinedQuery {
     user: &LocalUserView,
   ) -> LemmyResult<Vec<ReportCombinedView>> {
     let my_person_id = user.local_user.person_id;
-    let report_creator = person::id;
-    let item_creator = aliases::person1.field(person::id);
-    let resolver = aliases::person2.field(person::id).nullable();
 
     let conn = &mut get_conn(pool).await?;
-
-    let report_creator_join = post_report::creator_id
-      .eq(report_creator)
-      .or(comment_report::creator_id.eq(report_creator))
-      .or(private_message_report::creator_id.eq(report_creator))
-      .or(community_report::creator_id.eq(report_creator));
-
-    let item_creator_join = post::creator_id
-      .eq(item_creator)
-      .or(comment::creator_id.eq(item_creator))
-      .or(private_message::creator_id.eq(item_creator));
-
-    let resolver_join = private_message_report::resolver_id
-      .eq(resolver)
-      .or(post_report::resolver_id.eq(resolver))
-      .or(comment_report::resolver_id.eq(resolver))
-      .or(community_report::resolver_id.eq(resolver));
-
-    let post_join = post_report::post_id
-      .eq(post::id)
-      .or(comment::post_id.eq(post::id));
-
-    let community_join = community::table.on(
-      community_report::community_id
-        .eq(community::id)
-        .or(post::community_id.eq(community::id)),
-    );
-
-    // Notes: since the post_report_id and comment_report_id are optional columns,
-    // many joins must use an OR condition.
-    // For example, the report creator must be the person table joined to either:
-    // - post_report.creator_id
-    // - comment_report.creator_id
-    let mut query = report_combined::table
-      .left_join(post_report::table)
-      .left_join(comment_report::table)
-      .left_join(private_message_report::table)
-      .left_join(community_report::table)
-      // The report creator
-      .inner_join(person::table.on(report_creator_join))
-      // The comment
-      .left_join(comment::table.on(comment_report::comment_id.eq(comment::id)))
-      // The private message
-      .left_join(
-        private_message::table
-          .on(private_message_report::private_message_id.eq(private_message::id)),
-      )
-      // The post
-      .left_join(post::table.on(post_join))
-      // The item creator (`item_creator` is the id of this person)
-      .left_join(aliases::person1.on(item_creator_join))
-      // The resolver
-      .left_join(aliases::person2.on(resolver_join))
-      // The community
-      .left_join(community_join)
-      .left_join(actions_alias(
-        creator_community_actions,
-        item_creator,
-        post::community_id,
-      ))
-      .left_join(
-        local_user::table.on(
-          item_creator
-            .eq(local_user::person_id)
-            .and(local_user::admin.eq(true)),
-        ),
-      )
-      .left_join(actions(
-        community_actions::table,
-        Some(my_person_id),
-        community::id,
-      ))
-      .left_join(actions(post_actions::table, Some(my_person_id), post::id))
-      .left_join(actions(
-        person_actions::table,
-        Some(my_person_id),
-        item_creator,
-      ))
-      .left_join(post_aggregates::table.on(post_report::post_id.eq(post_aggregates::post_id)))
-      .left_join(
-        comment_aggregates::table.on(comment_report::comment_id.eq(comment_aggregates::comment_id)),
-      )
-      .left_join(
-        community_aggregates::table
-          .on(community_report::community_id.eq(community_aggregates::community_id)),
-      )
-      .left_join(actions(
-        comment_actions::table,
-        Some(my_person_id),
-        comment_report::comment_id,
-      ))
+    let mut query = ReportCombinedViewInternal::joins(my_person_id)
       .select((
         // Post-specific
         post_report::all_columns.nullable(),
diff --git a/crates/db_views/src/combined/search_combined_view.rs b/crates/db_views/src/combined/search_combined_view.rs
index 13054da0a8..05c999ef18 100644
--- a/crates/db_views/src/combined/search_combined_view.rs
+++ b/crates/db_views/src/combined/search_combined_view.rs
@@ -48,15 +48,7 @@ use lemmy_db_schema::{
     community::CommunityFollower,
   },
   traits::InternalToCombinedView,
-  utils::{
-    actions,
-    actions_alias,
-    functions::coalesce,
-    fuzzy_search,
-    get_conn,
-    DbPool,
-    ReverseTimestampKey,
-  },
+  utils::{functions::coalesce, fuzzy_search, get_conn, DbPool, ReverseTimestampKey},
   ListingType,
   SearchSortType,
   SearchType,
@@ -64,6 +56,123 @@ use lemmy_db_schema::{
 use lemmy_utils::error::LemmyResult;
 use SearchSortType::*;
 
+impl SearchCombinedViewInternal {
+  #[diesel::dsl::auto_type(no_type_alias)]
+  fn joins(my_person_id: Option<PersonId>) -> _ {
+    let item_creator = person::id;
+
+    let item_creator_join = person::table.on(
+      search_combined::person_id
+        .eq(item_creator.nullable())
+        .or(
+          search_combined::comment_id
+            .is_not_null()
+            .and(comment::creator_id.eq(item_creator)),
+        )
+        .or(
+          search_combined::post_id
+            .is_not_null()
+            .and(post::creator_id.eq(item_creator)),
+        )
+        .and(not(person::deleted)),
+    );
+
+    let comment_join = comment::table.on(
+      search_combined::comment_id
+        .eq(comment::id.nullable())
+        .and(not(comment::removed))
+        .and(not(comment::deleted)),
+    );
+
+    let post_join = post::table.on(
+      search_combined::post_id
+        .eq(post::id.nullable())
+        .or(comment::post_id.eq(post::id))
+        .and(not(post::removed))
+        .and(not(post::deleted)),
+    );
+
+    let community_join = community::table.on(
+      search_combined::community_id
+        .eq(community::id.nullable())
+        .or(post::community_id.eq(community::id))
+        .and(not(community::removed))
+        .and(not(community::deleted)),
+    );
+
+    let creator_community_actions_join = creator_community_actions.on(
+      creator_community_actions
+        .field(community_actions::community_id)
+        .eq(community::id)
+        .and(
+          creator_community_actions
+            .field(community_actions::person_id)
+            .eq(item_creator),
+        ),
+    );
+    let local_user_join = local_user::table.on(
+      item_creator
+        .eq(local_user::person_id)
+        .and(local_user::admin.eq(true)),
+    );
+
+    let community_actions_join = community_actions::table.on(
+      community_actions::community_id
+        .eq(community::id)
+        .and(community_actions::person_id.nullable().eq(my_person_id)),
+    );
+
+    let post_actions_join = post_actions::table.on(
+      post_actions::post_id
+        .eq(post::id)
+        .and(post_actions::person_id.nullable().eq(my_person_id)),
+    );
+
+    let person_actions_join = person_actions::table.on(
+      person_actions::target_id
+        .eq(item_creator)
+        .and(person_actions::person_id.nullable().eq(my_person_id)),
+    );
+
+    let comment_actions_join = comment_actions::table.on(
+      comment_actions::comment_id
+        .eq(comment::id)
+        .and(comment_actions::person_id.nullable().eq(my_person_id)),
+    );
+
+    let post_aggregates_join = post_aggregates::table.on(post::id.eq(post_aggregates::post_id));
+
+    let comment_aggregates_join = comment_aggregates::table
+      .on(search_combined::comment_id.eq(comment_aggregates::comment_id.nullable()));
+
+    let community_aggregates_join = community_aggregates::table
+      .on(search_combined::community_id.eq(community_aggregates::community_id.nullable()));
+
+    let image_details_join =
+      image_details::table.on(post::thumbnail_url.eq(image_details::link.nullable()));
+
+    let person_aggregates_join = person_aggregates::table
+      .on(search_combined::person_id.eq(person_aggregates::person_id.nullable()));
+
+    search_combined::table
+      .left_join(comment_join)
+      .left_join(post_join)
+      .left_join(item_creator_join)
+      .left_join(community_join)
+      .left_join(creator_community_actions_join)
+      .left_join(local_user_join)
+      .left_join(community_actions_join)
+      .left_join(post_actions_join)
+      .left_join(person_actions_join)
+      .left_join(person_aggregates_join)
+      .left_join(post_aggregates_join)
+      .left_join(comment_aggregates_join)
+      .left_join(community_aggregates_join)
+      .left_join(comment_actions_join)
+      .left_join(image_details_join)
+  }
+}
+
 impl SearchCombinedPaginationCursor {
   // get cursor for page that starts immediately after the given post
   pub fn after_post(view: &SearchCombinedView) -> SearchCombinedPaginationCursor {
@@ -136,85 +245,7 @@ impl SearchCombinedQuery {
       .filter(tag::deleted.eq(false))
       .single_value();
 
-    let item_creator_join = search_combined::person_id
-      .eq(item_creator.nullable())
-      .or(
-        search_combined::comment_id
-          .is_not_null()
-          .and(comment::creator_id.eq(item_creator)),
-      )
-      .or(
-        search_combined::post_id
-          .is_not_null()
-          .and(post::creator_id.eq(item_creator)),
-      )
-      .and(not(person::deleted));
-
-    let comment_join = search_combined::comment_id
-      .eq(comment::id.nullable())
-      .and(not(comment::removed))
-      .and(not(comment::deleted));
-
-    let post_join = search_combined::post_id
-      .eq(post::id.nullable())
-      .or(comment::post_id.eq(post::id))
-      .and(not(post::removed))
-      .and(not(post::deleted));
-
-    let community_join = search_combined::community_id
-      .eq(community::id.nullable())
-      .or(post::community_id.eq(community::id))
-      .and(not(community::removed))
-      .and(not(community::deleted));
-
-    // Notes: since the post_id and comment_id are optional columns,
-    // many joins must use an OR condition.
-    // For example, the creator must be the person table joined to either:
-    // - post.creator_id
-    // - comment.creator_id
-    let mut query = search_combined::table
-      // The comment
-      .left_join(comment::table.on(comment_join))
-      // The post
-      .left_join(post::table.on(post_join))
-      // The item creator
-      .left_join(person::table.on(item_creator_join))
-      // The community
-      .left_join(community::table.on(community_join))
-      .left_join(actions_alias(
-        creator_community_actions,
-        item_creator,
-        community::id,
-      ))
-      .left_join(
-        local_user::table.on(
-          item_creator
-            .eq(local_user::person_id)
-            .and(local_user::admin.eq(true)),
-        ),
-      )
-      .left_join(actions(
-        community_actions::table,
-        my_person_id,
-        community::id,
-      ))
-      .left_join(actions(post_actions::table, my_person_id, post::id))
-      .left_join(actions(person_actions::table, my_person_id, item_creator))
-      .left_join(
-        person_aggregates::table
-          .on(search_combined::person_id.eq(person_aggregates::person_id.nullable())),
-      )
-      .left_join(post_aggregates::table.on(post::id.eq(post_aggregates::post_id)))
-      .left_join(
-        comment_aggregates::table
-          .on(search_combined::comment_id.eq(comment_aggregates::comment_id.nullable())),
-      )
-      .left_join(
-        community_aggregates::table
-          .on(search_combined::community_id.eq(community_aggregates::community_id.nullable())),
-      )
-      .left_join(actions(comment_actions::table, my_person_id, comment::id))
-      .left_join(image_details::table.on(post::thumbnail_url.eq(image_details::link.nullable())))
+    let mut query = SearchCombinedViewInternal::joins(my_person_id)
       .select((
         // Post-specific
         post::all_columns.nullable(),
diff --git a/crates/db_views/src/comment/comment_view.rs b/crates/db_views/src/comment/comment_view.rs
index 13a849449a..9297eec603 100644
--- a/crates/db_views/src/comment/comment_view.rs
+++ b/crates/db_views/src/comment/comment_view.rs
@@ -1,7 +1,6 @@
 use crate::structs::{CommentSlimView, CommentView};
 use diesel::{
   dsl::{exists, not},
-  pg::Pg,
   result::Error,
   BoolExpressionMethods,
   ExpressionMethods,
@@ -9,13 +8,14 @@ use diesel::{
   NullableExpressionMethods,
   PgTextExpressionMethods,
   QueryDsl,
+  SelectableHelper,
 };
 use diesel_async::RunQueryDsl;
 use diesel_ltree::{nlevel, subpath, Ltree, LtreeExtensions};
 use lemmy_db_schema::{
   aliases::creator_community_actions,
   impls::local_user::LocalUserOptionHelper,
-  newtypes::{CommentId, CommunityId, LocalUserId, PersonId, PostId},
+  newtypes::{CommentId, CommunityId, PersonId, PostId},
   schema::{
     comment,
     comment_actions,
@@ -23,108 +23,82 @@ use lemmy_db_schema::{
     community,
     community_actions,
     instance_actions,
-    local_user,
     local_user_language,
     person,
     person_actions,
     post,
   },
-  source::{
-    community::{CommunityFollower, CommunityFollowerState},
-    local_user::LocalUser,
-    site::Site,
-  },
-  utils::{
-    actions,
-    actions_alias,
-    fuzzy_search,
-    limit_and_offset,
-    DbConn,
-    DbPool,
-    ListFn,
-    Queries,
-    ReadFn,
-  },
+  source::{community::CommunityFollowerState, local_user::LocalUser, site::Site},
+  utils::{fuzzy_search, get_conn, limit_and_offset, DbPool},
   CommentSortType,
   CommunityVisibility,
   ListingType,
 };
 
-type QueriesReadTypes<'a> = (CommentId, Option<&'a LocalUser>);
-type QueriesListTypes<'a> = (CommentQuery<'a>, &'a Site);
-
-fn queries<'a>() -> Queries<
-  impl ReadFn<'a, CommentView, QueriesReadTypes<'a>>,
-  impl ListFn<'a, CommentView, QueriesListTypes<'a>>,
-> {
-  let creator_is_admin = exists(
-    local_user::table.filter(
-      comment::creator_id
-        .eq(local_user::person_id)
-        .and(local_user::admin.eq(true)),
-    ),
-  );
-
-  let all_joins = move |query: comment::BoxedQuery<'a, Pg>, my_person_id: Option<PersonId>| {
-    query
+impl CommentView {
+  #[diesel::dsl::auto_type(no_type_alias)]
+  fn joins(my_person_id: Option<PersonId>) -> _ {
+    let community_join = community::table.on(post::community_id.eq(community::id));
+
+    let community_actions_join = community_actions::table.on(
+      community_actions::community_id
+        .eq(post::community_id)
+        .and(community_actions::person_id.nullable().eq(my_person_id)),
+    );
+
+    let comment_actions_join = comment_actions::table.on(
+      comment_actions::comment_id
+        .eq(comment_aggregates::comment_id)
+        .and(comment_actions::person_id.nullable().eq(my_person_id)),
+    );
+
+    let person_actions_join = person_actions::table.on(
+      person_actions::target_id
+        .eq(comment::creator_id)
+        .and(person_actions::person_id.nullable().eq(my_person_id)),
+    );
+
+    let instance_actions_join = instance_actions::table.on(
+      instance_actions::instance_id
+        .eq(community::instance_id)
+        .and(instance_actions::person_id.nullable().eq(my_person_id)),
+    );
+
+    let comment_creator_community_actions_join = creator_community_actions.on(
+      creator_community_actions
+        .field(community_actions::community_id)
+        .eq(post::community_id)
+        .and(
+          creator_community_actions
+            .field(community_actions::person_id)
+            .eq(comment::creator_id),
+        ),
+    );
+
+    comment::table
       .inner_join(person::table)
       .inner_join(post::table)
-      .inner_join(community::table.on(post::community_id.eq(community::id)))
+      .inner_join(community_join)
       .inner_join(comment_aggregates::table)
-      .left_join(actions(
-        community_actions::table,
-        my_person_id,
-        post::community_id,
-      ))
-      .left_join(actions(
-        comment_actions::table,
-        my_person_id,
-        comment_aggregates::comment_id,
-      ))
-      .left_join(actions(
-        person_actions::table,
-        my_person_id,
-        comment::creator_id,
-      ))
-      .left_join(actions(
-        instance_actions::table,
-        my_person_id,
-        community::instance_id,
-      ))
-      .left_join(actions_alias(
-        creator_community_actions,
-        comment::creator_id,
-        post::community_id,
-      ))
-      .select((
-        comment::all_columns,
-        person::all_columns,
-        post::all_columns,
-        community::all_columns,
-        comment_aggregates::all_columns,
-        creator_community_actions
-          .field(community_actions::received_ban)
-          .nullable()
-          .is_not_null(),
-        community_actions::received_ban.nullable().is_not_null(),
-        creator_community_actions
-          .field(community_actions::became_moderator)
-          .nullable()
-          .is_not_null(),
-        creator_is_admin,
-        CommunityFollower::select_subscribed_type(),
-        comment_actions::saved.nullable().is_not_null(),
-        person_actions::blocked.nullable().is_not_null(),
-        comment_actions::like_score.nullable(),
-      ))
-  };
+      .left_join(community_actions_join)
+      .left_join(comment_actions_join)
+      .left_join(person_actions_join)
+      .left_join(instance_actions_join)
+      .left_join(comment_creator_community_actions_join)
+  }
+
+  pub async fn read(
+    pool: &mut DbPool<'_>,
+    comment_id: CommentId,
+    my_local_user: Option<&'_ LocalUser>,
+  ) -> Result<Self, Error> {
+    let conn = &mut get_conn(pool).await?;
+
+    let mut query = Self::joins(my_local_user.person_id())
+      .filter(comment::id.eq(comment_id))
+      .select(Self::as_select())
+      .into_boxed();
 
-  let read = move |mut conn: DbConn<'a>,
-                   (comment_id, my_local_user): (CommentId, Option<&'a LocalUser>)| async move {
-    let mut query = all_joins(
-      comment::table.find(comment_id).into_boxed(),
-      my_local_user.person_id(),
-    );
     query = my_local_user.visible_communities_only(query);
 
     // Check permissions to view private community content.
@@ -138,14 +112,65 @@ fn queries<'a>() -> Queries<
           .or(community_actions::follow_state.eq(CommunityFollowerState::Accepted)),
       );
     }
-    query.first(&mut conn).await
-  };
 
-  let list = move |mut conn: DbConn<'a>, (o, site): (CommentQuery<'a>, &'a Site)| async move {
+    let mut res = query.first::<Self>(conn).await?;
+
+    // If a person is given, then my_vote (res.9), if None, should be 0, not null
+    // Necessary to differentiate between other person's votes
+    if my_local_user.is_some() && res.my_vote.is_none() {
+      res.my_vote = Some(0);
+    }
+
+    let is_admin = my_local_user.map(|u| u.admin).unwrap_or(false);
+    Ok(handle_deleted(res, is_admin))
+  }
+
+  pub fn map_to_slim(self) -> CommentSlimView {
+    CommentSlimView {
+      comment: self.comment,
+      creator: self.creator,
+      counts: self.counts,
+      creator_banned_from_community: self.creator_banned_from_community,
+      banned_from_community: self.banned_from_community,
+      creator_is_moderator: self.creator_is_moderator,
+      creator_is_admin: self.creator_is_admin,
+      subscribed: self.subscribed,
+      saved: self.saved,
+      creator_blocked: self.creator_blocked,
+      my_vote: self.my_vote,
+    }
+  }
+}
+
+#[derive(Default)]
+pub struct CommentQuery<'a> {
+  pub listing_type: Option<ListingType>,
+  pub sort: Option<CommentSortType>,
+  pub community_id: Option<CommunityId>,
+  pub post_id: Option<PostId>,
+  pub parent_path: Option<Ltree>,
+  pub creator_id: Option<PersonId>,
+  pub local_user: Option<&'a LocalUser>,
+  pub search_term: Option<String>,
+  pub liked_only: Option<bool>,
+  pub disliked_only: Option<bool>,
+  pub page: Option<i64>,
+  pub limit: Option<i64>,
+  pub max_depth: Option<i32>,
+}
+
+impl CommentQuery<'_> {
+  pub async fn list(self, site: &Site, pool: &mut DbPool<'_>) -> Result<Vec<CommentView>, Error> {
+    let conn = &mut get_conn(pool).await?;
+    let o = self;
+
     // The left join below will return None in this case
-    let local_user_id_join = o.local_user.local_user_id().unwrap_or(LocalUserId(-1));
+    let my_person_id = o.local_user.person_id();
+    let local_user_id = o.local_user.local_user_id();
 
-    let mut query = all_joins(comment::table.into_boxed(), o.local_user.person_id());
+    let mut query = CommentView::joins(my_person_id)
+      .select(CommentView::as_select())
+      .into_boxed();
 
     if let Some(creator_id) = o.creator_id {
       query = query.filter(comment::creator_id.eq(creator_id));
@@ -186,7 +211,7 @@ fn queries<'a>() -> Queries<
       }
     }
 
-    if let Some(my_id) = o.local_user.person_id() {
+    if let Some(my_id) = my_person_id {
       let not_creator_filter = comment::creator_id.ne(my_id);
       if o.liked_only.unwrap_or_default() {
         query = query
@@ -209,7 +234,11 @@ fn queries<'a>() -> Queries<
         local_user_language::table.filter(
           comment::language_id
             .eq(local_user_language::language_id)
-            .and(local_user_language::local_user_id.eq(local_user_id_join)),
+            .and(
+              local_user_language::local_user_id
+                .nullable()
+                .eq(local_user_id),
+            ),
         ),
       ));
 
@@ -287,74 +316,17 @@ fn queries<'a>() -> Queries<
       CommentSortType::Top => query.then_order_by(comment_aggregates::score.desc()),
     };
 
-    // Note: deleted and removed comments are done on the front side
-    query
+    let res = query
       .limit(limit)
       .offset(offset)
-      .load::<CommentView>(&mut conn)
-      .await
-  };
-
-  Queries::new(read, list)
-}
-
-impl CommentView {
-  pub async fn read(
-    pool: &mut DbPool<'_>,
-    comment_id: CommentId,
-    my_local_user: Option<&'_ LocalUser>,
-  ) -> Result<Self, Error> {
-    let is_admin = my_local_user.map(|u| u.admin).unwrap_or(false);
-    // If a person is given, then my_vote (res.9), if None, should be 0, not null
-    // Necessary to differentiate between other person's votes
-    let mut res = queries().read(pool, (comment_id, my_local_user)).await?;
-    if my_local_user.is_some() && res.my_vote.is_none() {
-      res.my_vote = Some(0);
-    }
-    Ok(handle_deleted(res, is_admin))
-  }
-
-  pub fn map_to_slim(self) -> CommentSlimView {
-    CommentSlimView {
-      comment: self.comment,
-      creator: self.creator,
-      counts: self.counts,
-      creator_banned_from_community: self.creator_banned_from_community,
-      banned_from_community: self.banned_from_community,
-      creator_is_moderator: self.creator_is_moderator,
-      creator_is_admin: self.creator_is_admin,
-      subscribed: self.subscribed,
-      saved: self.saved,
-      creator_blocked: self.creator_blocked,
-      my_vote: self.my_vote,
-    }
-  }
-}
+      .load::<CommentView>(conn)
+      .await?;
 
-#[derive(Default)]
-pub struct CommentQuery<'a> {
-  pub listing_type: Option<ListingType>,
-  pub sort: Option<CommentSortType>,
-  pub community_id: Option<CommunityId>,
-  pub post_id: Option<PostId>,
-  pub parent_path: Option<Ltree>,
-  pub creator_id: Option<PersonId>,
-  pub local_user: Option<&'a LocalUser>,
-  pub search_term: Option<String>,
-  pub liked_only: Option<bool>,
-  pub disliked_only: Option<bool>,
-  pub page: Option<i64>,
-  pub limit: Option<i64>,
-  pub max_depth: Option<i32>,
-}
+    let is_admin = o.local_user.map(|u| u.admin).unwrap_or(false);
 
-impl CommentQuery<'_> {
-  pub async fn list(self, site: &Site, pool: &mut DbPool<'_>) -> Result<Vec<CommentView>, Error> {
-    let is_admin = self.local_user.map(|u| u.admin).unwrap_or(false);
+    // Note: deleted and removed comments are done on the front side
     Ok(
-      queries()
-        .list(pool, (self, site))
-        .await?
+      res
         .into_iter()
         .map(|c| handle_deleted(c, is_admin))
         .collect(),
@@ -362,6 +334,7 @@ impl CommentQuery<'_> {
   }
 }
 
+/// Only show deleted / removed content for admins.
 fn handle_deleted(mut c: CommentView, is_admin: bool) -> CommentView {
   if !is_admin && (c.comment.deleted || c.comment.removed) {
     c.comment.content = String::new();
diff --git a/crates/db_views/src/community/community_follower_view.rs b/crates/db_views/src/community/community_follower_view.rs
index 487df63236..e439c14b99 100644
--- a/crates/db_views/src/community/community_follower_view.rs
+++ b/crates/db_views/src/community/community_follower_view.rs
@@ -8,6 +8,7 @@ use diesel::{
   ExpressionMethods,
   JoinOnDsl,
   QueryDsl,
+  SelectableHelper,
 };
 use diesel_async::RunQueryDsl;
 use lemmy_db_schema::{
@@ -17,13 +18,20 @@ use lemmy_db_schema::{
     community::{Community, CommunityFollower, CommunityFollowerState},
     person::Person,
   },
-  utils::{action_query, get_conn, limit_and_offset, DbPool},
+  utils::{get_conn, limit_and_offset, DbPool},
   CommunityVisibility,
   SubscribedType,
 };
 use lemmy_utils::error::{LemmyErrorExt, LemmyErrorType, LemmyResult};
 
 impl CommunityFollowerView {
+  #[diesel::dsl::auto_type(no_type_alias)]
+  fn joins() -> _ {
+    community_actions::table
+      .filter(community_actions::followed.is_not_null())
+      .inner_join(community::table)
+      .inner_join(person::table.on(community_actions::person_id.eq(person::id)))
+  }
   /// return a list of local community ids and remote inboxes that at least one user of the given
   /// instance has followed
   pub async fn get_instance_followed_community_inboxes(
@@ -39,9 +47,7 @@ impl CommunityFollowerView {
     // that would work for all instances that support fully shared inboxes.
     // It would be a bit more complicated though to keep it in sync.
 
-    community_actions::table
-      .inner_join(community::table)
-      .inner_join(person::table.on(community_actions::person_id.eq(person::id)))
+    Self::joins()
       .filter(person::instance_id.eq(instance_id))
       .filter(community::local) // this should be a no-op since community_followers table only has
       // local-person+remote-community or remote-person+local-community
@@ -53,12 +59,29 @@ impl CommunityFollowerView {
       .await
       .with_lemmy_type(LemmyErrorType::NotFound)
   }
+
+  pub async fn get_community_follower_inboxes(
+    pool: &mut DbPool<'_>,
+    community_id: CommunityId,
+  ) -> Result<Vec<DbUrl>, Error> {
+    let conn = &mut get_conn(pool).await?;
+    let res = Self::joins()
+      .filter(community_actions::community_id.eq(community_id))
+      .filter(not(person::local))
+      .select(person::inbox_url)
+      .distinct()
+      .load::<DbUrl>(conn)
+      .await?;
+
+    Ok(res)
+  }
+
   pub async fn count_community_followers(
     pool: &mut DbPool<'_>,
     community_id: CommunityId,
   ) -> Result<i64, Error> {
     let conn = &mut get_conn(pool).await?;
-    let res = action_query(community_actions::followed)
+    let res = Self::joins()
       .filter(community_actions::community_id.eq(community_id))
       .select(count_star())
       .first::<i64>(conn)
@@ -69,13 +92,11 @@ impl CommunityFollowerView {
 
   pub async fn for_person(pool: &mut DbPool<'_>, person_id: PersonId) -> Result<Vec<Self>, Error> {
     let conn = &mut get_conn(pool).await?;
-    action_query(community_actions::followed)
-      .inner_join(community::table)
-      .inner_join(person::table.on(community_actions::person_id.eq(person::id)))
-      .select((community::all_columns, person::all_columns))
+    Self::joins()
       .filter(community_actions::person_id.eq(person_id))
       .filter(community::deleted.eq(false))
       .filter(community::removed.eq(false))
+      .select(Self::as_select())
       .order_by(community::title)
       .load::<CommunityFollowerView>(conn)
       .await
@@ -124,9 +145,13 @@ impl CommunityFollowerView {
         ),
     ));
 
-    let mut query = action_query(community_actions::followed)
-      .inner_join(person::table.on(community_actions::person_id.eq(person::id)))
-      .inner_join(community::table)
+    let mut query = Self::joins()
+      .select((
+        person::all_columns,
+        community::all_columns,
+        is_new_instance,
+        CommunityFollower::select_subscribed_type(),
+      ))
       .into_boxed();
     if all_communities {
       // if param is false, only return items for communities where user is a mod
@@ -142,12 +167,6 @@ impl CommunityFollowerView {
       .order_by(community_actions::followed.asc())
       .limit(limit)
       .offset(offset)
-      .select((
-        person::all_columns,
-        community::all_columns,
-        is_new_instance,
-        CommunityFollower::select_subscribed_type(),
-      ))
       .load::<(Person, Community, bool, SubscribedType)>(conn)
       .await?;
     Ok(
@@ -170,8 +189,7 @@ impl CommunityFollowerView {
     community_id: CommunityId,
   ) -> Result<i64, Error> {
     let conn = &mut get_conn(pool).await?;
-    action_query(community_actions::followed)
-      .inner_join(person::table.on(community_actions::person_id.eq(person::id)))
+    Self::joins()
       .filter(community_actions::community_id.eq(community_id))
       .filter(community_actions::follow_state.eq(CommunityFollowerState::ApprovalRequired))
       .select(count(community_actions::community_id))
@@ -188,7 +206,7 @@ impl CommunityFollowerView {
     }
     let conn = &mut get_conn(pool).await?;
     select(exists(
-      action_query(community_actions::followed)
+      Self::joins()
         .filter(community_actions::community_id.eq(community.id))
         .filter(community_actions::person_id.eq(from_person_id))
         .filter(community_actions::follow_state.eq(CommunityFollowerState::Accepted)),
@@ -205,8 +223,7 @@ impl CommunityFollowerView {
   ) -> Result<(), Error> {
     let conn = &mut get_conn(pool).await?;
     select(exists(
-      action_query(community_actions::followed)
-        .inner_join(person::table.on(community_actions::person_id.eq(person::id)))
+      Self::joins()
         .filter(community_actions::community_id.eq(community_id))
         .filter(person::instance_id.eq(instance_id))
         .filter(community_actions::follow_state.eq(CommunityFollowerState::Accepted)),
@@ -224,8 +241,7 @@ impl CommunityFollowerView {
   ) -> Result<(), Error> {
     let conn = &mut get_conn(pool).await?;
     select(exists(
-      action_query(community_actions::followed)
-        .inner_join(person::table.on(community_actions::person_id.eq(person::id)))
+      Self::joins()
         .filter(community_actions::community_id.eq(community_id))
         .filter(person::instance_id.eq(instance_id))
         .filter(community_actions::follow_state.eq(CommunityFollowerState::Accepted)),
diff --git a/crates/db_views/src/community/community_moderator_view.rs b/crates/db_views/src/community/community_moderator_view.rs
index a9ada92e14..ad60675aae 100644
--- a/crates/db_views/src/community/community_moderator_view.rs
+++ b/crates/db_views/src/community/community_moderator_view.rs
@@ -1,26 +1,43 @@
 use crate::structs::CommunityModeratorView;
-use diesel::{dsl::exists, result::Error, select, ExpressionMethods, JoinOnDsl, QueryDsl};
+use diesel::{
+  dsl::exists,
+  result::Error,
+  select,
+  ExpressionMethods,
+  JoinOnDsl,
+  QueryDsl,
+  SelectableHelper,
+};
 use diesel_async::RunQueryDsl;
 use lemmy_db_schema::{
   impls::local_user::LocalUserOptionHelper,
   newtypes::{CommunityId, PersonId},
   schema::{community, community_actions, person},
   source::local_user::LocalUser,
-  utils::{action_query, find_action, get_conn, DbPool},
+  utils::{get_conn, DbPool},
 };
 use lemmy_utils::error::{LemmyErrorType, LemmyResult};
 
 impl CommunityModeratorView {
+  #[diesel::dsl::auto_type(no_type_alias)]
+  fn joins() -> _ {
+    community_actions::table
+      .filter(community_actions::became_moderator.is_not_null())
+      .inner_join(community::table)
+      .inner_join(person::table.on(person::id.eq(community_actions::person_id)))
+  }
+
   pub async fn check_is_community_moderator(
     pool: &mut DbPool<'_>,
-    find_community_id: CommunityId,
-    find_person_id: PersonId,
+    community_id: CommunityId,
+    person_id: PersonId,
   ) -> LemmyResult<()> {
     let conn = &mut get_conn(pool).await?;
-    select(exists(find_action(
-      community_actions::became_moderator,
-      (find_person_id, find_community_id),
-    )))
+    select(exists(
+      Self::joins()
+        .filter(community_actions::person_id.eq(person_id))
+        .filter(community_actions::community_id.eq(community_id)),
+    ))
     .get_result::<bool>(conn)
     .await?
     .then_some(())
@@ -29,12 +46,11 @@ impl CommunityModeratorView {
 
   pub(crate) async fn is_community_moderator_of_any(
     pool: &mut DbPool<'_>,
-    find_person_id: PersonId,
+    person_id: PersonId,
   ) -> LemmyResult<()> {
     let conn = &mut get_conn(pool).await?;
     select(exists(
-      action_query(community_actions::became_moderator)
-        .filter(community_actions::person_id.eq(find_person_id)),
+      Self::joins().filter(community_actions::person_id.eq(person_id)),
     ))
     .get_result::<bool>(conn)
     .await?
@@ -47,13 +63,11 @@ impl CommunityModeratorView {
     community_id: CommunityId,
   ) -> Result<Vec<Self>, Error> {
     let conn = &mut get_conn(pool).await?;
-    action_query(community_actions::became_moderator)
-      .inner_join(community::table)
-      .inner_join(person::table.on(person::id.eq(community_actions::person_id)))
+    Self::joins()
       .filter(community_actions::community_id.eq(community_id))
-      .select((community::all_columns, person::all_columns))
+      .select(Self::as_select())
       .order_by(community_actions::became_moderator)
-      .load::<CommunityModeratorView>(conn)
+      .load::<Self>(conn)
       .await
   }
 
@@ -63,11 +77,9 @@ impl CommunityModeratorView {
     local_user: Option<&LocalUser>,
   ) -> Result<Vec<Self>, Error> {
     let conn = &mut get_conn(pool).await?;
-    let mut query = action_query(community_actions::became_moderator)
-      .inner_join(community::table)
-      .inner_join(person::table.on(person::id.eq(community_actions::person_id)))
+    let mut query = Self::joins()
       .filter(community_actions::person_id.eq(person_id))
-      .select((community::all_columns, person::all_columns))
+      .select(Self::as_select())
       .into_boxed();
 
     query = local_user.visible_communities_only(query);
@@ -82,17 +94,15 @@ impl CommunityModeratorView {
       query = query.filter(community::removed.eq(false))
     }
 
-    query.load::<CommunityModeratorView>(conn).await
+    query.load::<Self>(conn).await
   }
 
   /// Finds all communities first mods / creators
   /// Ideally this should be a group by, but diesel doesn't support it yet
   pub async fn get_community_first_mods(pool: &mut DbPool<'_>) -> Result<Vec<Self>, Error> {
     let conn = &mut get_conn(pool).await?;
-    action_query(community_actions::became_moderator)
-      .inner_join(community::table)
-      .inner_join(person::table.on(person::id.eq(community_actions::person_id)))
-      .select((community::all_columns, person::all_columns))
+    Self::joins()
+      .select(Self::as_select())
       // A hacky workaround instead of group_bys
       // https://stackoverflow.com/questions/24042359/how-to-join-only-one-row-in-joined-table-with-postgres
       .distinct_on(community_actions::community_id)
@@ -100,7 +110,7 @@ impl CommunityModeratorView {
         community_actions::community_id,
         community_actions::became_moderator,
       ))
-      .load::<CommunityModeratorView>(conn)
+      .load::<Self>(conn)
       .await
   }
 }
diff --git a/crates/db_views/src/community/community_person_ban_view.rs b/crates/db_views/src/community/community_person_ban_view.rs
index 224ea8d53f..4c89046dc8 100644
--- a/crates/db_views/src/community/community_person_ban_view.rs
+++ b/crates/db_views/src/community/community_person_ban_view.rs
@@ -2,12 +2,14 @@ use crate::structs::CommunityPersonBanView;
 use diesel::{
   dsl::{exists, not},
   select,
+  ExpressionMethods,
+  QueryDsl,
 };
 use diesel_async::RunQueryDsl;
 use lemmy_db_schema::{
   newtypes::{CommunityId, PersonId},
   schema::community_actions,
-  utils::{find_action, get_conn, DbPool},
+  utils::{get_conn, DbPool},
 };
 use lemmy_utils::error::{LemmyErrorType, LemmyResult};
 
@@ -18,13 +20,13 @@ impl CommunityPersonBanView {
     from_community_id: CommunityId,
   ) -> LemmyResult<()> {
     let conn = &mut get_conn(pool).await?;
-    select(not(exists(find_action(
-      community_actions::received_ban,
-      (from_person_id, from_community_id),
-    ))))
-    .get_result::<bool>(conn)
-    .await?
-    .then_some(())
-    .ok_or(LemmyErrorType::PersonIsBannedFromCommunity.into())
+    let find_action = community_actions::table
+      .find((from_person_id, from_community_id))
+      .filter(community_actions::received_ban.is_not_null());
+    select(not(exists(find_action)))
+      .get_result::<bool>(conn)
+      .await?
+      .then_some(())
+      .ok_or(LemmyErrorType::PersonIsBannedFromCommunity.into())
   }
 }
diff --git a/crates/db_views/src/community/community_view.rs b/crates/db_views/src/community/community_view.rs
index fa9144c4b7..2728d09267 100644
--- a/crates/db_views/src/community/community_view.rs
+++ b/crates/db_views/src/community/community_view.rs
@@ -1,12 +1,12 @@
 use crate::structs::{CommunityModeratorView, CommunitySortType, CommunityView, PersonView};
 use diesel::{
-  pg::Pg,
   result::Error,
   BoolExpressionMethods,
   ExpressionMethods,
+  JoinOnDsl,
   NullableExpressionMethods,
-  PgTextExpressionMethods,
   QueryDsl,
+  SelectableHelper,
 };
 use diesel_async::RunQueryDsl;
 use lemmy_db_schema::{
@@ -14,175 +14,56 @@ use lemmy_db_schema::{
   newtypes::{CommunityId, PersonId},
   schema::{community, community_actions, community_aggregates, instance_actions},
   source::{
-    community::{CommunityFollower, CommunityFollowerState},
+    community::{Community, CommunityFollowerState},
     local_user::LocalUser,
     site::Site,
   },
-  utils::{
-    actions,
-    functions::lower,
-    fuzzy_search,
-    limit_and_offset,
-    DbConn,
-    DbPool,
-    ListFn,
-    Queries,
-    ReadFn,
-  },
+  utils::{functions::lower, get_conn, limit_and_offset, DbPool},
   ListingType,
-  PostSortType,
 };
 use lemmy_utils::error::{LemmyErrorType, LemmyResult};
 
-type QueriesReadTypes<'a> = (CommunityId, Option<&'a LocalUser>, bool);
-type QueriesListTypes<'a> = (CommunityQuery<'a>, &'a Site);
+impl CommunityView {
+  #[diesel::dsl::auto_type(no_type_alias)]
+  fn joins(person_id: Option<PersonId>) -> _ {
+    let community_actions_join = community_actions::table.on(
+      community_actions::community_id
+        .eq(community::id)
+        .and(community_actions::person_id.nullable().eq(person_id)),
+    );
 
-fn queries<'a>() -> Queries<
-  impl ReadFn<'a, CommunityView, QueriesReadTypes<'a>>,
-  impl ListFn<'a, CommunityView, QueriesListTypes<'a>>,
-> {
-  let all_joins = |query: community::BoxedQuery<'a, Pg>, my_local_user: Option<&'a LocalUser>| {
-    query
+    let instance_actions_join = instance_actions::table.on(
+      instance_actions::instance_id
+        .eq(community::instance_id)
+        .and(instance_actions::person_id.nullable().eq(person_id)),
+    );
+
+    community::table
       .inner_join(community_aggregates::table)
-      .left_join(actions(
-        community_actions::table,
-        my_local_user.person_id(),
-        community::id,
-      ))
-      .left_join(actions(
-        instance_actions::table,
-        my_local_user.person_id(),
-        community::instance_id,
-      ))
-  };
+      .left_join(community_actions_join)
+      .left_join(instance_actions_join)
+  }
 
-  let selection = (
-    community::all_columns,
-    CommunityFollower::select_subscribed_type(),
-    community_actions::blocked.nullable().is_not_null(),
-    community_aggregates::all_columns,
-    community_actions::received_ban.nullable().is_not_null(),
-  );
-
-  let not_removed_or_deleted = community::removed
-    .eq(false)
-    .and(community::deleted.eq(false));
-
-  let read = move |mut conn: DbConn<'a>,
-                   (community_id, my_local_user, is_mod_or_admin): (
-    CommunityId,
-    Option<&'a LocalUser>,
-    bool,
-  )| async move {
-    let mut query = all_joins(
-      community::table.find(community_id).into_boxed(),
-      my_local_user,
-    )
-    .select(selection);
+  pub async fn read(
+    pool: &mut DbPool<'_>,
+    community_id: CommunityId,
+    my_local_user: Option<&'_ LocalUser>,
+    is_mod_or_admin: bool,
+  ) -> Result<Self, Error> {
+    let conn = &mut get_conn(pool).await?;
+    let mut query = Self::joins(my_local_user.person_id())
+      .filter(community::id.eq(community_id))
+      .select(Self::as_select())
+      .into_boxed();
 
     // Hide deleted and removed for non-admins or mods
     if !is_mod_or_admin {
-      query = query.filter(not_removed_or_deleted);
+      query = query.filter(Community::hide_removed_and_deleted());
     }
 
     query = my_local_user.visible_communities_only(query);
 
-    query.first(&mut conn).await
-  };
-
-  let list = move |mut conn: DbConn<'a>, (o, site): (CommunityQuery<'a>, &'a Site)| async move {
-    use CommunitySortType::*;
-
-    let mut query = all_joins(community::table.into_boxed(), o.local_user).select(selection);
-
-    if let Some(search_term) = o.search_term {
-      let searcher = fuzzy_search(&search_term);
-      let name_filter = community::name.ilike(searcher.clone());
-      let title_filter = community::title.ilike(searcher.clone());
-      let description_filter = community::description.ilike(searcher.clone());
-      query = if o.title_only.unwrap_or_default() {
-        query.filter(name_filter.or(title_filter))
-      } else {
-        query.filter(name_filter.or(title_filter.or(description_filter)))
-      }
-    }
-
-    // Hide deleted and removed for non-admins or mods
-    if !o.is_mod_or_admin {
-      query = query.filter(not_removed_or_deleted).filter(
-        community::hidden
-          .eq(false)
-          .or(community_actions::follow_state.is_not_null()),
-      );
-    }
-
-    match o.sort.unwrap_or(Hot) {
-      Hot | Active | Scaled => query = query.order_by(community_aggregates::hot_rank.desc()),
-      NewComments | TopDay | TopTwelveHour | TopSixHour | TopHour => {
-        query = query.order_by(community_aggregates::users_active_day.desc())
-      }
-      New => query = query.order_by(community::published.desc()),
-      Old => query = query.order_by(community::published.asc()),
-      // Controversial is temporary until a CommentSortType is created
-      MostComments | Controversial => query = query.order_by(community_aggregates::comments.desc()),
-      TopAll | TopYear | TopNineMonths => {
-        query = query.order_by(community_aggregates::subscribers.desc())
-      }
-      TopSixMonths | TopThreeMonths => {
-        query = query.order_by(community_aggregates::users_active_half_year.desc())
-      }
-      TopMonth => query = query.order_by(community_aggregates::users_active_month.desc()),
-      TopWeek => query = query.order_by(community_aggregates::users_active_week.desc()),
-      NameAsc => query = query.order_by(lower(community::name).asc()),
-      NameDesc => query = query.order_by(lower(community::name).desc()),
-    };
-
-    let is_subscribed = community_actions::follow_state.eq(Some(CommunityFollowerState::Accepted));
-
-    if let Some(listing_type) = o.listing_type {
-      query = match listing_type {
-        ListingType::All => query.filter(community::hidden.eq(false).or(is_subscribed)),
-        ListingType::Subscribed => query.filter(is_subscribed),
-        ListingType::Local => query
-          .filter(community::local.eq(true))
-          .filter(community::hidden.eq(false).or(is_subscribed)),
-        ListingType::ModeratorView => {
-          query.filter(community_actions::became_moderator.is_not_null())
-        }
-      };
-    }
-
-    // Don't show blocked communities and communities on blocked instances. nsfw communities are
-    // also hidden (based on profile setting)
-    query = query.filter(instance_actions::blocked.is_null());
-    query = query.filter(community_actions::blocked.is_null());
-    if !(o.local_user.show_nsfw(site) || o.show_nsfw) {
-      query = query.filter(community::nsfw.eq(false));
-    }
-
-    query = o.local_user.visible_communities_only(query);
-
-    let (limit, offset) = limit_and_offset(o.page, o.limit)?;
-    query
-      .limit(limit)
-      .offset(offset)
-      .load::<CommunityView>(&mut conn)
-      .await
-  };
-
-  Queries::new(read, list)
-}
-
-impl CommunityView {
-  pub async fn read(
-    pool: &mut DbPool<'_>,
-    community_id: CommunityId,
-    my_local_user: Option<&'_ LocalUser>,
-    is_mod_or_admin: bool,
-  ) -> Result<Self, Error> {
-    queries()
-      .read(pool, (community_id, my_local_user, is_mod_or_admin))
-      .await
+    query.first(conn).await
   }
 
   pub async fn check_is_mod_or_admin(
@@ -222,38 +103,11 @@ impl CommunityView {
   }
 }
 
-impl From<PostSortType> for CommunitySortType {
-  fn from(value: PostSortType) -> Self {
-    match value {
-      PostSortType::Active => Self::Active,
-      PostSortType::Hot => Self::Hot,
-      PostSortType::New => Self::New,
-      PostSortType::Old => Self::Old,
-      PostSortType::TopDay => Self::TopDay,
-      PostSortType::TopWeek => Self::TopWeek,
-      PostSortType::TopMonth => Self::TopMonth,
-      PostSortType::TopYear => Self::TopYear,
-      PostSortType::TopAll => Self::TopAll,
-      PostSortType::MostComments => Self::MostComments,
-      PostSortType::NewComments => Self::NewComments,
-      PostSortType::TopHour => Self::TopHour,
-      PostSortType::TopSixHour => Self::TopSixHour,
-      PostSortType::TopTwelveHour => Self::TopTwelveHour,
-      PostSortType::TopThreeMonths => Self::TopThreeMonths,
-      PostSortType::TopSixMonths => Self::TopSixMonths,
-      PostSortType::TopNineMonths => Self::TopNineMonths,
-      PostSortType::Controversial => Self::Controversial,
-      PostSortType::Scaled => Self::Scaled,
-    }
-  }
-}
-
 #[derive(Default)]
 pub struct CommunityQuery<'a> {
   pub listing_type: Option<ListingType>,
   pub sort: Option<CommunitySortType>,
   pub local_user: Option<&'a LocalUser>,
-  pub search_term: Option<String>,
   pub title_only: Option<bool>,
   pub is_mod_or_admin: bool,
   pub show_nsfw: bool,
@@ -263,7 +117,73 @@ pub struct CommunityQuery<'a> {
 
 impl CommunityQuery<'_> {
   pub async fn list(self, site: &Site, pool: &mut DbPool<'_>) -> Result<Vec<CommunityView>, Error> {
-    queries().list(pool, (self, site)).await
+    use CommunitySortType::*;
+    let conn = &mut get_conn(pool).await?;
+    let o = self;
+
+    let mut query = CommunityView::joins(o.local_user.person_id())
+      .select(CommunityView::as_select())
+      .into_boxed();
+
+    // Hide deleted and removed for non-admins or mods
+    if !o.is_mod_or_admin {
+      query = query.filter(Community::hide_removed_and_deleted()).filter(
+        community::hidden
+          .eq(false)
+          .or(community_actions::follow_state.is_not_null()),
+      );
+    }
+
+    let is_subscribed = community_actions::follow_state.eq(Some(CommunityFollowerState::Accepted));
+
+    if let Some(listing_type) = o.listing_type {
+      query = match listing_type {
+        ListingType::All => query.filter(community::hidden.eq(false).or(is_subscribed)),
+        ListingType::Subscribed => query.filter(is_subscribed),
+        ListingType::Local => query
+          .filter(community::local.eq(true))
+          .filter(community::hidden.eq(false).or(is_subscribed)),
+        ListingType::ModeratorView => {
+          query.filter(community_actions::became_moderator.is_not_null())
+        }
+      };
+    }
+
+    // Don't show blocked communities and communities on blocked instances. nsfw communities are
+    // also hidden (based on profile setting)
+    query = query.filter(instance_actions::blocked.is_null());
+    query = query.filter(community_actions::blocked.is_null());
+    if !(o.local_user.show_nsfw(site) || o.show_nsfw) {
+      query = query.filter(community::nsfw.eq(false));
+    }
+
+    query = o.local_user.visible_communities_only(query);
+
+    match o.sort.unwrap_or_default() {
+      Hot => query = query.order_by(community_aggregates::hot_rank.desc()),
+      Comments => query = query.order_by(community_aggregates::comments.desc()),
+      Posts => query = query.order_by(community_aggregates::posts.desc()),
+      New => query = query.order_by(community::published.desc()),
+      Old => query = query.order_by(community::published.asc()),
+      Subscribers => query = query.order_by(community_aggregates::subscribers.desc()),
+      SubscribersLocal => query = query.order_by(community_aggregates::subscribers_local.desc()),
+      ActiveSixMonths => {
+        query = query.order_by(community_aggregates::users_active_half_year.desc())
+      }
+      ActiveMonthly => query = query.order_by(community_aggregates::users_active_month.desc()),
+      ActiveWeekly => query = query.order_by(community_aggregates::users_active_week.desc()),
+      ActiveDaily => query = query.order_by(community_aggregates::users_active_day.desc()),
+      NameAsc => query = query.order_by(lower(community::name).asc()),
+      NameDesc => query = query.order_by(lower(community::name).desc()),
+    };
+
+    let (limit, offset) = limit_and_offset(o.page, o.limit)?;
+
+    query
+      .limit(limit)
+      .offset(offset)
+      .load::<CommunityView>(conn)
+      .await
   }
 }
 
diff --git a/crates/db_views/src/local_user/local_user_view.rs b/crates/db_views/src/local_user/local_user_view.rs
index 68072cb5a1..6525d5ca79 100644
--- a/crates/db_views/src/local_user/local_user_view.rs
+++ b/crates/db_views/src/local_user/local_user_view.rs
@@ -1,6 +1,13 @@
 use crate::structs::LocalUserView;
 use actix_web::{dev::Payload, FromRequest, HttpMessage, HttpRequest};
-use diesel::{result::Error, BoolExpressionMethods, ExpressionMethods, JoinOnDsl, QueryDsl};
+use diesel::{
+  result::Error,
+  BoolExpressionMethods,
+  ExpressionMethods,
+  JoinOnDsl,
+  QueryDsl,
+  SelectableHelper,
+};
 use diesel_async::RunQueryDsl;
 use lemmy_db_schema::{
   newtypes::{LocalUserId, OAuthProviderId, PersonId},
@@ -13,118 +20,72 @@ use lemmy_db_schema::{
   traits::Crud,
   utils::{
     functions::{coalesce, lower},
-    DbConn,
+    get_conn,
     DbPool,
-    ListFn,
-    Queries,
-    ReadFn,
   },
 };
 use lemmy_utils::error::{LemmyError, LemmyErrorExt, LemmyErrorType, LemmyResult};
 use std::future::{ready, Ready};
 
-enum ReadBy<'a> {
-  Id(LocalUserId),
-  Person(PersonId),
-  Name(&'a str),
-  NameOrEmail(&'a str),
-  Email(&'a str),
-  OAuthID(OAuthProviderId, &'a str),
-}
-
-enum ListMode {
-  AdminsWithEmails,
-}
-
-fn queries<'a>(
-) -> Queries<impl ReadFn<'a, LocalUserView, ReadBy<'a>>, impl ListFn<'a, LocalUserView, ListMode>> {
-  let selection = (
-    local_user::all_columns,
-    local_user_vote_display_mode::all_columns,
-    person::all_columns,
-    person_aggregates::all_columns,
-  );
-
-  let read = move |mut conn: DbConn<'a>, search: ReadBy<'a>| async move {
-    let mut query = local_user::table.into_boxed();
-    query = match search {
-      ReadBy::Id(local_user_id) => query.filter(local_user::id.eq(local_user_id)),
-      ReadBy::Email(from_email) => {
-        query.filter(lower(coalesce(local_user::email, "")).eq(from_email.to_lowercase()))
-      }
-      _ => query,
-    };
-    let mut query = query.inner_join(person::table);
-    query = match search {
-      ReadBy::Person(person_id) => query.filter(person::id.eq(person_id)),
-      ReadBy::Name(name) => query.filter(lower(person::name).eq(name.to_lowercase())),
-      ReadBy::NameOrEmail(name_or_email) => query.filter(
-        lower(person::name)
-          .eq(lower(name_or_email.to_lowercase()))
-          .or(lower(coalesce(local_user::email, "")).eq(name_or_email.to_lowercase())),
-      ),
-      _ => query,
-    };
-    let query = query
+impl LocalUserView {
+  #[diesel::dsl::auto_type(no_type_alias)]
+  fn joins() -> _ {
+    local_user::table
       .inner_join(local_user_vote_display_mode::table)
-      .inner_join(person_aggregates::table.on(person::id.eq(person_aggregates::person_id)));
-
-    if let ReadBy::OAuthID(oauth_provider_id, oauth_user_id) = search {
-      query
-        .inner_join(oauth_account::table)
-        .filter(oauth_account::oauth_provider_id.eq(oauth_provider_id))
-        .filter(oauth_account::oauth_user_id.eq(oauth_user_id))
-        .select(selection)
-        .first(&mut conn)
-        .await
-    } else {
-      query.select(selection).first(&mut conn).await
-    }
-  };
-
-  let list = move |mut conn: DbConn<'a>, mode: ListMode| async move {
-    match mode {
-      ListMode::AdminsWithEmails => {
-        local_user::table
-          .inner_join(local_user_vote_display_mode::table)
-          .inner_join(person::table)
-          .inner_join(person_aggregates::table.on(person::id.eq(person_aggregates::person_id)))
-          .filter(local_user::email.is_not_null())
-          .filter(local_user::admin.eq(true))
-          .select(selection)
-          .load::<LocalUserView>(&mut conn)
-          .await
-      }
-    }
-  };
-
-  Queries::new(read, list)
-}
+      .inner_join(person::table)
+      .inner_join(person_aggregates::table.on(person::id.eq(person_aggregates::person_id)))
+  }
 
-impl LocalUserView {
   pub async fn read(pool: &mut DbPool<'_>, local_user_id: LocalUserId) -> Result<Self, Error> {
-    queries().read(pool, ReadBy::Id(local_user_id)).await
+    let conn = &mut get_conn(pool).await?;
+    Self::joins()
+      .filter(local_user::id.eq(local_user_id))
+      .select(Self::as_select())
+      .first(conn)
+      .await
   }
 
   pub async fn read_person(pool: &mut DbPool<'_>, person_id: PersonId) -> Result<Self, Error> {
-    queries().read(pool, ReadBy::Person(person_id)).await
+    let conn = &mut get_conn(pool).await?;
+    Self::joins()
+      .filter(person::id.eq(person_id))
+      .select(Self::as_select())
+      .first(conn)
+      .await
   }
 
   pub async fn read_from_name(pool: &mut DbPool<'_>, name: &str) -> Result<Self, Error> {
-    queries().read(pool, ReadBy::Name(name)).await
+    let conn = &mut get_conn(pool).await?;
+    Self::joins()
+      .filter(lower(person::name).eq(name.to_lowercase()))
+      .select(Self::as_select())
+      .first(conn)
+      .await
   }
 
   pub async fn find_by_email_or_name(
     pool: &mut DbPool<'_>,
     name_or_email: &str,
   ) -> Result<Self, Error> {
-    queries()
-      .read(pool, ReadBy::NameOrEmail(name_or_email))
+    let conn = &mut get_conn(pool).await?;
+    Self::joins()
+      .filter(
+        lower(person::name)
+          .eq(lower(name_or_email.to_lowercase()))
+          .or(lower(coalesce(local_user::email, "")).eq(name_or_email.to_lowercase())),
+      )
+      .select(Self::as_select())
+      .first(conn)
       .await
   }
 
   pub async fn find_by_email(pool: &mut DbPool<'_>, from_email: &str) -> Result<Self, Error> {
-    queries().read(pool, ReadBy::Email(from_email)).await
+    let conn = &mut get_conn(pool).await?;
+    Self::joins()
+      .filter(lower(coalesce(local_user::email, "")).eq(from_email.to_lowercase()))
+      .select(Self::as_select())
+      .first(conn)
+      .await
   }
 
   pub async fn find_by_oauth_id(
@@ -132,13 +93,24 @@ impl LocalUserView {
     oauth_provider_id: OAuthProviderId,
     oauth_user_id: &str,
   ) -> Result<Self, Error> {
-    queries()
-      .read(pool, ReadBy::OAuthID(oauth_provider_id, oauth_user_id))
+    let conn = &mut get_conn(pool).await?;
+    Self::joins()
+      .inner_join(oauth_account::table)
+      .filter(oauth_account::oauth_provider_id.eq(oauth_provider_id))
+      .filter(oauth_account::oauth_user_id.eq(oauth_user_id))
+      .select(Self::as_select())
+      .first(conn)
       .await
   }
 
   pub async fn list_admins_with_emails(pool: &mut DbPool<'_>) -> Result<Vec<Self>, Error> {
-    queries().list(pool, ListMode::AdminsWithEmails).await
+    let conn = &mut get_conn(pool).await?;
+    Self::joins()
+      .filter(local_user::email.is_not_null())
+      .filter(local_user::admin.eq(true))
+      .select(Self::as_select())
+      .load::<Self>(conn)
+      .await
   }
 
   pub async fn create_test_user(
diff --git a/crates/db_views/src/person/person_view.rs b/crates/db_views/src/person/person_view.rs
index 5c1a3ae70e..e0a2160641 100644
--- a/crates/db_views/src/person/person_view.rs
+++ b/crates/db_views/src/person/person_view.rs
@@ -1,173 +1,71 @@
 use crate::structs::PersonView;
 use diesel::{
-  pg::Pg,
   result::Error,
   BoolExpressionMethods,
   ExpressionMethods,
   NullableExpressionMethods,
-  PgTextExpressionMethods,
   QueryDsl,
+  SelectableHelper,
 };
 use diesel_async::RunQueryDsl;
 use lemmy_db_schema::{
   newtypes::PersonId,
   schema::{local_user, person, person_aggregates},
-  utils::{
-    functions::coalesce,
-    fuzzy_search,
-    limit_and_offset,
-    now,
-    DbConn,
-    DbPool,
-    ListFn,
-    Queries,
-    ReadFn,
-  },
-  ListingType,
-  PostSortType,
+  utils::{get_conn, now, DbPool},
 };
-use serde::{Deserialize, Serialize};
-use strum::{Display, EnumString};
 
-enum ListMode {
-  Admins,
-  Banned,
-  Query(PersonQuery),
-}
-
-#[derive(EnumString, Display, Debug, Serialize, Deserialize, Clone, Copy)]
-/// The person sort types. Converted automatically from `SortType`
-enum PersonSortType {
-  New,
-  Old,
-  MostComments,
-  CommentScore,
-  PostScore,
-  PostCount,
-}
-
-fn post_to_person_sort_type(sort: PostSortType) -> PersonSortType {
-  use PostSortType::*;
-  match sort {
-    Active | Hot | Controversial => PersonSortType::CommentScore,
-    New | NewComments => PersonSortType::New,
-    MostComments => PersonSortType::MostComments,
-    Old => PersonSortType::Old,
-    _ => PersonSortType::CommentScore,
-  }
-}
-
-fn queries<'a>(
-) -> Queries<impl ReadFn<'a, PersonView, (PersonId, bool)>, impl ListFn<'a, PersonView, ListMode>> {
-  let all_joins = move |query: person::BoxedQuery<'a, Pg>| {
-    query
+impl PersonView {
+  #[diesel::dsl::auto_type(no_type_alias)]
+  fn joins() -> _ {
+    person::table
       .inner_join(person_aggregates::table)
       .left_join(local_user::table)
-      .select((
-        person::all_columns,
-        person_aggregates::all_columns,
-        coalesce(local_user::admin.nullable(), false),
-      ))
-  };
-
-  let read = move |mut conn: DbConn<'a>, params: (PersonId, bool)| async move {
-    let (person_id, is_admin) = params;
-    let mut query = all_joins(person::table.find(person_id).into_boxed());
-    if !is_admin {
-      query = query.filter(person::deleted.eq(false));
-    }
-    query.first(&mut conn).await
-  };
-
-  let list = move |mut conn: DbConn<'a>, mode: ListMode| async move {
-    let mut query = all_joins(person::table.into_boxed()).filter(person::deleted.eq(false));
-    match mode {
-      ListMode::Admins => {
-        query = query
-          .filter(local_user::admin.eq(true))
-          .filter(person::deleted.eq(false))
-          .order_by(person::published);
-      }
-      ListMode::Banned => {
-        query = query
-          .filter(
-            person::local.eq(true).and(
-              person::banned.eq(true).and(
-                person::ban_expires
-                  .is_null()
-                  .or(person::ban_expires.gt(now().nullable())),
-              ),
-            ),
-          )
-          .filter(person::deleted.eq(false));
-      }
-      ListMode::Query(o) => {
-        if let Some(search_term) = o.search_term {
-          let searcher = fuzzy_search(&search_term);
-          query = query
-            .filter(person::name.ilike(searcher.clone()))
-            .or_filter(person::display_name.ilike(searcher));
-        }
-
-        let sort = o.sort.map(post_to_person_sort_type);
-        query = match sort.unwrap_or(PersonSortType::CommentScore) {
-          PersonSortType::New => query.order_by(person::published.desc()),
-          PersonSortType::Old => query.order_by(person::published.asc()),
-          PersonSortType::MostComments => query.order_by(person_aggregates::comment_count.desc()),
-          PersonSortType::CommentScore => query.order_by(person_aggregates::comment_score.desc()),
-          PersonSortType::PostScore => query.order_by(person_aggregates::post_score.desc()),
-          PersonSortType::PostCount => query.order_by(person_aggregates::post_count.desc()),
-        };
-
-        let (limit, offset) = limit_and_offset(o.page, o.limit)?;
-        query = query.limit(limit).offset(offset);
-
-        if let Some(listing_type) = o.listing_type {
-          query = match listing_type {
-            // return nothing as its not possible to follow users
-            ListingType::Subscribed => query.limit(0),
-            ListingType::Local => query.filter(person::local.eq(true)),
-            _ => query,
-          };
-        }
-      }
-    }
-    query.load::<PersonView>(&mut conn).await
-  };
-
-  Queries::new(read, list)
-}
+  }
 
-impl PersonView {
   pub async fn read(
     pool: &mut DbPool<'_>,
     person_id: PersonId,
     is_admin: bool,
   ) -> Result<Self, Error> {
-    queries().read(pool, (person_id, is_admin)).await
+    let conn = &mut get_conn(pool).await?;
+    let mut query = Self::joins()
+      .filter(person::id.eq(person_id))
+      .select(Self::as_select())
+      .into_boxed();
+
+    if !is_admin {
+      query = query.filter(person::deleted.eq(false))
+    }
+
+    query.first(conn).await
   }
 
   pub async fn admins(pool: &mut DbPool<'_>) -> Result<Vec<Self>, Error> {
-    queries().list(pool, ListMode::Admins).await
+    let conn = &mut get_conn(pool).await?;
+    Self::joins()
+      .filter(person::deleted.eq(false))
+      .filter(local_user::admin.eq(true))
+      .order_by(person::published)
+      .select(Self::as_select())
+      .load::<Self>(conn)
+      .await
   }
 
   pub async fn banned(pool: &mut DbPool<'_>) -> Result<Vec<Self>, Error> {
-    queries().list(pool, ListMode::Banned).await
-  }
-}
-
-#[derive(Default)]
-pub struct PersonQuery {
-  pub sort: Option<PostSortType>,
-  pub search_term: Option<String>,
-  pub listing_type: Option<ListingType>,
-  pub page: Option<i64>,
-  pub limit: Option<i64>,
-}
-
-impl PersonQuery {
-  pub async fn list(self, pool: &mut DbPool<'_>) -> Result<Vec<PersonView>, Error> {
-    queries().list(pool, ListMode::Query(self)).await
+    let conn = &mut get_conn(pool).await?;
+    Self::joins()
+      .filter(person::deleted.eq(false))
+      .filter(
+        person::banned.eq(true).and(
+          person::ban_expires
+            .is_null()
+            .or(person::ban_expires.gt(now().nullable())),
+        ),
+      )
+      .order_by(person::published)
+      .select(Self::as_select())
+      .load::<Self>(conn)
+      .await
   }
 }
 
@@ -258,15 +156,6 @@ mod tests {
     let read = PersonView::read(pool, data.alice.id, true).await;
     assert!(read.is_ok());
 
-    let list = PersonQuery {
-      sort: Some(PostSortType::New),
-      ..Default::default()
-    }
-    .list(pool)
-    .await?;
-    assert_length!(1, list);
-    assert_eq!(list[0].person.id, data.bob.id);
-
     cleanup(data, pool).await
   }
 
@@ -323,31 +212,4 @@ mod tests {
 
     cleanup(data, pool).await
   }
-
-  #[tokio::test]
-  #[serial]
-  async fn listing_type() -> LemmyResult<()> {
-    let pool = &build_db_pool_for_tests();
-    let pool = &mut pool.into();
-    let data = init_data(pool).await?;
-
-    let list = PersonQuery {
-      listing_type: Some(ListingType::Local),
-      ..Default::default()
-    }
-    .list(pool)
-    .await?;
-    assert_length!(1, list);
-    assert_eq!(list[0].person.id, data.alice.id);
-
-    let list = PersonQuery {
-      listing_type: Some(ListingType::All),
-      ..Default::default()
-    }
-    .list(pool)
-    .await?;
-    assert_length!(2, list);
-
-    cleanup(data, pool).await
-  }
 }
diff --git a/crates/db_views/src/post/post_view.rs b/crates/db_views/src/post/post_view.rs
index 7065b46377..0500733c9b 100644
--- a/crates/db_views/src/post/post_view.rs
+++ b/crates/db_views/src/post/post_view.rs
@@ -19,7 +19,7 @@ use lemmy_db_schema::{
   aggregates::structs::{post_aggregates_keys as key, PostAggregates},
   aliases::creator_community_actions,
   impls::local_user::LocalUserOptionHelper,
-  newtypes::{CommunityId, LocalUserId, PersonId, PostId},
+  newtypes::{CommunityId, PersonId, PostId},
   schema::{
     community,
     community_actions,
@@ -42,9 +42,6 @@ use lemmy_db_schema::{
     site::Site,
   },
   utils::{
-    action_query,
-    actions,
-    actions_alias,
     functions::coalesce,
     fuzzy_search,
     get_conn,
@@ -52,11 +49,7 @@ use lemmy_db_schema::{
     now,
     paginate,
     Commented,
-    DbConn,
     DbPool,
-    ListFn,
-    Queries,
-    ReadFn,
     ReverseTimestampKey,
   },
   CommunityVisibility,
@@ -66,37 +59,83 @@ use lemmy_db_schema::{
 use tracing::debug;
 use PostSortType::*;
 
-type QueriesReadTypes<'a> = (PostId, Option<&'a LocalUser>, bool);
-type QueriesListTypes<'a> = (PostQuery<'a>, &'a Site);
-
-fn queries<'a>() -> Queries<
-  impl ReadFn<'a, PostView, QueriesReadTypes<'a>>,
-  impl ListFn<'a, PostView, QueriesListTypes<'a>>,
-> {
-  let creator_is_admin = exists(
-    local_user::table.filter(
-      post_aggregates::creator_id
-        .eq(local_user::person_id)
-        .and(local_user::admin.eq(true)),
-    ),
-  );
-
-  // TODO maybe this should go to localuser also
-  let all_joins = move |query: post_aggregates::BoxedQuery<'a, Pg>,
-                        my_person_id: Option<PersonId>| {
-    // We fetch post tags by letting postgresql aggregate them internally in a subquery into JSON.
-    // This is a simple way to join m rows into n rows without duplicating the data and getting
-    // complex diesel types. In pure SQL you would usually do this either using a LEFT JOIN + then
-    // aggregating the results in the application code. But this results in a lot of duplicate
-    // data transferred (since each post will be returned once per tag that it has) and more
-    // complicated application code. The diesel docs suggest doing three separate sequential queries
-    // in this case (see https://diesel.rs/guides/relations.html#many-to-many-or-mn ): First fetch
-    // the posts, then fetch all relevant post-tag-association tuples from the db, and then fetch
-    // all the relevant tag objects.
-    //
-    // If we want to filter by post tag we will have to add
-    // separate logic below since this subquery can't affect filtering, but it is simple (`WHERE
-    // exists (select 1 from post_community_post_tags where community_post_tag_id in (1,2,3,4)`).
+impl PostView {
+  // TODO while we can abstract the joins into a function, the selects are currently impossible to
+  // do, because they rely on a few types that aren't yet publicly exported in diesel:
+  // https://github.com/diesel-rs/diesel/issues/4462
+
+  #[diesel::dsl::auto_type(no_type_alias)]
+  fn joins(my_person_id: Option<PersonId>) -> _ {
+    let community_actions_join = community_actions::table.on(
+      community_actions::community_id
+        .eq(post_aggregates::community_id)
+        .and(community_actions::person_id.nullable().eq(my_person_id)),
+    );
+
+    let person_actions_join = person_actions::table.on(
+      person_actions::target_id
+        .eq(post_aggregates::creator_id)
+        .and(person_actions::person_id.nullable().eq(my_person_id)),
+    );
+
+    let post_actions_join = post_actions::table.on(
+      post_actions::post_id
+        .eq(post_aggregates::post_id)
+        .and(post_actions::person_id.nullable().eq(my_person_id)),
+    );
+
+    let instance_actions_join = instance_actions::table.on(
+      instance_actions::instance_id
+        .eq(post_aggregates::instance_id)
+        .and(instance_actions::person_id.nullable().eq(my_person_id)),
+    );
+
+    let post_creator_community_actions_join = creator_community_actions.on(
+      creator_community_actions
+        .field(community_actions::community_id)
+        .eq(post_aggregates::community_id)
+        .and(
+          creator_community_actions
+            .field(community_actions::person_id)
+            .eq(post_aggregates::creator_id),
+        ),
+    );
+
+    let image_details_join =
+      image_details::table.on(post::thumbnail_url.eq(image_details::link.nullable()));
+
+    post_aggregates::table
+      .inner_join(person::table)
+      .inner_join(community::table)
+      .inner_join(post::table)
+      .left_join(image_details_join)
+      .left_join(community_actions_join)
+      .left_join(person_actions_join)
+      .left_join(post_actions_join)
+      .left_join(instance_actions_join)
+      .left_join(post_creator_community_actions_join)
+  }
+
+  #[diesel::dsl::auto_type(no_type_alias)]
+  fn creator_is_admin() -> _ {
+    exists(
+      local_user::table.filter(
+        post_aggregates::creator_id
+          .eq(local_user::person_id)
+          .and(local_user::admin.eq(true)),
+      ),
+    )
+  }
+
+  pub async fn read(
+    pool: &mut DbPool<'_>,
+    post_id: PostId,
+    my_local_user: Option<&'_ LocalUser>,
+    is_mod_or_admin: bool,
+  ) -> Result<Self, Error> {
+    let conn = &mut get_conn(pool).await?;
+    let my_person_id = my_local_user.person_id();
+
     let post_tags = post_tag::table
       .inner_join(tag::table)
       .select(diesel::dsl::sql::<diesel::sql_types::Json>(
@@ -106,36 +145,8 @@ fn queries<'a>() -> Queries<
       .filter(tag::deleted.eq(false))
       .single_value();
 
-    query
-      .inner_join(person::table)
-      .inner_join(community::table)
-      .inner_join(post::table)
-      .left_join(image_details::table.on(post::thumbnail_url.eq(image_details::link.nullable())))
-      .left_join(actions(
-        community_actions::table,
-        my_person_id,
-        post_aggregates::community_id,
-      ))
-      .left_join(actions(
-        person_actions::table,
-        my_person_id,
-        post_aggregates::creator_id,
-      ))
-      .left_join(actions(
-        post_actions::table,
-        my_person_id,
-        post_aggregates::post_id,
-      ))
-      .left_join(actions(
-        instance_actions::table,
-        my_person_id,
-        post_aggregates::instance_id,
-      ))
-      .left_join(actions_alias(
-        creator_community_actions,
-        post_aggregates::creator_id,
-        post_aggregates::community_id,
-      ))
+    let mut query = Self::joins(my_person_id)
+      .filter(post_aggregates::post_id.eq(post_id))
       .select((
         post::all_columns,
         person::all_columns,
@@ -150,7 +161,7 @@ fn queries<'a>() -> Queries<
           .field(community_actions::became_moderator)
           .nullable()
           .is_not_null(),
-        creator_is_admin,
+        Self::creator_is_admin(),
         post_aggregates::all_columns,
         CommunityFollower::select_subscribed_type(),
         post_actions::saved.nullable().is_not_null(),
@@ -164,24 +175,7 @@ fn queries<'a>() -> Queries<
         ),
         post_tags,
       ))
-  };
-
-  let read = move |mut conn: DbConn<'a>,
-                   (post_id, my_local_user, is_mod_or_admin): (
-    PostId,
-    Option<&'a LocalUser>,
-    bool,
-  )| async move {
-    // The left join below will return None in this case
-    let my_person_id = my_local_user.person_id();
-    let person_id_join = my_person_id.unwrap_or(PersonId(-1));
-
-    let mut query = all_joins(
-      post_aggregates::table
-        .filter(post_aggregates::post_id.eq(post_id))
-        .into_boxed(),
-      my_person_id,
-    );
+      .into_boxed();
 
     // Hide deleted and removed for non-admins or mods
     if !is_mod_or_admin {
@@ -189,23 +183,23 @@ fn queries<'a>() -> Queries<
         .filter(
           community::removed
             .eq(false)
-            .or(post::creator_id.eq(person_id_join)),
+            .or(post::creator_id.nullable().eq(my_person_id)),
         )
         .filter(
           post::removed
             .eq(false)
-            .or(post::creator_id.eq(person_id_join)),
+            .or(post::creator_id.nullable().eq(my_person_id)),
         )
         // users can see their own deleted posts
         .filter(
           community::deleted
             .eq(false)
-            .or(post::creator_id.eq(person_id_join)),
+            .or(post::creator_id.nullable().eq(my_person_id)),
         )
         .filter(
           post::deleted
             .eq(false)
-            .or(post::creator_id.eq(person_id_join)),
+            .or(post::creator_id.nullable().eq(my_person_id)),
         )
         // private communities can only by browsed by accepted followers
         .filter(
@@ -219,18 +213,216 @@ fn queries<'a>() -> Queries<
 
     Commented::new(query)
       .text("PostView::read")
-      .first(&mut conn)
+      .first(conn)
       .await
-  };
-
-  let list = move |mut conn: DbConn<'a>, (o, site): (PostQuery<'a>, &'a Site)| async move {
-    // The left join below will return None in this case
-    let local_user_id_join = o.local_user.local_user_id().unwrap_or(LocalUserId(-1));
+  }
+}
 
-    let mut query = all_joins(
-      post_aggregates::table.into_boxed(),
-      o.local_user.person_id(),
+impl PaginationCursor {
+  // get cursor for page that starts immediately after the given post
+  pub fn after_post(view: &PostView) -> PaginationCursor {
+    // hex encoding to prevent ossification
+    PaginationCursor(format!("P{:x}", view.counts.post_id.0))
+  }
+  pub async fn read(
+    &self,
+    pool: &mut DbPool<'_>,
+    local_user: Option<&LocalUser>,
+  ) -> Result<PaginationCursorData, Error> {
+    let err_msg = || Error::QueryBuilderError("Could not parse pagination token".into());
+    let post_id = PostId(
+      self
+        .0
+        .get(1..)
+        .and_then(|e| i32::from_str_radix(e, 16).ok())
+        .ok_or_else(err_msg)?,
     );
+    let post_aggregates = PostAggregates::read(pool, post_id).await?;
+    let post_actions = PostActionsCursor::read(pool, post_id, local_user.person_id()).await?;
+
+    Ok(PaginationCursorData {
+      post_aggregates,
+      post_actions,
+    })
+  }
+}
+
+// currently we use aggregates or actions as the pagination token.
+// we only use some of the properties, depending on which sort type we page by
+#[derive(Clone)]
+pub struct PaginationCursorData {
+  post_aggregates: PostAggregates,
+  post_actions: PostActionsCursor,
+}
+
+#[derive(Clone, Default)]
+pub struct PostQuery<'a> {
+  pub listing_type: Option<ListingType>,
+  pub sort: Option<PostSortType>,
+  pub creator_id: Option<PersonId>,
+  pub community_id: Option<CommunityId>,
+  // if true, the query should be handled as if community_id was not given except adding the
+  // literal filter
+  pub community_id_just_for_prefetch: bool,
+  pub local_user: Option<&'a LocalUser>,
+  pub search_term: Option<String>,
+  pub url_only: Option<bool>,
+  pub read_only: Option<bool>,
+  pub liked_only: Option<bool>,
+  pub disliked_only: Option<bool>,
+  pub title_only: Option<bool>,
+  pub page: Option<i64>,
+  pub limit: Option<i64>,
+  pub page_after: Option<PaginationCursorData>,
+  pub page_before_or_equal: Option<PostAggregates>,
+  pub page_back: Option<bool>,
+  pub show_hidden: Option<bool>,
+  pub show_read: Option<bool>,
+  pub show_nsfw: Option<bool>,
+  pub hide_media: Option<bool>,
+  pub no_comments_only: Option<bool>,
+}
+
+impl<'a> PostQuery<'a> {
+  #[allow(clippy::expect_used)]
+  async fn prefetch_upper_bound_for_page_before(
+    &self,
+    site: &Site,
+    pool: &mut DbPool<'_>,
+  ) -> Result<Option<PostQuery<'a>>, Error> {
+    // first get one page for the most popular community to get an upper bound for the page end for
+    // the real query. the reason this is needed is that when fetching posts for a single
+    // community PostgreSQL can optimize the query to use an index on e.g. (=, >=, >=, >=) and
+    // fetch only LIMIT rows but for the followed-communities query it has to query the index on
+    // (IN, >=, >=, >=) which it currently can't do at all (as of PG 16). see the discussion
+    // here: https://github.com/LemmyNet/lemmy/issues/2877#issuecomment-1673597190
+    //
+    // the results are correct no matter which community we fetch these for, since it basically
+    // covers the "worst case" of the whole page consisting of posts from one community
+    // but using the largest community decreases the pagination-frame so make the real query more
+    // efficient.
+    use lemmy_db_schema::schema::community_aggregates::dsl::{
+      community_aggregates,
+      community_id,
+      users_active_month,
+    };
+    let (limit, offset) = limit_and_offset(self.page, self.limit)?;
+    if offset != 0 && self.page_after.is_some() {
+      return Err(Error::QueryBuilderError(
+        "legacy pagination cannot be combined with v2 pagination".into(),
+      ));
+    }
+    let self_person_id = self.local_user.expect("part of the above if").person_id;
+    let largest_subscribed = {
+      let conn = &mut get_conn(pool).await?;
+      community_actions::table
+        .filter(community_actions::followed.is_not_null())
+        .filter(community_actions::person_id.eq(self_person_id))
+        .inner_join(community_aggregates.on(community_id.eq(community_actions::community_id)))
+        .order_by(users_active_month.desc())
+        .select(community_id)
+        .limit(1)
+        .get_result::<CommunityId>(conn)
+        .await
+        .optional()?
+    };
+    let Some(largest_subscribed) = largest_subscribed else {
+      // nothing subscribed to? no posts
+      return Ok(None);
+    };
+
+    let mut v = Box::pin(
+      PostQuery {
+        community_id: Some(largest_subscribed),
+        community_id_just_for_prefetch: true,
+        ..self.clone()
+      }
+      .list(site, pool),
+    )
+    .await?;
+
+    // take last element of array. if this query returned less than LIMIT elements,
+    // the heuristic is invalid since we can't guarantee the full query will return >= LIMIT results
+    // (return original query)
+    if (v.len() as i64) < limit {
+      Ok(Some(self.clone()))
+    } else {
+      let item = if self.page_back.unwrap_or_default() {
+        // for backward pagination, get first element instead
+        v.into_iter().next()
+      } else {
+        v.pop()
+      };
+      let limit_cursor = Some(item.expect("else case").counts);
+      Ok(Some(PostQuery {
+        page_before_or_equal: limit_cursor,
+        ..self.clone()
+      }))
+    }
+  }
+
+  pub async fn list(self, site: &Site, pool: &mut DbPool<'_>) -> Result<Vec<PostView>, Error> {
+    let o = if self.listing_type == Some(ListingType::Subscribed)
+      && self.community_id.is_none()
+      && self.local_user.is_some()
+      && self.page_before_or_equal.is_none()
+    {
+      if let Some(query) = self
+        .prefetch_upper_bound_for_page_before(site, pool)
+        .await?
+      {
+        query
+      } else {
+        self
+      }
+    } else {
+      self
+    };
+
+    let conn = &mut get_conn(pool).await?;
+
+    let my_person_id = o.local_user.person_id();
+    let my_local_user_id = o.local_user.local_user_id();
+
+    let post_tags = post_tag::table
+      .inner_join(tag::table)
+      .select(diesel::dsl::sql::<diesel::sql_types::Json>(
+        "json_agg(tag.*)",
+      ))
+      .filter(post_tag::post_id.eq(post_aggregates::post_id))
+      .filter(tag::deleted.eq(false))
+      .single_value();
+
+    let mut query = PostView::joins(my_person_id)
+      .select((
+        post::all_columns,
+        person::all_columns,
+        community::all_columns,
+        image_details::all_columns.nullable(),
+        creator_community_actions
+          .field(community_actions::received_ban)
+          .nullable()
+          .is_not_null(),
+        community_actions::received_ban.nullable().is_not_null(),
+        creator_community_actions
+          .field(community_actions::became_moderator)
+          .nullable()
+          .is_not_null(),
+        PostView::creator_is_admin(),
+        post_aggregates::all_columns,
+        CommunityFollower::select_subscribed_type(),
+        post_actions::saved.nullable().is_not_null(),
+        post_actions::read.nullable().is_not_null(),
+        post_actions::hidden.nullable().is_not_null(),
+        person_actions::blocked.nullable().is_not_null(),
+        post_actions::like_score.nullable(),
+        coalesce(
+          post_aggregates::comments.nullable() - post_actions::read_comments_amount.nullable(),
+          post_aggregates::comments,
+        ),
+        post_tags,
+      ))
+      .into_boxed();
 
     // hide posts from deleted communities
     query = query.filter(community::deleted.eq(false));
@@ -360,9 +552,11 @@ fn queries<'a>() -> Queries<
       if o.local_user.is_some() {
         query = query.filter(exists(
           local_user_language::table.filter(
-            post::language_id
-              .eq(local_user_language::language_id)
-              .and(local_user_language::local_user_id.eq(local_user_id_join)),
+            post::language_id.eq(local_user_language::language_id).and(
+              local_user_language::local_user_id
+                .nullable()
+                .eq(my_local_user_id),
+            ),
           ),
         ));
       }
@@ -448,188 +642,8 @@ fn queries<'a>() -> Queries<
         "getting upper bound for next query",
         o.community_id_just_for_prefetch,
       )
-      .load::<PostView>(&mut conn)
+      .load::<PostView>(conn)
       .await
-  };
-
-  Queries::new(read, list)
-}
-
-impl PostView {
-  pub async fn read(
-    pool: &mut DbPool<'_>,
-    post_id: PostId,
-    my_local_user: Option<&'_ LocalUser>,
-    is_mod_or_admin: bool,
-  ) -> Result<Self, Error> {
-    queries()
-      .read(pool, (post_id, my_local_user, is_mod_or_admin))
-      .await
-  }
-}
-
-impl PaginationCursor {
-  // get cursor for page that starts immediately after the given post
-  pub fn after_post(view: &PostView) -> PaginationCursor {
-    // hex encoding to prevent ossification
-    PaginationCursor(format!("P{:x}", view.counts.post_id.0))
-  }
-  pub async fn read(
-    &self,
-    pool: &mut DbPool<'_>,
-    local_user: Option<&LocalUser>,
-  ) -> Result<PaginationCursorData, Error> {
-    let err_msg = || Error::QueryBuilderError("Could not parse pagination token".into());
-    let post_id = PostId(
-      self
-        .0
-        .get(1..)
-        .and_then(|e| i32::from_str_radix(e, 16).ok())
-        .ok_or_else(err_msg)?,
-    );
-    let post_aggregates = PostAggregates::read(pool, post_id).await?;
-    let post_actions = PostActionsCursor::read(pool, post_id, local_user.person_id()).await?;
-
-    Ok(PaginationCursorData {
-      post_aggregates,
-      post_actions,
-    })
-  }
-}
-
-// currently we use aggregates or actions as the pagination token.
-// we only use some of the properties, depending on which sort type we page by
-#[derive(Clone)]
-pub struct PaginationCursorData {
-  post_aggregates: PostAggregates,
-  post_actions: PostActionsCursor,
-}
-
-#[derive(Clone, Default)]
-pub struct PostQuery<'a> {
-  pub listing_type: Option<ListingType>,
-  pub sort: Option<PostSortType>,
-  pub creator_id: Option<PersonId>,
-  pub community_id: Option<CommunityId>,
-  // if true, the query should be handled as if community_id was not given except adding the
-  // literal filter
-  pub community_id_just_for_prefetch: bool,
-  pub local_user: Option<&'a LocalUser>,
-  pub search_term: Option<String>,
-  pub url_only: Option<bool>,
-  pub read_only: Option<bool>,
-  pub liked_only: Option<bool>,
-  pub disliked_only: Option<bool>,
-  pub title_only: Option<bool>,
-  pub page: Option<i64>,
-  pub limit: Option<i64>,
-  pub page_after: Option<PaginationCursorData>,
-  pub page_before_or_equal: Option<PostAggregates>,
-  pub page_back: Option<bool>,
-  pub show_hidden: Option<bool>,
-  pub show_read: Option<bool>,
-  pub show_nsfw: Option<bool>,
-  pub hide_media: Option<bool>,
-  pub no_comments_only: Option<bool>,
-}
-
-impl<'a> PostQuery<'a> {
-  #[allow(clippy::expect_used)]
-  async fn prefetch_upper_bound_for_page_before(
-    &self,
-    site: &Site,
-    pool: &mut DbPool<'_>,
-  ) -> Result<Option<PostQuery<'a>>, Error> {
-    // first get one page for the most popular community to get an upper bound for the page end for
-    // the real query. the reason this is needed is that when fetching posts for a single
-    // community PostgreSQL can optimize the query to use an index on e.g. (=, >=, >=, >=) and
-    // fetch only LIMIT rows but for the followed-communities query it has to query the index on
-    // (IN, >=, >=, >=) which it currently can't do at all (as of PG 16). see the discussion
-    // here: https://github.com/LemmyNet/lemmy/issues/2877#issuecomment-1673597190
-    //
-    // the results are correct no matter which community we fetch these for, since it basically
-    // covers the "worst case" of the whole page consisting of posts from one community
-    // but using the largest community decreases the pagination-frame so make the real query more
-    // efficient.
-    use lemmy_db_schema::schema::community_aggregates::dsl::{
-      community_aggregates,
-      community_id,
-      users_active_month,
-    };
-    let (limit, offset) = limit_and_offset(self.page, self.limit)?;
-    if offset != 0 && self.page_after.is_some() {
-      return Err(Error::QueryBuilderError(
-        "legacy pagination cannot be combined with v2 pagination".into(),
-      ));
-    }
-    let self_person_id = self.local_user.expect("part of the above if").person_id;
-    let largest_subscribed = {
-      let conn = &mut get_conn(pool).await?;
-      action_query(community_actions::followed)
-        .filter(community_actions::person_id.eq(self_person_id))
-        .inner_join(community_aggregates.on(community_id.eq(community_actions::community_id)))
-        .order_by(users_active_month.desc())
-        .select(community_id)
-        .limit(1)
-        .get_result::<CommunityId>(conn)
-        .await
-        .optional()?
-    };
-    let Some(largest_subscribed) = largest_subscribed else {
-      // nothing subscribed to? no posts
-      return Ok(None);
-    };
-
-    let mut v = queries()
-      .list(
-        pool,
-        (
-          PostQuery {
-            community_id: Some(largest_subscribed),
-            community_id_just_for_prefetch: true,
-            ..self.clone()
-          },
-          site,
-        ),
-      )
-      .await?;
-    // take last element of array. if this query returned less than LIMIT elements,
-    // the heuristic is invalid since we can't guarantee the full query will return >= LIMIT results
-    // (return original query)
-    if (v.len() as i64) < limit {
-      Ok(Some(self.clone()))
-    } else {
-      let item = if self.page_back.unwrap_or_default() {
-        // for backward pagination, get first element instead
-        v.into_iter().next()
-      } else {
-        v.pop()
-      };
-      let limit_cursor = Some(item.expect("else case").counts);
-      Ok(Some(PostQuery {
-        page_before_or_equal: limit_cursor,
-        ..self.clone()
-      }))
-    }
-  }
-
-  pub async fn list(self, site: &Site, pool: &mut DbPool<'_>) -> Result<Vec<PostView>, Error> {
-    if self.listing_type == Some(ListingType::Subscribed)
-      && self.community_id.is_none()
-      && self.local_user.is_some()
-      && self.page_before_or_equal.is_none()
-    {
-      if let Some(query) = self
-        .prefetch_upper_bound_for_page_before(site, pool)
-        .await?
-      {
-        queries().list(pool, (query, site)).await
-      } else {
-        Ok(vec![])
-      }
-    } else {
-      queries().list(pool, (self, site)).await
-    }
   }
 }
 
diff --git a/crates/db_views/src/private_message/private_message_view.rs b/crates/db_views/src/private_message/private_message_view.rs
index 2345e7466a..799fdde827 100644
--- a/crates/db_views/src/private_message/private_message_view.rs
+++ b/crates/db_views/src/private_message/private_message_view.rs
@@ -1,41 +1,56 @@
 use crate::structs::PrivateMessageView;
-use diesel::{result::Error, ExpressionMethods, JoinOnDsl, QueryDsl};
+use diesel::{
+  result::Error,
+  BoolExpressionMethods,
+  ExpressionMethods,
+  JoinOnDsl,
+  QueryDsl,
+  SelectableHelper,
+};
 use diesel_async::RunQueryDsl;
 use lemmy_db_schema::{
   aliases,
   newtypes::PrivateMessageId,
   schema::{instance_actions, person, person_actions, private_message},
-  utils::{actions, get_conn, DbPool},
+  utils::{get_conn, DbPool},
 };
 
 impl PrivateMessageView {
+  #[diesel::dsl::auto_type(no_type_alias)]
+  fn joins() -> _ {
+    let recipient_id = aliases::person1.field(person::id);
+
+    let person_actions_join = person_actions::table.on(
+      person_actions::target_id
+        .eq(private_message::creator_id)
+        .and(person_actions::person_id.eq(recipient_id)),
+    );
+
+    let instance_actions_join = instance_actions::table.on(
+      instance_actions::instance_id
+        .eq(person::instance_id)
+        .and(instance_actions::person_id.eq(recipient_id)),
+    );
+
+    let creator_join = person::table.on(private_message::creator_id.eq(person::id));
+
+    let recipient_join = aliases::person1.on(private_message::recipient_id.eq(recipient_id));
+
+    private_message::table
+      .inner_join(creator_join)
+      .inner_join(recipient_join)
+      .left_join(person_actions_join)
+      .left_join(instance_actions_join)
+  }
+
   pub async fn read(
     pool: &mut DbPool<'_>,
     private_message_id: PrivateMessageId,
   ) -> Result<Self, Error> {
     let conn = &mut get_conn(pool).await?;
-
-    private_message::table
-      .find(private_message_id)
-      .inner_join(person::table.on(private_message::creator_id.eq(person::id)))
-      .inner_join(
-        aliases::person1.on(private_message::recipient_id.eq(aliases::person1.field(person::id))),
-      )
-      .left_join(actions(
-        person_actions::table,
-        Some(aliases::person1.field(person::id)),
-        private_message::creator_id,
-      ))
-      .left_join(actions(
-        instance_actions::table,
-        Some(aliases::person1.field(person::id)),
-        person::instance_id,
-      ))
-      .select((
-        private_message::all_columns,
-        person::all_columns,
-        aliases::person1.fields(person::all_columns),
-      ))
+    Self::joins()
+      .filter(private_message::id.eq(private_message_id))
+      .select(Self::as_select())
       .first(conn)
       .await
   }
diff --git a/crates/db_views/src/registration_applications/registration_application_view.rs b/crates/db_views/src/registration_applications/registration_application_view.rs
index cdb6af61dc..f10b085c4f 100644
--- a/crates/db_views/src/registration_applications/registration_application_view.rs
+++ b/crates/db_views/src/registration_applications/registration_application_view.rs
@@ -1,119 +1,69 @@
 use crate::structs::RegistrationApplicationView;
 use diesel::{
   dsl::count,
-  pg::Pg,
   result::Error,
   ExpressionMethods,
   JoinOnDsl,
   NullableExpressionMethods,
   QueryDsl,
+  SelectableHelper,
 };
 use diesel_async::RunQueryDsl;
 use lemmy_db_schema::{
   aliases,
   newtypes::{PersonId, RegistrationApplicationId},
   schema::{local_user, person, registration_application},
-  utils::{get_conn, limit_and_offset, DbConn, DbPool, ListFn, Queries, ReadFn},
+  source::registration_application::RegistrationApplication,
+  utils::{get_conn, limit_and_offset, DbPool},
 };
 
-enum ReadBy {
-  Id(RegistrationApplicationId),
-  Person(PersonId),
-}
-
-fn queries<'a>() -> Queries<
-  impl ReadFn<'a, RegistrationApplicationView, ReadBy>,
-  impl ListFn<'a, RegistrationApplicationView, RegistrationApplicationQuery>,
-> {
-  let all_joins = |query: registration_application::BoxedQuery<'a, Pg>| {
-    query
+impl RegistrationApplicationView {
+  #[diesel::dsl::auto_type(no_type_alias)]
+  fn joins() -> _ {
+    registration_application::table
       .inner_join(local_user::table.on(registration_application::local_user_id.eq(local_user::id)))
       .inner_join(person::table.on(local_user::person_id.eq(person::id)))
       .left_join(
         aliases::person1
           .on(registration_application::admin_id.eq(aliases::person1.field(person::id).nullable())),
       )
-      .order_by(registration_application::published.desc())
-      .select((
-        registration_application::all_columns,
-        local_user::all_columns,
-        person::all_columns,
-        aliases::person1.fields(person::all_columns).nullable(),
-      ))
-  };
-
-  let read = move |mut conn: DbConn<'a>, search: ReadBy| async move {
-    let mut query = all_joins(registration_application::table.into_boxed());
-
-    query = match search {
-      ReadBy::Id(id) => query.filter(registration_application::id.eq(id)),
-      ReadBy::Person(person_id) => query.filter(person::id.eq(person_id)),
-    };
-
-    query.first(&mut conn).await
-  };
-
-  let list = move |mut conn: DbConn<'a>, o: RegistrationApplicationQuery| async move {
-    let mut query = all_joins(registration_application::table.into_boxed());
-
-    // If viewing all applications, order by newest, but if viewing unresolved only, show the oldest
-    // first (FIFO)
-    if o.unread_only {
-      query = query
-        .filter(registration_application::admin_id.is_null())
-        .order_by(registration_application::published.asc());
-    } else {
-      query = query.order_by(registration_application::published.desc());
-    }
-
-    if o.verified_email_only {
-      query = query.filter(local_user::email_verified.eq(true))
-    }
-
-    let (limit, offset) = limit_and_offset(o.page, o.limit)?;
-
-    query = query.limit(limit).offset(offset);
-
-    query.load::<RegistrationApplicationView>(&mut conn).await
-  };
-
-  Queries::new(read, list)
-}
+  }
 
-impl RegistrationApplicationView {
   pub async fn read(pool: &mut DbPool<'_>, id: RegistrationApplicationId) -> Result<Self, Error> {
-    queries().read(pool, ReadBy::Id(id)).await
+    let conn = &mut get_conn(pool).await?;
+    Self::joins()
+      .filter(registration_application::id.eq(id))
+      .select(Self::as_select())
+      .first(conn)
+      .await
   }
 
   pub async fn read_by_person(pool: &mut DbPool<'_>, person_id: PersonId) -> Result<Self, Error> {
-    queries().read(pool, ReadBy::Person(person_id)).await
+    let conn = &mut get_conn(pool).await?;
+    Self::joins()
+      .filter(person::id.eq(person_id))
+      .select(Self::as_select())
+      .first(conn)
+      .await
   }
+
   /// Returns the current unread registration_application count
   pub async fn get_unread_count(
     pool: &mut DbPool<'_>,
     verified_email_only: bool,
   ) -> Result<i64, Error> {
     let conn = &mut get_conn(pool).await?;
-    let person_alias_1 = diesel::alias!(person as person1);
 
-    let mut query = registration_application::table
-      .inner_join(local_user::table.on(registration_application::local_user_id.eq(local_user::id)))
-      .inner_join(person::table.on(local_user::person_id.eq(person::id)))
-      .left_join(
-        person_alias_1
-          .on(registration_application::admin_id.eq(person_alias_1.field(person::id).nullable())),
-      )
-      .filter(registration_application::admin_id.is_null())
+    let mut query = Self::joins()
+      .filter(RegistrationApplication::is_unread())
+      .select(count(registration_application::id))
       .into_boxed();
 
     if verified_email_only {
       query = query.filter(local_user::email_verified.eq(true))
     }
 
-    query
-      .select(count(registration_application::id))
-      .first::<i64>(conn)
-      .await
+    query.first::<i64>(conn).await
   }
 }
 
@@ -130,7 +80,32 @@ impl RegistrationApplicationQuery {
     self,
     pool: &mut DbPool<'_>,
   ) -> Result<Vec<RegistrationApplicationView>, Error> {
-    queries().list(pool, self).await
+    let conn = &mut get_conn(pool).await?;
+    let o = self;
+
+    let mut query = RegistrationApplicationView::joins()
+      .select(RegistrationApplicationView::as_select())
+      .into_boxed();
+
+    if o.unread_only {
+      query = query
+        .filter(RegistrationApplication::is_unread())
+        .order_by(registration_application::published.asc());
+    } else {
+      query = query.order_by(registration_application::published.desc());
+    }
+
+    if o.verified_email_only {
+      query = query.filter(local_user::email_verified.eq(true))
+    }
+
+    let (limit, offset) = limit_and_offset(o.page, o.limit)?;
+
+    query
+      .limit(limit)
+      .offset(offset)
+      .load::<RegistrationApplicationView>(conn)
+      .await
   }
 }
 
diff --git a/crates/db_views/src/reports/comment_report_view.rs b/crates/db_views/src/reports/comment_report_view.rs
index 6154b9b565..a2fa22a26f 100644
--- a/crates/db_views/src/reports/comment_report_view.rs
+++ b/crates/db_views/src/reports/comment_report_view.rs
@@ -25,10 +25,78 @@ use lemmy_db_schema::{
     post,
   },
   source::community::CommunityFollower,
-  utils::{actions, actions_alias, functions::coalesce, get_conn, DbPool},
+  utils::{functions::coalesce, get_conn, DbPool},
 };
 
 impl CommentReportView {
+  #[diesel::dsl::auto_type(no_type_alias)]
+  fn joins(my_person_id: PersonId) -> _ {
+    let recipient_id = aliases::person1.field(person::id);
+    let resolver_id = aliases::person2.field(person::id);
+
+    let post_join = post::table.on(comment::post_id.eq(post::id));
+
+    let community_join = community::table.on(post::community_id.eq(community::id));
+
+    let report_creator_join = person::table.on(comment_report::creator_id.eq(person::id));
+
+    let local_user_join = local_user::table.on(
+      comment::creator_id
+        .eq(local_user::person_id)
+        .and(local_user::admin.eq(true)),
+    );
+
+    let comment_creator_join = aliases::person1.on(comment::creator_id.eq(recipient_id));
+
+    let comment_aggregates_join =
+      comment_aggregates::table.on(comment_report::comment_id.eq(comment_aggregates::comment_id));
+
+    let comment_actions_join = comment_actions::table.on(
+      comment_actions::comment_id
+        .eq(comment_report::comment_id)
+        .and(comment_actions::person_id.eq(my_person_id)),
+    );
+
+    let resolver_join = aliases::person2.on(comment_report::resolver_id.eq(resolver_id.nullable()));
+
+    let creator_community_actions_join = creator_community_actions.on(
+      creator_community_actions
+        .field(community_actions::community_id)
+        .eq(post::community_id)
+        .and(
+          creator_community_actions
+            .field(community_actions::person_id)
+            .eq(comment::creator_id),
+        ),
+    );
+
+    let person_actions_join = person_actions::table.on(
+      person_actions::target_id
+        .eq(comment::creator_id)
+        .and(person_actions::person_id.eq(my_person_id)),
+    );
+
+    let community_actions_join = community_actions::table.on(
+      community_actions::community_id
+        .eq(post::community_id)
+        .and(community_actions::person_id.eq(my_person_id)),
+    );
+
+    comment_report::table
+      .inner_join(comment::table)
+      .inner_join(post_join)
+      .inner_join(community_join)
+      .inner_join(report_creator_join)
+      .inner_join(comment_creator_join)
+      .inner_join(comment_aggregates_join)
+      .left_join(comment_actions_join)
+      .left_join(resolver_join)
+      .left_join(creator_community_actions_join)
+      .left_join(local_user_join)
+      .left_join(person_actions_join)
+      .left_join(community_actions_join)
+  }
+
   /// returns the CommentReportView for the provided report_id
   ///
   /// * `report_id` - the report id to obtain
@@ -38,47 +106,8 @@ impl CommentReportView {
     my_person_id: PersonId,
   ) -> Result<Self, Error> {
     let conn = &mut get_conn(pool).await?;
-    comment_report::table
-      .find(report_id)
-      .inner_join(comment::table)
-      .inner_join(post::table.on(comment::post_id.eq(post::id)))
-      .inner_join(community::table.on(post::community_id.eq(community::id)))
-      .inner_join(person::table.on(comment_report::creator_id.eq(person::id)))
-      .inner_join(aliases::person1.on(comment::creator_id.eq(aliases::person1.field(person::id))))
-      .inner_join(
-        comment_aggregates::table.on(comment_report::comment_id.eq(comment_aggregates::comment_id)),
-      )
-      .left_join(actions(
-        comment_actions::table,
-        Some(my_person_id),
-        comment_report::comment_id,
-      ))
-      .left_join(
-        aliases::person2
-          .on(comment_report::resolver_id.eq(aliases::person2.field(person::id).nullable())),
-      )
-      .left_join(actions_alias(
-        creator_community_actions,
-        comment::creator_id,
-        post::community_id,
-      ))
-      .left_join(
-        local_user::table.on(
-          comment::creator_id
-            .eq(local_user::person_id)
-            .and(local_user::admin.eq(true)),
-        ),
-      )
-      .left_join(actions(
-        person_actions::table,
-        Some(my_person_id),
-        comment::creator_id,
-      ))
-      .left_join(actions(
-        community_actions::table,
-        Some(my_person_id),
-        post::community_id,
-      ))
+    Self::joins(my_person_id)
+      .filter(comment_report::id.eq(report_id))
       .select((
         comment_report::all_columns,
         comment::all_columns,
diff --git a/crates/db_views/src/reports/post_report_view.rs b/crates/db_views/src/reports/post_report_view.rs
index 4c7fd676c4..100703f061 100644
--- a/crates/db_views/src/reports/post_report_view.rs
+++ b/crates/db_views/src/reports/post_report_view.rs
@@ -23,10 +23,75 @@ use lemmy_db_schema::{
     post_report,
   },
   source::community::CommunityFollower,
-  utils::{actions, actions_alias, functions::coalesce, get_conn, DbPool},
+  utils::{functions::coalesce, get_conn, DbPool},
 };
 
 impl PostReportView {
+  #[diesel::dsl::auto_type(no_type_alias)]
+  fn joins(my_person_id: PersonId) -> _ {
+    let recipient_id = aliases::person1.field(person::id);
+    let resolver_id = aliases::person2.field(person::id);
+
+    let community_join = community::table.on(post::community_id.eq(community::id));
+
+    let report_creator_join = person::table.on(post_report::creator_id.eq(person::id));
+
+    let post_creator_join = aliases::person1.on(post::creator_id.eq(recipient_id));
+
+    let creator_community_actions_join = creator_community_actions.on(
+      creator_community_actions
+        .field(community_actions::community_id)
+        .eq(post::community_id)
+        .and(
+          creator_community_actions
+            .field(community_actions::person_id)
+            .eq(post::creator_id),
+        ),
+    );
+
+    let community_actions_join = community_actions::table.on(
+      community_actions::community_id
+        .eq(post::community_id)
+        .and(community_actions::person_id.eq(my_person_id)),
+    );
+
+    let local_user_join = local_user::table.on(
+      post::creator_id
+        .eq(local_user::person_id)
+        .and(local_user::admin.eq(true)),
+    );
+
+    let post_actions_join = post_actions::table.on(
+      post_actions::post_id
+        .eq(post::id)
+        .and(post_actions::person_id.eq(my_person_id)),
+    );
+
+    let person_actions_join = person_actions::table.on(
+      person_actions::target_id
+        .eq(post::creator_id)
+        .and(person_actions::person_id.eq(my_person_id)),
+    );
+
+    let post_aggregates_join =
+      post_aggregates::table.on(post_report::post_id.eq(post_aggregates::post_id));
+
+    let resolver_join = aliases::person2.on(post_report::resolver_id.eq(resolver_id.nullable()));
+
+    post_report::table
+      .inner_join(post::table)
+      .inner_join(community_join)
+      .inner_join(report_creator_join)
+      .inner_join(post_creator_join)
+      .left_join(creator_community_actions_join)
+      .left_join(community_actions_join)
+      .left_join(local_user_join)
+      .left_join(post_actions_join)
+      .left_join(person_actions_join)
+      .inner_join(post_aggregates_join)
+      .left_join(resolver_join)
+  }
+
   /// returns the PostReportView for the provided report_id
   ///
   /// * `report_id` - the report id to obtain
@@ -37,40 +102,8 @@ impl PostReportView {
   ) -> Result<Self, Error> {
     let conn = &mut get_conn(pool).await?;
 
-    post_report::table
-      .find(report_id)
-      .inner_join(post::table)
-      .inner_join(community::table.on(post::community_id.eq(community::id)))
-      .inner_join(person::table.on(post_report::creator_id.eq(person::id)))
-      .inner_join(aliases::person1.on(post::creator_id.eq(aliases::person1.field(person::id))))
-      .left_join(actions_alias(
-        creator_community_actions,
-        post::creator_id,
-        post::community_id,
-      ))
-      .left_join(actions(
-        community_actions::table,
-        Some(my_person_id),
-        post::community_id,
-      ))
-      .left_join(
-        local_user::table.on(
-          post::creator_id
-            .eq(local_user::person_id)
-            .and(local_user::admin.eq(true)),
-        ),
-      )
-      .left_join(actions(post_actions::table, Some(my_person_id), post::id))
-      .left_join(actions(
-        person_actions::table,
-        Some(my_person_id),
-        post::creator_id,
-      ))
-      .inner_join(post_aggregates::table.on(post_report::post_id.eq(post_aggregates::post_id)))
-      .left_join(
-        aliases::person2
-          .on(post_report::resolver_id.eq(aliases::person2.field(person::id).nullable())),
-      )
+    Self::joins(my_person_id)
+      .filter(post_report::id.eq(report_id))
       .select((
         post_report::all_columns,
         post::all_columns,
diff --git a/crates/db_views/src/site/custom_emoji_view.rs b/crates/db_views/src/site/custom_emoji_view.rs
index 606e807e9b..a47e062759 100644
--- a/crates/db_views/src/site/custom_emoji_view.rs
+++ b/crates/db_views/src/site/custom_emoji_view.rs
@@ -1,5 +1,12 @@
 use crate::structs::CustomEmojiView;
-use diesel::{result::Error, ExpressionMethods, JoinOnDsl, NullableExpressionMethods, QueryDsl};
+use diesel::{
+  dsl::Nullable,
+  result::Error,
+  ExpressionMethods,
+  JoinOnDsl,
+  NullableExpressionMethods,
+  QueryDsl,
+};
 use diesel_async::RunQueryDsl;
 use lemmy_db_schema::{
   newtypes::CustomEmojiId,
@@ -9,20 +16,33 @@ use lemmy_db_schema::{
 };
 use std::collections::HashMap;
 
+type SelectionType = (
+  <custom_emoji::table as diesel::Table>::AllColumns,
+  Nullable<<custom_emoji_keyword::table as diesel::Table>::AllColumns>,
+);
+
+fn selection() -> SelectionType {
+  (
+    custom_emoji::all_columns,
+    custom_emoji_keyword::all_columns.nullable(), // (or all the columns if you want)
+  )
+}
 type CustomEmojiTuple = (CustomEmoji, Option<CustomEmojiKeyword>);
 
+// TODO this type is a mess, it should not be using vectors in a view.
 impl CustomEmojiView {
+  #[diesel::dsl::auto_type(no_type_alias)]
+  fn joins() -> _ {
+    custom_emoji::table.left_join(
+      custom_emoji_keyword::table.on(custom_emoji_keyword::custom_emoji_id.eq(custom_emoji::id)),
+    )
+  }
+
   pub async fn get(pool: &mut DbPool<'_>, emoji_id: CustomEmojiId) -> Result<Self, Error> {
     let conn = &mut get_conn(pool).await?;
-    let emojis = custom_emoji::table
-      .find(emoji_id)
-      .left_join(
-        custom_emoji_keyword::table.on(custom_emoji_keyword::custom_emoji_id.eq(custom_emoji::id)),
-      )
-      .select((
-        custom_emoji::all_columns,
-        custom_emoji_keyword::all_columns.nullable(), // (or all the columns if you want)
-      ))
+    let emojis = Self::joins()
+      .filter(custom_emoji::id.eq(emoji_id))
+      .select(selection())
       .load::<CustomEmojiTuple>(conn)
       .await?;
     if let Some(emoji) = CustomEmojiView::from_tuple_to_vec(emojis)
@@ -44,12 +64,7 @@ impl CustomEmojiView {
   ) -> Result<Vec<Self>, Error> {
     let conn = &mut get_conn(pool).await?;
 
-    let mut query = custom_emoji::table
-      .left_join(
-        custom_emoji_keyword::table.on(custom_emoji_keyword::custom_emoji_id.eq(custom_emoji::id)),
-      )
-      .order(custom_emoji::category)
-      .into_boxed();
+    let mut query = Self::joins().into_boxed();
 
     if !ignore_page_limits {
       let (limit, offset) = limit_and_offset(page, limit)?;
@@ -60,13 +75,10 @@ impl CustomEmojiView {
       query = query.filter(custom_emoji::category.eq(category))
     }
 
-    query = query.then_order_by(custom_emoji::id);
-
     let emojis = query
-      .select((
-        custom_emoji::all_columns,
-        custom_emoji_keyword::all_columns.nullable(), // (or all the columns if you want)
-      ))
+      .select(selection())
+      .order(custom_emoji::category)
+      .then_order_by(custom_emoji::id)
       .load::<CustomEmojiTuple>(conn)
       .await?;
 
diff --git a/crates/db_views/src/site/local_image_view.rs b/crates/db_views/src/site/local_image_view.rs
index 7b5b97095c..3d342528c6 100644
--- a/crates/db_views/src/site/local_image_view.rs
+++ b/crates/db_views/src/site/local_image_view.rs
@@ -1,5 +1,5 @@
 use crate::structs::LocalImageView;
-use diesel::{result::Error, ExpressionMethods, JoinOnDsl, QueryDsl};
+use diesel::{result::Error, ExpressionMethods, JoinOnDsl, QueryDsl, SelectableHelper};
 use diesel_async::RunQueryDsl;
 use lemmy_db_schema::{
   newtypes::LocalUserId,
@@ -8,31 +8,11 @@ use lemmy_db_schema::{
 };
 
 impl LocalImageView {
-  async fn get_all_helper(
-    pool: &mut DbPool<'_>,
-    user_id: Option<LocalUserId>,
-    page: Option<i64>,
-    limit: Option<i64>,
-    ignore_page_limits: bool,
-  ) -> Result<Vec<Self>, Error> {
-    let conn = &mut get_conn(pool).await?;
-    let mut query = local_image::table
+  #[diesel::dsl::auto_type(no_type_alias)]
+  fn joins() -> _ {
+    local_image::table
       .inner_join(local_user::table)
       .inner_join(person::table.on(local_user::person_id.eq(person::id)))
-      .select((local_image::all_columns, person::all_columns))
-      .order_by(local_image::published.desc())
-      .into_boxed();
-
-    if let Some(user_id) = user_id {
-      query = query.filter(local_image::local_user_id.eq(user_id))
-    }
-
-    if !ignore_page_limits {
-      let (limit, offset) = limit_and_offset(page, limit)?;
-      query = query.limit(limit).offset(offset);
-    }
-
-    query.load::<LocalImageView>(conn).await
   }
 
   pub async fn get_all_paged_by_local_user_id(
@@ -41,14 +21,28 @@ impl LocalImageView {
     page: Option<i64>,
     limit: Option<i64>,
   ) -> Result<Vec<Self>, Error> {
-    Self::get_all_helper(pool, Some(user_id), page, limit, false).await
+    let conn = &mut get_conn(pool).await?;
+    let (limit, offset) = limit_and_offset(page, limit)?;
+
+    Self::joins()
+      .filter(local_image::local_user_id.eq(user_id))
+      .select(Self::as_select())
+      .limit(limit)
+      .offset(offset)
+      .load::<Self>(conn)
+      .await
   }
 
   pub async fn get_all_by_local_user_id(
     pool: &mut DbPool<'_>,
     user_id: LocalUserId,
   ) -> Result<Vec<Self>, Error> {
-    Self::get_all_helper(pool, Some(user_id), None, None, true).await
+    let conn = &mut get_conn(pool).await?;
+    Self::joins()
+      .filter(local_image::local_user_id.eq(user_id))
+      .select(Self::as_select())
+      .load::<Self>(conn)
+      .await
   }
 
   pub async fn get_all(
@@ -56,6 +50,13 @@ impl LocalImageView {
     page: Option<i64>,
     limit: Option<i64>,
   ) -> Result<Vec<Self>, Error> {
-    Self::get_all_helper(pool, None, page, limit, false).await
+    let conn = &mut get_conn(pool).await?;
+    let (limit, offset) = limit_and_offset(page, limit)?;
+    Self::joins()
+      .select(Self::as_select())
+      .limit(limit)
+      .offset(offset)
+      .load::<Self>(conn)
+      .await
   }
 }
diff --git a/crates/db_views/src/site/site_view.rs b/crates/db_views/src/site/site_view.rs
index ed9aeb498c..ecf730e883 100644
--- a/crates/db_views/src/site/site_view.rs
+++ b/crates/db_views/src/site/site_view.rs
@@ -1,5 +1,5 @@
 use crate::structs::SiteView;
-use diesel::{ExpressionMethods, JoinOnDsl, OptionalExtension, QueryDsl};
+use diesel::{ExpressionMethods, JoinOnDsl, OptionalExtension, QueryDsl, SelectableHelper};
 use diesel_async::RunQueryDsl;
 use lemmy_db_schema::{
   schema::{local_site, local_site_rate_limit, site, site_aggregates},
@@ -17,12 +17,7 @@ impl SiteView {
           local_site_rate_limit::table.on(local_site::id.eq(local_site_rate_limit::local_site_id)),
         )
         .inner_join(site_aggregates::table)
-        .select((
-          site::all_columns,
-          local_site::all_columns,
-          local_site_rate_limit::all_columns,
-          site_aggregates::all_columns,
-        ))
+        .select(Self::as_select())
         .first(conn)
         .await
         .optional()?
diff --git a/crates/db_views/src/site/vote_view.rs b/crates/db_views/src/site/vote_view.rs
index 827cd3cc96..dac7857539 100644
--- a/crates/db_views/src/site/vote_view.rs
+++ b/crates/db_views/src/site/vote_view.rs
@@ -1,11 +1,18 @@
 use crate::structs::VoteView;
-use diesel::{result::Error, ExpressionMethods, NullableExpressionMethods, QueryDsl};
+use diesel::{
+  result::Error,
+  BoolExpressionMethods,
+  ExpressionMethods,
+  JoinOnDsl,
+  NullableExpressionMethods,
+  QueryDsl,
+};
 use diesel_async::RunQueryDsl;
 use lemmy_db_schema::{
   aliases::creator_community_actions,
   newtypes::{CommentId, PostId},
   schema::{comment, comment_actions, community_actions, person, post, post_actions},
-  utils::{action_query, actions_alias, get_conn, limit_and_offset, DbPool},
+  utils::{get_conn, limit_and_offset, DbPool},
 };
 
 impl VoteView {
@@ -18,14 +25,22 @@ impl VoteView {
     let conn = &mut get_conn(pool).await?;
     let (limit, offset) = limit_and_offset(page, limit)?;
 
-    action_query(post_actions::like_score)
+    let creator_community_actions_join = creator_community_actions.on(
+      creator_community_actions
+        .field(community_actions::community_id)
+        .eq(post::community_id)
+        .and(
+          creator_community_actions
+            .field(community_actions::person_id)
+            .eq(post_actions::person_id),
+        ),
+    );
+
+    post_actions::table
+      .filter(post_actions::like_score.is_not_null())
       .inner_join(person::table)
       .inner_join(post::table)
-      .left_join(actions_alias(
-        creator_community_actions,
-        post_actions::person_id,
-        post::community_id,
-      ))
+      .left_join(creator_community_actions_join)
       .filter(post_actions::post_id.eq(post_id))
       .select((
         person::all_columns,
@@ -51,14 +66,22 @@ impl VoteView {
     let conn = &mut get_conn(pool).await?;
     let (limit, offset) = limit_and_offset(page, limit)?;
 
-    action_query(comment_actions::like_score)
+    let creator_community_actions_join = creator_community_actions.on(
+      creator_community_actions
+        .field(community_actions::community_id)
+        .eq(post::community_id)
+        .and(
+          creator_community_actions
+            .field(community_actions::person_id)
+            .eq(comment_actions::person_id),
+        ),
+    );
+
+    comment_actions::table
+      .filter(comment_actions::like_score.is_not_null())
       .inner_join(person::table)
       .inner_join(comment::table.inner_join(post::table))
-      .left_join(actions_alias(
-        creator_community_actions,
-        comment_actions::person_id,
-        post::community_id,
-      ))
+      .left_join(creator_community_actions_join)
       .filter(comment_actions::comment_id.eq(comment_id))
       .select((
         person::all_columns,
diff --git a/crates/db_views/src/structs.rs b/crates/db_views/src/structs.rs
index 09a868be9f..c224162c72 100644
--- a/crates/db_views/src/structs.rs
+++ b/crates/db_views/src/structs.rs
@@ -1,7 +1,17 @@
 #[cfg(feature = "full")]
-use diesel::Queryable;
-#[cfg(feature = "full")]
-use diesel::{deserialize::FromSqlRow, expression::AsExpression, sql_types};
+use diesel::{
+  deserialize::FromSqlRow,
+  dsl::exists,
+  dsl::Nullable,
+  expression::AsExpression,
+  sql_types,
+  BoolExpressionMethods,
+  ExpressionMethods,
+  NullableExpressionMethods,
+  QueryDsl,
+  Queryable,
+  Selectable,
+};
 use lemmy_db_schema::{
   aggregates::structs::{
     CommentAggregates,
@@ -60,6 +70,14 @@ use lemmy_db_schema::{
   },
   SubscribedType,
 };
+#[cfg(feature = "full")]
+use lemmy_db_schema::{
+  aliases::{creator_community_actions, person1},
+  schema::{comment, comment_actions, community_actions, local_user, person, person_actions},
+  source::community::CommunityFollower,
+  utils::functions::coalesce,
+  Person1AliasAllColumnsTuple,
+};
 use serde::{Deserialize, Serialize};
 use serde_with::skip_serializing_none;
 #[cfg(feature = "full")]
@@ -93,24 +111,90 @@ pub struct CommentReportView {
 
 #[skip_serializing_none]
 #[derive(Debug, PartialEq, Serialize, Deserialize, Clone)]
-#[cfg_attr(feature = "full", derive(TS, Queryable))]
+#[cfg_attr(feature = "full", derive(TS, Queryable, Selectable))]
 #[cfg_attr(feature = "full", diesel(check_for_backend(diesel::pg::Pg)))]
 #[cfg_attr(feature = "full", ts(export))]
 /// A comment view.
 pub struct CommentView {
+  #[cfg_attr(feature = "full", diesel(embed))]
   pub comment: Comment,
+  #[cfg_attr(feature = "full", diesel(embed))]
   pub creator: Person,
+  #[cfg_attr(feature = "full", diesel(embed))]
   pub post: Post,
+  #[cfg_attr(feature = "full", diesel(embed))]
   pub community: Community,
+  #[cfg_attr(feature = "full", diesel(embed))]
   pub counts: CommentAggregates,
+  #[cfg_attr(feature = "full",
+    diesel(
+      select_expression =
+        creator_community_actions
+          .field(community_actions::received_ban)
+          .nullable()
+          .is_not_null()
+    )
+  )]
   pub creator_banned_from_community: bool,
+  #[cfg_attr(feature = "full",
+    diesel(
+      select_expression =
+        community_actions::received_ban.nullable().is_not_null()
+    )
+  )]
   pub banned_from_community: bool,
+  #[cfg_attr(feature = "full",
+    diesel(
+      select_expression =
+        creator_community_actions
+          .field(community_actions::became_moderator)
+          .nullable()
+          .is_not_null()
+    )
+  )]
   pub creator_is_moderator: bool,
+  #[cfg_attr(feature = "full",
+    diesel(
+      select_expression =
+        exists(
+          local_user::table.filter(
+            comment::creator_id
+              .eq(local_user::person_id)
+              .and(local_user::admin.eq(true))
+          )
+        )
+    )
+  )]
   pub creator_is_admin: bool,
+  #[cfg_attr(feature = "full",
+    diesel(
+      select_expression_type = Nullable<community_actions::follow_state>,
+      select_expression =
+        CommunityFollower::select_subscribed_type(),
+    )
+  )]
   pub subscribed: SubscribedType,
+  #[cfg_attr(feature = "full",
+    diesel(
+      select_expression =
+        comment_actions::saved.nullable().is_not_null()
+    )
+  )]
   pub saved: bool,
+  #[cfg_attr(feature = "full",
+    diesel(
+      select_expression =
+        person_actions::blocked.nullable().is_not_null()
+    )
+  )]
   pub creator_blocked: bool,
   #[cfg_attr(feature = "full", ts(optional))]
+  #[cfg_attr(feature = "full",
+    diesel(
+      select_expression =
+        comment_actions::like_score.nullable()
+    )
+  )]
   pub my_vote: Option<i16>,
 }
 
@@ -152,14 +236,18 @@ pub struct CommunityReportView {
 }
 
 #[derive(Debug, Serialize, Deserialize, Clone)]
-#[cfg_attr(feature = "full", derive(TS, Queryable))]
+#[cfg_attr(feature = "full", derive(TS, Queryable, Selectable))]
 #[cfg_attr(feature = "full", diesel(check_for_backend(diesel::pg::Pg)))]
 #[cfg_attr(feature = "full", ts(export))]
 /// A local user view.
 pub struct LocalUserView {
+  #[cfg_attr(feature = "full", diesel(embed))]
   pub local_user: LocalUser,
+  #[cfg_attr(feature = "full", diesel(embed))]
   pub local_user_vote_display_mode: LocalUserVoteDisplayMode,
+  #[cfg_attr(feature = "full", diesel(embed))]
   pub person: Person,
+  #[cfg_attr(feature = "full", diesel(embed))]
   pub counts: PersonAggregates,
 }
 
@@ -263,27 +351,40 @@ pub struct PrivateMessageReportView {
 
 #[skip_serializing_none]
 #[derive(Debug, PartialEq, Eq, Serialize, Deserialize, Clone)]
-#[cfg_attr(feature = "full", derive(TS, Queryable))]
+#[cfg_attr(feature = "full", derive(TS, Queryable, Selectable))]
 #[cfg_attr(feature = "full", diesel(check_for_backend(diesel::pg::Pg)))]
 #[cfg_attr(feature = "full", ts(export))]
 /// A registration application view.
 pub struct RegistrationApplicationView {
+  #[cfg_attr(feature = "full", diesel(embed))]
   pub registration_application: RegistrationApplication,
+  #[cfg_attr(feature = "full", diesel(embed))]
   pub creator_local_user: LocalUser,
+  #[cfg_attr(feature = "full", diesel(embed))]
   pub creator: Person,
   #[cfg_attr(feature = "full", ts(optional))]
+  #[cfg_attr(feature = "full",
+    diesel(
+      select_expression_type = Nullable<Person1AliasAllColumnsTuple>,
+      select_expression = person1.fields(person::all_columns).nullable()
+    )
+  )]
   pub admin: Option<Person>,
 }
 
 #[derive(Debug, Serialize, Deserialize, Clone)]
-#[cfg_attr(feature = "full", derive(TS, Queryable))]
+#[cfg_attr(feature = "full", derive(TS, Queryable, Selectable))]
 #[cfg_attr(feature = "full", diesel(check_for_backend(diesel::pg::Pg)))]
 #[cfg_attr(feature = "full", ts(export))]
 /// A site view.
 pub struct SiteView {
+  #[cfg_attr(feature = "full", diesel(embed))]
   pub site: Site,
+  #[cfg_attr(feature = "full", diesel(embed))]
   pub local_site: LocalSite,
+  #[cfg_attr(feature = "full", diesel(embed))]
   pub local_site_rate_limit: LocalSiteRateLimit,
+  #[cfg_attr(feature = "full", diesel(embed))]
   pub counts: SiteAggregates,
 }
 
@@ -311,12 +412,14 @@ pub struct VoteView {
 
 #[skip_serializing_none]
 #[derive(Debug, Serialize, Deserialize, Clone)]
-#[cfg_attr(feature = "full", derive(TS, Queryable))]
+#[cfg_attr(feature = "full", derive(TS, Queryable, Selectable))]
 #[cfg_attr(feature = "full", diesel(check_for_backend(diesel::pg::Pg)))]
 #[cfg_attr(feature = "full", ts(export))]
 /// A local image view.
 pub struct LocalImageView {
+  #[cfg_attr(feature = "full", diesel(embed))]
   pub local_image: LocalImage,
+  #[cfg_attr(feature = "full", diesel(embed))]
   pub person: Person,
 }
 
@@ -412,44 +515,68 @@ pub enum PersonContentCombinedView {
 }
 
 #[derive(Debug, Serialize, Deserialize, Clone)]
-#[cfg_attr(feature = "full", derive(TS, Queryable))]
+#[cfg_attr(feature = "full", derive(TS, Queryable, Selectable))]
 #[cfg_attr(feature = "full", diesel(check_for_backend(diesel::pg::Pg)))]
 #[cfg_attr(feature = "full", ts(export))]
 /// A community follower.
 pub struct CommunityFollowerView {
+  #[cfg_attr(feature = "full", diesel(embed))]
   pub community: Community,
+  #[cfg_attr(feature = "full", diesel(embed))]
   pub follower: Person,
 }
 
 #[derive(Debug, Serialize, Deserialize, Clone)]
-#[cfg_attr(feature = "full", derive(TS, Queryable))]
+#[cfg_attr(feature = "full", derive(TS, Queryable, Selectable))]
 #[cfg_attr(feature = "full", diesel(check_for_backend(diesel::pg::Pg)))]
 #[cfg_attr(feature = "full", ts(export))]
 /// A community moderator.
 pub struct CommunityModeratorView {
+  #[cfg_attr(feature = "full", diesel(embed))]
   pub community: Community,
+  #[cfg_attr(feature = "full", diesel(embed))]
   pub moderator: Person,
 }
 
 #[derive(Debug, Serialize, Deserialize, Clone)]
-#[cfg_attr(feature = "full", derive(Queryable))]
+#[cfg_attr(feature = "full", derive(Queryable, Selectable))]
 #[cfg_attr(feature = "full", diesel(check_for_backend(diesel::pg::Pg)))]
 /// A community person ban.
 pub struct CommunityPersonBanView {
+  #[cfg_attr(feature = "full", diesel(embed))]
   pub community: Community,
+  #[cfg_attr(feature = "full", diesel(embed))]
   pub person: Person,
 }
 
 #[derive(Debug, PartialEq, Serialize, Deserialize, Clone)]
-#[cfg_attr(feature = "full", derive(TS, Queryable))]
+#[cfg_attr(feature = "full", derive(TS, Queryable, Selectable))]
 #[cfg_attr(feature = "full", diesel(check_for_backend(diesel::pg::Pg)))]
 #[cfg_attr(feature = "full", ts(export))]
 /// A community view.
 pub struct CommunityView {
+  #[cfg_attr(feature = "full", diesel(embed))]
   pub community: Community,
+  #[cfg_attr(feature = "full",
+    diesel(
+      select_expression_type = Nullable<community_actions::follow_state>,
+      select_expression = CommunityFollower::select_subscribed_type()
+    )
+  )]
   pub subscribed: SubscribedType,
+  #[cfg_attr(feature = "full",
+    diesel(
+      select_expression = community_actions::blocked.nullable().is_not_null()
+    )
+  )]
   pub blocked: bool,
+  #[cfg_attr(feature = "full", diesel(embed))]
   pub counts: CommunityAggregates,
+  #[cfg_attr(feature = "full",
+    diesel(
+      select_expression = community_actions::received_ban.nullable().is_not_null()
+    )
+  )]
   pub banned_from_community: bool,
 }
 
@@ -458,28 +585,20 @@ pub struct CommunityView {
 #[cfg_attr(feature = "full", derive(TS))]
 #[cfg_attr(feature = "full", ts(export))]
 pub enum CommunitySortType {
+  ActiveSixMonths,
   #[default]
-  Active,
+  ActiveMonthly,
+  ActiveWeekly,
+  ActiveDaily,
   Hot,
   New,
   Old,
-  TopDay,
-  TopWeek,
-  TopMonth,
-  TopYear,
-  TopAll,
-  MostComments,
-  NewComments,
-  TopHour,
-  TopSixHour,
-  TopTwelveHour,
-  TopThreeMonths,
-  TopSixMonths,
-  TopNineMonths,
-  Controversial,
-  Scaled,
   NameAsc,
   NameDesc,
+  Comments,
+  Posts,
+  Subscribers,
+  SubscribersLocal,
 }
 
 #[skip_serializing_none]
@@ -563,13 +682,21 @@ pub struct CommentReplyView {
 }
 
 #[derive(Debug, PartialEq, Serialize, Deserialize, Clone)]
-#[cfg_attr(feature = "full", derive(TS, Queryable))]
+#[cfg_attr(feature = "full", derive(TS, Queryable, Selectable))]
 #[cfg_attr(feature = "full", diesel(check_for_backend(diesel::pg::Pg)))]
 #[cfg_attr(feature = "full", ts(export))]
 /// A person view.
 pub struct PersonView {
+  #[cfg_attr(feature = "full", diesel(embed))]
   pub person: Person,
+  #[cfg_attr(feature = "full", diesel(embed))]
   pub counts: PersonAggregates,
+  #[cfg_attr(feature = "full",
+    diesel(
+      select_expression_type = coalesce<diesel::sql_types::Bool, Nullable<local_user::admin>, bool>,
+      select_expression = coalesce(local_user::admin.nullable(), false)
+    )
+  )]
   pub is_admin: bool,
 }
 
@@ -585,13 +712,21 @@ pub struct PendingFollow {
 }
 
 #[derive(Debug, PartialEq, Eq, Serialize, Deserialize, Clone)]
-#[cfg_attr(feature = "full", derive(TS, Queryable))]
+#[cfg_attr(feature = "full", derive(TS, Queryable, Selectable))]
 #[cfg_attr(feature = "full", diesel(check_for_backend(diesel::pg::Pg)))]
 #[cfg_attr(feature = "full", ts(export))]
 /// A private message view.
 pub struct PrivateMessageView {
+  #[cfg_attr(feature = "full", diesel(embed))]
   pub private_message: PrivateMessage,
+  #[cfg_attr(feature = "full", diesel(embed))]
   pub creator: Person,
+  #[cfg_attr(feature = "full",
+    diesel(
+      select_expression_type = Person1AliasAllColumnsTuple,
+      select_expression = person1.fields(person::all_columns)
+    )
+  )]
   pub recipient: Person,
 }
 
@@ -888,36 +1023,64 @@ pub struct AdminAllowInstanceView {
 pub struct ModlogCombinedPaginationCursor(pub String);
 
 #[derive(Debug, PartialEq, Serialize, Deserialize, Clone)]
-#[cfg_attr(feature = "full", derive(Queryable))]
+#[cfg_attr(feature = "full", derive(Queryable, Selectable))]
 #[cfg_attr(feature = "full", diesel(check_for_backend(diesel::pg::Pg)))]
 /// A combined modlog view
 pub struct ModlogCombinedViewInternal {
   // Specific
+  #[cfg_attr(feature = "full", diesel(embed))]
   pub admin_allow_instance: Option<AdminAllowInstance>,
+  #[cfg_attr(feature = "full", diesel(embed))]
   pub admin_block_instance: Option<AdminBlockInstance>,
+  #[cfg_attr(feature = "full", diesel(embed))]
   pub admin_purge_comment: Option<AdminPurgeComment>,
+  #[cfg_attr(feature = "full", diesel(embed))]
   pub admin_purge_community: Option<AdminPurgeCommunity>,
+  #[cfg_attr(feature = "full", diesel(embed))]
   pub admin_purge_person: Option<AdminPurgePerson>,
+  #[cfg_attr(feature = "full", diesel(embed))]
   pub admin_purge_post: Option<AdminPurgePost>,
+  #[cfg_attr(feature = "full", diesel(embed))]
   pub mod_add: Option<ModAdd>,
+  #[cfg_attr(feature = "full", diesel(embed))]
   pub mod_add_community: Option<ModAddCommunity>,
+  #[cfg_attr(feature = "full", diesel(embed))]
   pub mod_ban: Option<ModBan>,
+  #[cfg_attr(feature = "full", diesel(embed))]
   pub mod_ban_from_community: Option<ModBanFromCommunity>,
+  #[cfg_attr(feature = "full", diesel(embed))]
   pub mod_feature_post: Option<ModFeaturePost>,
+  #[cfg_attr(feature = "full", diesel(embed))]
   pub mod_hide_community: Option<ModHideCommunity>,
+  #[cfg_attr(feature = "full", diesel(embed))]
   pub mod_lock_post: Option<ModLockPost>,
+  #[cfg_attr(feature = "full", diesel(embed))]
   pub mod_remove_comment: Option<ModRemoveComment>,
+  #[cfg_attr(feature = "full", diesel(embed))]
   pub mod_remove_community: Option<ModRemoveCommunity>,
+  #[cfg_attr(feature = "full", diesel(embed))]
   pub mod_remove_post: Option<ModRemovePost>,
+  #[cfg_attr(feature = "full", diesel(embed))]
   pub mod_transfer_community: Option<ModTransferCommunity>,
   // Specific fields
 
   // Shared
+  #[cfg_attr(feature = "full", diesel(embed))]
   pub moderator: Option<Person>,
+  #[cfg_attr(feature = "full",
+    diesel(
+      select_expression_type = Nullable<Person1AliasAllColumnsTuple>,
+      select_expression = person1.fields(person::all_columns).nullable()
+    )
+  )]
   pub other_person: Option<Person>,
+  #[cfg_attr(feature = "full", diesel(embed))]
   pub instance: Option<Instance>,
+  #[cfg_attr(feature = "full", diesel(embed))]
   pub community: Option<Community>,
+  #[cfg_attr(feature = "full", diesel(embed))]
   pub post: Option<Post>,
+  #[cfg_attr(feature = "full", diesel(embed))]
   pub comment: Option<Comment>,
 }
 
diff --git a/crates/routes/src/utils/scheduled_tasks.rs b/crates/routes/src/utils/scheduled_tasks.rs
index 83463c3442..3d50116a4b 100644
--- a/crates/routes/src/utils/scheduled_tasks.rs
+++ b/crates/routes/src/utils/scheduled_tasks.rs
@@ -39,15 +39,7 @@ use lemmy_db_schema::{
     post::{Post, PostUpdateForm},
   },
   traits::Crud,
-  utils::{
-    find_action,
-    functions::coalesce,
-    get_conn,
-    now,
-    uplete,
-    DbPool,
-    DELETED_REPLACEMENT_TEXT,
-  },
+  utils::{functions::coalesce, get_conn, now, uplete, DbPool, DELETED_REPLACEMENT_TEXT},
 };
 use lemmy_utils::error::{LemmyErrorType, LemmyResult};
 use reqwest_middleware::ClientWithMiddleware;
@@ -435,6 +427,10 @@ async fn publish_scheduled_posts(context: &Data<LemmyContext>) -> LemmyResult<()
   let pool = &mut context.pool();
   let mut conn = get_conn(pool).await?;
 
+  let not_banned_action = community_actions::table
+    .find((person::id, community::id))
+    .filter(community_actions::received_ban.is_not_null());
+
   let scheduled_posts: Vec<_> = post::table
     .inner_join(community::table)
     .inner_join(person::table)
@@ -446,10 +442,7 @@ async fn publish_scheduled_posts(context: &Data<LemmyContext>) -> LemmyResult<()
     .filter(not(person::banned.or(person::deleted)))
     .filter(not(community::removed.or(community::deleted)))
     // ensure that user isnt banned from community
-    .filter(not(exists(find_action(
-      community_actions::received_ban,
-      (person::id, community::id),
-    ))))
+    .filter(not(exists(not_banned_action)))
     .select((post::all_columns, community::all_columns))
     .get_results::<(Post, Community)>(&mut conn)
     .await?;
diff --git a/crates/utils/src/error.rs b/crates/utils/src/error.rs
index 806a4f86f2..fe764ed8c4 100644
--- a/crates/utils/src/error.rs
+++ b/crates/utils/src/error.rs
@@ -1,6 +1,6 @@
 use cfg_if::cfg_if;
 use serde::{Deserialize, Serialize};
-use std::{backtrace::Backtrace, fmt::Debug};
+use std::fmt::Debug;
 use strum::{Display, EnumIter};
 
 #[derive(Display, Debug, Serialize, Deserialize, Clone, PartialEq, Eq, EnumIter, Hash)]
@@ -192,7 +192,7 @@ pub enum FederationError {
 cfg_if! {
   if #[cfg(feature = "full")] {
 
-    use std::fmt;
+    use std::{fmt, backtrace::Backtrace};
     pub type LemmyResult<T> = Result<T, LemmyError>;
 
     pub struct LemmyError {


Test patch: