chore: rewritten relation-related queries

This commit is contained in:
əlemi 2024-06-25 03:49:48 +02:00
parent 9cf461c7c4
commit 934d8ca8ef
Signed by: alemi
GPG key ID: A4895B84D311642C
5 changed files with 59 additions and 72 deletions

View file

@ -1,6 +1,6 @@
use std::{collections::BTreeSet, sync::Arc};
use sea_orm::{DatabaseConnection, DbErr};
use sea_orm::{DatabaseConnection, DbErr, QuerySelect, SelectColumns};
use crate::{config::Config, model};
use uriproxy::UriClass;
@ -52,8 +52,19 @@ impl Context {
// TODO maybe we could provide a more descriptive error...
let pkey = actor.private_key.as_deref().ok_or_else(|| DbErr::RecordNotFound("application private key".into()))?.to_string();
let relay_sinks = model::relation::Entity::followers(&actor.id, &db).await?.ok_or_else(|| DbErr::RecordNotFound(actor.id.clone()))?;
let relay_sources = model::relation::Entity::following(&actor.id, &db).await?.ok_or_else(|| DbErr::RecordNotFound(actor.id.clone()))?;
let relay_sinks = crate::Query::related(None, Some(actor.internal), false)
.select_only()
.select_column(crate::model::actor::Column::Id)
.into_tuple::<String>()
.all(&db)
.await?;
let relay_sources = crate::Query::related(Some(actor.internal), None, false)
.select_only()
.select_column(crate::model::actor::Column::Id)
.into_tuple::<String>()
.all(&db)
.await?;
let relay = Relays {
sources: BTreeSet::from_iter(relay_sources),

View file

@ -1,4 +1,4 @@
use sea_orm::{entity::prelude::*, QuerySelect, SelectColumns};
use sea_orm::entity::prelude::*;
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq)]
#[sea_orm(table_name = "relations")]
@ -84,66 +84,3 @@ impl Related<super::instance::Entity> for Entity {
}
impl ActiveModelBehavior for ActiveModel {}
impl Entity {
// TODO this is 2 queries!!! can it be optimized down to 1?
pub async fn followers(uid: &str, db: &impl ConnectionTrait) -> Result<Option<Vec<String>>, DbErr> {
let Some(internal_id) = super::actor::Entity::ap_to_internal(uid, db).await?
else {
return Ok(None);
};
let out = Entity::find()
.join(
sea_orm::JoinType::InnerJoin,
Entity::belongs_to(super::actor::Entity)
.from(Column::Follower)
.to(super::actor::Column::Internal)
.into()
)
.filter(Column::Accept.is_not_null())
.filter(Column::Following.eq(internal_id))
.select_only()
.select_column(super::actor::Column::Id)
.into_tuple::<String>()
.all(db)
.await?;
Ok(Some(out))
}
// TODO this is 2 queries!!! can it be optimized down to 1?
pub async fn following(uid: &str, db: &impl ConnectionTrait) -> Result<Option<Vec<String>>, DbErr> {
let Some(internal_id) = super::actor::Entity::ap_to_internal(uid, db).await?
else {
return Ok(None);
};
let out = Entity::find()
.join(
sea_orm::JoinType::InnerJoin,
Entity::belongs_to(super::actor::Entity)
.from(Column::Following)
.to(super::actor::Column::Internal)
.into()
)
.filter(Column::Accept.is_not_null())
.filter(Column::Follower.eq(internal_id))
.select_only()
.select_column(super::actor::Column::Id)
.into_tuple::<String>()
.all(db)
.await?;
Ok(Some(out))
}
// TODO this is 3 queries!!! can it be optimized down to 1?
pub fn is_following(follower: i64, following: i64) -> sea_orm::Selector<sea_orm::SelectGetableTuple<i64>> {
Entity::find()
.filter(Column::Accept.is_not_null())
.filter(Column::Follower.eq(follower))
.filter(Column::Following.eq(following))
.select_only()
.select_column(Column::Internal)
.into_tuple::<i64>()
}
}

View file

@ -70,4 +70,37 @@ impl Query {
select
}
pub fn related(from: Option<i64>, to: Option<i64>, pending: bool) -> Select<model::relation::Entity> {
let mut condition = Condition::all();
if let Some(from) = from {
condition = condition.add(model::relation::Column::Follower.eq(from));
}
if let Some(to) = to {
condition = condition.add(model::relation::Column::Following.eq(to));
}
if !pending {
condition = condition.add(model::relation::Column::Accept.is_not_null());
}
let mut select = model::relation::Entity::find()
.join(
sea_orm::JoinType::InnerJoin,
model::relation::Entity::belongs_to(model::actor::Entity)
.from(model::relation::Column::Follower)
.to(model::actor::Column::Internal)
.into()
)
.filter(condition)
.select_only();
for column in model::actor::Column::iter() {
select = select.select_column_as(column, format!("{}{}", model::actor::Entity.table_name(), column.to_string()));
}
select
}
}

View file

@ -1,5 +1,5 @@
use apb::target::Addressed;
use sea_orm::{ActiveValue::{NotSet, Set}, ConnectionTrait, DbErr, EntityTrait};
use sea_orm::{ActiveValue::{NotSet, Set}, ConnectionTrait, DbErr, EntityTrait, QuerySelect, SelectColumns};
use crate::traits::fetch::Fetcher;
@ -126,9 +126,15 @@ async fn expand_addressing(targets: Vec<String>, tx: &impl ConnectionTrait) -> R
// only used for groups anyway??
if target.ends_with("/followers") {
let target_id = target.replace("/followers", "");
let mut followers = crate::model::relation::Entity::followers(&target_id, tx)
let target_internal = crate::model::actor::Entity::ap_to_internal(&target_id, tx)
.await?
.unwrap_or_else(Vec::new);
.ok_or_else(|| DbErr::RecordNotFound(target_id.clone()))?;
let mut followers = crate::Query::related(None, Some(target_internal), false)
.select_only()
.select_column(crate::model::actor::Column::Id)
.into_tuple::<String>()
.all(tx)
.await?;
if followers.is_empty() { // stuff with zero addressing will never be seen again!!! TODO
followers.push(target_id);
}

View file

@ -43,8 +43,8 @@ pub async fn view(
// TODO these two queries are fast because of indexes but still are 2 subqueries for each
// user GET, not even parallelized... should maybe add these as joins on the main query? so
// that it's one roundtrip only
let followed_by_me = model::relation::Entity::is_following(my_id, internal_uid).any(ctx.db()).await?;
let following_me = model::relation::Entity::is_following(internal_uid, my_id).any(ctx.db()).await?;
let followed_by_me = upub::Query::related(Some(my_id), Some(internal_uid), false).any(ctx.db()).await?;
let following_me = upub::Query::related(Some(internal_uid), Some(my_id), false).any(ctx.db()).await?;
(Some(followed_by_me), Some(following_me))
},
};