use crate::graph::GraphStorage;
use crate::graph::schema::value_type_name;
use crate::*;
#[derive(Clone)]
struct StepSelectColumn {
column: Column,
}
impl fmt::Display for StepSelectColumn {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "select(Column.{})", self.column)
}
}
pub(super) fn select_column_step<'g, S: GraphStorage + 'g>(column: Column) -> GraphStep<'g, S> {
GraphStep::Map("select", Box::new(StepSelectColumn { column }))
}
fn select_column_error(actual: String) -> Box<TraverserError> {
Box::new(TraverserError::cast_exception_target(
crate::error::SELECT_COLUMN_EXPECTED,
actual,
None,
None,
))
}
impl StepSelectColumn {
fn pick_from_map<S: GraphStorage>(
&self,
value: TraverserValue<S>,
) -> Result<TraverserValue<S>, TraverserValue<S>> {
match value {
TraverserValue::Entry(entry) => {
let (key, value) = *entry;
Ok(match self.column {
Column::Keys => key,
Column::Values => value,
})
}
TraverserValue::Object(obj) => Ok(TraverserValue::Array(match self.column {
Column::Keys => obj.0.into_keys().collect(),
Column::Values => obj.0.into_values().collect(),
})),
TraverserValue::Record { keys, values } => {
Ok(TraverserValue::Array(match self.column {
Column::Keys => keys
.iter()
.map(|k| TraverserValue::Computed(ElementProperty::String(k.clone()), None))
.collect(),
Column::Values => values,
}))
}
TraverserValue::Computed(ElementProperty::Object(obj), _) => {
self.pick_from_map(TraverserValue::from(ElementProperty::Object(obj)))
}
TraverserValue::Computed(ElementProperty::Map(map), _) => {
self.pick_from_map(TraverserValue::from(ElementProperty::Map(map)))
}
TraverserValue::Path(path) => Ok(match self.column {
Column::Keys => TraverserValue::path_labels(&path),
Column::Values => {
TraverserValue::Array(path.steps.into_iter().map(|step| step.value).collect())
}
}),
other => Err(other),
}
}
}
impl<'g, S: GraphStorage + 'g> GraphStepMap<'g, S> for StepSelectColumn {
#[inline]
fn process(
&self,
context: &mut TraverserContext<'_, S>,
source: Traverser<S>,
) -> TraverserResult<Traverser<S>> {
let stored = match &source.value {
TraverserValue::VertexProperty(prop, index, _) => Some(
context
.storage
.vertex_properties(*index)
.and_then(|p| p.get(prop.as_str())),
),
TraverserValue::EdgeProperty(prop, index, _) => Some(
context
.storage
.edge_properties(*index)
.and_then(|p| p.get(prop.as_str())),
),
_ => None,
};
let value = match stored {
Some(Some(property @ (ElementProperty::Object(_) | ElementProperty::Map(_)))) => {
TraverserValue::from(property)
}
Some(Some(property)) => return Err(select_column_error(value_type_name(property))),
Some(None) => return Err(select_column_error("null".to_string())),
None => source.value,
};
match self.pick_from_map(value) {
Ok(value) => Ok(Traverser::with_parts(
value,
source.path,
source.bulk,
source.sack,
)),
Err(TraverserValue::Computed(value, _)) => {
Err(select_column_error(value_type_name(&value)))
}
Err(other) => Err(select_column_error(other.type_name().to_string())),
}
}
fn materializes(&self) -> bool {
true
}
fn clone_box(&self) -> Box<dyn GraphStepMap<'g, S> + 'g> {
Box::new(self.clone())
}
}