linkrs_query/optimizer/heuristic/join_optimization/
join_elimination.rs1use crate::optimizer::heuristic::context::RewriteContext;
30use crate::optimizer::heuristic::pattern::Pattern;
31use crate::optimizer::heuristic::result::{RewriteResult, TransformResult};
32use crate::optimizer::heuristic::rule::RewriteRule;
33use crate::planning::plan::core::nodes::join::join_node::{
34 CrossJoinNode, InnerJoinNode, LeftJoinNode,
35};
36use crate::planning::plan::PlanNodeEnum;
37use linkrs_core::types::expr::contextual::ContextualExpression;
38
39#[derive(Debug)]
41pub struct JoinEliminationRule;
42
43impl JoinEliminationRule {
44 pub fn new() -> Self {
45 Self
46 }
47
48 fn is_always_true_condition(
49 &self,
50 hash_keys: &[ContextualExpression],
51 probe_keys: &[ContextualExpression],
52 ) -> bool {
53 if hash_keys.is_empty() && probe_keys.is_empty() {
54 return true;
55 }
56 false
57 }
58
59 fn is_self_join(&self, left: &PlanNodeEnum, right: &PlanNodeEnum) -> bool {
60 match (left, right) {
61 (PlanNodeEnum::ScanVertices(l), PlanNodeEnum::ScanVertices(r)) => {
62 l.space_id() == r.space_id() && l.tag() == r.tag()
63 }
64 (PlanNodeEnum::ScanEdges(l), PlanNodeEnum::ScanEdges(r)) => {
65 l.space_id() == r.space_id() && l.edge_type() == r.edge_type()
66 }
67 _ => false,
68 }
69 }
70
71 fn apply_to_inner_join(&self, join: &InnerJoinNode) -> RewriteResult<Option<TransformResult>> {
72 let left = join.left_input();
73 let right = join.right_input();
74
75 if self.is_self_join(left, right) {
76 let mut result = TransformResult::new();
77 result.erase_curr = true;
78 result.add_new_node(left.clone());
79 return Ok(Some(result));
80 }
81
82 if self.is_always_true_condition(join.hash_keys(), join.probe_keys()) {
83 match CrossJoinNode::new(left.clone(), right.clone()) {
84 Ok(cross_join) => {
85 let mut result = TransformResult::new();
86 result.erase_curr = true;
87 result.add_new_node(PlanNodeEnum::CrossJoin(cross_join));
88 return Ok(Some(result));
89 }
90 Err(_) => return Ok(None),
91 }
92 }
93
94 Ok(None)
95 }
96
97 fn apply_to_left_join(&self, join: &LeftJoinNode) -> RewriteResult<Option<TransformResult>> {
98 let _left = join.left_input();
99 let _right = join.right_input();
100
101 Ok(None)
102 }
103}
104
105impl Default for JoinEliminationRule {
106 fn default() -> Self {
107 Self::new()
108 }
109}
110
111impl RewriteRule for JoinEliminationRule {
112 fn name(&self) -> &'static str {
113 "JoinEliminationRule"
114 }
115
116 fn pattern(&self) -> Pattern {
117 Pattern::new_with_name("Join")
118 }
119
120 fn apply(
121 &self,
122 _ctx: &mut RewriteContext,
123 node: &PlanNodeEnum,
124 ) -> RewriteResult<Option<TransformResult>> {
125 match node {
126 PlanNodeEnum::InnerJoin(join) => self.apply_to_inner_join(join),
127 PlanNodeEnum::LeftJoin(join) => self.apply_to_left_join(join),
128 _ => Ok(None),
129 }
130 }
131}
132
133#[cfg(test)]
134mod tests {
135 use super::*;
136
137 #[test]
138 fn test_rule_name() {
139 let rule = JoinEliminationRule::new();
140 assert_eq!(rule.name(), "JoinEliminationRule");
141 }
142
143 #[test]
144 fn test_rule_pattern() {
145 let rule = JoinEliminationRule::new();
146 let pattern = rule.pattern();
147 assert!(pattern.node.is_some());
148 }
149}