1use crate::ids::{NodeRecord, RelRecord};
23
24#[derive(Debug, Clone, Copy, PartialEq, Eq)]
26pub enum ProjectionScanControl {
27 Continue,
28 Stop,
29}
30
31pub trait ProjectionSource {
33 fn admit_projection_memory(&self, _total_bytes: usize) -> Result<(), String> {
37 Ok(())
38 }
39
40 fn visit_projection_nodes(
41 &self,
42 visitor: &mut dyn FnMut(NodeRecord) -> ProjectionScanControl,
43 ) -> Result<ProjectionScanControl, String>;
44
45 fn visit_projection_relationships(
46 &self,
47 visitor: &mut dyn FnMut(RelRecord) -> ProjectionScanControl,
48 ) -> Result<ProjectionScanControl, String>;
49}
50
51#[derive(Debug, Clone, Copy, PartialEq, Eq)]
52pub enum ProjectionLayout {
53 Outgoing,
54 Incoming,
55 Bidirectional,
56 Undirected,
57}
58
59impl ProjectionLayout {
60 pub fn as_str(self) -> &'static str {
61 match self {
62 Self::Outgoing => "outgoing",
63 Self::Incoming => "incoming",
64 Self::Bidirectional => "bidirectional",
65 Self::Undirected => "undirected",
66 }
67 }
68
69 #[doc(hidden)]
70 pub fn stores_outgoing(self) -> bool {
71 matches!(
72 self,
73 Self::Outgoing | Self::Bidirectional | Self::Undirected
74 )
75 }
76
77 #[doc(hidden)]
78 pub fn stores_incoming(self) -> bool {
79 matches!(self, Self::Incoming | Self::Bidirectional)
80 }
81}
82
83#[derive(Debug, Clone, Copy, PartialEq, Eq)]
84pub struct ProjectionMemoryBudget {
85 max_bytes: Option<std::num::NonZeroUsize>,
86}
87
88impl ProjectionMemoryBudget {
89 pub const fn unlimited() -> Self {
90 Self { max_bytes: None }
91 }
92
93 pub const fn new(max_bytes: std::num::NonZeroUsize) -> Self {
94 Self {
95 max_bytes: Some(max_bytes),
96 }
97 }
98
99 pub fn max_bytes(self) -> Option<usize> {
100 self.max_bytes.map(std::num::NonZeroUsize::get)
101 }
102}
103
104#[derive(Debug, Clone, Copy, PartialEq, Eq)]
105pub struct ProjectionMemoryEstimate {
106 pub layout: ProjectionLayout,
107 pub node_count: usize,
108 pub relationship_count: usize,
109 pub projected_edge_count: usize,
110 pub estimated_bytes: usize,
111}
112
113#[derive(Debug, Clone, Copy, PartialEq, Eq)]
114pub struct GraphAlgorithmMemoryEstimate {
115 pub projection_bytes: usize,
116 pub algorithm_peak_bytes: usize,
117 pub result_bytes: usize,
118 pub total_peak_bytes: usize,
119}
120
121#[derive(Debug, Clone, PartialEq, Eq)]
122pub struct ProjectionMemoryAdmissionError {
123 pub estimate: ProjectionMemoryEstimate,
124 pub budget_bytes: usize,
125 pub storage_error: Option<String>,
126}
127
128impl std::fmt::Display for ProjectionMemoryAdmissionError {
129 fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
130 if let Some(error) = &self.storage_error {
131 return write!(
132 formatter,
133 "analytics projection storage scan failed: {error}"
134 );
135 }
136 write!(
137 formatter,
138 "analytics projection layout '{}' requires an estimated {} bytes for {} nodes and {} relationships, exceeding the {} byte budget",
139 self.estimate.layout.as_str(),
140 self.estimate.estimated_bytes,
141 self.estimate.node_count,
142 self.estimate.relationship_count,
143 self.budget_bytes,
144 )
145 }
146}
147
148impl std::error::Error for ProjectionMemoryAdmissionError {}
149
150#[doc(hidden)]
151impl ProjectionMemoryAdmissionError {
152 pub fn storage(error: impl std::fmt::Display) -> Self {
153 Self {
154 estimate: ProjectionMemoryEstimate {
155 layout: ProjectionLayout::Bidirectional,
156 node_count: 0,
157 relationship_count: 0,
158 projected_edge_count: 0,
159 estimated_bytes: 0,
160 },
161 budget_bytes: 0,
162 storage_error: Some(error.to_string()),
163 }
164 }
165}
166
167#[derive(Debug, Clone, Copy, PartialEq)]
168pub struct PageRankOptions {
169 pub iterations: usize,
170 pub damping: f64,
171}
172
173impl Default for PageRankOptions {
174 fn default() -> Self {
175 Self {
176 iterations: 20,
177 damping: 0.85,
178 }
179 }
180}
181
182#[derive(Debug, Clone, Copy, PartialEq)]
183pub struct PageRankScore {
184 pub node: crate::ids::NodeId,
185 pub score: f64,
186}
187
188#[derive(Debug, Clone, Copy, PartialEq)]
189pub struct LouvainOptions {
190 pub max_iterations: usize,
191 pub max_levels: usize,
192}
193
194impl Default for LouvainOptions {
195 fn default() -> Self {
196 Self {
197 max_iterations: 20,
198 max_levels: 1,
199 }
200 }
201}
202
203#[derive(Debug, Clone, Copy, PartialEq, Eq)]
204pub struct CommunityAssignment {
205 pub node: crate::ids::NodeId,
206 pub community: crate::ids::NodeId,
207}
208
209#[derive(Debug, Clone, Copy, PartialEq, Eq)]
210pub struct HierarchicalCommunityAssignment {
211 pub level: usize,
212 pub node: crate::ids::NodeId,
213 pub community: crate::ids::NodeId,
214}