From 2d01a5f9b02e797b21eb9d0f315c25fb51626d60 Mon Sep 17 00:00:00 2001 From: OmegaJak Date: Mon, 9 Oct 2023 16:46:27 -0500 Subject: [PATCH] Batch the remaining ones except those requiring deduplication --- src/database/models/collection_item.rs | 3 - src/database/models/project_item.rs | 122 ++++++++++++++----------- src/database/models/team_item.rs | 73 +++++++++++---- src/database/models/thread_item.rs | 24 ++--- src/routes/v2/collections.rs | 32 ++++--- src/routes/v2/projects.rs | 8 +- 6 files changed, 160 insertions(+), 102 deletions(-) diff --git a/src/database/models/collection_item.rs b/src/database/models/collection_item.rs index 61a90580..a24f5dcb 100644 --- a/src/database/models/collection_item.rs +++ b/src/database/models/collection_item.rs @@ -1,12 +1,9 @@ -use std::iter; - use super::ids::*; use crate::database::models; use crate::database::models::DatabaseError; use crate::database::redis::RedisPool; use crate::models::collections::CollectionStatus; use chrono::{DateTime, Utc}; -use itertools::Itertools; use serde::{Deserialize, Serialize}; const COLLECTIONS_NAMESPACE: &str = "collections"; diff --git a/src/database/models/project_item.rs b/src/database/models/project_item.rs index f841f934..4018adb2 100644 --- a/src/database/models/project_item.rs +++ b/src/database/models/project_item.rs @@ -5,6 +5,7 @@ use crate::database::redis::RedisPool; use crate::models::ids::base62_impl::{parse_base62, to_base62}; use crate::models::projects::{MonetizationStatus, ProjectStatus}; use chrono::{DateTime, Utc}; +use itertools::Itertools; use serde::{Deserialize, Serialize}; pub const PROJECTS_NAMESPACE: &str = "projects"; @@ -20,23 +21,25 @@ pub struct DonationUrl { } impl DonationUrl { - pub async fn insert_project( - &self, + pub async fn insert_many_projects( + donation_urls: Vec, project_id: ProjectId, transaction: &mut sqlx::Transaction<'_, sqlx::Postgres>, ) -> Result<(), sqlx::error::Error> { + let (project_ids, platform_ids, urls): (Vec<_>, Vec<_>, Vec<_>) = donation_urls + .into_iter() + .map(|url| (project_id.0, url.platform_id.0, url.url)) + .multiunzip(); sqlx::query!( " INSERT INTO mods_donations ( joining_mod_id, joining_platform_id, url ) - VALUES ( - $1, $2, $3 - ) + SELECT * FROM UNNEST($1::bigint[], $2::int[], $3::varchar[]) ", - project_id as ProjectId, - self.platform_id as DonationPlatformId, - self.url, + &project_ids[..], + &platform_ids[..], + &urls[..], ) .execute(&mut *transaction) .await?; @@ -56,26 +59,44 @@ pub struct GalleryItem { } impl GalleryItem { - pub async fn insert( - &self, + pub async fn insert_many( + items: Vec, project_id: ProjectId, transaction: &mut sqlx::Transaction<'_, sqlx::Postgres>, ) -> Result<(), sqlx::error::Error> { + let (project_ids, image_urls, featureds, titles, descriptions, orderings): ( + Vec<_>, + Vec<_>, + Vec<_>, + Vec<_>, + Vec<_>, + Vec<_>, + ) = items + .into_iter() + .map(|gi| { + ( + project_id.0, + gi.image_url, + gi.featured, + gi.title, + gi.description, + gi.ordering, + ) + }) + .multiunzip(); sqlx::query!( " INSERT INTO mods_gallery ( mod_id, image_url, featured, title, description, ordering ) - VALUES ( - $1, $2, $3, $4, $5, $6 - ) + SELECT * FROM UNNEST ($1::bigint[], $2::varchar[], $3::bool[], $4::varchar[], $5::varchar[], $6::bigint[]) ", - project_id as ProjectId, - self.image_url, - self.featured, - self.title, - self.description, - self.ordering + &project_ids[..], + &image_urls[..], + &featureds[..], + &titles[..] as &[Option], + &descriptions[..] as &[Option], + &orderings[..] ) .execute(&mut *transaction) .await?; @@ -160,46 +181,45 @@ impl ProjectBuilder { }; project_struct.insert(&mut *transaction).await?; + let ProjectBuilder { + donation_urls, + gallery_items, + categories, + additional_categories, + .. + } = self; + for mut version in self.initial_versions { version.project_id = self.project_id; version.insert(&mut *transaction).await?; } - for donation in self.donation_urls { - donation - .insert_project(self.project_id, &mut *transaction) - .await?; - } - - for gallery in self.gallery_items { - gallery.insert(self.project_id, &mut *transaction).await?; - } - - for category in self.categories { - sqlx::query!( - " - INSERT INTO mods_categories (joining_mod_id, joining_category_id, is_additional) - VALUES ($1, $2, FALSE) - ", - self.project_id as ProjectId, - category as CategoryId, - ) - .execute(&mut *transaction) + DonationUrl::insert_many_projects(donation_urls, self.project_id, &mut *transaction) .await?; - } - for category in self.additional_categories { - sqlx::query!( - " - INSERT INTO mods_categories (joining_mod_id, joining_category_id, is_additional) - VALUES ($1, $2, TRUE) - ", - self.project_id as ProjectId, - category as CategoryId, + GalleryItem::insert_many(gallery_items, self.project_id, &mut *transaction).await?; + + let project_id = self.project_id.0; + let (project_ids, category_ids, is_additionals): (Vec<_>, Vec<_>, Vec<_>) = categories + .into_iter() + .map(|c| (project_id, c.0, false)) + .chain( + additional_categories + .into_iter() + .map(|c| (project_id, c.0, true)), ) - .execute(&mut *transaction) - .await?; - } + .multiunzip(); + sqlx::query!( + " + INSERT INTO mods_categories (joining_mod_id, joining_category_id, is_additional) + SELECT * FROM UNNEST ($1::bigint[], $2::int[], $3::bool[]) + ", + &project_ids[..], + &category_ids[..], + &is_additionals[..] + ) + .execute(&mut *transaction) + .await?; Project::update_game_versions(self.project_id, &mut *transaction).await?; Project::update_loaders(self.project_id, &mut *transaction).await?; diff --git a/src/database/models/team_item.rs b/src/database/models/team_item.rs index 31d60b20..a6e7c783 100644 --- a/src/database/models/team_item.rs +++ b/src/database/models/team_item.rs @@ -41,26 +41,61 @@ impl TeamBuilder { .execute(&mut *transaction) .await?; - for member in self.members { - let team_member_id = generate_team_member_id(&mut *transaction).await?; - sqlx::query!( - " - INSERT INTO team_members (id, team_id, user_id, role, permissions, organization_permissions, accepted, payouts_split, ordering) - VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9) - ", - team_member_id as TeamMemberId, - team.id as TeamId, - member.user_id as UserId, - member.role, - member.permissions.bits() as i64, - member.organization_permissions.map(|p| p.bits() as i64), - member.accepted, - member.payouts_split, - member.ordering, - ) - .execute(&mut *transaction) - .await?; + let mut team_member_ids = Vec::new(); + for _ in self.members.iter() { + team_member_ids.push(generate_team_member_id(&mut *transaction).await?.0); } + let TeamBuilder { members } = self; + let ( + team_ids, + user_ids, + roles, + permissions, + organization_permissions, + accepteds, + payouts_splits, + orderings, + ): ( + Vec<_>, + Vec<_>, + Vec<_>, + Vec<_>, + Vec<_>, + Vec<_>, + Vec<_>, + Vec<_>, + ) = members + .into_iter() + .map(|m| { + ( + team.id.0, + m.user_id.0, + m.role, + m.permissions.bits() as i64, + m.organization_permissions.map(|p| p.bits() as i64), + m.accepted, + m.payouts_split, + m.ordering, + ) + }) + .multiunzip(); + sqlx::query!( + " + INSERT INTO team_members (id, team_id, user_id, role, permissions, organization_permissions, accepted, payouts_split, ordering) + SELECT * FROM UNNEST ($1::int8[], $2::int8[], $3::int8[], $4::varchar[], $5::int8[], $6::int8[], $7::bool[], $8::numeric[], $9::int8[]) + ", + &team_member_ids[..], + &team_ids[..], + &user_ids[..], + &roles[..], + &permissions[..], + &organization_permissions[..] as &[Option], + &accepteds[..], + &payouts_splits[..], + &orderings[..], + ) + .execute(&mut *transaction) + .await?; Ok(team_id) } diff --git a/src/database/models/thread_item.rs b/src/database/models/thread_item.rs index c81b2db4..e34b6531 100644 --- a/src/database/models/thread_item.rs +++ b/src/database/models/thread_item.rs @@ -1,3 +1,5 @@ +use std::os::unix::thread; + use super::ids::*; use crate::database::models::DatabaseError; use crate::models::threads::{MessageBody, ThreadType}; @@ -90,22 +92,20 @@ impl ThreadBuilder { .execute(&mut *transaction) .await?; - for member in &self.members { - sqlx::query!( - " + let (thread_ids, members): (Vec<_>, Vec<_>) = + self.members.iter().map(|m| (thread_id.0, m.0)).unzip(); + sqlx::query!( + " INSERT INTO threads_members ( thread_id, user_id ) - VALUES ( - $1, $2 - ) + SELECT * FROM UNNEST ($1::int8[], $2::int8[]) ", - thread_id as ThreadId, - *member as UserId, - ) - .execute(&mut *transaction) - .await?; - } + &thread_ids[..], + &members[..], + ) + .execute(&mut *transaction) + .await?; Ok(thread_id) } diff --git a/src/routes/v2/collections.rs b/src/routes/v2/collections.rs index 01372b0e..89778754 100644 --- a/src/routes/v2/collections.rs +++ b/src/routes/v2/collections.rs @@ -15,6 +15,7 @@ use crate::{database, models}; use actix_web::web::Data; use actix_web::{delete, get, patch, post, web, HttpRequest, HttpResponse}; use chrono::Utc; +use itertools::Itertools; use serde::{Deserialize, Serialize}; use sqlx::PgPool; use std::sync::Arc; @@ -301,6 +302,11 @@ pub async fn collection_edit( .execute(&mut *transaction) .await?; + let collection_item_ids = new_project_ids + .iter() + .map(|_| collection_item.id.0) + .collect_vec(); + let mut validated_project_ids = Vec::new(); for project_id in new_project_ids { let project = database::models::Project::get(project_id, &**pool, &redis) .await? @@ -309,20 +315,20 @@ pub async fn collection_edit( "The specified project {project_id} does not exist!" )) })?; - - // Insert- don't throw an error if it already exists - sqlx::query!( - " - INSERT INTO collections_mods (collection_id, mod_id) - VALUES ($1, $2) - ON CONFLICT DO NOTHING - ", - collection_item.id as database::models::ids::CollectionId, - project.inner.id as database::models::ids::ProjectId, - ) - .execute(&mut *transaction) - .await?; + validated_project_ids.push(project.inner.id.0); } + // Insert- don't throw an error if it already exists + sqlx::query!( + " + INSERT INTO collections_mods (collection_id, mod_id) + SELECT * FROM UNNEST ($1::int8[], $2::int8[]) + ON CONFLICT DO NOTHING + ", + &collection_item_ids[..], + &validated_project_ids[..], + ) + .execute(&mut *transaction) + .await?; } database::models::Collection::clear_cache(collection_item.id, &redis).await?; diff --git a/src/routes/v2/projects.rs b/src/routes/v2/projects.rs index 50967487..009d2561 100644 --- a/src/routes/v2/projects.rs +++ b/src/routes/v2/projects.rs @@ -2,6 +2,7 @@ use crate::auth::{filter_authorized_projects, get_user_from_headers, is_authoriz use crate::database; use crate::database::models::image_item; use crate::database::models::notification_item::NotificationBuilder; +use crate::database::models::project_item::GalleryItem; use crate::database::models::thread_item::ThreadMessageBuilder; use crate::database::redis::RedisPool; use crate::file_hosting::FileHost; @@ -2047,16 +2048,15 @@ pub async fn add_gallery_item( .await?; } - database::models::project_item::GalleryItem { + let gallery_item = vec![database::models::project_item::GalleryItem { image_url: file_url, featured: item.featured, title: item.title, description: item.description, created: Utc::now(), ordering: item.ordering.unwrap_or(0), - } - .insert(project_item.inner.id, &mut transaction) - .await?; + }]; + GalleryItem::insert_many(gallery_item, project_item.inner.id, &mut transaction).await?; database::models::Project::clear_cache( project_item.inner.id,