tuitbot_core/storage/watchtower/
edges.rs1use crate::error::StorageError;
4use crate::storage::DbPool;
5
6#[derive(Debug, Clone)]
8pub struct NewEdge {
9 pub source_node_id: i64,
10 pub target_node_id: i64,
11 pub edge_type: String,
12 pub edge_label: Option<String>,
13 pub source_chunk_id: Option<i64>,
14}
15
16#[derive(Debug, Clone, serde::Serialize)]
18pub struct NoteEdge {
19 pub id: i64,
20 pub account_id: String,
21 pub source_node_id: i64,
22 pub target_node_id: i64,
23 pub edge_type: String,
24 pub edge_label: Option<String>,
25 pub source_chunk_id: Option<i64>,
26 pub created_at: String,
27}
28
29type NoteEdgeRow = (
31 i64,
32 String,
33 i64,
34 i64,
35 String,
36 Option<String>,
37 Option<i64>,
38 String,
39);
40
41impl NoteEdge {
42 fn from_row(r: NoteEdgeRow) -> Self {
43 Self {
44 id: r.0,
45 account_id: r.1,
46 source_node_id: r.2,
47 target_node_id: r.3,
48 edge_type: r.4,
49 edge_label: r.5,
50 source_chunk_id: r.6,
51 created_at: r.7,
52 }
53 }
54}
55
56pub async fn delete_edges_for_source(
65 pool: &DbPool,
66 account_id: &str,
67 source_node_id: i64,
68) -> Result<u64, StorageError> {
69 let result = sqlx::query("DELETE FROM note_edges WHERE account_id = ? AND source_node_id = ?")
70 .bind(account_id)
71 .bind(source_node_id)
72 .execute(pool)
73 .await
74 .map_err(|e| StorageError::Query { source: e })?;
75
76 Ok(result.rows_affected())
77}
78
79pub async fn insert_edge(
85 pool: &DbPool,
86 account_id: &str,
87 edge: &NewEdge,
88) -> Result<(), StorageError> {
89 sqlx::query(
90 "INSERT OR IGNORE INTO note_edges \
91 (account_id, source_node_id, target_node_id, edge_type, edge_label, source_chunk_id) \
92 VALUES (?, ?, ?, ?, ?, ?)",
93 )
94 .bind(account_id)
95 .bind(edge.source_node_id)
96 .bind(edge.target_node_id)
97 .bind(&edge.edge_type)
98 .bind(&edge.edge_label)
99 .bind(edge.source_chunk_id)
100 .execute(pool)
101 .await
102 .map_err(|e| StorageError::Query { source: e })?;
103
104 Ok(())
105}
106
107pub async fn insert_edges(
109 pool: &DbPool,
110 account_id: &str,
111 edges: &[NewEdge],
112) -> Result<(), StorageError> {
113 for edge in edges {
114 insert_edge(pool, account_id, edge).await?;
115 }
116 Ok(())
117}
118
119pub async fn get_edges_for_source(
125 pool: &DbPool,
126 account_id: &str,
127 source_node_id: i64,
128) -> Result<Vec<NoteEdge>, StorageError> {
129 let rows: Vec<NoteEdgeRow> = sqlx::query_as(
130 "SELECT id, account_id, source_node_id, target_node_id, \
131 edge_type, edge_label, source_chunk_id, created_at \
132 FROM note_edges \
133 WHERE account_id = ? AND source_node_id = ? \
134 ORDER BY id",
135 )
136 .bind(account_id)
137 .bind(source_node_id)
138 .fetch_all(pool)
139 .await
140 .map_err(|e| StorageError::Query { source: e })?;
141
142 Ok(rows.into_iter().map(NoteEdge::from_row).collect())
143}
144
145pub async fn get_edges_for_target(
147 pool: &DbPool,
148 account_id: &str,
149 target_node_id: i64,
150) -> Result<Vec<NoteEdge>, StorageError> {
151 let rows: Vec<NoteEdgeRow> = sqlx::query_as(
152 "SELECT id, account_id, source_node_id, target_node_id, \
153 edge_type, edge_label, source_chunk_id, created_at \
154 FROM note_edges \
155 WHERE account_id = ? AND target_node_id = ? \
156 ORDER BY id",
157 )
158 .bind(account_id)
159 .bind(target_node_id)
160 .fetch_all(pool)
161 .await
162 .map_err(|e| StorageError::Query { source: e })?;
163
164 Ok(rows.into_iter().map(NoteEdge::from_row).collect())
165}