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,9 +1,11 @@
|
|
|
1
1
|
use std::collections::HashMap;
|
|
2
2
|
|
|
3
|
+
use rustc_hash::FxHashSet;
|
|
4
|
+
|
|
3
5
|
use crate::{
|
|
4
6
|
interpreter::instructions::{Block, VariablesSizes},
|
|
5
7
|
prelude::*,
|
|
6
|
-
store::TransactionBoxable,
|
|
8
|
+
store::{SelectNodeQuery, TransactionBoxable},
|
|
7
9
|
value_table::{MutableRowInterface, Row, RowInterface},
|
|
8
10
|
};
|
|
9
11
|
use interpreter::instructions;
|
|
@@ -60,6 +62,7 @@ try_into_gv_impl! {f64}
|
|
|
60
62
|
try_into_gv_impl! {String}
|
|
61
63
|
try_into_gv_impl! {graph::Node}
|
|
62
64
|
try_into_gv_impl! {graph::Edge}
|
|
65
|
+
try_into_gv_impl! {value::Tensor}
|
|
63
66
|
|
|
64
67
|
macro_rules! try_into_impl {
|
|
65
68
|
($typename:tt, $type:ty, $errorname:tt) => {
|
|
@@ -357,11 +360,14 @@ fn eval_instructions(
|
|
|
357
360
|
let props: value::Value = stack.try_pop_into()?;
|
|
358
361
|
let dst: graph::Node = stack.try_pop_into()?;
|
|
359
362
|
let src: graph::Node = stack.try_pop_into()?;
|
|
360
|
-
stack.push(crate::graph::
|
|
361
|
-
graph::Key::default(),
|
|
363
|
+
stack.push(crate::graph::SinglePath::new(
|
|
362
364
|
src,
|
|
363
|
-
|
|
364
|
-
|
|
365
|
+
graphcore::Edge::new(
|
|
366
|
+
None,
|
|
367
|
+
graph::Key::default(),
|
|
368
|
+
labels.to_owned(),
|
|
369
|
+
props.into_map(),
|
|
370
|
+
),
|
|
365
371
|
dst,
|
|
366
372
|
));
|
|
367
373
|
}
|
|
@@ -369,6 +375,7 @@ fn eval_instructions(
|
|
|
369
375
|
{
|
|
370
376
|
let props: value::Value = stack.try_pop_into()?;
|
|
371
377
|
stack.push(crate::graph::Node::new(
|
|
378
|
+
None,
|
|
372
379
|
crate::graph::Key::default(),
|
|
373
380
|
labels.clone(),
|
|
374
381
|
props.into_map(),
|
|
@@ -865,7 +872,7 @@ pub(crate) fn eval_update_property<TStore: store::Store>(
|
|
|
865
872
|
}
|
|
866
873
|
value::Value::Null =>
|
|
867
874
|
{}
|
|
868
|
-
_ => Err(
|
|
875
|
+
_ => Err(RunTimeError::ExpectedEdge {
|
|
869
876
|
context: "evaluator/eval_program",
|
|
870
877
|
})?,
|
|
871
878
|
}
|
|
@@ -1006,10 +1013,8 @@ fn compute_return_with_table(
|
|
|
1006
1013
|
for (row, aggregations_states) in aggregation_table
|
|
1007
1014
|
{
|
|
1008
1015
|
let mut out_row = value_table::Row::new(Default::default(), variables_sizes.total_size());
|
|
1009
|
-
for (idx, (rw_expr, aggregation_states)) in
|
|
1010
|
-
.iter()
|
|
1011
|
-
.zip(aggregations_states.into_iter())
|
|
1012
|
-
.enumerate()
|
|
1016
|
+
for (idx, (rw_expr, aggregation_states)) in
|
|
1017
|
+
variables.iter().zip(aggregations_states).enumerate()
|
|
1013
1018
|
{
|
|
1014
1019
|
if rw_expr.aggregations.is_empty()
|
|
1015
1020
|
{
|
|
@@ -1167,6 +1172,378 @@ fn filter_rows(
|
|
|
1167
1172
|
.collect()
|
|
1168
1173
|
}
|
|
1169
1174
|
|
|
1175
|
+
/// Executes a single `BlockMatch` (node or edge) against every row in `current_rows`,
|
|
1176
|
+
/// appending matching rows into `new_rows`.
|
|
1177
|
+
///
|
|
1178
|
+
/// `allow_recursive` controls whether a recursive edge range pattern is permitted.
|
|
1179
|
+
/// Pass `true` for `Block::Match` and `false` for `Block::Merge` — the latter
|
|
1180
|
+
/// returns `RunTimeError::UnsupportedRecursivePatternInMerge` when a recursive
|
|
1181
|
+
/// range is encountered.
|
|
1182
|
+
#[allow(clippy::too_many_arguments)]
|
|
1183
|
+
fn execute_block_match<TStore: store::Store>(
|
|
1184
|
+
block: &instructions::BlockMatch,
|
|
1185
|
+
store: &TStore,
|
|
1186
|
+
tx: &mut TStore::TransactionBox,
|
|
1187
|
+
graph_name: &str,
|
|
1188
|
+
stack: &mut Stack,
|
|
1189
|
+
parameters: &crate::value::ValueMap,
|
|
1190
|
+
current_rows: Vec<value_table::Row>,
|
|
1191
|
+
allow_recursive: bool,
|
|
1192
|
+
) -> Result<Vec<value_table::Row>>
|
|
1193
|
+
{
|
|
1194
|
+
let mut new_rows = Vec::<value_table::Row>::default();
|
|
1195
|
+
|
|
1196
|
+
for row in current_rows
|
|
1197
|
+
{
|
|
1198
|
+
match block
|
|
1199
|
+
{
|
|
1200
|
+
instructions::BlockMatch::MatchNode {
|
|
1201
|
+
instructions,
|
|
1202
|
+
variable,
|
|
1203
|
+
filter,
|
|
1204
|
+
} =>
|
|
1205
|
+
{
|
|
1206
|
+
eval_instructions(stack, &row, instructions, parameters)?;
|
|
1207
|
+
let query: store::SelectNodeQuery = stack.try_pop_into()?;
|
|
1208
|
+
let nodes = store.select_nodes(tx, graph_name, query)?;
|
|
1209
|
+
|
|
1210
|
+
for node in nodes.into_iter()
|
|
1211
|
+
{
|
|
1212
|
+
let mut new_row = row.clone();
|
|
1213
|
+
if let Some(variable) = variable
|
|
1214
|
+
{
|
|
1215
|
+
new_row.set_if_unset(*variable, node.to_owned().into())?;
|
|
1216
|
+
}
|
|
1217
|
+
let should_add_row = if filter.is_empty()
|
|
1218
|
+
{
|
|
1219
|
+
true
|
|
1220
|
+
}
|
|
1221
|
+
else
|
|
1222
|
+
{
|
|
1223
|
+
let mut stack = Stack::default();
|
|
1224
|
+
stack.push(true);
|
|
1225
|
+
stack.push(node);
|
|
1226
|
+
eval_instructions(&mut stack, &new_row, filter, parameters)?;
|
|
1227
|
+
stack.try_pop()?; // discard the sentinel
|
|
1228
|
+
stack.try_pop_into()?
|
|
1229
|
+
};
|
|
1230
|
+
if should_add_row
|
|
1231
|
+
{
|
|
1232
|
+
new_rows.push(new_row);
|
|
1233
|
+
}
|
|
1234
|
+
}
|
|
1235
|
+
}
|
|
1236
|
+
|
|
1237
|
+
instructions::BlockMatch::MatchEdge {
|
|
1238
|
+
instructions,
|
|
1239
|
+
left_variable,
|
|
1240
|
+
edge_variable,
|
|
1241
|
+
right_variable,
|
|
1242
|
+
path_variable,
|
|
1243
|
+
filter,
|
|
1244
|
+
directivity,
|
|
1245
|
+
recursive_range,
|
|
1246
|
+
} =>
|
|
1247
|
+
{
|
|
1248
|
+
eval_instructions(stack, &row, instructions, parameters)?;
|
|
1249
|
+
let query = stack.try_pop_into()?;
|
|
1250
|
+
|
|
1251
|
+
match recursive_range
|
|
1252
|
+
{
|
|
1253
|
+
None =>
|
|
1254
|
+
{
|
|
1255
|
+
let edges = store.select_edges(tx, graph_name, query, *directivity)?;
|
|
1256
|
+
|
|
1257
|
+
for edge in edges.into_iter()
|
|
1258
|
+
{
|
|
1259
|
+
let mut new_row = row.clone();
|
|
1260
|
+
let (left, right) = if edge.reversed
|
|
1261
|
+
{
|
|
1262
|
+
(edge.path.destination(), edge.path.source())
|
|
1263
|
+
}
|
|
1264
|
+
else
|
|
1265
|
+
{
|
|
1266
|
+
(edge.path.source(), edge.path.destination())
|
|
1267
|
+
};
|
|
1268
|
+
|
|
1269
|
+
if let Some(left_variable) = left_variable
|
|
1270
|
+
{
|
|
1271
|
+
new_row.set(*left_variable, left.to_owned().into())?;
|
|
1272
|
+
}
|
|
1273
|
+
if let Some(right_variable) = right_variable
|
|
1274
|
+
{
|
|
1275
|
+
new_row.set(*right_variable, right.to_owned().into())?;
|
|
1276
|
+
}
|
|
1277
|
+
if let Some(edge_variable) = edge_variable
|
|
1278
|
+
{
|
|
1279
|
+
new_row.set(*edge_variable, edge.path.to_edge().into())?;
|
|
1280
|
+
}
|
|
1281
|
+
if let Some(path_variable) = path_variable
|
|
1282
|
+
{
|
|
1283
|
+
new_row.set(*path_variable, edge.path.to_owned().into())?;
|
|
1284
|
+
}
|
|
1285
|
+
|
|
1286
|
+
let should_add_row = if filter.is_empty()
|
|
1287
|
+
{
|
|
1288
|
+
true
|
|
1289
|
+
}
|
|
1290
|
+
else
|
|
1291
|
+
{
|
|
1292
|
+
let mut stack = Stack::default();
|
|
1293
|
+
stack.push(true);
|
|
1294
|
+
stack.push(edge.path.to_edge());
|
|
1295
|
+
eval_instructions(&mut stack, &new_row, filter, parameters)?;
|
|
1296
|
+
stack.try_pop()?;
|
|
1297
|
+
stack.try_pop_into()?
|
|
1298
|
+
};
|
|
1299
|
+
|
|
1300
|
+
if should_add_row
|
|
1301
|
+
{
|
|
1302
|
+
new_rows.push(new_row);
|
|
1303
|
+
}
|
|
1304
|
+
}
|
|
1305
|
+
}
|
|
1306
|
+
|
|
1307
|
+
Some(recursive_range) =>
|
|
1308
|
+
{
|
|
1309
|
+
if !allow_recursive
|
|
1310
|
+
{
|
|
1311
|
+
return Err(InternalError::UnsupportedRecursivePatternInMerge.into());
|
|
1312
|
+
}
|
|
1313
|
+
|
|
1314
|
+
// Recursive DFS
|
|
1315
|
+
let min_hops = recursive_range.start;
|
|
1316
|
+
let max_hops = recursive_range.end;
|
|
1317
|
+
let capacity = max_hops.min(100);
|
|
1318
|
+
|
|
1319
|
+
let bound_dest_key: Option<graph::Key> = if let Some(right_col) = right_variable
|
|
1320
|
+
{
|
|
1321
|
+
match row.get(*right_col)?
|
|
1322
|
+
{
|
|
1323
|
+
value::Value::Node(node) => Some(node.key()),
|
|
1324
|
+
_ => None,
|
|
1325
|
+
}
|
|
1326
|
+
}
|
|
1327
|
+
else
|
|
1328
|
+
{
|
|
1329
|
+
None
|
|
1330
|
+
};
|
|
1331
|
+
|
|
1332
|
+
let start_nodes: Vec<graph::Node> = {
|
|
1333
|
+
let source_query = query.source_query();
|
|
1334
|
+
store.select_nodes(tx, graph_name, source_query)?
|
|
1335
|
+
};
|
|
1336
|
+
|
|
1337
|
+
struct Frame
|
|
1338
|
+
{
|
|
1339
|
+
node: graph::Node,
|
|
1340
|
+
edges: Vec<store::EdgeResult>,
|
|
1341
|
+
edge_index: usize,
|
|
1342
|
+
depth: usize,
|
|
1343
|
+
}
|
|
1344
|
+
|
|
1345
|
+
for start_node in start_nodes
|
|
1346
|
+
{
|
|
1347
|
+
let mut path_edges: Vec<graph::Edge> = Vec::with_capacity(capacity);
|
|
1348
|
+
let mut used_edges: FxHashSet<graph::Key> =
|
|
1349
|
+
FxHashSet::with_capacity_and_hasher(capacity, Default::default());
|
|
1350
|
+
let mut dedup_set: FxHashSet<(Option<graph::Key>, Option<graph::Key>, usize)> =
|
|
1351
|
+
FxHashSet::with_capacity_and_hasher(capacity, Default::default());
|
|
1352
|
+
|
|
1353
|
+
let initial_edges = {
|
|
1354
|
+
let q = store::SelectEdgeQuery::select_source_keys(
|
|
1355
|
+
store::SelectNodeQuery::select_keys(vec![start_node.key()]),
|
|
1356
|
+
);
|
|
1357
|
+
store.select_edges(tx, graph_name, q, *directivity)?
|
|
1358
|
+
};
|
|
1359
|
+
|
|
1360
|
+
let mut dfs_stack: Vec<Frame> = vec![Frame {
|
|
1361
|
+
node: start_node.clone(),
|
|
1362
|
+
edges: initial_edges,
|
|
1363
|
+
edge_index: 0,
|
|
1364
|
+
depth: 0,
|
|
1365
|
+
}];
|
|
1366
|
+
|
|
1367
|
+
while !dfs_stack.is_empty()
|
|
1368
|
+
{
|
|
1369
|
+
let mut push_next: Option<Frame> = None;
|
|
1370
|
+
let mut should_pop = false;
|
|
1371
|
+
|
|
1372
|
+
let dfs_intermediate_nodes: Vec<graph::Node> = if dfs_stack.len() >= 2
|
|
1373
|
+
{
|
|
1374
|
+
dfs_stack[1..dfs_stack.len() - 1]
|
|
1375
|
+
.iter()
|
|
1376
|
+
.map(|f| f.node.clone())
|
|
1377
|
+
.collect()
|
|
1378
|
+
}
|
|
1379
|
+
else
|
|
1380
|
+
{
|
|
1381
|
+
Vec::new()
|
|
1382
|
+
};
|
|
1383
|
+
|
|
1384
|
+
{
|
|
1385
|
+
let frame = dfs_stack.last_mut().unwrap();
|
|
1386
|
+
|
|
1387
|
+
// Emit row
|
|
1388
|
+
if frame.edge_index == 0 && frame.depth >= min_hops
|
|
1389
|
+
{
|
|
1390
|
+
let current_node = &frame.node;
|
|
1391
|
+
|
|
1392
|
+
let mut new_row = row.clone();
|
|
1393
|
+
|
|
1394
|
+
if let Some(left_var) = left_variable
|
|
1395
|
+
{
|
|
1396
|
+
new_row.set(*left_var, start_node.clone().into())?;
|
|
1397
|
+
}
|
|
1398
|
+
if let Some(right_var) = right_variable
|
|
1399
|
+
{
|
|
1400
|
+
new_row.set(*right_var, current_node.clone().into())?;
|
|
1401
|
+
}
|
|
1402
|
+
if let Some(edge_var) = edge_variable
|
|
1403
|
+
&& let Some(last_edge) = path_edges.last()
|
|
1404
|
+
{
|
|
1405
|
+
new_row.set(*edge_var, last_edge.clone().into())?;
|
|
1406
|
+
}
|
|
1407
|
+
if let Some(path_var) = path_variable
|
|
1408
|
+
{
|
|
1409
|
+
let path = graph::Path::new(
|
|
1410
|
+
start_node.clone(),
|
|
1411
|
+
path_edges
|
|
1412
|
+
.clone()
|
|
1413
|
+
.into_iter()
|
|
1414
|
+
.zip(
|
|
1415
|
+
dfs_intermediate_nodes
|
|
1416
|
+
.clone()
|
|
1417
|
+
.into_iter()
|
|
1418
|
+
.chain(std::iter::once(current_node.clone())),
|
|
1419
|
+
)
|
|
1420
|
+
.collect(),
|
|
1421
|
+
);
|
|
1422
|
+
new_row.set(*path_var, path.into())?;
|
|
1423
|
+
}
|
|
1424
|
+
|
|
1425
|
+
let passes_filter = if filter.is_empty()
|
|
1426
|
+
{
|
|
1427
|
+
true
|
|
1428
|
+
}
|
|
1429
|
+
else
|
|
1430
|
+
{
|
|
1431
|
+
let mut eval_stack = Stack::default();
|
|
1432
|
+
eval_stack.push(true);
|
|
1433
|
+
if let Some(last_edge) = path_edges.last()
|
|
1434
|
+
{
|
|
1435
|
+
eval_stack.push(last_edge.clone());
|
|
1436
|
+
}
|
|
1437
|
+
eval_instructions(&mut eval_stack, &new_row, filter, parameters)?;
|
|
1438
|
+
eval_stack.try_pop()?;
|
|
1439
|
+
eval_stack.try_pop_into()?
|
|
1440
|
+
};
|
|
1441
|
+
|
|
1442
|
+
let matches_dest = if let Some(dest_key) = bound_dest_key
|
|
1443
|
+
{
|
|
1444
|
+
current_node.key() == dest_key
|
|
1445
|
+
}
|
|
1446
|
+
else
|
|
1447
|
+
{
|
|
1448
|
+
true
|
|
1449
|
+
};
|
|
1450
|
+
|
|
1451
|
+
if passes_filter && matches_dest
|
|
1452
|
+
{
|
|
1453
|
+
let left_key_opt: Option<graph::Key> = if left_variable.is_some()
|
|
1454
|
+
{
|
|
1455
|
+
Some(start_node.key())
|
|
1456
|
+
}
|
|
1457
|
+
else
|
|
1458
|
+
{
|
|
1459
|
+
None
|
|
1460
|
+
};
|
|
1461
|
+
let right_key_opt: Option<graph::Key> = Some(current_node.key());
|
|
1462
|
+
let path_len = path_edges.len();
|
|
1463
|
+
let sig = (left_key_opt, right_key_opt, path_len);
|
|
1464
|
+
|
|
1465
|
+
if !dedup_set.contains(&sig)
|
|
1466
|
+
{
|
|
1467
|
+
dedup_set.insert(sig);
|
|
1468
|
+
new_rows.push(new_row);
|
|
1469
|
+
}
|
|
1470
|
+
}
|
|
1471
|
+
}
|
|
1472
|
+
|
|
1473
|
+
// Stop for max depth
|
|
1474
|
+
if frame.depth == max_hops || frame.edge_index >= frame.edges.len()
|
|
1475
|
+
{
|
|
1476
|
+
should_pop = true;
|
|
1477
|
+
}
|
|
1478
|
+
else
|
|
1479
|
+
{
|
|
1480
|
+
// Next hop
|
|
1481
|
+
let edge_result = &frame.edges[frame.edge_index];
|
|
1482
|
+
frame.edge_index += 1;
|
|
1483
|
+
|
|
1484
|
+
let edge = edge_result.path.to_edge();
|
|
1485
|
+
let edge_key = edge.key();
|
|
1486
|
+
|
|
1487
|
+
if used_edges.contains(&edge_key)
|
|
1488
|
+
{
|
|
1489
|
+
continue;
|
|
1490
|
+
}
|
|
1491
|
+
|
|
1492
|
+
let next_node = if edge_result.reversed
|
|
1493
|
+
{
|
|
1494
|
+
edge_result.path.source().clone()
|
|
1495
|
+
}
|
|
1496
|
+
else
|
|
1497
|
+
{
|
|
1498
|
+
edge_result.path.destination().clone()
|
|
1499
|
+
};
|
|
1500
|
+
|
|
1501
|
+
used_edges.insert(edge_key);
|
|
1502
|
+
path_edges.push(edge.clone());
|
|
1503
|
+
|
|
1504
|
+
let next_edges = {
|
|
1505
|
+
let q = store::SelectEdgeQuery::select_source_keys(
|
|
1506
|
+
store::SelectNodeQuery::select_keys(vec![next_node.key()]),
|
|
1507
|
+
);
|
|
1508
|
+
store.select_edges(tx, graph_name, q, *directivity)?
|
|
1509
|
+
};
|
|
1510
|
+
|
|
1511
|
+
push_next = Some(Frame {
|
|
1512
|
+
node: next_node,
|
|
1513
|
+
edges: next_edges,
|
|
1514
|
+
edge_index: 0,
|
|
1515
|
+
depth: frame.depth + 1,
|
|
1516
|
+
});
|
|
1517
|
+
}
|
|
1518
|
+
}
|
|
1519
|
+
|
|
1520
|
+
// Mutate stack
|
|
1521
|
+
if should_pop
|
|
1522
|
+
{
|
|
1523
|
+
dfs_stack.pop();
|
|
1524
|
+
if let Some(last_edge) = path_edges.pop()
|
|
1525
|
+
{
|
|
1526
|
+
used_edges.remove(&last_edge.key());
|
|
1527
|
+
}
|
|
1528
|
+
continue;
|
|
1529
|
+
}
|
|
1530
|
+
|
|
1531
|
+
if let Some(frame) = push_next
|
|
1532
|
+
{
|
|
1533
|
+
dfs_stack.push(frame);
|
|
1534
|
+
continue;
|
|
1535
|
+
}
|
|
1536
|
+
}
|
|
1537
|
+
}
|
|
1538
|
+
}
|
|
1539
|
+
}
|
|
1540
|
+
}
|
|
1541
|
+
}
|
|
1542
|
+
}
|
|
1543
|
+
|
|
1544
|
+
Ok(new_rows)
|
|
1545
|
+
}
|
|
1546
|
+
|
|
1170
1547
|
fn is_write_program(program: &super::Program) -> bool
|
|
1171
1548
|
{
|
|
1172
1549
|
program.iter().any(|b| match b
|
|
@@ -1176,12 +1553,15 @@ fn is_write_program(program: &super::Program) -> bool
|
|
|
1176
1553
|
| Block::Return { .. }
|
|
1177
1554
|
| Block::Unwind { .. }
|
|
1178
1555
|
| Block::Call { .. }
|
|
1179
|
-
| Block::With { .. }
|
|
1556
|
+
| Block::With { .. }
|
|
1557
|
+
| Block::VectorSearch { .. } => false,
|
|
1180
1558
|
Block::CreateGraph { .. }
|
|
1181
1559
|
| Block::DropGraph { .. }
|
|
1182
1560
|
| Block::Create { .. }
|
|
1183
1561
|
| Block::Update { .. }
|
|
1184
|
-
| Block::
|
|
1562
|
+
| Block::Merge { .. }
|
|
1563
|
+
| Block::Delete { .. }
|
|
1564
|
+
| Block::CreateVectorIndex { .. } => true,
|
|
1185
1565
|
})
|
|
1186
1566
|
}
|
|
1187
1567
|
|
|
@@ -1267,17 +1647,50 @@ pub(crate) fn eval_program<TStore: store::Store>(
|
|
|
1267
1647
|
crate::value::Value::Node(n) =>
|
|
1268
1648
|
{
|
|
1269
1649
|
store.create_nodes(&mut tx, &graph_name, vec![&n])?;
|
|
1270
|
-
if let Some(var) = var
|
|
1650
|
+
if let Some(var) = var.element
|
|
1271
1651
|
{
|
|
1272
|
-
new_row.set_if_unset(
|
|
1652
|
+
new_row.set_if_unset(
|
|
1653
|
+
var,
|
|
1654
|
+
crate::value::Value::Node(n.with_graph_name(graph_name.to_owned())),
|
|
1655
|
+
)?;
|
|
1273
1656
|
}
|
|
1274
1657
|
}
|
|
1275
|
-
crate::value::Value::
|
|
1658
|
+
crate::value::Value::SinglePath(p) =>
|
|
1276
1659
|
{
|
|
1277
|
-
store.create_edges(&mut tx, &graph_name, vec![
|
|
1278
|
-
|
|
1660
|
+
store.create_edges(&mut tx, &graph_name, vec![p.as_ref()])?;
|
|
1661
|
+
|
|
1662
|
+
match (var.element, var.path)
|
|
1279
1663
|
{
|
|
1280
|
-
|
|
1664
|
+
(Some(edge_var), Some(path_var)) =>
|
|
1665
|
+
{
|
|
1666
|
+
new_row.set(
|
|
1667
|
+
edge_var,
|
|
1668
|
+
crate::value::Value::Edge(
|
|
1669
|
+
p.clone().into_edge().with_graph_name(graph_name.to_owned()),
|
|
1670
|
+
),
|
|
1671
|
+
)?;
|
|
1672
|
+
new_row.set(
|
|
1673
|
+
path_var,
|
|
1674
|
+
crate::value::Value::SinglePath(
|
|
1675
|
+
p.with_graph_name(graph_name.to_owned()).into(),
|
|
1676
|
+
),
|
|
1677
|
+
)?;
|
|
1678
|
+
}
|
|
1679
|
+
(None, Some(path_var)) =>
|
|
1680
|
+
{
|
|
1681
|
+
new_row.set(path_var, crate::value::Value::SinglePath(p))?;
|
|
1682
|
+
}
|
|
1683
|
+
(Some(edge_var), None) =>
|
|
1684
|
+
{
|
|
1685
|
+
new_row.set(
|
|
1686
|
+
edge_var,
|
|
1687
|
+
crate::value::Value::Edge(
|
|
1688
|
+
p.into_edge().with_graph_name(graph_name.to_owned()),
|
|
1689
|
+
),
|
|
1690
|
+
)?;
|
|
1691
|
+
}
|
|
1692
|
+
(None, None) =>
|
|
1693
|
+
{}
|
|
1281
1694
|
}
|
|
1282
1695
|
}
|
|
1283
1696
|
_ =>
|
|
@@ -1304,111 +1717,16 @@ pub(crate) fn eval_program<TStore: store::Store>(
|
|
|
1304
1717
|
let mut current_rows: Vec<_> = vec![row.clone().extended(variables_size.total_size())?];
|
|
1305
1718
|
for block in blocks.iter()
|
|
1306
1719
|
{
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
{
|
|
1318
|
-
eval_instructions(&mut stack, &row, instructions, parameters)?;
|
|
1319
|
-
let query: store::SelectNodeQuery = stack.try_pop_into()?;
|
|
1320
|
-
let nodes = store.select_nodes(&mut tx, &graph_name, query)?;
|
|
1321
|
-
|
|
1322
|
-
for node in nodes.into_iter()
|
|
1323
|
-
{
|
|
1324
|
-
let mut new_row = row.clone();
|
|
1325
|
-
if let Some(variable) = variable
|
|
1326
|
-
{
|
|
1327
|
-
new_row.set_if_unset(*variable, node.to_owned().into())?;
|
|
1328
|
-
}
|
|
1329
|
-
let should_add_row = if filter.is_empty()
|
|
1330
|
-
{
|
|
1331
|
-
true
|
|
1332
|
-
}
|
|
1333
|
-
else
|
|
1334
|
-
{
|
|
1335
|
-
let mut stack = Stack::default();
|
|
1336
|
-
stack.push(true);
|
|
1337
|
-
stack.push(node);
|
|
1338
|
-
eval_instructions(&mut stack, &new_row, filter, parameters)?;
|
|
1339
|
-
stack.try_pop()?; // Get rid of the edge
|
|
1340
|
-
stack.try_pop_into()?
|
|
1341
|
-
};
|
|
1342
|
-
if should_add_row
|
|
1343
|
-
{
|
|
1344
|
-
new_rows.push(new_row);
|
|
1345
|
-
}
|
|
1346
|
-
}
|
|
1347
|
-
}
|
|
1348
|
-
instructions::BlockMatch::MatchEdge {
|
|
1349
|
-
instructions,
|
|
1350
|
-
left_variable,
|
|
1351
|
-
edge_variable,
|
|
1352
|
-
right_variable,
|
|
1353
|
-
path_variable,
|
|
1354
|
-
filter,
|
|
1355
|
-
directivity,
|
|
1356
|
-
} =>
|
|
1357
|
-
{
|
|
1358
|
-
eval_instructions(&mut stack, &row, instructions, parameters)?;
|
|
1359
|
-
let query = stack.try_pop_into()?;
|
|
1360
|
-
|
|
1361
|
-
let edges = store.select_edges(&mut tx, &graph_name, query, *directivity)?;
|
|
1362
|
-
|
|
1363
|
-
for edge in edges.into_iter()
|
|
1364
|
-
{
|
|
1365
|
-
let mut new_row = row.clone();
|
|
1366
|
-
let (left, right) = if edge.reversed
|
|
1367
|
-
{
|
|
1368
|
-
(edge.path.destination(), edge.path.source())
|
|
1369
|
-
}
|
|
1370
|
-
else
|
|
1371
|
-
{
|
|
1372
|
-
(edge.path.source(), edge.path.destination())
|
|
1373
|
-
};
|
|
1374
|
-
if let Some(left_variable) = left_variable.to_owned()
|
|
1375
|
-
{
|
|
1376
|
-
new_row.set(left_variable, left.to_owned().into())?;
|
|
1377
|
-
}
|
|
1378
|
-
if let Some(right_variable) = right_variable.to_owned()
|
|
1379
|
-
{
|
|
1380
|
-
new_row.set(right_variable, right.to_owned().into())?;
|
|
1381
|
-
}
|
|
1382
|
-
if let Some(edge_variable) = edge_variable.to_owned()
|
|
1383
|
-
{
|
|
1384
|
-
new_row.set(edge_variable, edge.path.to_edge().into())?;
|
|
1385
|
-
}
|
|
1386
|
-
if let Some(path_variable) = path_variable.to_owned()
|
|
1387
|
-
{
|
|
1388
|
-
new_row.set(path_variable, edge.path.to_owned().into())?;
|
|
1389
|
-
}
|
|
1390
|
-
let should_add_row = if filter.is_empty()
|
|
1391
|
-
{
|
|
1392
|
-
true
|
|
1393
|
-
}
|
|
1394
|
-
else
|
|
1395
|
-
{
|
|
1396
|
-
let mut stack = Stack::default();
|
|
1397
|
-
stack.push(true);
|
|
1398
|
-
stack.push(edge.path.into_edge());
|
|
1399
|
-
eval_instructions(&mut stack, &new_row, filter, parameters)?;
|
|
1400
|
-
stack.try_pop()?; // Get rid of the edge
|
|
1401
|
-
stack.try_pop_into()?
|
|
1402
|
-
};
|
|
1403
|
-
if should_add_row
|
|
1404
|
-
{
|
|
1405
|
-
new_rows.push(new_row);
|
|
1406
|
-
}
|
|
1407
|
-
}
|
|
1408
|
-
}
|
|
1409
|
-
}
|
|
1410
|
-
}
|
|
1411
|
-
current_rows = new_rows;
|
|
1720
|
+
current_rows = execute_block_match(
|
|
1721
|
+
block,
|
|
1722
|
+
store,
|
|
1723
|
+
&mut tx,
|
|
1724
|
+
&graph_name,
|
|
1725
|
+
&mut stack,
|
|
1726
|
+
parameters,
|
|
1727
|
+
current_rows,
|
|
1728
|
+
true, // recursive patterns allowed in MATCH
|
|
1729
|
+
)?;
|
|
1412
1730
|
}
|
|
1413
1731
|
if !filter.is_empty()
|
|
1414
1732
|
{
|
|
@@ -1445,7 +1763,7 @@ pub(crate) fn eval_program<TStore: store::Store>(
|
|
|
1445
1763
|
let mut data = Vec::<crate::value::Value>::new();
|
|
1446
1764
|
for row in output_table.into_row_iter()
|
|
1447
1765
|
{
|
|
1448
|
-
data.extend(row
|
|
1766
|
+
data.extend(row);
|
|
1449
1767
|
}
|
|
1450
1768
|
tx.close()?;
|
|
1451
1769
|
return Ok(graphcore::Table::new(headers, data)?.into());
|
|
@@ -1517,8 +1835,8 @@ pub(crate) fn eval_program<TStore: store::Store>(
|
|
|
1517
1835
|
let value: value::Value = stack.try_pop_into()?;
|
|
1518
1836
|
match value
|
|
1519
1837
|
{
|
|
1520
|
-
value::Value::Node(node) => nodes_keys.push(node.
|
|
1521
|
-
value::Value::Edge(edge) => edges_keys.push(edge.
|
|
1838
|
+
value::Value::Node(node) => nodes_keys.push(node.key()),
|
|
1839
|
+
value::Value::Edge(edge) => edges_keys.push(edge.key()),
|
|
1522
1840
|
value::Value::Null =>
|
|
1523
1841
|
{}
|
|
1524
1842
|
_ => return Err(RunTimeError::InvalidDelete.into()),
|
|
@@ -1530,7 +1848,7 @@ pub(crate) fn eval_program<TStore: store::Store>(
|
|
|
1530
1848
|
&mut tx,
|
|
1531
1849
|
&graph_name,
|
|
1532
1850
|
store::SelectEdgeQuery::select_keys(edges_keys),
|
|
1533
|
-
|
|
1851
|
+
graphcore::EdgeDirectivity::Undirected,
|
|
1534
1852
|
)?;
|
|
1535
1853
|
store.delete_nodes(
|
|
1536
1854
|
&mut tx,
|
|
@@ -1610,7 +1928,7 @@ pub(crate) fn eval_program<TStore: store::Store>(
|
|
|
1610
1928
|
}
|
|
1611
1929
|
value::Value::Null =>
|
|
1612
1930
|
{}
|
|
1613
|
-
_ => Err(
|
|
1931
|
+
_ => Err(RunTimeError::ExpectedEdge {
|
|
1614
1932
|
context: "evaluator/eval_program",
|
|
1615
1933
|
})?,
|
|
1616
1934
|
}
|
|
@@ -1660,7 +1978,7 @@ pub(crate) fn eval_program<TStore: store::Store>(
|
|
|
1660
1978
|
}
|
|
1661
1979
|
value::Value::Null =>
|
|
1662
1980
|
{}
|
|
1663
|
-
_ => Err(
|
|
1981
|
+
_ => Err(RunTimeError::ExpectedEdge {
|
|
1664
1982
|
context: "evaluator/eval_program",
|
|
1665
1983
|
})?,
|
|
1666
1984
|
}
|
|
@@ -1671,6 +1989,383 @@ pub(crate) fn eval_program<TStore: store::Store>(
|
|
|
1671
1989
|
}
|
|
1672
1990
|
input_table = output_table;
|
|
1673
1991
|
}
|
|
1992
|
+
instructions::Block::Merge {
|
|
1993
|
+
match_blocks,
|
|
1994
|
+
match_filter,
|
|
1995
|
+
create_actions,
|
|
1996
|
+
on_create_updates,
|
|
1997
|
+
on_match_updates,
|
|
1998
|
+
variables_size,
|
|
1999
|
+
} =>
|
|
2000
|
+
{
|
|
2001
|
+
let mut output_table = value_table::ValueTable::new(variables_size.persistent_variables);
|
|
2002
|
+
|
|
2003
|
+
for row in input_table.into_row_iter()
|
|
2004
|
+
{
|
|
2005
|
+
// Step 1: Try to match the pattern using the compiled match blocks
|
|
2006
|
+
let mut current_rows: Vec<_> = vec![row.clone().extended(variables_size.total_size())?];
|
|
2007
|
+
|
|
2008
|
+
for block in match_blocks.iter()
|
|
2009
|
+
{
|
|
2010
|
+
current_rows = execute_block_match(
|
|
2011
|
+
block,
|
|
2012
|
+
store,
|
|
2013
|
+
&mut tx,
|
|
2014
|
+
&graph_name,
|
|
2015
|
+
&mut stack,
|
|
2016
|
+
parameters,
|
|
2017
|
+
current_rows,
|
|
2018
|
+
false,
|
|
2019
|
+
)?;
|
|
2020
|
+
}
|
|
2021
|
+
|
|
2022
|
+
// Apply WHERE filter
|
|
2023
|
+
let matched_rows = if !match_filter.is_empty()
|
|
2024
|
+
{
|
|
2025
|
+
filter_rows(current_rows.into_iter(), match_filter, parameters)?
|
|
2026
|
+
}
|
|
2027
|
+
else
|
|
2028
|
+
{
|
|
2029
|
+
current_rows
|
|
2030
|
+
};
|
|
2031
|
+
|
|
2032
|
+
if matched_rows.is_empty()
|
|
2033
|
+
{
|
|
2034
|
+
// Step 2: No match - create new entities
|
|
2035
|
+
let mut new_row = row.extended(variables_size.total_size())?;
|
|
2036
|
+
for action in create_actions.iter()
|
|
2037
|
+
{
|
|
2038
|
+
eval_instructions(&mut stack, &new_row, &action.instructions, parameters)?;
|
|
2039
|
+
for (v, var) in stack
|
|
2040
|
+
.try_drain_into(action.variables.len())?
|
|
2041
|
+
.into_iter()
|
|
2042
|
+
.zip(action.variables.iter())
|
|
2043
|
+
{
|
|
2044
|
+
match v
|
|
2045
|
+
{
|
|
2046
|
+
crate::value::Value::Node(n) =>
|
|
2047
|
+
{
|
|
2048
|
+
if n.properties().values().any(|v| v.is_null())
|
|
2049
|
+
{
|
|
2050
|
+
return Err(RunTimeError::MergeReadOwnWrites.into());
|
|
2051
|
+
}
|
|
2052
|
+
store.create_nodes(&mut tx, &graph_name, vec![&n])?;
|
|
2053
|
+
if let Some(var) = var.element
|
|
2054
|
+
{
|
|
2055
|
+
new_row.set_if_unset(
|
|
2056
|
+
var,
|
|
2057
|
+
crate::value::Value::Node(n.with_graph_name(graph_name.to_owned())),
|
|
2058
|
+
)?;
|
|
2059
|
+
}
|
|
2060
|
+
}
|
|
2061
|
+
crate::value::Value::SinglePath(p) =>
|
|
2062
|
+
{
|
|
2063
|
+
if p.to_edge().properties().values().any(|v| v.is_null())
|
|
2064
|
+
{
|
|
2065
|
+
return Err(RunTimeError::MergeReadOwnWrites.into());
|
|
2066
|
+
}
|
|
2067
|
+
store.create_edges(&mut tx, &graph_name, vec![p.as_ref()])?;
|
|
2068
|
+
|
|
2069
|
+
match (var.element, var.path)
|
|
2070
|
+
{
|
|
2071
|
+
(Some(edge_var), Some(path_var)) =>
|
|
2072
|
+
{
|
|
2073
|
+
new_row.set(
|
|
2074
|
+
edge_var,
|
|
2075
|
+
crate::value::Value::Edge(
|
|
2076
|
+
p.clone().into_edge().with_graph_name(graph_name.to_owned()),
|
|
2077
|
+
),
|
|
2078
|
+
)?;
|
|
2079
|
+
new_row.set(
|
|
2080
|
+
path_var,
|
|
2081
|
+
crate::value::Value::SinglePath(
|
|
2082
|
+
p.with_graph_name(graph_name.to_owned()).into(),
|
|
2083
|
+
),
|
|
2084
|
+
)?;
|
|
2085
|
+
}
|
|
2086
|
+
(None, Some(path_var)) =>
|
|
2087
|
+
{
|
|
2088
|
+
new_row.set(path_var, crate::value::Value::SinglePath(p))?;
|
|
2089
|
+
}
|
|
2090
|
+
(Some(edge_var), None) =>
|
|
2091
|
+
{
|
|
2092
|
+
new_row.set(
|
|
2093
|
+
edge_var,
|
|
2094
|
+
crate::value::Value::Edge(
|
|
2095
|
+
p.into_edge().with_graph_name(graph_name.to_owned()),
|
|
2096
|
+
),
|
|
2097
|
+
)?;
|
|
2098
|
+
}
|
|
2099
|
+
(None, None) =>
|
|
2100
|
+
{}
|
|
2101
|
+
}
|
|
2102
|
+
}
|
|
2103
|
+
_ =>
|
|
2104
|
+
{
|
|
2105
|
+
return Err(InternalError::Unimplemented("executor/eval/create").into());
|
|
2106
|
+
}
|
|
2107
|
+
}
|
|
2108
|
+
}
|
|
2109
|
+
}
|
|
2110
|
+
|
|
2111
|
+
// Apply on_create updates
|
|
2112
|
+
for update in on_create_updates.iter()
|
|
2113
|
+
{
|
|
2114
|
+
match update
|
|
2115
|
+
{
|
|
2116
|
+
instructions::UpdateOne::SetProperty {
|
|
2117
|
+
target,
|
|
2118
|
+
path,
|
|
2119
|
+
instructions,
|
|
2120
|
+
} =>
|
|
2121
|
+
{
|
|
2122
|
+
eval_update_property(
|
|
2123
|
+
store,
|
|
2124
|
+
&mut tx,
|
|
2125
|
+
&graph_name,
|
|
2126
|
+
&mut new_row,
|
|
2127
|
+
*target,
|
|
2128
|
+
path,
|
|
2129
|
+
instructions,
|
|
2130
|
+
parameters,
|
|
2131
|
+
true,
|
|
2132
|
+
)?;
|
|
2133
|
+
}
|
|
2134
|
+
instructions::UpdateOne::AddProperty {
|
|
2135
|
+
target,
|
|
2136
|
+
path,
|
|
2137
|
+
instructions,
|
|
2138
|
+
} =>
|
|
2139
|
+
{
|
|
2140
|
+
eval_update_property(
|
|
2141
|
+
store,
|
|
2142
|
+
&mut tx,
|
|
2143
|
+
&graph_name,
|
|
2144
|
+
&mut new_row,
|
|
2145
|
+
*target,
|
|
2146
|
+
path,
|
|
2147
|
+
instructions,
|
|
2148
|
+
parameters,
|
|
2149
|
+
false,
|
|
2150
|
+
)?;
|
|
2151
|
+
}
|
|
2152
|
+
instructions::UpdateOne::RemoveProperty { target, path } =>
|
|
2153
|
+
{
|
|
2154
|
+
let var = new_row.get(*target)?;
|
|
2155
|
+
let mut piter = path.iter();
|
|
2156
|
+
match var
|
|
2157
|
+
{
|
|
2158
|
+
value::Value::Node(n) =>
|
|
2159
|
+
{
|
|
2160
|
+
let mut n = n.to_owned();
|
|
2161
|
+
n.properties_mut().remove_value(piter.next(), piter)?;
|
|
2162
|
+
store.update_node(&mut tx, &graph_name, &n)?;
|
|
2163
|
+
new_row.set(*target, n.into())?;
|
|
2164
|
+
}
|
|
2165
|
+
value::Value::Edge(e) =>
|
|
2166
|
+
{
|
|
2167
|
+
let mut e = e.to_owned();
|
|
2168
|
+
e.properties_mut().remove_value(piter.next(), piter)?;
|
|
2169
|
+
store.update_edge(&mut tx, &graph_name, &e)?;
|
|
2170
|
+
new_row.set(*target, e.into())?;
|
|
2171
|
+
}
|
|
2172
|
+
value::Value::Null =>
|
|
2173
|
+
{}
|
|
2174
|
+
_ => Err(RunTimeError::ExpectedEdge {
|
|
2175
|
+
context: "evaluator/eval_program",
|
|
2176
|
+
})?,
|
|
2177
|
+
}
|
|
2178
|
+
}
|
|
2179
|
+
instructions::UpdateOne::AddLabels { target, labels }
|
|
2180
|
+
| instructions::UpdateOne::RemoveLabels { target, labels } =>
|
|
2181
|
+
{
|
|
2182
|
+
let add_labels = match update
|
|
2183
|
+
{
|
|
2184
|
+
instructions::UpdateOne::AddLabels { .. } => true,
|
|
2185
|
+
instructions::UpdateOne::RemoveLabels { .. } => false,
|
|
2186
|
+
_ => Err(InternalError::Unreachable {
|
|
2187
|
+
context: "evaluator/eval_program/add_remove_labels",
|
|
2188
|
+
})?,
|
|
2189
|
+
};
|
|
2190
|
+
|
|
2191
|
+
let var = new_row.get(*target)?;
|
|
2192
|
+
match var
|
|
2193
|
+
{
|
|
2194
|
+
value::Value::Node(n) =>
|
|
2195
|
+
{
|
|
2196
|
+
let mut n = n.to_owned();
|
|
2197
|
+
if add_labels
|
|
2198
|
+
{
|
|
2199
|
+
n.labels_mut().append(&mut labels.clone());
|
|
2200
|
+
}
|
|
2201
|
+
else
|
|
2202
|
+
{
|
|
2203
|
+
n.labels_edit(|l| l.into_iter().filter(|x| !labels.contains(x)).collect());
|
|
2204
|
+
}
|
|
2205
|
+
store.update_node(&mut tx, &graph_name, &n)?;
|
|
2206
|
+
new_row.set(*target, n.into())?;
|
|
2207
|
+
}
|
|
2208
|
+
value::Value::Edge(e) =>
|
|
2209
|
+
{
|
|
2210
|
+
let mut e = e.to_owned();
|
|
2211
|
+
if add_labels
|
|
2212
|
+
{
|
|
2213
|
+
e.labels_mut().append(&mut labels.clone());
|
|
2214
|
+
}
|
|
2215
|
+
else
|
|
2216
|
+
{
|
|
2217
|
+
e.labels_edit(|l| l.into_iter().filter(|x| !labels.contains(x)).collect());
|
|
2218
|
+
}
|
|
2219
|
+
store.update_edge(&mut tx, &graph_name, &e)?;
|
|
2220
|
+
new_row.set(*target, e.into())?;
|
|
2221
|
+
}
|
|
2222
|
+
value::Value::Null =>
|
|
2223
|
+
{}
|
|
2224
|
+
_ => Err(RunTimeError::ExpectedEdge {
|
|
2225
|
+
context: "evaluator/eval_program",
|
|
2226
|
+
})?,
|
|
2227
|
+
}
|
|
2228
|
+
}
|
|
2229
|
+
}
|
|
2230
|
+
}
|
|
2231
|
+
|
|
2232
|
+
output_table.add_truncated_row(new_row)?;
|
|
2233
|
+
}
|
|
2234
|
+
else
|
|
2235
|
+
{
|
|
2236
|
+
// Step 3: Match succeeded - apply on_match updates to each matched row
|
|
2237
|
+
for mut matched_row in matched_rows
|
|
2238
|
+
{
|
|
2239
|
+
for update in on_match_updates.iter()
|
|
2240
|
+
{
|
|
2241
|
+
match update
|
|
2242
|
+
{
|
|
2243
|
+
instructions::UpdateOne::SetProperty {
|
|
2244
|
+
target,
|
|
2245
|
+
path,
|
|
2246
|
+
instructions,
|
|
2247
|
+
} =>
|
|
2248
|
+
{
|
|
2249
|
+
eval_update_property(
|
|
2250
|
+
store,
|
|
2251
|
+
&mut tx,
|
|
2252
|
+
&graph_name,
|
|
2253
|
+
&mut matched_row,
|
|
2254
|
+
*target,
|
|
2255
|
+
path,
|
|
2256
|
+
instructions,
|
|
2257
|
+
parameters,
|
|
2258
|
+
true,
|
|
2259
|
+
)?;
|
|
2260
|
+
}
|
|
2261
|
+
instructions::UpdateOne::AddProperty {
|
|
2262
|
+
target,
|
|
2263
|
+
path,
|
|
2264
|
+
instructions,
|
|
2265
|
+
} =>
|
|
2266
|
+
{
|
|
2267
|
+
eval_update_property(
|
|
2268
|
+
store,
|
|
2269
|
+
&mut tx,
|
|
2270
|
+
&graph_name,
|
|
2271
|
+
&mut matched_row,
|
|
2272
|
+
*target,
|
|
2273
|
+
path,
|
|
2274
|
+
instructions,
|
|
2275
|
+
parameters,
|
|
2276
|
+
false,
|
|
2277
|
+
)?;
|
|
2278
|
+
}
|
|
2279
|
+
instructions::UpdateOne::RemoveProperty { target, path } =>
|
|
2280
|
+
{
|
|
2281
|
+
let var = matched_row.get(*target)?;
|
|
2282
|
+
let mut piter = path.iter();
|
|
2283
|
+
match var
|
|
2284
|
+
{
|
|
2285
|
+
value::Value::Node(n) =>
|
|
2286
|
+
{
|
|
2287
|
+
let mut n = n.to_owned();
|
|
2288
|
+
n.properties_mut().remove_value(piter.next(), piter)?;
|
|
2289
|
+
store.update_node(&mut tx, &graph_name, &n)?;
|
|
2290
|
+
matched_row.set(*target, n.into())?;
|
|
2291
|
+
}
|
|
2292
|
+
value::Value::Edge(e) =>
|
|
2293
|
+
{
|
|
2294
|
+
let mut e = e.to_owned();
|
|
2295
|
+
e.properties_mut().remove_value(piter.next(), piter)?;
|
|
2296
|
+
store.update_edge(&mut tx, &graph_name, &e)?;
|
|
2297
|
+
matched_row.set(*target, e.into())?;
|
|
2298
|
+
}
|
|
2299
|
+
value::Value::Null =>
|
|
2300
|
+
{}
|
|
2301
|
+
_ => Err(RunTimeError::ExpectedEdge {
|
|
2302
|
+
context: "evaluator/eval_program",
|
|
2303
|
+
})?,
|
|
2304
|
+
}
|
|
2305
|
+
}
|
|
2306
|
+
instructions::UpdateOne::AddLabels { target, labels }
|
|
2307
|
+
| instructions::UpdateOne::RemoveLabels { target, labels } =>
|
|
2308
|
+
{
|
|
2309
|
+
let add_labels = match update
|
|
2310
|
+
{
|
|
2311
|
+
instructions::UpdateOne::AddLabels { .. } => true,
|
|
2312
|
+
instructions::UpdateOne::RemoveLabels { .. } => false,
|
|
2313
|
+
_ => Err(InternalError::Unreachable {
|
|
2314
|
+
context: "evaluator/eval_program/add_remove_labels",
|
|
2315
|
+
})?,
|
|
2316
|
+
};
|
|
2317
|
+
|
|
2318
|
+
let var = matched_row.get(*target)?;
|
|
2319
|
+
match var
|
|
2320
|
+
{
|
|
2321
|
+
value::Value::Node(n) =>
|
|
2322
|
+
{
|
|
2323
|
+
let mut n = n.to_owned();
|
|
2324
|
+
if add_labels
|
|
2325
|
+
{
|
|
2326
|
+
n.labels_mut().append(&mut labels.clone());
|
|
2327
|
+
}
|
|
2328
|
+
else
|
|
2329
|
+
{
|
|
2330
|
+
n.labels_edit(|l| {
|
|
2331
|
+
l.into_iter().filter(|x| !labels.contains(x)).collect()
|
|
2332
|
+
});
|
|
2333
|
+
}
|
|
2334
|
+
store.update_node(&mut tx, &graph_name, &n)?;
|
|
2335
|
+
matched_row.set(*target, n.into())?;
|
|
2336
|
+
}
|
|
2337
|
+
value::Value::Edge(e) =>
|
|
2338
|
+
{
|
|
2339
|
+
let mut e = e.to_owned();
|
|
2340
|
+
if add_labels
|
|
2341
|
+
{
|
|
2342
|
+
e.labels_mut().append(&mut labels.clone());
|
|
2343
|
+
}
|
|
2344
|
+
else
|
|
2345
|
+
{
|
|
2346
|
+
e.labels_edit(|l| {
|
|
2347
|
+
l.into_iter().filter(|x| !labels.contains(x)).collect()
|
|
2348
|
+
});
|
|
2349
|
+
}
|
|
2350
|
+
store.update_edge(&mut tx, &graph_name, &e)?;
|
|
2351
|
+
matched_row.set(*target, e.into())?;
|
|
2352
|
+
}
|
|
2353
|
+
value::Value::Null =>
|
|
2354
|
+
{}
|
|
2355
|
+
_ => Err(RunTimeError::ExpectedEdge {
|
|
2356
|
+
context: "evaluator/eval_program",
|
|
2357
|
+
})?,
|
|
2358
|
+
}
|
|
2359
|
+
}
|
|
2360
|
+
}
|
|
2361
|
+
}
|
|
2362
|
+
output_table.add_truncated_row(matched_row)?;
|
|
2363
|
+
}
|
|
2364
|
+
}
|
|
2365
|
+
}
|
|
2366
|
+
|
|
2367
|
+
input_table = output_table;
|
|
2368
|
+
}
|
|
1674
2369
|
instructions::Block::Call { arguments: _, name } =>
|
|
1675
2370
|
{
|
|
1676
2371
|
if name == "gqlite.internal.stats"
|
|
@@ -1694,6 +2389,149 @@ pub(crate) fn eval_program<TStore: store::Store>(
|
|
|
1694
2389
|
return Err(InternalError::Unimplemented("call for any other function").into());
|
|
1695
2390
|
}
|
|
1696
2391
|
}
|
|
2392
|
+
instructions::Block::VectorSearch {
|
|
2393
|
+
index,
|
|
2394
|
+
query_instructions,
|
|
2395
|
+
k,
|
|
2396
|
+
out_node_col_id,
|
|
2397
|
+
out_score_col_id,
|
|
2398
|
+
variables_size,
|
|
2399
|
+
} =>
|
|
2400
|
+
{
|
|
2401
|
+
let mut output_table = value_table::ValueTable::new(variables_size.persistent_variables);
|
|
2402
|
+
for row in input_table.into_row_iter()
|
|
2403
|
+
{
|
|
2404
|
+
let mut query_stack = Stack::default();
|
|
2405
|
+
eval_instructions(&mut query_stack, &row, query_instructions, parameters)?;
|
|
2406
|
+
|
|
2407
|
+
let query_tensor: value::Tensor = query_stack.try_pop_into()?;
|
|
2408
|
+
|
|
2409
|
+
let index_dimension = store
|
|
2410
|
+
.list_vector_indexes(&mut tx, &graph_name)?
|
|
2411
|
+
.into_iter()
|
|
2412
|
+
.find(|spec| spec.name == *index)
|
|
2413
|
+
.ok_or_else(|| {
|
|
2414
|
+
StoreError::InvalidFormat(format!(
|
|
2415
|
+
"Vector index '{}' does not exist in graph '{}'",
|
|
2416
|
+
index, graph_name
|
|
2417
|
+
))
|
|
2418
|
+
})?
|
|
2419
|
+
.dimension;
|
|
2420
|
+
if query_tensor.len() != index_dimension
|
|
2421
|
+
{
|
|
2422
|
+
return Err(
|
|
2423
|
+
StoreError::InvalidFormat(format!(
|
|
2424
|
+
"Vector dimension {} does not match index dimension {}",
|
|
2425
|
+
query_tensor.len(),
|
|
2426
|
+
index_dimension
|
|
2427
|
+
))
|
|
2428
|
+
.into(),
|
|
2429
|
+
);
|
|
2430
|
+
}
|
|
2431
|
+
|
|
2432
|
+
// Perform vector search
|
|
2433
|
+
let results = store.vector_search(&mut tx, &graph_name, index, &query_tensor, *k)?;
|
|
2434
|
+
|
|
2435
|
+
// Batch fetch all nodes using select_nodes by keys
|
|
2436
|
+
let node_keys: Vec<crate::graph::Key> = results.iter().map(|r| r.node_id).collect();
|
|
2437
|
+
let mut nodes_by_key = std::collections::HashMap::new();
|
|
2438
|
+
if !node_keys.is_empty()
|
|
2439
|
+
{
|
|
2440
|
+
let nodes = store.select_nodes(
|
|
2441
|
+
&mut tx,
|
|
2442
|
+
&graph_name,
|
|
2443
|
+
SelectNodeQuery::select_keys(node_keys),
|
|
2444
|
+
)?;
|
|
2445
|
+
for node in nodes
|
|
2446
|
+
{
|
|
2447
|
+
nodes_by_key.insert(node.key(), node);
|
|
2448
|
+
}
|
|
2449
|
+
}
|
|
2450
|
+
|
|
2451
|
+
// Add result rows to output table
|
|
2452
|
+
for result in results
|
|
2453
|
+
{
|
|
2454
|
+
let mut out_row = row.clone().extended(variables_size.total_size())?;
|
|
2455
|
+
// Look up the node from our pre-fetched map; skip the row if the node no longer
|
|
2456
|
+
// exists (stale vector index entry from a deleted node)
|
|
2457
|
+
if let Some(node) = nodes_by_key.get(&result.node_id)
|
|
2458
|
+
{
|
|
2459
|
+
out_row.set(*out_node_col_id, value::Value::Node(node.clone()))?;
|
|
2460
|
+
if let Some(score_col_id) = out_score_col_id
|
|
2461
|
+
{
|
|
2462
|
+
out_row.set(*score_col_id, (result.score as f64).into())?;
|
|
2463
|
+
}
|
|
2464
|
+
output_table.add_truncated_row(out_row)?;
|
|
2465
|
+
}
|
|
2466
|
+
}
|
|
2467
|
+
}
|
|
2468
|
+
input_table = output_table;
|
|
2469
|
+
}
|
|
2470
|
+
instructions::Block::CreateVectorIndex {
|
|
2471
|
+
name,
|
|
2472
|
+
label,
|
|
2473
|
+
property,
|
|
2474
|
+
dimension,
|
|
2475
|
+
metric,
|
|
2476
|
+
if_not_exists,
|
|
2477
|
+
} =>
|
|
2478
|
+
{
|
|
2479
|
+
let vector_metric = match metric
|
|
2480
|
+
{
|
|
2481
|
+
Some(gqlparser::oc::ast::Metric::Cosine) | None => store::VectorMetric::Cosine,
|
|
2482
|
+
Some(gqlparser::oc::ast::Metric::L2) => store::VectorMetric::L2,
|
|
2483
|
+
Some(gqlparser::oc::ast::Metric::DotProduct) => store::VectorMetric::DotProduct,
|
|
2484
|
+
};
|
|
2485
|
+
let spec = store::VectorIndexSpec {
|
|
2486
|
+
name: name.clone(),
|
|
2487
|
+
label: Some(label.clone()),
|
|
2488
|
+
property: Some(property.clone()),
|
|
2489
|
+
dimension: dimension.unwrap_or(0),
|
|
2490
|
+
metric: vector_metric,
|
|
2491
|
+
hnsw_params: Default::default(),
|
|
2492
|
+
dtype: value::DType::F32,
|
|
2493
|
+
};
|
|
2494
|
+
|
|
2495
|
+
let result = store.create_vector_index(&mut tx, &graph_name, spec);
|
|
2496
|
+
match result
|
|
2497
|
+
{
|
|
2498
|
+
Ok(()) =>
|
|
2499
|
+
{
|
|
2500
|
+
let existing_nodes = store.select_nodes(
|
|
2501
|
+
&mut tx,
|
|
2502
|
+
&graph_name,
|
|
2503
|
+
store::SelectNodeQuery::select_labels(vec![label.clone()]),
|
|
2504
|
+
)?;
|
|
2505
|
+
|
|
2506
|
+
for node in existing_nodes
|
|
2507
|
+
{
|
|
2508
|
+
store.apply_vector_indices_for_node(&mut tx, &graph_name, &node)?;
|
|
2509
|
+
}
|
|
2510
|
+
}
|
|
2511
|
+
Err(e) =>
|
|
2512
|
+
{
|
|
2513
|
+
if *if_not_exists
|
|
2514
|
+
{
|
|
2515
|
+
if let Error::StoreError(StoreError::DuplicatedVectorIndex { .. }) = e.error()
|
|
2516
|
+
{
|
|
2517
|
+
// silently skip — index already exists
|
|
2518
|
+
}
|
|
2519
|
+
else
|
|
2520
|
+
{
|
|
2521
|
+
return Err(e);
|
|
2522
|
+
}
|
|
2523
|
+
}
|
|
2524
|
+
else
|
|
2525
|
+
{
|
|
2526
|
+
return Err(
|
|
2527
|
+
error::map_error!(e, Error::StoreError(StoreError::DuplicatedVectorIndex { index_name }) => RunTimeError::DuplicatedVectorIndex {
|
|
2528
|
+
index_name: index_name.clone(),
|
|
2529
|
+
}),
|
|
2530
|
+
);
|
|
2531
|
+
}
|
|
2532
|
+
}
|
|
2533
|
+
}
|
|
2534
|
+
}
|
|
1697
2535
|
}
|
|
1698
2536
|
}
|
|
1699
2537
|
tx.close()?;
|
|
@@ -1708,7 +2546,7 @@ mod tests
|
|
|
1708
2546
|
prelude::*,
|
|
1709
2547
|
};
|
|
1710
2548
|
|
|
1711
|
-
use super::{
|
|
2549
|
+
use super::{TryPopInto, execute_boolean_operator};
|
|
1712
2550
|
|
|
1713
2551
|
fn test_execute_boolean_operator_(
|
|
1714
2552
|
instruction: &interpreter::instructions::Instruction,
|