gqlite 1.5.1 → 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 +10 -4
- 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 -9
- 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 +1 -1
- data/ext/gqlitedb/src/compiler/expression_analyser.rs +28 -20
- data/ext/gqlitedb/src/compiler/variables_manager.rs +97 -29
- data/ext/gqlitedb/src/compiler.rs +505 -225
- data/ext/gqlitedb/src/connection.rs +149 -11
- data/ext/gqlitedb/src/consts.rs +1 -2
- data/ext/gqlitedb/src/error.rs +123 -61
- data/ext/gqlitedb/src/functions/common.rs +39 -2
- data/ext/gqlitedb/src/functions/math.rs +8 -3
- data/ext/gqlitedb/src/functions/path.rs +48 -11
- data/ext/gqlitedb/src/functions/value.rs +1 -1
- data/ext/gqlitedb/src/functions.rs +23 -7
- 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/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 +735 -7
- 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 +569 -5
- data/ext/gqlitedb/src/store/vector_extract.rs +56 -0
- data/ext/gqlitedb/src/store.rs +123 -3
- 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 +90 -22
- 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
data/ext/gqlitedb/src/store.rs
CHANGED
|
@@ -10,6 +10,8 @@ pub(crate) mod redb;
|
|
|
10
10
|
#[cfg(feature = "sqlite")]
|
|
11
11
|
pub(crate) mod sqlite;
|
|
12
12
|
|
|
13
|
+
pub(crate) mod vector_extract;
|
|
14
|
+
|
|
13
15
|
use crate::prelude::*;
|
|
14
16
|
|
|
15
17
|
// ____ _ _ _ _ _
|
|
@@ -103,6 +105,64 @@ where
|
|
|
103
105
|
// ___) | || (_) | | | __/
|
|
104
106
|
// |____/ \__\___/|_| \___|
|
|
105
107
|
|
|
108
|
+
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, serde::Serialize, serde::Deserialize)]
|
|
109
|
+
pub(crate) enum VectorMetric
|
|
110
|
+
{
|
|
111
|
+
#[default]
|
|
112
|
+
Cosine,
|
|
113
|
+
L2,
|
|
114
|
+
DotProduct,
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
impl VectorMetric
|
|
118
|
+
{
|
|
119
|
+
pub(crate) fn from_debug_str(s: &str) -> Self
|
|
120
|
+
{
|
|
121
|
+
match s
|
|
122
|
+
{
|
|
123
|
+
"Cosine" => Self::Cosine,
|
|
124
|
+
"L2" => Self::L2,
|
|
125
|
+
"DotProduct" => Self::DotProduct,
|
|
126
|
+
_ => Self::Cosine,
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
#[derive(Debug, Clone)]
|
|
132
|
+
pub(crate) struct VectorIndexSpec
|
|
133
|
+
{
|
|
134
|
+
pub name: String,
|
|
135
|
+
pub label: Option<String>,
|
|
136
|
+
pub property: Option<String>,
|
|
137
|
+
pub dimension: usize,
|
|
138
|
+
pub metric: VectorMetric,
|
|
139
|
+
pub hnsw_params: redb::HnswParams,
|
|
140
|
+
pub dtype: value::DType,
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
impl Default for VectorIndexSpec
|
|
144
|
+
{
|
|
145
|
+
fn default() -> Self
|
|
146
|
+
{
|
|
147
|
+
Self {
|
|
148
|
+
name: String::new(),
|
|
149
|
+
label: None,
|
|
150
|
+
property: None,
|
|
151
|
+
dimension: 0,
|
|
152
|
+
metric: VectorMetric::Cosine,
|
|
153
|
+
hnsw_params: Default::default(),
|
|
154
|
+
dtype: value::DType::F32,
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
#[derive(Debug, Clone)]
|
|
160
|
+
pub(crate) struct VectorSearchResult
|
|
161
|
+
{
|
|
162
|
+
pub node_id: graph::Key,
|
|
163
|
+
pub score: f32,
|
|
164
|
+
}
|
|
165
|
+
|
|
106
166
|
pub(crate) trait Store
|
|
107
167
|
{
|
|
108
168
|
type TransactionBox: TransactionBoxable;
|
|
@@ -153,6 +213,7 @@ pub(crate) trait Store
|
|
|
153
213
|
graph_name: impl AsRef<str>,
|
|
154
214
|
query: SelectNodeQuery,
|
|
155
215
|
) -> Result<Vec<crate::graph::Node>>;
|
|
216
|
+
|
|
156
217
|
/// Add edge
|
|
157
218
|
fn create_edges<'a, T: IntoIterator<Item = &'a crate::graph::SinglePath>>(
|
|
158
219
|
&self,
|
|
@@ -172,7 +233,7 @@ pub(crate) trait Store
|
|
|
172
233
|
transaction: &mut Self::TransactionBox,
|
|
173
234
|
graph_name: impl AsRef<str>,
|
|
174
235
|
query: SelectEdgeQuery,
|
|
175
|
-
directivity:
|
|
236
|
+
directivity: graphcore::EdgeDirectivity,
|
|
176
237
|
) -> Result<()>;
|
|
177
238
|
/// Select edges
|
|
178
239
|
fn select_edges(
|
|
@@ -180,10 +241,63 @@ pub(crate) trait Store
|
|
|
180
241
|
transaction: &mut Self::TransactionBox,
|
|
181
242
|
graph_name: impl AsRef<str>,
|
|
182
243
|
query: SelectEdgeQuery,
|
|
183
|
-
directivity:
|
|
244
|
+
directivity: graphcore::EdgeDirectivity,
|
|
184
245
|
) -> Result<Vec<EdgeResult>>;
|
|
185
246
|
/// Compute store statistics
|
|
186
247
|
fn compute_statistics(&self, transaction: &mut Self::TransactionBox) -> Result<Statistics>;
|
|
248
|
+
|
|
249
|
+
// Vector search API
|
|
250
|
+
/// Create a vector index for a graph
|
|
251
|
+
fn create_vector_index(
|
|
252
|
+
&self,
|
|
253
|
+
transaction: &mut Self::TransactionBox,
|
|
254
|
+
graph_name: impl AsRef<str>,
|
|
255
|
+
spec: VectorIndexSpec,
|
|
256
|
+
) -> Result<()>;
|
|
257
|
+
|
|
258
|
+
/// List vector indices for a graph
|
|
259
|
+
#[allow(dead_code)]
|
|
260
|
+
fn list_vector_indexes(
|
|
261
|
+
&self,
|
|
262
|
+
transaction: &mut Self::TransactionBox,
|
|
263
|
+
graph_name: impl AsRef<str>,
|
|
264
|
+
) -> Result<Vec<VectorIndexSpec>>;
|
|
265
|
+
|
|
266
|
+
/// Insert or update a node vector embedding
|
|
267
|
+
fn upsert_vector(
|
|
268
|
+
&self,
|
|
269
|
+
transaction: &mut Self::TransactionBox,
|
|
270
|
+
graph_name: impl AsRef<str>,
|
|
271
|
+
index_name: impl AsRef<str>,
|
|
272
|
+
node_id: graph::Key,
|
|
273
|
+
tensor: &value::Tensor,
|
|
274
|
+
) -> Result<()>;
|
|
275
|
+
|
|
276
|
+
/// Perform top-K similarity search over a vector index
|
|
277
|
+
fn vector_search(
|
|
278
|
+
&self,
|
|
279
|
+
transaction: &mut Self::TransactionBox,
|
|
280
|
+
graph_name: impl AsRef<str>,
|
|
281
|
+
index_name: impl AsRef<str>,
|
|
282
|
+
query: &value::Tensor,
|
|
283
|
+
k: usize,
|
|
284
|
+
) -> Result<Vec<VectorSearchResult>>;
|
|
285
|
+
|
|
286
|
+
/// Drop a vector index from a graph
|
|
287
|
+
fn drop_vector_index(
|
|
288
|
+
&self,
|
|
289
|
+
transaction: &mut Self::TransactionBox,
|
|
290
|
+
graph_name: impl AsRef<str>,
|
|
291
|
+
index_name: impl AsRef<str>,
|
|
292
|
+
) -> Result<()>;
|
|
293
|
+
|
|
294
|
+
/// Apply vector indices to a node (upsert vectors into applicable indices)
|
|
295
|
+
fn apply_vector_indices_for_node(
|
|
296
|
+
&self,
|
|
297
|
+
transaction: &mut Self::TransactionBox,
|
|
298
|
+
graph_name: impl AsRef<str>,
|
|
299
|
+
node: &graph::Node,
|
|
300
|
+
) -> Result<()>;
|
|
187
301
|
}
|
|
188
302
|
|
|
189
303
|
// _____ _ ____ _ _
|
|
@@ -335,6 +449,12 @@ impl SelectEdgeQuery
|
|
|
335
449
|
&& self.destination.select_all
|
|
336
450
|
}
|
|
337
451
|
|
|
452
|
+
// expose source selector for callers that need start nodes (e.g., recursive traversals)
|
|
453
|
+
pub(crate) fn source_query(&self) -> SelectNodeQuery
|
|
454
|
+
{
|
|
455
|
+
self.source.clone()
|
|
456
|
+
}
|
|
457
|
+
|
|
338
458
|
pub(crate) fn select_all() -> Self
|
|
339
459
|
{
|
|
340
460
|
Self {
|
|
@@ -405,7 +525,7 @@ impl SelectEdgeQuery
|
|
|
405
525
|
destination: destination_query,
|
|
406
526
|
}
|
|
407
527
|
}
|
|
408
|
-
pub(crate) fn is_match(&self, edge: &graph::
|
|
528
|
+
pub(crate) fn is_match(&self, edge: &graph::SinglePath) -> bool
|
|
409
529
|
{
|
|
410
530
|
if let Some(keys) = &self.keys
|
|
411
531
|
&& !keys.iter().any(|x| edge.key() == *x)
|
|
@@ -1,6 +1,12 @@
|
|
|
1
1
|
use crate::{
|
|
2
2
|
compiler::compile,
|
|
3
|
-
interpreter::
|
|
3
|
+
interpreter::{
|
|
4
|
+
Program,
|
|
5
|
+
instructions::{
|
|
6
|
+
Block, BlockMatch, Instruction, Modifiers, OrderBy, RWExpression, VariablesSizes,
|
|
7
|
+
},
|
|
8
|
+
},
|
|
9
|
+
planner::{self, LogicalPlan, ProjectionKind},
|
|
4
10
|
prelude::*,
|
|
5
11
|
tests::templates::{ast, programs},
|
|
6
12
|
};
|
|
@@ -10,12 +16,28 @@ fn compare_program(actual: Program, expected: Program)
|
|
|
10
16
|
assert_eq!(format!("{:?}", actual), format!("{:?}", expected));
|
|
11
17
|
}
|
|
12
18
|
|
|
19
|
+
fn compile_statements(
|
|
20
|
+
function_manager: &functions::Manager,
|
|
21
|
+
statements: gqlparser::oc::ast::Statements,
|
|
22
|
+
) -> Program
|
|
23
|
+
{
|
|
24
|
+
compile(
|
|
25
|
+
function_manager,
|
|
26
|
+
statements
|
|
27
|
+
.into_iter()
|
|
28
|
+
.map(planner::plan)
|
|
29
|
+
.collect::<Result<Vec<_>, _>>()
|
|
30
|
+
.unwrap(),
|
|
31
|
+
)
|
|
32
|
+
.unwrap()
|
|
33
|
+
}
|
|
34
|
+
|
|
13
35
|
#[test]
|
|
14
36
|
fn test_compile_simple_create_node()
|
|
15
37
|
{
|
|
16
38
|
let function_manager = functions::Manager::new();
|
|
17
39
|
|
|
18
|
-
let program =
|
|
40
|
+
let program = compile_statements(&function_manager, ast::simple_create_node());
|
|
19
41
|
compare_program(program, programs::simple_create())
|
|
20
42
|
}
|
|
21
43
|
|
|
@@ -24,7 +46,7 @@ fn test_compile_create_named_node()
|
|
|
24
46
|
{
|
|
25
47
|
let function_manager = functions::Manager::new();
|
|
26
48
|
|
|
27
|
-
let program =
|
|
49
|
+
let program = compile_statements(&function_manager, ast::create_named_node());
|
|
28
50
|
compare_program(program, programs::create_named_node())
|
|
29
51
|
}
|
|
30
52
|
|
|
@@ -33,7 +55,7 @@ fn test_compile_create_named_node_double_return()
|
|
|
33
55
|
{
|
|
34
56
|
let function_manager = functions::Manager::new();
|
|
35
57
|
|
|
36
|
-
let program =
|
|
58
|
+
let program = compile_statements(&function_manager, ast::create_named_node_double_return());
|
|
37
59
|
compare_program(program, programs::create_named_node_double_return())
|
|
38
60
|
}
|
|
39
61
|
|
|
@@ -42,7 +64,7 @@ fn test_compile_double_with_return()
|
|
|
42
64
|
{
|
|
43
65
|
let function_manager = functions::Manager::new();
|
|
44
66
|
|
|
45
|
-
let program =
|
|
67
|
+
let program = compile_statements(&function_manager, ast::double_with_return());
|
|
46
68
|
compare_program(program, programs::double_with_return())
|
|
47
69
|
}
|
|
48
70
|
|
|
@@ -51,7 +73,7 @@ fn test_compile_unwind()
|
|
|
51
73
|
{
|
|
52
74
|
let function_manager = functions::Manager::new();
|
|
53
75
|
|
|
54
|
-
let program =
|
|
76
|
+
let program = compile_statements(&function_manager, ast::unwind());
|
|
55
77
|
compare_program(program, programs::unwind())
|
|
56
78
|
}
|
|
57
79
|
|
|
@@ -60,7 +82,7 @@ fn test_compile_match_loop()
|
|
|
60
82
|
{
|
|
61
83
|
let function_manager = functions::Manager::new();
|
|
62
84
|
|
|
63
|
-
let program =
|
|
85
|
+
let program = compile_statements(&function_manager, ast::match_loop());
|
|
64
86
|
compare_program(program, programs::match_loop())
|
|
65
87
|
}
|
|
66
88
|
|
|
@@ -69,7 +91,7 @@ fn test_compile_optional_match()
|
|
|
69
91
|
{
|
|
70
92
|
let function_manager = functions::Manager::new();
|
|
71
93
|
|
|
72
|
-
let program =
|
|
94
|
+
let program = compile_statements(&function_manager, ast::optional_match());
|
|
73
95
|
compare_program(program, programs::optional_match())
|
|
74
96
|
}
|
|
75
97
|
|
|
@@ -78,7 +100,7 @@ fn test_compile_match_count()
|
|
|
78
100
|
{
|
|
79
101
|
let function_manager = functions::Manager::new();
|
|
80
102
|
|
|
81
|
-
let program =
|
|
103
|
+
let program = compile_statements(&function_manager, ast::match_count());
|
|
82
104
|
compare_program(program, programs::match_count(&function_manager))
|
|
83
105
|
}
|
|
84
106
|
|
|
@@ -87,6 +109,181 @@ fn test_compile_aggregation()
|
|
|
87
109
|
{
|
|
88
110
|
let function_manager = functions::Manager::new();
|
|
89
111
|
|
|
90
|
-
let program =
|
|
112
|
+
let program = compile_statements(&function_manager, ast::aggregation());
|
|
91
113
|
compare_program(program, programs::aggregation(&function_manager))
|
|
92
114
|
}
|
|
115
|
+
|
|
116
|
+
#[test]
|
|
117
|
+
fn test_compile_logicalplan_nodescan_filter()
|
|
118
|
+
{
|
|
119
|
+
let function_manager = functions::Manager::new();
|
|
120
|
+
let identifiers = gqlparser::oc::ast::VariableIdentifiers::default();
|
|
121
|
+
let node_identifier = identifiers.create_variable_from_name("n");
|
|
122
|
+
|
|
123
|
+
let program = compile(
|
|
124
|
+
&function_manager,
|
|
125
|
+
vec![LogicalPlan::Filter {
|
|
126
|
+
source: Box::new(LogicalPlan::NodeScan {
|
|
127
|
+
patterns: vec![gqlparser::oc::ast::Pattern::Node(
|
|
128
|
+
gqlparser::oc::ast::NodePattern {
|
|
129
|
+
variable: Some(node_identifier.clone()),
|
|
130
|
+
labels: gqlparser::oc::ast::LabelExpression::None,
|
|
131
|
+
properties: None,
|
|
132
|
+
},
|
|
133
|
+
)],
|
|
134
|
+
optional: false,
|
|
135
|
+
}),
|
|
136
|
+
expression: gqlparser::oc::ast::Variable {
|
|
137
|
+
identifier: node_identifier,
|
|
138
|
+
}
|
|
139
|
+
.into(),
|
|
140
|
+
}],
|
|
141
|
+
)
|
|
142
|
+
.unwrap();
|
|
143
|
+
|
|
144
|
+
compare_program(
|
|
145
|
+
program,
|
|
146
|
+
vec![Block::Match {
|
|
147
|
+
blocks: vec![BlockMatch::MatchNode {
|
|
148
|
+
instructions: vec![
|
|
149
|
+
Instruction::Push {
|
|
150
|
+
value: crate::ValueMap::default().into(),
|
|
151
|
+
},
|
|
152
|
+
Instruction::CreateNodeQuery { labels: vec![] },
|
|
153
|
+
],
|
|
154
|
+
variable: Some(0),
|
|
155
|
+
filter: vec![],
|
|
156
|
+
}],
|
|
157
|
+
filter: vec![Instruction::GetVariable { col_id: 0 }],
|
|
158
|
+
optional: false,
|
|
159
|
+
variables_size: VariablesSizes {
|
|
160
|
+
persistent_variables: 1,
|
|
161
|
+
temporary_variables: 0,
|
|
162
|
+
},
|
|
163
|
+
}],
|
|
164
|
+
);
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
#[test]
|
|
168
|
+
fn test_compile_logicalplan_projection_modifiers()
|
|
169
|
+
{
|
|
170
|
+
let function_manager = functions::Manager::new();
|
|
171
|
+
let identifiers = gqlparser::oc::ast::VariableIdentifiers::default();
|
|
172
|
+
let item_identifier = identifiers.create_variable_from_name("i");
|
|
173
|
+
|
|
174
|
+
let program = compile(
|
|
175
|
+
&function_manager,
|
|
176
|
+
vec![
|
|
177
|
+
LogicalPlan::Unwind {
|
|
178
|
+
identifier: item_identifier.clone(),
|
|
179
|
+
expression: gqlparser::oc::ast::Array {
|
|
180
|
+
array: vec![gqlparser::oc::ast::Value { value: 0.into() }.into()],
|
|
181
|
+
}
|
|
182
|
+
.into(),
|
|
183
|
+
},
|
|
184
|
+
LogicalPlan::Limit {
|
|
185
|
+
source: Box::new(LogicalPlan::Skip {
|
|
186
|
+
source: Box::new(LogicalPlan::Sort {
|
|
187
|
+
source: Box::new(LogicalPlan::Filter {
|
|
188
|
+
source: Box::new(LogicalPlan::Projection {
|
|
189
|
+
kind: ProjectionKind::Return,
|
|
190
|
+
all: false,
|
|
191
|
+
expressions: vec![gqlparser::oc::ast::NamedExpression {
|
|
192
|
+
identifier: item_identifier.clone(),
|
|
193
|
+
expression: gqlparser::oc::ast::Variable {
|
|
194
|
+
identifier: item_identifier.clone(),
|
|
195
|
+
}
|
|
196
|
+
.into(),
|
|
197
|
+
}],
|
|
198
|
+
}),
|
|
199
|
+
expression: gqlparser::oc::ast::Variable {
|
|
200
|
+
identifier: item_identifier.clone(),
|
|
201
|
+
}
|
|
202
|
+
.into(),
|
|
203
|
+
}),
|
|
204
|
+
expressions: vec![gqlparser::oc::ast::OrderByExpression {
|
|
205
|
+
asc: false,
|
|
206
|
+
expression: gqlparser::oc::ast::Variable {
|
|
207
|
+
identifier: item_identifier.clone(),
|
|
208
|
+
}
|
|
209
|
+
.into(),
|
|
210
|
+
}],
|
|
211
|
+
}),
|
|
212
|
+
expression: gqlparser::oc::ast::Value { value: 0.into() }.into(),
|
|
213
|
+
}),
|
|
214
|
+
expression: gqlparser::oc::ast::Value { value: 1.into() }.into(),
|
|
215
|
+
},
|
|
216
|
+
],
|
|
217
|
+
)
|
|
218
|
+
.unwrap();
|
|
219
|
+
|
|
220
|
+
compare_program(
|
|
221
|
+
program,
|
|
222
|
+
vec![
|
|
223
|
+
Block::Unwind {
|
|
224
|
+
col_id: 0,
|
|
225
|
+
instructions: vec![
|
|
226
|
+
Instruction::Push { value: 0.into() },
|
|
227
|
+
Instruction::CreateArray { length: 1 },
|
|
228
|
+
],
|
|
229
|
+
variables_size: VariablesSizes {
|
|
230
|
+
persistent_variables: 1,
|
|
231
|
+
temporary_variables: 0,
|
|
232
|
+
},
|
|
233
|
+
},
|
|
234
|
+
Block::Return {
|
|
235
|
+
variables: vec![(
|
|
236
|
+
"i".into(),
|
|
237
|
+
RWExpression {
|
|
238
|
+
col_id: 0,
|
|
239
|
+
instructions: vec![Instruction::GetVariable { col_id: 0 }],
|
|
240
|
+
aggregations: vec![],
|
|
241
|
+
},
|
|
242
|
+
)],
|
|
243
|
+
filter: vec![Instruction::GetVariable { col_id: 0 }],
|
|
244
|
+
modifiers: Modifiers {
|
|
245
|
+
limit: Some(vec![Instruction::Push { value: 1.into() }]),
|
|
246
|
+
skip: Some(vec![Instruction::Push { value: 0.into() }]),
|
|
247
|
+
order_by: vec![OrderBy {
|
|
248
|
+
asc: false,
|
|
249
|
+
instructions: vec![Instruction::GetVariable { col_id: 0 }],
|
|
250
|
+
}],
|
|
251
|
+
},
|
|
252
|
+
variables_sizes: VariablesSizes {
|
|
253
|
+
persistent_variables: 1,
|
|
254
|
+
temporary_variables: 0,
|
|
255
|
+
},
|
|
256
|
+
},
|
|
257
|
+
],
|
|
258
|
+
);
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
#[test]
|
|
262
|
+
fn test_compile_match_with_vector_search()
|
|
263
|
+
{
|
|
264
|
+
let function_manager = functions::Manager::new();
|
|
265
|
+
let program = compile_statements(&function_manager, ast::match_with_vector_search("idx"));
|
|
266
|
+
// At this stage (pre-WI-3), Block::VectorSearch still carries `graph`.
|
|
267
|
+
// This test only asserts compilation succeeds without error and produces
|
|
268
|
+
// a VectorSearch block referencing the correct index name -- it does not
|
|
269
|
+
// assert on the (currently wrong, always "default") graph value, since
|
|
270
|
+
// that assertion belongs to the evaluator-level regression test below,
|
|
271
|
+
// where the wrongness is actually observable as a behavioral failure.
|
|
272
|
+
let has_vector_search_block = format!("{:?}", program).contains("VectorSearch");
|
|
273
|
+
assert!(
|
|
274
|
+
has_vector_search_block,
|
|
275
|
+
"expected a VectorSearch block in the compiled program"
|
|
276
|
+
);
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
#[test]
|
|
280
|
+
fn test_compile_multiple_vector_indexes_produces_distinct_instructions()
|
|
281
|
+
{
|
|
282
|
+
let function_manager = functions::Manager::new();
|
|
283
|
+
let mut statements = ast::create_vector_index("idx1");
|
|
284
|
+
statements.extend(ast::create_vector_index("idx2"));
|
|
285
|
+
let program = compile_statements(&function_manager, statements);
|
|
286
|
+
let debug = format!("{:?}", program);
|
|
287
|
+
assert!(debug.contains("\"idx1\""));
|
|
288
|
+
assert!(debug.contains("\"idx2\""));
|
|
289
|
+
}
|
|
@@ -1,8 +1,43 @@
|
|
|
1
1
|
#[test]
|
|
2
|
-
fn
|
|
2
|
+
fn test_create_match_return()
|
|
3
3
|
{
|
|
4
4
|
let db = crate::tests::postgres::create_tmp_db(10);
|
|
5
|
-
let builder = crate::Connection::builder()
|
|
5
|
+
let builder = crate::Connection::builder()
|
|
6
|
+
.set_option("url", db.connection_uri())
|
|
7
|
+
.backend(crate::Backend::Postgres);
|
|
6
8
|
let connection = builder.create().unwrap();
|
|
7
|
-
super::
|
|
9
|
+
super::test_create_match_return(connection);
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
#[test]
|
|
13
|
+
fn test_create_return()
|
|
14
|
+
{
|
|
15
|
+
let db = crate::tests::postgres::create_tmp_db(11);
|
|
16
|
+
let builder = crate::Connection::builder()
|
|
17
|
+
.set_option("url", db.connection_uri())
|
|
18
|
+
.backend(crate::Backend::Postgres);
|
|
19
|
+
let connection = builder.create().unwrap();
|
|
20
|
+
super::test_create_return(connection);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
#[test]
|
|
24
|
+
fn test_create_edge_return()
|
|
25
|
+
{
|
|
26
|
+
let db = crate::tests::postgres::create_tmp_db(12);
|
|
27
|
+
let builder = crate::Connection::builder()
|
|
28
|
+
.set_option("url", db.connection_uri())
|
|
29
|
+
.backend(crate::Backend::Postgres);
|
|
30
|
+
let connection = builder.create().unwrap();
|
|
31
|
+
super::test_create_edge_return(connection);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
#[test]
|
|
35
|
+
fn test_vector_search_dimension_mismatch()
|
|
36
|
+
{
|
|
37
|
+
let db = crate::tests::postgres::create_tmp_db(13);
|
|
38
|
+
let builder = crate::Connection::builder()
|
|
39
|
+
.set_option("url", db.connection_uri())
|
|
40
|
+
.backend(crate::Backend::Postgres);
|
|
41
|
+
let connection = builder.create().unwrap();
|
|
42
|
+
super::test_vector_search_dimension_mismatch(connection);
|
|
8
43
|
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
#[test]
|
|
2
|
+
fn test_create_match_return()
|
|
3
|
+
{
|
|
4
|
+
let builder = crate::Connection::builder().backend(crate::Backend::Redb);
|
|
5
|
+
let connection = builder.create().unwrap();
|
|
6
|
+
super::test_create_match_return(connection);
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
#[test]
|
|
10
|
+
fn test_create_return()
|
|
11
|
+
{
|
|
12
|
+
let builder = crate::Connection::builder().backend(crate::Backend::Redb);
|
|
13
|
+
let connection = builder.create().unwrap();
|
|
14
|
+
super::test_create_return(connection);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
#[test]
|
|
18
|
+
fn test_create_edge_return()
|
|
19
|
+
{
|
|
20
|
+
let builder = crate::Connection::builder().backend(crate::Backend::Redb);
|
|
21
|
+
let connection = builder.create().unwrap();
|
|
22
|
+
super::test_create_edge_return(connection);
|
|
23
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
#[test]
|
|
2
|
+
fn test_create_match_return()
|
|
3
|
+
{
|
|
4
|
+
let builder = crate::Connection::builder().backend(crate::Backend::SQLite);
|
|
5
|
+
let connection = builder.create().unwrap();
|
|
6
|
+
super::test_create_match_return(connection);
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
#[test]
|
|
10
|
+
fn test_create_return()
|
|
11
|
+
{
|
|
12
|
+
let builder = crate::Connection::builder().backend(crate::Backend::SQLite);
|
|
13
|
+
let connection = builder.create().unwrap();
|
|
14
|
+
super::test_create_return(connection);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
#[test]
|
|
18
|
+
fn test_create_edge_return()
|
|
19
|
+
{
|
|
20
|
+
let builder = crate::Connection::builder().backend(crate::Backend::SQLite);
|
|
21
|
+
let connection = builder.create().unwrap();
|
|
22
|
+
super::test_create_edge_return(connection);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
#[test]
|
|
26
|
+
fn test_vector_search_dimension_mismatch()
|
|
27
|
+
{
|
|
28
|
+
let builder = crate::Connection::builder().backend(crate::Backend::SQLite);
|
|
29
|
+
let connection = builder.create().unwrap();
|
|
30
|
+
super::test_vector_search_dimension_mismatch(connection);
|
|
31
|
+
}
|
|
@@ -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
|
+
}
|