use uqa_operators::OperatorTree;
use super::{tree_map::map_operator_children, QueryOptimizer};
impl QueryOptimizer {
pub(super) fn simplify_algebra(&self, op: OperatorTree) -> OperatorTree {
let op = self.recurse_simplify(op);
match op {
OperatorTree::Intersect(operands) => {
for child in &operands {
if child.is_empty() {
return OperatorTree::Intersect(Vec::new());
}
}
let mut seen: Vec<OperatorTree> = Vec::new();
'outer: for child in operands {
for s in &seen {
if same_membership_term(s, &child) {
continue 'outer;
}
}
seen.push(child);
}
let operands = seen;
let mut absorbed: Vec<OperatorTree> = Vec::new();
for (child_index, child) in operands.iter().enumerate() {
if let OperatorTree::Union(union_operands) = child {
let drop = is_membership_only(child)
&& operands.iter().enumerate().any(|(other_index, other)| {
other_index != child_index
&& union_operands
.iter()
.any(|union_term| same_membership_term(union_term, other))
});
if drop {
continue;
}
}
absorbed.push(child.clone());
}
if absorbed.len() == 1 {
if let Some(only) = absorbed.pop() {
return only;
}
}
OperatorTree::Intersect(absorbed)
}
OperatorTree::Union(operands) => {
let mut kept: Vec<OperatorTree> =
operands.into_iter().filter(|c| !c.is_empty()).collect();
let mut seen: Vec<OperatorTree> = Vec::new();
'outer: for child in kept.drain(..) {
for s in &seen {
if same_membership_term(s, &child) {
continue 'outer;
}
}
seen.push(child);
}
let operands = seen;
let mut absorbed: Vec<OperatorTree> = Vec::new();
for (child_index, child) in operands.iter().enumerate() {
if let OperatorTree::Intersect(int_operands) = child {
let drop = is_membership_only(child)
&& operands.iter().enumerate().any(|(other_index, other)| {
other_index != child_index
&& int_operands.iter().any(|intersect_term| {
same_membership_term(intersect_term, other)
})
});
if drop {
continue;
}
}
absorbed.push(child.clone());
}
if absorbed.len() == 1 {
if let Some(only) = absorbed.pop() {
return only;
}
}
if absorbed.is_empty() {
return OperatorTree::Union(Vec::new());
}
OperatorTree::Union(absorbed)
}
other => other,
}
}
pub(super) fn recurse_simplify(&self, op: OperatorTree) -> OperatorTree {
map_operator_children(op, |child| self.simplify_algebra(child))
}
}
fn is_membership_only(op: &OperatorTree) -> bool {
op.is_membership_only()
}
fn same_membership_term(left: &OperatorTree, right: &OperatorTree) -> bool {
if !is_membership_only(left) || !is_membership_only(right) {
return false;
}
if left.is_empty() || right.is_empty() {
return left.is_empty() && right.is_empty();
}
match (left, right) {
(
OperatorTree::Filter {
field: left_field,
predicate: left_predicate,
source: left_source,
},
OperatorTree::Filter {
field: right_field,
predicate: right_predicate,
source: right_source,
},
) => {
left_field == right_field
&& left_predicate == right_predicate
&& same_optional_membership_source(left_source.as_deref(), right_source.as_deref())
}
(
OperatorTree::IndexScan {
index_name: left_index,
field: left_field,
predicate: left_predicate,
},
OperatorTree::IndexScan {
index_name: right_index,
field: right_field,
predicate: right_predicate,
},
) => {
left_index == right_index
&& left_field == right_field
&& left_predicate == right_predicate
}
(OperatorTree::Intersect(left), OperatorTree::Intersect(right))
| (OperatorTree::Union(left), OperatorTree::Union(right)) => {
same_membership_multiset(left, right)
}
(OperatorTree::Complement(left), OperatorTree::Complement(right)) => {
same_membership_term(left, right)
}
(OperatorTree::Composed(left), OperatorTree::Composed(right)) => {
same_membership_sequence(left, right)
}
(
OperatorTree::VectorExclusion {
positive: left_positive,
negative: left_negative,
},
OperatorTree::VectorExclusion {
positive: right_positive,
negative: right_negative,
},
) => {
same_membership_term(left_positive, right_positive)
&& same_membership_term(left_negative, right_negative)
}
_ => false,
}
}
fn same_optional_membership_source(
left: Option<&OperatorTree>,
right: Option<&OperatorTree>,
) -> bool {
match (left, right) {
(None, None) => true,
(Some(left), Some(right)) => same_membership_term(left, right),
(None, Some(_)) | (Some(_), None) => false,
}
}
fn same_membership_sequence(left: &[OperatorTree], right: &[OperatorTree]) -> bool {
left.len() == right.len()
&& left
.iter()
.zip(right)
.all(|(left, right)| same_membership_term(left, right))
}
fn same_membership_multiset(left: &[OperatorTree], right: &[OperatorTree]) -> bool {
if left.len() != right.len() {
return false;
}
let mut matched = vec![false; right.len()];
for left_term in left {
let Some(index) = right.iter().enumerate().position(|(index, right_term)| {
!matched[index] && same_membership_term(left_term, right_term)
}) else {
return false;
};
matched[index] = true;
}
true
}