1use std::collections::{hash_map::DefaultHasher, BTreeMap, BTreeSet};
13use std::hash::{Hash, Hasher};
14use std::sync::Arc;
15
16use super::index_catalog::{StoredIndexEntity, StoredIndexKind};
17
18pub type DistinctValues = BTreeMap<(String, String), usize>;
21
22#[derive(Debug, Default, Clone, PartialEq, Eq)]
25pub struct GraphStats {
26 pub node_count: usize,
28 pub relationship_count: usize,
30 pub nodes_by_label: BTreeMap<String, usize>,
32 pub relationships_by_type: BTreeMap<String, usize>,
34 pub node_distinct_values: Arc<DistinctValues>,
44 pub relationship_distinct_values: Arc<DistinctValues>,
46 pub node_range_indexes: BTreeSet<(String, String)>,
48 pub relationship_range_indexes: BTreeSet<(String, String)>,
49 pub node_text_indexes: BTreeSet<(String, String)>,
51 pub relationship_text_indexes: BTreeSet<(String, String)>,
52 pub node_point_indexes: BTreeSet<(String, String)>,
54 pub relationship_point_indexes: BTreeSet<(String, String)>,
55 pub node_vector_indexes: BTreeSet<(String, String)>,
60 pub relationship_vector_indexes: BTreeSet<(String, String)>,
61}
62
63pub struct PlanSignature {
68 hasher: DefaultHasher,
69}
70
71impl Default for PlanSignature {
72 fn default() -> Self {
73 Self::new()
74 }
75}
76
77impl PlanSignature {
78 pub fn new() -> Self {
79 Self {
80 hasher: DefaultHasher::new(),
81 }
82 }
83
84 pub fn bucket(count: usize) -> u32 {
88 usize::BITS - count.leading_zeros()
89 }
90
91 pub fn totals(&mut self, nodes: usize, relationships: usize) {
92 (0u8, Self::bucket(nodes), Self::bucket(relationships)).hash(&mut self.hasher);
93 }
94
95 pub fn label(&mut self, label: &str, nodes: usize) {
98 (1u8, label, Self::bucket(nodes)).hash(&mut self.hasher);
99 }
100
101 pub fn relationship_type(&mut self, rel_type: &str, relationships: usize) {
102 (2u8, rel_type, Self::bucket(relationships)).hash(&mut self.hasher);
103 }
104
105 pub fn node_distinct(&mut self, label: &str, property: &str, distinct: usize) {
106 (3u8, label, property, Self::bucket(distinct)).hash(&mut self.hasher);
107 }
108
109 pub fn relationship_distinct(&mut self, rel_type: &str, property: &str, distinct: usize) {
110 (4u8, rel_type, property, Self::bucket(distinct)).hash(&mut self.hasher);
111 }
112
113 pub fn index(
116 &mut self,
117 entity: StoredIndexEntity,
118 kind: StoredIndexKind,
119 label: &str,
120 property: &str,
121 ) {
122 (5u8, entity, kind.as_str(), label, property).hash(&mut self.hasher);
123 }
124
125 pub fn finish(&self) -> u64 {
126 self.hasher.finish()
127 }
128}
129
130impl GraphStats {
131 pub fn estimate_node_property_equality(&self, label: &str, property: &str) -> Option<u64> {
135 let total = self.nodes_by_label.get(label).copied()? as u64;
136 let distinct = self
137 .node_distinct_values
138 .get(&(label.to_string(), property.to_string()))
139 .copied()
140 .unwrap_or(1)
141 .max(1) as u64;
142 Some(total.div_ceil(distinct))
145 }
146
147 pub fn label_count(&self, label: &str) -> Option<u64> {
148 self.nodes_by_label.get(label).copied().map(|c| c as u64)
149 }
150
151 pub fn relationship_type_count(&self, rel_type: &str) -> Option<u64> {
152 self.relationships_by_type
153 .get(rel_type)
154 .copied()
155 .map(|c| c as u64)
156 }
157
158 pub fn fingerprint(&self) -> u64 {
159 let mut hasher = DefaultHasher::new();
160 self.node_count.hash(&mut hasher);
161 self.relationship_count.hash(&mut hasher);
162 self.nodes_by_label.hash(&mut hasher);
163 self.relationships_by_type.hash(&mut hasher);
164 self.node_distinct_values.hash(&mut hasher);
165 self.relationship_distinct_values.hash(&mut hasher);
166 self.node_range_indexes.hash(&mut hasher);
167 self.relationship_range_indexes.hash(&mut hasher);
168 self.node_text_indexes.hash(&mut hasher);
169 self.relationship_text_indexes.hash(&mut hasher);
170 self.node_point_indexes.hash(&mut hasher);
171 self.relationship_point_indexes.hash(&mut hasher);
172 self.node_vector_indexes.hash(&mut hasher);
173 self.relationship_vector_indexes.hash(&mut hasher);
174 hasher.finish()
175 }
176
177 pub fn plan_signature(&self) -> u64 {
185 let mut sig = PlanSignature::new();
186 sig.totals(self.node_count, self.relationship_count);
187 for (label, count) in &self.nodes_by_label {
188 sig.label(label, *count);
189 }
190 for (rel_type, count) in &self.relationships_by_type {
191 sig.relationship_type(rel_type, *count);
192 }
193 for ((label, property), distinct) in self.node_distinct_values.iter() {
194 sig.node_distinct(label, property, *distinct);
195 }
196 for ((rel_type, property), distinct) in self.relationship_distinct_values.iter() {
197 sig.relationship_distinct(rel_type, property, *distinct);
198 }
199 let scopes = [
200 (
201 StoredIndexEntity::Node,
202 StoredIndexKind::Range,
203 &self.node_range_indexes,
204 ),
205 (
206 StoredIndexEntity::Node,
207 StoredIndexKind::Text,
208 &self.node_text_indexes,
209 ),
210 (
211 StoredIndexEntity::Node,
212 StoredIndexKind::Point,
213 &self.node_point_indexes,
214 ),
215 (
216 StoredIndexEntity::Node,
217 StoredIndexKind::Vector,
218 &self.node_vector_indexes,
219 ),
220 (
221 StoredIndexEntity::Relationship,
222 StoredIndexKind::Range,
223 &self.relationship_range_indexes,
224 ),
225 (
226 StoredIndexEntity::Relationship,
227 StoredIndexKind::Text,
228 &self.relationship_text_indexes,
229 ),
230 (
231 StoredIndexEntity::Relationship,
232 StoredIndexKind::Point,
233 &self.relationship_point_indexes,
234 ),
235 (
236 StoredIndexEntity::Relationship,
237 StoredIndexKind::Vector,
238 &self.relationship_vector_indexes,
239 ),
240 ];
241 for (entity, kind, set) in scopes {
242 for (label, property) in set {
243 sig.index(entity, kind, label, property);
244 }
245 }
246 sig.finish()
247 }
248
249 pub fn has_node_range_index(&self, label: &str, property: &str) -> bool {
250 self.node_range_indexes
251 .contains(&(label.to_owned(), property.to_owned()))
252 }
253
254 pub fn has_node_text_index(&self, label: &str, property: &str) -> bool {
255 self.node_text_indexes
256 .contains(&(label.to_owned(), property.to_owned()))
257 }
258
259 pub fn has_node_point_index(&self, label: &str, property: &str) -> bool {
260 self.node_point_indexes
261 .contains(&(label.to_owned(), property.to_owned()))
262 }
263
264 pub fn has_relationship_range_index(&self, rel_type: &str, property: &str) -> bool {
265 self.relationship_range_indexes
266 .contains(&(rel_type.to_owned(), property.to_owned()))
267 }
268
269 pub fn has_relationship_text_index(&self, rel_type: &str, property: &str) -> bool {
270 self.relationship_text_indexes
271 .contains(&(rel_type.to_owned(), property.to_owned()))
272 }
273
274 pub fn has_relationship_point_index(&self, rel_type: &str, property: &str) -> bool {
275 self.relationship_point_indexes
276 .contains(&(rel_type.to_owned(), property.to_owned()))
277 }
278
279 pub fn has_node_vector_index(&self, label: &str, property: &str) -> bool {
280 self.node_vector_indexes
281 .contains(&(label.to_owned(), property.to_owned()))
282 }
283
284 pub fn has_relationship_vector_index(&self, rel_type: &str, property: &str) -> bool {
285 self.relationship_vector_indexes
286 .contains(&(rel_type.to_owned(), property.to_owned()))
287 }
288}