use std::collections::{BTreeMap, BTreeSet};
use uqa_core::{Edge, EdgeId, Vertex, VertexId};
use crate::posting_list::GraphPostingListError;
use crate::types::Direction;
#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error)]
pub enum GraphStoreError {
#[error("graph {0:?} does not exist")]
UnknownGraph(String),
#[error("graph id space exhausted: {0}")]
IdExhausted(String),
#[error("invalid graph mutation: {0}")]
InvalidMutation(String),
#[error("invalid graph query: {0}")]
InvalidQuery(String),
#[error("corrupt graph state: {0}")]
CorruptGraph(String),
#[error("graph storage error: {0}")]
Storage(String),
#[error("graph serialization failure: {0}")]
SerializationFailure(String),
#[error(transparent)]
InvalidPostingList(#[from] GraphPostingListError),
}
pub type GraphStoreResult<T> = Result<T, GraphStoreError>;
pub trait GraphStore {
fn transaction<T>(
&mut self,
operation: impl FnOnce(&mut Self) -> GraphStoreResult<T>,
) -> GraphStoreResult<T>
where
Self: Sized;
fn create_graph(&mut self, name: &str) -> GraphStoreResult<()>;
fn drop_graph(&mut self, name: &str) -> GraphStoreResult<()>;
fn graph_names(&self) -> GraphStoreResult<Vec<String>>;
fn has_graph(&self, name: &str) -> GraphStoreResult<bool>;
fn union_graphs(&mut self, g1: &str, g2: &str, target: &str) -> GraphStoreResult<()>;
fn intersect_graphs(&mut self, g1: &str, g2: &str, target: &str) -> GraphStoreResult<()>;
fn difference_graphs(&mut self, g1: &str, g2: &str, target: &str) -> GraphStoreResult<()>;
fn copy_graph(&mut self, source: &str, target: &str) -> GraphStoreResult<()>;
fn add_vertex(&mut self, vertex: Vertex, graph: &str) -> GraphStoreResult<()>;
fn add_edge(&mut self, edge: Edge, graph: &str) -> GraphStoreResult<()>;
fn remove_vertex(&mut self, vertex_id: VertexId, graph: &str) -> GraphStoreResult<()>;
fn remove_edge(&mut self, edge_id: EdgeId, graph: &str) -> GraphStoreResult<()>;
fn neighbors(
&self,
vertex_id: VertexId,
label: Option<&str>,
direction: Direction,
graph: &str,
) -> GraphStoreResult<Vec<VertexId>>;
fn vertices_by_label(&self, label: &str, graph: &str) -> GraphStoreResult<Vec<Vertex>>;
fn vertex_ids_by_label(&self, label: &str, graph: &str) -> GraphStoreResult<Vec<VertexId>> {
Ok(self
.vertices_by_label(label, graph)?
.into_iter()
.map(|vertex| vertex.vertex_id)
.collect())
}
fn vertices_in_graph(&self, graph: &str) -> GraphStoreResult<Vec<Vertex>>;
fn edges_in_graph(&self, graph: &str) -> GraphStoreResult<Vec<Edge>>;
fn edges_by_label(&self, label: &str, graph: &str) -> GraphStoreResult<Vec<Edge>> {
self.edge_ids_by_label(label, graph)?
.into_iter()
.map(|id| {
self.get_edge(id)?
.ok_or_else(|| GraphStoreError::CorruptGraph(format!("missing edge {id}")))
})
.collect()
}
fn vertex_graphs(&self, vertex_id: VertexId) -> GraphStoreResult<BTreeSet<String>>;
fn edge_graphs(&self, edge_id: EdgeId) -> GraphStoreResult<BTreeSet<String>>;
fn out_edge_ids(&self, vertex_id: VertexId, graph: &str) -> GraphStoreResult<BTreeSet<EdgeId>>;
fn in_edge_ids(&self, vertex_id: VertexId, graph: &str) -> GraphStoreResult<BTreeSet<EdgeId>>;
fn edge_ids_by_label(&self, label: &str, graph: &str) -> GraphStoreResult<BTreeSet<EdgeId>>;
fn vertex_ids_in_graph(&self, graph: &str) -> GraphStoreResult<BTreeSet<VertexId>>;
fn vertex_id_page(
&self,
graph: &str,
after: Option<u64>,
limit: usize,
) -> GraphStoreResult<Vec<u64>> {
if !(1..=uqa_storage::MAX_GRAPH_ID_PAGE).contains(&limit) {
return Err(GraphStoreError::InvalidQuery(
"invalid graph page size".into(),
));
}
Ok(self
.vertex_ids_in_graph(graph)?
.into_iter()
.filter(|id| after.is_none_or(|after| *id > after))
.take(limit)
.collect())
}
fn edge_id_page(
&self,
graph: &str,
after: Option<u64>,
limit: usize,
) -> GraphStoreResult<Vec<u64>> {
if !(1..=uqa_storage::MAX_GRAPH_ID_PAGE).contains(&limit) {
return Err(GraphStoreError::InvalidQuery(
"invalid graph page size".into(),
));
}
let mut ids = BTreeSet::new();
for vertex in self.vertex_ids_in_graph(graph)? {
ids.extend(self.out_edge_ids(vertex, graph)?);
}
Ok(ids
.into_iter()
.filter(|id| after.is_none_or(|after| *id > after))
.take(limit)
.collect())
}
fn require_vertex_in_graph(&self, vertex_id: VertexId, graph: &str) -> GraphStoreResult<()> {
if !self.vertex_ids_in_graph(graph)?.contains(&vertex_id) {
return Err(GraphStoreError::InvalidQuery(format!(
"vertex {vertex_id} is not a member of graph {graph:?}"
)));
}
if self.get_vertex(vertex_id)?.is_none() {
return Err(GraphStoreError::CorruptGraph(format!(
"graph {graph:?} references missing vertex {vertex_id}"
)));
}
Ok(())
}
fn degree_distribution(&self, graph: &str) -> GraphStoreResult<BTreeMap<VertexId, u64>>;
fn label_degree(&self, label: &str, graph: &str) -> GraphStoreResult<f64>;
fn vertex_label_counts(&self, graph: &str) -> GraphStoreResult<BTreeMap<String, u64>>;
fn get_vertex(&self, vertex_id: VertexId) -> GraphStoreResult<Option<Vertex>>;
fn get_edge(&self, edge_id: EdgeId) -> GraphStoreResult<Option<Edge>>;
fn next_vertex_id(&mut self) -> GraphStoreResult<VertexId>;
fn next_edge_id(&mut self) -> GraphStoreResult<EdgeId>;
fn allocate_vertex_id(&mut self, _label: &str, _graph: &str) -> GraphStoreResult<VertexId> {
self.next_vertex_id()
}
fn allocate_edge_id(&mut self, _label: &str, _graph: &str) -> GraphStoreResult<EdgeId> {
self.next_edge_id()
}
fn clear(&mut self) -> GraphStoreResult<()>;
fn vertices(&self) -> GraphStoreResult<BTreeMap<VertexId, Vertex>>;
fn edges(&self) -> GraphStoreResult<BTreeMap<EdgeId, Edge>>;
}
impl From<uqa_storage::StorageBackendError> for GraphStoreError {
fn from(error: uqa_storage::StorageBackendError) -> Self {
Self::Storage(error.to_string())
}
}