mod sqlite;
pub mod native;
pub use sqlite::SqliteGraphBackend;
pub use native::NativeGraphBackend;
pub use crate::multi_hop::ChainStep;
#[allow(unused_imports)] pub use sqlite::types::{BackendDirection, EdgeSpec, NeighborQuery, NodeSpec};
#[cfg(feature = "native-v2")]
pub use crate::backend::native::v2::kv_store::types::{KvStoreError, KvValue};
#[cfg(feature = "native-v2")]
pub use crate::backend::native::v2::pubsub::{PubSubEvent, SubscriptionFilter};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum PubSubEventType {
NodeChanged = 1,
EdgeChanged = 2,
KVChanged = 3,
SnapshotCommitted = 4,
All = 255,
}
#[cfg(not(feature = "native-v2"))]
mod pubsub_types {
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum PubSubEvent {
NodeChanged {
node_id: i64,
snapshot_id: u64,
},
EdgeChanged {
edge_id: i64,
snapshot_id: u64,
},
KVChanged {
key_hash: u64,
snapshot_id: u64,
},
SnapshotCommitted {
snapshot_id: u64,
},
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub struct SubscriptionFilter {
pub node_changes: bool,
pub edge_changes: bool,
pub kv_changes: bool,
pub snapshot_commits: bool,
}
impl SubscriptionFilter {
pub fn all() -> Self {
Self {
node_changes: true,
edge_changes: true,
kv_changes: true,
snapshot_commits: true,
}
}
}
}
#[cfg(not(feature = "native-v2"))]
pub use pubsub_types::{PubSubEvent, SubscriptionFilter};
use crate::{
SqliteGraphError,
graph::GraphEntity,
pattern::{PatternMatch, PatternQuery},
snapshot::SnapshotId,
};
pub trait GraphBackend {
fn insert_node(&self, node: NodeSpec) -> Result<i64, SqliteGraphError>;
fn insert_edge(&self, edge: EdgeSpec) -> Result<i64, SqliteGraphError>;
fn update_node(&self, node_id: i64, node: NodeSpec) -> Result<i64, SqliteGraphError>;
fn delete_entity(&self, id: i64) -> Result<(), SqliteGraphError>;
fn entity_ids(&self) -> Result<Vec<i64>, SqliteGraphError>;
fn all_entity_ids(&self) -> Result<Vec<i64>, SqliteGraphError> {
self.entity_ids()
}
fn fetch_outgoing(&self, node: i64) -> Result<Vec<i64>, SqliteGraphError> {
self.neighbors(
SnapshotId::current(),
node,
NeighborQuery {
direction: BackendDirection::Outgoing,
edge_type: None,
},
)
}
fn fetch_incoming(&self, node: i64) -> Result<Vec<i64>, SqliteGraphError> {
self.neighbors(
SnapshotId::current(),
node,
NeighborQuery {
direction: BackendDirection::Incoming,
edge_type: None,
},
)
}
fn get_node(&self, snapshot_id: SnapshotId, id: i64) -> Result<GraphEntity, SqliteGraphError>;
fn neighbors(
&self,
snapshot_id: SnapshotId,
node: i64,
query: NeighborQuery,
) -> Result<Vec<i64>, SqliteGraphError>;
fn bfs(
&self,
snapshot_id: SnapshotId,
start: i64,
depth: u32,
) -> Result<Vec<i64>, SqliteGraphError>;
fn shortest_path(
&self,
snapshot_id: SnapshotId,
start: i64,
end: i64,
) -> Result<Option<Vec<i64>>, SqliteGraphError>;
fn node_degree(
&self,
snapshot_id: SnapshotId,
node: i64,
) -> Result<(usize, usize), SqliteGraphError>;
fn k_hop(
&self,
snapshot_id: SnapshotId,
start: i64,
depth: u32,
direction: BackendDirection,
) -> Result<Vec<i64>, SqliteGraphError>;
fn k_hop_filtered(
&self,
snapshot_id: SnapshotId,
start: i64,
depth: u32,
direction: BackendDirection,
allowed_edge_types: &[&str],
) -> Result<Vec<i64>, SqliteGraphError>;
fn chain_query(
&self,
snapshot_id: SnapshotId,
start: i64,
chain: &[crate::multi_hop::ChainStep],
) -> Result<Vec<i64>, SqliteGraphError>;
fn pattern_search(
&self,
snapshot_id: SnapshotId,
start: i64,
pattern: &PatternQuery,
) -> Result<Vec<PatternMatch>, SqliteGraphError>;
fn checkpoint(&self) -> Result<(), SqliteGraphError>;
fn flush(&self) -> Result<(), SqliteGraphError>;
fn backup(&self, backup_dir: &std::path::Path) -> Result<BackupResult, SqliteGraphError>;
fn snapshot_export(
&self,
export_dir: &std::path::Path,
) -> Result<SnapshotMetadata, SqliteGraphError>;
fn snapshot_import(
&self,
import_dir: &std::path::Path,
) -> Result<ImportMetadata, SqliteGraphError>;
#[cfg(feature = "native-v2")]
fn kv_get(
&self,
snapshot_id: SnapshotId,
key: &[u8],
) -> Result<Option<crate::backend::native::v2::kv_store::types::KvValue>, SqliteGraphError>;
#[cfg(feature = "native-v2")]
fn kv_set(
&self,
key: Vec<u8>,
value: crate::backend::native::v2::kv_store::types::KvValue,
ttl_seconds: Option<u64>,
) -> Result<(), SqliteGraphError>;
#[cfg(feature = "native-v2")]
fn kv_delete(&self, key: &[u8]) -> Result<(), SqliteGraphError>;
fn subscribe(
&self,
_filter: SubscriptionFilter,
) -> Result<(u64, std::sync::mpsc::Receiver<PubSubEvent>), SqliteGraphError> {
Err(SqliteGraphError::unsupported("Pub/Sub not supported by this backend"))
}
fn unsubscribe(&self, _subscriber_id: u64) -> Result<bool, SqliteGraphError> {
Err(SqliteGraphError::unsupported("Pub/Sub not supported by this backend"))
}
#[cfg(feature = "native-v2")]
fn kv_prefix_scan(
&self,
snapshot_id: SnapshotId,
prefix: &[u8],
) -> Result<
Vec<(
Vec<u8>,
crate::backend::native::v2::kv_store::types::KvValue,
)>,
SqliteGraphError,
>;
fn query_nodes_by_kind(
&self,
snapshot_id: SnapshotId,
kind: &str,
) -> Result<Vec<i64>, SqliteGraphError>;
fn query_nodes_by_name_pattern(
&self,
snapshot_id: SnapshotId,
pattern: &str,
) -> Result<Vec<i64>, SqliteGraphError>;
}
#[derive(Debug, Clone)]
pub struct SnapshotMetadata {
pub snapshot_path: std::path::PathBuf,
pub size_bytes: u64,
pub entity_count: u64,
pub edge_count: u64,
}
#[derive(Debug, Clone)]
pub struct ImportMetadata {
pub snapshot_path: std::path::PathBuf,
pub entities_imported: u64,
pub edges_imported: u64,
}
#[derive(Debug, Clone)]
pub struct BackupResult {
pub snapshot_path: std::path::PathBuf,
pub manifest_path: std::path::PathBuf,
pub size_bytes: u64,
pub checksum: u64,
pub record_count: u64,
pub duration_secs: f64,
pub timestamp: u64,
pub checkpoint_performed: bool,
}
impl<B> GraphBackend for &B
where
B: GraphBackend + ?Sized,
{
fn insert_node(&self, node: NodeSpec) -> Result<i64, SqliteGraphError> {
(*self).insert_node(node)
}
fn get_node(&self, snapshot_id: SnapshotId, id: i64) -> Result<GraphEntity, SqliteGraphError> {
(*self).get_node(snapshot_id, id)
}
fn insert_edge(&self, edge: EdgeSpec) -> Result<i64, SqliteGraphError> {
(*self).insert_edge(edge)
}
fn update_node(&self, node_id: i64, node: NodeSpec) -> Result<i64, SqliteGraphError> {
(*self).update_node(node_id, node)
}
fn delete_entity(&self, id: i64) -> Result<(), SqliteGraphError> {
(*self).delete_entity(id)
}
fn entity_ids(&self) -> Result<Vec<i64>, SqliteGraphError> {
(*self).entity_ids()
}
fn neighbors(
&self,
snapshot_id: SnapshotId,
node: i64,
query: NeighborQuery,
) -> Result<Vec<i64>, SqliteGraphError> {
(*self).neighbors(snapshot_id, node, query)
}
fn bfs(
&self,
snapshot_id: SnapshotId,
start: i64,
depth: u32,
) -> Result<Vec<i64>, SqliteGraphError> {
(*self).bfs(snapshot_id, start, depth)
}
fn shortest_path(
&self,
snapshot_id: SnapshotId,
start: i64,
end: i64,
) -> Result<Option<Vec<i64>>, SqliteGraphError> {
(*self).shortest_path(snapshot_id, start, end)
}
fn node_degree(
&self,
snapshot_id: SnapshotId,
node: i64,
) -> Result<(usize, usize), SqliteGraphError> {
(*self).node_degree(snapshot_id, node)
}
fn k_hop(
&self,
snapshot_id: SnapshotId,
start: i64,
depth: u32,
direction: BackendDirection,
) -> Result<Vec<i64>, SqliteGraphError> {
(*self).k_hop(snapshot_id, start, depth, direction)
}
fn k_hop_filtered(
&self,
snapshot_id: SnapshotId,
start: i64,
depth: u32,
direction: BackendDirection,
allowed_edge_types: &[&str],
) -> Result<Vec<i64>, SqliteGraphError> {
(*self).k_hop_filtered(snapshot_id, start, depth, direction, allowed_edge_types)
}
fn chain_query(
&self,
snapshot_id: SnapshotId,
start: i64,
chain: &[crate::multi_hop::ChainStep],
) -> Result<Vec<i64>, SqliteGraphError> {
(*self).chain_query(snapshot_id, start, chain)
}
fn pattern_search(
&self,
snapshot_id: SnapshotId,
start: i64,
pattern: &PatternQuery,
) -> Result<Vec<PatternMatch>, SqliteGraphError> {
(*self).pattern_search(snapshot_id, start, pattern)
}
fn checkpoint(&self) -> Result<(), SqliteGraphError> {
(*self).checkpoint()
}
fn flush(&self) -> Result<(), SqliteGraphError> {
(*self).flush()
}
fn backup(&self, backup_dir: &std::path::Path) -> Result<BackupResult, SqliteGraphError> {
(*self).backup(backup_dir)
}
fn snapshot_export(
&self,
export_dir: &std::path::Path,
) -> Result<SnapshotMetadata, SqliteGraphError> {
(*self).snapshot_export(export_dir)
}
fn snapshot_import(
&self,
import_dir: &std::path::Path,
) -> Result<ImportMetadata, SqliteGraphError> {
(*self).snapshot_import(import_dir)
}
#[cfg(feature = "native-v2")]
fn kv_get(
&self,
snapshot_id: SnapshotId,
key: &[u8],
) -> Result<Option<crate::backend::native::v2::kv_store::types::KvValue>, SqliteGraphError>
{
(*self).kv_get(snapshot_id, key)
}
#[cfg(feature = "native-v2")]
fn kv_set(
&self,
key: Vec<u8>,
value: crate::backend::native::v2::kv_store::types::KvValue,
ttl_seconds: Option<u64>,
) -> Result<(), SqliteGraphError> {
(*self).kv_set(key, value, ttl_seconds)
}
#[cfg(feature = "native-v2")]
fn kv_delete(&self, key: &[u8]) -> Result<(), SqliteGraphError> {
(*self).kv_delete(key)
}
fn subscribe(
&self,
filter: SubscriptionFilter,
) -> Result<(u64, std::sync::mpsc::Receiver<PubSubEvent>), SqliteGraphError> {
(*self).subscribe(filter)
}
fn unsubscribe(&self, subscriber_id: u64) -> Result<bool, SqliteGraphError> {
(*self).unsubscribe(subscriber_id)
}
#[cfg(feature = "native-v2")]
fn kv_prefix_scan(
&self,
snapshot_id: SnapshotId,
prefix: &[u8],
) -> Result<
Vec<(
Vec<u8>,
crate::backend::native::v2::kv_store::types::KvValue,
)>,
SqliteGraphError,
> {
(*self).kv_prefix_scan(snapshot_id, prefix)
}
fn query_nodes_by_kind(
&self,
snapshot_id: SnapshotId,
kind: &str,
) -> Result<Vec<i64>, SqliteGraphError> {
(*self).query_nodes_by_kind(snapshot_id, kind)
}
fn query_nodes_by_name_pattern(
&self,
snapshot_id: SnapshotId,
pattern: &str,
) -> Result<Vec<i64>, SqliteGraphError> {
(*self).query_nodes_by_name_pattern(snapshot_id, pattern)
}
}