Refactored aliases
ci/woodpecker/push/ociImagePush Pipeline is running Details

This commit is contained in:
Natty 2024-01-08 20:15:51 +01:00
parent 3b1eab8069
commit a658452138
Signed by: natty
GPG Key ID: BF6CB659ADEE60EC
8 changed files with 352 additions and 269 deletions

View File

@ -15,11 +15,10 @@ use chrono::Utc;
use ext_calckey_model_migration::{Migrator, MigratorTrait}; use ext_calckey_model_migration::{Migrator, MigratorTrait};
use futures_util::StreamExt; use futures_util::StreamExt;
use redis::IntoConnectionInfo; use redis::IntoConnectionInfo;
use sea_orm::sea_query::IntoIden;
use sea_orm::ActiveValue::Set; use sea_orm::ActiveValue::Set;
use sea_orm::{ use sea_orm::{
ColumnTrait, ConnectOptions, DatabaseConnection, DbErr, EntityTrait, QueryFilter, RelationDef, ColumnTrait, ConnectOptions, DatabaseConnection, DbErr, EntityTrait, QueryFilter,
RelationTrait, TransactionTrait, TransactionTrait,
}; };
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use std::future::Future; use std::future::Future;
@ -44,18 +43,6 @@ pub enum CalckeyDbError {
DbError(#[from] DbErr), DbError(#[from] DbErr),
} }
trait AliasSourceExt {
fn with_alias<I: IntoIden>(&self, alias: I) -> RelationDef;
}
impl<T: RelationTrait> AliasSourceExt for T {
fn with_alias<I: IntoIden>(&self, alias: I) -> RelationDef {
let mut def = self.def();
def.from_tbl = def.from_tbl.alias(alias);
def
}
}
impl CalckeyModel { impl CalckeyModel {
pub async fn new(config: ConnectorConfig) -> Result<Self, CalckeyDbError> { pub async fn new(config: ConnectorConfig) -> Result<Self, CalckeyDbError> {
let opt = ConnectOptions::new(config.url) let opt = ConnectOptions::new(config.url)

View File

@ -1,21 +1,70 @@
use ext_calckey_model_migration::{Alias, Expr, IntoIden, SelectExpr, SelectStatement, TableRef}; use ext_calckey_model_migration::{
use sea_orm::{ColumnTrait, Condition, EntityTrait, Iden, Iterable, JoinType, RelationDef}; Alias, Expr, IntoIden, Quote, SelectExpr, SelectStatement, TableRef,
};
use sea_orm::{
ColumnTrait, Condition, DynIden, EntityTrait, Iden, Iterable, JoinType, RelationDef,
RelationTrait,
};
use std::fmt::Write;
#[derive(Clone)]
pub enum MagIden {
DynIden(DynIden),
Alias(Alias),
}
impl MagIden {
pub fn alias(alias: &str) -> MagIden {
Self::Alias(Alias::new(alias))
}
}
impl From<DynIden> for MagIden {
fn from(value: DynIden) -> Self {
Self::DynIden(value)
}
}
impl Iden for MagIden {
fn prepare(&self, s: &mut dyn Write, q: Quote) {
match self {
MagIden::DynIden(di) => di.prepare(s, q),
MagIden::Alias(a) => a.prepare(s, q),
}
}
fn quoted(&self, q: Quote) -> String {
match self {
MagIden::DynIden(di) => di.quoted(q),
MagIden::Alias(a) => a.quoted(q),
}
}
fn to_string(&self) -> String {
match self {
MagIden::DynIden(di) => di.to_string(),
MagIden::Alias(a) => a.to_string(),
}
}
fn unquoted(&self, s: &mut dyn Write) {
match self {
MagIden::DynIden(di) => di.unquoted(s),
MagIden::Alias(a) => a.unquoted(s),
}
}
}
pub(crate) trait SelectColumnsExt { pub(crate) trait SelectColumnsExt {
fn add_aliased_columns<T: EntityTrait>(&mut self, alias: &str) -> &mut Self; fn add_aliased_columns<T: EntityTrait>(&mut self, alias: &MagIden) -> &mut Self;
fn join_columns<I: IntoIden>( fn join_columns(&mut self, join: JoinType, rel: RelationDef, alias: &MagIden) -> &mut Self;
fn join_columns_on(
&mut self, &mut self,
join: JoinType, join: JoinType,
rel: RelationDef, rel: RelationDef,
alias: I, alias: &MagIden,
) -> &mut Self;
fn join_columns_on<I: IntoIden>(
&mut self,
join: JoinType,
rel: RelationDef,
alias: I,
condition: Condition, condition: Condition,
) -> &mut Self; ) -> &mut Self;
} }
@ -41,17 +90,15 @@ pub(crate) fn join_columns_default(rel: RelationDef) -> Condition {
} }
impl SelectColumnsExt for SelectStatement { impl SelectColumnsExt for SelectStatement {
fn add_aliased_columns<T: EntityTrait>(&mut self, iden: &str) -> &mut Self { fn add_aliased_columns<T: EntityTrait>(&mut self, iden: &MagIden) -> &mut Self {
for col in T::Column::iter() { for col in T::Column::iter() {
let column: &T::Column = &col; let column: &T::Column = &col;
let alias = iden.join(&col);
let alias = format!("{}{}", iden, col.to_string()); let column_ref = iden.col(column.as_column_ref().1);
let column_ref = Expr::col((Alias::new(iden), column.as_column_ref().1));
self.expr(SelectExpr { self.expr(SelectExpr {
expr: col.select_as(column_ref), expr: col.select_as(column_ref),
alias: Some(Alias::new(&alias).into_iden()), alias: Some(alias.into_iden()),
window: None, window: None,
}); });
} }
@ -59,48 +106,66 @@ impl SelectColumnsExt for SelectStatement {
self self
} }
fn join_columns<I: IntoIden>( fn join_columns(&mut self, join: JoinType, mut rel: RelationDef, alias: &MagIden) -> &mut Self {
&mut self, rel.to_tbl = rel.to_tbl.alias(alias.clone().into_iden());
join: JoinType,
mut rel: RelationDef,
alias: I,
) -> &mut Self {
let alias = alias.into_iden();
rel.to_tbl = rel.to_tbl.alias(alias);
self.join(join, rel.to_tbl.clone(), join_columns_default(rel)); self.join(join, rel.to_tbl.clone(), join_columns_default(rel));
self self
} }
fn join_columns_on<I: IntoIden>( fn join_columns_on(
&mut self, &mut self,
join: JoinType, join: JoinType,
mut rel: RelationDef, mut rel: RelationDef,
alias: I, alias: &MagIden,
condition: Condition, condition: Condition,
) -> &mut Self { ) -> &mut Self {
let alias = alias.into_iden(); rel.to_tbl = rel.to_tbl.alias(alias.clone().into_iden());
rel.to_tbl = rel.to_tbl.alias(alias);
self.join(join, rel.to_tbl.clone(), condition); self.join(join, rel.to_tbl.clone(), condition);
self self
} }
} }
pub(crate) fn joined_prefix_str(prefix: &str, suffix: &str) -> String { pub trait AliasColumnExt {
format!("{prefix}{suffix}") fn col(&self, col: impl IntoIden) -> Expr;
} }
pub(crate) fn joined_prefix_alias(prefix: &str, suffix: &str) -> Alias { impl<T: Iden + ?Sized> AliasColumnExt for T {
Alias::new(joined_prefix_str(prefix, suffix)) fn col(&self, col: impl IntoIden) -> Expr {
Expr::col((Alias::new(self.to_string()).into_iden(), col.into_iden()))
}
} }
pub(crate) fn joined_prefix(prefix: &str, suffix: &str) -> impl IntoIden { pub trait AliasSuffixExt {
joined_prefix_alias(prefix, suffix).into_iden() fn join(&self, suffix: &dyn Iden) -> MagIden;
fn join_str(&self, suffix: &str) -> MagIden;
fn join_as_str(&self, suffix: &dyn Iden) -> String;
fn join_str_as_str(&self, suffix: &str) -> String;
}
impl<T: Iden + ?Sized> AliasSuffixExt for T {
fn join(&self, suffix: &dyn Iden) -> MagIden {
MagIden::alias(&self.join_as_str(suffix))
}
fn join_str(&self, suffix: &str) -> MagIden {
MagIden::alias(&self.join_str_as_str(suffix))
}
fn join_as_str(&self, suffix: &dyn Iden) -> String {
format!("{}{}", self.to_string(), suffix.to_string())
}
fn join_str_as_str(&self, suffix: &str) -> String {
format!("{}{}", self.to_string(), suffix)
}
} }
pub(crate) trait EntityPrefixExt { pub(crate) trait EntityPrefixExt {
fn base_prefix_str(&self) -> String; fn base_prefix_str(&self) -> String;
fn base_prefix_alias(&self) -> Alias; fn base_prefix(&self) -> MagIden;
fn base_prefix(&self) -> impl IntoIden;
} }
impl<T: EntityTrait> EntityPrefixExt for T { impl<T: EntityTrait> EntityPrefixExt for T {
@ -108,11 +173,28 @@ impl<T: EntityTrait> EntityPrefixExt for T {
format!("{}.", self.table_name()) format!("{}.", self.table_name())
} }
fn base_prefix_alias(&self) -> Alias { fn base_prefix(&self) -> MagIden {
Alias::new(self.base_prefix_str()) MagIden::alias(&self.base_prefix_str())
} }
}
fn base_prefix(&self) -> impl IntoIden {
self.base_prefix_alias().into_iden() pub trait AliasSourceExt {
fn with_from_alias(&self, alias: &MagIden) -> RelationDef;
fn with_alias(&self, from: &MagIden, to: &MagIden) -> RelationDef;
}
impl<T: RelationTrait> AliasSourceExt for T {
fn with_from_alias(&self, alias: &MagIden) -> RelationDef {
let mut def = self.def();
def.from_tbl = def.from_tbl.alias(Alias::new(alias.clone().to_string()));
def
}
fn with_alias(&self, from: &MagIden, to: &MagIden) -> RelationDef {
let mut def = self.def();
def.from_tbl = def.from_tbl.alias(from.clone().into_iden());
def.to_tbl = def.to_tbl.alias(to.clone().into_iden());
def
} }
} }

View File

@ -0,0 +1,76 @@
use crate::model_ext::{AliasSuffixExt, EntityPrefixExt, MagIden};
use crate::note_model::{INTERACTION_REACTION, INTERACTION_RENOTE, RENOTE, REPLY, USER};
use crate::user_model::UserData;
use ck::note;
use ext_calckey_model_migration::IntoIden;
use sea_orm::sea_query::Alias;
use sea_orm::Iden;
use sea_orm::{DbErr, FromQueryResult, QueryResult};
use serde::{Deserialize, Serialize};
pub mod sub_interaction_renote {
use sea_orm::{DeriveColumn, EnumIter, FromQueryResult};
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, PartialEq, Eq, FromQueryResult, Serialize, Deserialize)]
pub struct Model {
pub renotes: Option<i64>,
}
#[derive(Copy, Clone, Debug, EnumIter, DeriveColumn)]
pub enum Column {
Renotes,
}
}
pub mod sub_interaction_reaction {
use sea_orm::{DeriveColumn, EnumIter, FromQueryResult};
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, PartialEq, Eq, FromQueryResult, Serialize, Deserialize)]
pub struct Model {
pub reaction_name: Option<String>,
}
#[derive(Copy, Clone, Debug, EnumIter, DeriveColumn)]
pub enum Column {
ReactionName,
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct NoteData {
pub note: note::Model,
pub interaction_user_renote: Option<sub_interaction_renote::Model>,
pub interaction_user_reaction: Option<sub_interaction_reaction::Model>,
pub user: UserData,
pub reply: Option<Box<NoteData>>,
pub renote: Option<Box<NoteData>>,
}
impl FromQueryResult for NoteData {
fn from_query_result(res: &QueryResult, prefix: &str) -> Result<Self, DbErr> {
let prefix = if prefix.is_empty() {
note::Entity.base_prefix()
} else {
MagIden::alias(&prefix)
};
Ok(NoteData {
note: note::Model::from_query_result(res, &prefix.to_string())?,
interaction_user_renote: sub_interaction_renote::Model::from_query_result_optional(
res,
&prefix.join_str_as_str(INTERACTION_RENOTE),
)?,
interaction_user_reaction: sub_interaction_reaction::Model::from_query_result_optional(
res,
&prefix.join_str_as_str(INTERACTION_REACTION),
)?,
user: UserData::from_query_result(res, &prefix.join_str_as_str(USER))?,
reply: NoteData::from_query_result_optional(res, &prefix.join_str_as_str(REPLY))?
.map(Box::new),
renote: NoteData::from_query_result_optional(res, &prefix.join_str_as_str(RENOTE))?
.map(Box::new),
})
}
}

View File

@ -1,61 +1,23 @@
pub mod data;
use ext_calckey_model_migration::SelectStatement; use ext_calckey_model_migration::SelectStatement;
use sea_orm::sea_query::{Alias, Asterisk, Expr, IntoIden, Query, SelectExpr, SimpleExpr}; use sea_orm::sea_query::{Asterisk, Expr, IntoIden, Query, SelectExpr, SimpleExpr};
use sea_orm::{ use sea_orm::{
Condition, DbErr, EntityName, EntityTrait, FromQueryResult, Iden, JoinType, QueryFilter, Condition, CursorTrait, EntityName, EntityTrait, Iden, JoinType, QueryFilter, QueryOrder,
QueryOrder, QueryResult, QuerySelect, QueryTrait, Select, QuerySelect, QueryTrait, Select,
}; };
use serde::{Deserialize, Serialize};
use ck::{note, note_reaction, user, user_note_pining}; use ck::{note, note_reaction, user, user_note_pining};
use data::{sub_interaction_reaction, sub_interaction_renote, NoteData};
use magnetar_sdk::types::SpanFilter; use magnetar_sdk::types::SpanFilter;
use crate::model_ext::{ use crate::model_ext::{
join_columns_default, joined_prefix, joined_prefix_alias, joined_prefix_str, EntityPrefixExt, join_columns_default, AliasColumnExt, AliasSourceExt, AliasSuffixExt, EntityPrefixExt, MagIden,
SelectColumnsExt, SelectColumnsExt,
}; };
use crate::paginated::PaginatedModel; use crate::paginated::PaginatedModel;
use crate::user_model::{UserData, UserResolver}; use crate::user_model::{UserResolveOptions, UserResolver};
use crate::{AliasSourceExt, CalckeyDbError, CalckeyModel}; use crate::{CalckeyDbError, CalckeyModel};
pub mod sub_interaction_renote {
use sea_orm::{DeriveColumn, EnumIter, FromQueryResult};
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, PartialEq, Eq, FromQueryResult, Serialize, Deserialize)]
pub struct Model {
pub renotes: Option<i64>,
}
#[derive(Copy, Clone, Debug, EnumIter, DeriveColumn)]
pub enum Column {
Renotes,
}
}
pub mod sub_interaction_reaction {
use sea_orm::{DeriveColumn, EnumIter, FromQueryResult};
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, PartialEq, Eq, FromQueryResult, Serialize, Deserialize)]
pub struct Model {
pub reaction_name: Option<String>,
}
#[derive(Copy, Clone, Debug, EnumIter, DeriveColumn)]
pub enum Column {
ReactionName,
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct NoteData {
pub note: note::Model,
pub interaction_user_renote: Option<sub_interaction_renote::Model>,
pub interaction_user_reaction: Option<sub_interaction_reaction::Model>,
pub user: UserData,
pub reply: Option<Box<NoteData>>,
pub renote: Option<Box<NoteData>>,
}
const PIN: &str = "pin."; const PIN: &str = "pin.";
const INTERACTION_REACTION: &str = "interaction.reaction."; const INTERACTION_REACTION: &str = "interaction.reaction.";
@ -64,37 +26,13 @@ const USER: &str = "user.";
const REPLY: &str = "reply."; const REPLY: &str = "reply.";
const RENOTE: &str = "renote."; const RENOTE: &str = "renote.";
impl FromQueryResult for NoteData {
fn from_query_result(res: &QueryResult, prefix: &str) -> Result<Self, DbErr> {
let fallback = format!("{}.", note::Entity.table_name());
let prefix = if prefix.is_empty() { &fallback } else { prefix };
Ok(NoteData {
note: note::Model::from_query_result(res, prefix)?,
interaction_user_renote: sub_interaction_renote::Model::from_query_result_optional(
res,
&joined_prefix_str(prefix, INTERACTION_RENOTE),
)?,
interaction_user_reaction: sub_interaction_reaction::Model::from_query_result_optional(
res,
&joined_prefix_str(prefix, INTERACTION_REACTION),
)?,
user: UserData::from_query_result(res, &joined_prefix_str(prefix, USER))?,
reply: NoteData::from_query_result_optional(res, &joined_prefix_str(prefix, REPLY))?
.map(Box::new),
renote: NoteData::from_query_result_optional(res, &joined_prefix_str(prefix, RENOTE))?
.map(Box::new),
})
}
}
pub struct NoteResolver { pub struct NoteResolver {
db: CalckeyModel, db: CalckeyModel,
user_resolver: UserResolver, user_resolver: UserResolver,
} }
pub trait NoteVisibilityFilterFactory: Send + Sync { pub trait NoteVisibilityFilterFactory: Send + Sync {
fn with_note_and_user_tables(&self, note: Option<Alias>) -> SimpleExpr; fn with_note_and_user_tables(&self, note: &dyn Iden) -> SimpleExpr;
} }
pub struct NoteResolveOptions { pub struct NoteResolveOptions {
@ -102,45 +40,39 @@ pub struct NoteResolveOptions {
pub visibility_filter: Box<dyn NoteVisibilityFilterFactory>, pub visibility_filter: Box<dyn NoteVisibilityFilterFactory>,
pub time_range: Option<SpanFilter>, pub time_range: Option<SpanFilter>,
pub limit: Option<u64>, pub limit: Option<u64>,
pub with_user: bool,
pub with_reply_target: bool, pub with_reply_target: bool,
pub with_renote_target: bool, pub with_renote_target: bool,
pub with_interactions_from: Option<String>, // User ID pub with_interactions_from: Option<String>, // User ID
pub only_pins_from: Option<String>, // User ID pub only_pins_from: Option<String>, // User ID
pub user_options: UserResolveOptions,
} }
trait SelectNoteInteractionsExt { trait SelectNoteInteractionsExt {
fn add_sub_select_interaction_reaction( fn add_sub_select_interaction_reaction(
&mut self, &mut self,
source_note_alias: &str, note_tbl: &MagIden,
alias: &str,
user_id: &str, user_id: &str,
) -> &mut Self; ) -> &mut Self;
fn add_sub_select_interaction_renote( fn add_sub_select_interaction_renote(&mut self, note_tbl: &MagIden, user_id: &str)
&mut self, -> &mut Self;
source_note_alias: &str,
alias: &str,
user_id: &str,
) -> &mut Self;
} }
impl SelectNoteInteractionsExt for SelectStatement { impl SelectNoteInteractionsExt for SelectStatement {
fn add_sub_select_interaction_reaction( fn add_sub_select_interaction_reaction(
&mut self, &mut self,
iden: &str, note_tbl: &MagIden,
prefix_alias: &str,
user_id: &str, user_id: &str,
) -> &mut Self { ) -> &mut Self {
let note_id_col = Expr::col((Alias::new(iden), note::Column::Id)); let note_id_col = note_tbl.col(note::Column::Id);
let alias = note_tbl
let column = sub_interaction_reaction::Column::ReactionName; .join_str(INTERACTION_REACTION)
let alias = format!("{}{}", prefix_alias, column.to_string()); .join(&sub_interaction_reaction::Column::ReactionName);
let sub_query = Query::select() let sub_query = Query::select()
.expr(SelectExpr { .expr(SelectExpr {
expr: Expr::col(note_reaction::Column::Reaction).into(), expr: Expr::col(note_reaction::Column::Reaction).into(),
alias: Some(Alias::new(alias).into_iden()), alias: Some(alias.into_iden()),
window: None, window: None,
}) })
.from(note_reaction::Entity) .from(note_reaction::Entity)
@ -155,28 +87,26 @@ impl SelectNoteInteractionsExt for SelectStatement {
fn add_sub_select_interaction_renote( fn add_sub_select_interaction_renote(
&mut self, &mut self,
iden: &str, note_tbl: &MagIden,
prefix_alias: &str,
user_id: &str, user_id: &str,
) -> &mut Self { ) -> &mut Self {
let note_id_col = Expr::col((Alias::new(iden), note::Column::Id)); let interaction_tbl_prefix = note_tbl.join_str(INTERACTION_RENOTE);
let renote_note_tbl = interaction_tbl_prefix.join_str("note");
let renote_note_tbl = joined_prefix_alias(prefix_alias, "note"); let alias = interaction_tbl_prefix.join(&sub_interaction_renote::Column::Renotes);
let column = sub_interaction_renote::Column::Renotes;
let alias = format!("{}{}", prefix_alias, column.to_string());
let sub_query = Query::select() let sub_query = Query::select()
.expr(SelectExpr { .expr(SelectExpr {
expr: Expr::count(Expr::col(Asterisk)), expr: Expr::count(Expr::col(Asterisk)),
alias: Some(Alias::new(alias).into_iden()), alias: Some(alias.into_iden()),
window: None, window: None,
}) })
.from_as(note::Entity, renote_note_tbl.clone()) .from_as(note::Entity, renote_note_tbl.clone().into_iden())
.cond_where( .cond_where(
Expr::col((renote_note_tbl.clone(), note::Column::RenoteId)).eq(note_id_col), renote_note_tbl
.col(note::Column::RenoteId)
.eq(note_tbl.col(note::Column::Id)),
) )
.and_where(Expr::col((renote_note_tbl.clone(), note::Column::UserId)).eq(user_id)) .and_where(renote_note_tbl.col(note::Column::UserId).eq(user_id))
.take() .take()
.into_sub_query_statement(); .into_sub_query_statement();
@ -186,7 +116,7 @@ impl SelectNoteInteractionsExt for SelectStatement {
} }
fn ids_into_expr(ids: &Vec<String>) -> SimpleExpr { fn ids_into_expr(ids: &Vec<String>) -> SimpleExpr {
let id_col = Expr::col((note::Entity.base_prefix(), note::Column::Id)); let id_col = note::Entity.base_prefix().col(note::Column::Id);
if ids.len() == 1 { if ids.len() == 1 {
id_col.eq(&ids[0]) id_col.eq(&ids[0])
@ -207,11 +137,11 @@ impl NoteResolver {
let select = self.resolve(options); let select = self.resolve(options);
let visibility_filter = options let visibility_filter = options
.visibility_filter .visibility_filter
.with_note_and_user_tables(Some(note::Entity.base_prefix_alias())); .with_note_and_user_tables(&note::Entity.base_prefix());
let time_filter = options let time_filter = options
.time_range .time_range
.as_ref() .as_ref()
.and_then(note::Model::range_into_expr); .and_then(note::Entity::range_into_expr);
let id_filter = options.ids.as_ref().map(ids_into_expr); let id_filter = options.ids.as_ref().map(ids_into_expr);
let notes = select let notes = select
@ -232,11 +162,11 @@ impl NoteResolver {
let select = self.resolve(options); let select = self.resolve(options);
let visibility_filter = options let visibility_filter = options
.visibility_filter .visibility_filter
.with_note_and_user_tables(Some(note::Entity.base_prefix_alias())); .with_note_and_user_tables(&note::Entity.base_prefix());
let time_filter = options let time_filter = options
.time_range .time_range
.as_ref() .as_ref()
.and_then(note::Model::range_into_expr); .and_then(note::Entity::range_into_expr);
let id_filter = options.ids.as_ref().map(ids_into_expr); let id_filter = options.ids.as_ref().map(ids_into_expr);
let notes = select let notes = select
@ -245,7 +175,7 @@ impl NoteResolver {
.apply_if(time_filter, Select::<note::Entity>::filter) .apply_if(time_filter, Select::<note::Entity>::filter)
.apply_if(options.only_pins_from.as_deref(), |s, _| { .apply_if(options.only_pins_from.as_deref(), |s, _| {
s.order_by_desc(Expr::col(( s.order_by_desc(Expr::col((
joined_prefix(&note::Entity.base_prefix_str(), PIN), note::Entity.base_prefix().into_iden().join_str(PIN),
user_note_pining::Column::CreatedAt, user_note_pining::Column::CreatedAt,
))) )))
}) })
@ -257,59 +187,70 @@ impl NoteResolver {
Ok(notes) Ok(notes)
} }
/*
pub async fn get_children(&self, options: &NoteResolveOptions) -> Select<note::Entity> {
let max_breadth: usize = 10;
let nth_child = Alias::new("nth_child");
let mut select = Query::select();
select.distinct();
select.columns([Alias::new("id")]);
select.from(cte);
select.and_where(Expr::col(nth_child).lt(max_breadth))
}
*/
fn attach_note( fn attach_note(
&self, &self,
q: &mut SelectStatement, q: &mut SelectStatement,
prefix: &str, note_tbl: &MagIden,
reply_depth: usize, reply_depth: usize,
renote_depth: usize, renote_depth: usize,
options: &NoteResolveOptions, options: &NoteResolveOptions,
user_resolver: &UserResolver, user_resolver: &UserResolver,
) { ) {
q.add_aliased_columns::<note::Entity>(prefix); q.add_aliased_columns::<note::Entity>(note_tbl);
// Add the note's author // Add the note's author
q.add_aliased_columns::<user::Entity>(&joined_prefix_str(prefix, USER)); let user_tbl = note_tbl.join_str(USER);
q.add_aliased_columns::<user::Entity>(&user_tbl);
q.join_columns( q.join_columns(
JoinType::LeftJoin, JoinType::LeftJoin,
note::Relation::User.with_alias(Alias::new(prefix).into_iden()), note::Relation::User.with_from_alias(note_tbl),
joined_prefix(prefix, USER), &user_tbl,
); );
// user_resolver.resolve(q, &user_tbl, &options.user_options);
// Interactions like renotes or reactions from the specified user // Interactions like renotes or reactions from the specified user
if let Some(user_id) = &options.with_interactions_from { if let Some(user_id) = &options.with_interactions_from {
q.add_sub_select_interaction_reaction( q.add_sub_select_interaction_reaction(note_tbl, user_id);
prefix, q.add_sub_select_interaction_renote(note_tbl, user_id);
&joined_prefix_str(prefix, INTERACTION_REACTION),
user_id,
);
q.add_sub_select_interaction_renote(
prefix,
&joined_prefix_str(prefix, INTERACTION_RENOTE),
user_id,
);
} }
// Recursively attach reply parents // Recursively attach reply parents
if reply_depth > 0 { if reply_depth > 0 {
let reply_tbl = note_tbl.join_str(REPLY);
let visibility_filter = options let visibility_filter = options
.visibility_filter .visibility_filter
.with_note_and_user_tables(Some(joined_prefix_alias(prefix, REPLY))); .with_note_and_user_tables(&reply_tbl);
let mut rel = note::Relation::SelfRef2.with_alias(Alias::new(prefix).into_iden());
rel.to_tbl = rel.to_tbl.alias(joined_prefix_alias(prefix, REPLY));
let condition = Condition::all() let condition = Condition::all()
.add(join_columns_default(rel)) .add(join_columns_default(
note::Relation::SelfRef2.with_alias(note_tbl, &reply_tbl),
))
.add(visibility_filter); .add(visibility_filter);
q.join_columns_on( q.join_columns_on(
JoinType::LeftJoin, JoinType::LeftJoin,
note::Relation::SelfRef2.with_alias(Alias::new(prefix).into_iden()), note::Relation::SelfRef2.with_from_alias(note_tbl),
joined_prefix(prefix, REPLY), &reply_tbl,
condition, condition,
); );
self.attach_note( self.attach_note(
q, q,
&joined_prefix_str(prefix, REPLY), &reply_tbl,
reply_depth - 1, reply_depth - 1,
renote_depth, renote_depth,
options, options,
@ -319,24 +260,25 @@ impl NoteResolver {
// Recursively attach renote/quote targets // Recursively attach renote/quote targets
if renote_depth > 0 { if renote_depth > 0 {
let renote_tbl = note_tbl.join_str(RENOTE);
let visibility_filter = options let visibility_filter = options
.visibility_filter .visibility_filter
.with_note_and_user_tables(Some(joined_prefix_alias(prefix, RENOTE))); .with_note_and_user_tables(&renote_tbl);
let mut rel = note::Relation::SelfRef1.with_alias(Alias::new(prefix).into_iden());
rel.to_tbl = rel.to_tbl.alias(joined_prefix_alias(prefix, RENOTE));
let condition = Condition::all() let condition = Condition::all()
.add(join_columns_default(rel)) .add(join_columns_default(
note::Relation::SelfRef1.with_alias(note_tbl, &renote_tbl),
))
.add(visibility_filter); .add(visibility_filter);
q.join_columns_on( q.join_columns_on(
JoinType::LeftJoin, JoinType::LeftJoin,
note::Relation::SelfRef1.with_alias(Alias::new(prefix).into_iden()), note::Relation::SelfRef1.with_from_alias(note_tbl),
joined_prefix(prefix, RENOTE), &renote_tbl,
condition, condition,
); );
self.attach_note( self.attach_note(
q, q,
&joined_prefix_str(prefix, RENOTE), &renote_tbl,
reply_depth, reply_depth,
renote_depth - 1, renote_depth - 1,
options, options,
@ -346,30 +288,24 @@ impl NoteResolver {
} }
pub fn resolve(&self, options: &NoteResolveOptions) -> Select<note::Entity> { pub fn resolve(&self, options: &NoteResolveOptions) -> Select<note::Entity> {
let prefix = note::Entity.base_prefix_str(); let note_tbl = note::Entity.base_prefix();
let mut select = Query::select(); let mut select = Query::select();
select.from_as(note::Entity, Alias::new(&prefix)); select.from_as(note::Entity, note_tbl.clone().into_iden());
if let Some(pins_user) = &options.only_pins_from { if let Some(pins_user) = &options.only_pins_from {
select select
.join_columns( .join_columns(
JoinType::InnerJoin, JoinType::InnerJoin,
note::Relation::UserNotePining.with_alias(Alias::new(&prefix)), note::Relation::UserNotePining.with_from_alias(&note_tbl),
joined_prefix(&prefix, PIN), &note_tbl.join_str(PIN),
) )
.and_where( .and_where(note_tbl.col(user_note_pining::Column::UserId).eq(pins_user));
Expr::col((
joined_prefix(&prefix, PIN),
user_note_pining::Column::UserId,
))
.eq(pins_user),
);
} }
self.attach_note( self.attach_note(
&mut select, &mut select,
&prefix, &note_tbl,
options.with_reply_target.then_some(1).unwrap_or_default(), options.with_reply_target.then_some(1).unwrap_or_default(),
options.with_renote_target.then_some(1).unwrap_or_default(), options.with_renote_target.then_some(1).unwrap_or_default(),
options, options,

View File

@ -54,7 +54,7 @@ pub trait PaginatedModel: 'static {
} }
} }
impl PaginatedModel for ck::note::Model { impl PaginatedModel for ck::note::Entity {
fn time_column() -> impl Iden { fn time_column() -> impl Iden {
ck::note::Column::CreatedAt ck::note::Column::CreatedAt
} }
@ -64,7 +64,7 @@ impl PaginatedModel for ck::note::Model {
} }
} }
impl PaginatedModel for ck::user::Model { impl PaginatedModel for ck::user::Entity {
fn time_column() -> impl Iden { fn time_column() -> impl Iden {
ck::user::Column::CreatedAt ck::user::Column::CreatedAt
} }
@ -74,7 +74,7 @@ impl PaginatedModel for ck::user::Model {
} }
} }
impl PaginatedModel for ck::following::Model { impl PaginatedModel for ck::following::Entity {
fn time_column() -> impl Iden { fn time_column() -> impl Iden {
ck::following::Column::CreatedAt ck::following::Column::CreatedAt
} }
@ -84,7 +84,7 @@ impl PaginatedModel for ck::following::Model {
} }
} }
impl PaginatedModel for ck::follow_request::Model { impl PaginatedModel for ck::follow_request::Entity {
fn time_column() -> impl Iden { fn time_column() -> impl Iden {
ck::follow_request::Column::CreatedAt ck::follow_request::Column::CreatedAt
} }

View File

@ -1,11 +1,9 @@
use crate::model_ext::EntityPrefixExt; use crate::model_ext::{AliasSourceExt, AliasSuffixExt, EntityPrefixExt, MagIden};
use crate::{ use crate::{model_ext::SelectColumnsExt, CalckeyModel};
model_ext::{joined_prefix, joined_prefix_str, SelectColumnsExt}, use ck::{drive_file, user};
AliasSourceExt, CalckeyModel, use ext_calckey_model_migration::{IntoIden, SelectStatement};
}; use sea_orm::sea_query::Alias;
use ck::{drive_file, follow_request, following, user}; use sea_orm::{DbErr, FromQueryResult, Iden, JoinType, QueryResult};
use ext_calckey_model_migration::{Alias, SelectStatement};
use sea_orm::{DbErr, FromQueryResult, JoinType, QueryResult};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Serialize, Deserialize)] #[derive(Debug, Clone, Serialize, Deserialize)]
@ -24,18 +22,21 @@ const BANNER: &str = "banner.";
impl FromQueryResult for UserData { impl FromQueryResult for UserData {
fn from_query_result(res: &QueryResult, prefix: &str) -> Result<Self, DbErr> { fn from_query_result(res: &QueryResult, prefix: &str) -> Result<Self, DbErr> {
let fallback = user::Entity.base_prefix_str(); let prefix = if prefix.is_empty() {
let prefix = if prefix.is_empty() { &fallback } else { prefix }; user::Entity.base_prefix()
} else {
MagIden::alias(prefix)
};
Ok(UserData { Ok(UserData {
user: user::Model::from_query_result(res, prefix)?, user: user::Model::from_query_result(res, &prefix.to_string())?,
avatar: drive_file::Model::from_query_result_optional( avatar: drive_file::Model::from_query_result_optional(
res, res,
&joined_prefix_str(prefix, AVATAR), &prefix.join_str_as_str(AVATAR),
)?, )?,
banner: drive_file::Model::from_query_result_optional( banner: drive_file::Model::from_query_result_optional(
res, res,
&joined_prefix_str(prefix, BANNER), &prefix.join_str_as_str(BANNER),
)?, )?,
}) })
} }
@ -53,26 +54,29 @@ impl UserResolver {
pub fn resolve( pub fn resolve(
&self, &self,
q: &mut SelectStatement, q: &mut SelectStatement,
prefix: &str, user_tbl: &MagIden,
UserResolveOptions { UserResolveOptions {
with_avatar_and_banner, with_avatar_and_banner,
}: &UserResolveOptions, }: &UserResolveOptions,
) { ) {
q.add_aliased_columns::<user::Entity>(prefix); q.add_aliased_columns::<user::Entity>(user_tbl);
if *with_avatar_and_banner { if *with_avatar_and_banner {
q.add_aliased_columns::<drive_file::Entity>(&joined_prefix_str(prefix, AVATAR)) let avatar_tbl = user_tbl.join_str(AVATAR);
.add_aliased_columns::<drive_file::Entity>(&joined_prefix_str(prefix, BANNER)); let banner_tbl = user_tbl.join_str(BANNER);
q.add_aliased_columns::<drive_file::Entity>(&avatar_tbl)
.add_aliased_columns::<drive_file::Entity>(&banner_tbl);
q.join_columns( q.join_columns(
JoinType::LeftJoin, JoinType::LeftJoin,
user::Relation::DriveFile2.with_alias(Alias::new(prefix)), user::Relation::DriveFile2.with_from_alias(user_tbl),
joined_prefix(prefix, AVATAR), &avatar_tbl,
) )
.join_columns( .join_columns(
JoinType::LeftJoin, JoinType::LeftJoin,
user::Relation::DriveFile1.with_alias(Alias::new(prefix)), user::Relation::DriveFile1.with_from_alias(user_tbl),
joined_prefix(prefix, BANNER), &banner_tbl,
); );
} }
} }

View File

@ -1,8 +1,7 @@
use magnetar_calckey_model::ck; use magnetar_calckey_model::ck;
use magnetar_calckey_model::note_model::sub_interaction_renote; use magnetar_calckey_model::note_model::data::sub_interaction_renote;
use magnetar_sdk::types::note::{ use magnetar_sdk::types::note::{
NoteDetailExt, NoteSelfContextExt, PackNoteMaybeAttachments, PackNoteMaybeFull, PackPollBase, NoteDetailExt, NoteSelfContextExt, PackNoteMaybeFull, PackPollBase, ReactionPair,
ReactionPair,
}; };
use magnetar_sdk::types::user::PackUserBase; use magnetar_sdk::types::user::PackUserBase;
use magnetar_sdk::types::{ use magnetar_sdk::types::{

View File

@ -12,14 +12,15 @@ use futures_util::future::{try_join_all, BoxFuture};
use futures_util::{FutureExt, StreamExt, TryFutureExt, TryStreamExt}; use futures_util::{FutureExt, StreamExt, TryFutureExt, TryStreamExt};
use magnetar_calckey_model::ck::sea_orm_active_enums::NoteVisibilityEnum; use magnetar_calckey_model::ck::sea_orm_active_enums::NoteVisibilityEnum;
use magnetar_calckey_model::emoji::EmojiTag; use magnetar_calckey_model::emoji::EmojiTag;
use magnetar_calckey_model::note_model::{ use magnetar_calckey_model::model_ext::AliasColumnExt;
sub_interaction_reaction, sub_interaction_renote, NoteData, NoteResolveOptions, use magnetar_calckey_model::note_model::data::{
NoteVisibilityFilterFactory, sub_interaction_reaction, sub_interaction_renote, NoteData,
}; };
use magnetar_calckey_model::note_model::{NoteResolveOptions, NoteVisibilityFilterFactory};
use magnetar_calckey_model::poll::PollResolver; use magnetar_calckey_model::poll::PollResolver;
use magnetar_calckey_model::sea_orm::prelude::Expr; use magnetar_calckey_model::sea_orm::sea_query::{IntoIden, PgFunc, Query, SimpleExpr};
use magnetar_calckey_model::sea_orm::sea_query::{Alias, IntoIden, PgFunc, Query, SimpleExpr}; use magnetar_calckey_model::sea_orm::{ActiveEnum, ColumnTrait, Iden, IntoSimpleExpr};
use magnetar_calckey_model::sea_orm::{ActiveEnum, ColumnTrait, IntoSimpleExpr}; use magnetar_calckey_model::user_model::UserResolveOptions;
use magnetar_calckey_model::{ck, CalckeyDbError}; use magnetar_calckey_model::{ck, CalckeyDbError};
use magnetar_common::util::{parse_reaction, RawReaction}; use magnetar_common::util::{parse_reaction, RawReaction};
use magnetar_sdk::mmm::Token; use magnetar_sdk::mmm::Token;
@ -43,22 +44,12 @@ use super::user::UserShapedData;
pub struct NoteVisibilityFilterSimple(Option<String>); pub struct NoteVisibilityFilterSimple(Option<String>);
impl NoteVisibilityFilterFactory for NoteVisibilityFilterSimple { impl NoteVisibilityFilterFactory for NoteVisibilityFilterSimple {
fn with_note_and_user_tables(&self, note_tbl: Option<Alias>) -> SimpleExpr { fn with_note_and_user_tables(&self, note_tbl: &dyn Iden) -> SimpleExpr {
let note_tbl_name = let is_public = note_tbl
note_tbl.map_or_else(|| ck::note::Entity.into_iden(), |a| a.into_iden()); .col(ck::note::Column::Visibility)
let note_visibility = Expr::col((note_tbl_name.clone(), ck::note::Column::Visibility));
let note_mentions = Expr::col((note_tbl_name.clone(), ck::note::Column::Mentions));
let note_reply_user_id = Expr::col((note_tbl_name.clone(), ck::note::Column::ReplyUserId));
let note_visible_user_ids =
Expr::col((note_tbl_name.clone(), ck::note::Column::VisibleUserIds));
let note_user_id = Expr::col((note_tbl_name, ck::note::Column::UserId));
let is_public = note_visibility
.clone()
.eq(NoteVisibilityEnum::Public.as_enum()) .eq(NoteVisibilityEnum::Public.as_enum())
.or(note_visibility .or(note_tbl
.clone() .col(ck::note::Column::Visibility)
.eq(NoteVisibilityEnum::Home.as_enum())); .eq(NoteVisibilityEnum::Home.as_enum()));
let Some(user_id_str) = &self.0 else { let Some(user_id_str) = &self.0 else {
@ -67,30 +58,34 @@ impl NoteVisibilityFilterFactory for NoteVisibilityFilterSimple {
let self_user_id = SimpleExpr::Constant(user_id_str.into()); let self_user_id = SimpleExpr::Constant(user_id_str.into());
let is_self = note_user_id.clone().eq(self_user_id.clone()); let is_self = note_tbl
.col(ck::note::Column::UserId)
.eq(self_user_id.clone());
let is_visible_specified = { let is_visible_specified = {
let either_specified_or_followers = note_visibility let either_specified_or_followers = note_tbl
.clone() .col(ck::note::Column::Visibility)
.eq(NoteVisibilityEnum::Specified.as_enum()) .eq(NoteVisibilityEnum::Specified.as_enum())
.or(note_visibility .or(note_tbl
.clone() .col(ck::note::Column::Visibility)
.eq(NoteVisibilityEnum::Followers.as_enum())) .eq(NoteVisibilityEnum::Followers.as_enum()));
.into_simple_expr();
let mentioned_or_specified = self_user_id let mentioned_or_specified = self_user_id
.clone() .clone()
.eq(PgFunc::any(note_mentions.into_simple_expr())) .eq(PgFunc::any(
.or(self_user_id.eq(PgFunc::any(note_visible_user_ids))); note_tbl.col(ck::note::Column::Mentions).into_simple_expr(),
))
.or(self_user_id.eq(PgFunc::any(note_tbl.col(ck::note::Column::VisibleUserIds))));
either_specified_or_followers.and(mentioned_or_specified) either_specified_or_followers.and(mentioned_or_specified)
}; };
let is_visible_followers = { let is_visible_followers = {
note_visibility note_tbl
.col(ck::note::Column::Visibility)
.eq(NoteVisibilityEnum::Followers.as_enum()) .eq(NoteVisibilityEnum::Followers.as_enum())
.and( .and(
note_user_id.in_subquery( note_tbl.col(ck::note::Column::UserId).in_subquery(
Query::select() Query::select()
.column(ck::following::Column::FolloweeId) .column(ck::following::Column::FolloweeId)
.from(ck::following::Entity) .from(ck::following::Entity)
@ -98,7 +93,7 @@ impl NoteVisibilityFilterFactory for NoteVisibilityFilterSimple {
.to_owned(), .to_owned(),
), ),
) )
.or(note_reply_user_id.eq(user_id_str)) .or(note_tbl.col(ck::note::Column::ReplyUserId).eq(user_id_str))
}; };
is_self is_self
@ -584,7 +579,9 @@ impl NoteModel {
)), )),
time_range: None, time_range: None,
limit: None, limit: None,
with_user: self.with_context, user_options: UserResolveOptions {
with_avatar_and_banner: self.with_context,
},
with_reply_target: self.with_context, with_reply_target: self.with_context,
with_renote_target: self.with_context, with_renote_target: self.with_context,
with_interactions_from: self with_interactions_from: self
@ -620,7 +617,9 @@ impl NoteModel {
)), )),
time_range: None, time_range: None,
limit: None, limit: None,
with_user: self.with_context, user_options: UserResolveOptions {
with_avatar_and_banner: self.with_context,
},
with_reply_target: self.with_context, with_reply_target: self.with_context,
with_renote_target: self.with_context, with_renote_target: self.with_context,
with_interactions_from: self with_interactions_from: self