From: Nutomic <me@nutomic.com>
Date: Thu, 4 Aug 2022 19:30:17 +0000 (+0200)
Subject: Use typed-builder crate for queries (#2379)
X-Git-Url: http://these/git/%7B%60%24%7BghostArchiveUrl%7D/%22%7Burl%7D/static/gitweb.css?a=commitdiff_plain;h=8a4d9cc1ba81062b08dfca2e4b2594d1b053eca3;p=lemmy.git

Use typed-builder crate for queries (#2379)

* Use typed-builder crate for PrivateMessageQuery

* derive builder for all queries

* remove unnecessary clones

* fix tests
---

diff --git a/.drone.yml b/.drone.yml
index 9803de0c..de4114c6 100644
--- a/.drone.yml
+++ b/.drone.yml
@@ -41,7 +41,7 @@ steps:
     commands:
       - apt-get update
       - apt-get -y install --no-install-recommends postgresql-client
-      - cargo test --workspace --no-fail-fast
+      - cargo test --workspace --no-fail-fast --all-features
 
   - name: check defaults.hjson updated
     image: clux/muslrust:1.57.0
diff --git a/Cargo.lock b/Cargo.lock
index 4be531b0..18ac1edc 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -2020,6 +2020,7 @@ dependencies = [
  "serde",
  "serial_test",
  "tracing",
+ "typed-builder",
 ]
 
 [[package]]
@@ -2029,6 +2030,7 @@ dependencies = [
  "diesel",
  "lemmy_db_schema",
  "serde",
+ "typed-builder",
 ]
 
 [[package]]
@@ -4201,6 +4203,17 @@ version = "1.7.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "a9b2228007eba4120145f785df0f6c92ea538f5a3635a612ecf4e334c8c1446d"
 
+[[package]]
+name = "typed-builder"
+version = "0.10.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "89851716b67b937e393b3daa8423e67ddfc4bbbf1654bcf05488e95e0828db0c"
+dependencies = [
+ "proc-macro2 1.0.39",
+ "quote 1.0.18",
+ "syn 1.0.96",
+]
+
 [[package]]
 name = "typenum"
 version = "1.15.0"
diff --git a/crates/api/src/comment_report/list.rs b/crates/api/src/comment_report/list.rs
index c8678425..3a924aea 100644
--- a/crates/api/src/comment_report/list.rs
+++ b/crates/api/src/comment_report/list.rs
@@ -4,7 +4,7 @@ use lemmy_api_common::{
   comment::{ListCommentReports, ListCommentReportsResponse},
   utils::{blocking, get_local_user_view_from_jwt},
 };
-use lemmy_db_views::comment_report_view::CommentReportQueryBuilder;
+use lemmy_db_views::comment_report_view::CommentReportQuery;
 use lemmy_utils::{error::LemmyError, ConnectionId};
 use lemmy_websocket::LemmyContext;
 
@@ -32,11 +32,15 @@ impl Perform for ListCommentReports {
     let page = data.page;
     let limit = data.limit;
     let comment_reports = blocking(context.pool(), move |conn| {
-      CommentReportQueryBuilder::create(conn, person_id, admin)
+      CommentReportQuery::builder()
+        .conn(conn)
+        .my_person_id(person_id)
+        .admin(admin)
         .community_id(community_id)
         .unresolved_only(unresolved_only)
         .page(page)
         .limit(limit)
+        .build()
         .list()
     })
     .await??;
diff --git a/crates/api/src/local_user/notifications/list_mentions.rs b/crates/api/src/local_user/notifications/list_mentions.rs
index 0d43e614..b331012b 100644
--- a/crates/api/src/local_user/notifications/list_mentions.rs
+++ b/crates/api/src/local_user/notifications/list_mentions.rs
@@ -4,7 +4,7 @@ use lemmy_api_common::{
   person::{GetPersonMentions, GetPersonMentionsResponse},
   utils::{blocking, get_local_user_view_from_jwt},
 };
-use lemmy_db_views_actor::person_mention_view::PersonMentionQueryBuilder;
+use lemmy_db_views_actor::person_mention_view::PersonMentionQuery;
 use lemmy_utils::{error::LemmyError, ConnectionId};
 use lemmy_websocket::LemmyContext;
 
@@ -26,11 +26,12 @@ impl Perform for GetPersonMentions {
     let page = data.page;
     let limit = data.limit;
     let unread_only = data.unread_only;
-    let person_id = local_user_view.person.id;
-    let show_bot_accounts = local_user_view.local_user.show_bot_accounts;
+    let person_id = Some(local_user_view.person.id);
+    let show_bot_accounts = Some(local_user_view.local_user.show_bot_accounts);
 
     let mentions = blocking(context.pool(), move |conn| {
-      PersonMentionQueryBuilder::create(conn)
+      PersonMentionQuery::builder()
+        .conn(conn)
         .recipient_id(person_id)
         .my_person_id(person_id)
         .sort(sort)
@@ -38,6 +39,7 @@ impl Perform for GetPersonMentions {
         .show_bot_accounts(show_bot_accounts)
         .page(page)
         .limit(limit)
+        .build()
         .list()
     })
     .await??;
diff --git a/crates/api/src/local_user/notifications/list_replies.rs b/crates/api/src/local_user/notifications/list_replies.rs
index 048c14be..9a92f625 100644
--- a/crates/api/src/local_user/notifications/list_replies.rs
+++ b/crates/api/src/local_user/notifications/list_replies.rs
@@ -4,7 +4,7 @@ use lemmy_api_common::{
   person::{GetReplies, GetRepliesResponse},
   utils::{blocking, get_local_user_view_from_jwt},
 };
-use lemmy_db_views_actor::comment_reply_view::CommentReplyQueryBuilder;
+use lemmy_db_views_actor::comment_reply_view::CommentReplyQuery;
 use lemmy_utils::{error::LemmyError, ConnectionId};
 use lemmy_websocket::LemmyContext;
 
@@ -26,11 +26,12 @@ impl Perform for GetReplies {
     let page = data.page;
     let limit = data.limit;
     let unread_only = data.unread_only;
-    let person_id = local_user_view.person.id;
-    let show_bot_accounts = local_user_view.local_user.show_bot_accounts;
+    let person_id = Some(local_user_view.person.id);
+    let show_bot_accounts = Some(local_user_view.local_user.show_bot_accounts);
 
     let replies = blocking(context.pool(), move |conn| {
-      CommentReplyQueryBuilder::create(conn)
+      CommentReplyQuery::builder()
+        .conn(conn)
         .recipient_id(person_id)
         .my_person_id(person_id)
         .sort(sort)
@@ -38,6 +39,7 @@ impl Perform for GetReplies {
         .show_bot_accounts(show_bot_accounts)
         .page(page)
         .limit(limit)
+        .build()
         .list()
     })
     .await??;
diff --git a/crates/api/src/post_report/list.rs b/crates/api/src/post_report/list.rs
index 71cde96a..3a4d9dcc 100644
--- a/crates/api/src/post_report/list.rs
+++ b/crates/api/src/post_report/list.rs
@@ -4,7 +4,7 @@ use lemmy_api_common::{
   post::{ListPostReports, ListPostReportsResponse},
   utils::{blocking, get_local_user_view_from_jwt},
 };
-use lemmy_db_views::post_report_view::PostReportQueryBuilder;
+use lemmy_db_views::post_report_view::PostReportQuery;
 use lemmy_utils::{error::LemmyError, ConnectionId};
 use lemmy_websocket::LemmyContext;
 
@@ -32,11 +32,15 @@ impl Perform for ListPostReports {
     let page = data.page;
     let limit = data.limit;
     let post_reports = blocking(context.pool(), move |conn| {
-      PostReportQueryBuilder::create(conn, person_id, admin)
+      PostReportQuery::builder()
+        .conn(conn)
+        .my_person_id(person_id)
+        .admin(admin)
         .community_id(community_id)
         .unresolved_only(unresolved_only)
         .page(page)
         .limit(limit)
+        .build()
         .list()
     })
     .await??;
diff --git a/crates/api/src/site/registration_applications/list.rs b/crates/api/src/site/registration_applications/list.rs
index 60dfd446..106b0850 100644
--- a/crates/api/src/site/registration_applications/list.rs
+++ b/crates/api/src/site/registration_applications/list.rs
@@ -5,7 +5,7 @@ use lemmy_api_common::{
   utils::{blocking, get_local_user_view_from_jwt, is_admin},
 };
 use lemmy_db_schema::source::site::Site;
-use lemmy_db_views::registration_application_view::RegistrationApplicationQueryBuilder;
+use lemmy_db_views::registration_application_view::RegistrationApplicationQuery;
 use lemmy_utils::{error::LemmyError, ConnectionId};
 use lemmy_websocket::LemmyContext;
 
@@ -34,11 +34,13 @@ impl Perform for ListRegistrationApplications {
     let page = data.page;
     let limit = data.limit;
     let registration_applications = blocking(context.pool(), move |conn| {
-      RegistrationApplicationQueryBuilder::create(conn)
+      RegistrationApplicationQuery::builder()
+        .conn(conn)
         .unread_only(unread_only)
-        .verified_email_only(verified_email_only)
+        .verified_email_only(Some(verified_email_only))
         .page(page)
         .limit(limit)
+        .build()
         .list()
     })
     .await??;
diff --git a/crates/api/src/site/search.rs b/crates/api/src/site/search.rs
index af97978a..7c118a0b 100644
--- a/crates/api/src/site/search.rs
+++ b/crates/api/src/site/search.rs
@@ -11,11 +11,8 @@ use lemmy_db_schema::{
   utils::post_to_comment_sort_type,
   SearchType,
 };
-use lemmy_db_views::{comment_view::CommentQueryBuilder, post_view::PostQueryBuilder};
-use lemmy_db_views_actor::{
-  community_view::CommunityQueryBuilder,
-  person_view::PersonQueryBuilder,
-};
+use lemmy_db_views::{comment_view::CommentQuery, post_view::PostQuery};
+use lemmy_db_views_actor::{community_view::CommunityQuery, person_view::PersonQuery};
 use lemmy_utils::{error::LemmyError, ConnectionId};
 use lemmy_websocket::LemmyContext;
 
@@ -73,7 +70,8 @@ impl Perform for Search {
     match search_type {
       SearchType::Posts => {
         posts = blocking(context.pool(), move |conn| {
-          PostQueryBuilder::create(conn)
+          PostQuery::builder()
+            .conn(conn)
             .sort(sort)
             .show_nsfw(show_nsfw)
             .show_bot_accounts(show_bot_accounts)
@@ -83,19 +81,21 @@ impl Perform for Search {
             .community_actor_id(community_actor_id)
             .creator_id(creator_id)
             .my_person_id(person_id)
-            .search_term(q)
+            .search_term(Some(q))
             .page(page)
             .limit(limit)
+            .build()
             .list()
         })
         .await??;
       }
       SearchType::Comments => {
         comments = blocking(context.pool(), move |conn| {
-          CommentQueryBuilder::create(conn)
+          CommentQuery::builder()
+            .conn(conn)
             .sort(sort.map(post_to_comment_sort_type))
             .listing_type(listing_type)
-            .search_term(q)
+            .search_term(Some(q))
             .show_bot_accounts(show_bot_accounts)
             .community_id(community_id)
             .community_actor_id(community_actor_id)
@@ -103,30 +103,35 @@ impl Perform for Search {
             .my_person_id(person_id)
             .page(page)
             .limit(limit)
+            .build()
             .list()
         })
         .await??;
       }
       SearchType::Communities => {
         communities = blocking(context.pool(), move |conn| {
-          CommunityQueryBuilder::create(conn)
+          CommunityQuery::builder()
+            .conn(conn)
             .sort(sort)
             .listing_type(listing_type)
-            .search_term(q)
+            .search_term(Some(q))
             .my_person_id(person_id)
             .page(page)
             .limit(limit)
+            .build()
             .list()
         })
         .await??;
       }
       SearchType::Users => {
         users = blocking(context.pool(), move |conn| {
-          PersonQueryBuilder::create(conn)
+          PersonQuery::builder()
+            .conn(conn)
             .sort(sort)
-            .search_term(q)
+            .search_term(Some(q))
             .page(page)
             .limit(limit)
+            .build()
             .list()
         })
         .await??;
@@ -138,7 +143,8 @@ impl Perform for Search {
         let community_actor_id_2 = community_actor_id.to_owned();
 
         posts = blocking(context.pool(), move |conn| {
-          PostQueryBuilder::create(conn)
+          PostQuery::builder()
+            .conn(conn)
             .sort(sort)
             .show_nsfw(show_nsfw)
             .show_bot_accounts(show_bot_accounts)
@@ -148,9 +154,10 @@ impl Perform for Search {
             .community_actor_id(community_actor_id_2)
             .creator_id(creator_id)
             .my_person_id(person_id)
-            .search_term(q)
+            .search_term(Some(q))
             .page(page)
             .limit(limit)
+            .build()
             .list()
         })
         .await??;
@@ -159,10 +166,11 @@ impl Perform for Search {
         let community_actor_id = community_actor_id.to_owned();
 
         comments = blocking(context.pool(), move |conn| {
-          CommentQueryBuilder::create(conn)
+          CommentQuery::builder()
+            .conn(conn)
             .sort(sort.map(post_to_comment_sort_type))
             .listing_type(listing_type)
-            .search_term(q)
+            .search_term(Some(q))
             .show_bot_accounts(show_bot_accounts)
             .community_id(community_id)
             .community_actor_id(community_actor_id)
@@ -170,6 +178,7 @@ impl Perform for Search {
             .my_person_id(person_id)
             .page(page)
             .limit(limit)
+            .build()
             .list()
         })
         .await??;
@@ -180,13 +189,15 @@ impl Perform for Search {
           vec![]
         } else {
           blocking(context.pool(), move |conn| {
-            CommunityQueryBuilder::create(conn)
+            CommunityQuery::builder()
+              .conn(conn)
               .sort(sort)
               .listing_type(listing_type)
-              .search_term(q)
+              .search_term(Some(q))
               .my_person_id(person_id)
               .page(page)
               .limit(limit)
+              .build()
               .list()
           })
           .await??
@@ -198,11 +209,13 @@ impl Perform for Search {
           vec![]
         } else {
           blocking(context.pool(), move |conn| {
-            PersonQueryBuilder::create(conn)
+            PersonQuery::builder()
+              .conn(conn)
               .sort(sort)
-              .search_term(q)
+              .search_term(Some(q))
               .page(page)
               .limit(limit)
+              .build()
               .list()
           })
           .await??
@@ -210,7 +223,8 @@ impl Perform for Search {
       }
       SearchType::Url => {
         posts = blocking(context.pool(), move |conn| {
-          PostQueryBuilder::create(conn)
+          PostQuery::builder()
+            .conn(conn)
             .sort(sort)
             .show_nsfw(show_nsfw)
             .show_bot_accounts(show_bot_accounts)
@@ -220,9 +234,10 @@ impl Perform for Search {
             .community_id(community_id)
             .community_actor_id(community_actor_id)
             .creator_id(creator_id)
-            .url_search(q)
+            .url_search(Some(q))
             .page(page)
             .limit(limit)
+            .build()
             .list()
         })
         .await??;
diff --git a/crates/api_common/src/utils.rs b/crates/api_common/src/utils.rs
index c189e71d..bf99b7b9 100644
--- a/crates/api_common/src/utils.rs
+++ b/crates/api_common/src/utils.rs
@@ -20,7 +20,7 @@ use lemmy_db_schema::{
   ListingType,
 };
 use lemmy_db_views::{
-  comment_view::CommentQueryBuilder,
+  comment_view::CommentQuery,
   structs::{LocalUserSettingsView, LocalUserView},
 };
 use lemmy_db_views_actor::structs::{
@@ -667,10 +667,12 @@ pub async fn remove_user_data_in_community(
   // Comments
   // TODO Diesel doesn't allow updates with joins, so this has to be a loop
   let comments = blocking(pool, move |conn| {
-    CommentQueryBuilder::create(conn)
-      .creator_id(banned_person_id)
-      .community_id(community_id)
-      .limit(std::i64::MAX)
+    CommentQuery::builder()
+      .conn(conn)
+      .creator_id(Some(banned_person_id))
+      .community_id(Some(community_id))
+      .limit(Some(i64::MAX))
+      .build()
       .list()
   })
   .await??;
diff --git a/crates/api_crud/src/comment/list.rs b/crates/api_crud/src/comment/list.rs
index 998acc03..ca40d99c 100644
--- a/crates/api_crud/src/comment/list.rs
+++ b/crates/api_crud/src/comment/list.rs
@@ -14,7 +14,7 @@ use lemmy_db_schema::{
   source::{comment::Comment, community::Community},
   traits::{Crud, DeleteableOrRemoveable},
 };
-use lemmy_db_views::comment_view::CommentQueryBuilder;
+use lemmy_db_views::comment_view::CommentQuery;
 use lemmy_utils::{error::LemmyError, ConnectionId};
 use lemmy_websocket::LemmyContext;
 
@@ -71,8 +71,9 @@ impl PerformCrud for GetComments {
 
     let post_id = data.post_id;
     let mut comments = blocking(context.pool(), move |conn| {
-      CommentQueryBuilder::create(conn)
-        .listing_type(listing_type)
+      CommentQuery::builder()
+        .conn(conn)
+        .listing_type(Some(listing_type))
         .sort(sort)
         .max_depth(max_depth)
         .saved_only(saved_only)
@@ -84,6 +85,7 @@ impl PerformCrud for GetComments {
         .show_bot_accounts(show_bot_accounts)
         .page(page)
         .limit(limit)
+        .build()
         .list()
     })
     .await?
diff --git a/crates/api_crud/src/community/list.rs b/crates/api_crud/src/community/list.rs
index 7afb6178..c57e9eaa 100644
--- a/crates/api_crud/src/community/list.rs
+++ b/crates/api_crud/src/community/list.rs
@@ -5,7 +5,7 @@ use lemmy_api_common::{
   utils::{blocking, check_private_instance, get_local_user_view_from_jwt_opt},
 };
 use lemmy_db_schema::traits::DeleteableOrRemoveable;
-use lemmy_db_views_actor::community_view::CommunityQueryBuilder;
+use lemmy_db_views_actor::community_view::CommunityQuery;
 use lemmy_utils::{error::LemmyError, ConnectionId};
 use lemmy_websocket::LemmyContext;
 
@@ -39,13 +39,15 @@ impl PerformCrud for ListCommunities {
     let page = data.page;
     let limit = data.limit;
     let mut communities = blocking(context.pool(), move |conn| {
-      CommunityQueryBuilder::create(conn)
+      CommunityQuery::builder()
+        .conn(conn)
         .listing_type(listing_type)
         .sort(sort)
-        .show_nsfw(show_nsfw)
+        .show_nsfw(Some(show_nsfw))
         .my_person_id(person_id)
         .page(page)
         .limit(limit)
+        .build()
         .list()
     })
     .await??;
diff --git a/crates/api_crud/src/post/list.rs b/crates/api_crud/src/post/list.rs
index 1af4f55b..468910b2 100644
--- a/crates/api_crud/src/post/list.rs
+++ b/crates/api_crud/src/post/list.rs
@@ -11,7 +11,7 @@ use lemmy_api_common::{
 };
 use lemmy_apub::{fetcher::resolve_actor_identifier, objects::community::ApubCommunity};
 use lemmy_db_schema::{source::community::Community, traits::DeleteableOrRemoveable};
-use lemmy_db_views::post_view::PostQueryBuilder;
+use lemmy_db_views::post_view::PostQuery;
 use lemmy_utils::{error::LemmyError, ConnectionId};
 use lemmy_websocket::LemmyContext;
 
@@ -59,8 +59,9 @@ impl PerformCrud for GetPosts {
     let saved_only = data.saved_only;
 
     let mut posts = blocking(context.pool(), move |conn| {
-      PostQueryBuilder::create(conn)
-        .listing_type(listing_type)
+      PostQuery::builder()
+        .conn(conn)
+        .listing_type(Some(listing_type))
         .sort(sort)
         .show_nsfw(show_nsfw)
         .show_bot_accounts(show_bot_accounts)
@@ -71,6 +72,7 @@ impl PerformCrud for GetPosts {
         .my_person_id(person_id)
         .page(page)
         .limit(limit)
+        .build()
         .list()
     })
     .await?
diff --git a/crates/api_crud/src/private_message/read.rs b/crates/api_crud/src/private_message/read.rs
index bb09be2d..ebd9ddda 100644
--- a/crates/api_crud/src/private_message/read.rs
+++ b/crates/api_crud/src/private_message/read.rs
@@ -5,7 +5,7 @@ use lemmy_api_common::{
   utils::{blocking, get_local_user_view_from_jwt},
 };
 use lemmy_db_schema::traits::DeleteableOrRemoveable;
-use lemmy_db_views::private_message_view::PrivateMessageQueryBuilder;
+use lemmy_db_views::private_message_view::PrivateMessageQuery;
 use lemmy_utils::{error::LemmyError, ConnectionId};
 use lemmy_websocket::LemmyContext;
 
@@ -28,10 +28,13 @@ impl PerformCrud for GetPrivateMessages {
     let limit = data.limit;
     let unread_only = data.unread_only;
     let mut messages = blocking(context.pool(), move |conn| {
-      PrivateMessageQueryBuilder::create(conn, person_id)
+      PrivateMessageQuery::builder()
+        .conn(conn)
+        .recipient_id(person_id)
         .page(page)
         .limit(limit)
         .unread_only(unread_only)
+        .build()
         .list()
     })
     .await??;
diff --git a/crates/api_crud/src/user/read.rs b/crates/api_crud/src/user/read.rs
index 2f537925..7ec52501 100644
--- a/crates/api_crud/src/user/read.rs
+++ b/crates/api_crud/src/user/read.rs
@@ -6,7 +6,7 @@ use lemmy_api_common::{
 };
 use lemmy_apub::{fetcher::resolve_actor_identifier, objects::person::ApubPerson};
 use lemmy_db_schema::{source::person::Person, utils::post_to_comment_sort_type};
-use lemmy_db_views::{comment_view::CommentQueryBuilder, post_view::PostQueryBuilder};
+use lemmy_db_views::{comment_view::CommentQuery, post_view::PostQuery};
 use lemmy_db_views_actor::structs::{CommunityModeratorView, PersonViewSafe};
 use lemmy_utils::{error::LemmyError, ConnectionId};
 use lemmy_websocket::LemmyContext;
@@ -74,7 +74,8 @@ impl PerformCrud for GetPersonDetails {
     let community_id = data.community_id;
 
     let (posts, comments) = blocking(context.pool(), move |conn| {
-      let mut posts_query = PostQueryBuilder::create(conn)
+      let posts_query = PostQuery::builder()
+        .conn(conn)
         .sort(sort)
         .show_nsfw(show_nsfw)
         .show_bot_accounts(show_bot_accounts)
@@ -85,7 +86,8 @@ impl PerformCrud for GetPersonDetails {
         .page(page)
         .limit(limit);
 
-      let mut comments_query = CommentQueryBuilder::create(conn)
+      let comments_query = CommentQuery::builder()
+        .conn(conn)
         .my_person_id(person_id)
         .show_bot_accounts(show_bot_accounts)
         .sort(sort.map(post_to_comment_sort_type))
@@ -96,13 +98,20 @@ impl PerformCrud for GetPersonDetails {
 
       // If its saved only, you don't care what creator it was
       // Or, if its not saved, then you only want it for that specific creator
-      if !saved_only.unwrap_or(false) {
-        posts_query = posts_query.creator_id(person_details_id);
-        comments_query = comments_query.creator_id(person_details_id);
-      }
-
-      let posts = posts_query.list()?;
-      let comments = comments_query.list()?;
+      let (posts, comments) = if !saved_only.unwrap_or(false) {
+        (
+          posts_query
+            .creator_id(Some(person_details_id))
+            .build()
+            .list()?,
+          comments_query
+            .creator_id(Some(person_details_id))
+            .build()
+            .list()?,
+        )
+      } else {
+        (posts_query.build().list()?, comments_query.build().list()?)
+      };
 
       Ok((posts, comments)) as Result<_, LemmyError>
     })
diff --git a/crates/db_schema/src/traits.rs b/crates/db_schema/src/traits.rs
index 22cf669d..f2f6ae92 100644
--- a/crates/db_schema/src/traits.rs
+++ b/crates/db_schema/src/traits.rs
@@ -130,22 +130,6 @@ pub trait DeleteableOrRemoveable {
   fn blank_out_deleted_or_removed_info(self) -> Self;
 }
 
-pub trait MaybeOptional<T> {
-  fn get_optional(self) -> Option<T>;
-}
-
-impl<T> MaybeOptional<T> for T {
-  fn get_optional(self) -> Option<T> {
-    Some(self)
-  }
-}
-
-impl<T> MaybeOptional<T> for Option<T> {
-  fn get_optional(self) -> Option<T> {
-    self
-  }
-}
-
 pub trait ToSafe {
   type SafeColumns;
   fn safe_columns_tuple() -> Self::SafeColumns;
diff --git a/crates/db_views/Cargo.toml b/crates/db_views/Cargo.toml
index 75daa09a..85448b26 100644
--- a/crates/db_views/Cargo.toml
+++ b/crates/db_views/Cargo.toml
@@ -20,6 +20,7 @@ diesel = { version = "1.4.8", features = ["postgres","chrono","r2d2","serde_json
 serde = { version = "1.0.136", features = ["derive"] }
 tracing = { version = "0.1.32", optional = true }
 diesel_ltree = "0.2.7"
+typed-builder = "0.10.0"
 
 [dev-dependencies]
 serial_test = "0.6.0"
diff --git a/crates/db_views/src/comment_report_view.rs b/crates/db_views/src/comment_report_view.rs
index ed0a5c18..8c9c124d 100644
--- a/crates/db_views/src/comment_report_view.rs
+++ b/crates/db_views/src/comment_report_view.rs
@@ -23,9 +23,10 @@ use lemmy_db_schema::{
     person::{Person, PersonAlias1, PersonAlias2, PersonSafe, PersonSafeAlias1, PersonSafeAlias2},
     post::Post,
   },
-  traits::{MaybeOptional, ToSafe, ViewToVec},
+  traits::{ToSafe, ViewToVec},
   utils::limit_and_offset,
 };
+use typed_builder::TypedBuilder;
 
 type CommentReportViewTuple = (
   CommentReport,
@@ -159,9 +160,14 @@ impl CommentReportView {
   }
 }
 
-pub struct CommentReportQueryBuilder<'a> {
+#[derive(TypedBuilder)]
+#[builder(field_defaults(default))]
+pub struct CommentReportQuery<'a> {
+  #[builder(!default)]
   conn: &'a PgConnection,
+  #[builder(!default)]
   my_person_id: PersonId,
+  #[builder(!default)]
   admin: bool,
   community_id: Option<CommunityId>,
   page: Option<i64>,
@@ -169,39 +175,7 @@ pub struct CommentReportQueryBuilder<'a> {
   unresolved_only: Option<bool>,
 }
 
-impl<'a> CommentReportQueryBuilder<'a> {
-  pub fn create(conn: &'a PgConnection, my_person_id: PersonId, admin: bool) -> Self {
-    CommentReportQueryBuilder {
-      conn,
-      my_person_id,
-      admin,
-      community_id: None,
-      page: None,
-      limit: None,
-      unresolved_only: Some(true),
-    }
-  }
-
-  pub fn community_id<T: MaybeOptional<CommunityId>>(mut self, community_id: T) -> Self {
-    self.community_id = community_id.get_optional();
-    self
-  }
-
-  pub fn page<T: MaybeOptional<i64>>(mut self, page: T) -> Self {
-    self.page = page.get_optional();
-    self
-  }
-
-  pub fn limit<T: MaybeOptional<i64>>(mut self, limit: T) -> Self {
-    self.limit = limit.get_optional();
-    self
-  }
-
-  pub fn unresolved_only<T: MaybeOptional<bool>>(mut self, unresolved_only: T) -> Self {
-    self.unresolved_only = unresolved_only.get_optional();
-    self
-  }
-
+impl<'a> CommentReportQuery<'a> {
   pub fn list(self) -> Result<Vec<CommentReportView>, Error> {
     let mut query = comment_report::table
       .inner_join(comment::table)
@@ -252,7 +226,7 @@ impl<'a> CommentReportQueryBuilder<'a> {
       query = query.filter(post::community_id.eq(community_id));
     }
 
-    if self.unresolved_only.unwrap_or(false) {
+    if self.unresolved_only.unwrap_or(true) {
       query = query.filter(comment_report::resolved.eq(false));
     }
 
@@ -286,18 +260,18 @@ impl ViewToVec for CommentReportView {
   type DbTuple = CommentReportViewTuple;
   fn from_tuple_to_vec(items: Vec<Self::DbTuple>) -> Vec<Self> {
     items
-      .iter()
+      .into_iter()
       .map(|a| Self {
-        comment_report: a.0.to_owned(),
-        comment: a.1.to_owned(),
-        post: a.2.to_owned(),
-        community: a.3.to_owned(),
-        creator: a.4.to_owned(),
-        comment_creator: a.5.to_owned(),
-        counts: a.6.to_owned(),
+        comment_report: a.0,
+        comment: a.1,
+        post: a.2,
+        community: a.3,
+        creator: a.4,
+        comment_creator: a.5,
+        counts: a.6,
         creator_banned_from_community: a.7.is_some(),
         my_vote: a.8,
-        resolver: a.9.to_owned(),
+        resolver: a.9,
       })
       .collect::<Vec<Self>>()
   }
@@ -305,7 +279,7 @@ impl ViewToVec for CommentReportView {
 
 #[cfg(test)]
 mod tests {
-  use crate::comment_report_view::{CommentReportQueryBuilder, CommentReportView};
+  use crate::comment_report_view::{CommentReportQuery, CommentReportView};
   use lemmy_db_schema::{
     aggregates::structs::CommentAggregates,
     source::{comment::*, comment_report::*, community::*, person::*, post::*},
@@ -504,7 +478,11 @@ mod tests {
     };
 
     // Do a batch read of timmys reports
-    let reports = CommentReportQueryBuilder::create(&conn, inserted_timmy.id, false)
+    let reports = CommentReportQuery::builder()
+      .conn(&conn)
+      .my_person_id(inserted_timmy.id)
+      .admin(false)
+      .build()
       .list()
       .unwrap();
 
@@ -566,10 +544,15 @@ mod tests {
 
     // Do a batch read of timmys reports
     // It should only show saras, which is unresolved
-    let reports_after_resolve = CommentReportQueryBuilder::create(&conn, inserted_timmy.id, false)
+    let reports_after_resolve = CommentReportQuery::builder()
+      .conn(&conn)
+      .my_person_id(inserted_timmy.id)
+      .admin(false)
+      .build()
       .list()
       .unwrap();
     assert_eq!(reports_after_resolve[0], expected_sara_report_view);
+    assert_eq!(reports_after_resolve.len(), 1);
 
     // Make sure the counts are correct
     let report_count_after_resolved =
diff --git a/crates/db_views/src/comment_view.rs b/crates/db_views/src/comment_view.rs
index f3d5c798..05c6a698 100644
--- a/crates/db_views/src/comment_view.rs
+++ b/crates/db_views/src/comment_view.rs
@@ -24,11 +24,12 @@ use lemmy_db_schema::{
     person_block::PersonBlock,
     post::Post,
   },
-  traits::{MaybeOptional, ToSafe, ViewToVec},
+  traits::{ToSafe, ViewToVec},
   utils::{functions::hot_rank, fuzzy_search, limit_and_offset_unlimited},
   CommentSortType,
   ListingType,
 };
+use typed_builder::TypedBuilder;
 
 type CommentViewTuple = (
   Comment,
@@ -146,7 +147,10 @@ impl CommentView {
   }
 }
 
-pub struct CommentQueryBuilder<'a> {
+#[derive(TypedBuilder)]
+#[builder(field_defaults(default))]
+pub struct CommentQuery<'a> {
+  #[builder(!default)]
   conn: &'a PgConnection,
   listing_type: Option<ListingType>,
   sort: Option<CommentSortType>,
@@ -164,97 +168,7 @@ pub struct CommentQueryBuilder<'a> {
   max_depth: Option<i32>,
 }
 
-impl<'a> CommentQueryBuilder<'a> {
-  pub fn create(conn: &'a PgConnection) -> Self {
-    CommentQueryBuilder {
-      conn,
-      listing_type: Some(ListingType::All),
-      sort: None,
-      community_id: None,
-      community_actor_id: None,
-      post_id: None,
-      parent_path: None,
-      creator_id: None,
-      my_person_id: None,
-      search_term: None,
-      saved_only: None,
-      show_bot_accounts: None,
-      page: None,
-      limit: None,
-      max_depth: None,
-    }
-  }
-
-  pub fn listing_type<T: MaybeOptional<ListingType>>(mut self, listing_type: T) -> Self {
-    self.listing_type = listing_type.get_optional();
-    self
-  }
-
-  pub fn sort<T: MaybeOptional<CommentSortType>>(mut self, sort: T) -> Self {
-    self.sort = sort.get_optional();
-    self
-  }
-
-  pub fn post_id<T: MaybeOptional<PostId>>(mut self, post_id: T) -> Self {
-    self.post_id = post_id.get_optional();
-    self
-  }
-
-  pub fn creator_id<T: MaybeOptional<PersonId>>(mut self, creator_id: T) -> Self {
-    self.creator_id = creator_id.get_optional();
-    self
-  }
-
-  pub fn community_id<T: MaybeOptional<CommunityId>>(mut self, community_id: T) -> Self {
-    self.community_id = community_id.get_optional();
-    self
-  }
-
-  pub fn my_person_id<T: MaybeOptional<PersonId>>(mut self, my_person_id: T) -> Self {
-    self.my_person_id = my_person_id.get_optional();
-    self
-  }
-
-  pub fn community_actor_id<T: MaybeOptional<DbUrl>>(mut self, community_actor_id: T) -> Self {
-    self.community_actor_id = community_actor_id.get_optional();
-    self
-  }
-
-  pub fn search_term<T: MaybeOptional<String>>(mut self, search_term: T) -> Self {
-    self.search_term = search_term.get_optional();
-    self
-  }
-
-  pub fn saved_only<T: MaybeOptional<bool>>(mut self, saved_only: T) -> Self {
-    self.saved_only = saved_only.get_optional();
-    self
-  }
-
-  pub fn show_bot_accounts<T: MaybeOptional<bool>>(mut self, show_bot_accounts: T) -> Self {
-    self.show_bot_accounts = show_bot_accounts.get_optional();
-    self
-  }
-
-  pub fn parent_path<T: MaybeOptional<Ltree>>(mut self, parent_path: T) -> Self {
-    self.parent_path = parent_path.get_optional();
-    self
-  }
-
-  pub fn page<T: MaybeOptional<i64>>(mut self, page: T) -> Self {
-    self.page = page.get_optional();
-    self
-  }
-
-  pub fn limit<T: MaybeOptional<i64>>(mut self, limit: T) -> Self {
-    self.limit = limit.get_optional();
-    self
-  }
-
-  pub fn max_depth<T: MaybeOptional<i32>>(mut self, max_depth: T) -> Self {
-    self.max_depth = max_depth.get_optional();
-    self
-  }
-
+impl<'a> CommentQuery<'a> {
   pub fn list(self) -> Result<Vec<CommentView>, Error> {
     use diesel::dsl::*;
 
@@ -437,13 +351,13 @@ impl ViewToVec for CommentView {
   type DbTuple = CommentViewTuple;
   fn from_tuple_to_vec(items: Vec<Self::DbTuple>) -> Vec<Self> {
     items
-      .iter()
+      .into_iter()
       .map(|a| Self {
-        comment: a.0.to_owned(),
-        creator: a.1.to_owned(),
-        post: a.2.to_owned(),
-        community: a.3.to_owned(),
-        counts: a.4.to_owned(),
+        comment: a.0,
+        creator: a.1,
+        post: a.2,
+        community: a.3,
+        counts: a.4,
         creator_banned_from_community: a.5.is_some(),
         subscribed: CommunityFollower::to_subscribed_type(&a.6),
         saved: a.7.is_some(),
@@ -691,8 +605,10 @@ mod tests {
     let mut expected_comment_view_with_person = expected_comment_view_no_person.to_owned();
     expected_comment_view_with_person.my_vote = Some(1);
 
-    let read_comment_views_no_person = CommentQueryBuilder::create(&conn)
-      .post_id(inserted_post.id)
+    let read_comment_views_no_person = CommentQuery::builder()
+      .conn(&conn)
+      .post_id(Some(inserted_post.id))
+      .build()
       .list()
       .unwrap();
 
@@ -701,9 +617,11 @@ mod tests {
       read_comment_views_no_person[0]
     );
 
-    let read_comment_views_with_person = CommentQueryBuilder::create(&conn)
-      .post_id(inserted_post.id)
-      .my_person_id(inserted_person.id)
+    let read_comment_views_with_person = CommentQuery::builder()
+      .conn(&conn)
+      .post_id(Some(inserted_post.id))
+      .my_person_id(Some(inserted_person.id))
+      .build()
       .list()
       .unwrap();
 
@@ -722,16 +640,20 @@ mod tests {
     assert!(read_comment_from_blocked_person.creator_blocked);
 
     let top_path = inserted_comment_0.path;
-    let read_comment_views_top_path = CommentQueryBuilder::create(&conn)
-      .post_id(inserted_post.id)
-      .parent_path(top_path)
+    let read_comment_views_top_path = CommentQuery::builder()
+      .conn(&conn)
+      .post_id(Some(inserted_post.id))
+      .parent_path(Some(top_path))
+      .build()
       .list()
       .unwrap();
 
     let child_path = inserted_comment_1.to_owned().path;
-    let read_comment_views_child_path = CommentQueryBuilder::create(&conn)
-      .post_id(inserted_post.id)
-      .parent_path(child_path)
+    let read_comment_views_child_path = CommentQuery::builder()
+      .conn(&conn)
+      .post_id(Some(inserted_post.id))
+      .parent_path(Some(child_path))
+      .build()
       .list()
       .unwrap();
 
@@ -747,9 +669,11 @@ mod tests {
     assert!(child_comments.contains(&inserted_comment_1));
     assert!(!child_comments.contains(&inserted_comment_2));
 
-    let read_comment_views_top_max_depth = CommentQueryBuilder::create(&conn)
-      .post_id(inserted_post.id)
-      .max_depth(1)
+    let read_comment_views_top_max_depth = CommentQuery::builder()
+      .conn(&conn)
+      .post_id(Some(inserted_post.id))
+      .max_depth(Some(1))
+      .build()
       .list()
       .unwrap();
 
@@ -761,11 +685,13 @@ mod tests {
     assert_eq!(1, read_comment_views_top_max_depth.len());
 
     let child_path = inserted_comment_1.path;
-    let read_comment_views_parent_max_depth = CommentQueryBuilder::create(&conn)
-      .post_id(inserted_post.id)
-      .parent_path(child_path)
-      .max_depth(1)
-      .sort(CommentSortType::New)
+    let read_comment_views_parent_max_depth = CommentQuery::builder()
+      .conn(&conn)
+      .post_id(Some(inserted_post.id))
+      .parent_path(Some(child_path))
+      .max_depth(Some(1))
+      .sort(Some(CommentSortType::New))
+      .build()
       .list()
       .unwrap();
 
diff --git a/crates/db_views/src/post_report_view.rs b/crates/db_views/src/post_report_view.rs
index 1991fab2..1a519fff 100644
--- a/crates/db_views/src/post_report_view.rs
+++ b/crates/db_views/src/post_report_view.rs
@@ -21,9 +21,10 @@ use lemmy_db_schema::{
     post::Post,
     post_report::PostReport,
   },
-  traits::{MaybeOptional, ToSafe, ViewToVec},
+  traits::{ToSafe, ViewToVec},
   utils::limit_and_offset,
 };
+use typed_builder::TypedBuilder;
 
 type PostReportViewTuple = (
   PostReport,
@@ -148,9 +149,14 @@ impl PostReportView {
   }
 }
 
-pub struct PostReportQueryBuilder<'a> {
+#[derive(TypedBuilder)]
+#[builder(field_defaults(default))]
+pub struct PostReportQuery<'a> {
+  #[builder(!default)]
   conn: &'a PgConnection,
+  #[builder(!default)]
   my_person_id: PersonId,
+  #[builder(!default)]
   admin: bool,
   community_id: Option<CommunityId>,
   page: Option<i64>,
@@ -158,39 +164,7 @@ pub struct PostReportQueryBuilder<'a> {
   unresolved_only: Option<bool>,
 }
 
-impl<'a> PostReportQueryBuilder<'a> {
-  pub fn create(conn: &'a PgConnection, my_person_id: PersonId, admin: bool) -> Self {
-    PostReportQueryBuilder {
-      conn,
-      my_person_id,
-      admin,
-      community_id: None,
-      page: None,
-      limit: None,
-      unresolved_only: Some(true),
-    }
-  }
-
-  pub fn community_id<T: MaybeOptional<CommunityId>>(mut self, community_id: T) -> Self {
-    self.community_id = community_id.get_optional();
-    self
-  }
-
-  pub fn page<T: MaybeOptional<i64>>(mut self, page: T) -> Self {
-    self.page = page.get_optional();
-    self
-  }
-
-  pub fn limit<T: MaybeOptional<i64>>(mut self, limit: T) -> Self {
-    self.limit = limit.get_optional();
-    self
-  }
-
-  pub fn unresolved_only<T: MaybeOptional<bool>>(mut self, unresolved_only: T) -> Self {
-    self.unresolved_only = unresolved_only.get_optional();
-    self
-  }
-
+impl<'a> PostReportQuery<'a> {
   pub fn list(self) -> Result<Vec<PostReportView>, Error> {
     let mut query = post_report::table
       .inner_join(post::table)
@@ -237,7 +211,7 @@ impl<'a> PostReportQueryBuilder<'a> {
       query = query.filter(post::community_id.eq(community_id));
     }
 
-    if self.unresolved_only.unwrap_or(false) {
+    if self.unresolved_only.unwrap_or(true) {
       query = query.filter(post_report::resolved.eq(false));
     }
 
@@ -271,17 +245,17 @@ impl ViewToVec for PostReportView {
   type DbTuple = PostReportViewTuple;
   fn from_tuple_to_vec(items: Vec<Self::DbTuple>) -> Vec<Self> {
     items
-      .iter()
+      .into_iter()
       .map(|a| Self {
-        post_report: a.0.to_owned(),
-        post: a.1.to_owned(),
-        community: a.2.to_owned(),
-        creator: a.3.to_owned(),
-        post_creator: a.4.to_owned(),
+        post_report: a.0,
+        post: a.1,
+        community: a.2,
+        creator: a.3,
+        post_creator: a.4,
         creator_banned_from_community: a.5.is_some(),
         my_vote: a.6,
-        counts: a.7.to_owned(),
-        resolver: a.8.to_owned(),
+        counts: a.7,
+        resolver: a.8,
       })
       .collect::<Vec<Self>>()
   }
@@ -289,7 +263,7 @@ impl ViewToVec for PostReportView {
 
 #[cfg(test)]
 mod tests {
-  use crate::post_report_view::{PostReportQueryBuilder, PostReportView};
+  use crate::post_report_view::{PostReportQuery, PostReportView};
   use lemmy_db_schema::{
     aggregates::structs::PostAggregates,
     source::{
@@ -491,7 +465,11 @@ mod tests {
     };
 
     // Do a batch read of timmys reports
-    let reports = PostReportQueryBuilder::create(&conn, inserted_timmy.id, false)
+    let reports = PostReportQuery::builder()
+      .conn(&conn)
+      .my_person_id(inserted_timmy.id)
+      .admin(false)
+      .build()
       .list()
       .unwrap();
 
@@ -551,7 +529,11 @@ mod tests {
 
     // Do a batch read of timmys reports
     // It should only show saras, which is unresolved
-    let reports_after_resolve = PostReportQueryBuilder::create(&conn, inserted_timmy.id, false)
+    let reports_after_resolve = PostReportQuery::builder()
+      .conn(&conn)
+      .my_person_id(inserted_timmy.id)
+      .admin(false)
+      .build()
       .list()
       .unwrap();
     assert_eq!(reports_after_resolve[0], expected_sara_report_view);
diff --git a/crates/db_views/src/post_view.rs b/crates/db_views/src/post_view.rs
index 46424010..c9b47217 100644
--- a/crates/db_views/src/post_view.rs
+++ b/crates/db_views/src/post_view.rs
@@ -22,12 +22,13 @@ use lemmy_db_schema::{
     person_block::PersonBlock,
     post::{Post, PostRead, PostSaved},
   },
-  traits::{MaybeOptional, ToSafe, ViewToVec},
+  traits::{ToSafe, ViewToVec},
   utils::{functions::hot_rank, fuzzy_search, limit_and_offset},
   ListingType,
   SortType,
 };
 use tracing::debug;
+use typed_builder::TypedBuilder;
 
 type PostViewTuple = (
   Post,
@@ -151,7 +152,10 @@ impl PostView {
   }
 }
 
-pub struct PostQueryBuilder<'a> {
+#[derive(TypedBuilder)]
+#[builder(field_defaults(default))]
+pub struct PostQuery<'a> {
+  #[builder(!default)]
   conn: &'a PgConnection,
   listing_type: Option<ListingType>,
   sort: Option<SortType>,
@@ -169,97 +173,7 @@ pub struct PostQueryBuilder<'a> {
   limit: Option<i64>,
 }
 
-impl<'a> PostQueryBuilder<'a> {
-  pub fn create(conn: &'a PgConnection) -> Self {
-    PostQueryBuilder {
-      conn,
-      listing_type: Some(ListingType::All),
-      sort: None,
-      creator_id: None,
-      community_id: None,
-      community_actor_id: None,
-      my_person_id: None,
-      search_term: None,
-      url_search: None,
-      show_nsfw: None,
-      show_bot_accounts: None,
-      show_read_posts: None,
-      saved_only: None,
-      page: None,
-      limit: None,
-    }
-  }
-
-  pub fn listing_type<T: MaybeOptional<ListingType>>(mut self, listing_type: T) -> Self {
-    self.listing_type = listing_type.get_optional();
-    self
-  }
-
-  pub fn sort<T: MaybeOptional<SortType>>(mut self, sort: T) -> Self {
-    self.sort = sort.get_optional();
-    self
-  }
-
-  pub fn community_id<T: MaybeOptional<CommunityId>>(mut self, community_id: T) -> Self {
-    self.community_id = community_id.get_optional();
-    self
-  }
-
-  pub fn my_person_id<T: MaybeOptional<PersonId>>(mut self, my_person_id: T) -> Self {
-    self.my_person_id = my_person_id.get_optional();
-    self
-  }
-
-  pub fn community_actor_id<T: MaybeOptional<DbUrl>>(mut self, community_actor_id: T) -> Self {
-    self.community_actor_id = community_actor_id.get_optional();
-    self
-  }
-
-  pub fn creator_id<T: MaybeOptional<PersonId>>(mut self, creator_id: T) -> Self {
-    self.creator_id = creator_id.get_optional();
-    self
-  }
-
-  pub fn search_term<T: MaybeOptional<String>>(mut self, search_term: T) -> Self {
-    self.search_term = search_term.get_optional();
-    self
-  }
-
-  pub fn url_search<T: MaybeOptional<String>>(mut self, url_search: T) -> Self {
-    self.url_search = url_search.get_optional();
-    self
-  }
-
-  pub fn show_nsfw<T: MaybeOptional<bool>>(mut self, show_nsfw: T) -> Self {
-    self.show_nsfw = show_nsfw.get_optional();
-    self
-  }
-
-  pub fn show_bot_accounts<T: MaybeOptional<bool>>(mut self, show_bot_accounts: T) -> Self {
-    self.show_bot_accounts = show_bot_accounts.get_optional();
-    self
-  }
-
-  pub fn show_read_posts<T: MaybeOptional<bool>>(mut self, show_read_posts: T) -> Self {
-    self.show_read_posts = show_read_posts.get_optional();
-    self
-  }
-
-  pub fn saved_only<T: MaybeOptional<bool>>(mut self, saved_only: T) -> Self {
-    self.saved_only = saved_only.get_optional();
-    self
-  }
-
-  pub fn page<T: MaybeOptional<i64>>(mut self, page: T) -> Self {
-    self.page = page.get_optional();
-    self
-  }
-
-  pub fn limit<T: MaybeOptional<i64>>(mut self, limit: T) -> Self {
-    self.limit = limit.get_optional();
-    self
-  }
-
+impl<'a> PostQuery<'a> {
   pub fn list(self) -> Result<Vec<PostView>, Error> {
     use diesel::dsl::*;
 
@@ -477,13 +391,13 @@ impl ViewToVec for PostView {
   type DbTuple = PostViewTuple;
   fn from_tuple_to_vec(items: Vec<Self::DbTuple>) -> Vec<Self> {
     items
-      .iter()
+      .into_iter()
       .map(|a| Self {
-        post: a.0.to_owned(),
-        creator: a.1.to_owned(),
-        community: a.2.to_owned(),
+        post: a.0,
+        creator: a.1,
+        community: a.2,
         creator_banned_from_community: a.3.is_some(),
-        counts: a.4.to_owned(),
+        counts: a.4,
         subscribed: CommunityFollower::to_subscribed_type(&a.5),
         saved: a.6.is_some(),
         read: a.7.is_some(),
@@ -496,7 +410,7 @@ impl ViewToVec for PostView {
 
 #[cfg(test)]
 mod tests {
-  use crate::post_view::{PostQueryBuilder, PostView};
+  use crate::post_view::{PostQuery, PostView};
   use lemmy_db_schema::{
     aggregates::structs::PostAggregates,
     source::{
@@ -610,17 +524,21 @@ mod tests {
       score: 1,
     };
 
-    let read_post_listings_with_person = PostQueryBuilder::create(&conn)
-      .sort(SortType::New)
-      .show_bot_accounts(false)
-      .community_id(inserted_community.id)
-      .my_person_id(inserted_person.id)
+    let read_post_listings_with_person = PostQuery::builder()
+      .conn(&conn)
+      .sort(Some(SortType::New))
+      .show_bot_accounts(Some(false))
+      .community_id(Some(inserted_community.id))
+      .my_person_id(Some(inserted_person.id))
+      .build()
       .list()
       .unwrap();
 
-    let read_post_listings_no_person = PostQueryBuilder::create(&conn)
-      .sort(SortType::New)
-      .community_id(inserted_community.id)
+    let read_post_listings_no_person = PostQuery::builder()
+      .conn(&conn)
+      .sort(Some(SortType::New))
+      .community_id(Some(inserted_community.id))
+      .build()
       .list()
       .unwrap();
 
@@ -717,11 +635,13 @@ mod tests {
     };
     CommunityBlock::block(&conn, &community_block).unwrap();
 
-    let read_post_listings_with_person_after_block = PostQueryBuilder::create(&conn)
-      .sort(SortType::New)
-      .show_bot_accounts(false)
-      .community_id(inserted_community.id)
-      .my_person_id(inserted_person.id)
+    let read_post_listings_with_person_after_block = PostQuery::builder()
+      .conn(&conn)
+      .sort(Some(SortType::New))
+      .show_bot_accounts(Some(false))
+      .community_id(Some(inserted_community.id))
+      .my_person_id(Some(inserted_person.id))
+      .build()
       .list()
       .unwrap();
 
diff --git a/crates/db_views/src/private_message_view.rs b/crates/db_views/src/private_message_view.rs
index 185bd302..151c50aa 100644
--- a/crates/db_views/src/private_message_view.rs
+++ b/crates/db_views/src/private_message_view.rs
@@ -7,10 +7,11 @@ use lemmy_db_schema::{
     person::{Person, PersonAlias1, PersonSafe, PersonSafeAlias1},
     private_message::PrivateMessage,
   },
-  traits::{MaybeOptional, ToSafe, ViewToVec},
+  traits::{ToSafe, ViewToVec},
   utils::limit_and_offset,
 };
 use tracing::debug;
+use typed_builder::TypedBuilder;
 
 type PrivateMessageViewTuple = (PrivateMessage, PersonSafe, PersonSafeAlias1);
 
@@ -47,40 +48,19 @@ impl PrivateMessageView {
   }
 }
 
-pub struct PrivateMessageQueryBuilder<'a> {
+#[derive(TypedBuilder)]
+#[builder(field_defaults(default))]
+pub struct PrivateMessageQuery<'a> {
+  #[builder(!default)]
   conn: &'a PgConnection,
+  #[builder(!default)]
   recipient_id: PersonId,
   unread_only: Option<bool>,
   page: Option<i64>,
   limit: Option<i64>,
 }
 
-impl<'a> PrivateMessageQueryBuilder<'a> {
-  pub fn create(conn: &'a PgConnection, recipient_id: PersonId) -> Self {
-    PrivateMessageQueryBuilder {
-      conn,
-      recipient_id,
-      unread_only: None,
-      page: None,
-      limit: None,
-    }
-  }
-
-  pub fn unread_only<T: MaybeOptional<bool>>(mut self, unread_only: T) -> Self {
-    self.unread_only = unread_only.get_optional();
-    self
-  }
-
-  pub fn page<T: MaybeOptional<i64>>(mut self, page: T) -> Self {
-    self.page = page.get_optional();
-    self
-  }
-
-  pub fn limit<T: MaybeOptional<i64>>(mut self, limit: T) -> Self {
-    self.limit = limit.get_optional();
-    self
-  }
-
+impl<'a> PrivateMessageQuery<'a> {
   pub fn list(self) -> Result<Vec<PrivateMessageView>, Error> {
     let mut query = private_message::table
       .inner_join(person::table.on(private_message::creator_id.eq(person::id)))
@@ -130,11 +110,11 @@ impl ViewToVec for PrivateMessageView {
   type DbTuple = PrivateMessageViewTuple;
   fn from_tuple_to_vec(items: Vec<Self::DbTuple>) -> Vec<Self> {
     items
-      .iter()
+      .into_iter()
       .map(|a| Self {
-        private_message: a.0.to_owned(),
-        creator: a.1.to_owned(),
-        recipient: a.2.to_owned(),
+        private_message: a.0,
+        creator: a.1,
+        recipient: a.2,
       })
       .collect::<Vec<Self>>()
   }
diff --git a/crates/db_views/src/registration_application_view.rs b/crates/db_views/src/registration_application_view.rs
index 02d1ec24..77ad416b 100644
--- a/crates/db_views/src/registration_application_view.rs
+++ b/crates/db_views/src/registration_application_view.rs
@@ -7,9 +7,10 @@ use lemmy_db_schema::{
     person::{Person, PersonAlias1, PersonSafe, PersonSafeAlias1},
     registration_application::RegistrationApplication,
   },
-  traits::{MaybeOptional, ToSafe, ToSafeSettings, ViewToVec},
+  traits::{ToSafe, ToSafeSettings, ViewToVec},
   utils::limit_and_offset,
 };
+use typed_builder::TypedBuilder;
 
 type RegistrationApplicationViewTuple = (
   RegistrationApplication,
@@ -70,7 +71,10 @@ impl RegistrationApplicationView {
   }
 }
 
-pub struct RegistrationApplicationQueryBuilder<'a> {
+#[derive(TypedBuilder)]
+#[builder(field_defaults(default))]
+pub struct RegistrationApplicationQuery<'a> {
+  #[builder(!default)]
   conn: &'a PgConnection,
   unread_only: Option<bool>,
   verified_email_only: Option<bool>,
@@ -78,37 +82,7 @@ pub struct RegistrationApplicationQueryBuilder<'a> {
   limit: Option<i64>,
 }
 
-impl<'a> RegistrationApplicationQueryBuilder<'a> {
-  pub fn create(conn: &'a PgConnection) -> Self {
-    RegistrationApplicationQueryBuilder {
-      conn,
-      unread_only: None,
-      verified_email_only: None,
-      page: None,
-      limit: None,
-    }
-  }
-
-  pub fn unread_only<T: MaybeOptional<bool>>(mut self, unread_only: T) -> Self {
-    self.unread_only = unread_only.get_optional();
-    self
-  }
-
-  pub fn verified_email_only<T: MaybeOptional<bool>>(mut self, verified_email_only: T) -> Self {
-    self.verified_email_only = verified_email_only.get_optional();
-    self
-  }
-
-  pub fn page<T: MaybeOptional<i64>>(mut self, page: T) -> Self {
-    self.page = page.get_optional();
-    self
-  }
-
-  pub fn limit<T: MaybeOptional<i64>>(mut self, limit: T) -> Self {
-    self.limit = limit.get_optional();
-    self
-  }
-
+impl<'a> RegistrationApplicationQuery<'a> {
   pub fn list(self) -> Result<Vec<RegistrationApplicationView>, Error> {
     let mut query = registration_application::table
       .inner_join(local_user::table.on(registration_application::local_user_id.eq(local_user::id)))
@@ -151,12 +125,12 @@ impl ViewToVec for RegistrationApplicationView {
   type DbTuple = RegistrationApplicationViewTuple;
   fn from_tuple_to_vec(items: Vec<Self::DbTuple>) -> Vec<Self> {
     items
-      .iter()
+      .into_iter()
       .map(|a| Self {
-        registration_application: a.0.to_owned(),
-        creator_local_user: a.1.to_owned(),
-        creator: a.2.to_owned(),
-        admin: a.3.to_owned(),
+        registration_application: a.0,
+        creator_local_user: a.1,
+        creator: a.2,
+        admin: a.3,
       })
       .collect::<Vec<Self>>()
   }
@@ -165,7 +139,7 @@ impl ViewToVec for RegistrationApplicationView {
 #[cfg(test)]
 mod tests {
   use crate::registration_application_view::{
-    RegistrationApplicationQueryBuilder,
+    RegistrationApplicationQuery,
     RegistrationApplicationView,
   };
   use lemmy_db_schema::{
@@ -302,8 +276,10 @@ mod tests {
     assert_eq!(read_sara_app_view, expected_sara_app_view);
 
     // Do a batch read of the applications
-    let apps = RegistrationApplicationQueryBuilder::create(&conn)
-      .unread_only(true)
+    let apps = RegistrationApplicationQuery::builder()
+      .conn(&conn)
+      .unread_only(Some(true))
+      .build()
       .list()
       .unwrap();
 
@@ -369,8 +345,10 @@ mod tests {
 
     // Do a batch read of apps again
     // It should show only jessicas which is unresolved
-    let apps_after_resolve = RegistrationApplicationQueryBuilder::create(&conn)
-      .unread_only(true)
+    let apps_after_resolve = RegistrationApplicationQuery::builder()
+      .conn(&conn)
+      .unread_only(Some(true))
+      .build()
       .list()
       .unwrap();
     assert_eq!(apps_after_resolve, vec![read_jess_app_view]);
@@ -381,7 +359,9 @@ mod tests {
     assert_eq!(unread_count_after_approve, 1);
 
     // Make sure the not undenied_only has all the apps
-    let all_apps = RegistrationApplicationQueryBuilder::create(&conn)
+    let all_apps = RegistrationApplicationQuery::builder()
+      .conn(&conn)
+      .build()
       .list()
       .unwrap();
     assert_eq!(all_apps.len(), 2);
diff --git a/crates/db_views_actor/Cargo.toml b/crates/db_views_actor/Cargo.toml
index ba31b778..d64d1e19 100644
--- a/crates/db_views_actor/Cargo.toml
+++ b/crates/db_views_actor/Cargo.toml
@@ -18,3 +18,4 @@ full = ["lemmy_db_schema/full", "diesel"]
 lemmy_db_schema = { version = "=0.16.5", path = "../db_schema" }
 diesel = { version = "1.4.8", features = ["postgres","chrono","r2d2","serde_json"], optional = true }
 serde = { version = "1.0.136", features = ["derive"] }
+typed-builder = "0.10.0"
diff --git a/crates/db_views_actor/src/comment_reply_view.rs b/crates/db_views_actor/src/comment_reply_view.rs
index 611f8906..c8f9df9c 100644
--- a/crates/db_views_actor/src/comment_reply_view.rs
+++ b/crates/db_views_actor/src/comment_reply_view.rs
@@ -25,10 +25,11 @@ use lemmy_db_schema::{
     person_block::PersonBlock,
     post::Post,
   },
-  traits::{MaybeOptional, ToSafe, ViewToVec},
+  traits::{ToSafe, ViewToVec},
   utils::{functions::hot_rank, limit_and_offset},
   CommentSortType,
 };
+use typed_builder::TypedBuilder;
 
 type CommentReplyViewTuple = (
   CommentReply,
@@ -159,7 +160,10 @@ impl CommentReplyView {
   }
 }
 
-pub struct CommentReplyQueryBuilder<'a> {
+#[derive(TypedBuilder)]
+#[builder(field_defaults(default))]
+pub struct CommentReplyQuery<'a> {
+  #[builder(!default)]
   conn: &'a PgConnection,
   my_person_id: Option<PersonId>,
   recipient_id: Option<PersonId>,
@@ -170,55 +174,7 @@ pub struct CommentReplyQueryBuilder<'a> {
   limit: Option<i64>,
 }
 
-impl<'a> CommentReplyQueryBuilder<'a> {
-  pub fn create(conn: &'a PgConnection) -> Self {
-    CommentReplyQueryBuilder {
-      conn,
-      my_person_id: None,
-      recipient_id: None,
-      sort: None,
-      unread_only: None,
-      show_bot_accounts: None,
-      page: None,
-      limit: None,
-    }
-  }
-
-  pub fn sort<T: MaybeOptional<CommentSortType>>(mut self, sort: T) -> Self {
-    self.sort = sort.get_optional();
-    self
-  }
-
-  pub fn unread_only<T: MaybeOptional<bool>>(mut self, unread_only: T) -> Self {
-    self.unread_only = unread_only.get_optional();
-    self
-  }
-
-  pub fn show_bot_accounts<T: MaybeOptional<bool>>(mut self, show_bot_accounts: T) -> Self {
-    self.show_bot_accounts = show_bot_accounts.get_optional();
-    self
-  }
-
-  pub fn recipient_id<T: MaybeOptional<PersonId>>(mut self, recipient_id: T) -> Self {
-    self.recipient_id = recipient_id.get_optional();
-    self
-  }
-
-  pub fn my_person_id<T: MaybeOptional<PersonId>>(mut self, my_person_id: T) -> Self {
-    self.my_person_id = my_person_id.get_optional();
-    self
-  }
-
-  pub fn page<T: MaybeOptional<i64>>(mut self, page: T) -> Self {
-    self.page = page.get_optional();
-    self
-  }
-
-  pub fn limit<T: MaybeOptional<i64>>(mut self, limit: T) -> Self {
-    self.limit = limit.get_optional();
-    self
-  }
-
+impl<'a> CommentReplyQuery<'a> {
   pub fn list(self) -> Result<Vec<CommentReplyView>, Error> {
     use diesel::dsl::*;
 
diff --git a/crates/db_views_actor/src/community_block_view.rs b/crates/db_views_actor/src/community_block_view.rs
index 7c753cd2..59d48ea2 100644
--- a/crates/db_views_actor/src/community_block_view.rs
+++ b/crates/db_views_actor/src/community_block_view.rs
@@ -33,10 +33,10 @@ impl ViewToVec for CommunityBlockView {
   type DbTuple = CommunityBlockViewTuple;
   fn from_tuple_to_vec(items: Vec<Self::DbTuple>) -> Vec<Self> {
     items
-      .iter()
+      .into_iter()
       .map(|a| Self {
-        person: a.0.to_owned(),
-        community: a.1.to_owned(),
+        person: a.0,
+        community: a.1,
       })
       .collect::<Vec<Self>>()
   }
diff --git a/crates/db_views_actor/src/community_follower_view.rs b/crates/db_views_actor/src/community_follower_view.rs
index 6d5d94a5..6441536e 100644
--- a/crates/db_views_actor/src/community_follower_view.rs
+++ b/crates/db_views_actor/src/community_follower_view.rs
@@ -48,10 +48,10 @@ impl ViewToVec for CommunityFollowerView {
   type DbTuple = CommunityFollowerViewTuple;
   fn from_tuple_to_vec(items: Vec<Self::DbTuple>) -> Vec<Self> {
     items
-      .iter()
+      .into_iter()
       .map(|a| Self {
-        community: a.0.to_owned(),
-        follower: a.1.to_owned(),
+        community: a.0,
+        follower: a.1,
       })
       .collect::<Vec<Self>>()
   }
diff --git a/crates/db_views_actor/src/community_moderator_view.rs b/crates/db_views_actor/src/community_moderator_view.rs
index 13e7d8cf..b2df9551 100644
--- a/crates/db_views_actor/src/community_moderator_view.rs
+++ b/crates/db_views_actor/src/community_moderator_view.rs
@@ -70,10 +70,10 @@ impl ViewToVec for CommunityModeratorView {
   type DbTuple = CommunityModeratorViewTuple;
   fn from_tuple_to_vec(items: Vec<Self::DbTuple>) -> Vec<Self> {
     items
-      .iter()
+      .into_iter()
       .map(|a| Self {
-        community: a.0.to_owned(),
-        moderator: a.1.to_owned(),
+        community: a.0,
+        moderator: a.1,
       })
       .collect::<Vec<Self>>()
   }
diff --git a/crates/db_views_actor/src/community_view.rs b/crates/db_views_actor/src/community_view.rs
index 289f77c4..69fae0c7 100644
--- a/crates/db_views_actor/src/community_view.rs
+++ b/crates/db_views_actor/src/community_view.rs
@@ -8,11 +8,12 @@ use lemmy_db_schema::{
     community::{Community, CommunityFollower, CommunitySafe},
     community_block::CommunityBlock,
   },
-  traits::{MaybeOptional, ToSafe, ViewToVec},
+  traits::{ToSafe, ViewToVec},
   utils::{functions::hot_rank, fuzzy_search, limit_and_offset},
   ListingType,
   SortType,
 };
+use typed_builder::TypedBuilder;
 
 type CommunityViewTuple = (
   CommunitySafe,
@@ -91,7 +92,10 @@ impl CommunityView {
   }
 }
 
-pub struct CommunityQueryBuilder<'a> {
+#[derive(TypedBuilder)]
+#[builder(field_defaults(default))]
+pub struct CommunityQuery<'a> {
+  #[builder(!default)]
   conn: &'a PgConnection,
   listing_type: Option<ListingType>,
   sort: Option<SortType>,
@@ -102,55 +106,7 @@ pub struct CommunityQueryBuilder<'a> {
   limit: Option<i64>,
 }
 
-impl<'a> CommunityQueryBuilder<'a> {
-  pub fn create(conn: &'a PgConnection) -> Self {
-    CommunityQueryBuilder {
-      conn,
-      my_person_id: None,
-      listing_type: Some(ListingType::All),
-      sort: None,
-      show_nsfw: None,
-      search_term: None,
-      page: None,
-      limit: None,
-    }
-  }
-
-  pub fn listing_type<T: MaybeOptional<ListingType>>(mut self, listing_type: T) -> Self {
-    self.listing_type = listing_type.get_optional();
-    self
-  }
-
-  pub fn sort<T: MaybeOptional<SortType>>(mut self, sort: T) -> Self {
-    self.sort = sort.get_optional();
-    self
-  }
-
-  pub fn show_nsfw<T: MaybeOptional<bool>>(mut self, show_nsfw: T) -> Self {
-    self.show_nsfw = show_nsfw.get_optional();
-    self
-  }
-
-  pub fn search_term<T: MaybeOptional<String>>(mut self, search_term: T) -> Self {
-    self.search_term = search_term.get_optional();
-    self
-  }
-
-  pub fn my_person_id<T: MaybeOptional<PersonId>>(mut self, my_person_id: T) -> Self {
-    self.my_person_id = my_person_id.get_optional();
-    self
-  }
-
-  pub fn page<T: MaybeOptional<i64>>(mut self, page: T) -> Self {
-    self.page = page.get_optional();
-    self
-  }
-
-  pub fn limit<T: MaybeOptional<i64>>(mut self, limit: T) -> Self {
-    self.limit = limit.get_optional();
-    self
-  }
-
+impl<'a> CommunityQuery<'a> {
   pub fn list(self) -> Result<Vec<CommunityView>, Error> {
     // The left join below will return None in this case
     let person_id_join = self.my_person_id.unwrap_or(PersonId(-1));
@@ -258,10 +214,10 @@ impl ViewToVec for CommunityView {
   type DbTuple = CommunityViewTuple;
   fn from_tuple_to_vec(items: Vec<Self::DbTuple>) -> Vec<Self> {
     items
-      .iter()
+      .into_iter()
       .map(|a| Self {
-        community: a.0.to_owned(),
-        counts: a.1.to_owned(),
+        community: a.0,
+        counts: a.1,
         subscribed: CommunityFollower::to_subscribed_type(&a.2),
         blocked: a.3.is_some(),
       })
diff --git a/crates/db_views_actor/src/person_block_view.rs b/crates/db_views_actor/src/person_block_view.rs
index b93f8167..19ff2633 100644
--- a/crates/db_views_actor/src/person_block_view.rs
+++ b/crates/db_views_actor/src/person_block_view.rs
@@ -30,10 +30,10 @@ impl ViewToVec for PersonBlockView {
   type DbTuple = PersonBlockViewTuple;
   fn from_tuple_to_vec(items: Vec<Self::DbTuple>) -> Vec<Self> {
     items
-      .iter()
+      .into_iter()
       .map(|a| Self {
-        person: a.0.to_owned(),
-        target: a.1.to_owned(),
+        person: a.0,
+        target: a.1,
       })
       .collect::<Vec<Self>>()
   }
diff --git a/crates/db_views_actor/src/person_mention_view.rs b/crates/db_views_actor/src/person_mention_view.rs
index 44fefdc4..30136705 100644
--- a/crates/db_views_actor/src/person_mention_view.rs
+++ b/crates/db_views_actor/src/person_mention_view.rs
@@ -25,10 +25,11 @@ use lemmy_db_schema::{
     person_mention::PersonMention,
     post::Post,
   },
-  traits::{MaybeOptional, ToSafe, ViewToVec},
+  traits::{ToSafe, ViewToVec},
   utils::{functions::hot_rank, limit_and_offset},
   CommentSortType,
 };
+use typed_builder::TypedBuilder;
 
 type PersonMentionViewTuple = (
   PersonMention,
@@ -159,7 +160,10 @@ impl PersonMentionView {
   }
 }
 
-pub struct PersonMentionQueryBuilder<'a> {
+#[derive(TypedBuilder)]
+#[builder(field_defaults(default))]
+pub struct PersonMentionQuery<'a> {
+  #[builder(!default)]
   conn: &'a PgConnection,
   my_person_id: Option<PersonId>,
   recipient_id: Option<PersonId>,
@@ -170,55 +174,7 @@ pub struct PersonMentionQueryBuilder<'a> {
   limit: Option<i64>,
 }
 
-impl<'a> PersonMentionQueryBuilder<'a> {
-  pub fn create(conn: &'a PgConnection) -> Self {
-    PersonMentionQueryBuilder {
-      conn,
-      my_person_id: None,
-      recipient_id: None,
-      sort: None,
-      unread_only: None,
-      show_bot_accounts: None,
-      page: None,
-      limit: None,
-    }
-  }
-
-  pub fn sort<T: MaybeOptional<CommentSortType>>(mut self, sort: T) -> Self {
-    self.sort = sort.get_optional();
-    self
-  }
-
-  pub fn unread_only<T: MaybeOptional<bool>>(mut self, unread_only: T) -> Self {
-    self.unread_only = unread_only.get_optional();
-    self
-  }
-
-  pub fn show_bot_accounts<T: MaybeOptional<bool>>(mut self, show_bot_accounts: T) -> Self {
-    self.show_bot_accounts = show_bot_accounts.get_optional();
-    self
-  }
-
-  pub fn recipient_id<T: MaybeOptional<PersonId>>(mut self, recipient_id: T) -> Self {
-    self.recipient_id = recipient_id.get_optional();
-    self
-  }
-
-  pub fn my_person_id<T: MaybeOptional<PersonId>>(mut self, my_person_id: T) -> Self {
-    self.my_person_id = my_person_id.get_optional();
-    self
-  }
-
-  pub fn page<T: MaybeOptional<i64>>(mut self, page: T) -> Self {
-    self.page = page.get_optional();
-    self
-  }
-
-  pub fn limit<T: MaybeOptional<i64>>(mut self, limit: T) -> Self {
-    self.limit = limit.get_optional();
-    self
-  }
-
+impl<'a> PersonMentionQuery<'a> {
   pub fn list(self) -> Result<Vec<PersonMentionView>, Error> {
     use diesel::dsl::*;
 
@@ -324,15 +280,15 @@ impl ViewToVec for PersonMentionView {
   type DbTuple = PersonMentionViewTuple;
   fn from_tuple_to_vec(items: Vec<Self::DbTuple>) -> Vec<Self> {
     items
-      .iter()
+      .into_iter()
       .map(|a| Self {
-        person_mention: a.0.to_owned(),
-        comment: a.1.to_owned(),
-        creator: a.2.to_owned(),
-        post: a.3.to_owned(),
-        community: a.4.to_owned(),
-        recipient: a.5.to_owned(),
-        counts: a.6.to_owned(),
+        person_mention: a.0,
+        comment: a.1,
+        creator: a.2,
+        post: a.3,
+        community: a.4,
+        recipient: a.5,
+        counts: a.6,
         creator_banned_from_community: a.7.is_some(),
         subscribed: CommunityFollower::to_subscribed_type(&a.8),
         saved: a.9.is_some(),
diff --git a/crates/db_views_actor/src/person_view.rs b/crates/db_views_actor/src/person_view.rs
index cf4874ce..5fae93ca 100644
--- a/crates/db_views_actor/src/person_view.rs
+++ b/crates/db_views_actor/src/person_view.rs
@@ -5,10 +5,11 @@ use lemmy_db_schema::{
   newtypes::PersonId,
   schema::{person, person_aggregates},
   source::person::{Person, PersonSafe},
-  traits::{MaybeOptional, ToSafe, ViewToVec},
+  traits::{ToSafe, ViewToVec},
   utils::{fuzzy_search, limit_and_offset},
   SortType,
 };
+use typed_builder::TypedBuilder;
 
 type PersonViewSafeTuple = (PersonSafe, PersonAggregates);
 
@@ -50,7 +51,10 @@ impl PersonViewSafe {
   }
 }
 
-pub struct PersonQueryBuilder<'a> {
+#[derive(TypedBuilder)]
+#[builder(field_defaults(default))]
+pub struct PersonQuery<'a> {
+  #[builder(!default)]
   conn: &'a PgConnection,
   sort: Option<SortType>,
   search_term: Option<String>,
@@ -58,37 +62,7 @@ pub struct PersonQueryBuilder<'a> {
   limit: Option<i64>,
 }
 
-impl<'a> PersonQueryBuilder<'a> {
-  pub fn create(conn: &'a PgConnection) -> Self {
-    PersonQueryBuilder {
-      conn,
-      search_term: None,
-      sort: None,
-      page: None,
-      limit: None,
-    }
-  }
-
-  pub fn sort<T: MaybeOptional<SortType>>(mut self, sort: T) -> Self {
-    self.sort = sort.get_optional();
-    self
-  }
-
-  pub fn search_term<T: MaybeOptional<String>>(mut self, search_term: T) -> Self {
-    self.search_term = search_term.get_optional();
-    self
-  }
-
-  pub fn page<T: MaybeOptional<i64>>(mut self, page: T) -> Self {
-    self.page = page.get_optional();
-    self
-  }
-
-  pub fn limit<T: MaybeOptional<i64>>(mut self, limit: T) -> Self {
-    self.limit = limit.get_optional();
-    self
-  }
-
+impl<'a> PersonQuery<'a> {
   pub fn list(self) -> Result<Vec<PersonViewSafe>, Error> {
     let mut query = person::table
       .inner_join(person_aggregates::table)
@@ -138,10 +112,10 @@ impl ViewToVec for PersonViewSafe {
   type DbTuple = PersonViewSafeTuple;
   fn from_tuple_to_vec(items: Vec<Self::DbTuple>) -> Vec<Self> {
     items
-      .iter()
+      .into_iter()
       .map(|a| Self {
-        person: a.0.to_owned(),
-        counts: a.1.to_owned(),
+        person: a.0,
+        counts: a.1,
       })
       .collect::<Vec<Self>>()
   }
diff --git a/crates/db_views_moderator/src/admin_purge_comment_view.rs b/crates/db_views_moderator/src/admin_purge_comment_view.rs
index c2928de6..033991a8 100644
--- a/crates/db_views_moderator/src/admin_purge_comment_view.rs
+++ b/crates/db_views_moderator/src/admin_purge_comment_view.rs
@@ -51,11 +51,11 @@ impl ViewToVec for AdminPurgeCommentView {
   type DbTuple = AdminPurgeCommentViewTuple;
   fn from_tuple_to_vec(items: Vec<Self::DbTuple>) -> Vec<Self> {
     items
-      .iter()
+      .into_iter()
       .map(|a| Self {
-        admin_purge_comment: a.0.to_owned(),
-        admin: a.1.to_owned(),
-        post: a.2.to_owned(),
+        admin_purge_comment: a.0,
+        admin: a.1,
+        post: a.2,
       })
       .collect::<Vec<Self>>()
   }
diff --git a/crates/db_views_moderator/src/admin_purge_community_view.rs b/crates/db_views_moderator/src/admin_purge_community_view.rs
index 20903efc..7510779f 100644
--- a/crates/db_views_moderator/src/admin_purge_community_view.rs
+++ b/crates/db_views_moderator/src/admin_purge_community_view.rs
@@ -48,10 +48,10 @@ impl ViewToVec for AdminPurgeCommunityView {
   type DbTuple = AdminPurgeCommunityViewTuple;
   fn from_tuple_to_vec(items: Vec<Self::DbTuple>) -> Vec<Self> {
     items
-      .iter()
+      .into_iter()
       .map(|a| Self {
-        admin_purge_community: a.0.to_owned(),
-        admin: a.1.to_owned(),
+        admin_purge_community: a.0,
+        admin: a.1,
       })
       .collect::<Vec<Self>>()
   }
diff --git a/crates/db_views_moderator/src/admin_purge_person_view.rs b/crates/db_views_moderator/src/admin_purge_person_view.rs
index e6ab093b..eb0b714a 100644
--- a/crates/db_views_moderator/src/admin_purge_person_view.rs
+++ b/crates/db_views_moderator/src/admin_purge_person_view.rs
@@ -48,10 +48,10 @@ impl ViewToVec for AdminPurgePersonView {
   type DbTuple = AdminPurgePersonViewTuple;
   fn from_tuple_to_vec(items: Vec<Self::DbTuple>) -> Vec<Self> {
     items
-      .iter()
+      .into_iter()
       .map(|a| Self {
-        admin_purge_person: a.0.to_owned(),
-        admin: a.1.to_owned(),
+        admin_purge_person: a.0,
+        admin: a.1,
       })
       .collect::<Vec<Self>>()
   }
diff --git a/crates/db_views_moderator/src/admin_purge_post_view.rs b/crates/db_views_moderator/src/admin_purge_post_view.rs
index 981d51b3..368b7c53 100644
--- a/crates/db_views_moderator/src/admin_purge_post_view.rs
+++ b/crates/db_views_moderator/src/admin_purge_post_view.rs
@@ -51,11 +51,11 @@ impl ViewToVec for AdminPurgePostView {
   type DbTuple = AdminPurgePostViewTuple;
   fn from_tuple_to_vec(items: Vec<Self::DbTuple>) -> Vec<Self> {
     items
-      .iter()
+      .into_iter()
       .map(|a| Self {
-        admin_purge_post: a.0.to_owned(),
-        admin: a.1.to_owned(),
-        community: a.2.to_owned(),
+        admin_purge_post: a.0,
+        admin: a.1,
+        community: a.2,
       })
       .collect::<Vec<Self>>()
   }
diff --git a/crates/db_views_moderator/src/mod_add_community_view.rs b/crates/db_views_moderator/src/mod_add_community_view.rs
index 32fe7c77..0182b809 100644
--- a/crates/db_views_moderator/src/mod_add_community_view.rs
+++ b/crates/db_views_moderator/src/mod_add_community_view.rs
@@ -60,12 +60,12 @@ impl ViewToVec for ModAddCommunityView {
   type DbTuple = ModAddCommunityViewTuple;
   fn from_tuple_to_vec(items: Vec<Self::DbTuple>) -> Vec<Self> {
     items
-      .iter()
+      .into_iter()
       .map(|a| Self {
-        mod_add_community: a.0.to_owned(),
-        moderator: a.1.to_owned(),
-        community: a.2.to_owned(),
-        modded_person: a.3.to_owned(),
+        mod_add_community: a.0,
+        moderator: a.1,
+        community: a.2,
+        modded_person: a.3,
       })
       .collect::<Vec<Self>>()
   }
diff --git a/crates/db_views_moderator/src/mod_add_view.rs b/crates/db_views_moderator/src/mod_add_view.rs
index 874615a8..86c7ef19 100644
--- a/crates/db_views_moderator/src/mod_add_view.rs
+++ b/crates/db_views_moderator/src/mod_add_view.rs
@@ -50,11 +50,11 @@ impl ViewToVec for ModAddView {
   type DbTuple = ModAddViewTuple;
   fn from_tuple_to_vec(items: Vec<Self::DbTuple>) -> Vec<Self> {
     items
-      .iter()
+      .into_iter()
       .map(|a| Self {
-        mod_add: a.0.to_owned(),
-        moderator: a.1.to_owned(),
-        modded_person: a.2.to_owned(),
+        mod_add: a.0,
+        moderator: a.1,
+        modded_person: a.2,
       })
       .collect::<Vec<Self>>()
   }
diff --git a/crates/db_views_moderator/src/mod_ban_from_community_view.rs b/crates/db_views_moderator/src/mod_ban_from_community_view.rs
index f5ce3fa9..70696aac 100644
--- a/crates/db_views_moderator/src/mod_ban_from_community_view.rs
+++ b/crates/db_views_moderator/src/mod_ban_from_community_view.rs
@@ -65,12 +65,12 @@ impl ViewToVec for ModBanFromCommunityView {
   type DbTuple = ModBanFromCommunityViewTuple;
   fn from_tuple_to_vec(items: Vec<Self::DbTuple>) -> Vec<Self> {
     items
-      .iter()
+      .into_iter()
       .map(|a| Self {
-        mod_ban_from_community: a.0.to_owned(),
-        moderator: a.1.to_owned(),
-        community: a.2.to_owned(),
-        banned_person: a.3.to_owned(),
+        mod_ban_from_community: a.0,
+        moderator: a.1,
+        community: a.2,
+        banned_person: a.3,
       })
       .collect::<Vec<Self>>()
   }
diff --git a/crates/db_views_moderator/src/mod_ban_view.rs b/crates/db_views_moderator/src/mod_ban_view.rs
index 06e610de..482d4c37 100644
--- a/crates/db_views_moderator/src/mod_ban_view.rs
+++ b/crates/db_views_moderator/src/mod_ban_view.rs
@@ -50,11 +50,11 @@ impl ViewToVec for ModBanView {
   type DbTuple = ModBanViewTuple;
   fn from_tuple_to_vec(items: Vec<Self::DbTuple>) -> Vec<Self> {
     items
-      .iter()
+      .into_iter()
       .map(|a| Self {
-        mod_ban: a.0.to_owned(),
-        moderator: a.1.to_owned(),
-        banned_person: a.2.to_owned(),
+        mod_ban: a.0,
+        moderator: a.1,
+        banned_person: a.2,
       })
       .collect::<Vec<Self>>()
   }
diff --git a/crates/db_views_moderator/src/mod_hide_community_view.rs b/crates/db_views_moderator/src/mod_hide_community_view.rs
index ce0a3e73..10397076 100644
--- a/crates/db_views_moderator/src/mod_hide_community_view.rs
+++ b/crates/db_views_moderator/src/mod_hide_community_view.rs
@@ -57,11 +57,11 @@ impl ViewToVec for ModHideCommunityView {
   type DbTuple = ModHideCommunityViewTuple;
   fn from_tuple_to_vec(items: Vec<Self::DbTuple>) -> Vec<Self> {
     items
-      .iter()
+      .into_iter()
       .map(|a| Self {
-        mod_hide_community: a.0.to_owned(),
-        admin: a.1.to_owned(),
-        community: a.2.to_owned(),
+        mod_hide_community: a.0,
+        admin: a.1,
+        community: a.2,
       })
       .collect::<Vec<Self>>()
   }
diff --git a/crates/db_views_moderator/src/mod_lock_post_view.rs b/crates/db_views_moderator/src/mod_lock_post_view.rs
index 32ec5732..dba2d588 100644
--- a/crates/db_views_moderator/src/mod_lock_post_view.rs
+++ b/crates/db_views_moderator/src/mod_lock_post_view.rs
@@ -59,12 +59,12 @@ impl ViewToVec for ModLockPostView {
   type DbTuple = ModLockPostViewTuple;
   fn from_tuple_to_vec(items: Vec<Self::DbTuple>) -> Vec<Self> {
     items
-      .iter()
+      .into_iter()
       .map(|a| Self {
-        mod_lock_post: a.0.to_owned(),
-        moderator: a.1.to_owned(),
-        post: a.2.to_owned(),
-        community: a.3.to_owned(),
+        mod_lock_post: a.0,
+        moderator: a.1,
+        post: a.2,
+        community: a.3,
       })
       .collect::<Vec<Self>>()
   }
diff --git a/crates/db_views_moderator/src/mod_remove_comment_view.rs b/crates/db_views_moderator/src/mod_remove_comment_view.rs
index a3c7e647..29d3a19c 100644
--- a/crates/db_views_moderator/src/mod_remove_comment_view.rs
+++ b/crates/db_views_moderator/src/mod_remove_comment_view.rs
@@ -71,14 +71,14 @@ impl ViewToVec for ModRemoveCommentView {
   type DbTuple = ModRemoveCommentViewTuple;
   fn from_tuple_to_vec(items: Vec<Self::DbTuple>) -> Vec<Self> {
     items
-      .iter()
+      .into_iter()
       .map(|a| Self {
-        mod_remove_comment: a.0.to_owned(),
-        moderator: a.1.to_owned(),
-        comment: a.2.to_owned(),
-        commenter: a.3.to_owned(),
-        post: a.4.to_owned(),
-        community: a.5.to_owned(),
+        mod_remove_comment: a.0,
+        moderator: a.1,
+        comment: a.2,
+        commenter: a.3,
+        post: a.4,
+        community: a.5,
       })
       .collect::<Vec<Self>>()
   }
diff --git a/crates/db_views_moderator/src/mod_remove_community_view.rs b/crates/db_views_moderator/src/mod_remove_community_view.rs
index 91fcf511..b49fa1d5 100644
--- a/crates/db_views_moderator/src/mod_remove_community_view.rs
+++ b/crates/db_views_moderator/src/mod_remove_community_view.rs
@@ -51,11 +51,11 @@ impl ViewToVec for ModRemoveCommunityView {
   type DbTuple = ModRemoveCommunityTuple;
   fn from_tuple_to_vec(items: Vec<Self::DbTuple>) -> Vec<Self> {
     items
-      .iter()
+      .into_iter()
       .map(|a| Self {
-        mod_remove_community: a.0.to_owned(),
-        moderator: a.1.to_owned(),
-        community: a.2.to_owned(),
+        mod_remove_community: a.0,
+        moderator: a.1,
+        community: a.2,
       })
       .collect::<Vec<Self>>()
   }
diff --git a/crates/db_views_moderator/src/mod_remove_post_view.rs b/crates/db_views_moderator/src/mod_remove_post_view.rs
index 0368a24b..e46f5ea1 100644
--- a/crates/db_views_moderator/src/mod_remove_post_view.rs
+++ b/crates/db_views_moderator/src/mod_remove_post_view.rs
@@ -59,12 +59,12 @@ impl ViewToVec for ModRemovePostView {
   type DbTuple = ModRemovePostViewTuple;
   fn from_tuple_to_vec(items: Vec<Self::DbTuple>) -> Vec<Self> {
     items
-      .iter()
+      .into_iter()
       .map(|a| Self {
-        mod_remove_post: a.0.to_owned(),
-        moderator: a.1.to_owned(),
-        post: a.2.to_owned(),
-        community: a.3.to_owned(),
+        mod_remove_post: a.0,
+        moderator: a.1,
+        post: a.2,
+        community: a.3,
       })
       .collect::<Vec<Self>>()
   }
diff --git a/crates/db_views_moderator/src/mod_sticky_post_view.rs b/crates/db_views_moderator/src/mod_sticky_post_view.rs
index cd0b59ab..5e0d2056 100644
--- a/crates/db_views_moderator/src/mod_sticky_post_view.rs
+++ b/crates/db_views_moderator/src/mod_sticky_post_view.rs
@@ -59,12 +59,12 @@ impl ViewToVec for ModStickyPostView {
   type DbTuple = ModStickyPostViewTuple;
   fn from_tuple_to_vec(items: Vec<Self::DbTuple>) -> Vec<Self> {
     items
-      .iter()
+      .into_iter()
       .map(|a| Self {
-        mod_sticky_post: a.0.to_owned(),
-        moderator: a.1.to_owned(),
-        post: a.2.to_owned(),
-        community: a.3.to_owned(),
+        mod_sticky_post: a.0,
+        moderator: a.1,
+        post: a.2,
+        community: a.3,
       })
       .collect::<Vec<Self>>()
   }
diff --git a/crates/db_views_moderator/src/mod_transfer_community_view.rs b/crates/db_views_moderator/src/mod_transfer_community_view.rs
index 6e0df733..bc8ccd97 100644
--- a/crates/db_views_moderator/src/mod_transfer_community_view.rs
+++ b/crates/db_views_moderator/src/mod_transfer_community_view.rs
@@ -65,12 +65,12 @@ impl ViewToVec for ModTransferCommunityView {
   type DbTuple = ModTransferCommunityViewTuple;
   fn from_tuple_to_vec(items: Vec<Self::DbTuple>) -> Vec<Self> {
     items
-      .iter()
+      .into_iter()
       .map(|a| Self {
-        mod_transfer_community: a.0.to_owned(),
-        moderator: a.1.to_owned(),
-        community: a.2.to_owned(),
-        modded_person: a.3.to_owned(),
+        mod_transfer_community: a.0,
+        moderator: a.1,
+        community: a.2,
+        modded_person: a.3,
       })
       .collect::<Vec<Self>>()
   }
diff --git a/crates/routes/src/feeds.rs b/crates/routes/src/feeds.rs
index 547f07a7..5840687a 100644
--- a/crates/routes/src/feeds.rs
+++ b/crates/routes/src/feeds.rs
@@ -12,12 +12,12 @@ use lemmy_db_schema::{
   SortType,
 };
 use lemmy_db_views::{
-  post_view::PostQueryBuilder,
+  post_view::PostQuery,
   structs::{PostView, SiteView},
 };
 use lemmy_db_views_actor::{
-  comment_reply_view::CommentReplyQueryBuilder,
-  person_mention_view::PersonMentionQueryBuilder,
+  comment_reply_view::CommentReplyQuery,
+  person_mention_view::PersonMentionQuery,
   structs::{CommentReplyView, PersonMentionView},
 };
 use lemmy_utils::{claims::Claims, error::LemmyError, utils::markdown_to_html};
@@ -91,10 +91,12 @@ async fn get_feed_data(
   let site_view = blocking(context.pool(), SiteView::read_local).await??;
 
   let posts = blocking(context.pool(), move |conn| {
-    PostQueryBuilder::create(conn)
-      .listing_type(listing_type)
-      .sort(sort_type)
-      .limit(RSS_FETCH_LIMIT)
+    PostQuery::builder()
+      .conn(conn)
+      .listing_type(Some(listing_type))
+      .sort(Some(sort_type))
+      .limit(Some(RSS_FETCH_LIMIT))
+      .build()
       .list()
   })
   .await??;
@@ -184,11 +186,13 @@ fn get_feed_user(
   let site_view = SiteView::read_local(conn)?;
   let person = Person::read_from_name(conn, user_name, false)?;
 
-  let posts = PostQueryBuilder::create(conn)
-    .listing_type(ListingType::All)
-    .sort(*sort_type)
-    .creator_id(person.id)
-    .limit(RSS_FETCH_LIMIT)
+  let posts = PostQuery::builder()
+    .conn(conn)
+    .listing_type(Some(ListingType::All))
+    .sort(Some(*sort_type))
+    .creator_id(Some(person.id))
+    .limit(Some(RSS_FETCH_LIMIT))
+    .build()
     .list()?;
 
   let items = create_post_items(posts, protocol_and_hostname)?;
@@ -213,10 +217,12 @@ fn get_feed_community(
   let site_view = SiteView::read_local(conn)?;
   let community = Community::read_from_name(conn, community_name, false)?;
 
-  let posts = PostQueryBuilder::create(conn)
-    .sort(*sort_type)
-    .community_id(community.id)
-    .limit(RSS_FETCH_LIMIT)
+  let posts = PostQuery::builder()
+    .conn(conn)
+    .sort(Some(*sort_type))
+    .community_id(Some(community.id))
+    .limit(Some(RSS_FETCH_LIMIT))
+    .build()
     .list()?;
 
   let items = create_post_items(posts, protocol_and_hostname)?;
@@ -247,13 +253,15 @@ fn get_feed_front(
   let local_user_id = LocalUserId(Claims::decode(jwt, jwt_secret)?.claims.sub);
   let local_user = LocalUser::read(conn, local_user_id)?;
 
-  let posts = PostQueryBuilder::create(conn)
-    .listing_type(ListingType::Subscribed)
-    .my_person_id(local_user.person_id)
-    .show_bot_accounts(local_user.show_bot_accounts)
-    .show_read_posts(local_user.show_read_posts)
-    .sort(*sort_type)
-    .limit(RSS_FETCH_LIMIT)
+  let posts = PostQuery::builder()
+    .conn(conn)
+    .listing_type(Some(ListingType::Subscribed))
+    .my_person_id(Some(local_user.person_id))
+    .show_bot_accounts(Some(local_user.show_bot_accounts))
+    .show_read_posts(Some(local_user.show_read_posts))
+    .sort(Some(*sort_type))
+    .limit(Some(RSS_FETCH_LIMIT))
+    .build()
     .list()?;
 
   let items = create_post_items(posts, protocol_and_hostname)?;
@@ -287,20 +295,24 @@ fn get_feed_inbox(
 
   let sort = CommentSortType::New;
 
-  let replies = CommentReplyQueryBuilder::create(conn)
-    .recipient_id(person_id)
-    .my_person_id(person_id)
-    .show_bot_accounts(show_bot_accounts)
-    .sort(sort)
-    .limit(RSS_FETCH_LIMIT)
+  let replies = CommentReplyQuery::builder()
+    .conn(conn)
+    .recipient_id(Some(person_id))
+    .my_person_id(Some(person_id))
+    .show_bot_accounts(Some(show_bot_accounts))
+    .sort(Some(sort))
+    .limit(Some(RSS_FETCH_LIMIT))
+    .build()
     .list()?;
 
-  let mentions = PersonMentionQueryBuilder::create(conn)
-    .recipient_id(person_id)
-    .my_person_id(person_id)
-    .show_bot_accounts(show_bot_accounts)
-    .sort(sort)
-    .limit(RSS_FETCH_LIMIT)
+  let mentions = PersonMentionQuery::builder()
+    .conn(conn)
+    .recipient_id(Some(person_id))
+    .my_person_id(Some(person_id))
+    .show_bot_accounts(Some(show_bot_accounts))
+    .sort(Some(sort))
+    .limit(Some(RSS_FETCH_LIMIT))
+    .build()
     .list()?;
 
   let items = create_reply_and_mention_items(replies, mentions, protocol_and_hostname)?;