use std::{
collections::{BTreeMap, HashMap},
hash::{Hash, Hasher},
};
use crate::{
prelude::*,
traits::{IdType, Property},
IdGenerator,
};
use serde::{de::DeserializeOwned, Serialize};
mod extend;
mod insert;
mod iter;
mod merge;
mod query;
mod remove;
#[derive(Debug)]
pub enum SledTripleStoreError {
SledError(sled::Error),
SerializationError(bincode::Error),
KeySizeError,
MissingPropertyData,
}
impl From<sled::Error> for SledTripleStoreError {
fn from(e: sled::Error) -> Self {
SledTripleStoreError::SledError(e)
}
}
impl From<bincode::Error> for SledTripleStoreError {
fn from(e: bincode::Error) -> Self {
SledTripleStoreError::SerializationError(e)
}
}
pub struct SledTripleStore<
Id: IdType,
NodeProps: Property + Serialize + DeserializeOwned,
EdgeProps: Serialize + DeserializeOwned,
> {
_phantom: std::marker::PhantomData<(Id, NodeProps, EdgeProps)>,
node_props: sled::Tree,
edge_props: sled::Tree,
spo_data: sled::Tree,
pos_data: sled::Tree,
osp_data: sled::Tree,
id_generator: Box<dyn IdGenerator<Id>>,
}
impl<
Id: IdType,
NodeProps: Property + Serialize + DeserializeOwned,
EdgeProps: Property + Serialize + DeserializeOwned,
> SledTripleStore<Id, NodeProps, EdgeProps>
{
pub fn new(
db: &sled::Db,
id_generator: impl IdGenerator<Id> + 'static,
) -> Result<Self, SledTripleStoreError> {
let node_data = db
.open_tree(b"node_data")
.map_err(|e| SledTripleStoreError::SledError(e))?;
let edge_data = db.open_tree(b"edge_data")?;
let spo_data = db.open_tree(b"spo_data")?;
let pos_data = db.open_tree(b"pos_data")?;
let osp_data = db.open_tree(b"osp_data")?;
Ok(Self {
node_props: node_data,
edge_props: edge_data,
spo_data,
pos_data,
osp_data,
id_generator: Box::new(id_generator),
_phantom: std::marker::PhantomData,
})
}
}
impl<
Id: IdType,
NodeProps: Property + Serialize + DeserializeOwned,
EdgeProps: Property + Serialize + DeserializeOwned,
> TripleStoreError for SledTripleStore<Id, NodeProps, EdgeProps>
{
type Error = SledTripleStoreError;
}
impl<
Id: IdType,
NodeProps: Property + Serialize + DeserializeOwned,
EdgeProps: Property + Serialize + DeserializeOwned,
> TripleStore<Id, NodeProps, EdgeProps> for SledTripleStore<Id, NodeProps, EdgeProps>
{
}
impl<
Id: IdType,
NodeProps: Property + Serialize + DeserializeOwned,
EdgeProps: Property + Serialize + DeserializeOwned,
> std::fmt::Debug for SledTripleStore<Id, NodeProps, EdgeProps>
{
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str("SledTripleStore:\n")?;
f.write_str(" Node Properties:\n")?;
for r in self.node_props.iter() {
let (id, node_props) = r.map_err(|_| std::fmt::Error)?;
f.write_fmt(format_args!(
" {} -> {:?}\n",
Id::try_from_be_bytes(&id).ok_or(std::fmt::Error)?,
bincode::deserialize(&node_props).map_err(|_| std::fmt::Error)?
))?;
}
f.write_str(" Edge Properties:\n")?;
let ulid_to_spo_edge_hash = self
.spo_data
.iter()
.map(|r| r.map_err(|_| std::fmt::Error))
.collect::<Result<Vec<_>, _>>()?
.into_iter()
.map(|(k, v)| {
let hash;
{
let mut hash_builder = std::hash::DefaultHasher::new();
k.as_ref().hash(&mut hash_builder);
hash = hash_builder.finish();
}
(v.clone(), hash)
})
.collect::<HashMap<_, _>>();
let hash_to_edge_data = self
.edge_props
.iter()
.map(|r| r.map_err(|_| std::fmt::Error))
.collect::<Result<Vec<_>, _>>()?
.into_iter()
.map(|(ulid, edge_data)| match ulid_to_spo_edge_hash.get(&ulid) {
Some(hash) => (Some(hash), edge_data),
None => (None, edge_data),
})
.collect::<BTreeMap<_, _>>();
for (hash, node_props) in hash_to_edge_data {
match hash {
None => {
f.write_fmt(format_args!(" _ -> {:?}\n", node_props))?;
}
Some(hash) => {
f.write_fmt(format_args!(" {:#016x} -> {:?}\n", hash, node_props))?;
}
}
}
f.write_str(" Edges (SPO):\n")?;
for r in self.spo_data.iter() {
let (triple, ulid) = r.map_err(|_| std::fmt::Error)?;
let triple =
Id::decode_spo_triple(&triple[..].try_into().map_err(|_| std::fmt::Error)?);
f.write_fmt(format_args!(
" ({}, {}, {}) -> ",
triple.sub, triple.pred, triple.obj
))?;
match ulid_to_spo_edge_hash.get(&ulid) {
Some(hash) => {
f.write_fmt(format_args!("{:#016x}\n", hash))?;
}
None => {
f.write_str("_\n")?;
}
}
}
f.write_str(" Edges (POS):\n")?;
for r in self.pos_data.iter() {
let (triple, ulid) = r.map_err(|_| std::fmt::Error)?;
let triple =
Id::decode_pos_triple(&triple[..].try_into().map_err(|_| std::fmt::Error)?);
f.write_fmt(format_args!(
" ({}, {}, {}) -> ",
triple.sub, triple.pred, triple.obj
))?;
match ulid_to_spo_edge_hash.get(&ulid) {
Some(hash) => {
f.write_fmt(format_args!("{:#016x}\n", hash))?;
}
None => {
f.write_str("_\n")?;
}
}
}
f.write_str(" Edges (OSP):\n")?;
for r in self.osp_data.iter() {
let (triple, ulid) = r.map_err(|_| std::fmt::Error)?;
let triple =
Id::decode_osp_triple(&triple[..].try_into().map_err(|_| std::fmt::Error)?);
f.write_fmt(format_args!(
" ({}, {}, {}) -> ",
triple.sub, triple.pred, triple.obj
))?;
match ulid_to_spo_edge_hash.get(&ulid) {
Some(hash) => {
f.write_fmt(format_args!("{:#016x}\n", hash))?;
}
None => {
f.write_str("_\n")?;
}
}
}
Ok(())
}
}
#[cfg(test)]
pub(crate) fn create_test_db() -> Result<(tempdir::TempDir, sled::Db), sled::Error> {
let temp_dir = tempdir::TempDir::new("SledTripleStore")?;
let db = sled::open(temp_dir.path())?;
Ok((temp_dir, db))
}