mirror of
https://github.com/LemmyNet/lemmy.git
synced 2025-01-10 20:15:56 +00:00
chore: restructure / rename tag tables
This commit is contained in:
parent
37246e96d6
commit
89a906827d
11 changed files with 194 additions and 180 deletions
|
@ -1,5 +1,5 @@
|
||||||
use lemmy_db_schema::{
|
use lemmy_db_schema::{
|
||||||
newtypes::{CommentId, CommunityId, CommunityPostTagId, DbUrl, LanguageId, PostId, PostReportId},
|
newtypes::{CommentId, CommunityId, DbUrl, LanguageId, PostId, PostReportId, TagId},
|
||||||
ListingType,
|
ListingType,
|
||||||
PostFeatureType,
|
PostFeatureType,
|
||||||
PostSortType,
|
PostSortType,
|
||||||
|
@ -37,7 +37,7 @@ pub struct CreatePost {
|
||||||
/// Instead of fetching a thumbnail, use a custom one.
|
/// Instead of fetching a thumbnail, use a custom one.
|
||||||
#[cfg_attr(feature = "full", ts(optional))]
|
#[cfg_attr(feature = "full", ts(optional))]
|
||||||
pub custom_thumbnail: Option<String>,
|
pub custom_thumbnail: Option<String>,
|
||||||
pub community_post_tags: Option<Vec<CommunityPostTagId>>,
|
pub tags: Option<Vec<TagId>>,
|
||||||
/// Time when this post should be scheduled. Null means publish immediately.
|
/// Time when this post should be scheduled. Null means publish immediately.
|
||||||
#[cfg_attr(feature = "full", ts(optional))]
|
#[cfg_attr(feature = "full", ts(optional))]
|
||||||
pub scheduled_publish_time: Option<i64>,
|
pub scheduled_publish_time: Option<i64>,
|
||||||
|
@ -165,7 +165,7 @@ pub struct EditPost {
|
||||||
/// Instead of fetching a thumbnail, use a custom one.
|
/// Instead of fetching a thumbnail, use a custom one.
|
||||||
#[cfg_attr(feature = "full", ts(optional))]
|
#[cfg_attr(feature = "full", ts(optional))]
|
||||||
pub custom_thumbnail: Option<String>,
|
pub custom_thumbnail: Option<String>,
|
||||||
pub community_post_tags: Option<Vec<CommunityPostTagId>>,
|
pub tags: Option<Vec<TagId>>,
|
||||||
/// Time when this post should be scheduled. Null means publish immediately.
|
/// Time when this post should be scheduled. Null means publish immediately.
|
||||||
#[cfg_attr(feature = "full", ts(optional))]
|
#[cfg_attr(feature = "full", ts(optional))]
|
||||||
pub scheduled_publish_time: Option<i64>,
|
pub scheduled_publish_time: Option<i64>,
|
||||||
|
|
|
@ -6,7 +6,6 @@ pub mod comment_reply;
|
||||||
pub mod comment_report;
|
pub mod comment_report;
|
||||||
pub mod community;
|
pub mod community;
|
||||||
pub mod community_block;
|
pub mod community_block;
|
||||||
pub mod community_post_tag;
|
|
||||||
pub mod custom_emoji;
|
pub mod custom_emoji;
|
||||||
pub mod email_verification;
|
pub mod email_verification;
|
||||||
pub mod federation_allowlist;
|
pub mod federation_allowlist;
|
||||||
|
@ -36,4 +35,5 @@ pub mod private_message_report;
|
||||||
pub mod registration_application;
|
pub mod registration_application;
|
||||||
pub mod secret;
|
pub mod secret;
|
||||||
pub mod site;
|
pub mod site;
|
||||||
|
pub mod tag;
|
||||||
pub mod tagline;
|
pub mod tagline;
|
||||||
|
|
|
@ -1,10 +1,12 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
newtypes::CommunityPostTagId,
|
newtypes::{CommunityId, TagId},
|
||||||
schema::{community_post_tag, post_community_post_tag},
|
schema::{community_post_tag, post_tag, tag},
|
||||||
source::community_post_tag::{
|
source::community_post_tag::{
|
||||||
CommunityPostTag,
|
CommunityPostTag,
|
||||||
CommunityPostTagInsertForm,
|
CommunityPostTagInsertForm,
|
||||||
PostCommunityPostTagInsertForm,
|
PostTagInsertForm,
|
||||||
|
Tag,
|
||||||
|
TagInsertForm,
|
||||||
},
|
},
|
||||||
traits::Crud,
|
traits::Crud,
|
||||||
utils::{get_conn, DbPool},
|
utils::{get_conn, DbPool},
|
||||||
|
@ -14,13 +16,42 @@ use diesel::{insert_into, result::Error, QueryDsl};
|
||||||
use diesel_async::RunQueryDsl;
|
use diesel_async::RunQueryDsl;
|
||||||
use lemmy_utils::error::LemmyResult;
|
use lemmy_utils::error::LemmyResult;
|
||||||
|
|
||||||
|
#[async_trait]
|
||||||
|
impl Crud for Tag {
|
||||||
|
type InsertForm = TagInsertForm;
|
||||||
|
|
||||||
|
type UpdateForm = TagInsertForm;
|
||||||
|
|
||||||
|
type IdType = TagId;
|
||||||
|
|
||||||
|
async fn create(pool: &mut DbPool<'_>, form: &Self::InsertForm) -> Result<Self, Error> {
|
||||||
|
let conn = &mut get_conn(pool).await?;
|
||||||
|
insert_into(tag::table)
|
||||||
|
.values(form)
|
||||||
|
.get_result::<Self>(conn)
|
||||||
|
.await
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn update(
|
||||||
|
pool: &mut DbPool<'_>,
|
||||||
|
pid: TagId,
|
||||||
|
form: &Self::UpdateForm,
|
||||||
|
) -> Result<Self, Error> {
|
||||||
|
let conn = &mut get_conn(pool).await?;
|
||||||
|
diesel::update(tag::table.find(pid))
|
||||||
|
.set(form)
|
||||||
|
.get_result::<Self>(conn)
|
||||||
|
.await
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
impl Crud for CommunityPostTag {
|
impl Crud for CommunityPostTag {
|
||||||
type InsertForm = CommunityPostTagInsertForm;
|
type InsertForm = CommunityPostTagInsertForm;
|
||||||
|
|
||||||
type UpdateForm = CommunityPostTagInsertForm;
|
type UpdateForm = CommunityPostTagInsertForm;
|
||||||
|
|
||||||
type IdType = CommunityPostTagId;
|
type IdType = (CommunityId, TagId);
|
||||||
|
|
||||||
async fn create(pool: &mut DbPool<'_>, form: &Self::InsertForm) -> Result<Self, Error> {
|
async fn create(pool: &mut DbPool<'_>, form: &Self::InsertForm) -> Result<Self, Error> {
|
||||||
let conn = &mut get_conn(pool).await?;
|
let conn = &mut get_conn(pool).await?;
|
||||||
|
@ -32,7 +63,7 @@ impl Crud for CommunityPostTag {
|
||||||
|
|
||||||
async fn update(
|
async fn update(
|
||||||
pool: &mut DbPool<'_>,
|
pool: &mut DbPool<'_>,
|
||||||
pid: CommunityPostTagId,
|
pid: (CommunityId, TagId),
|
||||||
form: &Self::UpdateForm,
|
form: &Self::UpdateForm,
|
||||||
) -> Result<Self, Error> {
|
) -> Result<Self, Error> {
|
||||||
let conn = &mut get_conn(pool).await?;
|
let conn = &mut get_conn(pool).await?;
|
||||||
|
@ -43,13 +74,13 @@ impl Crud for CommunityPostTag {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PostCommunityPostTagInsertForm {
|
impl PostTagInsertForm {
|
||||||
pub async fn insert_tag_associations(
|
pub async fn insert_tag_associations(
|
||||||
pool: &mut DbPool<'_>,
|
pool: &mut DbPool<'_>,
|
||||||
tags: &[PostCommunityPostTagInsertForm],
|
tags: &[PostTagInsertForm],
|
||||||
) -> LemmyResult<()> {
|
) -> LemmyResult<()> {
|
||||||
let conn = &mut get_conn(pool).await?;
|
let conn = &mut get_conn(pool).await?;
|
||||||
insert_into(post_community_post_tag::table)
|
insert_into(post_tag::table)
|
||||||
.values(tags)
|
.values(tags)
|
||||||
.execute(conn)
|
.execute(conn)
|
||||||
.await
|
.await
|
|
@ -287,5 +287,5 @@ impl InstanceId {
|
||||||
#[derive(Debug, Copy, Clone, Hash, Eq, PartialEq, Default, Serialize, Deserialize)]
|
#[derive(Debug, Copy, Clone, Hash, Eq, PartialEq, Default, Serialize, Deserialize)]
|
||||||
#[cfg_attr(feature = "full", derive(DieselNewType, TS))]
|
#[cfg_attr(feature = "full", derive(DieselNewType, TS))]
|
||||||
#[cfg_attr(feature = "full", ts(export))]
|
#[cfg_attr(feature = "full", ts(export))]
|
||||||
/// The post id.
|
/// The internal tag id.
|
||||||
pub struct CommunityPostTagId(pub i32);
|
pub struct TagId(pub i32);
|
||||||
|
|
|
@ -264,14 +264,10 @@ diesel::table! {
|
||||||
}
|
}
|
||||||
|
|
||||||
diesel::table! {
|
diesel::table! {
|
||||||
community_post_tag (id) {
|
community_post_tag (community_id, tag_id) {
|
||||||
id -> Int4,
|
|
||||||
ap_id -> Text,
|
|
||||||
community_id -> Int4,
|
community_id -> Int4,
|
||||||
name -> Text,
|
tag_id -> Int4,
|
||||||
published -> Timestamptz,
|
published -> Timestamptz,
|
||||||
updated -> Nullable<Timestamptz>,
|
|
||||||
deleted -> Nullable<Timestamptz>,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -820,13 +816,6 @@ diesel::table! {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
diesel::table! {
|
|
||||||
post_community_post_tag (post_id, community_post_tag_id) {
|
|
||||||
post_id -> Int4,
|
|
||||||
community_post_tag_id -> Int4,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
diesel::table! {
|
diesel::table! {
|
||||||
post_report (id) {
|
post_report (id) {
|
||||||
id -> Int4,
|
id -> Int4,
|
||||||
|
@ -844,6 +833,14 @@ diesel::table! {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
diesel::table! {
|
||||||
|
post_tag (post_id, tag_id) {
|
||||||
|
post_id -> Int4,
|
||||||
|
tag_id -> Int4,
|
||||||
|
published -> Timestamptz,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
diesel::table! {
|
diesel::table! {
|
||||||
private_message (id) {
|
private_message (id) {
|
||||||
id -> Int4,
|
id -> Int4,
|
||||||
|
@ -969,6 +966,17 @@ diesel::table! {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
diesel::table! {
|
||||||
|
tag (id) {
|
||||||
|
id -> Int4,
|
||||||
|
ap_id -> Text,
|
||||||
|
name -> Text,
|
||||||
|
published -> Timestamptz,
|
||||||
|
updated -> Nullable<Timestamptz>,
|
||||||
|
deleted -> Nullable<Timestamptz>,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
diesel::table! {
|
diesel::table! {
|
||||||
tagline (id) {
|
tagline (id) {
|
||||||
id -> Int4,
|
id -> Int4,
|
||||||
|
@ -1003,6 +1011,7 @@ diesel::joinable!(community_aggregates -> community (community_id));
|
||||||
diesel::joinable!(community_language -> community (community_id));
|
diesel::joinable!(community_language -> community (community_id));
|
||||||
diesel::joinable!(community_language -> language (language_id));
|
diesel::joinable!(community_language -> language (language_id));
|
||||||
diesel::joinable!(community_post_tag -> community (community_id));
|
diesel::joinable!(community_post_tag -> community (community_id));
|
||||||
|
diesel::joinable!(community_post_tag -> tag (tag_id));
|
||||||
diesel::joinable!(custom_emoji_keyword -> custom_emoji (custom_emoji_id));
|
diesel::joinable!(custom_emoji_keyword -> custom_emoji (custom_emoji_id));
|
||||||
diesel::joinable!(email_verification -> local_user (local_user_id));
|
diesel::joinable!(email_verification -> local_user (local_user_id));
|
||||||
diesel::joinable!(federation_allowlist -> instance (instance_id));
|
diesel::joinable!(federation_allowlist -> instance (instance_id));
|
||||||
|
@ -1050,9 +1059,9 @@ diesel::joinable!(post_aggregates -> community (community_id));
|
||||||
diesel::joinable!(post_aggregates -> instance (instance_id));
|
diesel::joinable!(post_aggregates -> instance (instance_id));
|
||||||
diesel::joinable!(post_aggregates -> person (creator_id));
|
diesel::joinable!(post_aggregates -> person (creator_id));
|
||||||
diesel::joinable!(post_aggregates -> post (post_id));
|
diesel::joinable!(post_aggregates -> post (post_id));
|
||||||
diesel::joinable!(post_community_post_tag -> community_post_tag (community_post_tag_id));
|
|
||||||
diesel::joinable!(post_community_post_tag -> post (post_id));
|
|
||||||
diesel::joinable!(post_report -> post (post_id));
|
diesel::joinable!(post_report -> post (post_id));
|
||||||
|
diesel::joinable!(post_tag -> post (post_id));
|
||||||
|
diesel::joinable!(post_tag -> tag (tag_id));
|
||||||
diesel::joinable!(private_message_report -> private_message (private_message_id));
|
diesel::joinable!(private_message_report -> private_message (private_message_id));
|
||||||
diesel::joinable!(registration_application -> local_user (local_user_id));
|
diesel::joinable!(registration_application -> local_user (local_user_id));
|
||||||
diesel::joinable!(registration_application -> person (admin_id));
|
diesel::joinable!(registration_application -> person (admin_id));
|
||||||
|
@ -1119,8 +1128,8 @@ diesel::allow_tables_to_appear_in_same_query!(
|
||||||
post,
|
post,
|
||||||
post_actions,
|
post_actions,
|
||||||
post_aggregates,
|
post_aggregates,
|
||||||
post_community_post_tag,
|
|
||||||
post_report,
|
post_report,
|
||||||
|
post_tag,
|
||||||
private_message,
|
private_message,
|
||||||
private_message_report,
|
private_message_report,
|
||||||
received_activity,
|
received_activity,
|
||||||
|
@ -1131,5 +1140,6 @@ diesel::allow_tables_to_appear_in_same_query!(
|
||||||
site,
|
site,
|
||||||
site_aggregates,
|
site_aggregates,
|
||||||
site_language,
|
site_language,
|
||||||
|
tag,
|
||||||
tagline,
|
tagline,
|
||||||
);
|
);
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
use crate::newtypes::{CommunityId, CommunityPostTagId, DbUrl, PostId};
|
use crate::newtypes::{CommunityId, DbUrl, PostId, TagId};
|
||||||
#[cfg(feature = "full")]
|
#[cfg(feature = "full")]
|
||||||
use crate::schema::{community_post_tag, post_community_post_tag};
|
use crate::schema::{community_post_tag, post_tag, tag};
|
||||||
use chrono::{DateTime, Utc};
|
use chrono::{DateTime, Utc};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use serde_with::skip_serializing_none;
|
use serde_with::skip_serializing_none;
|
||||||
|
@ -13,25 +13,36 @@ use ts_rs::TS;
|
||||||
#[skip_serializing_none]
|
#[skip_serializing_none]
|
||||||
#[derive(Debug, PartialEq, Serialize, Deserialize, Clone)]
|
#[derive(Debug, PartialEq, Serialize, Deserialize, Clone)]
|
||||||
#[cfg_attr(feature = "full", derive(TS, Queryable, Selectable, Identifiable))]
|
#[cfg_attr(feature = "full", derive(TS, Queryable, Selectable, Identifiable))]
|
||||||
#[cfg_attr(feature = "full", diesel(table_name = community_post_tag))]
|
#[cfg_attr(feature = "full", diesel(table_name = tag))]
|
||||||
#[cfg_attr(feature = "full", diesel(check_for_backend(diesel::pg::Pg)))]
|
#[cfg_attr(feature = "full", diesel(check_for_backend(diesel::pg::Pg)))]
|
||||||
#[cfg_attr(feature = "full", ts(export))]
|
#[cfg_attr(feature = "full", ts(export))]
|
||||||
pub struct CommunityPostTag {
|
pub struct Tag {
|
||||||
pub id: CommunityPostTagId,
|
pub id: TagId,
|
||||||
pub ap_id: DbUrl,
|
pub ap_id: DbUrl,
|
||||||
pub community_id: CommunityId,
|
|
||||||
pub name: String,
|
pub name: String,
|
||||||
pub published: DateTime<Utc>,
|
pub published: DateTime<Utc>,
|
||||||
pub updated: Option<DateTime<Utc>>,
|
pub updated: Option<DateTime<Utc>>,
|
||||||
pub deleted: Option<DateTime<Utc>>,
|
pub deleted: Option<DateTime<Utc>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[skip_serializing_none]
|
||||||
|
#[derive(Debug, PartialEq, Serialize, Deserialize, Clone)]
|
||||||
|
#[cfg_attr(feature = "full", derive(TS, Queryable, Selectable, Identifiable))]
|
||||||
|
#[cfg_attr(feature = "full", diesel(table_name = community_post_tag))]
|
||||||
|
#[cfg_attr(feature = "full", diesel(check_for_backend(diesel::pg::Pg)))]
|
||||||
|
#[cfg_attr(feature = "full", diesel(primary_key(community_id, tag_id)))]
|
||||||
|
#[cfg_attr(feature = "full", ts(export))]
|
||||||
|
pub struct CommunityPostTag {
|
||||||
|
pub community_id: CommunityId,
|
||||||
|
pub tag_id: TagId,
|
||||||
|
pub published: DateTime<Utc>,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
#[cfg_attr(feature = "full", derive(Insertable, AsChangeset))]
|
#[cfg_attr(feature = "full", derive(Insertable, AsChangeset))]
|
||||||
#[cfg_attr(feature = "full", diesel(table_name = community_post_tag))]
|
#[cfg_attr(feature = "full", diesel(table_name = tag))]
|
||||||
pub struct CommunityPostTagInsertForm {
|
pub struct TagInsertForm {
|
||||||
pub ap_id: DbUrl,
|
pub ap_id: DbUrl,
|
||||||
pub community_id: CommunityId,
|
|
||||||
pub name: String,
|
pub name: String,
|
||||||
// default now
|
// default now
|
||||||
pub published: Option<DateTime<Utc>>,
|
pub published: Option<DateTime<Utc>>,
|
||||||
|
@ -41,8 +52,17 @@ pub struct CommunityPostTagInsertForm {
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
#[cfg_attr(feature = "full", derive(Insertable, AsChangeset))]
|
#[cfg_attr(feature = "full", derive(Insertable, AsChangeset))]
|
||||||
#[cfg_attr(feature = "full", diesel(table_name = post_community_post_tag))]
|
#[cfg_attr(feature = "full", diesel(table_name = community_post_tag))]
|
||||||
pub struct PostCommunityPostTagInsertForm {
|
pub struct CommunityPostTagInsertForm {
|
||||||
pub post_id: PostId,
|
pub community_id: CommunityId,
|
||||||
pub community_post_tag_id: CommunityPostTagId,
|
pub tag_id: TagId,
|
||||||
|
pub published: Option<DateTime<Utc>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
#[cfg_attr(feature = "full", derive(Insertable, AsChangeset))]
|
||||||
|
#[cfg_attr(feature = "full", diesel(table_name = post_tag))]
|
||||||
|
pub struct PostTagInsertForm {
|
||||||
|
pub post_id: PostId,
|
||||||
|
pub tag_id: TagId,
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,7 +5,9 @@ use diesel::{
|
||||||
pg::Pg,
|
pg::Pg,
|
||||||
query_builder::AsQuery,
|
query_builder::AsQuery,
|
||||||
result::Error,
|
result::Error,
|
||||||
|
sql_types,
|
||||||
BoolExpressionMethods,
|
BoolExpressionMethods,
|
||||||
|
BoxableExpression,
|
||||||
ExpressionMethods,
|
ExpressionMethods,
|
||||||
JoinOnDsl,
|
JoinOnDsl,
|
||||||
NullableExpressionMethods,
|
NullableExpressionMethods,
|
||||||
|
@ -23,7 +25,6 @@ use lemmy_db_schema::{
|
||||||
schema::{
|
schema::{
|
||||||
community,
|
community,
|
||||||
community_actions,
|
community_actions,
|
||||||
community_post_tag,
|
|
||||||
image_details,
|
image_details,
|
||||||
instance_actions,
|
instance_actions,
|
||||||
local_user,
|
local_user,
|
||||||
|
@ -33,7 +34,8 @@ use lemmy_db_schema::{
|
||||||
post,
|
post,
|
||||||
post_actions,
|
post_actions,
|
||||||
post_aggregates,
|
post_aggregates,
|
||||||
post_community_post_tag,
|
post_tag,
|
||||||
|
tag,
|
||||||
},
|
},
|
||||||
source::{
|
source::{
|
||||||
community::{CommunityFollower, CommunityFollowerState},
|
community::{CommunityFollower, CommunityFollowerState},
|
||||||
|
@ -82,82 +84,7 @@ fn queries<'a>() -> Queries<
|
||||||
// TODO maybe this should go to localuser also
|
// TODO maybe this should go to localuser also
|
||||||
let all_joins = move |query: post_aggregates::BoxedQuery<'a, Pg>,
|
let all_joins = move |query: post_aggregates::BoxedQuery<'a, Pg>,
|
||||||
my_person_id: Option<PersonId>| {
|
my_person_id: Option<PersonId>| {
|
||||||
let is_local_user_banned_from_community_selection: Box<
|
// We fetch post tags by letting postgresql aggregate them internally in a subquery into JSON.
|
||||||
dyn BoxableExpression<_, Pg, SqlType = sql_types::Bool>,
|
|
||||||
> = if let Some(person_id) = my_person_id {
|
|
||||||
Box::new(is_local_user_banned_from_community(person_id))
|
|
||||||
} else {
|
|
||||||
Box::new(false.into_sql::<sql_types::Bool>())
|
|
||||||
};
|
|
||||||
|
|
||||||
let is_read_selection: Box<dyn BoxableExpression<_, Pg, SqlType = sql_types::Bool>> =
|
|
||||||
if let Some(person_id) = my_person_id {
|
|
||||||
Box::new(is_read(person_id))
|
|
||||||
} else {
|
|
||||||
Box::new(false.into_sql::<sql_types::Bool>())
|
|
||||||
};
|
|
||||||
|
|
||||||
let is_hidden_selection: Box<dyn BoxableExpression<_, Pg, SqlType = sql_types::Bool>> =
|
|
||||||
if let Some(person_id) = my_person_id {
|
|
||||||
Box::new(is_hidden(person_id))
|
|
||||||
} else {
|
|
||||||
Box::new(false.into_sql::<sql_types::Bool>())
|
|
||||||
};
|
|
||||||
|
|
||||||
let is_creator_blocked_selection: Box<dyn BoxableExpression<_, Pg, SqlType = sql_types::Bool>> =
|
|
||||||
if let Some(person_id) = my_person_id {
|
|
||||||
Box::new(is_creator_blocked(person_id))
|
|
||||||
} else {
|
|
||||||
Box::new(false.into_sql::<sql_types::Bool>())
|
|
||||||
};
|
|
||||||
|
|
||||||
let subscribed_type_selection: Box<
|
|
||||||
dyn BoxableExpression<
|
|
||||||
_,
|
|
||||||
Pg,
|
|
||||||
SqlType = sql_types::Nullable<lemmy_db_schema::schema::sql_types::CommunityFollowerState>,
|
|
||||||
>,
|
|
||||||
> = if let Some(person_id) = my_person_id {
|
|
||||||
Box::new(
|
|
||||||
community_follower::table
|
|
||||||
.filter(
|
|
||||||
post_aggregates::community_id
|
|
||||||
.eq(community_follower::community_id)
|
|
||||||
.and(community_follower::person_id.eq(person_id)),
|
|
||||||
)
|
|
||||||
.select(CommunityFollower::select_subscribed_type())
|
|
||||||
.single_value(),
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
Box::new(None::<CommunityFollowerState>.into_sql::<sql_types::Nullable<lemmy_db_schema::schema::sql_types::CommunityFollowerState>>())
|
|
||||||
};
|
|
||||||
|
|
||||||
let score_selection: Box<
|
|
||||||
dyn BoxableExpression<_, Pg, SqlType = sql_types::Nullable<sql_types::SmallInt>>,
|
|
||||||
> = if let Some(person_id) = my_person_id {
|
|
||||||
Box::new(score(person_id))
|
|
||||||
} else {
|
|
||||||
Box::new(None::<i16>.into_sql::<sql_types::Nullable<sql_types::SmallInt>>())
|
|
||||||
};
|
|
||||||
|
|
||||||
let read_comments: Box<
|
|
||||||
dyn BoxableExpression<_, Pg, SqlType = sql_types::Nullable<sql_types::BigInt>>,
|
|
||||||
> = if let Some(person_id) = my_person_id {
|
|
||||||
Box::new(
|
|
||||||
person_post_aggregates::table
|
|
||||||
.filter(
|
|
||||||
post_aggregates::post_id
|
|
||||||
.eq(person_post_aggregates::post_id)
|
|
||||||
.and(person_post_aggregates::person_id.eq(person_id)),
|
|
||||||
)
|
|
||||||
.select(person_post_aggregates::read_comments.nullable())
|
|
||||||
.single_value(),
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
Box::new(None::<i64>.into_sql::<sql_types::Nullable<sql_types::BigInt>>())
|
|
||||||
};
|
|
||||||
|
|
||||||
// We fetch post tags by letting postgresql aggregate them internally in a subquery into JSON.
|
|
||||||
// This is a simple way to join m rows into n rows without duplicating the data and getting
|
// This is a simple way to join m rows into n rows without duplicating the data and getting
|
||||||
// complex diesel types. In pure SQL you would usually do this either using a LEFT JOIN + then
|
// complex diesel types. In pure SQL you would usually do this either using a LEFT JOIN + then
|
||||||
// aggregating the results in the application code. But this results in a lot of duplicate
|
// aggregating the results in the application code. But this results in a lot of duplicate
|
||||||
|
@ -170,15 +97,15 @@ fn queries<'a>() -> Queries<
|
||||||
// If we want to filter by post tag we will have to add
|
// If we want to filter by post tag we will have to add
|
||||||
// separate logic below since this subquery can't affect filtering, but it is simple (`WHERE
|
// separate logic below since this subquery can't affect filtering, but it is simple (`WHERE
|
||||||
// exists (select 1 from post_community_post_tags where community_post_tag_id in (1,2,3,4)`).
|
// exists (select 1 from post_community_post_tags where community_post_tag_id in (1,2,3,4)`).
|
||||||
let community_post_tags: Box<
|
let post_tags: Box<
|
||||||
dyn BoxableExpression<_, Pg, SqlType = sql_types::Nullable<sql_types::Json>>,
|
dyn BoxableExpression<_, Pg, SqlType = sql_types::Nullable<sql_types::Json>>,
|
||||||
> = Box::new(
|
> = Box::new(
|
||||||
post_community_post_tag::table
|
post_tag::table
|
||||||
.inner_join(community_post_tag::table)
|
.inner_join(tag::table)
|
||||||
.select(diesel::dsl::sql::<diesel::sql_types::Json>(
|
.select(diesel::dsl::sql::<diesel::sql_types::Json>(
|
||||||
"json_agg(community_post_tag.*)",
|
"json_agg(tag.*)",
|
||||||
))
|
))
|
||||||
.filter(post_community_post_tag::post_id.eq(post_aggregates::post_id))
|
.filter(post_tag::post_id.eq(post_aggregates::post_id))
|
||||||
.single_value(),
|
.single_value(),
|
||||||
);
|
);
|
||||||
query
|
query
|
||||||
|
@ -237,7 +164,7 @@ fn queries<'a>() -> Queries<
|
||||||
post_aggregates::comments.nullable() - post_actions::read_comments_amount.nullable(),
|
post_aggregates::comments.nullable() - post_actions::read_comments_amount.nullable(),
|
||||||
post_aggregates::comments,
|
post_aggregates::comments,
|
||||||
),
|
),
|
||||||
community_post_tags,
|
post_tags,
|
||||||
))
|
))
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -737,7 +664,9 @@ mod tests {
|
||||||
community_post_tag::{
|
community_post_tag::{
|
||||||
CommunityPostTag,
|
CommunityPostTag,
|
||||||
CommunityPostTagInsertForm,
|
CommunityPostTagInsertForm,
|
||||||
PostCommunityPostTagInsertForm,
|
PostTagInsertForm,
|
||||||
|
Tag,
|
||||||
|
TagInsertForm,
|
||||||
},
|
},
|
||||||
instance::Instance,
|
instance::Instance,
|
||||||
instance_block::{InstanceBlock, InstanceBlockForm},
|
instance_block::{InstanceBlock, InstanceBlockForm},
|
||||||
|
@ -766,7 +695,7 @@ mod tests {
|
||||||
PostSortType,
|
PostSortType,
|
||||||
SubscribedType,
|
SubscribedType,
|
||||||
};
|
};
|
||||||
use lemmy_utils::error::LemmyResult;
|
use lemmy_utils::error::{LemmyErrorType, LemmyResult};
|
||||||
use pretty_assertions::assert_eq;
|
use pretty_assertions::assert_eq;
|
||||||
use serial_test::serial;
|
use serial_test::serial;
|
||||||
use std::time::{Duration, Instant};
|
use std::time::{Duration, Instant};
|
||||||
|
@ -791,8 +720,8 @@ mod tests {
|
||||||
inserted_post: Post,
|
inserted_post: Post,
|
||||||
inserted_bot_post: Post,
|
inserted_bot_post: Post,
|
||||||
inserted_post_with_tags: Post,
|
inserted_post_with_tags: Post,
|
||||||
tag_1: CommunityPostTag,
|
tag_1: Tag,
|
||||||
tag_2: CommunityPostTag,
|
tag_2: Tag,
|
||||||
site: Site,
|
site: Site,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -865,13 +794,12 @@ mod tests {
|
||||||
PersonBlock::block(pool, &person_block).await?;
|
PersonBlock::block(pool, &person_block).await?;
|
||||||
|
|
||||||
// Two community post tags
|
// Two community post tags
|
||||||
let tag_1 = CommunityPostTag::create(
|
let tag_1 = Tag::create(
|
||||||
pool,
|
pool,
|
||||||
&CommunityPostTagInsertForm {
|
&TagInsertForm {
|
||||||
ap_id: Url::parse(&format!("{}/tags/test_tag1", inserted_community.actor_id))
|
ap_id: Url::parse(&format!("{}/tags/test_tag1", inserted_community.actor_id))
|
||||||
.expect("valid")
|
.expect("valid")
|
||||||
.into(),
|
.into(),
|
||||||
community_id: inserted_community.id,
|
|
||||||
name: "Test Tag 1".into(),
|
name: "Test Tag 1".into(),
|
||||||
published: None,
|
published: None,
|
||||||
updated: None,
|
updated: None,
|
||||||
|
@ -879,13 +807,21 @@ mod tests {
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
let tag_2 = CommunityPostTag::create(
|
CommunityPostTag::create(
|
||||||
pool,
|
pool,
|
||||||
&CommunityPostTagInsertForm {
|
&CommunityPostTagInsertForm {
|
||||||
|
community_id: inserted_community.id,
|
||||||
|
tag_id: tag_1.id,
|
||||||
|
published: None,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
let tag_2 = Tag::create(
|
||||||
|
pool,
|
||||||
|
&TagInsertForm {
|
||||||
ap_id: Url::parse(&format!("{}/tags/test_tag2", inserted_community.actor_id))
|
ap_id: Url::parse(&format!("{}/tags/test_tag2", inserted_community.actor_id))
|
||||||
.expect("valid")
|
.expect("valid")
|
||||||
.into(),
|
.into(),
|
||||||
community_id: inserted_community.id,
|
|
||||||
name: "Test Tag 2".into(),
|
name: "Test Tag 2".into(),
|
||||||
published: None,
|
published: None,
|
||||||
updated: None,
|
updated: None,
|
||||||
|
@ -893,14 +829,21 @@ mod tests {
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
|
CommunityPostTag::create(
|
||||||
|
pool,
|
||||||
|
&CommunityPostTagInsertForm {
|
||||||
|
community_id: inserted_community.id,
|
||||||
|
tag_id: tag_2.id,
|
||||||
|
published: None,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
|
||||||
// A sample post
|
// A sample post
|
||||||
let new_post = PostInsertForm::builder()
|
let new_post = PostInsertForm {
|
||||||
.name(POST.to_string())
|
language_id: Some(LanguageId(47)),
|
||||||
.creator_id(inserted_person.id)
|
..PostInsertForm::new(POST.to_string(), inserted_person.id, inserted_community.id)
|
||||||
.community_id(inserted_community.id)
|
};
|
||||||
.language_id(Some(LanguageId(47)))
|
|
||||||
.build();
|
|
||||||
|
|
||||||
let inserted_post = Post::create(pool, &new_post).await?;
|
let inserted_post = Post::create(pool, &new_post).await?;
|
||||||
|
|
||||||
|
@ -912,26 +855,27 @@ mod tests {
|
||||||
let inserted_bot_post = Post::create(pool, &new_bot_post).await?;
|
let inserted_bot_post = Post::create(pool, &new_bot_post).await?;
|
||||||
|
|
||||||
// A sample post with tags
|
// A sample post with tags
|
||||||
let new_post = PostInsertForm::builder()
|
let new_post = PostInsertForm {
|
||||||
.name(POST_WITH_TAGS.to_string())
|
language_id: Some(LanguageId(47)),
|
||||||
.creator_id(inserted_person.id)
|
..PostInsertForm::new(
|
||||||
.community_id(inserted_community.id)
|
POST_WITH_TAGS.to_string(),
|
||||||
.language_id(Some(LanguageId(47)))
|
inserted_person.id,
|
||||||
.build();
|
inserted_community.id,
|
||||||
|
)
|
||||||
|
};
|
||||||
|
|
||||||
let inserted_post_with_tags = Post::create(pool, &new_post).await?;
|
let inserted_post_with_tags = Post::create(pool, &new_post).await?;
|
||||||
let inserted_tags = vec![
|
let inserted_tags = vec![
|
||||||
PostCommunityPostTagInsertForm {
|
PostTagInsertForm {
|
||||||
post_id: inserted_post_with_tags.id,
|
post_id: inserted_post_with_tags.id,
|
||||||
community_post_tag_id: tag_1.id,
|
tag_id: tag_1.id,
|
||||||
},
|
},
|
||||||
PostCommunityPostTagInsertForm {
|
PostTagInsertForm {
|
||||||
post_id: inserted_post_with_tags.id,
|
post_id: inserted_post_with_tags.id,
|
||||||
community_post_tag_id: tag_2.id,
|
tag_id: tag_2.id,
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
PostCommunityPostTagInsertForm::insert_tag_associations(pool, &inserted_tags).await?;
|
PostTagInsertForm::insert_tag_associations(pool, &inserted_tags).await?;
|
||||||
|
|
||||||
|
|
||||||
let local_user_view = LocalUserView {
|
let local_user_view = LocalUserView {
|
||||||
local_user: inserted_local_user,
|
local_user: inserted_local_user,
|
||||||
|
@ -1782,7 +1726,7 @@ mod tests {
|
||||||
);
|
);
|
||||||
|
|
||||||
// Make sure that hidden field is true.
|
// Make sure that hidden field is true.
|
||||||
assert!(&post_listings_show_hidden.at(1).is_some_and(|p| p.hidden));
|
assert!(&post_listings_show_hidden.get(1).is_some_and(|p| p.hidden));
|
||||||
|
|
||||||
cleanup(data, pool).await
|
cleanup(data, pool).await
|
||||||
}
|
}
|
||||||
|
@ -1824,7 +1768,7 @@ mod tests {
|
||||||
assert!(
|
assert!(
|
||||||
&post_listings_show_nsfw
|
&post_listings_show_nsfw
|
||||||
.first()
|
.first()
|
||||||
.ok_or(LemmyErrorType::CouldntFindPost)?
|
.ok_or(LemmyErrorType::NotFound)?
|
||||||
.post
|
.post
|
||||||
.nsfw
|
.nsfw
|
||||||
);
|
);
|
||||||
|
@ -2262,7 +2206,7 @@ mod tests {
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
#[serial]
|
#[serial]
|
||||||
async fn post_tags_present() -> LemmyResult<()> {
|
async fn post_tags_present() -> LemmyResult<()> {
|
||||||
let pool = &build_db_pool_for_tests().await;
|
let pool = &build_db_pool_for_tests();
|
||||||
let pool = &mut pool.into();
|
let pool = &mut pool.into();
|
||||||
let data = init_data(pool).await?;
|
let data = init_data(pool).await?;
|
||||||
|
|
||||||
|
@ -2272,8 +2216,7 @@ mod tests {
|
||||||
Some(&data.local_user_view.local_user),
|
Some(&data.local_user_view.local_user),
|
||||||
false,
|
false,
|
||||||
)
|
)
|
||||||
.await?
|
.await?;
|
||||||
.ok_or(LemmyErrorType::CouldntFindPost)?;
|
|
||||||
|
|
||||||
assert_eq!(2, post_view.community_post_tags.tags.len());
|
assert_eq!(2, post_view.community_post_tags.tags.len());
|
||||||
assert_eq!(data.tag_1.name, post_view.community_post_tags.tags[0].name);
|
assert_eq!(data.tag_1.name, post_view.community_post_tags.tags[0].name);
|
||||||
|
|
|
@ -8,7 +8,7 @@ use lemmy_db_schema::{
|
||||||
comment::Comment,
|
comment::Comment,
|
||||||
comment_report::CommentReport,
|
comment_report::CommentReport,
|
||||||
community::Community,
|
community::Community,
|
||||||
community_post_tag::CommunityPostTag,
|
community_post_tag::Tag,
|
||||||
custom_emoji::CustomEmoji,
|
custom_emoji::CustomEmoji,
|
||||||
custom_emoji_keyword::CustomEmojiKeyword,
|
custom_emoji_keyword::CustomEmojiKeyword,
|
||||||
images::{ImageDetails, LocalImage},
|
images::{ImageDetails, LocalImage},
|
||||||
|
@ -247,5 +247,5 @@ pub struct LocalImageView {
|
||||||
#[serde(transparent)]
|
#[serde(transparent)]
|
||||||
#[cfg_attr(feature = "full", diesel(sql_type = Nullable<sql_types::Json>))]
|
#[cfg_attr(feature = "full", diesel(sql_type = Nullable<sql_types::Json>))]
|
||||||
pub struct PostCommunityPostTags {
|
pub struct PostCommunityPostTags {
|
||||||
pub tags: Vec<CommunityPostTag>,
|
pub tags: Vec<Tag>,
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,18 +0,0 @@
|
||||||
-- a tag for a post, valid in a community. created by mods of a community
|
|
||||||
CREATE TABLE community_post_tag (
|
|
||||||
id serial PRIMARY KEY,
|
|
||||||
ap_id text NOT NULL UNIQUE,
|
|
||||||
community_id int NOT NULL REFERENCES community (id) ON UPDATE CASCADE ON DELETE CASCADE,
|
|
||||||
name text NOT NULL,
|
|
||||||
published timestamptz NOT NULL DEFAULT now(),
|
|
||||||
updated timestamptz,
|
|
||||||
deleted timestamptz
|
|
||||||
);
|
|
||||||
|
|
||||||
-- an association between a post and a community post tag. created/updated by the post author or mods of a community
|
|
||||||
CREATE TABLE post_community_post_tag (
|
|
||||||
post_id int NOT NULL REFERENCES post (id) ON UPDATE CASCADE ON DELETE CASCADE,
|
|
||||||
community_post_tag_id int NOT NULL REFERENCES community_post_tag (id) ON UPDATE CASCADE ON DELETE CASCADE,
|
|
||||||
PRIMARY KEY (post_id, community_post_tag_id)
|
|
||||||
);
|
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
-- This file should undo anything in `up.sql`
|
-- This file should undo anything in `up.sql`
|
||||||
DROP TABLE post_community_post_tag;
|
DROP TABLE post_tag;
|
||||||
|
|
||||||
DROP TABLE community_post_tag;
|
DROP TABLE community_post_tag;
|
||||||
|
|
||||||
|
DROP TABLE tag;
|
||||||
|
|
26
migrations/2024-12-17-144959_community-post-tags/up.sql
Normal file
26
migrations/2024-12-17-144959_community-post-tags/up.sql
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
-- a tag for a post, valid in a community. created by mods of a community
|
||||||
|
CREATE TABLE tag (
|
||||||
|
id serial PRIMARY KEY,
|
||||||
|
ap_id text NOT NULL UNIQUE,
|
||||||
|
name text NOT NULL,
|
||||||
|
published timestamptz NOT NULL DEFAULT now(),
|
||||||
|
updated timestamptz,
|
||||||
|
deleted timestamptz
|
||||||
|
);
|
||||||
|
|
||||||
|
-- indicates this tag was created by the mod of a community and can be applied to posts in this community
|
||||||
|
CREATE TABLE community_post_tag (
|
||||||
|
community_id int NOT NULL REFERENCES community (id) ON UPDATE CASCADE ON DELETE CASCADE,
|
||||||
|
tag_id int NOT NULL REFERENCES tag (id) ON UPDATE CASCADE ON DELETE CASCADE,
|
||||||
|
published timestamptz NOT NULL DEFAULT now(),
|
||||||
|
PRIMARY KEY (community_id, tag_id)
|
||||||
|
);
|
||||||
|
|
||||||
|
-- an association between a post and a tag. created/updated by the post author or mods of a community
|
||||||
|
CREATE TABLE post_tag (
|
||||||
|
post_id int NOT NULL REFERENCES post (id) ON UPDATE CASCADE ON DELETE CASCADE,
|
||||||
|
tag_id int NOT NULL REFERENCES tag (id) ON UPDATE CASCADE ON DELETE CASCADE,
|
||||||
|
published timestamptz NOT NULL DEFAULT now(),
|
||||||
|
PRIMARY KEY (post_id, tag_id)
|
||||||
|
);
|
||||||
|
|
Loading…
Reference in a new issue