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.
- checksums.yaml +4 -4
- data/ext/Cargo.toml +12 -6
- data/ext/db-index/Cargo.toml +29 -0
- data/ext/db-index/src/hnsw/error.rs +66 -0
- data/ext/db-index/src/hnsw/graph_store.rs +272 -0
- data/ext/db-index/src/hnsw/id.rs +36 -0
- data/ext/db-index/src/hnsw/implementation.rs +1517 -0
- data/ext/db-index/src/hnsw/kernels.rs +1228 -0
- data/ext/db-index/src/hnsw/metric.rs +244 -0
- data/ext/db-index/src/hnsw/scalar.rs +72 -0
- data/ext/db-index/src/hnsw/simple_store.rs +140 -0
- data/ext/db-index/src/hnsw/store.rs +472 -0
- data/ext/db-index/src/hnsw/vector.rs +105 -0
- data/ext/db-index/src/hnsw/vectors.rs +568 -0
- data/ext/db-index/src/hnsw.rs +42 -0
- data/ext/db-index/src/lib.rs +3 -0
- data/ext/gqlitedb/Cargo.toml +19 -8
- data/ext/gqlitedb/benches/common/pokec.rs +62 -2
- data/ext/gqlitedb/benches/pokec_divan.rs +66 -2
- data/ext/gqlitedb/benches/pokec_iai.rs +60 -3
- data/ext/gqlitedb/release.toml +2 -2
- data/ext/gqlitedb/src/aggregators/arithmetic.rs +3 -1
- data/ext/gqlitedb/src/aggregators/containers.rs +1 -1
- data/ext/gqlitedb/src/aggregators/stats.rs +21 -12
- data/ext/gqlitedb/src/capi.rs +20 -24
- data/ext/gqlitedb/src/compiler/expression_analyser.rs +28 -20
- data/ext/gqlitedb/src/compiler/variables_manager.rs +104 -38
- data/ext/gqlitedb/src/compiler.rs +506 -227
- data/ext/gqlitedb/src/connection.rs +151 -11
- data/ext/gqlitedb/src/consts.rs +1 -2
- data/ext/gqlitedb/src/error.rs +123 -61
- data/ext/gqlitedb/src/functions/common.rs +64 -0
- data/ext/gqlitedb/src/functions/containers.rs +2 -46
- data/ext/gqlitedb/src/functions/edge.rs +1 -1
- data/ext/gqlitedb/src/functions/math.rs +87 -15
- data/ext/gqlitedb/src/functions/node.rs +1 -1
- data/ext/gqlitedb/src/functions/path.rs +48 -11
- data/ext/gqlitedb/src/functions/scalar.rs +47 -4
- data/ext/gqlitedb/src/functions/string.rs +123 -1
- data/ext/gqlitedb/src/functions/value.rs +1 -1
- data/ext/gqlitedb/src/functions.rs +165 -28
- data/ext/gqlitedb/src/graph.rs +1 -9
- data/ext/gqlitedb/src/interpreter/evaluators.rs +968 -130
- data/ext/gqlitedb/src/interpreter/instructions.rs +39 -2
- data/ext/gqlitedb/src/lib.rs +5 -4
- data/ext/gqlitedb/src/parser/gql.pest +1 -1
- data/ext/gqlitedb/src/planner.rs +329 -0
- data/ext/gqlitedb/src/prelude.rs +3 -3
- data/ext/gqlitedb/src/store/pgrx.rs +1 -1
- data/ext/gqlitedb/src/store/postgres.rs +742 -16
- data/ext/gqlitedb/src/store/redb/hnsw_store.rs +702 -0
- data/ext/gqlitedb/src/store/redb/index.rs +274 -0
- data/ext/gqlitedb/src/store/redb.rs +1268 -113
- data/ext/gqlitedb/src/store/sqlbase/sqlmetadata.rs +28 -0
- data/ext/gqlitedb/src/store/sqlbase/sqlstore.rs +103 -0
- data/ext/gqlitedb/src/store/sqlbase.rs +146 -16
- data/ext/gqlitedb/src/store/sqlite.rs +576 -14
- data/ext/gqlitedb/src/store/vector_extract.rs +56 -0
- data/ext/gqlitedb/src/store.rs +140 -29
- data/ext/gqlitedb/src/tests/compiler.rs +207 -10
- data/ext/gqlitedb/src/tests/connection/postgres.rs +38 -3
- data/ext/gqlitedb/src/tests/connection/redb.rs +23 -0
- data/ext/gqlitedb/src/tests/connection/sqlite.rs +31 -0
- data/ext/gqlitedb/src/tests/connection.rs +61 -1
- data/ext/gqlitedb/src/tests/evaluators.rs +162 -7
- data/ext/gqlitedb/src/tests/parser.rs +54 -23
- data/ext/gqlitedb/src/tests/planner.rs +511 -0
- data/ext/gqlitedb/src/tests/store/postgres.rs +7 -0
- data/ext/gqlitedb/src/tests/store/redb.rs +8 -0
- data/ext/gqlitedb/src/tests/store/sqlite.rs +8 -0
- data/ext/gqlitedb/src/tests/store/vector_index/postgres.rs +182 -0
- data/ext/gqlitedb/src/tests/store/vector_index/redb.rs +386 -0
- data/ext/gqlitedb/src/tests/store/vector_index/sqlite.rs +166 -0
- data/ext/gqlitedb/src/tests/store/vector_index.rs +2313 -0
- data/ext/gqlitedb/src/tests/store.rs +78 -14
- data/ext/gqlitedb/src/tests/templates/ast.rs +92 -16
- data/ext/gqlitedb/src/tests/templates/programs.rs +14 -7
- data/ext/gqlitedb/src/tests.rs +15 -9
- data/ext/gqlitedb/src/utils.rs +6 -1
- data/ext/gqlitedb/src/value/compare.rs +61 -3
- data/ext/gqlitedb/src/value.rs +136 -7
- data/ext/gqlitedb/templates/sql/postgres/metadata_delete.sql +1 -0
- data/ext/gqlitedb/templates/sql/postgres/node_select.sql +1 -1
- data/ext/gqlitedb/templates/sql/sqlite/metadata_delete.sql +1 -0
- data/ext/gqliterb/src/lib.rs +53 -8
- data/ext/gqlparser/Cargo.toml +25 -0
- data/ext/gqlparser/README.MD +9 -0
- data/ext/gqlparser/benches/pokec_divan.rs +34 -0
- data/ext/gqlparser/src/common.rs +69 -0
- data/ext/gqlparser/src/gqls/ast.rs +69 -0
- data/ext/gqlparser/src/gqls/constraint.rs +79 -0
- data/ext/gqlparser/src/gqls/error.rs +22 -0
- data/ext/gqlparser/src/gqls/parser.rs +813 -0
- data/ext/gqlparser/src/gqls/prelude.rs +8 -0
- data/ext/gqlparser/src/gqls/properties.rs +115 -0
- data/ext/gqlparser/src/gqls/resolve.rs +207 -0
- data/ext/gqlparser/src/gqls.rs +265 -0
- data/ext/gqlparser/src/lib.rs +7 -0
- data/ext/gqlparser/src/oc/ast.rs +680 -0
- data/ext/gqlparser/src/oc/error.rs +172 -0
- data/ext/gqlparser/src/oc/lexer.rs +429 -0
- data/ext/gqlparser/src/oc/parser/tests.rs +2284 -0
- data/ext/gqlparser/src/oc/parser.rs +2005 -0
- data/ext/gqlparser/src/oc.rs +33 -0
- data/ext/gqlparser/src/prelude.rs +3 -0
- data/ext/graphcore/Cargo.toml +1 -0
- data/ext/graphcore/src/error.rs +26 -0
- data/ext/graphcore/src/graph.rs +177 -51
- data/ext/graphcore/src/lib.rs +4 -2
- data/ext/graphcore/src/open_cypher.rs +12 -0
- data/ext/graphcore/src/table.rs +50 -2
- data/ext/graphcore/src/timestamp.rs +127 -104
- data/ext/graphcore/src/value/tensor.rs +739 -0
- data/ext/graphcore/src/value/value_map.rs +1 -1
- data/ext/graphcore/src/value.rs +343 -19
- metadata +93 -28
- data/ext/gqlitedb/src/parser/ast.rs +0 -604
- data/ext/gqlitedb/src/parser/parser_impl.rs +0 -1213
- data/ext/gqlitedb/src/parser.rs +0 -4
|
@@ -1,7 +1,11 @@
|
|
|
1
1
|
#[cfg(all(feature = "postgres", not(windows)))]
|
|
2
2
|
pub(crate) mod postgres;
|
|
3
|
+
#[cfg(feature = "redb")]
|
|
4
|
+
mod redb;
|
|
5
|
+
#[cfg(feature = "sqlite")]
|
|
6
|
+
pub(crate) mod sqlite;
|
|
3
7
|
|
|
4
|
-
fn
|
|
8
|
+
fn test_create_match_return(connection: crate::Connection)
|
|
5
9
|
{
|
|
6
10
|
connection
|
|
7
11
|
.execute_oc_query("CREATE (n:a)", Default::default())
|
|
@@ -14,6 +18,62 @@ fn test_connection(connection: crate::Connection)
|
|
|
14
18
|
assert_eq!(table.rows(), 1);
|
|
15
19
|
assert_eq!(*table.headers(), vec!["n".to_string()]);
|
|
16
20
|
let node: graphcore::Node = table.value(0, 0).unwrap().try_into().unwrap();
|
|
21
|
+
println!("{node:?}");
|
|
22
|
+
assert_eq!(node.graph_name().as_ref().unwrap(), "default");
|
|
17
23
|
assert_eq!(*node.labels(), vec!["a".to_string()]);
|
|
18
24
|
assert_eq!(node.properties().len(), 0);
|
|
19
25
|
}
|
|
26
|
+
|
|
27
|
+
fn test_create_return(connection: crate::Connection)
|
|
28
|
+
{
|
|
29
|
+
let r = connection
|
|
30
|
+
.execute_oc_query("CREATE (n:a) RETURN n", Default::default())
|
|
31
|
+
.unwrap();
|
|
32
|
+
let table = r.try_into_table().unwrap();
|
|
33
|
+
assert_eq!(table.columns(), 1);
|
|
34
|
+
assert_eq!(table.rows(), 1);
|
|
35
|
+
assert_eq!(*table.headers(), vec!["n".to_string()]);
|
|
36
|
+
let node: graphcore::Node = table.value(0, 0).unwrap().try_into().unwrap();
|
|
37
|
+
println!("{node:?}");
|
|
38
|
+
assert_eq!(node.graph_name().as_ref().unwrap(), "default");
|
|
39
|
+
assert_eq!(*node.labels(), vec!["a".to_string()]);
|
|
40
|
+
assert_eq!(node.properties().len(), 0);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
fn test_create_edge_return(connection: crate::Connection)
|
|
44
|
+
{
|
|
45
|
+
let r = connection
|
|
46
|
+
.execute_oc_query("CREATE ()-[e:a]->() RETURN e", Default::default())
|
|
47
|
+
.unwrap();
|
|
48
|
+
let table = r.try_into_table().unwrap();
|
|
49
|
+
assert_eq!(table.columns(), 1);
|
|
50
|
+
assert_eq!(table.rows(), 1);
|
|
51
|
+
assert_eq!(*table.headers(), vec!["e".to_string()]);
|
|
52
|
+
let edge: graphcore::Edge = table.value(0, 0).unwrap().try_into().unwrap();
|
|
53
|
+
println!("{edge:?}");
|
|
54
|
+
assert_eq!(edge.graph_name().as_ref().unwrap(), "default");
|
|
55
|
+
assert_eq!(*edge.labels(), vec!["a".to_string()]);
|
|
56
|
+
assert_eq!(edge.properties().len(), 0);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
fn test_vector_search_dimension_mismatch(connection: crate::Connection)
|
|
60
|
+
{
|
|
61
|
+
connection
|
|
62
|
+
.execute_oc_query(
|
|
63
|
+
"CREATE (:Doc { embedding: [0.1, 0.2, 0.3], title: 'doc1' })",
|
|
64
|
+
Default::default(),
|
|
65
|
+
)
|
|
66
|
+
.unwrap();
|
|
67
|
+
connection
|
|
68
|
+
.execute_oc_query(
|
|
69
|
+
"CREATE VECTOR INDEX doc_idx ON :Doc (embedding) OPTIONS { dimension: 3, metric: \"cosine\" }",
|
|
70
|
+
Default::default(),
|
|
71
|
+
)
|
|
72
|
+
.unwrap();
|
|
73
|
+
|
|
74
|
+
let result = connection.execute_oc_query(
|
|
75
|
+
"MATCH (n:Doc) SEARCH n IN (VECTOR INDEX doc_idx FOR [0.5, 0.5] LIMIT 1) RETURN n.title",
|
|
76
|
+
Default::default(),
|
|
77
|
+
);
|
|
78
|
+
assert!(result.is_err(), "dimension mismatch should raise an error");
|
|
79
|
+
}
|
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
use crate::{
|
|
2
|
+
compiler::compile,
|
|
2
3
|
interpreter::evaluators::eval_program,
|
|
4
|
+
planner,
|
|
3
5
|
prelude::*,
|
|
6
|
+
query_result::QueryResult,
|
|
4
7
|
store::{Store, TransactionBoxable},
|
|
5
8
|
tests::{check_stats, templates::programs},
|
|
6
9
|
};
|
|
@@ -74,18 +77,21 @@ fn test_evaluate_match_loop()
|
|
|
74
77
|
let temp_file = crate::tests::create_tmp_file();
|
|
75
78
|
let store = crate::store::redb::Store::open(temp_file.path()).unwrap();
|
|
76
79
|
|
|
77
|
-
let node = graph::Node::new(
|
|
80
|
+
let node = graph::Node::new(
|
|
81
|
+
Some("default".into()),
|
|
82
|
+
graph::Key::new(1),
|
|
83
|
+
vec![],
|
|
84
|
+
Default::default(),
|
|
85
|
+
);
|
|
78
86
|
let mut tx = store.begin_write().unwrap();
|
|
79
87
|
store.create_nodes(&mut tx, "default", vec![&node]).unwrap();
|
|
80
88
|
store
|
|
81
89
|
.create_edges(
|
|
82
90
|
&mut tx,
|
|
83
91
|
"default",
|
|
84
|
-
vec![&graph::
|
|
85
|
-
graph::Key::new(2),
|
|
92
|
+
vec![&graph::SinglePath::new(
|
|
86
93
|
node.clone(),
|
|
87
|
-
vec![],
|
|
88
|
-
Default::default(),
|
|
94
|
+
graph::Edge::new(None, graph::Key::new(2), vec![], Default::default()),
|
|
89
95
|
node.clone(),
|
|
90
96
|
)],
|
|
91
97
|
)
|
|
@@ -125,7 +131,7 @@ fn test_evaluate_match_count()
|
|
|
125
131
|
assert_eq!(value, crate::table![("count(*)"), [[0]]].into());
|
|
126
132
|
|
|
127
133
|
// Count 1
|
|
128
|
-
let node = graph::Node::new(graph::Key::new(1), vec![], Default::default());
|
|
134
|
+
let node = graph::Node::new(None, graph::Key::new(1), vec![], Default::default());
|
|
129
135
|
let mut tx = store.begin_write().unwrap();
|
|
130
136
|
store.create_nodes(&mut tx, "default", vec![&node]).unwrap();
|
|
131
137
|
tx.close().unwrap();
|
|
@@ -147,12 +153,19 @@ fn test_evaluate_aggregation()
|
|
|
147
153
|
|
|
148
154
|
let nodes = [
|
|
149
155
|
graph::Node::new(
|
|
156
|
+
None,
|
|
150
157
|
graph::Key::new(1),
|
|
151
158
|
vec![],
|
|
152
159
|
value::value_map!("name" => "a", "num" => 33),
|
|
153
160
|
),
|
|
154
|
-
graph::Node::new(graph::Key::new(2), vec![], value::value_map!("name" => "a")),
|
|
155
161
|
graph::Node::new(
|
|
162
|
+
None,
|
|
163
|
+
graph::Key::new(2),
|
|
164
|
+
vec![],
|
|
165
|
+
value::value_map!("name" => "a"),
|
|
166
|
+
),
|
|
167
|
+
graph::Node::new(
|
|
168
|
+
None,
|
|
156
169
|
graph::Key::new(3),
|
|
157
170
|
vec![],
|
|
158
171
|
value::value_map!("name" => "b", "num" => 42),
|
|
@@ -176,3 +189,145 @@ fn test_evaluate_aggregation()
|
|
|
176
189
|
crate::table![("n.name", "count(n.num)"), [["a", 1], ["b", 1]]],
|
|
177
190
|
);
|
|
178
191
|
}
|
|
192
|
+
|
|
193
|
+
fn run_query(store: &impl Store, source: &str) -> Result<QueryResult>
|
|
194
|
+
{
|
|
195
|
+
let function_manager = functions::Manager::new();
|
|
196
|
+
let queries = gqlparser::oc::parse_oc_query(source).map_err(|_| {
|
|
197
|
+
error::Error::CompileTime(error::CompileTimeError::ParseError {
|
|
198
|
+
error_kind: gqlparser::oc::ErrorKind::UnexpectedToken,
|
|
199
|
+
text: source.to_string(),
|
|
200
|
+
span: 0..source.len(),
|
|
201
|
+
})
|
|
202
|
+
})?;
|
|
203
|
+
let statements = queries
|
|
204
|
+
.into_iter()
|
|
205
|
+
.next()
|
|
206
|
+
.and_then(|q| match q
|
|
207
|
+
{
|
|
208
|
+
gqlparser::oc::ast::Query::Statements(s) => Some(s),
|
|
209
|
+
_ => None,
|
|
210
|
+
})
|
|
211
|
+
.ok_or_else(|| {
|
|
212
|
+
error::Error::CompileTime(error::CompileTimeError::UnexpectedSyntax {
|
|
213
|
+
expected: "statements".to_string(),
|
|
214
|
+
got: "other".to_string(),
|
|
215
|
+
line: 0,
|
|
216
|
+
col: 0,
|
|
217
|
+
})
|
|
218
|
+
})?;
|
|
219
|
+
let program = compile(
|
|
220
|
+
&function_manager,
|
|
221
|
+
statements
|
|
222
|
+
.into_iter()
|
|
223
|
+
.map(planner::plan)
|
|
224
|
+
.collect::<Result<Vec<_>, _>>()?,
|
|
225
|
+
)?;
|
|
226
|
+
eval_program(store, &program, &Default::default())
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
#[test]
|
|
230
|
+
fn test_vector_search_on_non_default_graph_after_use()
|
|
231
|
+
{
|
|
232
|
+
let temp_file = crate::tests::create_tmp_file();
|
|
233
|
+
let store = crate::store::redb::Store::open(temp_file.path()).unwrap();
|
|
234
|
+
|
|
235
|
+
// Single index, single graph, never touching "default" at all -- the
|
|
236
|
+
// minimal reproduction of the bug report.
|
|
237
|
+
run_query(&store, "CREATE GRAPH g1").unwrap();
|
|
238
|
+
run_query(
|
|
239
|
+
&store,
|
|
240
|
+
"USE g1 CREATE (:Doc {title: 'A', embedding: [1.0, 0.0, 0.0]})",
|
|
241
|
+
)
|
|
242
|
+
.unwrap();
|
|
243
|
+
run_query(
|
|
244
|
+
&store,
|
|
245
|
+
"USE g1 CREATE VECTOR INDEX idx ON :Doc (embedding) OPTIONS { dimension: 3, metric: \"cosine\" }",
|
|
246
|
+
)
|
|
247
|
+
.unwrap();
|
|
248
|
+
|
|
249
|
+
// EXPECTED TO FAIL before #444: this currently raises
|
|
250
|
+
// StoreError::InvalidFormat("Vector index 'idx' does not exist in graph 'default'")
|
|
251
|
+
// because LogicalPlan::VectorSearch hardcodes graph: "default" at plan
|
|
252
|
+
// time (planner.rs), regardless of the USE g1 immediately above.
|
|
253
|
+
let result = run_query(
|
|
254
|
+
&store,
|
|
255
|
+
"USE g1 MATCH (n:Doc) SEARCH n IN (VECTOR INDEX idx FOR [1.0, 0.0, 0.0] LIMIT 1) RETURN n.title",
|
|
256
|
+
);
|
|
257
|
+
assert!(
|
|
258
|
+
result.is_ok(),
|
|
259
|
+
"expected search on g1 to find idx registered on g1, got: {:?}",
|
|
260
|
+
result
|
|
261
|
+
);
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
#[test]
|
|
265
|
+
fn test_vector_search_multiple_indexes_same_graph()
|
|
266
|
+
{
|
|
267
|
+
let temp_file = crate::tests::create_tmp_file();
|
|
268
|
+
let store = crate::store::redb::Store::open(temp_file.path()).unwrap();
|
|
269
|
+
|
|
270
|
+
run_query(&store, "CREATE GRAPH g1").unwrap();
|
|
271
|
+
run_query(
|
|
272
|
+
&store,
|
|
273
|
+
"USE g1 CREATE (:Doc {title: 'A', embedding: [1.0, 0.0, 0.0]}) \
|
|
274
|
+
CREATE (:Note {title: 'B', tags: [0.0, 1.0, 0.0]})",
|
|
275
|
+
)
|
|
276
|
+
.unwrap();
|
|
277
|
+
run_query(
|
|
278
|
+
&store,
|
|
279
|
+
"USE g1 CREATE VECTOR INDEX idx_doc ON :Doc (embedding) OPTIONS { dimension: 3, metric: \"cosine\" }",
|
|
280
|
+
)
|
|
281
|
+
.unwrap();
|
|
282
|
+
run_query(
|
|
283
|
+
&store,
|
|
284
|
+
"USE g1 CREATE VECTOR INDEX idx_note ON :Note (tags) OPTIONS { dimension: 3, metric: \"cosine\" }",
|
|
285
|
+
)
|
|
286
|
+
.unwrap();
|
|
287
|
+
|
|
288
|
+
// EXPECTED TO FAIL before #444, same root cause as above.
|
|
289
|
+
let doc_result = run_query(
|
|
290
|
+
&store,
|
|
291
|
+
"USE g1 MATCH (n:Doc) SEARCH n IN (VECTOR INDEX idx_doc FOR [1.0, 0.0, 0.0] LIMIT 1) RETURN n.title",
|
|
292
|
+
);
|
|
293
|
+
assert!(doc_result.is_ok());
|
|
294
|
+
|
|
295
|
+
let note_result = run_query(
|
|
296
|
+
&store,
|
|
297
|
+
"USE g1 MATCH (n:Note) SEARCH n IN (VECTOR INDEX idx_note FOR [0.0, 1.0, 0.0] LIMIT 1) RETURN n.title",
|
|
298
|
+
);
|
|
299
|
+
assert!(note_result.is_ok());
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
#[test]
|
|
303
|
+
fn test_vector_search_after_switching_between_two_graphs()
|
|
304
|
+
{
|
|
305
|
+
let temp_file = crate::tests::create_tmp_file();
|
|
306
|
+
let store = crate::store::redb::Store::open(temp_file.path()).unwrap();
|
|
307
|
+
|
|
308
|
+
run_query(&store, "CREATE GRAPH g1 CREATE GRAPH g2").unwrap();
|
|
309
|
+
run_query(
|
|
310
|
+
&store,
|
|
311
|
+
"USE g1 CREATE (:Doc {title: 'A', embedding: [1.0, 0.0, 0.0]}) \
|
|
312
|
+
CREATE VECTOR INDEX idx1 ON :Doc (embedding) OPTIONS { dimension: 3, metric: \"cosine\" }",
|
|
313
|
+
)
|
|
314
|
+
.unwrap();
|
|
315
|
+
run_query(
|
|
316
|
+
&store,
|
|
317
|
+
"USE g2 CREATE (:Doc {title: 'B', embedding: [0.0, 1.0, 0.0]}) \
|
|
318
|
+
CREATE VECTOR INDEX idx2 ON :Doc (embedding) OPTIONS { dimension: 3, metric: \"cosine\" }",
|
|
319
|
+
)
|
|
320
|
+
.unwrap();
|
|
321
|
+
|
|
322
|
+
// After switching back to g1, idx1 must resolve against g1 -- direct
|
|
323
|
+
// multi-USE regression case. EXPECTED TO FAIL before WI 3.
|
|
324
|
+
let result = run_query(
|
|
325
|
+
&store,
|
|
326
|
+
"USE g1 MATCH (n:Doc) SEARCH n IN (VECTOR INDEX idx1 FOR [1.0, 0.0, 0.0] LIMIT 1) RETURN n.title",
|
|
327
|
+
);
|
|
328
|
+
assert!(
|
|
329
|
+
result.is_ok(),
|
|
330
|
+
"idx1 must be found on g1 after switching away and back: {:?}",
|
|
331
|
+
result
|
|
332
|
+
);
|
|
333
|
+
}
|
|
@@ -1,81 +1,112 @@
|
|
|
1
|
-
use
|
|
2
|
-
parser::{self, parse},
|
|
3
|
-
tests::templates::ast,
|
|
4
|
-
};
|
|
1
|
+
use gqlparser::oc::{self, parse_oc_query as parse};
|
|
5
2
|
|
|
6
|
-
|
|
3
|
+
use crate::tests::templates::ast;
|
|
4
|
+
|
|
5
|
+
fn compare_ast(actual: Vec<oc::ast::Statement>, expected: Vec<oc::ast::Statement>)
|
|
7
6
|
{
|
|
8
7
|
assert_eq!(format!("{:?}", actual), format!("{:?}", expected));
|
|
9
8
|
}
|
|
10
9
|
|
|
10
|
+
fn parse_query(source: &str) -> Vec<oc::ast::Statement>
|
|
11
|
+
{
|
|
12
|
+
let queries = parse(source).unwrap();
|
|
13
|
+
let oc::ast::Query::Statements(ast) = queries.into_iter().next().unwrap()
|
|
14
|
+
else
|
|
15
|
+
{
|
|
16
|
+
panic!("Expected Statements query");
|
|
17
|
+
};
|
|
18
|
+
ast
|
|
19
|
+
}
|
|
20
|
+
|
|
11
21
|
#[test]
|
|
12
22
|
fn test_parse_simple_create_node()
|
|
13
23
|
{
|
|
14
|
-
let ast =
|
|
15
|
-
let ast = ast.into_iter().next().unwrap();
|
|
24
|
+
let ast = parse_query("CREATE ()");
|
|
16
25
|
compare_ast(ast, ast::simple_create_node())
|
|
17
26
|
}
|
|
18
27
|
|
|
19
28
|
#[test]
|
|
20
29
|
fn test_parse_create_named_node()
|
|
21
30
|
{
|
|
22
|
-
let ast =
|
|
23
|
-
let ast = ast.into_iter().next().unwrap();
|
|
31
|
+
let ast = parse_query("CREATE (n {name: 'foo'}) RETURN n.name AS p");
|
|
24
32
|
compare_ast(ast, ast::create_named_node())
|
|
25
33
|
}
|
|
26
34
|
|
|
27
35
|
#[test]
|
|
28
36
|
fn test_parse_create_named_node_double_return()
|
|
29
37
|
{
|
|
30
|
-
let ast =
|
|
31
|
-
let ast = ast.into_iter().next().unwrap();
|
|
38
|
+
let ast = parse_query("CREATE (n {id: 12, name: 'foo'}) RETURN n.id AS id, n.name AS p");
|
|
32
39
|
compare_ast(ast, ast::create_named_node_double_return())
|
|
33
40
|
}
|
|
34
41
|
|
|
35
42
|
#[test]
|
|
36
43
|
fn test_parse_double_with_return()
|
|
37
44
|
{
|
|
38
|
-
let ast =
|
|
39
|
-
let ast = ast.into_iter().next().unwrap();
|
|
45
|
+
let ast = parse_query("WITH 1 AS n, 2 AS m WITH n AS a, m AS b RETURN a");
|
|
40
46
|
compare_ast(ast, ast::double_with_return())
|
|
41
47
|
}
|
|
42
48
|
|
|
43
49
|
#[test]
|
|
44
50
|
fn test_parse_unwind()
|
|
45
51
|
{
|
|
46
|
-
let ast =
|
|
47
|
-
let ast = ast.into_iter().next().unwrap();
|
|
52
|
+
let ast = parse_query("UNWIND [0] AS i RETURN i");
|
|
48
53
|
compare_ast(ast, ast::unwind())
|
|
49
54
|
}
|
|
50
55
|
|
|
51
56
|
#[test]
|
|
52
57
|
fn test_parse_match_loop()
|
|
53
58
|
{
|
|
54
|
-
let ast =
|
|
55
|
-
let ast = ast.into_iter().next().unwrap();
|
|
59
|
+
let ast = parse_query("MATCH (n)-[]->(n) RETURN n");
|
|
56
60
|
compare_ast(ast, ast::match_loop());
|
|
57
61
|
}
|
|
58
62
|
|
|
59
63
|
#[test]
|
|
60
64
|
fn test_parse_optional_match()
|
|
61
65
|
{
|
|
62
|
-
let ast =
|
|
63
|
-
let ast = ast.into_iter().next().unwrap();
|
|
66
|
+
let ast = parse_query("OPTIONAL MATCH (a) RETURN a");
|
|
64
67
|
compare_ast(ast, ast::optional_match());
|
|
65
68
|
}
|
|
66
69
|
|
|
67
70
|
#[test]
|
|
68
71
|
fn test_parse_match_count()
|
|
69
72
|
{
|
|
70
|
-
let ast =
|
|
71
|
-
let ast = ast.into_iter().next().unwrap();
|
|
73
|
+
let ast = parse_query("MATCH (a) RETURN count(*)");
|
|
72
74
|
compare_ast(ast, ast::match_count());
|
|
73
75
|
}
|
|
74
76
|
|
|
75
77
|
#[test]
|
|
76
78
|
fn test_parse_aggregation()
|
|
77
79
|
{
|
|
78
|
-
let ast =
|
|
79
|
-
let ast = ast.into_iter().next().unwrap();
|
|
80
|
+
let ast = parse_query("MATCH (n) RETURN n.name, count(n.num)");
|
|
80
81
|
compare_ast(ast, ast::aggregation());
|
|
81
82
|
}
|
|
83
|
+
|
|
84
|
+
#[test]
|
|
85
|
+
fn test_parse_create_vector_index()
|
|
86
|
+
{
|
|
87
|
+
let ast = parse_query(
|
|
88
|
+
"CREATE VECTOR INDEX idx ON :Doc (embedding) OPTIONS { dimension: 3, metric: \"cosine\" }",
|
|
89
|
+
);
|
|
90
|
+
compare_ast(ast, ast::create_vector_index("idx"))
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
#[test]
|
|
94
|
+
fn test_parse_match_with_vector_search()
|
|
95
|
+
{
|
|
96
|
+
let ast = parse_query(
|
|
97
|
+
"MATCH (n:Doc) SEARCH n IN (VECTOR INDEX idx FOR [0.1, 0.2, 0.3] LIMIT 2) RETURN n.title AS p",
|
|
98
|
+
);
|
|
99
|
+
compare_ast(ast, ast::match_with_vector_search("idx"))
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
#[test]
|
|
103
|
+
fn test_parse_multiple_vector_index_creation()
|
|
104
|
+
{
|
|
105
|
+
let ast = parse_query(
|
|
106
|
+
"CREATE VECTOR INDEX idx1 ON :Doc (embedding) OPTIONS { dimension: 3, metric: \"cosine\" } \
|
|
107
|
+
CREATE VECTOR INDEX idx2 ON :Doc (embedding) OPTIONS { dimension: 3, metric: \"cosine\" }",
|
|
108
|
+
);
|
|
109
|
+
let mut expected = ast::create_vector_index("idx1");
|
|
110
|
+
expected.extend(ast::create_vector_index("idx2"));
|
|
111
|
+
compare_ast(ast, expected)
|
|
112
|
+
}
|