Refactor get_relationship function and optimize SQL query
This commit is contained in:
parent
a81d0ef216
commit
7de7f7a501
2 changed files with 122 additions and 39 deletions
|
@ -14,40 +14,71 @@ use crate::models::profiles::types::DbActorProfile;
|
||||||
use crate::utils::id::new_uuid;
|
use crate::utils::id::new_uuid;
|
||||||
use super::types::{
|
use super::types::{
|
||||||
DbFollowRequest,
|
DbFollowRequest,
|
||||||
|
DbRelationship,
|
||||||
FollowRequestStatus,
|
FollowRequestStatus,
|
||||||
Relationship,
|
RelationshipMap,
|
||||||
|
RelationshipType,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
async fn get_relationships(
|
||||||
|
db_client: &impl GenericClient,
|
||||||
|
source_id: &Uuid,
|
||||||
|
target_id: &Uuid,
|
||||||
|
) -> Result<Vec<DbRelationship>, DatabaseError> {
|
||||||
|
let rows = db_client.query(
|
||||||
|
"
|
||||||
|
SELECT source_id, target_id, $4::smallint AS relationship_type
|
||||||
|
FROM relationship
|
||||||
|
WHERE
|
||||||
|
source_id = $1 AND target_id = $2
|
||||||
|
OR
|
||||||
|
source_id = $2 AND target_id = $1
|
||||||
|
UNION ALL
|
||||||
|
SELECT source_id, target_id, $5 AS relationship_type
|
||||||
|
FROM follow_request
|
||||||
|
WHERE
|
||||||
|
source_id = $1 AND target_id = $2
|
||||||
|
AND request_status = $3
|
||||||
|
",
|
||||||
|
&[
|
||||||
|
&source_id,
|
||||||
|
&target_id,
|
||||||
|
&FollowRequestStatus::Pending,
|
||||||
|
&RelationshipType::Follow,
|
||||||
|
&RelationshipType::FollowRequest,
|
||||||
|
],
|
||||||
|
).await?;
|
||||||
|
let relationships = rows.iter()
|
||||||
|
.map(DbRelationship::try_from)
|
||||||
|
.collect::<Result<_, _>>()?;
|
||||||
|
Ok(relationships)
|
||||||
|
}
|
||||||
|
|
||||||
pub async fn get_relationship(
|
pub async fn get_relationship(
|
||||||
db_client: &impl GenericClient,
|
db_client: &impl GenericClient,
|
||||||
source_id: &Uuid,
|
source_id: &Uuid,
|
||||||
target_id: &Uuid,
|
target_id: &Uuid,
|
||||||
) -> Result<Relationship, DatabaseError> {
|
) -> Result<RelationshipMap, DatabaseError> {
|
||||||
let maybe_row = db_client.query_opt(
|
// NOTE: this method returns relationship map even if target does not exist
|
||||||
"
|
let relationships = get_relationships(db_client, source_id, target_id).await?;
|
||||||
SELECT
|
let mut relationship_map = RelationshipMap { id: *target_id, ..Default::default() };
|
||||||
actor_profile.id AS profile_id,
|
for relationship in relationships {
|
||||||
EXISTS (
|
match relationship.relationship_type {
|
||||||
SELECT 1 FROM relationship
|
RelationshipType::Follow => {
|
||||||
WHERE source_id = $1 AND target_id = actor_profile.id
|
if relationship.is_direct(source_id, target_id)? {
|
||||||
) AS following,
|
relationship_map.following = true;
|
||||||
EXISTS (
|
} else {
|
||||||
SELECT 1 FROM relationship
|
relationship_map.followed_by = true;
|
||||||
WHERE source_id = actor_profile.id AND target_id = $1
|
};
|
||||||
) AS followed_by,
|
},
|
||||||
EXISTS (
|
RelationshipType::FollowRequest => {
|
||||||
SELECT 1 FROM follow_request
|
if relationship.is_direct(source_id, target_id)? {
|
||||||
WHERE source_id = $1 AND target_id = actor_profile.id
|
relationship_map.requested = true;
|
||||||
AND request_status = $3
|
};
|
||||||
) AS requested
|
},
|
||||||
FROM actor_profile
|
};
|
||||||
WHERE actor_profile.id = $2
|
};
|
||||||
",
|
Ok(relationship_map)
|
||||||
&[&source_id, &target_id, &FollowRequestStatus::Pending],
|
|
||||||
).await?;
|
|
||||||
let row = maybe_row.ok_or(DatabaseError::NotFound("profile"))?;
|
|
||||||
let relationship = Relationship::try_from(&row)?;
|
|
||||||
Ok(relationship)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn follow(
|
pub async fn follow(
|
||||||
|
|
|
@ -8,29 +8,81 @@ use uuid::Uuid;
|
||||||
use crate::database::int_enum::{int_enum_from_sql, int_enum_to_sql};
|
use crate::database::int_enum::{int_enum_from_sql, int_enum_to_sql};
|
||||||
use crate::errors::ConversionError;
|
use crate::errors::ConversionError;
|
||||||
|
|
||||||
#[derive(Serialize)]
|
#[derive(Debug)]
|
||||||
pub struct Relationship {
|
pub enum RelationshipType {
|
||||||
pub id: Uuid,
|
Follow,
|
||||||
pub following: bool,
|
FollowRequest,
|
||||||
pub followed_by: bool,
|
|
||||||
pub requested: bool,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TryFrom<&Row> for Relationship {
|
impl From<&RelationshipType> for i16 {
|
||||||
|
fn from(value: &RelationshipType) -> i16 {
|
||||||
|
match value {
|
||||||
|
RelationshipType::Follow => 1,
|
||||||
|
RelationshipType::FollowRequest => 2,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TryFrom<i16> for RelationshipType {
|
||||||
|
type Error = ConversionError;
|
||||||
|
|
||||||
|
fn try_from(value: i16) -> Result<Self, Self::Error> {
|
||||||
|
let relationship_type = match value {
|
||||||
|
1 => Self::Follow,
|
||||||
|
2 => Self::FollowRequest,
|
||||||
|
_ => return Err(ConversionError),
|
||||||
|
};
|
||||||
|
Ok(relationship_type)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int_enum_from_sql!(RelationshipType);
|
||||||
|
int_enum_to_sql!(RelationshipType);
|
||||||
|
|
||||||
|
pub struct DbRelationship {
|
||||||
|
pub source_id: Uuid,
|
||||||
|
pub target_id: Uuid,
|
||||||
|
pub relationship_type: RelationshipType,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DbRelationship {
|
||||||
|
pub fn is_direct(
|
||||||
|
&self,
|
||||||
|
source_id: &Uuid,
|
||||||
|
target_id: &Uuid,
|
||||||
|
) -> Result<bool, ConversionError> {
|
||||||
|
if &self.source_id == source_id && &self.target_id == target_id {
|
||||||
|
Ok(true)
|
||||||
|
} else if &self.source_id == target_id && &self.target_id == source_id {
|
||||||
|
Ok(false)
|
||||||
|
} else {
|
||||||
|
Err(ConversionError)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TryFrom<&Row> for DbRelationship {
|
||||||
|
|
||||||
type Error = tokio_postgres::Error;
|
type Error = tokio_postgres::Error;
|
||||||
|
|
||||||
fn try_from(row: &Row) -> Result<Self, Self::Error> {
|
fn try_from(row: &Row) -> Result<Self, Self::Error> {
|
||||||
let relationship = Relationship {
|
let relationship = Self {
|
||||||
id: row.try_get("profile_id")?,
|
source_id: row.try_get("source_id")?,
|
||||||
following: row.try_get("following")?,
|
target_id: row.try_get("target_id")?,
|
||||||
followed_by: row.try_get("followed_by")?,
|
relationship_type: row.try_get("relationship_type")?,
|
||||||
requested: row.try_get("requested")?,
|
|
||||||
};
|
};
|
||||||
Ok(relationship)
|
Ok(relationship)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Default, Serialize)]
|
||||||
|
pub struct RelationshipMap {
|
||||||
|
pub id: Uuid,
|
||||||
|
pub following: bool,
|
||||||
|
pub followed_by: bool,
|
||||||
|
pub requested: bool,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum FollowRequestStatus {
|
pub enum FollowRequestStatus {
|
||||||
Pending,
|
Pending,
|
||||||
|
|
Loading…
Reference in a new issue