1use super::schema_events::{SchemaChangeCallback, SchemaChangeEvent};
2use crate::event_dispatch::{EventFilter, EventSubscriptions, SubscriptionId};
3use crate::metadata::sequence::SequenceDef;
4use crate::types::{EdgeTypeInfo, PropertyDef, SpaceInfo, TagInfo};
5use crate::StorageError;
6use dashmap::DashMap;
7use parking_lot::RwLock;
8use std::collections::HashMap;
9use std::path::Path;
10use std::sync::atomic::{AtomicU32, AtomicU64, Ordering};
11use std::sync::Arc;
12
13const SCHEMA_FORMAT_VERSION: u32 = 1;
14
15#[derive(serde::Serialize, serde::Deserialize)]
16struct SchemaSnapshot {
17 version: u32,
18 spaces: Vec<SpaceInfo>,
19 tags: Vec<(u64, TagInfo)>,
20 edge_types: Vec<(u64, EdgeTypeInfo)>,
21 space_id_counter: u64,
22 tag_id_counters: Vec<(u64, u32)>,
23 edge_type_id_counters: Vec<(u64, u32)>,
24 #[serde(default)]
26 serial_next: Vec<(u64, String, u64)>,
27 #[serde(default)]
29 sequences: Vec<SequenceDef>,
30}
31
32#[derive(Debug, Clone)]
33struct SpaceData {
34 info: SpaceInfo,
35}
36
37#[derive(Debug, Clone)]
38struct TagData {
39 info: TagInfo,
40}
41
42#[derive(Debug, Clone)]
43struct EdgeTypeData {
44 info: EdgeTypeInfo,
45}
46
47pub struct SchemaManager {
48 spaces: Arc<RwLock<HashMap<u64, SpaceData>>>,
49 space_name_index: Arc<RwLock<HashMap<String, u64>>>,
50 tags: Arc<RwLock<HashMap<(u64, u32), TagData>>>,
51 edge_types: Arc<RwLock<HashMap<(u64, u32), EdgeTypeData>>>,
52 space_id_counter: Arc<AtomicU64>,
53 tag_id_counter: Arc<DashMap<u64, AtomicU32>>,
54 edge_type_id_counter: Arc<DashMap<u64, AtomicU32>>,
55 serial_next: Arc<RwLock<Vec<(u64, String, u64)>>>,
56 sequences: Arc<RwLock<HashMap<String, SequenceDef>>>,
57 schema_callbacks: Arc<EventSubscriptions<SchemaChangeEvent>>,
58}
59
60impl Clone for SchemaManager {
61 fn clone(&self) -> Self {
62 Self {
63 spaces: self.spaces.clone(),
64 space_name_index: self.space_name_index.clone(),
65 tags: self.tags.clone(),
66 edge_types: self.edge_types.clone(),
67 space_id_counter: self.space_id_counter.clone(),
68 tag_id_counter: self.tag_id_counter.clone(),
69 edge_type_id_counter: self.edge_type_id_counter.clone(),
70 serial_next: self.serial_next.clone(),
71 sequences: self.sequences.clone(),
72 schema_callbacks: self.schema_callbacks.clone(),
73 }
74 }
75}
76
77impl std::fmt::Debug for SchemaManager {
78 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
79 f.debug_struct("SchemaManager")
80 .field("spaces_count", &self.spaces.read().len())
81 .finish()
82 }
83}
84
85impl SchemaManager {
86 pub fn new() -> Self {
87 Self {
88 spaces: Arc::new(RwLock::new(HashMap::new())),
89 space_name_index: Arc::new(RwLock::new(HashMap::new())),
90 tags: Arc::new(RwLock::new(HashMap::new())),
91 edge_types: Arc::new(RwLock::new(HashMap::new())),
92 space_id_counter: Arc::new(AtomicU64::new(0)),
93 tag_id_counter: Arc::new(DashMap::new()),
94 edge_type_id_counter: Arc::new(DashMap::new()),
95 serial_next: Arc::new(RwLock::new(Vec::new())),
96 sequences: Arc::new(RwLock::new(HashMap::new())),
97 schema_callbacks: Arc::new(EventSubscriptions::new()),
98 }
99 }
100
101 pub fn with_shared_schema_callbacks(
107 shared: Arc<EventSubscriptions<SchemaChangeEvent>>,
108 ) -> Self {
109 let mut manager = Self::new();
110 manager.schema_callbacks = shared;
111 manager
112 }
113
114 pub fn shared_schema_callbacks(&self) -> Arc<EventSubscriptions<SchemaChangeEvent>> {
116 Arc::clone(&self.schema_callbacks)
117 }
118
119 pub fn register_schema_callback(&self, callback: SchemaChangeCallback) -> SubscriptionId {
121 self.schema_callbacks.add(callback)
122 }
123
124 pub fn register_schema_callback_filtered(
126 &self,
127 callback: SchemaChangeCallback,
128 filter: EventFilter<SchemaChangeEvent>,
129 ) -> SubscriptionId {
130 self.schema_callbacks.add_filtered(callback, Some(filter))
131 }
132
133 pub fn unregister_schema_callback(&self, id: SubscriptionId) -> bool {
135 self.schema_callbacks.remove(id)
136 }
137
138 pub fn schema_callback_count(&self) -> usize {
140 self.schema_callbacks.len()
141 }
142
143 fn emit_schema_event(&self, event: SchemaChangeEvent) {
144 if self.schema_callbacks.is_empty() {
145 return;
146 }
147 self.schema_callbacks.dispatch("schema", &event);
148 }
149
150 pub fn set_serial_next(&self, entries: Vec<(u64, String, u64)>) {
153 *self.serial_next.write() = entries;
154 }
155
156 pub fn serial_next(&self) -> Vec<(u64, String, u64)> {
158 self.serial_next.read().clone()
159 }
160
161 fn get_next_space_id(&self) -> u64 {
162 self.space_id_counter.fetch_add(1, Ordering::SeqCst) + 1
163 }
164
165 pub fn peek_next_space_id(&self) -> u64 {
166 self.space_id_counter.load(Ordering::SeqCst) + 1
167 }
168
169 fn get_next_tag_id(&self, _space_id: u64) -> u32 {
170 let entry = self
171 .tag_id_counter
172 .entry(0)
173 .or_insert_with(|| AtomicU32::new(0));
174 entry.fetch_add(1, Ordering::SeqCst) + 1
175 }
176
177 pub fn peek_next_tag_id(&self) -> u32 {
178 let entry = self
179 .tag_id_counter
180 .entry(0)
181 .or_insert_with(|| AtomicU32::new(0));
182 entry.load(Ordering::SeqCst) + 1
183 }
184
185 fn get_next_edge_type_id(&self, _space_id: u64) -> u32 {
186 let entry = self
187 .edge_type_id_counter
188 .entry(0)
189 .or_insert_with(|| AtomicU32::new(0));
190 entry.fetch_add(1, Ordering::SeqCst) + 1
191 }
192
193 pub fn peek_next_edge_type_id(&self) -> u32 {
194 let entry = self
195 .edge_type_id_counter
196 .entry(0)
197 .or_insert_with(|| AtomicU32::new(0));
198 entry.load(Ordering::SeqCst) + 1
199 }
200
201 pub fn create_space(&self, space: &mut SpaceInfo) -> Result<bool, StorageError> {
202 let mut name_index = self.space_name_index.write();
203 if name_index.contains_key(&space.space_name) {
204 return Ok(false);
205 }
206
207 let space_id = if space.space_id == 0 {
208 self.get_next_space_id()
209 } else {
210 let current = self.space_id_counter.load(Ordering::SeqCst);
211 if space.space_id > current {
212 self.space_id_counter
213 .store(space.space_id, Ordering::SeqCst);
214 }
215
216 let spaces = self.spaces.read();
217 if spaces.contains_key(&space.space_id) {
218 return Err(StorageError::label_already_exists(format!(
219 "space_id {}",
220 space.space_id
221 )));
222 }
223 space.space_id
224 };
225 space.space_id = space_id;
226
227 name_index.insert(space.space_name.clone(), space_id);
228 drop(name_index);
229
230 let mut spaces = self.spaces.write();
231 spaces.insert(
232 space_id,
233 SpaceData {
234 info: space.clone(),
235 },
236 );
237 drop(spaces);
238 let space_name = space.space_name.clone();
239 self.emit_schema_event(SchemaChangeEvent::SpaceCreated {
240 space_id,
241 space_name,
242 });
243
244 Ok(true)
245 }
246
247 pub fn drop_space(&self, space_name: &str) -> Result<bool, StorageError> {
248 let mut name_index = self.space_name_index.write();
249 if let Some(space_id) = name_index.remove(space_name) {
250 drop(name_index);
251
252 let mut spaces = self.spaces.write();
253 spaces.remove(&space_id);
254 drop(spaces);
255
256 let mut tags = self.tags.write();
257 tags.retain(|(sid, _), _| *sid != space_id);
258 drop(tags);
259
260 let mut edge_types = self.edge_types.write();
261 edge_types.retain(|(sid, _), _| *sid != space_id);
262 drop(edge_types);
263
264 self.emit_schema_event(SchemaChangeEvent::SpaceDropped {
265 space_id,
266 space_name: space_name.to_string(),
267 });
268
269 Ok(true)
270 } else {
271 Ok(false)
272 }
273 }
274
275 pub fn clear_space(&self, space_name: &str) -> Result<bool, StorageError> {
276 let space_info = self.get_space(space_name)?.ok_or_else(|| {
277 StorageError::db_error(format!("Space \"{}\" does not exist", space_name))
278 })?;
279
280 let space_id = space_info.space_id;
281
282 let mut tags = self.tags.write();
283 tags.retain(|(sid, _), _| *sid != space_id);
284 drop(tags);
285
286 let mut edge_types = self.edge_types.write();
287 edge_types.retain(|(sid, _), _| *sid != space_id);
288 drop(edge_types);
289
290 self.emit_schema_event(SchemaChangeEvent::SpaceCleared {
291 space_id,
292 space_name: space_name.to_string(),
293 });
294
295 Ok(true)
296 }
297
298 pub fn get_space(&self, space_name: &str) -> Result<Option<SpaceInfo>, StorageError> {
299 let name_index = self.space_name_index.read();
300 if let Some(space_id) = name_index.get(space_name) {
301 let spaces = self.spaces.read();
302 if let Some(data) = spaces.get(space_id) {
303 return Ok(Some(data.info.clone()));
304 }
305 }
306 Ok(None)
307 }
308
309 pub fn get_space_id(&self, space_name: &str) -> Result<u64, StorageError> {
310 let name_index = self.space_name_index.read();
311 if let Some(space_id) = name_index.get(space_name) {
312 Ok(*space_id)
313 } else {
314 Err(StorageError::db_error(format!(
315 "Space \"{}\" does not exist",
316 space_name
317 )))
318 }
319 }
320
321 pub fn get_space_by_id(&self, space_id: u64) -> Result<Option<SpaceInfo>, StorageError> {
322 let spaces = self.spaces.read();
323 Ok(spaces.get(&space_id).map(|d| d.info.clone()))
324 }
325
326 pub fn list_spaces(&self) -> Result<Vec<SpaceInfo>, StorageError> {
327 let spaces = self.spaces.read();
328 Ok(spaces.values().map(|d| d.info.clone()).collect())
329 }
330
331 pub fn update_space(&self, space: &SpaceInfo) -> Result<bool, StorageError> {
332 let applied = {
333 let mut spaces = self.spaces.write();
334 if let std::collections::hash_map::Entry::Occupied(mut e) = spaces.entry(space.space_id)
335 {
336 e.insert(SpaceData {
337 info: space.clone(),
338 });
339 true
340 } else {
341 false
342 }
343 };
344 if applied {
345 self.emit_schema_event(SchemaChangeEvent::SpaceUpdated {
346 space_id: space.space_id,
347 space_name: space.space_name.clone(),
348 });
349 }
350 Ok(applied)
351 }
352
353 pub fn create_tag(&self, space_name: &str, tag: &TagInfo) -> Result<u32, StorageError> {
354 let space_info = self.get_space(space_name)?.ok_or_else(|| {
355 StorageError::db_error(format!("Space \"{}\" does not exist", space_name))
356 })?;
357
358 let existing_tags = self.list_tags(space_name)?;
359 if existing_tags.iter().any(|t| t.tag_name == tag.tag_name) {
360 return Err(StorageError::label_already_exists(tag.tag_name.clone()));
361 }
362
363 let tag_id = self.get_next_tag_id(space_info.space_id);
364 let mut tag_with_id = tag.clone();
365 tag_with_id.tag_id = tag_id;
366
367 {
368 let mut tags = self.tags.write();
369 tags.insert((space_info.space_id, tag_id), TagData { info: tag_with_id });
370 }
371
372 self.emit_schema_event(SchemaChangeEvent::TagCreated {
373 space_id: space_info.space_id,
374 space_name: space_name.to_string(),
375 tag_id,
376 tag_name: tag.tag_name.clone(),
377 });
378
379 Ok(tag_id)
380 }
381
382 pub fn create_tag_with_id(
383 &self,
384 space_name: &str,
385 tag: &TagInfo,
386 tag_id: u32,
387 ) -> Result<u32, StorageError> {
388 let space_info = self.get_space(space_name)?.ok_or_else(|| {
389 StorageError::db_error(format!("Space \"{}\" does not exist", space_name))
390 })?;
391
392 let existing_tags = self.list_tags(space_name)?;
393 if existing_tags
394 .iter()
395 .any(|existing| existing.tag_name == tag.tag_name)
396 {
397 return Err(StorageError::label_already_exists(tag.tag_name.clone()));
398 }
399
400 let mut tag_with_id = tag.clone();
401 tag_with_id.tag_id = tag_id;
402
403 {
404 let mut tags = self.tags.write();
405 tags.insert((space_info.space_id, tag_id), TagData { info: tag_with_id });
406 }
407
408 let entry = self
409 .tag_id_counter
410 .entry(0)
411 .or_insert_with(|| AtomicU32::new(0));
412 let current = entry.load(Ordering::SeqCst);
413 if tag_id > current {
414 entry.store(tag_id, Ordering::SeqCst);
415 }
416
417 self.emit_schema_event(SchemaChangeEvent::TagCreated {
418 space_id: space_info.space_id,
419 space_name: space_name.to_string(),
420 tag_id,
421 tag_name: tag.tag_name.clone(),
422 });
423
424 Ok(tag_id)
425 }
426
427 pub fn drop_tag(&self, space_name: &str, tag_name: &str) -> Result<bool, StorageError> {
428 let space_info = self.get_space(space_name)?.ok_or_else(|| {
429 StorageError::db_error(format!("Space \"{}\" does not exist", space_name))
430 })?;
431
432 let removed = {
433 let mut tags = self.tags.write();
434 let tag_key = tags
435 .iter()
436 .find(|((sid, _), data)| {
437 *sid == space_info.space_id && data.info.tag_name == tag_name
438 })
439 .map(|(k, _)| *k);
440
441 if let Some(key) = tag_key {
442 let removed = tags.remove(&key);
443 removed.map(|d| d.info.tag_id)
444 } else {
445 None
446 }
447 };
448
449 if let Some(tag_id) = removed {
450 self.emit_schema_event(SchemaChangeEvent::TagDropped {
451 space_id: space_info.space_id,
452 space_name: space_name.to_string(),
453 tag_id,
454 tag_name: tag_name.to_string(),
455 });
456 Ok(true)
457 } else {
458 Ok(false)
459 }
460 }
461
462 pub fn get_tag(
463 &self,
464 space_name: &str,
465 tag_name: &str,
466 ) -> Result<Option<TagInfo>, StorageError> {
467 let space_info = self.get_space(space_name)?.ok_or_else(|| {
468 StorageError::db_error(format!("Space \"{}\" does not exist", space_name))
469 })?;
470
471 let tags = self.tags.read();
472 Ok(tags
473 .iter()
474 .find(|((sid, _), data)| *sid == space_info.space_id && data.info.tag_name == tag_name)
475 .map(|(_, data)| data)
476 .map(|d| d.info.clone()))
477 }
478
479 pub fn list_tags(&self, space_name: &str) -> Result<Vec<TagInfo>, StorageError> {
480 let space_info = self.get_space(space_name)?.ok_or_else(|| {
481 StorageError::db_error(format!("Space \"{}\" does not exist", space_name))
482 })?;
483
484 let tags = self.tags.read();
485 Ok(tags
486 .iter()
487 .filter(|((sid, _), _)| *sid == space_info.space_id)
488 .map(|(_, data)| data.info.clone())
489 .collect())
490 }
491
492 pub fn find_tag_by_id(&self, tag_id: u32) -> Option<(String, TagInfo)> {
493 let tags = self.tags.read();
494 let spaces = self.spaces.read();
495
496 tags.iter().find_map(|((space_id, current_tag_id), data)| {
497 if *current_tag_id != tag_id {
498 return None;
499 }
500
501 let space_name = spaces
502 .get(space_id)
503 .map(|space| space.info.space_name.clone())?;
504
505 Some((space_name, data.info.clone()))
506 })
507 }
508
509 pub fn update_tag(&self, space_name: &str, tag: &TagInfo) -> Result<bool, StorageError> {
510 let space_info = self.get_space(space_name)?.ok_or_else(|| {
511 StorageError::db_error(format!("Space \"{}\" does not exist", space_name))
512 })?;
513
514 let (applied, tag_id) = {
515 let mut tags = self.tags.write();
516 let tag_key = tags
517 .iter()
518 .find(|((sid, _), data)| {
519 *sid == space_info.space_id && data.info.tag_name == tag.tag_name
520 })
521 .map(|(k, _)| *k);
522
523 if let Some(key) = tag_key {
524 if let Some(data) = tags.get_mut(&key) {
525 let tag_id = data.info.tag_id;
526 data.info = tag.clone();
527 (true, tag_id)
528 } else {
529 (false, 0)
530 }
531 } else {
532 (false, 0)
533 }
534 };
535 if applied {
536 self.emit_schema_event(SchemaChangeEvent::TagAltered {
537 space_id: space_info.space_id,
538 space_name: space_name.to_string(),
539 tag_id,
540 tag_name: tag.tag_name.clone(),
541 added_properties: tag.properties.iter().map(|p| p.name.clone()).collect(),
542 removed_properties: Vec::new(),
543 });
544 }
545 Ok(applied)
546 }
547
548 pub fn create_edge_type(
549 &self,
550 space_name: &str,
551 edge_type: &EdgeTypeInfo,
552 ) -> Result<u32, StorageError> {
553 let space_info = self.get_space(space_name)?.ok_or_else(|| {
554 StorageError::db_error(format!("Space \"{}\" does not exist", space_name))
555 })?;
556
557 let existing = self.list_edge_types(space_name)?;
558 if existing
559 .iter()
560 .any(|e| e.edge_type_name == edge_type.edge_type_name)
561 {
562 return Err(StorageError::label_already_exists(
563 edge_type.edge_type_name.clone(),
564 ));
565 }
566
567 let edge_type_id = self.get_next_edge_type_id(space_info.space_id);
568 let mut edge_with_id = edge_type.clone();
569 edge_with_id.edge_type_id = edge_type_id;
570
571 {
572 let mut edge_types = self.edge_types.write();
573 edge_types.insert(
574 (space_info.space_id, edge_type_id),
575 EdgeTypeData { info: edge_with_id },
576 );
577 }
578
579 self.emit_schema_event(SchemaChangeEvent::EdgeTypeCreated {
580 space_id: space_info.space_id,
581 space_name: space_name.to_string(),
582 edge_type_id,
583 type_name: edge_type.edge_type_name.clone(),
584 });
585
586 Ok(edge_type_id)
587 }
588
589 pub fn create_edge_type_with_id(
590 &self,
591 space_name: &str,
592 edge_type: &EdgeTypeInfo,
593 edge_type_id: u32,
594 ) -> Result<u32, StorageError> {
595 let space_info = self.get_space(space_name)?.ok_or_else(|| {
596 StorageError::db_error(format!("Space \"{}\" does not exist", space_name))
597 })?;
598
599 let existing = self.list_edge_types(space_name)?;
600 if existing
601 .iter()
602 .any(|e| e.edge_type_name == edge_type.edge_type_name)
603 {
604 return Err(StorageError::label_already_exists(
605 edge_type.edge_type_name.clone(),
606 ));
607 }
608
609 let mut edge_with_id = edge_type.clone();
610 edge_with_id.edge_type_id = edge_type_id;
611
612 {
613 let mut edge_types = self.edge_types.write();
614 edge_types.insert(
615 (space_info.space_id, edge_type_id),
616 EdgeTypeData { info: edge_with_id },
617 );
618 }
619
620 let entry = self
621 .edge_type_id_counter
622 .entry(0)
623 .or_insert_with(|| AtomicU32::new(0));
624 let current = entry.load(Ordering::SeqCst);
625 if edge_type_id > current {
626 entry.store(edge_type_id, Ordering::SeqCst);
627 }
628
629 self.emit_schema_event(SchemaChangeEvent::EdgeTypeCreated {
630 space_id: space_info.space_id,
631 space_name: space_name.to_string(),
632 edge_type_id,
633 type_name: edge_type.edge_type_name.clone(),
634 });
635
636 Ok(edge_type_id)
637 }
638
639 pub fn drop_edge_type(
640 &self,
641 space_name: &str,
642 edge_type_name: &str,
643 ) -> Result<bool, StorageError> {
644 let space_info = self.get_space(space_name)?.ok_or_else(|| {
645 StorageError::db_error(format!("Space \"{}\" does not exist", space_name))
646 })?;
647
648 let removed: Option<(u32, String)> = {
649 let mut edge_types = self.edge_types.write();
650 let key = edge_types
651 .iter()
652 .find(|((sid, _), data)| {
653 *sid == space_info.space_id && data.info.edge_type_name == edge_type_name
654 })
655 .map(|(k, _)| *k);
656
657 if let Some(k) = key {
658 edge_types
659 .remove(&k)
660 .map(|d| (d.info.edge_type_id, d.info.edge_type_name))
661 } else {
662 None
663 }
664 };
665
666 if let Some((edge_type_id, type_name)) = removed {
667 self.emit_schema_event(SchemaChangeEvent::EdgeTypeDropped {
668 space_id: space_info.space_id,
669 space_name: space_name.to_string(),
670 edge_type_id,
671 type_name,
672 });
673 Ok(true)
674 } else {
675 Ok(false)
676 }
677 }
678
679 pub fn get_edge_type(
680 &self,
681 space_name: &str,
682 edge_type_name: &str,
683 ) -> Result<Option<EdgeTypeInfo>, StorageError> {
684 let space_info = self.get_space(space_name)?.ok_or_else(|| {
685 StorageError::db_error(format!("Space \"{}\" does not exist", space_name))
686 })?;
687
688 let edge_types = self.edge_types.read();
689 Ok(edge_types
690 .iter()
691 .find(|((sid, _), data)| {
692 *sid == space_info.space_id && data.info.edge_type_name == edge_type_name
693 })
694 .map(|(_, data)| data)
695 .map(|d| d.info.clone()))
696 }
697
698 pub fn list_edge_types(&self, space_name: &str) -> Result<Vec<EdgeTypeInfo>, StorageError> {
699 let space_info = self.get_space(space_name)?.ok_or_else(|| {
700 StorageError::db_error(format!("Space \"{}\" does not exist", space_name))
701 })?;
702
703 let edge_types = self.edge_types.read();
704 Ok(edge_types
705 .iter()
706 .filter(|((sid, _), _)| *sid == space_info.space_id)
707 .map(|(_, data)| data.info.clone())
708 .collect())
709 }
710
711 pub fn find_edge_type_by_id(&self, edge_type_id: u32) -> Option<(String, EdgeTypeInfo)> {
712 let edge_types = self.edge_types.read();
713 let spaces = self.spaces.read();
714
715 edge_types
716 .iter()
717 .find_map(|((space_id, current_edge_type_id), data)| {
718 if *current_edge_type_id != edge_type_id {
719 return None;
720 }
721
722 let space_name = spaces
723 .get(space_id)
724 .map(|space| space.info.space_name.clone())?;
725
726 Some((space_name, data.info.clone()))
727 })
728 }
729
730 pub fn update_edge_type(
731 &self,
732 space_name: &str,
733 edge_type: &EdgeTypeInfo,
734 ) -> Result<bool, StorageError> {
735 let space_info = self.get_space(space_name)?.ok_or_else(|| {
736 StorageError::db_error(format!("Space \"{}\" does not exist", space_name))
737 })?;
738
739 let (applied, edge_type_id) = {
740 let mut edge_types = self.edge_types.write();
741 let key = edge_types
742 .iter()
743 .find(|((sid, _), data)| {
744 *sid == space_info.space_id
745 && data.info.edge_type_name == edge_type.edge_type_name
746 })
747 .map(|(k, _)| *k);
748
749 if let Some(k) = key {
750 if let Some(data) = edge_types.get_mut(&k) {
751 let id = data.info.edge_type_id;
752 data.info = edge_type.clone();
753 (true, id)
754 } else {
755 (false, 0)
756 }
757 } else {
758 (false, 0)
759 }
760 };
761 if applied {
762 self.emit_schema_event(SchemaChangeEvent::EdgeTypeAltered {
763 space_id: space_info.space_id,
764 space_name: space_name.to_string(),
765 edge_type_id,
766 type_name: edge_type.edge_type_name.clone(),
767 added_properties: edge_type
768 .properties
769 .iter()
770 .map(|p| p.name.clone())
771 .collect(),
772 removed_properties: Vec::new(),
773 });
774 }
775 Ok(applied)
776 }
777
778 pub fn alter_tag(
779 &self,
780 space_name: &str,
781 tag_name: &str,
782 additions: Vec<PropertyDef>,
783 deletions: Vec<String>,
784 new_comment: Option<String>,
785 ) -> Result<bool, StorageError> {
786 let space_info = self.get_space(space_name)?.ok_or_else(|| {
787 StorageError::db_error(format!("Space \"{}\" does not exist", space_name))
788 })?;
789
790 let applied: Option<(u32, Vec<String>, Vec<String>)> = {
791 let mut tags = self.tags.write();
792 let tag_key = tags
793 .iter()
794 .find(|((sid, _), data)| {
795 *sid == space_info.space_id && data.info.tag_name == tag_name
796 })
797 .map(|(k, _)| *k);
798
799 if let Some(key) = tag_key {
800 if let Some(data) = tags.get_mut(&key) {
801 let added: Vec<String> = additions.iter().map(|p| p.name.clone()).collect();
802 for prop in additions {
803 if !data.info.properties.iter().any(|p| p.name == prop.name) {
804 data.info.properties.push(prop);
805 }
806 }
807 data.info
808 .properties
809 .retain(|p| !deletions.contains(&p.name));
810 if let Some(col) = data.info.ttl_col.clone() {
811 if !data.info.properties.iter().any(|p| p.name == col) {
812 data.info.ttl_duration = None;
813 data.info.ttl_col = None;
814 }
815 }
816 if let Some(comment) = new_comment {
817 if comment.is_empty() {
818 data.info.comment = None;
819 } else {
820 data.info.comment = Some(comment);
821 }
822 }
823 Some((data.info.tag_id, added, deletions))
824 } else {
825 None
826 }
827 } else {
828 None
829 }
830 };
831 if let Some((tag_id, added, removed)) = applied {
832 self.emit_schema_event(SchemaChangeEvent::TagAltered {
833 space_id: space_info.space_id,
834 space_name: space_name.to_string(),
835 tag_id,
836 tag_name: tag_name.to_string(),
837 added_properties: added,
838 removed_properties: removed,
839 });
840 Ok(true)
841 } else {
842 Ok(false)
843 }
844 }
845 pub fn rename_tag_property(
846 &self,
847 space_name: &str,
848 tag_name: &str,
849 old_name: &str,
850 new_name: &str,
851 ) -> Result<bool, StorageError> {
852 let space_info = self.get_space(space_name)?.ok_or_else(|| {
853 StorageError::db_error(format!("Space \"{}\" does not exist", space_name))
854 })?;
855
856 let applied: Option<(u32, Vec<String>, Vec<String>)> = {
857 let mut tags = self.tags.write();
858 let tag_key = tags
859 .iter()
860 .find(|((sid, _), data)| {
861 *sid == space_info.space_id && data.info.tag_name == tag_name
862 })
863 .map(|(k, _)| *k);
864
865 if let Some(key) = tag_key {
866 if let Some(data) = tags.get_mut(&key) {
867 if let Some(prop) = data.info.properties.iter_mut().find(|p| p.name == old_name)
868 {
869 prop.name = new_name.to_string();
870 if data.info.ttl_col.as_deref() == Some(old_name) {
871 data.info.ttl_col = Some(new_name.to_string());
872 }
873 Some((
874 data.info.tag_id,
875 vec![new_name.to_string()],
876 vec![old_name.to_string()],
877 ))
878 } else {
879 None
880 }
881 } else {
882 None
883 }
884 } else {
885 None
886 }
887 };
888 if let Some((tag_id, added, removed)) = applied {
889 self.emit_schema_event(SchemaChangeEvent::TagAltered {
890 space_id: space_info.space_id,
891 space_name: space_name.to_string(),
892 tag_id,
893 tag_name: tag_name.to_string(),
894 added_properties: added,
895 removed_properties: removed,
896 });
897 Ok(true)
898 } else {
899 Ok(false)
900 }
901 }
902
903 pub fn rename_tag(
904 &self,
905 space_name: &str,
906 old_name: &str,
907 new_name: &str,
908 ) -> Result<bool, StorageError> {
909 let space_info = self.get_space(space_name)?.ok_or_else(|| {
910 StorageError::db_error(format!("Space \"{}\" does not exist", space_name))
911 })?;
912
913 let mut tags = self.tags.write();
914
915 let tag_key = tags
916 .iter()
917 .find(|((sid, _), data)| *sid == space_info.space_id && data.info.tag_name == old_name)
918 .map(|(k, _)| *k);
919
920 if let Some(old_key) = tag_key {
921 let data = tags
922 .remove(&old_key)
923 .ok_or_else(|| StorageError::db_error(format!("Tag \"{}\" not found", old_name)))?;
924
925 if tags
926 .iter()
927 .any(|((sid, _), d)| *sid == space_info.space_id && d.info.tag_name == new_name)
928 {
929 tags.insert(old_key, data);
930 return Err(StorageError::db_error(format!(
931 "Tag \"{}\" already exists",
932 new_name
933 )));
934 }
935
936 let mut new_data = data;
937 new_data.info.tag_name = new_name.to_string();
938 let renamed_tag_id = new_data.info.tag_id;
939
940 let new_key = (old_key.0, old_key.1);
941 tags.insert(new_key, new_data);
942 drop(tags);
943
944 {
945 let mut edge_types = self.edge_types.write();
946 for ((sid, _), data) in edge_types.iter_mut() {
947 if *sid == space_info.space_id {
948 if data.info.src_tag_name == old_name {
949 data.info.src_tag_name = new_name.to_string();
950 }
951 if data.info.dst_tag_name == old_name {
952 data.info.dst_tag_name = new_name.to_string();
953 }
954 }
955 }
956 }
957
958 self.emit_schema_event(SchemaChangeEvent::TagRenamed {
959 space_id: space_info.space_id,
960 space_name: space_name.to_string(),
961 tag_id: renamed_tag_id,
962 old_name: old_name.to_string(),
963 new_name: new_name.to_string(),
964 });
965
966 return Ok(true);
967 }
968
969 Ok(false)
970 }
971
972 pub fn rename_edge_type(
973 &self,
974 space_name: &str,
975 old_name: &str,
976 new_name: &str,
977 ) -> Result<bool, StorageError> {
978 let space_info = self.get_space(space_name)?.ok_or_else(|| {
979 StorageError::db_error(format!("Space \"{}\" does not exist", space_name))
980 })?;
981
982 let renamed: Option<u32> = {
983 let mut edge_types = self.edge_types.write();
984
985 let key = edge_types
986 .iter()
987 .find(|((sid, _), data)| {
988 *sid == space_info.space_id && data.info.edge_type_name == old_name
989 })
990 .map(|(k, _)| *k);
991
992 if let Some(old_key) = key {
993 let data = edge_types.remove(&old_key).ok_or_else(|| {
994 StorageError::db_error(format!("Edge type \"{}\" not found", old_name))
995 })?;
996
997 if edge_types.iter().any(|((sid, _), d)| {
998 *sid == space_info.space_id && d.info.edge_type_name == new_name
999 }) {
1000 edge_types.insert(old_key, data);
1001 return Err(StorageError::db_error(format!(
1002 "Edge type \"{}\" already exists",
1003 new_name
1004 )));
1005 }
1006
1007 let mut new_data = data;
1008 new_data.info.edge_type_name = new_name.to_string();
1009 let renamed_id = new_data.info.edge_type_id;
1010
1011 let new_key = (old_key.0, old_key.1);
1012 edge_types.insert(new_key, new_data);
1013
1014 Some(renamed_id)
1015 } else {
1016 None
1017 }
1018 };
1019
1020 if let Some(edge_type_id) = renamed {
1021 self.emit_schema_event(SchemaChangeEvent::EdgeTypeRenamed {
1022 space_id: space_info.space_id,
1023 space_name: space_name.to_string(),
1024 edge_type_id,
1025 old_name: old_name.to_string(),
1026 new_name: new_name.to_string(),
1027 });
1028 return Ok(true);
1029 }
1030
1031 Ok(false)
1032 }
1033
1034 pub fn rename_edge_property(
1035 &self,
1036 space_name: &str,
1037 edge_type_name: &str,
1038 old_name: &str,
1039 new_name: &str,
1040 ) -> Result<bool, StorageError> {
1041 let space_info = self.get_space(space_name)?.ok_or_else(|| {
1042 StorageError::db_error(format!("Space \"{}\" does not exist", space_name))
1043 })?;
1044
1045 let applied: Option<(u32, Vec<String>, Vec<String>)> = {
1046 let mut edge_types = self.edge_types.write();
1047 let key = edge_types
1048 .iter()
1049 .find(|((sid, _), data)| {
1050 *sid == space_info.space_id && data.info.edge_type_name == edge_type_name
1051 })
1052 .map(|(k, _)| *k);
1053
1054 if let Some(key) = key {
1055 if let Some(data) = edge_types.get_mut(&key) {
1056 if let Some(prop) = data
1057 .info
1058 .properties
1059 .iter_mut()
1060 .find(|p| p.name == old_name)
1061 {
1062 prop.name = new_name.to_string();
1063 if data.info.ttl_col.as_deref() == Some(old_name) {
1064 data.info.ttl_col = Some(new_name.to_string());
1065 }
1066 Some((
1067 data.info.edge_type_id,
1068 vec![new_name.to_string()],
1069 vec![old_name.to_string()],
1070 ))
1071 } else {
1072 None
1073 }
1074 } else {
1075 None
1076 }
1077 } else {
1078 None
1079 }
1080 };
1081 if let Some((edge_type_id, added, removed)) = applied {
1082 self.emit_schema_event(SchemaChangeEvent::EdgeTypeAltered {
1083 space_id: space_info.space_id,
1084 space_name: space_name.to_string(),
1085 edge_type_id,
1086 type_name: edge_type_name.to_string(),
1087 added_properties: added,
1088 removed_properties: removed,
1089 });
1090 Ok(true)
1091 } else {
1092 Ok(false)
1093 }
1094 }
1095
1096 pub fn alter_edge_type(
1097 &self,
1098 space_name: &str,
1099 edge_type_name: &str,
1100 additions: Vec<PropertyDef>,
1101 deletions: Vec<String>,
1102 new_comment: Option<String>,
1103 ) -> Result<bool, StorageError> {
1104 let space_info = self.get_space(space_name)?.ok_or_else(|| {
1105 StorageError::db_error(format!("Space \"{}\" does not exist", space_name))
1106 })?;
1107
1108 let applied: Option<(u32, Vec<String>, Vec<String>)> = {
1109 let mut edge_types = self.edge_types.write();
1110 let key = edge_types
1111 .iter()
1112 .find(|((sid, _), data)| {
1113 *sid == space_info.space_id && data.info.edge_type_name == edge_type_name
1114 })
1115 .map(|(k, _)| *k);
1116
1117 if let Some(k) = key {
1118 if let Some(data) = edge_types.get_mut(&k) {
1119 let added: Vec<String> = additions.iter().map(|p| p.name.clone()).collect();
1120 for prop in additions {
1121 if !data.info.properties.iter().any(|p| p.name == prop.name) {
1122 data.info.properties.push(prop);
1123 }
1124 }
1125 data.info
1126 .properties
1127 .retain(|p| !deletions.contains(&p.name));
1128 if let Some(col) = data.info.ttl_col.clone() {
1129 if !data.info.properties.iter().any(|p| p.name == col) {
1130 data.info.ttl_duration = None;
1131 data.info.ttl_col = None;
1132 }
1133 }
1134 if let Some(comment) = new_comment {
1135 if comment.is_empty() {
1136 data.info.comment = None;
1137 } else {
1138 data.info.comment = Some(comment);
1139 }
1140 }
1141 Some((data.info.edge_type_id, added, deletions))
1142 } else {
1143 None
1144 }
1145 } else {
1146 None
1147 }
1148 };
1149 if let Some((edge_type_id, added, removed)) = applied {
1150 self.emit_schema_event(SchemaChangeEvent::EdgeTypeAltered {
1151 space_id: space_info.space_id,
1152 space_name: space_name.to_string(),
1153 edge_type_id,
1154 type_name: edge_type_name.to_string(),
1155 added_properties: added,
1156 removed_properties: removed,
1157 });
1158 return Ok(true);
1159 }
1160 Ok(false)
1161 }
1162
1163 pub fn update_edge_endpoints(
1164 &self,
1165 space_name: &str,
1166 edge_type_name: &str,
1167 src_tag_name: &str,
1168 dst_tag_name: &str,
1169 ) -> Result<bool, StorageError> {
1170 let space_info = self.get_space(space_name)?.ok_or_else(|| {
1171 StorageError::db_error(format!("Space \"{}\" does not exist", space_name))
1172 })?;
1173
1174 let applied: Option<u32> = {
1175 let mut edge_types = self.edge_types.write();
1176 let key = edge_types
1177 .iter()
1178 .find(|((sid, _), data)| {
1179 *sid == space_info.space_id && data.info.edge_type_name == edge_type_name
1180 })
1181 .map(|(k, _)| *k);
1182
1183 if let Some(k) = key {
1184 if let Some(data) = edge_types.get_mut(&k) {
1185 data.info.src_tag_name = src_tag_name.to_string();
1186 data.info.dst_tag_name = dst_tag_name.to_string();
1187 Some(data.info.edge_type_id)
1188 } else {
1189 None
1190 }
1191 } else {
1192 None
1193 }
1194 };
1195 if let Some(edge_type_id) = applied {
1196 self.emit_schema_event(SchemaChangeEvent::EdgeTypeAltered {
1197 space_id: space_info.space_id,
1198 space_name: space_name.to_string(),
1199 edge_type_id,
1200 type_name: edge_type_name.to_string(),
1201 added_properties: Vec::new(),
1202 removed_properties: Vec::new(),
1203 });
1204 return Ok(true);
1205 }
1206 Ok(false)
1207 }
1208
1209 pub fn save_schema(&self, path: &Path) -> Result<(), StorageError> {
1210 use std::fs::{self, File};
1211 use std::io::Write;
1212
1213 if let Some(parent) = path.parent() {
1214 fs::create_dir_all(parent).map_err(|e| StorageError::io_error(e.to_string()))?;
1215 }
1216
1217 let spaces: Vec<SpaceInfo> = self
1218 .spaces
1219 .read()
1220 .values()
1221 .map(|d| d.info.clone())
1222 .collect();
1223
1224 let tags: Vec<(u64, TagInfo)> = self
1225 .tags
1226 .read()
1227 .iter()
1228 .map(|((space_id, _), data)| (*space_id, data.info.clone()))
1229 .collect();
1230
1231 let edge_types: Vec<(u64, EdgeTypeInfo)> = self
1232 .edge_types
1233 .read()
1234 .iter()
1235 .map(|((space_id, _), data)| (*space_id, data.info.clone()))
1236 .collect();
1237
1238 let space_id_counter = self
1239 .space_id_counter
1240 .load(std::sync::atomic::Ordering::SeqCst);
1241
1242 let tag_id_counters: Vec<(u64, u32)> = self
1243 .tag_id_counter
1244 .iter()
1245 .map(|entry| (*entry.key(), entry.value().load(Ordering::SeqCst)))
1246 .collect();
1247
1248 let edge_type_id_counters: Vec<(u64, u32)> = self
1249 .edge_type_id_counter
1250 .iter()
1251 .map(|entry| (*entry.key(), entry.value().load(Ordering::SeqCst)))
1252 .collect();
1253
1254 let snapshot = SchemaSnapshot {
1255 version: SCHEMA_FORMAT_VERSION,
1256 spaces,
1257 tags,
1258 edge_types,
1259 space_id_counter,
1260 tag_id_counters,
1261 edge_type_id_counters,
1262 serial_next: self.serial_next.read().clone(),
1263 sequences: self.sequences.read().values().cloned().collect(),
1264 };
1265
1266 let json = serde_json::to_string_pretty(&snapshot)
1267 .map_err(|e| StorageError::serialize_error(e.to_string()))?;
1268
1269 let mut file = File::create(path).map_err(|e| StorageError::io_error(e.to_string()))?;
1270
1271 file.write_all(json.as_bytes())
1272 .map_err(|e| StorageError::io_error(e.to_string()))?;
1273
1274 Ok(())
1275 }
1276
1277 pub fn load_schema(&self, path: &Path) -> Result<(), StorageError> {
1278 use std::fs::File;
1279 use std::io::Read;
1280
1281 if !path.exists() {
1282 return Ok(());
1283 }
1284
1285 let mut file = File::open(path).map_err(|e| StorageError::io_error(e.to_string()))?;
1286
1287 let mut json = String::new();
1288 file.read_to_string(&mut json)
1289 .map_err(|e| StorageError::io_error(e.to_string()))?;
1290
1291 let snapshot: SchemaSnapshot = serde_json::from_str(&json)
1292 .map_err(|e| StorageError::deserialize_error(e.to_string()))?;
1293
1294 if snapshot.version > SCHEMA_FORMAT_VERSION {
1295 return Err(StorageError::deserialize_error(format!(
1296 "Schema version {} is newer than supported version {}",
1297 snapshot.version, SCHEMA_FORMAT_VERSION
1298 )));
1299 }
1300
1301 self.spaces.write().clear();
1302 self.space_name_index.write().clear();
1303 self.tags.write().clear();
1304 self.edge_types.write().clear();
1305 self.tag_id_counter.clear();
1306 self.edge_type_id_counter.clear();
1307
1308 let max_tag_counter = snapshot
1309 .tag_id_counters
1310 .iter()
1311 .map(|(_, counter)| *counter)
1312 .max()
1313 .unwrap_or(0)
1314 .max(
1315 snapshot
1316 .tags
1317 .iter()
1318 .map(|(_, tag)| tag.tag_id)
1319 .max()
1320 .unwrap_or(0),
1321 );
1322 let max_edge_type_counter = snapshot
1323 .edge_type_id_counters
1324 .iter()
1325 .map(|(_, counter)| *counter)
1326 .max()
1327 .unwrap_or(0)
1328 .max(
1329 snapshot
1330 .edge_types
1331 .iter()
1332 .map(|(_, edge_type)| edge_type.edge_type_id)
1333 .max()
1334 .unwrap_or(0),
1335 );
1336
1337 for space in snapshot.spaces {
1338 self.space_name_index
1339 .write()
1340 .insert(space.space_name.clone(), space.space_id);
1341 self.spaces
1342 .write()
1343 .insert(space.space_id, SpaceData { info: space });
1344 }
1345
1346 for (space_id, tag) in snapshot.tags {
1347 self.tags
1348 .write()
1349 .insert((space_id, tag.tag_id), TagData { info: tag });
1350 }
1351
1352 for (space_id, edge_type) in snapshot.edge_types {
1353 self.edge_types.write().insert(
1354 (space_id, edge_type.edge_type_id),
1355 EdgeTypeData { info: edge_type },
1356 );
1357 }
1358
1359 self.space_id_counter
1360 .store(snapshot.space_id_counter, Ordering::SeqCst);
1361
1362 for (space_id, counter) in snapshot.tag_id_counters {
1363 self.tag_id_counter
1364 .insert(space_id, AtomicU32::new(counter));
1365 }
1366 self.tag_id_counter
1367 .insert(0, AtomicU32::new(max_tag_counter));
1368
1369 for (space_id, counter) in snapshot.edge_type_id_counters {
1370 self.edge_type_id_counter
1371 .insert(space_id, AtomicU32::new(counter));
1372 }
1373 self.edge_type_id_counter
1374 .insert(0, AtomicU32::new(max_edge_type_counter));
1375
1376 *self.serial_next.write() = snapshot.serial_next;
1377
1378 let mut sequences = self.sequences.write();
1379 sequences.clear();
1380 for def in snapshot.sequences {
1381 sequences.insert(def.name.clone(), def);
1382 }
1383
1384 Ok(())
1385 }
1386
1387 pub fn create_sequence(
1391 &self,
1392 name: String,
1393 start: i64,
1394 increment: i64,
1395 min_value: i64,
1396 max_value: i64,
1397 cycle: bool,
1398 ) -> Result<(), StorageError> {
1399 {
1400 let mut sequences = self.sequences.write();
1401 if sequences.contains_key(&name) {
1402 return Err(StorageError::db_error(format!(
1403 "Sequence '{}' already exists",
1404 name
1405 )));
1406 }
1407 let def = SequenceDef::new(name.clone(), start, increment, min_value, max_value, cycle);
1408 sequences.insert(name.clone(), def);
1409 }
1410 self.emit_schema_event(SchemaChangeEvent::SequenceCreated { name });
1411 Ok(())
1412 }
1413
1414 pub fn drop_sequence(&self, name: &str) -> Result<bool, StorageError> {
1416 let removed = {
1417 let mut sequences = self.sequences.write();
1418 sequences.remove(name).is_some()
1419 };
1420 if removed {
1421 self.emit_schema_event(SchemaChangeEvent::SequenceDropped {
1422 name: name.to_string(),
1423 });
1424 }
1425 Ok(removed)
1426 }
1427
1428 pub fn get_sequence(&self, name: &str) -> Option<SequenceDef> {
1430 let sequences = self.sequences.read();
1431 sequences.get(name).cloned()
1432 }
1433
1434 pub fn sequence_current_value(&self, name: &str) -> Result<i64, StorageError> {
1436 let sequences = self.sequences.read();
1437 let def = sequences
1438 .get(name)
1439 .ok_or_else(|| StorageError::db_error(format!("Sequence '{}' does not exist", name)))?;
1440 Ok(def.current_value())
1441 }
1442
1443 pub fn sequence_next_value(&self, name: &str) -> Result<i64, StorageError> {
1445 let sequences = self.sequences.read();
1446 let def = sequences
1447 .get(name)
1448 .ok_or_else(|| StorageError::db_error(format!("Sequence '{}' does not exist", name)))?;
1449 def.next_value()
1450 }
1451
1452 pub fn alter_sequence(
1454 &self,
1455 name: &str,
1456 increment: Option<i64>,
1457 min_value: Option<i64>,
1458 max_value: Option<i64>,
1459 cycle: Option<bool>,
1460 ) -> Result<(), StorageError> {
1461 {
1462 let mut sequences = self.sequences.write();
1463 let def = sequences.get(name).ok_or_else(|| {
1464 StorageError::db_error(format!("Sequence '{}' does not exist", name))
1465 })?;
1466
1467 let new_increment = increment.unwrap_or(def.increment);
1468 let new_min = min_value.unwrap_or(def.min_value);
1469 let new_max = max_value.unwrap_or(def.max_value);
1470 let new_cycle = cycle.unwrap_or(def.cycle);
1471 let current = def.current_value();
1472
1473 let new_def = SequenceDef::new(
1474 name.to_string(),
1475 current,
1476 new_increment,
1477 new_min,
1478 new_max,
1479 new_cycle,
1480 );
1481 sequences.insert(name.to_string(), new_def);
1482 }
1483 self.emit_schema_event(SchemaChangeEvent::SequenceAltered {
1484 name: name.to_string(),
1485 });
1486 Ok(())
1487 }
1488
1489 pub fn list_sequences(&self) -> Vec<String> {
1491 let sequences = self.sequences.read();
1492 sequences.keys().cloned().collect()
1493 }
1494}
1495
1496impl Default for SchemaManager {
1497 fn default() -> Self {
1498 Self::new()
1499 }
1500}
1501
1502#[cfg(test)]
1503mod tests {
1504 use super::*;
1505 use crate::types::{EdgeTypeInfo, SpaceInfo, TagInfo};
1506
1507 #[test]
1508 fn schema_names_are_scoped_by_space() {
1509 let manager = SchemaManager::new();
1510 let mut first = SpaceInfo::new("first".to_string());
1511 let mut second = SpaceInfo::new("second".to_string());
1512 manager
1513 .create_space(&mut first)
1514 .expect("create first space");
1515 manager
1516 .create_space(&mut second)
1517 .expect("create second space");
1518
1519 let first_tag_id = manager
1520 .create_tag("first", &TagInfo::new("person".to_string()))
1521 .expect("create first tag");
1522 let second_tag_id = manager
1523 .create_tag("second", &TagInfo::new("person".to_string()))
1524 .expect("create second tag");
1525
1526 assert_ne!(first_tag_id, second_tag_id);
1527 assert_eq!(
1528 manager
1529 .get_tag("first", "person")
1530 .expect("get first tag")
1531 .expect("first tag exists")
1532 .tag_id,
1533 first_tag_id
1534 );
1535 assert_eq!(
1536 manager
1537 .get_tag("second", "person")
1538 .expect("get second tag")
1539 .expect("second tag exists")
1540 .tag_id,
1541 second_tag_id
1542 );
1543
1544 let first_edge_id = manager
1545 .create_edge_type("first", &EdgeTypeInfo::new("knows".to_string()))
1546 .expect("create first edge");
1547 let second_edge_id = manager
1548 .create_edge_type("second", &EdgeTypeInfo::new("knows".to_string()))
1549 .expect("create second edge");
1550
1551 assert_ne!(first_edge_id, second_edge_id);
1552 assert_eq!(
1553 manager
1554 .get_edge_type("first", "knows")
1555 .expect("get first edge")
1556 .expect("first edge exists")
1557 .edge_type_id,
1558 first_edge_id
1559 );
1560 assert_eq!(
1561 manager
1562 .get_edge_type("second", "knows")
1563 .expect("get second edge")
1564 .expect("second edge exists")
1565 .edge_type_id,
1566 second_edge_id
1567 );
1568 }
1569}