use cypherlite_core::{CypherLiteError, EdgeId, NodeId, NodeRecord, PropertyValue, Result};
use super::BTree;
pub struct NodeStore {
tree: BTree<u64, NodeRecord>,
next_id: u64,
}
impl NodeStore {
pub fn new(next_id: u64) -> Self {
Self {
tree: BTree::new(),
next_id,
}
}
pub fn create_node(
&mut self,
labels: Vec<u32>,
properties: Vec<(u32, PropertyValue)>,
) -> NodeId {
let node_id = NodeId(self.next_id);
self.next_id += 1;
let record = NodeRecord {
node_id,
labels,
properties,
next_edge_id: None,
overflow_page: None,
};
self.tree.insert(node_id.0, record);
node_id
}
pub fn get_node(&self, node_id: NodeId) -> Option<&NodeRecord> {
self.tree.search(&node_id.0)
}
pub fn get_node_mut(&mut self, node_id: NodeId) -> Option<&mut NodeRecord> {
self.tree.search_mut(&node_id.0)
}
pub fn update_node(
&mut self,
node_id: NodeId,
properties: Vec<(u32, PropertyValue)>,
) -> Result<()> {
match self.tree.search_mut(&node_id.0) {
Some(record) => {
record.properties = properties;
Ok(())
}
None => Err(CypherLiteError::NodeNotFound(node_id.0)),
}
}
pub fn delete_node(&mut self, node_id: NodeId) -> Result<NodeRecord> {
self.tree
.delete(&node_id.0)
.ok_or(CypherLiteError::NodeNotFound(node_id.0))
}
pub fn next_id(&self) -> u64 {
self.next_id
}
pub fn len(&self) -> usize {
self.tree.len()
}
pub fn is_empty(&self) -> bool {
self.tree.is_empty()
}
pub fn set_next_edge(&mut self, node_id: NodeId, edge_id: Option<EdgeId>) -> Result<()> {
match self.tree.search_mut(&node_id.0) {
Some(record) => {
record.next_edge_id = edge_id;
Ok(())
}
None => Err(CypherLiteError::NodeNotFound(node_id.0)),
}
}
pub fn iter(&self) -> impl Iterator<Item = (&u64, &NodeRecord)> {
self.tree.iter()
}
pub fn scan_all(&self) -> Vec<&NodeRecord> {
self.tree.iter().map(|(_, record)| record).collect()
}
pub fn scan_by_label(&self, label_id: u32) -> Vec<&NodeRecord> {
self.tree
.iter()
.filter_map(|(_, record)| {
if record.labels.contains(&label_id) {
Some(record)
} else {
None
}
})
.collect()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_create_node() {
let mut store = NodeStore::new(1);
let id1 = store.create_node(vec![1], vec![(1, PropertyValue::String("Alice".into()))]);
let id2 = store.create_node(vec![1], vec![(1, PropertyValue::String("Bob".into()))]);
assert_eq!(id1, NodeId(1));
assert_eq!(id2, NodeId(2));
assert_eq!(store.len(), 2);
}
#[test]
fn test_get_node() {
let mut store = NodeStore::new(1);
let id = store.create_node(vec![100], vec![(1, PropertyValue::Int64(42))]);
let node = store.get_node(id).expect("found");
assert_eq!(node.node_id, id);
assert_eq!(node.labels, vec![100]);
}
#[test]
fn test_get_nonexistent_node() {
let store = NodeStore::new(1);
assert!(store.get_node(NodeId(999)).is_none());
}
#[test]
fn test_update_node() {
let mut store = NodeStore::new(1);
let id = store.create_node(vec![], vec![(1, PropertyValue::Int64(10))]);
store
.update_node(id, vec![(1, PropertyValue::Int64(20))])
.expect("update");
let node = store.get_node(id).expect("found");
assert_eq!(node.properties[0].1, PropertyValue::Int64(20));
}
#[test]
fn test_update_nonexistent_node() {
let mut store = NodeStore::new(1);
let result = store.update_node(NodeId(999), vec![]);
assert!(matches!(result, Err(CypherLiteError::NodeNotFound(999))));
}
#[test]
fn test_delete_node() {
let mut store = NodeStore::new(1);
let id = store.create_node(vec![], vec![]);
let deleted = store.delete_node(id).expect("delete");
assert_eq!(deleted.node_id, id);
assert!(store.get_node(id).is_none());
assert_eq!(store.len(), 0);
}
#[test]
fn test_delete_nonexistent_node() {
let mut store = NodeStore::new(1);
let result = store.delete_node(NodeId(999));
assert!(matches!(result, Err(CypherLiteError::NodeNotFound(999))));
}
#[test]
fn test_set_next_edge() {
let mut store = NodeStore::new(1);
let id = store.create_node(vec![], vec![]);
assert!(store.get_node(id).expect("f").next_edge_id.is_none());
store.set_next_edge(id, Some(EdgeId(10))).expect("set");
assert_eq!(
store.get_node(id).expect("f").next_edge_id,
Some(EdgeId(10))
);
}
#[test]
fn test_next_id_increments() {
let mut store = NodeStore::new(1);
assert_eq!(store.next_id(), 1);
store.create_node(vec![], vec![]);
assert_eq!(store.next_id(), 2);
store.create_node(vec![], vec![]);
assert_eq!(store.next_id(), 3);
}
#[test]
fn test_node_store_empty() {
let store = NodeStore::new(1);
assert!(store.is_empty());
assert_eq!(store.len(), 0);
}
#[test]
fn test_scan_all_empty() {
let store = NodeStore::new(1);
let nodes = store.scan_all();
assert!(nodes.is_empty());
}
#[test]
fn test_scan_all_returns_all_nodes() {
let mut store = NodeStore::new(1);
store.create_node(vec![1], vec![]);
store.create_node(vec![2], vec![]);
store.create_node(vec![3], vec![]);
let nodes = store.scan_all();
assert_eq!(nodes.len(), 3);
}
#[test]
fn test_scan_by_label_returns_matching() {
let mut store = NodeStore::new(1);
store.create_node(vec![1, 2], vec![]);
store.create_node(vec![2, 3], vec![]);
store.create_node(vec![3], vec![]);
let nodes = store.scan_by_label(2);
assert_eq!(nodes.len(), 2);
}
#[test]
fn test_scan_by_label_nonexistent_returns_empty() {
let mut store = NodeStore::new(1);
store.create_node(vec![1], vec![]);
let nodes = store.scan_by_label(999);
assert!(nodes.is_empty());
}
#[test]
fn test_many_nodes() {
let mut store = NodeStore::new(1);
for _ in 0..1000 {
store.create_node(vec![1], vec![]);
}
assert_eq!(store.len(), 1000);
assert!(store.get_node(NodeId(500)).is_some());
}
}