forked from alemi/upub
chore: rewritten relation-related queries
This commit is contained in:
parent
9cf461c7c4
commit
934d8ca8ef
5 changed files with 59 additions and 72 deletions
|
@ -1,6 +1,6 @@
|
||||||
use std::{collections::BTreeSet, sync::Arc};
|
use std::{collections::BTreeSet, sync::Arc};
|
||||||
|
|
||||||
use sea_orm::{DatabaseConnection, DbErr};
|
use sea_orm::{DatabaseConnection, DbErr, QuerySelect, SelectColumns};
|
||||||
|
|
||||||
use crate::{config::Config, model};
|
use crate::{config::Config, model};
|
||||||
use uriproxy::UriClass;
|
use uriproxy::UriClass;
|
||||||
|
@ -52,8 +52,19 @@ impl Context {
|
||||||
// TODO maybe we could provide a more descriptive error...
|
// 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 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_sinks = crate::Query::related(None, Some(actor.internal), false)
|
||||||
let relay_sources = model::relation::Entity::following(&actor.id, &db).await?.ok_or_else(|| DbErr::RecordNotFound(actor.id.clone()))?;
|
.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 {
|
let relay = Relays {
|
||||||
sources: BTreeSet::from_iter(relay_sources),
|
sources: BTreeSet::from_iter(relay_sources),
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
use sea_orm::{entity::prelude::*, QuerySelect, SelectColumns};
|
use sea_orm::entity::prelude::*;
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq)]
|
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq)]
|
||||||
#[sea_orm(table_name = "relations")]
|
#[sea_orm(table_name = "relations")]
|
||||||
|
@ -84,66 +84,3 @@ impl Related<super::instance::Entity> for Entity {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ActiveModelBehavior for ActiveModel {}
|
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>()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -70,4 +70,37 @@ impl Query {
|
||||||
|
|
||||||
select
|
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
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
use apb::target::Addressed;
|
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;
|
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??
|
// only used for groups anyway??
|
||||||
if target.ends_with("/followers") {
|
if target.ends_with("/followers") {
|
||||||
let target_id = target.replace("/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?
|
.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
|
if followers.is_empty() { // stuff with zero addressing will never be seen again!!! TODO
|
||||||
followers.push(target_id);
|
followers.push(target_id);
|
||||||
}
|
}
|
||||||
|
|
|
@ -43,8 +43,8 @@ pub async fn view(
|
||||||
// TODO these two queries are fast because of indexes but still are 2 subqueries for each
|
// 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
|
// user GET, not even parallelized... should maybe add these as joins on the main query? so
|
||||||
// that it's one roundtrip only
|
// that it's one roundtrip only
|
||||||
let followed_by_me = model::relation::Entity::is_following(my_id, internal_uid).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 = model::relation::Entity::is_following(internal_uid, my_id).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))
|
(Some(followed_by_me), Some(following_me))
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
Loading…
Reference in a new issue