1use super::{Attributes, EdgeIndex, GraphRecord, Group, NodeIndex};
2use crate::{
3 errors::GraphError,
4 graphrecord::{GraphRecordAttribute, datatypes::DataType},
5};
6use graphrecords_utils::aliases::GrHashMap;
7#[cfg(feature = "serde")]
8use serde::{Deserialize, Serialize};
9use std::{
10 collections::{HashMap, hash_map::Entry},
11 ops::Deref,
12};
13
14#[derive(Debug, Clone, Copy, PartialEq, Eq)]
15#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
16pub enum AttributeType {
17 Categorical,
18 Continuous,
19 Temporal,
20 Unstructured,
21}
22
23impl AttributeType {
24 #[must_use]
25 pub fn infer(data_type: &DataType) -> Self {
26 match data_type {
27 DataType::String | DataType::Null | DataType::Any => Self::Unstructured,
28 DataType::Int | DataType::Float => Self::Continuous,
29 DataType::Bool => Self::Categorical,
30 DataType::DateTime | DataType::Duration => Self::Temporal,
31 DataType::Union((first_dataype, second_dataype)) => {
32 Self::infer(first_dataype).merge(Self::infer(second_dataype))
33 }
34 DataType::Option(dataype) => Self::infer(dataype),
35 }
36 }
37
38 const fn merge(self, other: Self) -> Self {
39 match (self, other) {
40 (Self::Categorical, Self::Unstructured) | (Self::Unstructured, Self::Categorical) => {
41 Self::Unstructured
42 }
43 (Self::Categorical, _) | (_, Self::Categorical) => Self::Categorical,
44 (Self::Continuous, Self::Continuous) => Self::Continuous,
45 (Self::Temporal, Self::Temporal) => Self::Temporal,
46 _ => Self::Unstructured,
47 }
48 }
49}
50
51impl DataType {
52 fn merge(&self, other: &Self) -> Self {
53 if self.evaluate(other) {
54 self.clone()
55 } else {
56 match (self, other) {
57 (Self::Null, _) => Self::Option(Box::new(other.clone())),
58 (_, Self::Null) => Self::Option(Box::new(self.clone())),
59 (_, Self::Any) => Self::Any,
60 (Self::Option(option1), Self::Option(option2)) => {
61 Self::Option(Box::new(option1.merge(option2)))
62 }
63 (Self::Option(option), _) => Self::Option(Box::new(option.merge(other))),
64 (_, Self::Option(option)) => Self::Option(Box::new(self.merge(option))),
65 _ => Self::Union((Box::new(self.clone()), Box::new(other.clone()))),
66 }
67 }
68 }
69}
70
71#[derive(Debug, Clone, PartialEq)]
72#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
73pub struct AttributeDataType {
74 data_type: DataType,
75 attribute_type: AttributeType,
76}
77
78impl AttributeDataType {
79 fn validate(data_type: &DataType, attribute_type: AttributeType) -> Result<(), GraphError> {
80 match (attribute_type, data_type) {
81 (AttributeType::Categorical | AttributeType::Unstructured, _)
82 | (AttributeType::Continuous, DataType::Int | DataType::Float | DataType::Null)
83 | (AttributeType::Temporal, DataType::DateTime | DataType::Duration | DataType::Null) => {
84 Ok(())
85 }
86
87 (_, DataType::Option(option)) => Self::validate(option, attribute_type),
88 (_, DataType::Union((first_datatype, second_datatype))) => {
89 Self::validate(first_datatype, attribute_type)?;
90 Self::validate(second_datatype, attribute_type)
91 }
92
93 (AttributeType::Continuous, _) => Err(GraphError::SchemaError(
94 "Continuous attribute must be of (sub-)type Int or Float.".to_string(),
95 )),
96
97 (AttributeType::Temporal, _) => Err(GraphError::SchemaError(
98 "Temporal attribute must be of (sub-)type DateTime or Duration.".to_string(),
99 )),
100 }
101 }
102
103 pub fn new(data_type: DataType, attribute_type: AttributeType) -> Result<Self, GraphError> {
104 Self::validate(&data_type, attribute_type)?;
105
106 Ok(Self {
107 data_type,
108 attribute_type,
109 })
110 }
111
112 #[must_use]
113 pub const fn data_type(&self) -> &DataType {
114 &self.data_type
115 }
116
117 #[must_use]
118 pub const fn attribute_type(&self) -> &AttributeType {
119 &self.attribute_type
120 }
121
122 fn merge(&mut self, other: &Self) {
123 match (self.data_type.clone(), other.data_type.clone()) {
124 (DataType::Null, _) => {
125 self.data_type = self.data_type.merge(&other.data_type);
126 self.attribute_type = other.attribute_type;
127 }
128 (_, DataType::Null) => {
129 self.data_type = self.data_type.merge(&other.data_type);
130 }
131 _ => {
132 self.data_type = self.data_type.merge(&other.data_type);
133 self.attribute_type = self.attribute_type.merge(other.attribute_type);
134 }
135 }
136 }
137}
138
139impl From<DataType> for AttributeDataType {
140 fn from(value: DataType) -> Self {
141 let attribute_type = AttributeType::infer(&value);
142
143 Self {
144 data_type: value,
145 attribute_type,
146 }
147 }
148}
149
150impl From<(DataType, AttributeType)> for AttributeDataType {
151 fn from(value: (DataType, AttributeType)) -> Self {
152 Self {
153 data_type: value.0,
154 attribute_type: value.1,
155 }
156 }
157}
158
159#[derive(Debug, Clone, Copy)]
160enum AttributeSchemaKind<'a> {
161 Node(&'a NodeIndex),
162 Edge(&'a EdgeIndex),
163}
164
165impl AttributeSchemaKind<'_> {
166 fn error_message(&self, key: &GraphRecordAttribute, data_type: &DataType) -> String {
167 match self {
168 Self::Node(index) => {
169 format!("Attribute {key} of type {data_type} not found on node with index {index}")
170 }
171 Self::Edge(index) => {
172 format!("Attribute {key} of type {data_type} not found on edge with index {index}")
173 }
174 }
175 }
176
177 fn error_message_expected(
178 &self,
179 key: &GraphRecordAttribute,
180 data_type: &DataType,
181 expected_data_type: &DataType,
182 ) -> String {
183 match self {
184 Self::Node(index) => format!(
185 "Attribute {key} of node with index {index} is of type {data_type}. Expected {expected_data_type}."
186 ),
187 Self::Edge(index) => format!(
188 "Attribute {key} of edge with index {index} is of type {data_type}. Expected {expected_data_type}."
189 ),
190 }
191 }
192
193 fn error_message_too_many(&self, attributes: &[String]) -> String {
194 match self {
195 Self::Node(index) => format!(
196 "Attributes [{}] of node with index {} do not exist in schema.",
197 attributes.join(", "),
198 index
199 ),
200 Self::Edge(index) => format!(
201 "Attributes [{}] of edge with index {} do not exist in schema.",
202 attributes.join(", "),
203 index
204 ),
205 }
206 }
207}
208
209type AttributeSchemaMapping = HashMap<GraphRecordAttribute, AttributeDataType>;
210
211#[derive(Debug, Clone, PartialEq, Default)]
212#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
213pub struct AttributeSchema(AttributeSchemaMapping);
214
215impl Deref for AttributeSchema {
216 type Target = AttributeSchemaMapping;
217
218 fn deref(&self) -> &Self::Target {
219 &self.0
220 }
221}
222
223impl<T> From<T> for AttributeSchema
224where
225 T: Into<AttributeSchemaMapping>,
226{
227 fn from(value: T) -> Self {
228 Self(value.into())
229 }
230}
231
232impl AttributeSchema {
233 #[must_use]
234 pub const fn new(mapping: HashMap<GraphRecordAttribute, AttributeDataType>) -> Self {
235 Self(mapping)
236 }
237
238 fn validate(
239 &self,
240 attributes: &Attributes,
241 kind: &AttributeSchemaKind,
242 ) -> Result<(), GraphError> {
243 let mut matched_count = 0;
244 let mut attributes_not_in_schema = Vec::new();
245
246 for (key, value) in attributes {
247 match self.0.get(key) {
248 Some(schema) => {
249 let data_type = DataType::from(value);
250
251 if !schema.data_type.evaluate(&data_type) {
252 return Err(GraphError::SchemaError(kind.error_message_expected(
253 key,
254 &data_type,
255 &schema.data_type,
256 )));
257 }
258
259 matched_count += 1;
260 }
261 None => {
262 attributes_not_in_schema.push(key.to_string());
263 }
264 }
265 }
266
267 if matched_count < self.0.len() {
268 for (key, schema) in &self.0 {
269 if !attributes.contains_key(key) && !matches!(schema.data_type, DataType::Option(_))
270 {
271 return Err(GraphError::SchemaError(
272 kind.error_message(key, &schema.data_type),
273 ));
274 }
275 }
276 }
277
278 if !attributes_not_in_schema.is_empty() {
279 return Err(GraphError::SchemaError(
280 kind.error_message_too_many(&attributes_not_in_schema),
281 ));
282 }
283
284 Ok(())
285 }
286
287 fn update(&mut self, attributes: &Attributes, empty: bool) {
288 for (attribute, data_type) in &mut self.0 {
289 if !attributes.contains_key(attribute) {
290 data_type.data_type = data_type.data_type.merge(&DataType::Null);
291 }
292 }
293
294 for (attribute, value) in attributes {
295 let data_type = DataType::from(value);
296 let attribute_type = AttributeType::infer(&data_type);
297
298 let mut attribute_data_type = AttributeDataType::new(data_type, attribute_type)
299 .expect("AttributeType was inferred from DataType.");
300
301 match self.0.entry(attribute.clone()) {
302 Entry::Occupied(entry) => {
303 entry.into_mut().merge(&attribute_data_type);
304 }
305 Entry::Vacant(entry) => {
306 if !empty {
307 attribute_data_type.data_type =
308 attribute_data_type.data_type.merge(&DataType::Null);
309 }
310
311 entry.insert(attribute_data_type);
312 }
313 }
314 }
315 }
316
317 #[must_use]
318 pub fn infer(attributes: Vec<&Attributes>) -> Self {
319 let mut schema = Self::default();
320
321 let mut empty = true;
322
323 for attributes in attributes {
324 schema.update(attributes, empty);
325
326 empty = false;
327 }
328
329 schema
330 }
331}
332
333#[derive(Debug, Clone, PartialEq, Default)]
334#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
335pub struct GroupSchema {
336 nodes: AttributeSchema,
337 edges: AttributeSchema,
338}
339
340impl GroupSchema {
341 #[must_use]
342 pub const fn new(nodes: AttributeSchema, edges: AttributeSchema) -> Self {
343 Self { nodes, edges }
344 }
345
346 #[must_use]
347 pub fn nodes(&self) -> &AttributeSchemaMapping {
348 &self.nodes
349 }
350
351 #[must_use]
352 pub fn edges(&self) -> &AttributeSchemaMapping {
353 &self.edges
354 }
355
356 pub fn validate_node<'a>(
357 &self,
358 index: &'a NodeIndex,
359 attributes: &'a Attributes,
360 ) -> Result<(), GraphError> {
361 self.nodes
362 .validate(attributes, &AttributeSchemaKind::Node(index))
363 }
364
365 pub fn validate_edge<'a>(
366 &self,
367 index: &'a EdgeIndex,
368 attributes: &'a Attributes,
369 ) -> Result<(), GraphError> {
370 self.edges
371 .validate(attributes, &AttributeSchemaKind::Edge(index))
372 }
373
374 #[must_use]
375 pub fn infer(nodes: Vec<&Attributes>, edges: Vec<&Attributes>) -> Self {
376 Self {
377 nodes: AttributeSchema::infer(nodes),
378 edges: AttributeSchema::infer(edges),
379 }
380 }
381
382 pub(crate) fn update_node(&mut self, attributes: &Attributes, empty: bool) {
383 self.nodes.update(attributes, empty);
384 }
385
386 pub(crate) fn update_edge(&mut self, attributes: &Attributes, empty: bool) {
387 self.edges.update(attributes, empty);
388 }
389}
390
391#[derive(Debug, Clone, PartialEq, Eq, Default)]
392#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
393pub enum SchemaType {
394 #[default]
395 Inferred,
396 Provided,
397}
398
399#[derive(Debug, Clone, PartialEq, Default)]
400#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
401pub struct Schema {
402 groups: HashMap<Group, GroupSchema>,
403 ungrouped: GroupSchema,
404 r#type: SchemaType,
405}
406
407impl Schema {
408 #[must_use]
409 pub const fn new_inferred(groups: HashMap<Group, GroupSchema>, ungrouped: GroupSchema) -> Self {
410 Self {
411 groups,
412 ungrouped,
413 r#type: SchemaType::Inferred,
414 }
415 }
416
417 #[must_use]
418 pub const fn new_provided(groups: HashMap<Group, GroupSchema>, ungrouped: GroupSchema) -> Self {
419 Self {
420 groups,
421 ungrouped,
422 r#type: SchemaType::Provided,
423 }
424 }
425
426 #[must_use]
427 pub fn infer(graphrecord: &GraphRecord) -> Self {
428 let mut group_mapping: GrHashMap<_, _> = graphrecord
429 .groups()
430 .map(|group| (group, (Vec::new(), Vec::new())))
431 .collect();
432
433 let mut ungrouped = (Vec::new(), Vec::new());
434
435 for node_index in graphrecord.node_indices() {
436 #[expect(clippy::missing_panics_doc, reason = "infallible")]
437 let mut groups_of_node = graphrecord
438 .groups_of_node(node_index)
439 .expect("Node must exist.")
440 .peekable();
441
442 if groups_of_node.peek().is_none() {
443 ungrouped.0.push(node_index);
444 continue;
445 }
446
447 for group in groups_of_node {
448 #[expect(clippy::missing_panics_doc, reason = "infallible")]
449 let group_nodes = &mut group_mapping.get_mut(&group).expect("Group must exist.").0;
450
451 group_nodes.push(node_index);
452 }
453 }
454
455 for edge_index in graphrecord.edge_indices() {
456 #[expect(clippy::missing_panics_doc, reason = "infallible")]
457 let mut groups_of_edge = graphrecord
458 .groups_of_edge(edge_index)
459 .expect("Edge must exist.")
460 .peekable();
461
462 if groups_of_edge.peek().is_none() {
463 ungrouped.1.push(edge_index);
464 continue;
465 }
466
467 for group in groups_of_edge {
468 #[expect(clippy::missing_panics_doc, reason = "infallible")]
469 let group_edges = &mut group_mapping.get_mut(&group).expect("Group must exist.").1;
470
471 group_edges.push(edge_index);
472 }
473 }
474
475 let group_schemas =
476 group_mapping
477 .into_iter()
478 .map(|(group, (nodes_in_group, edges_in_group))| {
479 #[expect(clippy::missing_panics_doc, reason = "infallible")]
480 let node_attributes: Vec<_> = nodes_in_group
481 .into_iter()
482 .map(|node| graphrecord.node_attributes(node).expect("Node must exist."))
483 .collect();
484 #[expect(clippy::missing_panics_doc, reason = "infallible")]
485 let edge_attributes: Vec<_> = edges_in_group
486 .into_iter()
487 .map(|edge| graphrecord.edge_attributes(edge).expect("Edge must exist."))
488 .collect();
489
490 let schema = GroupSchema::infer(node_attributes, edge_attributes);
491
492 (group.clone(), schema)
493 });
494
495 #[expect(clippy::missing_panics_doc, reason = "infallible")]
496 let ungrouped_schema = GroupSchema::infer(
497 ungrouped
498 .0
499 .into_iter()
500 .map(|node| graphrecord.node_attributes(node).expect("Node must exist."))
501 .collect::<Vec<_>>(),
502 ungrouped
503 .1
504 .into_iter()
505 .map(|edge| graphrecord.edge_attributes(edge).expect("Edge must exist."))
506 .collect::<Vec<_>>(),
507 );
508
509 Self {
510 groups: group_schemas.collect(),
511 ungrouped: ungrouped_schema,
512 r#type: SchemaType::Inferred,
513 }
514 }
515
516 #[must_use]
517 pub const fn groups(&self) -> &HashMap<Group, GroupSchema> {
518 &self.groups
519 }
520
521 pub fn group(&self, group: &Group) -> Result<&GroupSchema, GraphError> {
522 self.groups
523 .get(group)
524 .ok_or_else(|| GraphError::SchemaError(format!("Group {group} not found in schema.")))
525 }
526
527 #[must_use]
528 pub const fn ungrouped(&self) -> &GroupSchema {
529 &self.ungrouped
530 }
531
532 #[must_use]
533 pub const fn schema_type(&self) -> &SchemaType {
534 &self.r#type
535 }
536
537 pub fn validate_node<'a>(
538 &self,
539 index: &'a NodeIndex,
540 attributes: &'a Attributes,
541 group: Option<&'a Group>,
542 ) -> Result<(), GraphError> {
543 match group {
544 Some(group) => {
545 let schema = self.groups.get(group).ok_or_else(|| {
546 GraphError::SchemaError(format!("Group {group} not found in schema."))
547 })?;
548
549 schema.validate_node(index, attributes)
550 }
551 None => self.ungrouped.validate_node(index, attributes),
552 }
553 }
554
555 pub fn validate_edge<'a>(
556 &self,
557 index: &'a EdgeIndex,
558 attributes: &'a Attributes,
559 group: Option<&'a Group>,
560 ) -> Result<(), GraphError> {
561 match group {
562 Some(group) => {
563 let schema = self.groups.get(group).ok_or_else(|| {
564 GraphError::SchemaError(format!("Group {group} not found in schema."))
565 })?;
566
567 schema.validate_edge(index, attributes)
568 }
569 None => self.ungrouped.validate_edge(index, attributes),
570 }
571 }
572
573 pub(crate) fn update_node(
574 &mut self,
575 attributes: &Attributes,
576 group: Option<&Group>,
577 empty: bool,
578 ) {
579 match group {
580 Some(group) => {
581 self.groups
582 .entry(group.clone())
583 .or_default()
584 .update_node(attributes, empty);
585 }
586 None => self.ungrouped.update_node(attributes, empty),
587 }
588 }
589
590 pub(crate) fn update_edge(
591 &mut self,
592 attributes: &Attributes,
593 group: Option<&Group>,
594 empty: bool,
595 ) {
596 match group {
597 Some(group) => {
598 self.groups
599 .entry(group.clone())
600 .or_default()
601 .update_edge(attributes, empty);
602 }
603 None => self.ungrouped.update_edge(attributes, empty),
604 }
605 }
606
607 pub fn set_node_attribute(
608 &mut self,
609 attribute: &GraphRecordAttribute,
610 data_type: DataType,
611 attribute_type: AttributeType,
612 group: Option<&Group>,
613 ) -> Result<(), GraphError> {
614 let attribute_data_type = AttributeDataType::new(data_type, attribute_type)?;
615
616 match group {
617 Some(group) => {
618 let group_schema = self.groups.entry(group.clone()).or_default();
619 group_schema
620 .nodes
621 .0
622 .insert(attribute.clone(), attribute_data_type);
623 }
624 None => {
625 self.ungrouped
626 .nodes
627 .0
628 .insert(attribute.clone(), attribute_data_type);
629 }
630 }
631
632 Ok(())
633 }
634
635 pub fn set_edge_attribute(
636 &mut self,
637 attribute: &GraphRecordAttribute,
638 data_type: DataType,
639 attribute_type: AttributeType,
640 group: Option<&Group>,
641 ) -> Result<(), GraphError> {
642 let attribute_data_type = AttributeDataType::new(data_type, attribute_type)?;
643
644 match group {
645 Some(group) => {
646 let group_schema = self.groups.entry(group.clone()).or_default();
647 group_schema
648 .edges
649 .0
650 .insert(attribute.clone(), attribute_data_type);
651 }
652 None => {
653 self.ungrouped
654 .edges
655 .0
656 .insert(attribute.clone(), attribute_data_type);
657 }
658 }
659
660 Ok(())
661 }
662
663 pub fn update_node_attribute(
664 &mut self,
665 attribute: &GraphRecordAttribute,
666 data_type: DataType,
667 attribute_type: AttributeType,
668 group: Option<&Group>,
669 ) -> Result<(), GraphError> {
670 let attribute_data_type = AttributeDataType::new(data_type, attribute_type)?;
671
672 match group {
673 Some(group) => {
674 let group_schema = self.groups.entry(group.clone()).or_default();
675 group_schema
676 .nodes
677 .0
678 .entry(attribute.clone())
679 .and_modify(|value| value.merge(&attribute_data_type))
680 .or_insert(attribute_data_type);
681 }
682 None => {
683 self.ungrouped
684 .nodes
685 .0
686 .entry(attribute.clone())
687 .and_modify(|value| value.merge(&attribute_data_type))
688 .or_insert(attribute_data_type);
689 }
690 }
691
692 Ok(())
693 }
694
695 pub fn update_edge_attribute(
696 &mut self,
697 attribute: &GraphRecordAttribute,
698 data_type: DataType,
699 attribute_type: AttributeType,
700 group: Option<&Group>,
701 ) -> Result<(), GraphError> {
702 let attribute_data_type = AttributeDataType::new(data_type, attribute_type)?;
703
704 match group {
705 Some(group) => {
706 let group_schema = self.groups.entry(group.clone()).or_default();
707 group_schema
708 .edges
709 .0
710 .entry(attribute.clone())
711 .and_modify(|value| value.merge(&attribute_data_type))
712 .or_insert(attribute_data_type);
713 }
714 None => {
715 self.ungrouped
716 .edges
717 .0
718 .entry(attribute.clone())
719 .and_modify(|value| value.merge(&attribute_data_type))
720 .or_insert(attribute_data_type);
721 }
722 }
723
724 Ok(())
725 }
726
727 pub fn remove_node_attribute(
728 &mut self,
729 attribute: &GraphRecordAttribute,
730 group: Option<&Group>,
731 ) {
732 match group {
733 Some(group) => {
734 if let Some(group_schema) = self.groups.get_mut(group) {
735 group_schema.nodes.0.remove(attribute);
736 }
737 }
738 None => {
739 self.ungrouped.nodes.0.remove(attribute);
740 }
741 }
742 }
743
744 pub fn remove_edge_attribute(
745 &mut self,
746 attribute: &GraphRecordAttribute,
747 group: Option<&Group>,
748 ) {
749 match group {
750 Some(group) => {
751 if let Some(group_schema) = self.groups.get_mut(group) {
752 group_schema.edges.0.remove(attribute);
753 }
754 }
755 None => {
756 self.ungrouped.edges.0.remove(attribute);
757 }
758 }
759 }
760
761 pub fn add_group(&mut self, group: Group, schema: GroupSchema) -> Result<(), GraphError> {
762 if self.groups.contains_key(&group) {
763 return Err(GraphError::SchemaError(format!(
764 "Group {group} already exists in schema."
765 )));
766 }
767
768 self.groups.insert(group, schema);
769
770 Ok(())
771 }
772
773 pub fn remove_group(&mut self, group: &Group) {
774 self.groups.remove(group);
775 }
776
777 pub const fn freeze(&mut self) {
778 self.r#type = SchemaType::Provided;
779 }
780
781 pub const fn unfreeze(&mut self) {
782 self.r#type = SchemaType::Inferred;
783 }
784}
785
786#[cfg(test)]
787mod test {
788 use super::{AttributeDataType, GroupSchema};
789 use crate::{
790 GraphRecord,
791 graphrecord::{
792 Attributes, Schema, SchemaType,
793 datatypes::DataType,
794 schema::{AttributeSchema, AttributeSchemaKind, AttributeType},
795 },
796 };
797 use std::collections::HashMap;
798
799 #[test]
800 fn test_attribute_type_infer() {
801 assert_eq!(
802 AttributeType::infer(&DataType::String),
803 AttributeType::Unstructured
804 );
805 assert_eq!(
806 AttributeType::infer(&DataType::Int),
807 AttributeType::Continuous
808 );
809 assert_eq!(
810 AttributeType::infer(&DataType::Float),
811 AttributeType::Continuous
812 );
813 assert_eq!(
814 AttributeType::infer(&DataType::Bool),
815 AttributeType::Categorical
816 );
817 assert_eq!(
818 AttributeType::infer(&DataType::DateTime),
819 AttributeType::Temporal
820 );
821 assert_eq!(
822 AttributeType::infer(&DataType::Duration),
823 AttributeType::Temporal
824 );
825 assert_eq!(
826 AttributeType::infer(&DataType::Null),
827 AttributeType::Unstructured
828 );
829 assert_eq!(
830 AttributeType::infer(&DataType::Any),
831 AttributeType::Unstructured
832 );
833 assert_eq!(
834 AttributeType::infer(&DataType::Union((
835 Box::new(DataType::Int),
836 Box::new(DataType::Float)
837 ))),
838 AttributeType::Continuous
839 );
840 assert_eq!(
841 AttributeType::infer(&DataType::Option(Box::new(DataType::Int))),
842 AttributeType::Continuous
843 );
844 }
845
846 #[test]
847 fn test_attribute_type_merge() {
848 assert_eq!(
849 AttributeType::Categorical.merge(AttributeType::Unstructured),
850 AttributeType::Unstructured
851 );
852 assert_eq!(
853 AttributeType::Unstructured.merge(AttributeType::Categorical),
854 AttributeType::Unstructured
855 );
856
857 assert_eq!(
858 AttributeType::Categorical.merge(AttributeType::Categorical),
859 AttributeType::Categorical
860 );
861 assert_eq!(
862 AttributeType::Categorical.merge(AttributeType::Continuous),
863 AttributeType::Categorical
864 );
865 assert_eq!(
866 AttributeType::Categorical.merge(AttributeType::Temporal),
867 AttributeType::Categorical
868 );
869
870 assert_eq!(
871 AttributeType::Continuous.merge(AttributeType::Categorical),
872 AttributeType::Categorical
873 );
874 assert_eq!(
875 AttributeType::Temporal.merge(AttributeType::Categorical),
876 AttributeType::Categorical
877 );
878
879 assert_eq!(
880 AttributeType::Continuous.merge(AttributeType::Continuous),
881 AttributeType::Continuous
882 );
883
884 assert_eq!(
885 AttributeType::Temporal.merge(AttributeType::Temporal),
886 AttributeType::Temporal
887 );
888
889 assert_eq!(
890 AttributeType::Continuous.merge(AttributeType::Temporal),
891 AttributeType::Unstructured
892 );
893 assert_eq!(
894 AttributeType::Continuous.merge(AttributeType::Unstructured),
895 AttributeType::Unstructured
896 );
897
898 assert_eq!(
899 AttributeType::Temporal.merge(AttributeType::Continuous),
900 AttributeType::Unstructured
901 );
902 assert_eq!(
903 AttributeType::Temporal.merge(AttributeType::Unstructured),
904 AttributeType::Unstructured
905 );
906
907 assert_eq!(
908 AttributeType::Unstructured.merge(AttributeType::Continuous),
909 AttributeType::Unstructured
910 );
911 assert_eq!(
912 AttributeType::Unstructured.merge(AttributeType::Temporal),
913 AttributeType::Unstructured
914 );
915 assert_eq!(
916 AttributeType::Unstructured.merge(AttributeType::Unstructured),
917 AttributeType::Unstructured
918 );
919 }
920
921 #[test]
922 fn test_data_type_merge() {
923 assert_eq!(DataType::Int.merge(&DataType::Int), DataType::Int);
924 assert_eq!(
925 DataType::Int.merge(&DataType::Float),
926 DataType::Union((Box::new(DataType::Int), Box::new(DataType::Float)))
927 );
928 assert_eq!(
929 DataType::Int.merge(&DataType::Null),
930 DataType::Option(Box::new(DataType::Int))
931 );
932 assert_eq!(
933 DataType::Null.merge(&DataType::Int),
934 DataType::Option(Box::new(DataType::Int))
935 );
936 assert_eq!(DataType::Null.merge(&DataType::Null), DataType::Null);
937 assert_eq!(DataType::Int.merge(&DataType::Any), DataType::Any);
938 assert_eq!(DataType::Any.merge(&DataType::Int), DataType::Any);
939 assert_eq!(
940 DataType::Option(Box::new(DataType::Int)).merge(&DataType::String),
941 DataType::Option(Box::new(DataType::Union((
942 Box::new(DataType::Int),
943 Box::new(DataType::String)
944 ))))
945 );
946 assert_eq!(
947 DataType::Int.merge(&DataType::Option(Box::new(DataType::Int))),
948 DataType::Option(Box::new(DataType::Int))
949 );
950 assert_eq!(
951 DataType::Option(Box::new(DataType::Int))
952 .merge(&DataType::Option(Box::new(DataType::String))),
953 DataType::Option(Box::new(DataType::Union((
954 Box::new(DataType::Int),
955 Box::new(DataType::String)
956 ))))
957 );
958 }
959
960 #[test]
961 fn test_attribute_data_type_new() {
962 assert!(AttributeDataType::new(DataType::String, AttributeType::Categorical).is_ok());
963 assert!(AttributeDataType::new(DataType::String, AttributeType::Continuous).is_err());
964 assert!(AttributeDataType::new(DataType::String, AttributeType::Temporal).is_err());
965 assert!(AttributeDataType::new(DataType::String, AttributeType::Unstructured).is_ok());
966
967 assert!(AttributeDataType::new(DataType::Int, AttributeType::Categorical).is_ok());
968 assert!(AttributeDataType::new(DataType::Int, AttributeType::Continuous).is_ok());
969 assert!(AttributeDataType::new(DataType::Int, AttributeType::Temporal).is_err());
970 assert!(AttributeDataType::new(DataType::Int, AttributeType::Unstructured).is_ok());
971
972 assert!(AttributeDataType::new(DataType::Float, AttributeType::Categorical).is_ok());
973 assert!(AttributeDataType::new(DataType::Float, AttributeType::Continuous).is_ok());
974 assert!(AttributeDataType::new(DataType::Float, AttributeType::Temporal).is_err());
975 assert!(AttributeDataType::new(DataType::Float, AttributeType::Unstructured).is_ok());
976
977 assert!(AttributeDataType::new(DataType::Bool, AttributeType::Categorical).is_ok());
978 assert!(AttributeDataType::new(DataType::Bool, AttributeType::Continuous).is_err());
979 assert!(AttributeDataType::new(DataType::Bool, AttributeType::Temporal).is_err());
980 assert!(AttributeDataType::new(DataType::Bool, AttributeType::Unstructured).is_ok());
981
982 assert!(AttributeDataType::new(DataType::DateTime, AttributeType::Categorical).is_ok());
983 assert!(AttributeDataType::new(DataType::DateTime, AttributeType::Continuous).is_err());
984 assert!(AttributeDataType::new(DataType::DateTime, AttributeType::Temporal).is_ok());
985 assert!(AttributeDataType::new(DataType::DateTime, AttributeType::Unstructured).is_ok());
986
987 assert!(AttributeDataType::new(DataType::Duration, AttributeType::Categorical).is_ok());
988 assert!(AttributeDataType::new(DataType::Duration, AttributeType::Continuous).is_err());
989 assert!(AttributeDataType::new(DataType::Duration, AttributeType::Temporal).is_ok());
990 assert!(AttributeDataType::new(DataType::Duration, AttributeType::Unstructured).is_ok());
991
992 assert!(AttributeDataType::new(DataType::Null, AttributeType::Categorical).is_ok());
993 assert!(AttributeDataType::new(DataType::Null, AttributeType::Continuous).is_ok());
994 assert!(AttributeDataType::new(DataType::Null, AttributeType::Temporal).is_ok());
995 assert!(AttributeDataType::new(DataType::Null, AttributeType::Unstructured).is_ok());
996
997 assert!(AttributeDataType::new(DataType::Any, AttributeType::Categorical).is_ok());
998 assert!(AttributeDataType::new(DataType::Any, AttributeType::Continuous).is_err());
999 assert!(AttributeDataType::new(DataType::Any, AttributeType::Temporal).is_err());
1000 assert!(AttributeDataType::new(DataType::Any, AttributeType::Unstructured).is_ok());
1001
1002 assert!(
1003 AttributeDataType::new(
1004 DataType::Option(Box::new(DataType::Int)),
1005 AttributeType::Categorical
1006 )
1007 .is_ok()
1008 );
1009 assert!(
1010 AttributeDataType::new(
1011 DataType::Option(Box::new(DataType::Int)),
1012 AttributeType::Continuous
1013 )
1014 .is_ok()
1015 );
1016 assert!(
1017 AttributeDataType::new(
1018 DataType::Option(Box::new(DataType::Int)),
1019 AttributeType::Temporal
1020 )
1021 .is_err()
1022 );
1023 assert!(
1024 AttributeDataType::new(
1025 DataType::Option(Box::new(DataType::Int)),
1026 AttributeType::Unstructured
1027 )
1028 .is_ok()
1029 );
1030
1031 assert!(
1032 AttributeDataType::new(
1033 DataType::Union((Box::new(DataType::Int), Box::new(DataType::Float))),
1034 AttributeType::Categorical
1035 )
1036 .is_ok()
1037 );
1038 assert!(
1039 AttributeDataType::new(
1040 DataType::Union((Box::new(DataType::Int), Box::new(DataType::Float))),
1041 AttributeType::Continuous
1042 )
1043 .is_ok()
1044 );
1045 assert!(
1046 AttributeDataType::new(
1047 DataType::Union((Box::new(DataType::Int), Box::new(DataType::Float))),
1048 AttributeType::Temporal
1049 )
1050 .is_err()
1051 );
1052 assert!(
1053 AttributeDataType::new(
1054 DataType::Union((Box::new(DataType::Int), Box::new(DataType::Float))),
1055 AttributeType::Unstructured
1056 )
1057 .is_ok()
1058 );
1059 }
1060
1061 #[test]
1062 fn test_attribute_data_type_data_type() {
1063 let attribute_data_type = AttributeDataType::new(DataType::Int, AttributeType::Categorical)
1064 .expect("AttributeType was inferred from DataType.");
1065
1066 assert_eq!(attribute_data_type.data_type(), &DataType::Int);
1067 }
1068
1069 #[test]
1070 fn test_attribute_data_type_attribute_type() {
1071 let attribute_data_type = AttributeDataType::new(DataType::Int, AttributeType::Categorical)
1072 .expect("AttributeType was inferred from DataType.");
1073
1074 assert_eq!(
1075 attribute_data_type.attribute_type(),
1076 &AttributeType::Categorical
1077 );
1078 }
1079
1080 #[test]
1081 fn test_attribute_data_type_merge() {
1082 let mut attribute_data_type =
1083 AttributeDataType::new(DataType::Int, AttributeType::Categorical)
1084 .expect("AttributeType was inferred from DataType.");
1085
1086 attribute_data_type.merge(
1087 &AttributeDataType::new(DataType::Float, AttributeType::Continuous)
1088 .expect("AttributeType was inferred from DataType."),
1089 );
1090
1091 assert_eq!(
1092 attribute_data_type.data_type(),
1093 &DataType::Union((Box::new(DataType::Int), Box::new(DataType::Float)))
1094 );
1095 assert_eq!(
1096 attribute_data_type.attribute_type(),
1097 &AttributeType::Categorical
1098 );
1099 }
1100
1101 #[test]
1102 fn test_attribute_data_type_from_data_type() {
1103 let attribute_data_type: AttributeDataType = DataType::Int.into();
1104
1105 assert_eq!(attribute_data_type.data_type(), &DataType::Int);
1106 assert_eq!(
1107 attribute_data_type.attribute_type(),
1108 &AttributeType::Continuous
1109 );
1110 }
1111
1112 #[test]
1113 fn test_attribute_data_type_from_tuple() {
1114 let attribute_data_type: AttributeDataType =
1115 (DataType::Int, AttributeType::Categorical).into();
1116
1117 assert_eq!(attribute_data_type.data_type(), &DataType::Int);
1118 assert_eq!(
1119 attribute_data_type.attribute_type(),
1120 &AttributeType::Categorical
1121 );
1122 }
1123
1124 #[test]
1125 fn test_attribute_schema_kind_error_message() {
1126 let index = 0;
1127 let key = "key";
1128 let data_type = DataType::Int;
1129
1130 assert_eq!(
1131 AttributeSchemaKind::Node(&(index.into())).error_message(&(key.into()), &data_type),
1132 "Attribute key of type Int not found on node with index 0"
1133 );
1134 assert_eq!(
1135 AttributeSchemaKind::Edge(&(index as u32)).error_message(&(key.into()), &data_type),
1136 "Attribute key of type Int not found on edge with index 0"
1137 );
1138 }
1139
1140 #[test]
1141 fn test_attribute_schema_kind_error_message_expected() {
1142 let index = 0;
1143 let key = "key";
1144 let data_type = DataType::Int;
1145 let expected_data_type = DataType::Float;
1146
1147 assert_eq!(
1148 AttributeSchemaKind::Node(&(index.into())).error_message_expected(
1149 &(key.into()),
1150 &data_type,
1151 &expected_data_type
1152 ),
1153 "Attribute key of node with index 0 is of type Int. Expected Float."
1154 );
1155 assert_eq!(
1156 AttributeSchemaKind::Edge(&(index as u32)).error_message_expected(
1157 &(key.into()),
1158 &data_type,
1159 &expected_data_type
1160 ),
1161 "Attribute key of edge with index 0 is of type Int. Expected Float."
1162 );
1163 }
1164
1165 #[test]
1166 fn test_attribute_schema_kind_error_message_too_many() {
1167 let index = 0;
1168 let attributes = vec!["key1".to_string(), "key2".to_string()];
1169
1170 assert_eq!(
1171 AttributeSchemaKind::Node(&(index.into())).error_message_too_many(&attributes),
1172 "Attributes [key1, key2] of node with index 0 do not exist in schema."
1173 );
1174 assert_eq!(
1175 AttributeSchemaKind::Edge(&(index as u32)).error_message_too_many(&attributes),
1176 "Attributes [key1, key2] of edge with index 0 do not exist in schema."
1177 );
1178 }
1179
1180 #[test]
1181 fn test_attribute_schema_deref() {
1182 let schema = AttributeSchema::new(
1183 vec![
1184 (
1185 "key1".into(),
1186 AttributeDataType::new(DataType::Int, AttributeType::Categorical)
1187 .expect("AttributeType was inferred from DataType."),
1188 ),
1189 (
1190 "key2".into(),
1191 AttributeDataType::new(DataType::Float, AttributeType::Continuous)
1192 .expect("AttributeType was inferred from DataType."),
1193 ),
1194 ]
1195 .into_iter()
1196 .collect(),
1197 );
1198
1199 assert_eq!(
1200 schema.get(&"key1".into()).unwrap().data_type(),
1201 &DataType::Int
1202 );
1203 assert_eq!(
1204 schema.get(&"key2".into()).unwrap().data_type(),
1205 &DataType::Float
1206 );
1207 }
1208
1209 #[test]
1210 fn test_attribute_schema_validate() {
1211 let attribute_schema = AttributeSchema::new(
1212 vec![
1213 (
1214 "key1".into(),
1215 AttributeDataType::new(DataType::Int, AttributeType::Categorical)
1216 .expect("AttributeType was inferred from DataType."),
1217 ),
1218 (
1219 "key2".into(),
1220 AttributeDataType::new(DataType::Float, AttributeType::Continuous)
1221 .expect("AttributeType was inferred from DataType."),
1222 ),
1223 ]
1224 .into_iter()
1225 .collect(),
1226 );
1227
1228 let attributes: Attributes = vec![("key1".into(), 0.into()), ("key2".into(), 0.0.into())]
1229 .into_iter()
1230 .collect();
1231
1232 assert!(
1233 attribute_schema
1234 .validate(&attributes, &AttributeSchemaKind::Node(&0.into()))
1235 .is_ok()
1236 );
1237
1238 let attributes: Attributes = vec![("key1".into(), 0.0.into()), ("key2".into(), 0.into())]
1239 .into_iter()
1240 .collect();
1241
1242 assert!(
1243 attribute_schema
1244 .validate(&attributes, &AttributeSchemaKind::Node(&0.into()))
1245 .is_err_and(|error| { matches!(error, crate::errors::GraphError::SchemaError(_)) })
1246 );
1247
1248 let attributes: Attributes = vec![
1249 ("key1".into(), 0.into()),
1250 ("key2".into(), 0.0.into()),
1251 ("key3".into(), 0.0.into()),
1252 ]
1253 .into_iter()
1254 .collect();
1255
1256 assert!(
1257 attribute_schema
1258 .validate(&attributes, &AttributeSchemaKind::Node(&0.into()))
1259 .is_err_and(|error| { matches!(error, crate::errors::GraphError::SchemaError(_)) })
1260 );
1261 }
1262
1263 #[test]
1264 fn test_attribute_schema_update() {
1265 let mut schema = AttributeSchema::default();
1266 let attributes: Attributes =
1267 vec![("key1".into(), 0.into()), ("key2".into(), "test".into())]
1268 .into_iter()
1269 .collect();
1270
1271 schema.update(&attributes, true);
1272
1273 assert_eq!(schema.0.len(), 2);
1274 assert_eq!(
1275 schema.0.get(&"key1".into()).unwrap().data_type(),
1276 &DataType::Int
1277 );
1278 assert_eq!(
1279 schema.0.get(&"key2".into()).unwrap().data_type(),
1280 &DataType::String
1281 );
1282
1283 let new_attributes: Attributes =
1284 vec![("key1".into(), 0.5.into()), ("key3".into(), true.into())]
1285 .into_iter()
1286 .collect();
1287
1288 schema.update(&new_attributes, false);
1289
1290 assert_eq!(schema.0.len(), 3);
1291 assert_eq!(
1292 schema.0.get(&"key1".into()).unwrap().data_type(),
1293 &DataType::Union((Box::new(DataType::Int), Box::new(DataType::Float)))
1294 );
1295 assert_eq!(
1296 schema.0.get(&"key2".into()).unwrap().data_type(),
1297 &DataType::Option(Box::new(DataType::String))
1298 );
1299 assert_eq!(
1300 schema.0.get(&"key3".into()).unwrap().data_type(),
1301 &DataType::Option(Box::new(DataType::Bool))
1302 );
1303 }
1304
1305 #[test]
1306 fn test_attribute_schema_infer() {
1307 let attributes1: Attributes =
1308 vec![("key1".into(), 0.into()), ("key2".into(), "test".into())]
1309 .into_iter()
1310 .collect();
1311
1312 let attributes2: Attributes = vec![("key1".into(), 1.into()), ("key3".into(), true.into())]
1313 .into_iter()
1314 .collect();
1315
1316 let schema = AttributeSchema::infer(vec![&attributes1, &attributes2]);
1317
1318 assert_eq!(schema.0.len(), 3);
1319 assert_eq!(
1320 schema.0.get(&"key1".into()).unwrap().data_type(),
1321 &DataType::Int
1322 );
1323 assert_eq!(
1324 schema.0.get(&"key2".into()).unwrap().data_type(),
1325 &DataType::Option(Box::new(DataType::String))
1326 );
1327 assert_eq!(
1328 schema.0.get(&"key3".into()).unwrap().data_type(),
1329 &DataType::Option(Box::new(DataType::Bool))
1330 );
1331 }
1332
1333 #[test]
1334 fn test_group_schema_nodes() {
1335 let nodes = AttributeSchema::new(
1336 vec![
1337 (
1338 "key1".into(),
1339 AttributeDataType::new(DataType::Int, AttributeType::Categorical)
1340 .expect("AttributeType was inferred from DataType."),
1341 ),
1342 (
1343 "key2".into(),
1344 AttributeDataType::new(DataType::Float, AttributeType::Continuous)
1345 .expect("AttributeType was inferred from DataType."),
1346 ),
1347 ]
1348 .into_iter()
1349 .collect(),
1350 );
1351
1352 let group_schema = GroupSchema::new(nodes.clone(), AttributeSchema::default());
1353
1354 assert_eq!(group_schema.nodes(), &nodes.0);
1355 }
1356
1357 #[test]
1358 fn test_group_schema_edges() {
1359 let edges = AttributeSchema::new(
1360 vec![
1361 (
1362 "key1".into(),
1363 AttributeDataType::new(DataType::Int, AttributeType::Categorical)
1364 .expect("AttributeType was inferred from DataType."),
1365 ),
1366 (
1367 "key2".into(),
1368 AttributeDataType::new(DataType::Float, AttributeType::Continuous)
1369 .expect("AttributeType was inferred from DataType."),
1370 ),
1371 ]
1372 .into_iter()
1373 .collect(),
1374 );
1375
1376 let group_schema = GroupSchema::new(AttributeSchema::default(), edges.clone());
1377
1378 assert_eq!(group_schema.edges(), &edges.0);
1379 }
1380
1381 #[test]
1382 fn test_group_schema_validate_node() {
1383 let nodes = AttributeSchema::new(
1384 vec![
1385 (
1386 "key1".into(),
1387 AttributeDataType::new(DataType::Int, AttributeType::Categorical)
1388 .expect("AttributeType was inferred from DataType."),
1389 ),
1390 (
1391 "key2".into(),
1392 AttributeDataType::new(DataType::Float, AttributeType::Continuous)
1393 .expect("AttributeType was inferred from DataType."),
1394 ),
1395 ]
1396 .into_iter()
1397 .collect(),
1398 );
1399
1400 let group_schema = GroupSchema::new(nodes, AttributeSchema::default());
1401
1402 let attributes: Attributes = vec![("key1".into(), 0.into()), ("key2".into(), 0.0.into())]
1403 .into_iter()
1404 .collect();
1405
1406 assert!(group_schema.validate_node(&0.into(), &attributes).is_ok());
1407
1408 let attributes: Attributes = vec![("key1".into(), 0.0.into()), ("key2".into(), 0.into())]
1409 .into_iter()
1410 .collect();
1411
1412 assert!(
1413 group_schema
1414 .validate_node(&0.into(), &attributes)
1415 .is_err_and(|error| { matches!(error, crate::errors::GraphError::SchemaError(_)) })
1416 );
1417 }
1418
1419 #[test]
1420 fn test_group_schema_validate_edge() {
1421 let edges = AttributeSchema::new(
1422 vec![
1423 (
1424 "key1".into(),
1425 AttributeDataType::new(DataType::Int, AttributeType::Categorical)
1426 .expect("AttributeType was inferred from DataType."),
1427 ),
1428 (
1429 "key2".into(),
1430 AttributeDataType::new(DataType::Float, AttributeType::Continuous)
1431 .expect("AttributeType was inferred from DataType."),
1432 ),
1433 ]
1434 .into_iter()
1435 .collect(),
1436 );
1437
1438 let group_schema = GroupSchema::new(AttributeSchema::default(), edges);
1439
1440 let attributes: Attributes = vec![("key1".into(), 0.into()), ("key2".into(), 0.0.into())]
1441 .into_iter()
1442 .collect();
1443
1444 assert!(group_schema.validate_edge(&0, &attributes).is_ok());
1445
1446 let attributes: Attributes = vec![("key1".into(), 0.0.into()), ("key2".into(), 0.into())]
1447 .into_iter()
1448 .collect();
1449
1450 assert!(
1451 group_schema
1452 .validate_edge(&0, &attributes)
1453 .is_err_and(|error| { matches!(error, crate::errors::GraphError::SchemaError(_)) })
1454 );
1455 }
1456
1457 #[test]
1458 fn test_group_schema_infer() {
1459 let node_attributes1: Attributes =
1460 vec![("key1".into(), 0.into()), ("key2".into(), "test".into())]
1461 .into_iter()
1462 .collect();
1463
1464 let node_attributes2: Attributes =
1465 vec![("key1".into(), 1.into()), ("key3".into(), true.into())]
1466 .into_iter()
1467 .collect();
1468
1469 let edge_attributes: Attributes =
1470 vec![("key4".into(), 0.5.into()), ("key5".into(), "edge".into())]
1471 .into_iter()
1472 .collect();
1473
1474 let group_schema = GroupSchema::infer(
1475 vec![&node_attributes1, &node_attributes2],
1476 vec![&edge_attributes],
1477 );
1478
1479 assert_eq!(group_schema.nodes().len(), 3);
1480 assert_eq!(group_schema.edges().len(), 2);
1481
1482 assert_eq!(
1483 group_schema
1484 .nodes()
1485 .get(&"key1".into())
1486 .unwrap()
1487 .data_type(),
1488 &DataType::Int
1489 );
1490 assert_eq!(
1491 group_schema
1492 .nodes()
1493 .get(&"key2".into())
1494 .unwrap()
1495 .data_type(),
1496 &DataType::Option(Box::new(DataType::String))
1497 );
1498 assert_eq!(
1499 group_schema
1500 .nodes()
1501 .get(&"key3".into())
1502 .unwrap()
1503 .data_type(),
1504 &DataType::Option(Box::new(DataType::Bool))
1505 );
1506
1507 assert_eq!(
1508 group_schema
1509 .edges()
1510 .get(&"key4".into())
1511 .unwrap()
1512 .data_type(),
1513 &DataType::Float
1514 );
1515 assert_eq!(
1516 group_schema
1517 .edges()
1518 .get(&"key5".into())
1519 .unwrap()
1520 .data_type(),
1521 &DataType::String
1522 );
1523 }
1524
1525 #[test]
1526 fn test_group_schema_update_node() {
1527 let mut group_schema = GroupSchema::default();
1528 let attributes = Attributes::from([("key1".into(), 0.into()), ("key2".into(), 0.0.into())]);
1529
1530 group_schema.update_node(&attributes, true);
1531
1532 assert_eq!(group_schema.nodes().len(), 2);
1533 assert_eq!(
1534 group_schema
1535 .nodes()
1536 .get(&"key1".into())
1537 .unwrap()
1538 .data_type(),
1539 &DataType::Int
1540 );
1541 assert_eq!(
1542 group_schema
1543 .nodes()
1544 .get(&"key2".into())
1545 .unwrap()
1546 .data_type(),
1547 &DataType::Float
1548 );
1549 }
1550
1551 #[test]
1552 fn test_group_schema_update_edge() {
1553 let mut group_schema = GroupSchema::default();
1554 let attributes =
1555 Attributes::from([("key3".into(), true.into()), ("key4".into(), "test".into())]);
1556
1557 group_schema.update_edge(&attributes, true);
1558
1559 assert_eq!(group_schema.edges().len(), 2);
1560 assert_eq!(
1561 group_schema
1562 .edges()
1563 .get(&"key3".into())
1564 .unwrap()
1565 .data_type(),
1566 &DataType::Bool
1567 );
1568 assert_eq!(
1569 group_schema
1570 .edges()
1571 .get(&"key4".into())
1572 .unwrap()
1573 .data_type(),
1574 &DataType::String
1575 );
1576 }
1577
1578 #[test]
1579 fn test_schema_infer() {
1580 let mut graphrecord = GraphRecord::new();
1581 graphrecord
1582 .add_node(0.into(), Attributes::from([("key1".into(), 0.into())]))
1583 .unwrap();
1584 graphrecord
1585 .add_node(1.into(), Attributes::from([("key2".into(), 0.0.into())]))
1586 .unwrap();
1587 graphrecord
1588 .add_edge(
1589 0.into(),
1590 1.into(),
1591 Attributes::from([("key3".into(), true.into())]),
1592 )
1593 .unwrap();
1594
1595 let schema = Schema::infer(&graphrecord);
1596
1597 assert_eq!(schema.ungrouped().nodes().len(), 2);
1598 assert_eq!(schema.ungrouped().edges().len(), 1);
1599
1600 graphrecord
1601 .add_group("test".into(), Some(vec![0.into(), 1.into()]), Some(vec![0]))
1602 .unwrap();
1603
1604 let schema = Schema::infer(&graphrecord);
1605
1606 assert_eq!(schema.groups().len(), 1);
1607 assert_eq!(schema.group(&"test".into()).unwrap().nodes().len(), 2);
1608 assert_eq!(schema.group(&"test".into()).unwrap().edges().len(), 1);
1609 }
1610
1611 #[test]
1612 fn test_schema_groups() {
1613 let schema = Schema::new_inferred(
1614 vec![("group1".into(), GroupSchema::default())]
1615 .into_iter()
1616 .collect(),
1617 GroupSchema::default(),
1618 );
1619 assert_eq!(schema.groups().len(), 1);
1620 assert!(schema.groups().contains_key(&"group1".into()));
1621 }
1622
1623 #[test]
1624 fn test_schema_group() {
1625 let schema = Schema::new_inferred(
1626 vec![("group1".into(), GroupSchema::default())]
1627 .into_iter()
1628 .collect(),
1629 GroupSchema::default(),
1630 );
1631 assert!(schema.group(&"group1".into()).is_ok());
1632 assert!(schema.group(&"non_existent".into()).is_err());
1633 }
1634
1635 #[test]
1636 fn test_schema_default() {
1637 let default_schema = GroupSchema::default();
1638 let schema = Schema::new_inferred(HashMap::new(), default_schema.clone());
1639 assert_eq!(schema.ungrouped(), &default_schema);
1640 }
1641
1642 #[test]
1643 fn test_schema_schema_type() {
1644 let schema = Schema::new_inferred(HashMap::new(), GroupSchema::default());
1645 assert_eq!(schema.schema_type(), &SchemaType::Inferred);
1646 }
1647
1648 #[test]
1649 fn test_schema_validate_node() {
1650 let mut schema = Schema::new_inferred(
1651 HashMap::new(),
1652 GroupSchema::new(AttributeSchema::default(), AttributeSchema::default()),
1653 );
1654 schema
1655 .set_node_attribute(
1656 &"key1".into(),
1657 DataType::Int,
1658 AttributeType::Continuous,
1659 None,
1660 )
1661 .unwrap();
1662
1663 let attributes = Attributes::from([("key1".into(), 0.into())]);
1664 assert!(schema.validate_node(&0.into(), &attributes, None).is_ok());
1665
1666 let invalid_attributes = Attributes::from([("key1".into(), "invalid".into())]);
1667 assert!(
1668 schema
1669 .validate_node(&0.into(), &invalid_attributes, None)
1670 .is_err()
1671 );
1672 }
1673
1674 #[test]
1675 fn test_schema_validate_edge() {
1676 let mut schema = Schema::new_inferred(
1677 HashMap::new(),
1678 GroupSchema::new(AttributeSchema::default(), AttributeSchema::default()),
1679 );
1680 schema
1681 .set_edge_attribute(
1682 &"key1".into(),
1683 DataType::Bool,
1684 AttributeType::Categorical,
1685 None,
1686 )
1687 .unwrap();
1688
1689 let attributes = Attributes::from([("key1".into(), true.into())]);
1690 assert!(schema.validate_edge(&0, &attributes, None).is_ok());
1691
1692 let invalid_attributes = Attributes::from([("key1".into(), 0.into())]);
1693 assert!(schema.validate_edge(&0, &invalid_attributes, None).is_err());
1694 }
1695
1696 #[test]
1697 fn test_schema_update_node() {
1698 let mut schema = Schema::new_inferred(
1699 HashMap::new(),
1700 GroupSchema::new(AttributeSchema::default(), AttributeSchema::default()),
1701 );
1702 let attributes = Attributes::from([("key1".into(), 0.into()), ("key2".into(), 0.0.into())]);
1703
1704 schema.update_node(&attributes, None, true);
1705
1706 assert_eq!(schema.ungrouped().nodes().len(), 2);
1707 assert_eq!(
1708 schema
1709 .ungrouped()
1710 .nodes()
1711 .get(&"key1".into())
1712 .unwrap()
1713 .data_type(),
1714 &DataType::Int
1715 );
1716 assert_eq!(
1717 schema
1718 .ungrouped()
1719 .nodes()
1720 .get(&"key2".into())
1721 .unwrap()
1722 .data_type(),
1723 &DataType::Float
1724 );
1725 }
1726
1727 #[test]
1728 fn test_schema_update_edge() {
1729 let mut schema = Schema::new_inferred(
1730 HashMap::new(),
1731 GroupSchema::new(AttributeSchema::default(), AttributeSchema::default()),
1732 );
1733 let attributes =
1734 Attributes::from([("key3".into(), true.into()), ("key4".into(), "test".into())]);
1735
1736 schema.update_edge(&attributes, None, true);
1737
1738 assert_eq!(schema.ungrouped().edges().len(), 2);
1739 assert_eq!(
1740 schema
1741 .ungrouped()
1742 .edges()
1743 .get(&"key3".into())
1744 .unwrap()
1745 .data_type(),
1746 &DataType::Bool
1747 );
1748 assert_eq!(
1749 schema
1750 .ungrouped()
1751 .edges()
1752 .get(&"key4".into())
1753 .unwrap()
1754 .data_type(),
1755 &DataType::String
1756 );
1757 }
1758
1759 #[test]
1760 fn test_schema_set_node_attribute() {
1761 let mut schema = Schema::new_inferred(HashMap::new(), GroupSchema::default());
1762 assert!(
1763 schema
1764 .set_node_attribute(
1765 &"key1".into(),
1766 DataType::Int,
1767 AttributeType::Continuous,
1768 None
1769 )
1770 .is_ok()
1771 );
1772 assert_eq!(
1773 schema
1774 .ungrouped()
1775 .nodes()
1776 .get(&"key1".into())
1777 .unwrap()
1778 .data_type(),
1779 &DataType::Int
1780 );
1781 assert!(
1782 schema
1783 .set_node_attribute(
1784 &"key1".into(),
1785 DataType::Float,
1786 AttributeType::Continuous,
1787 None
1788 )
1789 .is_ok()
1790 );
1791 assert_eq!(
1792 schema
1793 .ungrouped()
1794 .nodes()
1795 .get(&"key1".into())
1796 .unwrap()
1797 .data_type(),
1798 &DataType::Float
1799 );
1800
1801 assert!(
1802 schema
1803 .set_node_attribute(
1804 &"key1".into(),
1805 DataType::Float,
1806 AttributeType::Continuous,
1807 Some(&"group1".into())
1808 )
1809 .is_ok()
1810 );
1811 assert_eq!(
1812 schema
1813 .group(&"group1".into())
1814 .unwrap()
1815 .nodes()
1816 .get(&"key1".into())
1817 .unwrap()
1818 .data_type(),
1819 &DataType::Float
1820 );
1821 }
1822
1823 #[test]
1824 fn test_schema_set_edge_attribute() {
1825 let mut schema = Schema::new_inferred(HashMap::new(), GroupSchema::default());
1826 assert!(
1827 schema
1828 .set_edge_attribute(
1829 &"key1".into(),
1830 DataType::Bool,
1831 AttributeType::Categorical,
1832 None
1833 )
1834 .is_ok()
1835 );
1836 assert_eq!(
1837 schema
1838 .ungrouped()
1839 .edges()
1840 .get(&"key1".into())
1841 .unwrap()
1842 .data_type(),
1843 &DataType::Bool
1844 );
1845 assert!(
1846 schema
1847 .set_edge_attribute(
1848 &"key1".into(),
1849 DataType::Float,
1850 AttributeType::Continuous,
1851 None
1852 )
1853 .is_ok()
1854 );
1855 assert_eq!(
1856 schema
1857 .ungrouped()
1858 .edges()
1859 .get(&"key1".into())
1860 .unwrap()
1861 .data_type(),
1862 &DataType::Float
1863 );
1864
1865 assert!(
1866 schema
1867 .set_edge_attribute(
1868 &"key1".into(),
1869 DataType::Float,
1870 AttributeType::Continuous,
1871 Some(&"group1".into())
1872 )
1873 .is_ok()
1874 );
1875 assert_eq!(
1876 schema
1877 .group(&"group1".into())
1878 .unwrap()
1879 .edges()
1880 .get(&"key1".into())
1881 .unwrap()
1882 .data_type(),
1883 &DataType::Float
1884 );
1885 }
1886
1887 #[test]
1888 fn test_schema_update_node_attribute() {
1889 let mut schema = Schema::new_inferred(HashMap::new(), GroupSchema::default());
1890 schema
1891 .set_node_attribute(
1892 &"key1".into(),
1893 DataType::Int,
1894 AttributeType::Continuous,
1895 None,
1896 )
1897 .unwrap();
1898 assert!(
1899 schema
1900 .update_node_attribute(
1901 &"key1".into(),
1902 DataType::Float,
1903 AttributeType::Continuous,
1904 None
1905 )
1906 .is_ok()
1907 );
1908 assert_eq!(
1909 schema
1910 .ungrouped()
1911 .nodes()
1912 .get(&"key1".into())
1913 .unwrap()
1914 .data_type(),
1915 &DataType::Union((Box::new(DataType::Int), Box::new(DataType::Float)))
1916 );
1917
1918 schema
1919 .set_node_attribute(
1920 &"key1".into(),
1921 DataType::Int,
1922 AttributeType::Continuous,
1923 Some(&"group1".into()),
1924 )
1925 .unwrap();
1926 assert!(
1927 schema
1928 .update_node_attribute(
1929 &"key1".into(),
1930 DataType::Float,
1931 AttributeType::Continuous,
1932 Some(&"group1".into())
1933 )
1934 .is_ok()
1935 );
1936 assert_eq!(
1937 schema
1938 .group(&"group1".into())
1939 .unwrap()
1940 .nodes()
1941 .get(&"key1".into())
1942 .unwrap()
1943 .data_type(),
1944 &DataType::Union((Box::new(DataType::Int), Box::new(DataType::Float)))
1945 );
1946 }
1947
1948 #[test]
1949 fn test_schema_update_edge_attribute() {
1950 let mut schema = Schema::new_inferred(HashMap::new(), GroupSchema::default());
1951 schema
1952 .set_edge_attribute(
1953 &"key1".into(),
1954 DataType::Bool,
1955 AttributeType::Categorical,
1956 None,
1957 )
1958 .unwrap();
1959 assert!(
1960 schema
1961 .update_edge_attribute(
1962 &"key1".into(),
1963 DataType::String,
1964 AttributeType::Unstructured,
1965 None
1966 )
1967 .is_ok()
1968 );
1969 assert_eq!(
1970 schema
1971 .ungrouped()
1972 .edges()
1973 .get(&"key1".into())
1974 .unwrap()
1975 .data_type(),
1976 &DataType::Union((Box::new(DataType::Bool), Box::new(DataType::String)))
1977 );
1978
1979 schema
1980 .set_edge_attribute(
1981 &"key1".into(),
1982 DataType::Bool,
1983 AttributeType::Categorical,
1984 Some(&"group1".into()),
1985 )
1986 .unwrap();
1987 assert!(
1988 schema
1989 .update_edge_attribute(
1990 &"key1".into(),
1991 DataType::String,
1992 AttributeType::Unstructured,
1993 Some(&"group1".into())
1994 )
1995 .is_ok()
1996 );
1997 assert_eq!(
1998 schema
1999 .group(&"group1".into())
2000 .unwrap()
2001 .edges()
2002 .get(&"key1".into())
2003 .unwrap()
2004 .data_type(),
2005 &DataType::Union((Box::new(DataType::Bool), Box::new(DataType::String)))
2006 );
2007 }
2008
2009 #[test]
2010 fn test_schema_remove_node_attribute() {
2011 let mut schema = Schema::new_inferred(HashMap::new(), GroupSchema::default());
2012 schema
2013 .set_node_attribute(
2014 &"key1".into(),
2015 DataType::Int,
2016 AttributeType::Continuous,
2017 None,
2018 )
2019 .unwrap();
2020 schema.remove_node_attribute(&"key1".into(), None);
2021 assert!(!schema.ungrouped().nodes().contains_key(&"key1".into()));
2022
2023 schema
2024 .set_node_attribute(
2025 &"key1".into(),
2026 DataType::Int,
2027 AttributeType::Continuous,
2028 Some(&"group1".into()),
2029 )
2030 .unwrap();
2031 schema.remove_node_attribute(&"key1".into(), Some(&"group1".into()));
2032 assert!(
2033 !schema
2034 .group(&"group1".into())
2035 .unwrap()
2036 .nodes()
2037 .contains_key(&"key1".into())
2038 );
2039 }
2040
2041 #[test]
2042 fn test_schema_remove_edge_attribute() {
2043 let mut schema = Schema::new_inferred(HashMap::new(), GroupSchema::default());
2044 schema
2045 .set_edge_attribute(
2046 &"key1".into(),
2047 DataType::Bool,
2048 AttributeType::Categorical,
2049 None,
2050 )
2051 .unwrap();
2052 schema.remove_edge_attribute(&"key1".into(), None);
2053 assert!(!schema.ungrouped().edges().contains_key(&"key1".into()));
2054
2055 schema
2056 .set_edge_attribute(
2057 &"key1".into(),
2058 DataType::Bool,
2059 AttributeType::Categorical,
2060 Some(&"group1".into()),
2061 )
2062 .unwrap();
2063 schema.remove_edge_attribute(&"key1".into(), Some(&"group1".into()));
2064 assert!(
2065 !schema
2066 .group(&"group1".into())
2067 .unwrap()
2068 .edges()
2069 .contains_key(&"key1".into())
2070 );
2071 }
2072
2073 #[test]
2074 fn test_schema_add_group() {
2075 let attribute_schema = AttributeSchema::new(
2076 vec![
2077 (
2078 "key1".into(),
2079 AttributeDataType::new(DataType::Int, AttributeType::Categorical)
2080 .expect("AttributeType was inferred from DataType."),
2081 ),
2082 (
2083 "key2".into(),
2084 AttributeDataType::new(DataType::Float, AttributeType::Continuous)
2085 .expect("AttributeType was inferred from DataType."),
2086 ),
2087 ]
2088 .into_iter()
2089 .collect(),
2090 );
2091
2092 let mut schema = Schema::new_inferred(HashMap::new(), GroupSchema::default());
2093 schema
2094 .add_group(
2095 "group1".into(),
2096 GroupSchema::new(attribute_schema.clone(), AttributeSchema::default()),
2097 )
2098 .unwrap();
2099 assert_eq!(
2100 attribute_schema,
2101 schema.group(&"group1".into()).unwrap().nodes
2102 );
2103
2104 assert!(
2105 schema
2106 .add_group("group1".into(), GroupSchema::default())
2107 .is_err_and(|error| { matches!(error, crate::errors::GraphError::SchemaError(_)) })
2108 );
2109 }
2110
2111 #[test]
2112 fn test_schema_remove_group() {
2113 let mut schema = Schema::new_inferred(
2114 vec![("group1".into(), GroupSchema::default())]
2115 .into_iter()
2116 .collect(),
2117 GroupSchema::default(),
2118 );
2119 schema.remove_group(&"group1".into());
2120 assert!(!schema.groups().contains_key(&"group1".into()));
2121 }
2122
2123 #[test]
2124 fn test_schema_freeze_unfreeze() {
2125 let mut schema = Schema::new_inferred(HashMap::new(), GroupSchema::default());
2126 assert_eq!(schema.schema_type(), &SchemaType::Inferred);
2127
2128 schema.freeze();
2129 assert_eq!(schema.schema_type(), &SchemaType::Provided);
2130
2131 schema.unfreeze();
2132 assert_eq!(schema.schema_type(), &SchemaType::Inferred);
2133 }
2134}