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 super::types::{
|
||||
DbFollowRequest,
|
||||
DbRelationship,
|
||||
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(
|
||||
db_client: &impl GenericClient,
|
||||
source_id: &Uuid,
|
||||
target_id: &Uuid,
|
||||
) -> Result<Relationship, DatabaseError> {
|
||||
let maybe_row = db_client.query_opt(
|
||||
"
|
||||
SELECT
|
||||
actor_profile.id AS profile_id,
|
||||
EXISTS (
|
||||
SELECT 1 FROM relationship
|
||||
WHERE source_id = $1 AND target_id = actor_profile.id
|
||||
) AS following,
|
||||
EXISTS (
|
||||
SELECT 1 FROM relationship
|
||||
WHERE source_id = actor_profile.id AND target_id = $1
|
||||
) AS followed_by,
|
||||
EXISTS (
|
||||
SELECT 1 FROM follow_request
|
||||
WHERE source_id = $1 AND target_id = actor_profile.id
|
||||
AND request_status = $3
|
||||
) AS requested
|
||||
FROM actor_profile
|
||||
WHERE actor_profile.id = $2
|
||||
",
|
||||
&[&source_id, &target_id, &FollowRequestStatus::Pending],
|
||||
).await?;
|
||||
let row = maybe_row.ok_or(DatabaseError::NotFound("profile"))?;
|
||||
let relationship = Relationship::try_from(&row)?;
|
||||
Ok(relationship)
|
||||
) -> Result<RelationshipMap, DatabaseError> {
|
||||
// NOTE: this method returns relationship map even if target does not exist
|
||||
let relationships = get_relationships(db_client, source_id, target_id).await?;
|
||||
let mut relationship_map = RelationshipMap { id: *target_id, ..Default::default() };
|
||||
for relationship in relationships {
|
||||
match relationship.relationship_type {
|
||||
RelationshipType::Follow => {
|
||||
if relationship.is_direct(source_id, target_id)? {
|
||||
relationship_map.following = true;
|
||||
} else {
|
||||
relationship_map.followed_by = true;
|
||||
};
|
||||
},
|
||||
RelationshipType::FollowRequest => {
|
||||
if relationship.is_direct(source_id, target_id)? {
|
||||
relationship_map.requested = true;
|
||||
};
|
||||
},
|
||||
};
|
||||
};
|
||||
Ok(relationship_map)
|
||||
}
|
||||
|
||||
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::errors::ConversionError;
|
||||
|
||||
#[derive(Serialize)]
|
||||
pub struct Relationship {
|
||||
pub id: Uuid,
|
||||
pub following: bool,
|
||||
pub followed_by: bool,
|
||||
pub requested: bool,
|
||||
#[derive(Debug)]
|
||||
pub enum RelationshipType {
|
||||
Follow,
|
||||
FollowRequest,
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
fn try_from(row: &Row) -> Result<Self, Self::Error> {
|
||||
let relationship = Relationship {
|
||||
id: row.try_get("profile_id")?,
|
||||
following: row.try_get("following")?,
|
||||
followed_by: row.try_get("followed_by")?,
|
||||
requested: row.try_get("requested")?,
|
||||
let relationship = Self {
|
||||
source_id: row.try_get("source_id")?,
|
||||
target_id: row.try_get("target_id")?,
|
||||
relationship_type: row.try_get("relationship_type")?,
|
||||
};
|
||||
Ok(relationship)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default, Serialize)]
|
||||
pub struct RelationshipMap {
|
||||
pub id: Uuid,
|
||||
pub following: bool,
|
||||
pub followed_by: bool,
|
||||
pub requested: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum FollowRequestStatus {
|
||||
Pending,
|
||||
|
|
Loading…
Reference in a new issue