term_guard/optimizer/
analyzer.rs1use crate::core::Constraint;
4use crate::prelude::TermError;
5use std::collections::HashMap;
6use std::sync::Arc;
7
8#[derive(Debug, Clone)]
10pub struct ConstraintAnalysis {
11 pub name: String,
13 pub constraint: Arc<dyn Constraint>,
15 pub table_name: String,
17 pub aggregations: Vec<AggregationType>,
19 pub columns: Vec<String>,
21 pub has_predicates: bool,
23 pub is_combinable: bool,
25}
26
27#[derive(Debug, Clone, PartialEq, Eq, Hash)]
29pub enum AggregationType {
30 Count,
31 CountDistinct,
32 Sum,
33 Avg,
34 Min,
35 Max,
36 StdDev,
37 Variance,
38}
39
40#[derive(Debug)]
42pub struct QueryAnalyzer {
43 cache: HashMap<String, ConstraintAnalysis>,
45}
46
47impl QueryAnalyzer {
48 pub fn new() -> Self {
50 Self {
51 cache: HashMap::new(),
52 }
53 }
54
55 pub fn analyze(
57 &mut self,
58 constraints: &[(String, Arc<dyn Constraint>)],
59 ) -> Result<Vec<ConstraintAnalysis>, TermError> {
60 let mut analyses = Vec::new();
61
62 for (name, constraint) in constraints {
63 if let Some(cached) = self.cache.get(name) {
65 analyses.push(cached.clone());
66 continue;
67 }
68
69 let analysis = self.analyze_constraint(name.clone(), constraint.clone())?;
71
72 self.cache.insert(name.clone(), analysis.clone());
74 analyses.push(analysis);
75 }
76
77 Ok(analyses)
78 }
79
80 pub fn analyze_constraint(
82 &self,
83 name: String,
84 constraint: Arc<dyn Constraint>,
85 ) -> Result<ConstraintAnalysis, TermError> {
86 let constraint_name = constraint.name();
90
91 let aggregations = match constraint_name {
93 "completeness" => vec![AggregationType::Count],
94 "uniqueness" => vec![AggregationType::Count, AggregationType::CountDistinct],
95 "compliance" => vec![AggregationType::Count],
96 "min" => vec![AggregationType::Min],
97 "max" => vec![AggregationType::Max],
98 "mean" => vec![AggregationType::Avg],
99 "sum" => vec![AggregationType::Sum],
100 "standard_deviation" => vec![AggregationType::StdDev],
101 "quantile" => vec![AggregationType::Count], "entropy" => vec![AggregationType::Count], "mutual_information" => vec![AggregationType::Count], "histogram" => vec![AggregationType::Count],
105 _ => vec![AggregationType::Count], };
107
108 let columns = self.extract_columns(constraint_name);
110
111 let has_predicates = matches!(
113 constraint_name,
114 "compliance" | "pattern_match" | "containment"
115 );
116
117 let is_combinable = !matches!(
119 constraint_name,
120 "quantile" | "entropy" | "mutual_information" | "anomaly_detection"
121 );
122
123 Ok(ConstraintAnalysis {
124 name,
125 constraint,
126 table_name: "data".to_string(),
127 aggregations,
128 columns,
129 has_predicates,
130 is_combinable,
131 })
132 }
133
134 fn extract_columns(&self, constraint_name: &str) -> Vec<String> {
136 match constraint_name {
139 "completeness" | "uniqueness" | "min" | "max" | "mean" | "sum" => {
140 vec!["column".to_string()] }
142 "mutual_information" => {
143 vec!["column1".to_string(), "column2".to_string()]
144 }
145 _ => vec![],
146 }
147 }
148
149 pub fn clear_cache(&mut self) {
151 self.cache.clear();
152 }
153}
154
155impl Default for QueryAnalyzer {
156 fn default() -> Self {
157 Self::new()
158 }
159}
160
161#[cfg(test)]
163mod tests {
164 use super::*;
165 #[test]
168 fn test_analyzer_creation() {
169 let analyzer = QueryAnalyzer::new();
170 assert!(analyzer.cache.is_empty());
171 }
172
173 }