Skip to main content

oxirs_arq/optimizer/
statistics.rs

1//! Optimizer Statistics
2//!
3//! Collection and management of statistics for query optimization.
4
5use std::collections::HashMap;
6use std::time::Duration;
7
8use super::execution_tracking::{ExecutionRecord, OptimizationDecision, OptimizationType};
9use super::index_types::{IndexStatistics, IndexType};
10use crate::algebra::Variable;
11
12/// Query optimization statistics
13#[derive(Debug, Clone, Default)]
14pub struct Statistics {
15    /// Index statistics by type
16    pub index_stats: HashMap<IndexType, IndexStatistics>,
17    /// Join selectivity statistics
18    pub join_selectivities: HashMap<String, f64>,
19    /// Filter selectivity statistics
20    pub filter_selectivities: HashMap<String, f64>,
21    /// Average execution times by pattern
22    pub execution_times: HashMap<String, Duration>,
23    /// Cardinality estimates
24    pub cardinalities: HashMap<String, usize>,
25    /// Pattern cardinality estimates
26    pub pattern_cardinality: HashMap<String, usize>,
27    /// Predicate frequency statistics
28    pub predicate_frequency: HashMap<String, usize>,
29    /// Subject cardinality statistics
30    pub subject_cardinality: HashMap<String, usize>,
31    /// Object cardinality statistics
32    pub object_cardinality: HashMap<String, usize>,
33    /// Variable selectivity statistics
34    pub variable_selectivity: HashMap<Variable, f64>,
35    /// Optimization success rates
36    pub optimization_success_rates: HashMap<OptimizationType, f64>,
37    /// Total queries processed
38    pub total_queries: usize,
39    /// Total optimization time
40    pub total_optimization_time: Duration,
41}
42
43impl Statistics {
44    /// Create new statistics
45    pub fn new() -> Self {
46        Self::default()
47    }
48
49    /// Update statistics with execution record
50    pub fn update_with_execution(&mut self, record: &ExecutionRecord) {
51        self.total_queries += 1;
52
53        // Update optimization success rates
54        for decision in &record.optimization_decisions {
55            self.update_optimization_success_rate(decision);
56        }
57
58        // Update execution times
59        let query_hash = record.query_hash.to_string();
60        self.execution_times
61            .insert(query_hash.clone(), record.execution_time);
62        self.cardinalities.insert(query_hash, record.cardinality);
63    }
64
65    /// Update index statistics
66    pub fn update_index_stats(&mut self, index_type: IndexType, stats: IndexStatistics) {
67        self.index_stats.insert(index_type, stats);
68    }
69
70    /// Get join selectivity
71    pub fn get_join_selectivity(&self, join_pattern: &str) -> Option<f64> {
72        self.join_selectivities.get(join_pattern).copied()
73    }
74
75    /// Set join selectivity
76    pub fn set_join_selectivity(&mut self, join_pattern: String, selectivity: f64) {
77        self.join_selectivities.insert(join_pattern, selectivity);
78    }
79
80    /// Get filter selectivity
81    pub fn get_filter_selectivity(&self, filter_pattern: &str) -> Option<f64> {
82        self.filter_selectivities.get(filter_pattern).copied()
83    }
84
85    /// Set filter selectivity
86    pub fn set_filter_selectivity(&mut self, filter_pattern: String, selectivity: f64) {
87        self.filter_selectivities
88            .insert(filter_pattern, selectivity);
89    }
90
91    /// Get estimated cardinality
92    pub fn get_estimated_cardinality(&self, pattern: &str) -> Option<usize> {
93        self.cardinalities.get(pattern).copied()
94    }
95
96    /// Get optimization success rate
97    pub fn get_optimization_success_rate(&self, opt_type: &OptimizationType) -> f64 {
98        self.optimization_success_rates
99            .get(opt_type)
100            .copied()
101            .unwrap_or(0.5) // Default 50% success rate
102    }
103
104    /// Add optimization time
105    pub fn add_optimization_time(&mut self, duration: Duration) {
106        self.total_optimization_time += duration;
107    }
108
109    /// Get average optimization time per query
110    pub fn average_optimization_time(&self) -> Duration {
111        if self.total_queries > 0 {
112            self.total_optimization_time / self.total_queries as u32
113        } else {
114            Duration::default()
115        }
116    }
117
118    fn update_optimization_success_rate(&mut self, decision: &OptimizationDecision) {
119        let current_rate = self
120            .optimization_success_rates
121            .get(&decision.optimization_type)
122            .copied()
123            .unwrap_or(0.5);
124
125        // Simple exponential moving average
126        let alpha = 0.1;
127        let success_value = if decision.success { 1.0 } else { 0.0 };
128        let new_rate = alpha * success_value + (1.0 - alpha) * current_rate;
129
130        self.optimization_success_rates
131            .insert(decision.optimization_type.clone(), new_rate);
132    }
133}
134
135// Implement PartialEq for OptimizationType to use as HashMap key
136impl PartialEq for OptimizationType {
137    fn eq(&self, other: &Self) -> bool {
138        std::mem::discriminant(self) == std::mem::discriminant(other)
139    }
140}
141
142impl Eq for OptimizationType {}
143
144impl std::hash::Hash for OptimizationType {
145    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
146        std::mem::discriminant(self).hash(state);
147    }
148}