use crate::optimizer::cost::CostModelConfig;
use crate::planning::statements::seeks::seek_strategy_base::{
NodePattern, SeekStrategyContext, SeekStrategySelector, SeekStrategyType,
};
use linkrs_core::types::graph_schema::EdgeDirection;
use linkrs_core::{StorageError, Value};
use std::sync::Arc;
pub type PlannerError = StorageError;
#[derive(Debug, Clone)]
pub struct VariableLengthPathPattern<'a> {
pub start: &'a NodePattern,
pub edge: &'a EdgePattern,
pub end: &'a NodePattern,
pub lower: Option<usize>,
pub upper: Option<usize>,
}
#[derive(Debug)]
pub struct MatchPathPlanner;
impl Default for MatchPathPlanner {
fn default() -> Self {
Self::new()
}
}
impl MatchPathPlanner {
pub fn new() -> Self {
Self
}
pub fn plan_path_pattern(
&self,
pattern: &PathPattern,
space_id: u64,
) -> Result<PathPlan, PlannerError> {
match &pattern.kind {
PathPatternKind::Simple { start, edge, end } => {
self.plan_simple_pattern(start, edge, end, space_id)
}
PathPatternKind::VariableLength {
start,
edge,
end,
lower,
upper,
} => {
let pattern = VariableLengthPathPattern {
start,
edge,
end,
lower: *lower,
upper: *upper,
};
self.plan_variable_length_pattern(&pattern, space_id)
}
PathPatternKind::Named { name, inner } => {
let inner_plan = self.plan_path_pattern(inner, space_id)?;
Ok(PathPlan::Named {
name: name.clone(),
inner: Box::new(inner_plan),
})
}
}
}
fn plan_simple_pattern(
&self,
start: &NodePattern,
edge: &EdgePattern,
end: &NodePattern,
space_id: u64,
) -> Result<PathPlan, PlannerError> {
let start_finder = self.plan_start_finder(start, space_id)?;
let edge_traversal = self.plan_edge_traversal(edge)?;
let end_finder = self.plan_end_finder(end)?;
Ok(PathPlan::Simple {
start: Box::new(start_finder),
edge: edge_traversal,
end: end_finder,
})
}
fn plan_variable_length_pattern(
&self,
pattern: &VariableLengthPathPattern,
space_id: u64,
) -> Result<PathPlan, PlannerError> {
let start_finder = self.plan_start_finder(pattern.start, space_id)?;
let edge_types = self.extract_edge_types(pattern.edge)?;
let direction = self.extract_direction(pattern.edge)?;
let end_finder = self.plan_end_finder(pattern.end)?;
Ok(PathPlan::VariableLength {
start: Box::new(start_finder),
edge_types,
direction,
end: end_finder,
lower: pattern.lower,
upper: pattern.upper,
})
}
fn plan_start_finder(
&self,
pattern: &NodePattern,
space_id: u64,
) -> Result<StartVidFinder, PlannerError> {
let context = SeekStrategyContext::new(space_id, pattern.clone(), vec![]);
let selector = SeekStrategySelector::new(CostModelConfig::default());
let strategy_type = selector.select_strategy(&context);
let finder = match strategy_type {
SeekStrategyType::VertexSeek => StartVidFinder::VertexSeek {
pattern: pattern.clone(),
},
SeekStrategyType::IndexSeek => StartVidFinder::IndexScan {
pattern: pattern.clone(),
},
SeekStrategyType::PropIndexSeek => StartVidFinder::PropIndexScan {
pattern: pattern.clone(),
},
SeekStrategyType::VariablePropIndexSeek => StartVidFinder::VariablePropIndexScan {
pattern: pattern.clone(),
},
SeekStrategyType::EdgeSeek => StartVidFinder::EdgeScan {
pattern: pattern.clone(),
},
SeekStrategyType::ScanSeek => StartVidFinder::FullScan {
pattern: pattern.clone(),
},
};
Ok(finder)
}
fn plan_end_finder(&self, pattern: &NodePattern) -> Result<EndCondition, PlannerError> {
Ok(EndCondition {
pattern: pattern.clone(),
})
}
fn plan_edge_traversal(&self, edge: &EdgePattern) -> Result<EdgeTraversal, PlannerError> {
let direction = self.extract_direction(edge)?;
let edge_types = self.extract_edge_types(edge)?;
let properties = edge.properties.clone();
Ok(EdgeTraversal {
direction,
edge_types,
properties,
})
}
fn extract_direction(&self, edge: &EdgePattern) -> Result<EdgeDirection, PlannerError> {
Ok(match edge.direction {
Some(ref dir) => *dir,
None => EdgeDirection::Both,
})
}
fn extract_edge_types(&self, edge: &EdgePattern) -> Result<Vec<String>, PlannerError> {
Ok(edge.types.clone().unwrap_or_default())
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct PathPattern {
pub kind: PathPatternKind,
}
#[derive(Debug, Clone, PartialEq)]
pub enum PathPatternKind {
Simple {
start: NodePattern,
edge: EdgePattern,
end: NodePattern,
},
VariableLength {
start: NodePattern,
edge: EdgePattern,
end: NodePattern,
lower: Option<usize>,
upper: Option<usize>,
},
Named {
name: String,
inner: Box<PathPattern>,
},
}
#[derive(Debug, Clone, PartialEq)]
pub struct EdgePattern {
pub types: Option<Vec<String>>,
pub direction: Option<EdgeDirection>,
pub properties: Vec<(Arc<str>, Value)>,
}
#[derive(Debug)]
pub enum StartVidFinder {
VertexSeek { pattern: NodePattern },
IndexScan { pattern: NodePattern },
PropIndexScan { pattern: NodePattern },
VariablePropIndexScan { pattern: NodePattern },
EdgeScan { pattern: NodePattern },
FullScan { pattern: NodePattern },
}
#[derive(Debug, Clone, PartialEq)]
pub struct EndCondition {
pub pattern: NodePattern,
}
#[derive(Debug)]
pub enum PathPlan {
Simple {
start: Box<StartVidFinder>,
edge: EdgeTraversal,
end: EndCondition,
},
VariableLength {
start: Box<StartVidFinder>,
edge_types: Vec<String>,
direction: EdgeDirection,
end: EndCondition,
lower: Option<usize>,
upper: Option<usize>,
},
Named {
name: String,
inner: Box<PathPlan>,
},
}
#[derive(Debug)]
pub struct EdgeTraversal {
pub direction: EdgeDirection,
pub edge_types: Vec<String>,
pub properties: Vec<(Arc<str>, Value)>,
}
impl PathPattern {
pub fn simple(start: NodePattern, edge: EdgePattern, end: NodePattern) -> Self {
Self {
kind: PathPatternKind::Simple { start, edge, end },
}
}
pub fn variable_length(
start: NodePattern,
edge: EdgePattern,
end: NodePattern,
lower: Option<usize>,
upper: Option<usize>,
) -> Self {
Self {
kind: PathPatternKind::VariableLength {
start,
edge,
end,
lower,
upper,
},
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_match_path_planner_new() {
let _planner = MatchPathPlanner::new();
}
#[test]
fn test_path_pattern_simple() {
let _pattern = PathPattern::simple(
NodePattern {
vid: Some(Value::string("start")),
labels: vec![],
properties: vec![],
},
EdgePattern {
types: Some(vec!["follows".to_string()]),
direction: Some(EdgeDirection::Out),
properties: vec![],
},
NodePattern {
vid: Some(Value::string("end")),
labels: vec![],
properties: vec![],
},
);
}
#[test]
fn test_path_pattern_variable_length() {
let _pattern = PathPattern::variable_length(
NodePattern {
vid: None,
labels: vec!["person".to_string()],
properties: vec![],
},
EdgePattern {
types: Some(vec!["follows".to_string()]),
direction: Some(EdgeDirection::Out),
properties: vec![],
},
NodePattern {
vid: None,
labels: vec!["person".to_string()],
properties: vec![],
},
Some(1),
Some(5),
);
}
}