nexus_common/models/post/
relationships.rs1use crate::db::{get_neo4j_graph, queries, RedisOps};
2use crate::types::DynError;
3use pubky_app_specs::{PubkyAppPost, PubkyAppPostKind};
4use serde::{Deserialize, Serialize};
5use utoipa::ToSchema;
6
7#[derive(Serialize, Deserialize, ToSchema, Default, Debug)]
8pub struct PostRelationships {
9 pub replied: Option<String>,
11 pub reposted: Option<String>,
13 pub mentioned: Vec<String>,
15}
16
17impl RedisOps for PostRelationships {}
18
19impl PostRelationships {
20 pub async fn get_by_id(
22 author_id: &str,
23 post_id: &str,
24 ) -> Result<Option<PostRelationships>, DynError> {
25 match Self::get_from_index(author_id, post_id).await? {
26 Some(counts) => Ok(Some(counts)),
27 None => {
28 let graph_response = Self::get_from_graph(author_id, post_id).await?;
29 if let Some(post_relationships) = graph_response {
30 post_relationships.put_to_index(author_id, post_id).await?;
31 return Ok(Some(post_relationships));
32 }
33 Ok(None)
34 }
35 }
36 }
37
38 pub async fn get_from_index(
39 author_id: &str,
40 post_id: &str,
41 ) -> Result<Option<PostRelationships>, DynError> {
42 if let Some(post_relationships) =
43 Self::try_from_index_json(&[author_id, post_id], None).await?
44 {
45 return Ok(Some(post_relationships));
46 }
47 Ok(None)
48 }
49
50 pub async fn get_from_graph(
52 author_id: &str,
53 post_id: &str,
54 ) -> Result<Option<PostRelationships>, DynError> {
55 let mut result;
56 {
57 let graph = get_neo4j_graph()?;
58 let query = queries::get::post_relationships(author_id, post_id);
59
60 let graph = graph.lock().await;
61 result = graph.execute(query).await?;
62 }
63
64 if let Some(row) = result.next().await? {
65 let replied_post_id: Option<String> = row.get("replied_post_id").unwrap_or(None);
66 let replied_author_id: Option<String> = row.get("replied_author_id").unwrap_or(None);
67 let reposted_post_id: Option<String> = row.get("reposted_post_id").unwrap_or(None);
68 let reposted_author_id: Option<String> = row.get("reposted_author_id").unwrap_or(None);
69 let mentioned: Vec<String> = row.get("mentioned_user_ids").unwrap_or(Vec::new());
70
71 let replied = match (replied_author_id, replied_post_id) {
72 (Some(author_id), Some(post_id)) => {
73 Some(format!("pubky://{author_id}/pub/pubky.app/posts/{post_id}"))
74 }
75 _ => None,
76 };
77 let reposted = match (reposted_author_id, reposted_post_id) {
78 (Some(author_id), Some(post_id)) => {
79 Some(format!("pubky://{author_id}/pub/pubky.app/posts/{post_id}"))
80 }
81 _ => None,
82 };
83 Ok(Some(Self {
84 replied,
85 reposted,
86 mentioned,
87 }))
88 } else {
89 Ok(None)
90 }
91 }
92
93 pub fn from_homeserver(post: &PubkyAppPost) -> Self {
95 let mut relationship = Self::default();
96
97 if let Some(parent_uri) = &post.parent {
98 relationship.replied = Some(parent_uri.to_string());
99 }
100
101 if let Some(embed) = &post.embed {
102 if let PubkyAppPostKind::Short = embed.kind {
103 relationship.reposted = Some(embed.uri.clone());
104 }
105 }
106 relationship
107 }
108
109 pub async fn put_to_index(&self, author_id: &str, post_id: &str) -> Result<(), DynError> {
110 self.put_index_json(&[author_id, post_id], None, None)
111 .await?;
112 Ok(())
113 }
114
115 pub async fn delete(author_id: &str, post_id: &str) -> Result<(), DynError> {
116 Self::remove_from_index_multiple_json(&[&[author_id, post_id]]).await?;
117 Ok(())
118 }
119
120 pub async fn reindex(author_id: &str, post_id: &str) -> Result<(), DynError> {
121 match Self::get_from_graph(author_id, post_id).await? {
122 Some(relationships) => relationships.put_to_index(author_id, post_id).await?,
123 None => tracing::error!(
124 "{}:{} Could not found post relationships in the graph",
125 author_id,
126 post_id
127 ),
128 }
129 Ok(())
130 }
131}