gqlite 1.5.0 → 1.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (119) hide show
  1. checksums.yaml +4 -4
  2. data/ext/Cargo.toml +12 -6
  3. data/ext/db-index/Cargo.toml +29 -0
  4. data/ext/db-index/src/hnsw/error.rs +66 -0
  5. data/ext/db-index/src/hnsw/graph_store.rs +272 -0
  6. data/ext/db-index/src/hnsw/id.rs +36 -0
  7. data/ext/db-index/src/hnsw/implementation.rs +1517 -0
  8. data/ext/db-index/src/hnsw/kernels.rs +1228 -0
  9. data/ext/db-index/src/hnsw/metric.rs +244 -0
  10. data/ext/db-index/src/hnsw/scalar.rs +72 -0
  11. data/ext/db-index/src/hnsw/simple_store.rs +140 -0
  12. data/ext/db-index/src/hnsw/store.rs +472 -0
  13. data/ext/db-index/src/hnsw/vector.rs +105 -0
  14. data/ext/db-index/src/hnsw/vectors.rs +568 -0
  15. data/ext/db-index/src/hnsw.rs +42 -0
  16. data/ext/db-index/src/lib.rs +3 -0
  17. data/ext/gqlitedb/Cargo.toml +19 -8
  18. data/ext/gqlitedb/benches/common/pokec.rs +62 -2
  19. data/ext/gqlitedb/benches/pokec_divan.rs +66 -2
  20. data/ext/gqlitedb/benches/pokec_iai.rs +60 -3
  21. data/ext/gqlitedb/release.toml +2 -2
  22. data/ext/gqlitedb/src/aggregators/arithmetic.rs +3 -1
  23. data/ext/gqlitedb/src/aggregators/containers.rs +1 -1
  24. data/ext/gqlitedb/src/aggregators/stats.rs +21 -12
  25. data/ext/gqlitedb/src/capi.rs +20 -24
  26. data/ext/gqlitedb/src/compiler/expression_analyser.rs +28 -20
  27. data/ext/gqlitedb/src/compiler/variables_manager.rs +104 -38
  28. data/ext/gqlitedb/src/compiler.rs +506 -227
  29. data/ext/gqlitedb/src/connection.rs +151 -11
  30. data/ext/gqlitedb/src/consts.rs +1 -2
  31. data/ext/gqlitedb/src/error.rs +123 -61
  32. data/ext/gqlitedb/src/functions/common.rs +64 -0
  33. data/ext/gqlitedb/src/functions/containers.rs +2 -46
  34. data/ext/gqlitedb/src/functions/edge.rs +1 -1
  35. data/ext/gqlitedb/src/functions/math.rs +87 -15
  36. data/ext/gqlitedb/src/functions/node.rs +1 -1
  37. data/ext/gqlitedb/src/functions/path.rs +48 -11
  38. data/ext/gqlitedb/src/functions/scalar.rs +47 -4
  39. data/ext/gqlitedb/src/functions/string.rs +123 -1
  40. data/ext/gqlitedb/src/functions/value.rs +1 -1
  41. data/ext/gqlitedb/src/functions.rs +165 -28
  42. data/ext/gqlitedb/src/graph.rs +1 -9
  43. data/ext/gqlitedb/src/interpreter/evaluators.rs +968 -130
  44. data/ext/gqlitedb/src/interpreter/instructions.rs +39 -2
  45. data/ext/gqlitedb/src/lib.rs +5 -4
  46. data/ext/gqlitedb/src/parser/gql.pest +1 -1
  47. data/ext/gqlitedb/src/planner.rs +329 -0
  48. data/ext/gqlitedb/src/prelude.rs +3 -3
  49. data/ext/gqlitedb/src/store/pgrx.rs +1 -1
  50. data/ext/gqlitedb/src/store/postgres.rs +742 -16
  51. data/ext/gqlitedb/src/store/redb/hnsw_store.rs +702 -0
  52. data/ext/gqlitedb/src/store/redb/index.rs +274 -0
  53. data/ext/gqlitedb/src/store/redb.rs +1268 -113
  54. data/ext/gqlitedb/src/store/sqlbase/sqlmetadata.rs +28 -0
  55. data/ext/gqlitedb/src/store/sqlbase/sqlstore.rs +103 -0
  56. data/ext/gqlitedb/src/store/sqlbase.rs +146 -16
  57. data/ext/gqlitedb/src/store/sqlite.rs +576 -14
  58. data/ext/gqlitedb/src/store/vector_extract.rs +56 -0
  59. data/ext/gqlitedb/src/store.rs +140 -29
  60. data/ext/gqlitedb/src/tests/compiler.rs +207 -10
  61. data/ext/gqlitedb/src/tests/connection/postgres.rs +38 -3
  62. data/ext/gqlitedb/src/tests/connection/redb.rs +23 -0
  63. data/ext/gqlitedb/src/tests/connection/sqlite.rs +31 -0
  64. data/ext/gqlitedb/src/tests/connection.rs +61 -1
  65. data/ext/gqlitedb/src/tests/evaluators.rs +162 -7
  66. data/ext/gqlitedb/src/tests/parser.rs +54 -23
  67. data/ext/gqlitedb/src/tests/planner.rs +511 -0
  68. data/ext/gqlitedb/src/tests/store/postgres.rs +7 -0
  69. data/ext/gqlitedb/src/tests/store/redb.rs +8 -0
  70. data/ext/gqlitedb/src/tests/store/sqlite.rs +8 -0
  71. data/ext/gqlitedb/src/tests/store/vector_index/postgres.rs +182 -0
  72. data/ext/gqlitedb/src/tests/store/vector_index/redb.rs +386 -0
  73. data/ext/gqlitedb/src/tests/store/vector_index/sqlite.rs +166 -0
  74. data/ext/gqlitedb/src/tests/store/vector_index.rs +2313 -0
  75. data/ext/gqlitedb/src/tests/store.rs +78 -14
  76. data/ext/gqlitedb/src/tests/templates/ast.rs +92 -16
  77. data/ext/gqlitedb/src/tests/templates/programs.rs +14 -7
  78. data/ext/gqlitedb/src/tests.rs +15 -9
  79. data/ext/gqlitedb/src/utils.rs +6 -1
  80. data/ext/gqlitedb/src/value/compare.rs +61 -3
  81. data/ext/gqlitedb/src/value.rs +136 -7
  82. data/ext/gqlitedb/templates/sql/postgres/metadata_delete.sql +1 -0
  83. data/ext/gqlitedb/templates/sql/postgres/node_select.sql +1 -1
  84. data/ext/gqlitedb/templates/sql/sqlite/metadata_delete.sql +1 -0
  85. data/ext/gqliterb/src/lib.rs +53 -8
  86. data/ext/gqlparser/Cargo.toml +25 -0
  87. data/ext/gqlparser/README.MD +9 -0
  88. data/ext/gqlparser/benches/pokec_divan.rs +34 -0
  89. data/ext/gqlparser/src/common.rs +69 -0
  90. data/ext/gqlparser/src/gqls/ast.rs +69 -0
  91. data/ext/gqlparser/src/gqls/constraint.rs +79 -0
  92. data/ext/gqlparser/src/gqls/error.rs +22 -0
  93. data/ext/gqlparser/src/gqls/parser.rs +813 -0
  94. data/ext/gqlparser/src/gqls/prelude.rs +8 -0
  95. data/ext/gqlparser/src/gqls/properties.rs +115 -0
  96. data/ext/gqlparser/src/gqls/resolve.rs +207 -0
  97. data/ext/gqlparser/src/gqls.rs +265 -0
  98. data/ext/gqlparser/src/lib.rs +7 -0
  99. data/ext/gqlparser/src/oc/ast.rs +680 -0
  100. data/ext/gqlparser/src/oc/error.rs +172 -0
  101. data/ext/gqlparser/src/oc/lexer.rs +429 -0
  102. data/ext/gqlparser/src/oc/parser/tests.rs +2284 -0
  103. data/ext/gqlparser/src/oc/parser.rs +2005 -0
  104. data/ext/gqlparser/src/oc.rs +33 -0
  105. data/ext/gqlparser/src/prelude.rs +3 -0
  106. data/ext/graphcore/Cargo.toml +1 -0
  107. data/ext/graphcore/src/error.rs +26 -0
  108. data/ext/graphcore/src/graph.rs +177 -51
  109. data/ext/graphcore/src/lib.rs +4 -2
  110. data/ext/graphcore/src/open_cypher.rs +12 -0
  111. data/ext/graphcore/src/table.rs +50 -2
  112. data/ext/graphcore/src/timestamp.rs +127 -104
  113. data/ext/graphcore/src/value/tensor.rs +739 -0
  114. data/ext/graphcore/src/value/value_map.rs +1 -1
  115. data/ext/graphcore/src/value.rs +343 -19
  116. metadata +93 -28
  117. data/ext/gqlitedb/src/parser/ast.rs +0 -604
  118. data/ext/gqlitedb/src/parser/parser_impl.rs +0 -1213
  119. data/ext/gqlitedb/src/parser.rs +0 -4
@@ -0,0 +1,8 @@
1
+ //! Import this prelude for base types
2
+
3
+ pub use crate::gqls::{
4
+ constraint::Constraint,
5
+ properties::{LiteralAlternativeType, LiteralBaseType, Property},
6
+ resolve::resolve_constrained_type,
7
+ };
8
+ pub use graphcore;
@@ -0,0 +1,115 @@
1
+ //! Module with properties definitions
2
+
3
+ use indexmap::IndexMap;
4
+
5
+ use super::Error;
6
+
7
+ /// Literal types
8
+ #[derive(Debug, Clone, PartialEq, Eq)]
9
+ pub enum LiteralBaseType
10
+ {
11
+ /// Boolean value
12
+ Boolean,
13
+ /// i64 value
14
+ Integer,
15
+ /// f64 value
16
+ Float,
17
+ /// String value
18
+ String,
19
+ /// Timestamp value
20
+ TimeStamp,
21
+ }
22
+
23
+ /// This structure allow to map several literal types
24
+ #[derive(Debug, Clone, Copy, PartialEq, Eq)]
25
+ pub struct LiteralAlternativeType(u8);
26
+
27
+ impl LiteralAlternativeType
28
+ {
29
+ /// Check if the alternative contains the given type
30
+ pub fn has_type(&self, ltype: LiteralBaseType) -> bool
31
+ {
32
+ let c: LiteralAlternativeType = ltype.into();
33
+ (self.0 & c.0) != 0
34
+ }
35
+ }
36
+
37
+ impl From<LiteralBaseType> for LiteralAlternativeType
38
+ {
39
+ fn from(value: LiteralBaseType) -> Self
40
+ {
41
+ LiteralAlternativeType(match value
42
+ {
43
+ LiteralBaseType::Boolean => 1,
44
+ LiteralBaseType::Integer => 1 << 1,
45
+ LiteralBaseType::Float => 1 << 2,
46
+ LiteralBaseType::String => 1 << 3,
47
+ LiteralBaseType::TimeStamp => 1 << 4,
48
+ })
49
+ }
50
+ }
51
+
52
+ impl TryFrom<LiteralAlternativeType> for LiteralBaseType
53
+ {
54
+ type Error = Error;
55
+ fn try_from(value: LiteralAlternativeType) -> Result<Self, Self::Error>
56
+ {
57
+ if value.0.count_ones() == 1
58
+ {
59
+ if value.has_type(LiteralBaseType::Boolean)
60
+ {
61
+ Ok(LiteralBaseType::Boolean)
62
+ }
63
+ else if value.has_type(LiteralBaseType::Float)
64
+ {
65
+ Ok(LiteralBaseType::Float)
66
+ }
67
+ else if value.has_type(LiteralBaseType::Integer)
68
+ {
69
+ Ok(LiteralBaseType::Integer)
70
+ }
71
+ else if value.has_type(LiteralBaseType::String)
72
+ {
73
+ Ok(LiteralBaseType::String)
74
+ }
75
+ else if value.has_type(LiteralBaseType::TimeStamp)
76
+ {
77
+ Ok(LiteralBaseType::TimeStamp)
78
+ }
79
+ else
80
+ {
81
+ Err(Error::InvalidType)
82
+ }
83
+ }
84
+ else
85
+ {
86
+ Err(Error::MultiTypeCannotBeConvertedToSingle)
87
+ }
88
+ }
89
+ }
90
+
91
+ /// Type constraint on a property
92
+ #[derive(Debug, Clone, PartialEq, Eq)]
93
+ pub enum Property
94
+ {
95
+ /// Can have any value
96
+ Any,
97
+ /// Is optional, other are assumed required, unless contained in optional
98
+ Optional(Box<Property>),
99
+ /// Literal value (string, float...)
100
+ Literal(LiteralAlternativeType),
101
+ /// A map of property
102
+ Map(IndexMap<String, Property>),
103
+ /// An array of property
104
+ Array(Box<Property>),
105
+ /// A reference to a named constrained type
106
+ Named(String),
107
+ }
108
+
109
+ impl From<LiteralBaseType> for Property
110
+ {
111
+ fn from(value: LiteralBaseType) -> Self
112
+ {
113
+ Property::Literal(value.into())
114
+ }
115
+ }
@@ -0,0 +1,207 @@
1
+ //! Type resolution with cycle detection for named constrained types.
2
+
3
+ use indexmap::IndexMap;
4
+ use std::collections::HashSet;
5
+
6
+ use crate::gqls::{
7
+ Error,
8
+ ast::{ConstrainedTypeDef, TypeRef},
9
+ constraint::Constraint,
10
+ properties::LiteralBaseType,
11
+ };
12
+
13
+ /// Resolves a named constrained type to its flattened `(base type, combined constraint)`,
14
+ /// walking `TypeRef::Named` references and accumulating each link's constraint via `And`.
15
+ /// This mirrors `gqls-macros`'s existing `expand_element` recursion pattern for `<:` parent
16
+ /// chains — but adds the cycle detection that pattern currently lacks.
17
+ pub fn resolve_constrained_type(
18
+ types: &IndexMap<String, ConstrainedTypeDef>,
19
+ name: &str,
20
+ ) -> Result<(LiteralBaseType, Constraint), Error>
21
+ {
22
+ let mut path = Vec::new();
23
+ let mut visiting = HashSet::new();
24
+ resolve_inner(types, name, &mut path, &mut visiting)
25
+ }
26
+
27
+ fn resolve_inner(
28
+ types: &IndexMap<String, ConstrainedTypeDef>,
29
+ name: &str,
30
+ path: &mut Vec<String>,
31
+ visiting: &mut HashSet<String>,
32
+ ) -> Result<(LiteralBaseType, Constraint), Error>
33
+ {
34
+ if !visiting.insert(name.to_string())
35
+ {
36
+ path.push(name.to_string());
37
+ return Err(Error::Cycle(path.clone()));
38
+ }
39
+ path.push(name.to_string());
40
+
41
+ let def = types
42
+ .get(name)
43
+ .ok_or_else(|| Error::UndefinedType(name.to_string()))?;
44
+
45
+ let result = match &def.base
46
+ {
47
+ TypeRef::Base(base) =>
48
+ {
49
+ let constraint = def.constraint.clone().unwrap_or(Constraint::And(vec![]));
50
+ Ok((base.clone(), constraint))
51
+ }
52
+ TypeRef::Named(parent_name) =>
53
+ {
54
+ let (base, parent_constraint) = resolve_inner(types, parent_name, path, visiting)?;
55
+ let combined = match &def.constraint
56
+ {
57
+ Some(c) => Constraint::And(vec![parent_constraint, c.clone()]),
58
+ None => parent_constraint,
59
+ };
60
+ Ok((base, combined))
61
+ }
62
+ };
63
+
64
+ visiting.remove(name);
65
+ path.pop();
66
+ result
67
+ }
68
+
69
+ #[cfg(test)]
70
+ mod tests
71
+ {
72
+ use super::*;
73
+ use graphcore::Value;
74
+
75
+ fn types(pairs: &[(&str, TypeRef, Option<Constraint>)]) -> IndexMap<String, ConstrainedTypeDef>
76
+ {
77
+ pairs
78
+ .iter()
79
+ .map(|(name, base, constraint)| {
80
+ (
81
+ name.to_string(),
82
+ ConstrainedTypeDef {
83
+ base: base.clone(),
84
+ constraint: constraint.clone(),
85
+ },
86
+ )
87
+ })
88
+ .collect()
89
+ }
90
+
91
+ #[test]
92
+ fn resolves_single_level_type()
93
+ {
94
+ let ts = types(&[(
95
+ "FactTier",
96
+ TypeRef::Base(LiteralBaseType::String),
97
+ Some(Constraint::In(vec![
98
+ Value::String("Context".into()),
99
+ Value::String("Critical".into()),
100
+ ])),
101
+ )]);
102
+ let (base, constraint) = resolve_constrained_type(&ts, "FactTier").unwrap();
103
+ assert_eq!(base, LiteralBaseType::String);
104
+ assert_eq!(
105
+ constraint,
106
+ Constraint::In(vec![
107
+ Value::String("Context".into()),
108
+ Value::String("Critical".into())
109
+ ])
110
+ );
111
+ }
112
+
113
+ #[test]
114
+ fn resolves_two_level_chain_combining_constraints()
115
+ {
116
+ let ts = types(&[
117
+ (
118
+ "Bob",
119
+ TypeRef::Base(LiteralBaseType::Integer),
120
+ Some(Constraint::GreaterThan(Value::Integer(10))),
121
+ ),
122
+ (
123
+ "SuperBob",
124
+ TypeRef::Named("Bob".into()),
125
+ Some(Constraint::LessThan(Value::Integer(20))),
126
+ ),
127
+ ]);
128
+ let (base, constraint) = resolve_constrained_type(&ts, "SuperBob").unwrap();
129
+ assert_eq!(base, LiteralBaseType::Integer);
130
+ assert_eq!(
131
+ constraint,
132
+ Constraint::And(vec![
133
+ Constraint::GreaterThan(Value::Integer(10)),
134
+ Constraint::LessThan(Value::Integer(20)),
135
+ ])
136
+ );
137
+ }
138
+
139
+ #[test]
140
+ fn resolves_three_level_chain()
141
+ {
142
+ let ts = types(&[
143
+ (
144
+ "A",
145
+ TypeRef::Base(LiteralBaseType::Integer),
146
+ Some(Constraint::GreaterThan(Value::Integer(0))),
147
+ ),
148
+ (
149
+ "B",
150
+ TypeRef::Named("A".into()),
151
+ Some(Constraint::LessThan(Value::Integer(100))),
152
+ ),
153
+ (
154
+ "C",
155
+ TypeRef::Named("B".into()),
156
+ Some(Constraint::Ne(Value::Integer(50))),
157
+ ),
158
+ ]);
159
+ let (base, constraint) = resolve_constrained_type(&ts, "C").unwrap();
160
+ assert_eq!(base, LiteralBaseType::Integer);
161
+ assert_eq!(
162
+ constraint,
163
+ Constraint::And(vec![
164
+ Constraint::And(vec![
165
+ Constraint::GreaterThan(Value::Integer(0)),
166
+ Constraint::LessThan(Value::Integer(100)),
167
+ ]),
168
+ Constraint::Ne(Value::Integer(50)),
169
+ ])
170
+ );
171
+ }
172
+
173
+ #[test]
174
+ fn direct_self_reference_is_a_cycle_not_a_stack_overflow()
175
+ {
176
+ let ts = types(&[("A", TypeRef::Named("A".into()), None)]);
177
+ let err = resolve_constrained_type(&ts, "A").unwrap_err();
178
+ assert!(matches!(err, Error::Cycle(_)));
179
+ }
180
+
181
+ #[test]
182
+ fn indirect_cycle_is_detected()
183
+ {
184
+ let ts = types(&[
185
+ ("A", TypeRef::Named("B".into()), None),
186
+ ("B", TypeRef::Named("A".into()), None),
187
+ ]);
188
+ let err = resolve_constrained_type(&ts, "A").unwrap_err();
189
+ match err
190
+ {
191
+ Error::Cycle(path) =>
192
+ {
193
+ assert!(path.contains(&"A".to_string()));
194
+ assert!(path.contains(&"B".to_string()));
195
+ }
196
+ other => panic!("expected Cycle, got {other:?}"),
197
+ }
198
+ }
199
+
200
+ #[test]
201
+ fn undefined_type_reference_is_a_clear_error()
202
+ {
203
+ let ts = types(&[("SuperBob", TypeRef::Named("Bob".into()), None)]);
204
+ let err = resolve_constrained_type(&ts, "SuperBob").unwrap_err();
205
+ assert_eq!(err, Error::UndefinedType("Bob".to_string()));
206
+ }
207
+ }
@@ -0,0 +1,265 @@
1
+ //! GQL Schema parser
2
+
3
+ pub mod ast;
4
+ pub mod constraint;
5
+ mod error;
6
+ mod parser;
7
+ pub mod prelude;
8
+ pub mod properties;
9
+ pub mod resolve;
10
+
11
+ pub use error::Error;
12
+
13
+ /// Result type used by this crate
14
+ pub type Result<T, E = error::Error> = std::result::Result<T, E>;
15
+
16
+ use indexmap::IndexMap;
17
+ use nom::Finish;
18
+
19
+ /// Parse a schmea into an AST.
20
+ pub fn parse_schema(input: &str) -> Result<ast::Ast>
21
+ {
22
+ // Parse schema
23
+ let (rem, parse_tree) = parser::parse_schema(input)
24
+ .finish()
25
+ .map_err(|e| Error::Parse(nom_language::error::convert_error(input, e)))?;
26
+
27
+ if !rem.is_empty()
28
+ {
29
+ return Err(Error::IncompleteParsing(rem.to_string()));
30
+ }
31
+
32
+ // Convert to AST
33
+ let mut nodes: Vec<ast::Node> = Default::default();
34
+ let mut edges: Vec<ast::Edge> = Default::default();
35
+ let mut properties_definitions: IndexMap<String, ast::PropertiesDefinition> = Default::default();
36
+ let mut constrained_types: IndexMap<String, ast::ConstrainedTypeDef> = Default::default();
37
+
38
+ for element in parse_tree
39
+ {
40
+ match element
41
+ {
42
+ parser::ParseTreeElement::Property {
43
+ label,
44
+ properties,
45
+ parent,
46
+ } =>
47
+ {
48
+ let parents = parent.map_or_else(Vec::new, |x| vec![x]);
49
+ properties_definitions.insert(
50
+ label,
51
+ ast::PropertiesDefinition {
52
+ parents,
53
+ properties,
54
+ },
55
+ );
56
+ }
57
+ parser::ParseTreeElement::Node { label } =>
58
+ {
59
+ nodes.push(ast::Node { label });
60
+ }
61
+ parser::ParseTreeElement::Edge {
62
+ source,
63
+ label,
64
+ destination,
65
+ } =>
66
+ {
67
+ edges.push(ast::Edge {
68
+ source,
69
+ label,
70
+ destination,
71
+ });
72
+ }
73
+ parser::ParseTreeElement::ConstrainedType { name, def } =>
74
+ {
75
+ constrained_types.insert(name, def);
76
+ }
77
+ }
78
+ }
79
+
80
+ Ok(ast::Ast {
81
+ elements: properties_definitions,
82
+ nodes,
83
+ edges,
84
+ constrained_types,
85
+ })
86
+ }
87
+
88
+ #[cfg(test)]
89
+ mod test
90
+ {
91
+ use crate::gqls::prelude::*;
92
+
93
+ #[test]
94
+ fn parse_simple()
95
+ {
96
+ let ast = super::parse_schema(
97
+ r#"
98
+ // Properties definitions
99
+ Person{
100
+ firstName: STRING, lastName : STRING
101
+ },
102
+ Message {
103
+ creationDate: TIMESTAMP, browserUsed: STRING
104
+ },
105
+ Comment <: Message {},
106
+ Post <: Message {
107
+ imageFile: STRING?
108
+ },
109
+ REPLY_OF {},
110
+ KNOWS {
111
+ creationDate : TIMESTAMP
112
+ },
113
+
114
+ // Nodes
115
+ (Person), (Post), (Comment),
116
+
117
+ // Edges
118
+ (Person)-[KNOWS]->(Person),
119
+ (Person)-[LIKES]->(Message),
120
+ (Message)-[HAS_CREATOR]->(Person),
121
+ (Comment)-[REPLY_OF]->(Message)
122
+ "#,
123
+ )
124
+ .unwrap();
125
+
126
+ // Check nodes
127
+ assert_eq!(ast.nodes.len(), 3);
128
+ assert_eq!(&ast.nodes[0].label, "Person");
129
+ assert_eq!(&ast.nodes[1].label, "Post");
130
+ assert_eq!(&ast.nodes[2].label, "Comment");
131
+
132
+ assert_eq!(ast.edges.len(), 4);
133
+ let e0 = &ast.edges[0];
134
+ assert_eq!(&e0.source, "Person");
135
+ assert_eq!(&e0.label, "KNOWS");
136
+ assert_eq!(&e0.destination, "Person");
137
+ let e1 = &ast.edges[1];
138
+ assert_eq!(&e1.source, "Person");
139
+ assert_eq!(&e1.label, "LIKES");
140
+ assert_eq!(&e1.destination, "Message");
141
+ let e2 = &ast.edges[2];
142
+ assert_eq!(&e2.source, "Message");
143
+ assert_eq!(&e2.label, "HAS_CREATOR");
144
+ assert_eq!(&e2.destination, "Person");
145
+ let e3 = &ast.edges[3];
146
+ assert_eq!(&e3.source, "Comment");
147
+ assert_eq!(&e3.label, "REPLY_OF");
148
+ assert_eq!(&e3.destination, "Message");
149
+
150
+ let (pd0_key, pd0_v) = &ast.elements.get_index(0).unwrap();
151
+ assert_eq!(pd0_key.as_str(), "Person");
152
+ assert!(pd0_v.parents.is_empty());
153
+ assert_eq!(pd0_v.properties.len(), 2);
154
+ assert_eq!(
155
+ pd0_v.properties["firstName"],
156
+ LiteralBaseType::String.into()
157
+ );
158
+ assert_eq!(pd0_v.properties["lastName"], LiteralBaseType::String.into());
159
+
160
+ let (pd1_key, pd1_v) = &ast.elements.get_index(1).unwrap();
161
+ assert_eq!(pd1_key.as_str(), "Message");
162
+ assert!(pd1_v.parents.is_empty());
163
+ assert_eq!(pd1_v.properties.len(), 2);
164
+ assert_eq!(
165
+ pd1_v.properties["creationDate"],
166
+ LiteralBaseType::TimeStamp.into()
167
+ );
168
+ assert_eq!(
169
+ pd1_v.properties["browserUsed"],
170
+ LiteralBaseType::String.into()
171
+ );
172
+
173
+ let (pd2_key, pd2_v) = &ast.elements.get_index(2).unwrap();
174
+ assert_eq!(pd2_key.as_str(), "Comment");
175
+ assert_eq!(pd2_v.parents, vec!["Message".to_string()]);
176
+ assert!(pd2_v.properties.is_empty());
177
+
178
+ let (pd3_key, pd3_v) = &ast.elements.get_index(3).unwrap();
179
+ assert_eq!(pd3_key.as_str(), "Post");
180
+ assert_eq!(pd3_v.parents, vec!["Message".to_string()]);
181
+ assert_eq!(pd3_v.properties.len(), 1);
182
+ assert_eq!(
183
+ pd3_v.properties["imageFile"],
184
+ Property::Optional(Box::new(LiteralBaseType::String.into()))
185
+ );
186
+
187
+ let (pd4_key, pd4_v) = &ast.elements.get_index(4).unwrap();
188
+ assert_eq!(pd4_key.as_str(), "REPLY_OF");
189
+ assert!(pd4_v.parents.is_empty());
190
+ assert!(pd4_v.properties.is_empty());
191
+
192
+ let (pd5_key, pd5_v) = &ast.elements.get_index(5).unwrap();
193
+ assert_eq!(pd5_key.as_str(), "KNOWS");
194
+ assert!(pd5_v.parents.is_empty());
195
+ assert_eq!(pd5_v.properties.len(), 1);
196
+ assert_eq!(
197
+ pd5_v.properties["creationDate"],
198
+ LiteralBaseType::TimeStamp.into()
199
+ );
200
+ }
201
+
202
+ #[test]
203
+ fn parse_with_type_declarations()
204
+ {
205
+ use crate::gqls::ast::TypeRef;
206
+ use crate::gqls::constraint::Constraint;
207
+ use graphcore::Value;
208
+
209
+ let ast = super::parse_schema(
210
+ r#"
211
+ // Type declarations
212
+ type Age = INTEGER: > 0,
213
+ type Priority = INTEGER,
214
+ type HighPriority = Priority: > 50,
215
+
216
+ // Properties definitions
217
+ Person{
218
+ firstName: STRING, lastName : STRING
219
+ },
220
+ Message {
221
+ creationDate: TIMESTAMP, browserUsed: STRING
222
+ },
223
+
224
+ // Nodes
225
+ (Person), (Message),
226
+
227
+ // Edges
228
+ (Person)-[KNOWS]->(Person)
229
+ "#,
230
+ )
231
+ .unwrap();
232
+
233
+ // Verify type declarations were parsed
234
+ assert_eq!(ast.constrained_types.len(), 3);
235
+
236
+ // Check Age type
237
+ let age_def = &ast.constrained_types["Age"];
238
+ assert_eq!(age_def.base, TypeRef::Base(LiteralBaseType::Integer));
239
+ assert_eq!(
240
+ age_def.constraint,
241
+ Some(Constraint::GreaterThan(Value::Integer(0)))
242
+ );
243
+
244
+ // Check Priority type (no constraint)
245
+ let priority_def = &ast.constrained_types["Priority"];
246
+ assert_eq!(priority_def.base, TypeRef::Base(LiteralBaseType::Integer));
247
+ assert!(priority_def.constraint.is_none());
248
+
249
+ // Check HighPriority type (references another type)
250
+ let high_priority_def = &ast.constrained_types["HighPriority"];
251
+ assert_eq!(
252
+ high_priority_def.base,
253
+ TypeRef::Named("Priority".to_string())
254
+ );
255
+ assert_eq!(
256
+ high_priority_def.constraint,
257
+ Some(Constraint::GreaterThan(Value::Integer(50)))
258
+ );
259
+
260
+ // Verify regular elements still work
261
+ assert_eq!(ast.elements.len(), 2);
262
+ assert_eq!(ast.nodes.len(), 2);
263
+ assert_eq!(ast.edges.len(), 1);
264
+ }
265
+ }
@@ -0,0 +1,7 @@
1
+ #![doc = include_str!("../README.MD")]
2
+ // #![warn(missing_docs)]
3
+
4
+ mod common;
5
+ pub mod gqls;
6
+ pub mod oc;
7
+ pub mod prelude;