use crate::optimizer::heuristic::context::RewriteContext;
use crate::optimizer::heuristic::pattern::Pattern;
use crate::optimizer::heuristic::result::{RewriteResult, TransformResult};
use crate::optimizer::heuristic::rule::RewriteRule;
use crate::planning::plan::core::nodes::base::plan_node_traits::{
MultipleInputNode, SingleInputNode,
};
use crate::planning::plan::core::nodes::operation::project_node::ProjectNode;
use crate::planning::plan::core::nodes::traversal::traversal_node::AppendVerticesNode;
use crate::planning::plan::PlanNodeEnum;
use linkrs_core::types::expr::contextual::ContextualExpression;
use linkrs_core::types::expr::visitor_checkers::PathBuildContainsChecker;
#[derive(Debug)]
pub struct EliminateAppendVerticesRule;
impl EliminateAppendVerticesRule {
pub fn new() -> Self {
Self
}
fn is_anonymous_var(&self, name: &str) -> bool {
name.starts_with("__anon_")
}
fn contains_path_build(&self, expr: &ContextualExpression) -> bool {
let expr_meta = match expr.expression() {
Some(e) => e,
None => return false,
};
PathBuildContainsChecker::check(expr_meta.inner())
}
fn can_eliminate_append_vertices(
&self,
project: &ProjectNode,
append_vertices: &AppendVerticesNode,
) -> bool {
for col in project.columns() {
if self.contains_path_build(&col.expression) {
return false;
}
}
if append_vertices.v_filter().is_some() {
return false;
}
let col_names = append_vertices.col_names();
if let Some(last_col) = col_names.last() {
if !self.is_anonymous_var(last_col) {
return false;
}
} else {
return false;
}
true
}
}
impl Default for EliminateAppendVerticesRule {
fn default() -> Self {
Self::new()
}
}
impl RewriteRule for EliminateAppendVerticesRule {
fn name(&self) -> &'static str {
"EliminateAppendVerticesRule"
}
fn pattern(&self) -> Pattern {
Pattern::new_with_name("Project").with_dependency_name("AppendVertices")
}
fn apply(
&self,
_ctx: &mut RewriteContext,
node: &PlanNodeEnum,
) -> RewriteResult<Option<TransformResult>> {
let project = match node {
PlanNodeEnum::Project(n) => n,
_ => return Ok(None),
};
let input = project.input();
let append_vertices = match input {
PlanNodeEnum::AppendVertices(n) => n,
_ => return Ok(None),
};
if !self.can_eliminate_append_vertices(project, append_vertices) {
return Ok(None);
}
let append_inputs = append_vertices.inputs();
if append_inputs.is_empty() {
return Ok(None);
}
let mut result = TransformResult::new();
result.erase_curr = true;
let new_project = PlanNodeEnum::Project(project.clone());
result.add_new_node(new_project);
Ok(Some(result))
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::optimizer::heuristic::rule::RewriteRule;
#[test]
fn test_eliminate_append_vertices_rule_name() {
let rule = EliminateAppendVerticesRule::new();
assert_eq!(rule.name(), "EliminateAppendVerticesRule");
}
#[test]
fn test_eliminate_append_vertices_rule_pattern() {
let rule = EliminateAppendVerticesRule::new();
let pattern = rule.pattern();
assert!(pattern.node.is_some());
}
#[test]
fn test_is_anonymous_var() {
let rule = EliminateAppendVerticesRule::new();
assert!(rule.is_anonymous_var("__anon_123"));
assert!(rule.is_anonymous_var("__anon_var"));
assert!(!rule.is_anonymous_var("normal_var"));
assert!(!rule.is_anonymous_var("v"));
}
}