use crate::error::Result;
use crate::types::{NodeId, PropertyValue};
use crate::index::{Index, IndexQuery};
use std::collections::BTreeMap;
pub struct BTreeIndex {
map: BTreeMap<PropertyValue, Vec<NodeId>>,
}
impl BTreeIndex {
pub fn new() -> Self {
BTreeIndex {
map: BTreeMap::new(),
}
}
pub fn get(&self, value: &PropertyValue) -> Option<&Vec<NodeId>> {
self.map.get(value)
}
fn range_query<F>(&self, predicate: F) -> Vec<NodeId>
where
F: Fn(&PropertyValue) -> bool,
{
self.map
.iter()
.filter(|(k, _)| predicate(k))
.flat_map(|(_, nodes)| nodes.iter().copied())
.collect()
}
}
impl Index for BTreeIndex {
fn insert(&mut self, value: PropertyValue, node_id: NodeId) -> Result<()> {
self.map
.entry(value)
.or_default()
.push(node_id);
Ok(())
}
fn remove(&mut self, value: &PropertyValue, node_id: NodeId) -> Result<()> {
if let Some(nodes) = self.map.get_mut(value) {
nodes.retain(|&id| id != node_id);
if nodes.is_empty() {
self.map.remove(value);
}
}
Ok(())
}
fn query(&self, query: &IndexQuery) -> Result<Vec<NodeId>> {
match query {
IndexQuery::Equals(value) => {
Ok(self.map.get(value).cloned().unwrap_or_default())
}
IndexQuery::GreaterThan(value) => {
Ok(self.range_query(|k| k > value))
}
IndexQuery::GreaterThanOrEqual(value) => {
Ok(self.range_query(|k| k >= value))
}
IndexQuery::LessThan(value) => {
Ok(self.range_query(|k| k < value))
}
IndexQuery::LessThanOrEqual(value) => {
Ok(self.range_query(|k| k <= value))
}
IndexQuery::Between(min, max) => {
Ok(self.range_query(|k| k >= min && k <= max))
}
IndexQuery::FullText(_) => {
Err(crate::error::NopalError::index_error(
"BTree index does not support full-text search".to_string()
))
}
}
}
fn clear(&mut self) -> Result<()> {
self.map.clear();
Ok(())
}
fn size(&self) -> usize {
self.map.len()
}
}
impl Default for BTreeIndex {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_btree_index_insert_query() {
let mut index = BTreeIndex::new();
let node1 = uuid::Uuid::new_v4();
let node2 = uuid::Uuid::new_v4();
let node3 = uuid::Uuid::new_v4();
index.insert(PropertyValue::Int(10), node1).unwrap();
index.insert(PropertyValue::Int(20), node2).unwrap();
index.insert(PropertyValue::Int(30), node3).unwrap();
let result = index.query(&IndexQuery::Equals(PropertyValue::Int(20))).unwrap();
assert_eq!(result.len(), 1);
assert_eq!(result[0], node2);
let result = index.query(&IndexQuery::GreaterThan(PropertyValue::Int(15))).unwrap();
assert_eq!(result.len(), 2);
assert!(result.contains(&node2));
assert!(result.contains(&node3));
let result = index.query(&IndexQuery::LessThan(PropertyValue::Int(25))).unwrap();
assert_eq!(result.len(), 2);
assert!(result.contains(&node1));
assert!(result.contains(&node2));
}
#[test]
fn test_btree_index_range_queries() {
let mut index = BTreeIndex::new();
let nodes: Vec<NodeId> = (0..10).map(|_| uuid::Uuid::new_v4()).collect();
for (i, &node) in nodes.iter().enumerate() {
index.insert(PropertyValue::Int((i * 10) as i64), node).unwrap();
}
let result = index.query(&IndexQuery::Between(
PropertyValue::Int(20),
PropertyValue::Int(50),
)).unwrap();
assert_eq!(result.len(), 4);
let result = index.query(&IndexQuery::GreaterThanOrEqual(
PropertyValue::Int(70)
)).unwrap();
assert_eq!(result.len(), 3);
let result = index.query(&IndexQuery::LessThanOrEqual(
PropertyValue::Int(30)
)).unwrap();
assert_eq!(result.len(), 4); }
#[test]
fn test_btree_index_strings() {
let mut index = BTreeIndex::new();
let node_alice = uuid::Uuid::new_v4();
let node_bob = uuid::Uuid::new_v4();
let node_charlie = uuid::Uuid::new_v4();
index.insert(PropertyValue::String("Alice".to_string()), node_alice).unwrap();
index.insert(PropertyValue::String("Bob".to_string()), node_bob).unwrap();
index.insert(PropertyValue::String("Charlie".to_string()), node_charlie).unwrap();
let result = index.query(&IndexQuery::GreaterThan(
PropertyValue::String("B".to_string())
)).unwrap();
assert_eq!(result.len(), 2);
let result = index.query(&IndexQuery::LessThan(
PropertyValue::String("C".to_string())
)).unwrap();
assert_eq!(result.len(), 2); }
#[test]
fn test_btree_index_floats() {
let mut index = BTreeIndex::new();
let node1 = uuid::Uuid::new_v4();
let node2 = uuid::Uuid::new_v4();
let node3 = uuid::Uuid::new_v4();
index.insert(PropertyValue::Float(1.5), node1).unwrap();
index.insert(PropertyValue::Float(2.7), node2).unwrap();
index.insert(PropertyValue::Float(3.9), node3).unwrap();
let result = index.query(&IndexQuery::Between(
PropertyValue::Float(2.0),
PropertyValue::Float(4.0),
)).unwrap();
assert_eq!(result.len(), 2); }
}