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, Deserialize, ToSchema)]
148pub struct AddEdgesBatchRequest {
149 pub edges: Vec<AddEdgeRequest>,
151}
152
153#[derive(Debug, Serialize, ToSchema)]
155pub struct AddEdgesBatchResponse {
156 pub added: usize,
160}
161
162#[derive(Debug, Serialize, ToSchema)]
168pub struct EdgeCountResponse {
169 pub count: usize,
171}
172
173#[derive(Debug, Deserialize, IntoParams)]
175pub struct NodeEdgeQueryParams {
176 #[serde(default = "default_direction")]
178 #[param(example = "out")]
179 pub direction: String,
180 #[param(example = "KNOWS")]
182 pub label: Option<String>,
183}
184
185fn default_direction() -> String {
186 "out".to_string()
187}
188
189#[derive(Debug, Serialize, ToSchema)]
191pub struct NodeListResponse {
192 #[serde(serialize_with = "serde_id::serialize_ids_as_strings")]
194 #[cfg_attr(feature = "openapi", schema(schema_with = serde_id::ids_array_schema))]
195 pub node_ids: Vec<u64>,
196 pub count: usize,
198}
199
200#[derive(Debug, Deserialize, ToSchema)]
202pub struct UpsertNodePayloadRequest {
203 pub payload: serde_json::Value,
205}
206
207#[derive(Debug, Serialize, ToSchema)]
209pub struct NodePayloadResponse {
210 #[serde(serialize_with = "serde_id::serialize_id_as_string")]
212 #[cfg_attr(feature = "openapi", schema(value_type = String))]
213 pub node_id: u64,
214 pub payload: Option<serde_json::Value>,
216}
217
218#[derive(Debug, Deserialize, ToSchema)]
220pub struct ParallelTraverseRequest {
221 #[serde(deserialize_with = "serde_id::deserialize_ids_from_string_or_number")]
224 #[cfg_attr(feature = "openapi", schema(schema_with = serde_id::ids_array_schema))]
225 pub sources: Vec<u64>,
226 #[serde(default = "default_max_depth")]
228 pub max_depth: u32,
229 #[serde(default = "default_limit")]
231 pub limit: usize,
232 #[serde(default)]
234 pub rel_types: Vec<String>,
235}
236
237#[derive(Debug, Deserialize, ToSchema)]
239pub struct GraphSearchRequest {
240 pub vector: Vec<f32>,
242 #[serde(default = "default_graph_search_k")]
244 pub top_k: usize,
245}
246
247fn default_graph_search_k() -> usize {
248 10
249}
250
251#[derive(Debug, Serialize, ToSchema)]
253pub struct GraphSearchResponse {
254 pub results: Vec<GraphSearchResultItem>,
256}
257
258#[derive(Debug, Serialize, ToSchema)]
260pub struct GraphSearchResultItem {
261 #[serde(serialize_with = "serde_id::serialize_id_as_string")]
263 #[cfg_attr(feature = "openapi", schema(value_type = String))]
264 pub id: u64,
265 pub score: f32,
267 pub payload: Option<serde_json::Value>,
269}
270
271#[derive(Debug, Deserialize, IntoParams)]
277pub struct StreamTraverseParams {
278 #[serde(deserialize_with = "serde_id::deserialize_id_from_string_or_number")]
280 #[param(example = 123)]
281 pub start_node: u64,
282 #[serde(default = "default_algorithm")]
284 #[param(example = "bfs")]
285 pub algorithm: String,
286 #[serde(default = "default_stream_max_depth")]
288 #[param(example = 5)]
289 pub max_depth: u32,
290 #[serde(default = "default_stream_limit")]
292 #[param(example = 1000)]
293 pub limit: usize,
294 #[serde(default)]
296 #[param(example = "KNOWS,FOLLOWS")]
297 pub relationship_types: Option<String>,
298}
299
300fn default_algorithm() -> String {
301 "bfs".to_string()
302}
303
304fn default_stream_max_depth() -> u32 {
305 5
306}
307
308fn default_stream_limit() -> usize {
309 1000
310}
311
312#[derive(Debug, Serialize, ToSchema)]
314pub struct StreamNodeEvent {
315 #[serde(serialize_with = "serde_id::serialize_id_as_string")]
317 #[cfg_attr(feature = "openapi", schema(value_type = String))]
318 pub id: u64,
319 pub depth: u32,
321 #[serde(serialize_with = "serde_id::serialize_ids_as_strings")]
323 #[cfg_attr(feature = "openapi", schema(schema_with = serde_id::ids_array_schema))]
324 pub path: Vec<u64>,
325}
326
327#[derive(Debug, Serialize, ToSchema)]
329pub struct StreamStatsEvent {
330 pub nodes_visited: usize,
332 pub elapsed_ms: u64,
334}
335
336#[derive(Debug, Serialize, ToSchema)]
338pub struct StreamDoneEvent {
339 pub total_nodes: usize,
341 pub max_depth_reached: u32,
343 pub elapsed_ms: u64,
345}
346
347#[derive(Debug, Serialize, ToSchema)]
349pub struct StreamErrorEvent {
350 pub error: String,
352}
353
354#[cfg(test)]
355mod tests {
356 use super::*;
357
358 #[test]
360 fn test_traversal_path_serialized_as_strings() {
361 let above_safe = (1_u64 << 53) + 1; let item = TraversalResultItem {
363 target_id: 2,
364 depth: 1,
365 path: vec![above_safe],
366 };
367 let json = serde_json::to_value(&item).unwrap();
368 assert_eq!(json["path"], serde_json::json!(["9007199254740993"]));
369 }
370
371 #[test]
373 fn test_stream_node_event_path_serialized_as_strings() {
374 let above_safe = (1_u64 << 53) + 1;
375 let event = StreamNodeEvent {
376 id: 1,
377 depth: 1,
378 path: vec![above_safe],
379 };
380 let json = serde_json::to_value(&event).unwrap();
381 assert_eq!(json["path"], serde_json::json!(["9007199254740993"]));
382 }
383
384 #[test]
386 fn test_node_list_ids_serialized_as_strings() {
387 let above_safe = (1_u64 << 53) + 1;
388 let response = NodeListResponse {
389 node_ids: vec![1, above_safe],
390 count: 2,
391 };
392 let json = serde_json::to_value(&response).unwrap();
393 assert_eq!(
394 json["node_ids"],
395 serde_json::json!(["1", "9007199254740993"])
396 );
397 }
398
399 #[test]
401 fn test_parallel_sources_accepts_strings_and_numbers() {
402 let from_strings: ParallelTraverseRequest =
403 serde_json::from_value(serde_json::json!({ "sources": ["9007199254740993", "2"] }))
404 .expect("string sources must deserialize");
405 assert_eq!(from_strings.sources, vec![(1_u64 << 53) + 1, 2]);
406
407 let from_numbers: ParallelTraverseRequest =
408 serde_json::from_value(serde_json::json!({ "sources": [3, 4] }))
409 .expect("numeric sources must still deserialize");
410 assert_eq!(from_numbers.sources, vec![3, 4]);
411 }
412}