use std::collections::HashMap;
use smol_str::SmolStr;
use crate::{
engine::volcano::builder::PhysicalPlan,
gremlin::{
type_bridge::primitive_to_value,
value::{Edge as UserEdge, Map, Path, Property as UserProperty, Value, Vertex as UserVertex},
},
schema::Schema,
types::{
gvalue::{GValue, Primitive},
keys::{CanonicalKey, LabelId, VertexKey},
prop_key::{ID_KEY_ID, LABEL_KEY_ID, RANK_KEY_ID},
StoreError,
},
};
pub(crate) struct SchemaCache {
vertex_labels: HashMap<LabelId, SmolStr>,
edge_labels: HashMap<LabelId, SmolStr>,
prop_key_str: HashMap<u16, SmolStr>,
prop_key_id: HashMap<SmolStr, u16>,
}
impl SchemaCache {
pub(crate) fn from_schema(schema: &Schema) -> Self {
let mut vertex_labels = HashMap::new();
for id in 1..=schema.vertex_labels_count() as LabelId {
if let Some(name) = schema.vertex_label_str(id) {
vertex_labels.insert(id, name.clone());
}
}
let mut edge_labels = HashMap::new();
for id in 1..=schema.edge_labels_count() as LabelId {
if let Some(name) = schema.edge_label_str(id) {
edge_labels.insert(id, name.clone());
}
}
let mut prop_key_str = HashMap::new();
let mut prop_key_id = HashMap::new();
for (&id, key) in &schema.prop_keys {
prop_key_str.insert(id, key.clone());
prop_key_id.insert(key.clone(), id);
}
Self { vertex_labels, edge_labels, prop_key_str, prop_key_id }
}
#[inline]
pub(crate) fn vertex_label(&self, label_id: LabelId) -> &SmolStr {
self.vertex_labels.get(&label_id).unwrap_or_else(|| {
static EMPTY: SmolStr = SmolStr::new_inline("");
&EMPTY
})
}
#[inline]
pub(crate) fn edge_label(&self, label_id: LabelId) -> &SmolStr {
self.edge_labels.get(&label_id).unwrap_or_else(|| {
static EMPTY: SmolStr = SmolStr::new_inline("");
&EMPTY
})
}
#[inline]
pub(crate) fn prop_key_str(&self, id: u16) -> Option<&SmolStr> {
self.prop_key_str.get(&id)
}
#[inline]
pub(crate) fn prop_key_id(&self, name: &str) -> Option<u16> {
self.prop_key_id.get(name).copied()
}
}
pub(crate) fn materialize(
gv: &GValue,
ctx: &mut dyn crate::engine::GraphCtx,
cache: &SchemaCache,
prop_keys: Option<&[SmolStr]>,
) -> Result<Value, StoreError> {
match gv {
GValue::Scalar(ref p) => Ok(primitive_to_value(p.clone())),
GValue::Vertex(vk) => materialize_vertex(*vk, ctx, cache, prop_keys),
GValue::Edge(ek) => materialize_edge(*ek, ctx, cache, prop_keys),
GValue::Property(ref p) => {
let key = cache.prop_key_str(p.key).cloned().unwrap_or_else(|| SmolStr::from(format!("key_{}", p.key)));
Ok(Value::Property(UserProperty { key, value: Box::new(primitive_to_value(p.value.clone())) }))
}
GValue::List(ref list) => {
let mut out = Vec::with_capacity(list.len());
for item in list {
out.push(materialize(item, ctx, cache, prop_keys)?);
}
Ok(Value::List(out))
}
GValue::Map(ref map) => {
let mut out = Map::new();
for (k, v) in map {
out.entries.push((materialize(k, ctx, cache, prop_keys)?, materialize(v, ctx, cache, prop_keys)?));
}
Ok(Value::Map(out))
}
GValue::Path(ref path) => {
let mut objects = Vec::with_capacity(path.len());
let mut labels: Vec<Vec<String>> = Vec::with_capacity(path.len());
for (val, step_labels) in path {
objects.push(materialize(val, ctx, cache, prop_keys)?);
labels.push(match step_labels {
Some(ls) => ls.iter().map(|s| s.to_string()).collect(),
None => vec![],
});
}
Ok(Value::Path(Path { objects, labels }))
}
}
}
fn materialize_vertex(
vk: VertexKey,
ctx: &mut dyn crate::engine::GraphCtx,
cache: &SchemaCache,
prop_keys: Option<&[SmolStr]>,
) -> Result<Value, StoreError> {
match prop_keys {
None => match ctx.get_value(&CanonicalKey::Vertex(vk), LABEL_KEY_ID)? {
None => Err(StoreError::NotFound),
Some(Primitive::Int32(label_id)) => Ok(Value::Vertex(UserVertex {
id: vk,
label: cache.vertex_label(label_id).clone(),
properties: HashMap::new(),
})),
_ => Err(StoreError::CorruptData("vertex label_id not Int32")),
},
Some([]) => match ctx.get_all_props(&CanonicalKey::Vertex(vk))? {
None => Err(StoreError::NotFound),
Some((label_id, props)) => {
let label = cache.vertex_label(label_id).clone();
let mut properties: HashMap<SmolStr, Vec<Value>> = HashMap::new();
for (key, prim) in props {
properties.entry(key).or_default().push(primitive_to_value(prim));
}
Ok(Value::Vertex(UserVertex { id: vk, label, properties }))
}
},
Some(keys) => match ctx.get_value(&CanonicalKey::Vertex(vk), LABEL_KEY_ID)? {
None => Err(StoreError::NotFound),
Some(Primitive::Int32(label_id)) => {
let label = cache.vertex_label(label_id).clone();
let mut properties: HashMap<SmolStr, Vec<Value>> = HashMap::new();
for key in keys {
let Some(prop_key_id) = cache.prop_key_id(key) else { continue };
if matches!(prop_key_id, ID_KEY_ID | LABEL_KEY_ID | RANK_KEY_ID) {
continue;
}
if let Some(val) = ctx.get_value(&CanonicalKey::Vertex(vk), prop_key_id)? {
properties.entry(key.clone()).or_default().push(primitive_to_value(val));
}
}
Ok(Value::Vertex(UserVertex { id: vk, label, properties }))
}
_ => Err(StoreError::CorruptData("vertex label_id not Int32")),
},
}
}
fn materialize_edge(
ek: crate::types::keys::EdgeKey,
ctx: &mut dyn crate::engine::GraphCtx,
cache: &SchemaCache,
prop_keys: Option<&[SmolStr]>,
) -> Result<Value, StoreError> {
let cek = ek.canonical_edge_key();
match prop_keys {
None => Ok(Value::Edge(UserEdge {
id: ek.to_id_string(),
out_v: cek.src_id,
in_v: cek.dst_id,
label: cache.edge_label(ek.label_id).clone(),
rank: cek.rank,
properties: HashMap::new(),
})),
Some([]) => match ctx.get_all_props(&CanonicalKey::Edge(cek))? {
None => Err(StoreError::NotFound),
Some((label_id, props)) => {
let label = cache.edge_label(label_id).clone();
let mut properties: HashMap<SmolStr, Value> = HashMap::new();
for (key, prim) in props {
properties.insert(key, primitive_to_value(prim));
}
Ok(Value::Edge(UserEdge {
id: ek.to_id_string(),
out_v: cek.src_id,
in_v: cek.dst_id,
label,
rank: cek.rank,
properties,
}))
}
},
Some(keys) => {
if ctx.get_edge(&ek)?.is_none() {
return Err(StoreError::NotFound);
}
let label = cache.edge_label(ek.label_id).clone();
let mut properties: HashMap<SmolStr, Value> = HashMap::new();
for key in keys {
let Some(prop_key_id) = cache.prop_key_id(key) else { continue };
if matches!(prop_key_id, ID_KEY_ID | LABEL_KEY_ID | RANK_KEY_ID) {
continue;
}
if let Some(val) = ctx.get_value(&CanonicalKey::Edge(cek), prop_key_id)? {
properties.insert(key.clone(), primitive_to_value(val));
}
}
Ok(Value::Edge(UserEdge {
id: ek.to_id_string(),
out_v: cek.src_id,
in_v: cek.dst_id,
label,
rank: cek.rank,
properties,
}))
}
}
}
pub struct BuiltTraversal<'g> {
pub(super) graph: &'g mut dyn crate::engine::GraphCtx,
pub(super) plan: PhysicalPlan,
pub(super) prop_keys: Option<Vec<SmolStr>>,
pub(super) cache: SchemaCache,
}
impl<'g> Iterator for BuiltTraversal<'g> {
type Item = Result<Value, StoreError>;
fn next(&mut self) -> Option<Self::Item> {
match self.plan.next(self.graph) {
Err(e) => Some(Err(e)),
Ok(None) => None,
Ok(Some(t)) => {
if let GValue::Scalar(ref p) = &t.value {
return Some(Ok(primitive_to_value(p.clone())));
}
Some(materialize(&t.value, self.graph, &self.cache, self.prop_keys.as_deref()))
}
}
}
}