nexus_common/models/post/
bookmark.rs1use crate::db::{execute_graph_operation, get_neo4j_graph, queries, OperationOutcome, RedisOps};
2use crate::types::DynError;
3use neo4rs::Relation;
4use serde::{Deserialize, Serialize};
5use utoipa::ToSchema;
6
7use super::PostStream;
8
9#[derive(Serialize, Deserialize, ToSchema, Default, Debug)]
10pub struct Bookmark {
11 pub id: String,
12 pub indexed_at: i64,
13}
14
15impl RedisOps for Bookmark {}
16
17impl Bookmark {
18 pub async fn put_to_graph(
19 author_id: &str,
20 post_id: &str,
21 user_id: &str,
22 bookmark_id: &str,
23 indexed_at: i64,
24 ) -> Result<OperationOutcome, DynError> {
25 let query = queries::put::create_post_bookmark(
26 user_id,
27 author_id,
28 post_id,
29 bookmark_id,
30 indexed_at,
31 );
32
33 execute_graph_operation(query).await
34 }
35
36 pub async fn get_by_id(
38 author_id: &str,
39 post_id: &str,
40 viewer_id: Option<&str>,
41 ) -> Result<Option<Bookmark>, DynError> {
42 let viewer_id = match viewer_id {
44 Some(viewer_id) => viewer_id,
45 None => return Ok(None),
46 };
47 match Self::get_from_index(author_id, post_id, viewer_id).await? {
48 Some(counts) => Ok(Some(counts)),
49 None => {
50 let graph_response = Self::get_from_graph(author_id, post_id, viewer_id).await?;
51 if let Some(bookmark) = graph_response {
52 bookmark.put_to_index(author_id, post_id, viewer_id).await?;
53 return Ok(Some(bookmark));
54 }
55 Ok(None)
56 }
57 }
58 }
59
60 pub async fn get_from_index(
61 author_id: &str,
62 post_id: &str,
63 viewer_id: &str,
64 ) -> Result<Option<Bookmark>, DynError> {
65 if let Some(bookmark) =
66 Self::try_from_index_json(&[author_id, post_id, viewer_id], None).await?
67 {
68 return Ok(Some(bookmark));
69 }
70 Ok(None)
71 }
72
73 pub async fn get_from_graph(
75 author_id: &str,
76 post_id: &str,
77 viewer_id: &str,
78 ) -> Result<Option<Bookmark>, DynError> {
79 let mut result;
80 {
81 let graph = get_neo4j_graph()?;
82 let query = queries::get::post_bookmark(author_id, post_id, viewer_id);
83
84 let graph = graph.lock().await;
85 result = graph.execute(query).await?;
86 }
87
88 if let Some(row) = result.next().await? {
89 let relation: Relation = match row.get("b") {
91 Ok(value) => value,
92 Err(_) => return Ok(None),
93 };
94 let bookmark = Self {
95 id: relation.get("id").unwrap_or_default(),
96 indexed_at: relation.get("indexed_at").unwrap_or_default(),
97 };
98 Ok(Some(bookmark))
99 } else {
100 Ok(None)
101 }
102 }
103
104 pub async fn put_to_index(
105 &self,
106 author_id: &str,
107 post_id: &str,
108 viewer_id: &str,
109 ) -> Result<(), DynError> {
110 self.put_index_json(&[author_id, post_id, viewer_id], None, None)
111 .await?;
112 PostStream::add_to_bookmarks_sorted_set(self, viewer_id, post_id, author_id).await?;
113 Ok(())
114 }
115
116 pub async fn reindex(user_id: &str) -> Result<(), DynError> {
119 let mut result;
120 {
121 let graph = get_neo4j_graph()?;
122 let query = queries::get::user_bookmarks(user_id);
123
124 let graph = graph.lock().await;
125 result = graph.execute(query).await?;
126 }
127
128 while let Some(row) = result.next().await? {
129 if let Some(relation) = row.get::<Option<Relation>>("b")? {
130 let bookmark = Bookmark {
131 id: relation.get("id").unwrap_or_default(),
132 indexed_at: relation.get("indexed_at").unwrap_or_default(),
133 };
134 let author_id = row.get("author_id")?;
135 let post_id = row.get("post_id")?;
136 bookmark.put_to_index(author_id, post_id, user_id).await?;
137 }
138 }
139 Ok(())
140 }
141
142 pub async fn del_from_graph(
143 user_id: &str,
144 bookmark_id: &str,
145 ) -> Result<Option<(String, String)>, DynError> {
146 let mut result;
147 {
148 let graph = get_neo4j_graph()?;
149 let query = queries::del::delete_bookmark(user_id, bookmark_id);
150
151 let graph = graph.lock().await;
152 result = graph.execute(query).await?;
153 }
154
155 while let Some(row) = result.next().await? {
156 let post_id: Option<String> = row.get("post_id").unwrap_or(None);
157 let author_id: Option<String> = row.get("author_id").unwrap_or(None);
158 if let (Some(post_id), Some(author_id)) = (post_id, author_id) {
159 return Ok(Some((post_id, author_id)));
160 }
161 }
162 Ok(None)
163 }
164
165 pub async fn del_from_index(
166 bookmarker_id: &str,
167 post_id: &str,
168 author_id: &str,
169 ) -> Result<(), DynError> {
170 Self::remove_from_index_multiple_json(&[&[author_id, post_id, bookmarker_id]]).await?;
171 PostStream::remove_from_bookmarks_sorted_set(bookmarker_id, post_id, author_id).await?;
172 Ok(())
173 }
174}