velesdb_server/handlers/graph/
types.rs1use serde::{Deserialize, Serialize};
6use utoipa::{IntoParams, ToSchema};
7use velesdb_core::api_types::serde_id;
8
9#[derive(Debug, Clone, Serialize, ToSchema)]
11pub struct TraversalResultItem {
12 #[serde(serialize_with = "serde_id::serialize_id_as_string")]
14 #[cfg_attr(feature = "openapi", schema(value_type = String))]
15 pub target_id: u64,
16 pub depth: u32,
18 #[serde(serialize_with = "serde_id::serialize_ids_as_strings")]
20 #[cfg_attr(feature = "openapi", schema(schema_with = serde_id::ids_array_schema))]
21 pub path: Vec<u64>,
22}
23
24#[derive(Debug, Deserialize, IntoParams)]
26pub struct EdgeQueryParams {
27 #[param(example = "KNOWS")]
29 pub label: Option<String>,
30}
31
32#[derive(Debug, Deserialize, ToSchema)]
34pub struct TraverseRequest {
35 #[serde(deserialize_with = "serde_id::deserialize_id_from_string_or_number")]
37 #[cfg_attr(feature = "openapi", schema(schema_with = serde_id::id_input_schema))]
38 pub source: u64,
39 #[serde(default = "default_strategy")]
41 pub strategy: String,
42 #[serde(default = "default_max_depth")]
44 pub max_depth: u32,
45 #[serde(default = "default_limit")]
47 pub limit: usize,
48 #[serde(default)]
50 pub rel_types: Vec<String>,
51}
52
53fn default_strategy() -> String {
54 "bfs".to_string()
55}
56
57fn default_max_depth() -> u32 {
58 3
59}
60
61fn default_limit() -> usize {
62 100
63}
64
65#[derive(Debug, Serialize, ToSchema)]
67pub struct TraverseResponse {
68 pub results: Vec<TraversalResultItem>,
70 pub has_more: bool,
72 pub stats: TraversalStats,
74}
75
76#[derive(Debug, Serialize, ToSchema)]
78pub struct TraversalStats {
79 pub visited: usize,
81 pub depth_reached: u32,
83}
84
85#[derive(Debug, Serialize, ToSchema)]
87pub struct DegreeResponse {
88 pub in_degree: usize,
90 pub out_degree: usize,
92}
93
94#[derive(Debug, Serialize, ToSchema)]
96pub struct EdgesResponse {
97 pub edges: Vec<EdgeResponse>,
99 pub count: usize,
101}
102
103#[derive(Debug, Serialize, ToSchema)]
105pub struct EdgeResponse {
106 #[serde(serialize_with = "serde_id::serialize_id_as_string")]
108 #[cfg_attr(feature = "openapi", schema(value_type = String))]
109 pub id: u64,
110 #[serde(serialize_with = "serde_id::serialize_id_as_string")]
112 #[cfg_attr(feature = "openapi", schema(value_type = String))]
113 pub source: u64,
114 #[serde(serialize_with = "serde_id::serialize_id_as_string")]
116 #[cfg_attr(feature = "openapi", schema(value_type = String))]
117 pub target: u64,
118 pub label: String,
120 pub properties: serde_json::Value,
122}
123
124#[derive(Debug, Deserialize, ToSchema)]
126pub struct AddEdgeRequest {
127 #[serde(deserialize_with = "serde_id::deserialize_id_from_string_or_number")]
129 #[cfg_attr(feature = "openapi", schema(schema_with = serde_id::id_input_schema))]
130 pub id: u64,
131 #[serde(deserialize_with = "serde_id::deserialize_id_from_string_or_number")]
133 #[cfg_attr(feature = "openapi", schema(schema_with = serde_id::id_input_schema))]
134 pub source: u64,
135 #[serde(deserialize_with = "serde_id::deserialize_id_from_string_or_number")]
137 #[cfg_attr(feature = "openapi", schema(schema_with = serde_id::id_input_schema))]
138 pub target: u64,
139 pub label: String,
141 #[serde(default)]
143 pub properties: serde_json::Value,
144}
145
146#[derive(Debug, Serialize, ToSchema)]
152pub struct EdgeCountResponse {
153 pub count: usize,
155}
156
157#[derive(Debug, Deserialize, IntoParams)]
159pub struct NodeEdgeQueryParams {
160 #[serde(default = "default_direction")]
162 #[param(example = "out")]
163 pub direction: String,
164 #[param(example = "KNOWS")]
166 pub label: Option<String>,
167}
168
169fn default_direction() -> String {
170 "out".to_string()
171}
172
173#[derive(Debug, Serialize, ToSchema)]
175pub struct NodeListResponse {
176 #[serde(serialize_with = "serde_id::serialize_ids_as_strings")]
178 #[cfg_attr(feature = "openapi", schema(schema_with = serde_id::ids_array_schema))]
179 pub node_ids: Vec<u64>,
180 pub count: usize,
182}
183
184#[derive(Debug, Deserialize, ToSchema)]
186pub struct UpsertNodePayloadRequest {
187 pub payload: serde_json::Value,
189}
190
191#[derive(Debug, Serialize, ToSchema)]
193pub struct NodePayloadResponse {
194 #[serde(serialize_with = "serde_id::serialize_id_as_string")]
196 #[cfg_attr(feature = "openapi", schema(value_type = String))]
197 pub node_id: u64,
198 pub payload: Option<serde_json::Value>,
200}
201
202#[derive(Debug, Deserialize, ToSchema)]
204pub struct ParallelTraverseRequest {
205 #[serde(deserialize_with = "serde_id::deserialize_ids_from_string_or_number")]
208 #[cfg_attr(feature = "openapi", schema(schema_with = serde_id::ids_array_schema))]
209 pub sources: Vec<u64>,
210 #[serde(default = "default_max_depth")]
212 pub max_depth: u32,
213 #[serde(default = "default_limit")]
215 pub limit: usize,
216 #[serde(default)]
218 pub rel_types: Vec<String>,
219}
220
221#[derive(Debug, Deserialize, ToSchema)]
223pub struct GraphSearchRequest {
224 pub vector: Vec<f32>,
226 #[serde(default = "default_graph_search_k")]
228 pub top_k: usize,
229}
230
231fn default_graph_search_k() -> usize {
232 10
233}
234
235#[derive(Debug, Serialize, ToSchema)]
237pub struct GraphSearchResponse {
238 pub results: Vec<GraphSearchResultItem>,
240}
241
242#[derive(Debug, Serialize, ToSchema)]
244pub struct GraphSearchResultItem {
245 #[serde(serialize_with = "serde_id::serialize_id_as_string")]
247 #[cfg_attr(feature = "openapi", schema(value_type = String))]
248 pub id: u64,
249 pub score: f32,
251 pub payload: Option<serde_json::Value>,
253}
254
255#[derive(Debug, Deserialize, IntoParams)]
261pub struct StreamTraverseParams {
262 #[serde(deserialize_with = "serde_id::deserialize_id_from_string_or_number")]
264 #[param(example = 123)]
265 pub start_node: u64,
266 #[serde(default = "default_algorithm")]
268 #[param(example = "bfs")]
269 pub algorithm: String,
270 #[serde(default = "default_stream_max_depth")]
272 #[param(example = 5)]
273 pub max_depth: u32,
274 #[serde(default = "default_stream_limit")]
276 #[param(example = 1000)]
277 pub limit: usize,
278 #[serde(default)]
280 #[param(example = "KNOWS,FOLLOWS")]
281 pub relationship_types: Option<String>,
282}
283
284fn default_algorithm() -> String {
285 "bfs".to_string()
286}
287
288fn default_stream_max_depth() -> u32 {
289 5
290}
291
292fn default_stream_limit() -> usize {
293 1000
294}
295
296#[derive(Debug, Serialize, ToSchema)]
298pub struct StreamNodeEvent {
299 #[serde(serialize_with = "serde_id::serialize_id_as_string")]
301 #[cfg_attr(feature = "openapi", schema(value_type = String))]
302 pub id: u64,
303 pub depth: u32,
305 #[serde(serialize_with = "serde_id::serialize_ids_as_strings")]
307 #[cfg_attr(feature = "openapi", schema(schema_with = serde_id::ids_array_schema))]
308 pub path: Vec<u64>,
309}
310
311#[derive(Debug, Serialize, ToSchema)]
313pub struct StreamStatsEvent {
314 pub nodes_visited: usize,
316 pub elapsed_ms: u64,
318}
319
320#[derive(Debug, Serialize, ToSchema)]
322pub struct StreamDoneEvent {
323 pub total_nodes: usize,
325 pub max_depth_reached: u32,
327 pub elapsed_ms: u64,
329}
330
331#[derive(Debug, Serialize, ToSchema)]
333pub struct StreamErrorEvent {
334 pub error: String,
336}
337
338#[cfg(test)]
339mod tests {
340 use super::*;
341
342 #[test]
344 fn test_traversal_path_serialized_as_strings() {
345 let above_safe = (1_u64 << 53) + 1; let item = TraversalResultItem {
347 target_id: 2,
348 depth: 1,
349 path: vec![above_safe],
350 };
351 let json = serde_json::to_value(&item).unwrap();
352 assert_eq!(json["path"], serde_json::json!(["9007199254740993"]));
353 }
354
355 #[test]
357 fn test_stream_node_event_path_serialized_as_strings() {
358 let above_safe = (1_u64 << 53) + 1;
359 let event = StreamNodeEvent {
360 id: 1,
361 depth: 1,
362 path: vec![above_safe],
363 };
364 let json = serde_json::to_value(&event).unwrap();
365 assert_eq!(json["path"], serde_json::json!(["9007199254740993"]));
366 }
367
368 #[test]
370 fn test_node_list_ids_serialized_as_strings() {
371 let above_safe = (1_u64 << 53) + 1;
372 let response = NodeListResponse {
373 node_ids: vec![1, above_safe],
374 count: 2,
375 };
376 let json = serde_json::to_value(&response).unwrap();
377 assert_eq!(
378 json["node_ids"],
379 serde_json::json!(["1", "9007199254740993"])
380 );
381 }
382
383 #[test]
385 fn test_parallel_sources_accepts_strings_and_numbers() {
386 let from_strings: ParallelTraverseRequest =
387 serde_json::from_value(serde_json::json!({ "sources": ["9007199254740993", "2"] }))
388 .expect("string sources must deserialize");
389 assert_eq!(from_strings.sources, vec![(1_u64 << 53) + 1, 2]);
390
391 let from_numbers: ParallelTraverseRequest =
392 serde_json::from_value(serde_json::json!({ "sources": [3, 4] }))
393 .expect("numeric sources must still deserialize");
394 assert_eq!(from_numbers.sources, vec![3, 4]);
395 }
396}