use super::triple::{EmptyValue, OspKey, PosKey, SpoKey, Triple};
use crate::btree::BTree;
use crate::dictionary::NodeId;
use crate::error::Result;
use crate::storage::BufferPool;
use oxicode::Decode;
use std::sync::Arc;
pub struct TripleIndex<K>
where
K: Ord + Clone + serde::Serialize + for<'de> serde::Deserialize<'de> + std::fmt::Debug,
{
btree: BTree<K, EmptyValue>,
}
impl<K> TripleIndex<K>
where
K: Ord + Clone + serde::Serialize + for<'de> serde::Deserialize<'de> + std::fmt::Debug,
{
pub fn new(buffer_pool: Arc<BufferPool>) -> Self {
TripleIndex {
btree: BTree::new(buffer_pool),
}
}
pub fn insert(&mut self, key: K) -> Result<()> {
self.btree.insert(key, EmptyValue)?;
Ok(())
}
pub fn contains(&self, key: &K) -> Result<bool> {
Ok(self.btree.search(key)?.is_some())
}
pub fn delete(&mut self, key: &K) -> Result<bool> {
Ok(self.btree.delete(key)?.is_some())
}
pub fn range_scan(&self, start_key: Option<K>, end_key: Option<K>) -> Result<Vec<K>> {
let mut results = Vec::new();
let iter = self.btree.range_scan(start_key, end_key)?;
for item in iter {
let (key, _value) = item?;
results.push(key);
}
Ok(results)
}
}
pub type SpoIndex = TripleIndex<SpoKey>;
pub type PosIndex = TripleIndex<PosKey>;
pub type OspIndex = TripleIndex<OspKey>;
pub struct TripleIndexes {
spo: SpoIndex,
pos: PosIndex,
osp: OspIndex,
}
impl TripleIndexes {
pub fn new(buffer_pool: Arc<BufferPool>) -> Self {
TripleIndexes {
spo: TripleIndex::new(buffer_pool.clone()),
pos: TripleIndex::new(buffer_pool.clone()),
osp: TripleIndex::new(buffer_pool),
}
}
pub fn insert(&mut self, triple: Triple) -> Result<()> {
self.spo.insert(triple.into())?;
self.pos.insert(triple.into())?;
self.osp.insert(triple.into())?;
Ok(())
}
pub fn contains(&self, triple: &Triple) -> Result<bool> {
self.spo.contains(&(*triple).into())
}
pub fn delete(&mut self, triple: &Triple) -> Result<bool> {
let spo_key: SpoKey = (*triple).into();
let pos_key: PosKey = (*triple).into();
let osp_key: OspKey = (*triple).into();
let exists = self.spo.delete(&spo_key)?;
if exists {
self.pos.delete(&pos_key)?;
self.osp.delete(&osp_key)?;
}
Ok(exists)
}
pub fn query_pattern(
&self,
s: Option<NodeId>,
p: Option<NodeId>,
o: Option<NodeId>,
) -> Result<Vec<Triple>> {
match (s, p, o) {
(Some(s), Some(p), Some(o)) => {
let triple = Triple::new(s, p, o);
if self.contains(&triple)? {
Ok(vec![triple])
} else {
Ok(Vec::new())
}
}
(Some(s), Some(p), None) => {
let start_key = SpoKey(s, p, NodeId::NULL);
let end_key = SpoKey(s, p.next(), NodeId::NULL);
let keys = self.spo.range_scan(Some(start_key), Some(end_key))?;
Ok(keys
.into_iter()
.map(|k| Triple::new(k.0, k.1, k.2))
.collect())
}
(Some(s), None, None) => {
let start_key = SpoKey(s, NodeId::NULL, NodeId::NULL);
let end_key = SpoKey(s.next(), NodeId::NULL, NodeId::NULL);
let keys = self.spo.range_scan(Some(start_key), Some(end_key))?;
Ok(keys
.into_iter()
.map(|k| Triple::new(k.0, k.1, k.2))
.collect())
}
(None, Some(p), Some(o)) => {
let start_key = PosKey(p, o, NodeId::NULL);
let end_key = PosKey(p, o.next(), NodeId::NULL);
let keys = self.pos.range_scan(Some(start_key), Some(end_key))?;
Ok(keys
.into_iter()
.map(|k| Triple::new(k.2, k.0, k.1))
.collect())
}
(None, Some(p), None) => {
let start_key = PosKey(p, NodeId::NULL, NodeId::NULL);
let end_key = PosKey(p.next(), NodeId::NULL, NodeId::NULL);
let keys = self.pos.range_scan(Some(start_key), Some(end_key))?;
Ok(keys
.into_iter()
.map(|k| Triple::new(k.2, k.0, k.1))
.collect())
}
(Some(s), None, Some(o)) => {
let start_key = OspKey(o, s, NodeId::NULL);
let end_key = OspKey(o, s.next(), NodeId::NULL);
let keys = self.osp.range_scan(Some(start_key), Some(end_key))?;
Ok(keys
.into_iter()
.map(|k| Triple::new(k.1, k.2, k.0))
.collect())
}
(None, None, Some(o)) => {
let start_key = OspKey(o, NodeId::NULL, NodeId::NULL);
let end_key = OspKey(o.next(), NodeId::NULL, NodeId::NULL);
let keys = self.osp.range_scan(Some(start_key), Some(end_key))?;
Ok(keys
.into_iter()
.map(|k| Triple::new(k.1, k.2, k.0))
.collect())
}
(None, None, None) => {
let keys = self.spo.range_scan(None, None)?;
Ok(keys
.into_iter()
.map(|k| Triple::new(k.0, k.1, k.2))
.collect())
}
}
}
pub fn spo(&self) -> &SpoIndex {
&self.spo
}
pub fn pos(&self) -> &PosIndex {
&self.pos
}
pub fn osp(&self) -> &OspIndex {
&self.osp
}
pub fn spo_mut(&mut self) -> &mut SpoIndex {
&mut self.spo
}
pub fn pos_mut(&mut self) -> &mut PosIndex {
&mut self.pos
}
pub fn osp_mut(&mut self) -> &mut OspIndex {
&mut self.osp
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::storage::FileManager;
use tempfile::TempDir;
fn create_test_indexes() -> (TempDir, TripleIndexes) {
let temp_dir = TempDir::new().unwrap();
let db_path = temp_dir.path().join("test.db");
let file_manager = Arc::new(FileManager::open(&db_path, true).unwrap());
let buffer_pool = Arc::new(BufferPool::new(100, file_manager));
let indexes = TripleIndexes::new(buffer_pool);
(temp_dir, indexes)
}
#[test]
fn test_triple_index_insert_contains() -> Result<()> {
let (_temp_dir, mut indexes) = create_test_indexes();
let triple = Triple::new(NodeId::new(1), NodeId::new(2), NodeId::new(3));
assert!(!indexes.contains(&triple)?);
indexes.insert(triple)?;
assert!(indexes.contains(&triple)?);
Ok(())
}
#[test]
fn test_triple_index_delete() -> Result<()> {
let (_temp_dir, mut indexes) = create_test_indexes();
let triple = Triple::new(NodeId::new(10), NodeId::new(20), NodeId::new(30));
indexes.insert(triple)?;
assert!(indexes.contains(&triple)?);
let deleted = indexes.delete(&triple)?;
assert!(deleted);
assert!(!indexes.contains(&triple)?);
let deleted_again = indexes.delete(&triple)?;
assert!(!deleted_again);
Ok(())
}
#[test]
fn test_triple_index_multiple_triples() -> Result<()> {
let (_temp_dir, mut indexes) = create_test_indexes();
let triple1 = Triple::new(NodeId::new(1), NodeId::new(2), NodeId::new(3));
let triple2 = Triple::new(NodeId::new(1), NodeId::new(2), NodeId::new(4));
let triple3 = Triple::new(NodeId::new(2), NodeId::new(3), NodeId::new(4));
indexes.insert(triple1)?;
indexes.insert(triple2)?;
indexes.insert(triple3)?;
assert!(indexes.contains(&triple1)?);
assert!(indexes.contains(&triple2)?);
assert!(indexes.contains(&triple3)?);
indexes.delete(&triple2)?;
assert!(indexes.contains(&triple1)?);
assert!(!indexes.contains(&triple2)?);
assert!(indexes.contains(&triple3)?);
Ok(())
}
#[test]
fn test_all_three_indexes_updated() -> Result<()> {
let (_temp_dir, mut indexes) = create_test_indexes();
let triple = Triple::new(NodeId::new(100), NodeId::new(200), NodeId::new(300));
indexes.insert(triple)?;
assert!(indexes.spo().contains(&triple.into())?);
assert!(indexes.pos().contains(&triple.into())?);
assert!(indexes.osp().contains(&triple.into())?);
indexes.delete(&triple)?;
assert!(!indexes.spo().contains(&triple.into())?);
assert!(!indexes.pos().contains(&triple.into())?);
assert!(!indexes.osp().contains(&triple.into())?);
Ok(())
}
}