1use std::collections::BTreeSet;
5
6use lora_ast::Direction;
7
8use crate::encoded::PropEdit;
9use crate::{
10 ConstraintDefinition, ConstraintRequest, CreateConstraintError, CreateConstraintOutcome,
11 CreateIndexError, CreateIndexOutcome, DropConstraintError, DropConstraintOutcome,
12 DropIndexError, DropIndexOutcome, GraphStats, GraphStorage, GraphStorageMut, IndexDefinition,
13 IndexRequest, LoraVector, MutationEvent, NodeId, NodeRecord, NodeRef, Properties,
14 PropertyValue, RelRef, RelationshipId, RelationshipRecord, StoredIndexEntity,
15};
16
17use super::property_index::PropertyIndexKey;
18use super::InMemoryGraph;
19
20impl GraphStorage for InMemoryGraph {
21 fn list_indexes(&self) -> Vec<IndexDefinition> {
24 self.index_catalog_read().list()
25 }
26
27 fn get_index(&self, name: &str) -> Option<IndexDefinition> {
28 self.index_catalog_read().get(name).cloned()
29 }
30
31 fn fulltext_search(&self, name: &str, query: &str, limit: Option<usize>) -> Vec<(u64, f64)> {
32 let node_hits = self
34 .fulltext_indexes_read(StoredIndexEntity::Node)
35 .get(name)
36 .map(|idx| idx.query(query, limit));
37 if let Some(hits) = node_hits {
38 if !hits.is_empty() {
39 return hits;
40 }
41 }
42 let rel_hits = self
43 .fulltext_indexes_read(StoredIndexEntity::Relationship)
44 .get(name)
45 .map(|idx| idx.query(query, limit));
46 rel_hits.unwrap_or_default()
47 }
48
49 fn vector_search(
50 &self,
51 name: &str,
52 query: &LoraVector,
53 k: usize,
54 restrict_to: Option<&std::collections::BTreeSet<u64>>,
55 ) -> Vec<(u64, f64)> {
56 self.lazy_populate_vector_index(name);
63
64 if let Some(hits) =
68 self.vector_indexes_read(StoredIndexEntity::Node)
69 .query(name, query, k, restrict_to)
70 {
71 if !hits.is_empty() {
72 return hits;
73 }
74 }
75 self.vector_indexes_read(StoredIndexEntity::Relationship)
76 .query(name, query, k, restrict_to)
77 .unwrap_or_default()
78 }
79
80 fn list_constraints(&self) -> Vec<ConstraintDefinition> {
81 self.constraint_catalog_read().list()
82 }
83
84 fn get_constraint(&self, name: &str) -> Option<ConstraintDefinition> {
85 self.constraint_catalog_read().get(name).cloned()
86 }
87
88 fn check_node_create_against_constraints(
89 &self,
90 labels: &[String],
91 properties: &Properties,
92 ) -> Result<(), String> {
93 if !self.has_active_constraints() {
94 return Ok(());
95 }
96 let catalog = self.constraint_catalog_read();
97 crate::memory::constraint_enforce::check_node_create(&catalog, self, labels, properties)
98 .map_err(|e| format!("[{}] {e}", e.gql_status()))
99 }
100
101 fn check_relationship_create_against_constraints(
102 &self,
103 rel_type: &str,
104 properties: &Properties,
105 ) -> Result<(), String> {
106 if !self.has_active_constraints() {
107 return Ok(());
108 }
109 let catalog = self.constraint_catalog_read();
110 crate::memory::constraint_enforce::check_relationship_create(
111 &catalog, self, rel_type, properties,
112 )
113 .map_err(|e| format!("[{}] {e}", e.gql_status()))
114 }
115
116 fn check_node_create_deferring_existence(
117 &self,
118 labels: &[String],
119 properties: &Properties,
120 ) -> Result<(), String> {
121 if !self.has_active_constraints() {
122 return Ok(());
123 }
124 let catalog = self.constraint_catalog_read();
125 crate::memory::constraint_enforce::check_node_create_deferred(
126 &catalog, self, labels, properties,
127 )
128 .map_err(|e| format!("[{}] {e}", e.gql_status()))
129 }
130
131 fn check_relationship_create_deferring_existence(
132 &self,
133 rel_type: &str,
134 properties: &Properties,
135 ) -> Result<(), String> {
136 if !self.has_active_constraints() {
137 return Ok(());
138 }
139 let catalog = self.constraint_catalog_read();
140 crate::memory::constraint_enforce::check_relationship_create_deferred(
141 &catalog, self, rel_type, properties,
142 )
143 .map_err(|e| format!("[{}] {e}", e.gql_status()))
144 }
145
146 fn check_node_existence_constraints(&self, node_id: NodeId) -> Result<(), String> {
147 if !self.has_active_constraints() {
148 return Ok(());
149 }
150 let catalog = self.constraint_catalog_read();
151 crate::memory::constraint_enforce::check_node_existence(&catalog, self, node_id)
152 .map_err(|e| format!("[{}] {e}", e.gql_status()))
153 }
154
155 fn check_relationship_existence_constraints(
156 &self,
157 rel_id: RelationshipId,
158 ) -> Result<(), String> {
159 if !self.has_active_constraints() {
160 return Ok(());
161 }
162 let catalog = self.constraint_catalog_read();
163 crate::memory::constraint_enforce::check_relationship_existence(&catalog, self, rel_id)
164 .map_err(|e| format!("[{}] {e}", e.gql_status()))
165 }
166
167 fn check_node_set_property_against_constraints(
168 &self,
169 node_id: NodeId,
170 key: &str,
171 value: &PropertyValue,
172 ) -> Result<(), String> {
173 if !self.has_active_constraints() {
174 return Ok(());
175 }
176 let catalog = self.constraint_catalog_read();
177 crate::memory::constraint_enforce::check_node_set_property(
178 &catalog, self, node_id, key, value,
179 )
180 .map_err(|e| format!("[{}] {e}", e.gql_status()))
181 }
182
183 fn check_node_remove_property_against_constraints(
184 &self,
185 node_id: NodeId,
186 key: &str,
187 ) -> Result<(), String> {
188 if !self.has_active_constraints() {
189 return Ok(());
190 }
191 let catalog = self.constraint_catalog_read();
192 crate::memory::constraint_enforce::check_node_remove_property(&catalog, self, node_id, key)
193 .map_err(|e| format!("[{}] {e}", e.gql_status()))
194 }
195
196 fn check_node_replace_properties_against_constraints(
197 &self,
198 node_id: NodeId,
199 properties: &Properties,
200 ) -> Result<(), String> {
201 if !self.has_active_constraints() {
202 return Ok(());
203 }
204 let catalog = self.constraint_catalog_read();
205 crate::memory::constraint_enforce::check_node_replace_properties(
206 &catalog, self, node_id, properties,
207 )
208 .map_err(|e| format!("[{}] {e}", e.gql_status()))
209 }
210
211 fn check_relationship_set_property_against_constraints(
212 &self,
213 rel_id: RelationshipId,
214 key: &str,
215 value: &PropertyValue,
216 ) -> Result<(), String> {
217 if !self.has_active_constraints() {
218 return Ok(());
219 }
220 let catalog = self.constraint_catalog_read();
221 crate::memory::constraint_enforce::check_relationship_set_property(
222 &catalog, self, rel_id, key, value,
223 )
224 .map_err(|e| format!("[{}] {e}", e.gql_status()))
225 }
226
227 fn check_relationship_remove_property_against_constraints(
228 &self,
229 rel_id: RelationshipId,
230 key: &str,
231 ) -> Result<(), String> {
232 if !self.has_active_constraints() {
233 return Ok(());
234 }
235 let catalog = self.constraint_catalog_read();
236 crate::memory::constraint_enforce::check_relationship_remove_property(
237 &catalog, self, rel_id, key,
238 )
239 .map_err(|e| format!("[{}] {e}", e.gql_status()))
240 }
241
242 fn check_relationship_replace_properties_against_constraints(
243 &self,
244 rel_id: RelationshipId,
245 properties: &Properties,
246 ) -> Result<(), String> {
247 if !self.has_active_constraints() {
248 return Ok(());
249 }
250 let catalog = self.constraint_catalog_read();
251 crate::memory::constraint_enforce::check_relationship_replace_properties(
252 &catalog, self, rel_id, properties,
253 )
254 .map_err(|e| format!("[{}] {e}", e.gql_status()))
255 }
256
257 fn check_node_add_label_against_constraints(
258 &self,
259 node_id: NodeId,
260 label: &str,
261 ) -> Result<(), String> {
262 if !self.has_active_constraints() {
263 return Ok(());
264 }
265 let catalog = self.constraint_catalog_read();
266 crate::memory::constraint_enforce::check_node_add_label(&catalog, self, node_id, label)
267 .map_err(|e| format!("[{}] {e}", e.gql_status()))
268 }
269
270 fn check_node_replace_properties_deferring_existence(
271 &self,
272 node_id: NodeId,
273 properties: &Properties,
274 ) -> Result<(), String> {
275 if !self.has_active_constraints() {
276 return Ok(());
277 }
278 let catalog = self.constraint_catalog_read();
279 crate::memory::constraint_enforce::check_node_replace_properties_deferred(
280 &catalog, self, node_id, properties,
281 )
282 .map_err(|e| format!("[{}] {e}", e.gql_status()))
283 }
284
285 fn check_relationship_replace_properties_deferring_existence(
286 &self,
287 rel_id: RelationshipId,
288 properties: &Properties,
289 ) -> Result<(), String> {
290 if !self.has_active_constraints() {
291 return Ok(());
292 }
293 let catalog = self.constraint_catalog_read();
294 crate::memory::constraint_enforce::check_relationship_replace_properties_deferred(
295 &catalog, self, rel_id, properties,
296 )
297 .map_err(|e| format!("[{}] {e}", e.gql_status()))
298 }
299
300 fn check_node_add_label_deferring_existence(
301 &self,
302 node_id: NodeId,
303 label: &str,
304 ) -> Result<(), String> {
305 if !self.has_active_constraints() {
306 return Ok(());
307 }
308 let catalog = self.constraint_catalog_read();
309 crate::memory::constraint_enforce::check_node_add_label_deferred(
310 &catalog, self, node_id, label,
311 )
312 .map_err(|e| format!("[{}] {e}", e.gql_status()))
313 }
314
315 fn graph_stats(&self) -> GraphStats {
316 InMemoryGraph::graph_stats(self)
317 }
318
319 fn plan_signature(&self) -> u64 {
320 InMemoryGraph::plan_signature(self)
321 }
322
323 fn node_text_candidates(
324 &self,
325 label: &str,
326 property: &str,
327 query: &str,
328 ) -> Option<Vec<NodeId>> {
329 let registry = self.text_indexes_read(crate::StoredIndexEntity::Node);
330 let candidates = registry.candidates(label, property, query)?;
331 Some(candidates.into_iter().collect())
332 }
333
334 fn node_range_candidates(
335 &self,
336 label: &str,
337 property: &str,
338 lo: Option<&PropertyValue>,
339 hi: Option<&PropertyValue>,
340 ) -> Option<Vec<NodeId>> {
341 let registry = self.sorted_indexes_read(crate::StoredIndexEntity::Node);
342 let candidates = registry.range_candidates(label, property, lo, hi)?;
346 Some(candidates.into_iter().collect())
347 }
348
349 fn node_range_other_temporal_kind_ids(
350 &self,
351 label: &str,
352 property: &str,
353 like: &PropertyValue,
354 ) -> Option<Vec<NodeId>> {
355 self.sorted_indexes_read(crate::StoredIndexEntity::Node)
356 .other_temporal_kind_ids(label, property, like)
357 }
358
359 fn node_range_ordered_chunk(
360 &self,
361 label: &str,
362 property: &str,
363 lo: Option<&PropertyValue>,
364 hi: Option<&PropertyValue>,
365 descending: bool,
366 after: Option<(&PropertyValue, NodeId)>,
367 max: usize,
368 ) -> Option<Vec<NodeId>> {
369 self.sorted_indexes_read(crate::StoredIndexEntity::Node)
370 .ordered_chunk(label, property, lo, hi, descending, after, max)
371 }
372
373 fn node_point_within_bbox(
374 &self,
375 label: &str,
376 property: &str,
377 ll: (f64, f64),
378 ur: (f64, f64),
379 ) -> Option<Vec<NodeId>> {
380 let registry = self.point_indexes_read(crate::StoredIndexEntity::Node);
381 let candidates = registry.within_bbox(label, property, ll, ur)?;
382 Some(candidates.into_iter().collect())
383 }
384
385 fn node_point_within_distance(
386 &self,
387 label: &str,
388 property: &str,
389 center: (f64, f64),
390 max_distance: f64,
391 ) -> Option<Vec<NodeId>> {
392 let registry = self.point_indexes_read(crate::StoredIndexEntity::Node);
393 let candidates = registry.within_distance(label, property, center, max_distance)?;
394 Some(candidates.into_iter().collect())
395 }
396
397 fn relationship_text_candidates(
398 &self,
399 rel_type: &str,
400 property: &str,
401 query: &str,
402 ) -> Option<Vec<RelationshipId>> {
403 let registry = self.text_indexes_read(crate::StoredIndexEntity::Relationship);
404 let candidates = registry.candidates(rel_type, property, query)?;
405 Some(candidates.into_iter().collect())
406 }
407
408 fn relationship_range_candidates(
409 &self,
410 rel_type: &str,
411 property: &str,
412 lo: Option<&PropertyValue>,
413 hi: Option<&PropertyValue>,
414 ) -> Option<Vec<RelationshipId>> {
415 let registry = self.sorted_indexes_read(crate::StoredIndexEntity::Relationship);
416 let candidates = registry.range_candidates(rel_type, property, lo, hi)?;
417 Some(candidates.into_iter().collect())
418 }
419
420 fn relationship_range_other_temporal_kind_ids(
421 &self,
422 rel_type: &str,
423 property: &str,
424 like: &PropertyValue,
425 ) -> Option<Vec<RelationshipId>> {
426 self.sorted_indexes_read(crate::StoredIndexEntity::Relationship)
427 .other_temporal_kind_ids(rel_type, property, like)
428 }
429
430 fn relationship_point_within_bbox(
431 &self,
432 rel_type: &str,
433 property: &str,
434 ll: (f64, f64),
435 ur: (f64, f64),
436 ) -> Option<Vec<RelationshipId>> {
437 let registry = self.point_indexes_read(crate::StoredIndexEntity::Relationship);
438 let candidates = registry.within_bbox(rel_type, property, ll, ur)?;
439 Some(candidates.into_iter().collect())
440 }
441
442 fn relationship_point_within_distance(
443 &self,
444 rel_type: &str,
445 property: &str,
446 center: (f64, f64),
447 max_distance: f64,
448 ) -> Option<Vec<RelationshipId>> {
449 let registry = self.point_indexes_read(crate::StoredIndexEntity::Relationship);
450 let candidates = registry.within_distance(rel_type, property, center, max_distance)?;
451 Some(candidates.into_iter().collect())
452 }
453
454 fn contains_node(&self, id: NodeId) -> bool {
455 self.has_node_at(id)
456 }
457
458 fn node(&self, id: NodeId) -> Option<NodeRecord> {
459 self.node_at(id).map(|r| r.to_record())
460 }
461
462 fn all_node_ids(&self) -> Vec<NodeId> {
463 self.iter_node_ids().collect()
464 }
465
466 fn node_ids_by_label(&self, label: &str) -> Vec<NodeId> {
467 match self.nodes_by_label.get(label) {
468 Some(ids) => ids.to_vec(),
469 None => Vec::new(),
470 }
471 }
472
473 fn scan_node_ids(
474 &self,
475 label: Option<&str>,
476 cursor: &mut u64,
477 max: usize,
478 out: &mut Vec<NodeId>,
479 ) -> bool {
480 let max = max.max(1);
481 match label {
482 Some(label) => {
483 let Some(ids) = self.nodes_by_label.get(label) else {
484 return false;
485 };
486 fill_page(ids.iter_from(*cursor), cursor, max, out)
488 }
489 None => {
490 let Ok(from) = usize::try_from(*cursor) else {
492 return false;
493 };
494 let slots = self.nodes.slots_from(from).map(|slot| slot as NodeId);
495 fill_page(slots, cursor, max, out)
496 }
497 }
498 }
499
500 fn contains_relationship(&self, id: RelationshipId) -> bool {
501 self.has_rel_at(id)
502 }
503
504 fn relationship(&self, id: RelationshipId) -> Option<RelationshipRecord> {
505 self.rel_at(id).map(|r| r.to_record())
506 }
507
508 fn all_rel_ids(&self) -> Vec<RelationshipId> {
509 self.iter_rel_ids().collect()
510 }
511
512 fn rel_ids_by_type(&self, rel_type: &str) -> Vec<RelationshipId> {
513 match self.relationships_by_type.get(rel_type) {
514 Some(ids) => ids.to_vec(),
515 None => Vec::new(),
516 }
517 }
518
519 fn relationship_endpoints(&self, id: RelationshipId) -> Option<(NodeId, NodeId)> {
520 self.rel_endpoints_at(id)
521 }
522
523 fn expand_ids(
524 &self,
525 node_id: NodeId,
526 direction: Direction,
527 types: &[String],
528 ) -> Vec<(RelationshipId, NodeId)> {
529 if !self.has_node_at(node_id) {
530 return Vec::new();
531 }
532
533 let mut out: Vec<(RelationshipId, NodeId)> = Vec::new();
534 let _ = self.try_for_each_adjacent_id_unchecked(
535 node_id,
536 direction,
537 types,
538 |rel_id, other_id| {
539 out.push((rel_id, other_id));
540 Ok::<(), ()>(())
541 },
542 );
543 out
544 }
545
546 fn try_for_each_expand_id<F, E>(
547 &self,
548 node_id: NodeId,
549 direction: Direction,
550 types: &[String],
551 visit: F,
552 ) -> Result<(), E>
553 where
554 F: FnMut(RelationshipId, NodeId) -> Result<(), E>,
555 Self: Sized,
556 {
557 self.try_for_each_adjacent_id(node_id, direction, types, visit)
558 }
559
560 fn all_labels(&self) -> Vec<String> {
561 self.nodes_by_label.keys().map(|k| k.to_string()).collect()
562 }
563
564 fn all_relationship_types(&self) -> Vec<String> {
565 self.relationships_by_type
566 .keys()
567 .map(|k| k.to_string())
568 .collect()
569 }
570
571 fn with_node<F, R>(&self, id: NodeId, f: F) -> Option<R>
574 where
575 F: FnOnce(NodeRef<'_>) -> R,
576 Self: Sized,
577 {
578 self.node_at(id).map(f)
579 }
580
581 fn with_relationship<F, R>(&self, id: RelationshipId, f: F) -> Option<R>
582 where
583 F: FnOnce(RelRef<'_>) -> R,
584 Self: Sized,
585 {
586 self.rel_at(id).map(f)
587 }
588
589 fn has_node(&self, id: NodeId) -> bool {
592 self.has_node_at(id)
593 }
594
595 fn has_relationship(&self, id: RelationshipId) -> bool {
596 self.has_rel_at(id)
597 }
598
599 fn node_count(&self) -> usize {
600 self.live_node_count
601 }
602
603 fn relationship_count(&self) -> usize {
604 self.live_rel_count
605 }
606
607 fn node_count_by_label(&self, label: &str) -> usize {
608 self.nodes_by_label.get(label).map_or(0, |ids| ids.len())
609 }
610
611 fn all_nodes(&self) -> Vec<NodeRecord> {
614 self.iter_node_refs().map(|r| r.to_record()).collect()
615 }
616
617 fn nodes_by_label(&self, label: &str) -> Vec<NodeRecord> {
618 self.nodes_by_label
619 .get(label)
620 .into_iter()
621 .flat_map(|ids| ids.iter())
622 .filter_map(|id| self.node_at(id).map(|r| r.to_record()))
623 .collect()
624 }
625
626 fn all_relationships(&self) -> Vec<RelationshipRecord> {
627 self.iter_rel_refs().map(|r| r.to_record()).collect()
628 }
629
630 fn relationships_by_type(&self, rel_type: &str) -> Vec<RelationshipRecord> {
631 self.relationships_by_type
632 .get(rel_type)
633 .into_iter()
634 .flat_map(|ids| ids.iter())
635 .filter_map(|id| self.rel_at(id).map(|r| r.to_record()))
636 .collect()
637 }
638
639 fn relationship_ids_of(&self, node_id: NodeId, direction: Direction) -> Vec<RelationshipId> {
640 self.relationship_ids_for_direction(node_id, direction)
641 }
642
643 fn outgoing_relationships(&self, node_id: NodeId) -> Vec<RelationshipRecord> {
644 self.outgoing_at(node_id)
645 .into_iter()
646 .flat_map(|adj| adj.iter())
647 .filter_map(|entry| self.rel_at(entry.rel).map(|r| r.to_record()))
648 .collect()
649 }
650
651 fn incoming_relationships(&self, node_id: NodeId) -> Vec<RelationshipRecord> {
652 self.incoming_at(node_id)
653 .into_iter()
654 .flat_map(|adj| adj.iter())
655 .filter_map(|entry| self.rel_at(entry.rel).map(|r| r.to_record()))
656 .collect()
657 }
658
659 fn relationships_of(&self, node_id: NodeId, direction: Direction) -> Vec<RelationshipRecord> {
660 let mut out = Vec::new();
661 let _ = self.try_for_each_expand_id(node_id, direction, &[], |rel_id, _| {
662 if let Some(rel) = self.rel_at(rel_id) {
663 out.push(rel.to_record());
664 }
665 Ok::<(), ()>(())
666 });
667 out
668 }
669
670 fn degree(&self, node_id: NodeId, direction: Direction) -> usize {
671 let out = || self.outgoing_at(node_id).map_or(0, |adj| adj.count());
673 match direction {
674 Direction::Right => out(),
675 Direction::Left => self.incoming_at(node_id).map_or(0, |adj| adj.count()),
676 Direction::Undirected => {
677 let incoming_non_self = self
678 .incoming_at(node_id)
679 .into_iter()
680 .flat_map(|adj| adj.iter())
681 .filter(|entry| entry.neighbour != node_id)
682 .count();
683 out() + incoming_non_self
684 }
685 }
686 }
687
688 fn expand(
689 &self,
690 node_id: NodeId,
691 direction: Direction,
692 types: &[String],
693 ) -> Vec<(RelationshipRecord, NodeRecord)> {
694 if !self.has_node_at(node_id) {
695 return Vec::new();
696 }
697
698 let mut out = Vec::new();
699 let _ = self.try_for_each_expand_id(node_id, direction, types, |rel_id, other_id| {
700 if let (Some(rel), Some(other)) = (self.rel_at(rel_id), self.node_at(other_id)) {
701 out.push((rel.to_record(), other.to_record()));
702 }
703 Ok::<(), ()>(())
704 });
705 out
706 }
707
708 fn neighbors(
709 &self,
710 node_id: NodeId,
711 direction: Direction,
712 types: &[String],
713 ) -> Vec<NodeRecord> {
714 let mut out = Vec::new();
715 let _ = self.try_for_each_expand_id(node_id, direction, types, |_, other_id| {
716 if let Some(node) = self.node_at(other_id) {
717 out.push(node.to_record());
718 }
719 Ok::<(), ()>(())
720 });
721 out
722 }
723
724 fn all_node_property_keys(&self) -> Vec<String> {
725 let mut keys = BTreeSet::new();
726 for node in self.iter_node_refs() {
727 for key in node.properties().keys() {
728 keys.insert(key.to_string());
729 }
730 }
731 keys.into_iter().collect()
732 }
733
734 fn all_relationship_property_keys(&self) -> Vec<String> {
737 let mut keys = BTreeSet::new();
738 for rel in self.iter_rel_refs() {
739 for key in rel.properties().keys() {
740 keys.insert(key.to_string());
741 }
742 }
743 keys.into_iter().collect()
744 }
745
746 fn label_property_keys(&self, label: &str) -> Vec<String> {
747 let mut keys = BTreeSet::new();
748
749 if let Some(ids) = self.nodes_by_label.get(label) {
750 for id in ids.iter() {
751 if let Some(node) = self.node_at(id) {
752 for key in node.properties().keys() {
753 keys.insert(key.to_string());
754 }
755 }
756 }
757 }
758
759 keys.into_iter().collect()
760 }
761
762 fn rel_type_property_keys(&self, rel_type: &str) -> Vec<String> {
763 let mut keys = BTreeSet::new();
764
765 if let Some(ids) = self.relationships_by_type.get(rel_type) {
766 for id in ids.iter() {
767 if let Some(rel) = self.rel_at(id) {
768 for key in rel.properties().keys() {
769 keys.insert(key.to_string());
770 }
771 }
772 }
773 }
774
775 keys.into_iter().collect()
776 }
777
778 fn find_nodes_by_property(
779 &self,
780 label: Option<&str>,
781 key: &str,
782 value: &PropertyValue,
783 ) -> Vec<NodeRecord>
784 where
785 Self: Sized,
786 {
787 if PropertyIndexKey::from_value(value).is_none() {
788 return self.scan_nodes_by_property(label, key, value);
789 }
790
791 self.ensure_node_property_index(key);
792 if label.is_none() {
793 self.ensure_any_scope_node_property_index(key);
794 }
795 let indexes = self.indexes_read();
796
797 match label {
798 Some(label) => {
799 let Some(ids) = indexes.node_properties.scoped_ids_for(label, key, value) else {
800 return Vec::new();
801 };
802 ids.iter()
803 .filter_map(|id| self.node_at(id).map(|r| r.to_record()))
804 .collect()
805 }
806 None => indexes
807 .node_properties
808 .ids_for(key, value)
809 .into_iter()
810 .flat_map(|ids| ids.iter())
811 .filter_map(|id| self.node_at(id).map(|r| r.to_record()))
812 .collect(),
813 }
814 }
815
816 fn find_node_ids_by_property(
817 &self,
818 label: Option<&str>,
819 key: &str,
820 value: &PropertyValue,
821 ) -> Vec<NodeId>
822 where
823 Self: Sized,
824 {
825 if PropertyIndexKey::from_value(value).is_none() {
826 return self.scan_node_ids_by_property(label, key, value);
827 }
828
829 self.ensure_node_property_index(key);
830 if label.is_none() {
831 self.ensure_any_scope_node_property_index(key);
832 }
833 let indexes = self.indexes_read();
834
835 match label {
836 Some(label) => indexes
837 .node_properties
838 .scoped_ids_for(label, key, value)
839 .map(|ids| ids.to_vec())
840 .unwrap_or_default(),
841 None => indexes
842 .node_properties
843 .ids_for(key, value)
844 .map(|ids| ids.to_vec())
845 .unwrap_or_default(),
846 }
847 }
848 fn find_relationships_by_property(
851 &self,
852 rel_type: Option<&str>,
853 key: &str,
854 value: &PropertyValue,
855 ) -> Vec<RelationshipRecord>
856 where
857 Self: Sized,
858 {
859 if PropertyIndexKey::from_value(value).is_none() {
860 return self.scan_relationships_by_property(rel_type, key, value);
861 }
862
863 self.ensure_relationship_property_index(key);
864 if rel_type.is_none() {
865 self.ensure_any_scope_relationship_property_index(key);
866 }
867 let indexes = self.indexes_read();
868
869 match rel_type {
870 Some(rel_type) => {
871 let Some(ids) = indexes
872 .relationship_properties
873 .scoped_ids_for(rel_type, key, value)
874 else {
875 return Vec::new();
876 };
877 ids.iter()
878 .filter_map(|id| self.rel_at(id).map(|r| r.to_record()))
879 .collect()
880 }
881 None => indexes
882 .relationship_properties
883 .ids_for(key, value)
884 .into_iter()
885 .flat_map(|ids| ids.iter())
886 .filter_map(|id| self.rel_at(id).map(|r| r.to_record()))
887 .collect(),
888 }
889 }
890
891 fn find_relationship_ids_by_property(
892 &self,
893 rel_type: Option<&str>,
894 key: &str,
895 value: &PropertyValue,
896 ) -> Vec<RelationshipId>
897 where
898 Self: Sized,
899 {
900 if PropertyIndexKey::from_value(value).is_none() {
901 return self.scan_relationship_ids_by_property(rel_type, key, value);
902 }
903
904 self.ensure_relationship_property_index(key);
905 if rel_type.is_none() {
906 self.ensure_any_scope_relationship_property_index(key);
907 }
908 let indexes = self.indexes_read();
909
910 match rel_type {
911 Some(rel_type) => indexes
912 .relationship_properties
913 .scoped_ids_for(rel_type, key, value)
914 .map(|ids| ids.to_vec())
915 .unwrap_or_default(),
916 None => indexes
917 .relationship_properties
918 .ids_for(key, value)
919 .map(|ids| ids.to_vec())
920 .unwrap_or_default(),
921 }
922 }
923
924 fn node_exists_with_label_and_property(
925 &self,
926 label: &str,
927 key: &str,
928 value: &PropertyValue,
929 ) -> bool
930 where
931 Self: Sized,
932 {
933 if PropertyIndexKey::from_value(value).is_none() {
934 return self.any_node_by_property(label, key, value);
935 }
936
937 self.ensure_node_property_index(key);
938 let indexes = self.indexes_read();
939 indexes
940 .node_properties
941 .scoped_ids_for(label, key, value)
942 .map(|ids| !ids.is_empty())
943 .unwrap_or(false)
944 }
945
946 fn relationship_exists_with_type_and_property(
947 &self,
948 rel_type: &str,
949 key: &str,
950 value: &PropertyValue,
951 ) -> bool
952 where
953 Self: Sized,
954 {
955 if PropertyIndexKey::from_value(value).is_none() {
956 return self.any_relationship_by_property(rel_type, key, value);
957 }
958
959 self.ensure_relationship_property_index(key);
960 let indexes = self.indexes_read();
961 indexes
962 .relationship_properties
963 .scoped_ids_for(rel_type, key, value)
964 .map(|ids| !ids.is_empty())
965 .unwrap_or(false)
966 }
967}
968
969impl GraphStorageMut for InMemoryGraph {
970 fn try_create_node(
971 &mut self,
972 labels: Vec<String>,
973 properties: Properties,
974 ) -> Option<NodeRecord> {
975 let (id, idx) = self.try_reserve_next_node_slot()?;
976 let labels = Self::normalize_labels(labels);
977
978 let node = NodeRecord {
979 id,
980 labels,
981 properties,
982 };
983
984 self.put_node_at_slot(idx, &node);
985
986 self.on_node_created(&node);
987
988 self.emit(|| MutationEvent::CreateNode {
992 id,
993 labels: node.labels.to_strings(),
994 properties: node.properties.clone(),
995 });
996
997 Some(node)
998 }
999
1000 fn create_relationship(
1001 &mut self,
1002 src: NodeId,
1003 dst: NodeId,
1004 rel_type: &str,
1005 properties: Properties,
1006 ) -> Option<RelationshipRecord> {
1007 if !self.has_node_at(src) || !self.has_node_at(dst) {
1008 return None;
1009 }
1010
1011 let trimmed = rel_type.trim();
1012 if trimmed.is_empty() {
1013 return None;
1014 }
1015
1016 let (id, idx) = self.try_reserve_next_rel_slot()?;
1017 let rel = RelationshipRecord {
1018 id,
1019 src,
1020 dst,
1021 rel_type: trimmed.into(),
1022 properties,
1023 };
1024
1025 self.put_rel_at_slot(idx, &rel);
1026 self.on_relationship_created(&rel);
1027
1028 self.emit(|| MutationEvent::CreateRelationship {
1029 id,
1030 src,
1031 dst,
1032 rel_type: rel.rel_type.to_string(),
1033 properties: rel.properties.clone(),
1034 });
1035
1036 Some(rel)
1037 }
1038
1039 fn set_node_property(&mut self, node_id: NodeId, key: String, value: PropertyValue) -> bool {
1040 let Some(old) = self.edit_node_property(node_id, &key, PropEdit::Set(&value)) else {
1041 return false;
1042 };
1043 self.on_node_property_set(node_id, &key, old.as_ref(), &value);
1044
1045 self.emit(|| MutationEvent::SetNodeProperty {
1046 node_id,
1047 key: key.clone(),
1048 value: value.clone(),
1049 });
1050
1051 true
1052 }
1053
1054 fn remove_node_property(&mut self, node_id: NodeId, key: &str) -> bool {
1055 let Some(removed) = self
1056 .edit_node_property(node_id, key, PropEdit::Remove)
1057 .flatten()
1058 else {
1059 return false;
1060 };
1061
1062 self.on_node_property_removed(node_id, key, &removed);
1063
1064 self.emit(|| MutationEvent::RemoveNodeProperty {
1065 node_id,
1066 key: key.to_string(),
1067 });
1068
1069 true
1070 }
1071
1072 fn add_node_label(&mut self, node_id: NodeId, label: &str) -> bool {
1073 let label = label.trim();
1074 if label.is_empty() {
1075 return false;
1076 }
1077
1078 let applied = match self.node_at(node_id) {
1079 Some(node) if node.has_label(label) => return false,
1080 Some(_) => self
1081 .update_node(node_id, |node| node.labels.push(label))
1082 .is_some(),
1083 None => false,
1084 };
1085 if applied {
1086 self.on_node_label_added(node_id, label);
1087 self.emit(|| MutationEvent::AddNodeLabel {
1088 node_id,
1089 label: label.to_string(),
1090 });
1091 }
1092 applied
1093 }
1094
1095 fn remove_node_label(&mut self, node_id: NodeId, label: &str) -> bool {
1096 let applied = self
1097 .node_at(node_id)
1098 .is_some_and(|node| node.has_label(label))
1099 && self
1100 .update_node(node_id, |node| node.labels.retain(|l| l != label))
1101 .is_some();
1102 if applied {
1103 self.on_node_label_removed(node_id, label);
1104 self.emit(|| MutationEvent::RemoveNodeLabel {
1105 node_id,
1106 label: label.to_string(),
1107 });
1108 }
1109 applied
1110 }
1111
1112 fn set_relationship_property(
1113 &mut self,
1114 rel_id: RelationshipId,
1115 key: String,
1116 value: PropertyValue,
1117 ) -> bool {
1118 let Some(old) = self.edit_rel_property(rel_id, &key, PropEdit::Set(&value)) else {
1119 return false;
1120 };
1121 self.on_relationship_property_set(rel_id, &key, old.as_ref(), &value);
1122
1123 self.emit(|| MutationEvent::SetRelationshipProperty {
1124 rel_id,
1125 key: key.clone(),
1126 value: value.clone(),
1127 });
1128
1129 true
1130 }
1131
1132 fn remove_relationship_property(&mut self, rel_id: RelationshipId, key: &str) -> bool {
1133 let Some(removed) = self
1134 .edit_rel_property(rel_id, key, PropEdit::Remove)
1135 .flatten()
1136 else {
1137 return false;
1138 };
1139
1140 self.on_relationship_property_removed(rel_id, key, &removed);
1141
1142 self.emit(|| MutationEvent::RemoveRelationshipProperty {
1143 rel_id,
1144 key: key.to_string(),
1145 });
1146
1147 true
1148 }
1149
1150 fn delete_relationship(&mut self, rel_id: RelationshipId) -> bool {
1151 let applied = match self.take_rel(rel_id) {
1152 Some(rel) => {
1153 if let Some(sink) = &self.deleted_sink {
1154 sink.relationship_deleted(&rel);
1155 }
1156 self.on_relationship_deleted(&rel);
1157 true
1158 }
1159 None => false,
1160 };
1161 if applied {
1162 self.emit(|| MutationEvent::DeleteRelationship { rel_id });
1163 }
1164 applied
1165 }
1166
1167 fn delete_node(&mut self, node_id: NodeId) -> bool {
1168 if !self.has_node_at(node_id) {
1169 return false;
1170 }
1171
1172 if self.has_incident_relationships(node_id) {
1173 return false;
1174 }
1175
1176 let node = match self.take_node(node_id) {
1177 Some(node) => node,
1178 None => return false,
1179 };
1180
1181 if let Some(sink) = &self.deleted_sink {
1182 sink.node_deleted(&node);
1183 }
1184 self.on_node_deleted(&node);
1185
1186 self.emit(|| MutationEvent::DeleteNode { node_id });
1189
1190 true
1191 }
1192
1193 fn detach_delete_node(&mut self, node_id: NodeId) -> bool {
1194 if !self.has_node_at(node_id) {
1195 return false;
1196 }
1197
1198 let rel_ids: Vec<_> = self
1199 .incident_relationship_ids(node_id)
1200 .into_iter()
1201 .collect();
1202
1203 self.clear_adjacency(node_id);
1215 for rel_id in rel_ids {
1216 let _ = self.delete_relationship(rel_id);
1217 }
1218
1219 if self.delete_node(node_id) {
1220 self.emit(|| MutationEvent::DetachDeleteNode { node_id });
1221 true
1222 } else {
1223 false
1224 }
1225 }
1226
1227 fn clear(&mut self) {
1228 let recorder = self.recorder.take();
1232 let deleted_sink = self.deleted_sink.take();
1233 *self = Self::default();
1234 self.recorder = recorder;
1235 self.deleted_sink = deleted_sink;
1236 self.emit(|| MutationEvent::Clear);
1237 }
1238
1239 fn create_index(
1240 &mut self,
1241 request: IndexRequest,
1242 if_not_exists: bool,
1243 ) -> Result<CreateIndexOutcome, CreateIndexError> {
1244 self.register_index(request, if_not_exists)
1250 }
1251
1252 fn drop_index(
1253 &mut self,
1254 name: &str,
1255 if_exists: bool,
1256 ) -> Result<DropIndexOutcome, DropIndexError> {
1257 self.drop_named_index(name, if_exists)
1258 }
1259
1260 fn create_constraint(
1261 &mut self,
1262 request: ConstraintRequest,
1263 if_not_exists: bool,
1264 ) -> Result<CreateConstraintOutcome, CreateConstraintError> {
1265 self.register_constraint(request, if_not_exists)
1266 }
1267
1268 fn drop_constraint(
1269 &mut self,
1270 name: &str,
1271 if_exists: bool,
1272 ) -> Result<DropConstraintOutcome, DropConstraintError> {
1273 self.drop_named_constraint(name, if_exists)
1274 }
1275}
1276
1277#[inline]
1280fn fill_page(
1281 mut ids: impl Iterator<Item = NodeId>,
1282 cursor: &mut u64,
1283 max: usize,
1284 out: &mut Vec<NodeId>,
1285) -> bool {
1286 for id in ids.by_ref().take(max) {
1287 out.push(id);
1288 *cursor = id.saturating_add(1);
1289 }
1290 ids.next().is_some()
1291}