Skip to main content

cypherlite_query/executor/operators/
node_scan.rs

1// NodeScanOp: scans all nodes or nodes by label
2
3use crate::executor::{Record, Value};
4use cypherlite_storage::StorageEngine;
5
6/// Scan nodes from the engine, optionally filtered by label.
7/// Each node produces a Record with the variable bound to Value::Node(node_id).
8pub fn execute_node_scan(
9    variable: &str,
10    label_id: Option<u32>,
11    engine: &StorageEngine,
12) -> Vec<Record> {
13    let nodes = match label_id {
14        Some(lid) => engine.scan_nodes_by_label(lid),
15        None => engine.scan_nodes(),
16    };
17
18    nodes
19        .into_iter()
20        .map(|node| {
21            let mut record = Record::new();
22            record.insert(variable.to_string(), Value::Node(node.node_id));
23            record
24        })
25        .collect()
26}
27
28#[cfg(test)]
29mod tests {
30    use super::*;
31    use cypherlite_core::{DatabaseConfig, LabelRegistry, SyncMode};
32    use tempfile::tempdir;
33
34    fn test_engine(dir: &std::path::Path) -> StorageEngine {
35        let config = DatabaseConfig {
36            path: dir.join("test.cyl"),
37            wal_sync_mode: SyncMode::Normal,
38            ..Default::default()
39        };
40        StorageEngine::open(config).expect("open")
41    }
42
43    // EXEC-T001: NodeScan with label filter
44    #[test]
45    fn test_node_scan_with_label_filter() {
46        let dir = tempdir().expect("tempdir");
47        let mut engine = test_engine(dir.path());
48
49        let person_label = engine.get_or_create_label("Person");
50        let company_label = engine.get_or_create_label("Company");
51
52        engine.create_node(vec![person_label], vec![]);
53        engine.create_node(vec![person_label], vec![]);
54        engine.create_node(vec![company_label], vec![]);
55
56        let records = execute_node_scan("n", Some(person_label), &engine);
57        assert_eq!(records.len(), 2);
58
59        for record in &records {
60            assert!(record.contains_key("n"));
61            assert!(matches!(record.get("n"), Some(Value::Node(_))));
62        }
63    }
64
65    #[test]
66    fn test_node_scan_all_nodes() {
67        let dir = tempdir().expect("tempdir");
68        let mut engine = test_engine(dir.path());
69
70        engine.create_node(vec![0], vec![]);
71        engine.create_node(vec![1], vec![]);
72        engine.create_node(vec![2], vec![]);
73
74        let records = execute_node_scan("n", None, &engine);
75        assert_eq!(records.len(), 3);
76    }
77
78    #[test]
79    fn test_node_scan_empty_database() {
80        let dir = tempdir().expect("tempdir");
81        let engine = test_engine(dir.path());
82
83        let records = execute_node_scan("n", Some(0), &engine);
84        assert!(records.is_empty());
85    }
86}