use crate::{
store::traits::{GraphSnapshot, GraphStore},
types::{
element::{Edge, Property, Vertex},
keys::{
AdjacentEdgeCursor, AdjacentEdgesOptions, CanonicalEdgeKey, CanonicalKey, Direction, EdgeKey, LabelId,
VertexKey,
},
prop_key::PropKey,
prop_key::{ID_KEY_ID, LABEL_KEY_ID},
Primitive, StoreError,
},
};
use std::collections::{hash_map::Entry, HashMap};
use super::ScanConfig;
pub(crate) struct LogicalSnapshot<S: GraphStore> {
store: S::Snapshot,
vertices: HashMap<VertexKey, Vertex>,
edges: HashMap<CanonicalEdgeKey, Edge>,
vertex_degree: HashMap<VertexKey, (u32, u32, LabelId)>,
pub(crate) scan_config: ScanConfig,
pub(crate) schema: std::sync::Arc<std::sync::RwLock<crate::schema::Schema>>,
}
impl<S: GraphStore> LogicalSnapshot<S> {
pub fn new(snapshot: S::Snapshot, schema: std::sync::Arc<std::sync::RwLock<crate::schema::Schema>>) -> Self {
Self {
store: snapshot,
vertices: HashMap::new(),
edges: HashMap::new(),
vertex_degree: HashMap::new(),
scan_config: ScanConfig::default(),
schema,
}
}
pub(crate) fn clear_caches(&mut self) {
self.vertices.clear();
self.edges.clear();
}
fn cache_vertex_label(&mut self, id: VertexKey, label_id: LabelId) {
self.vertices.entry(id).or_insert_with(|| Vertex::label_only(id, label_id));
}
fn ensure_vertex_props_loaded(&mut self, id: VertexKey) -> Result<(), StoreError> {
if self.vertices.get(&id).is_some_and(Vertex::is_label_only) {
match self.store.get_vertex(id)? {
Some(fresh) => {
self.vertices.insert(id, fresh);
}
None => return Err(StoreError::CorruptData("vertex missing for LabelOnly cache entry")),
}
}
Ok(())
}
pub(crate) fn get_vertex(&mut self, key: VertexKey) -> Result<Option<VertexKey>, StoreError> {
if !self.vertices.contains_key(&key) {
match self.store.get_vertex(key)? {
None => return Ok(None),
Some(vt) => {
self.vertices.insert(key, vt);
}
}
}
Ok(Some(key))
}
pub(crate) fn get_edge(&mut self, key: &EdgeKey) -> Result<Option<EdgeKey>, StoreError> {
let cek = key.canonical_edge_key();
if !self.edges.contains_key(&cek) {
match self.store.get_edge(key)? {
None => return Ok(None),
Some(eg) => {
if let Some(l) = eg.src_label {
self.cache_vertex_label(eg.src_id, l);
}
if let Some(l) = eg.dst_label {
self.cache_vertex_label(eg.dst_id, l);
}
self.edges.insert(cek, eg);
}
}
}
Ok(Some(*key))
}
pub(crate) fn get_vertices(&mut self, keys: &[VertexKey]) -> Result<Vec<VertexKey>, StoreError> {
let mut missing_keys = Vec::new();
for &k in keys {
if !self.vertices.contains_key(&k) {
missing_keys.push(k);
}
}
if !missing_keys.is_empty() {
let fetched = self.store.get_vertices(&missing_keys)?;
for vt in fetched {
self.vertices.insert(vt.id, vt);
}
}
let mut result = Vec::new();
for &k in keys {
if self.vertices.contains_key(&k) {
result.push(k);
}
}
Ok(result)
}
pub(crate) fn get_edges(&mut self, keys: &[EdgeKey]) -> Result<Vec<EdgeKey>, StoreError> {
let mut missing_keys = Vec::new();
for k in keys {
let cek = k.canonical_edge_key();
if !self.edges.contains_key(&cek) {
missing_keys.push(*k);
}
}
if !missing_keys.is_empty() {
let fetched = self.store.get_edges(&missing_keys)?;
for eg in fetched {
if let Some(l) = eg.src_label {
self.cache_vertex_label(eg.src_id, l);
}
if let Some(l) = eg.dst_label {
self.cache_vertex_label(eg.dst_id, l);
}
self.edges.insert(eg.canonical_key(), eg);
}
}
let mut result = Vec::new();
for k in keys {
let cek = k.canonical_edge_key();
if self.edges.contains_key(&cek) {
result.push(*k);
}
}
Ok(result)
}
pub(crate) fn get_adjacent_edges(
&mut self,
vertex: VertexKey,
direction: Direction,
opts: AdjacentEdgesOptions<'_>,
limit: Option<u32>,
) -> Result<(Vec<EdgeKey>, Option<AdjacentEdgeCursor>), StoreError> {
let (committed, cursor) = self.store.get_adjacent_edges(vertex, direction, opts, limit)?;
let mut result = Vec::with_capacity(committed.len());
for edge in committed {
if let Some(l) = edge.src_label {
self.cache_vertex_label(edge.src_id, l);
}
if let Some(l) = edge.dst_label {
self.cache_vertex_label(edge.dst_id, l);
}
let cek = edge.canonical_key();
result.push(match direction {
Direction::OUT => cek.out_key(),
Direction::IN => cek.in_key(),
});
self.edges.entry(cek).or_insert(edge);
}
Ok((result, cursor))
}
pub(crate) fn scan_vertices(
&mut self,
label: Option<LabelId>,
start_from: Option<VertexKey>,
limit: u32,
) -> Result<(Vec<VertexKey>, Option<VertexKey>), StoreError> {
let (committed, cursor) = self.store.scan_vertices(label, start_from, limit)?;
let mut result = Vec::with_capacity(committed.len());
for vt in committed {
result.push(vt.id);
match self.vertices.entry(vt.id) {
Entry::Vacant(e) => {
e.insert(vt);
}
Entry::Occupied(mut e) if e.get().is_label_only() => {
e.insert(vt);
}
Entry::Occupied(_) => {}
}
}
Ok((result, cursor))
}
pub(crate) fn scan_edges(
&mut self,
label: Option<LabelId>,
start_from: Option<CanonicalEdgeKey>,
limit: u32,
) -> Result<(Vec<EdgeKey>, Option<CanonicalEdgeKey>), StoreError> {
let (committed, cursor) = self.store.scan_edges(label, start_from, limit)?;
let mut result = Vec::with_capacity(committed.len());
for edge in committed {
if let Some(l) = edge.src_label {
self.cache_vertex_label(edge.src_id, l);
}
if let Some(l) = edge.dst_label {
self.cache_vertex_label(edge.dst_id, l);
}
let cek = edge.canonical_key();
result.push(cek.out_key());
self.edges.entry(cek).or_insert(edge);
}
Ok((result, cursor))
}
pub(crate) fn get_property(
&mut self,
key: &CanonicalKey,
prop_key_id: u16,
) -> Result<Option<Property>, StoreError> {
match *key {
CanonicalKey::Vertex(vk) => {
if self.get_vertex(vk)?.is_some() {
if prop_key_id != ID_KEY_ID && prop_key_id != LABEL_KEY_ID {
self.ensure_vertex_props_loaded(vk)?;
}
Ok(self.vertices.get(&vk).unwrap().get_property(prop_key_id))
} else {
Ok(None)
}
}
CanonicalKey::Edge(ek) => Ok(self.edges.get(&ek).and_then(|eg| eg.get_property(prop_key_id))),
CanonicalKey::Empty => Err(StoreError::TraversalError("Property owner cannot be empty".to_string())),
}
}
pub(crate) fn get_value(&mut self, key: &CanonicalKey, prop_key_id: u16) -> Result<Option<Primitive>, StoreError> {
match *key {
CanonicalKey::Vertex(vk) => {
if self.get_vertex(vk)?.is_some() {
if prop_key_id != ID_KEY_ID && prop_key_id != LABEL_KEY_ID {
self.ensure_vertex_props_loaded(vk)?;
}
Ok(self.vertices.get(&vk).unwrap().get_value(prop_key_id))
} else {
Ok(None)
}
}
CanonicalKey::Edge(ek) => Ok(self.edges.get(&ek).and_then(|eg| eg.get_value(prop_key_id))),
CanonicalKey::Empty => {
Err(StoreError::UnexpectedDataType("expected Vertex or Edge for get property value".to_string()))
}
}
}
#[allow(clippy::type_complexity)]
pub(crate) fn get_all_props(
&mut self,
key: &CanonicalKey,
) -> Result<Option<(LabelId, Vec<(PropKey, Primitive)>)>, StoreError> {
match *key {
CanonicalKey::Vertex(vk) => {
if self.get_vertex(vk)?.is_none() {
return Ok(None);
}
self.ensure_vertex_props_loaded(vk)?;
let vt = self.vertices.get_mut(&vk).unwrap();
let label_id = vt.label_id;
let schema = self.schema.read().unwrap();
let props = vt
.props()
.iter()
.map(|(&k, v)| {
let name = schema
.prop_key_str(k)
.cloned()
.unwrap_or_else(|| smol_str::SmolStr::from(format!("__key_{}", k)));
(name, v.clone())
})
.collect();
Ok(Some((label_id, props)))
}
CanonicalKey::Edge(ek) => {
if self.get_edge(&ek.out_key())?.is_none() {
return Ok(None);
}
let eg = self.edges.get_mut(&ek).unwrap();
let label_id = eg.label_id;
let schema = self.schema.read().unwrap();
let props = eg
.props()
.iter()
.map(|(&k, v)| {
let name = schema
.prop_key_str(k)
.cloned()
.unwrap_or_else(|| smol_str::SmolStr::from(format!("__key_{}", k)));
(name, v.clone())
})
.collect();
Ok(Some((label_id, props)))
}
CanonicalKey::Empty => Err(StoreError::TraversalError("Element key cannot be empty".to_string())),
}
}
fn get_vertex_degree(&mut self, key: VertexKey) -> Result<Option<(u32, u32, LabelId)>, StoreError> {
if let Some(&cached) = self.vertex_degree.get(&key) {
return Ok(Some(cached));
}
match self.store.get_vertex_degree(key)? {
None => Ok(None),
Some(t) => {
self.vertex_degree.insert(key, t);
Ok(Some(t))
}
}
}
pub(crate) fn get_degree(
&mut self,
key: VertexKey,
direction: crate::types::DegreeDirection,
) -> Result<u64, StoreError> {
let (out_cnt, in_cnt, label_id) = match self.get_vertex_degree(key)? {
Some(t) => t,
None => return Ok(0),
};
self.cache_vertex_label(key, label_id);
Ok(match direction {
crate::types::DegreeDirection::Out => out_cnt as u64,
crate::types::DegreeDirection::In => in_cnt as u64,
crate::types::DegreeDirection::Both => out_cnt as u64 + in_cnt as u64,
})
}
}