oxirs_core/sparql/
executor.rs1use super::*;
21use crate::rdf_store::{OxirsQueryResults, StorageBackend};
22use crate::{OxirsError, Result};
23use scirs2_core::metrics::{Counter, Histogram, Timer};
24use std::collections::HashMap;
25use std::sync::Arc;
26
27#[derive(Debug, Clone)]
29pub struct ExecutorStats {
30 pub total_queries: u64,
32 pub select_queries: u64,
34 pub ask_queries: u64,
36 pub construct_queries: u64,
38 pub describe_queries: u64,
40 pub pattern_matches: u64,
42 pub avg_execution_time_secs: f64,
44 pub total_observations: u64,
46}
47
48#[derive(Debug, Clone)]
50pub(crate) struct ValuesClause {
51 pub(crate) variables: Vec<String>,
52 pub(crate) rows: Vec<Vec<String>>,
53}
54
55pub struct QueryExecutor<'a> {
63 pub(crate) backend: &'a StorageBackend,
64 pub(crate) query_timer: Arc<Timer>,
66 pub(crate) select_counter: Arc<Counter>,
68 pub(crate) ask_counter: Arc<Counter>,
70 pub(crate) construct_counter: Arc<Counter>,
72 pub(crate) describe_counter: Arc<Counter>,
74 pub(crate) pattern_counter: Arc<Counter>,
76 pub(crate) result_size_histogram: Arc<Histogram>,
78}
79
80impl<'a> QueryExecutor<'a> {
81 pub fn new(backend: &'a StorageBackend) -> Self {
89 Self {
90 backend,
91 query_timer: Arc::new(Timer::new("query_execution_time".to_string())),
92 select_counter: Arc::new(Counter::new("select_queries".to_string())),
93 ask_counter: Arc::new(Counter::new("ask_queries".to_string())),
94 construct_counter: Arc::new(Counter::new("construct_queries".to_string())),
95 describe_counter: Arc::new(Counter::new("describe_queries".to_string())),
96 pattern_counter: Arc::new(Counter::new("pattern_matches".to_string())),
97 result_size_histogram: Arc::new(Histogram::new("result_set_size".to_string())),
98 }
99 }
100
101 pub fn get_stats(&self) -> ExecutorStats {
109 let select = self.select_counter.get();
110 let ask = self.ask_counter.get();
111 let construct = self.construct_counter.get();
112 let describe = self.describe_counter.get();
113
114 let timer_stats = self.query_timer.get_stats();
115
116 ExecutorStats {
117 total_queries: select + ask + construct + describe,
118 select_queries: select,
119 ask_queries: ask,
120 construct_queries: construct,
121 describe_queries: describe,
122 pattern_matches: self.pattern_counter.get(),
123 avg_execution_time_secs: timer_stats.mean,
124 total_observations: timer_stats.count,
125 }
126 }
127
128 pub fn execute(&self, sparql: &str) -> Result<OxirsQueryResults> {
130 self.query(sparql)
131 }
132
133 pub fn query(&self, sparql: &str) -> Result<OxirsQueryResults> {
135 let start = std::time::Instant::now();
136
137 let sparql = sparql.trim();
138
139 let (prefixes, expanded_query) = self.extract_and_expand_prefixes(sparql)?;
140
141 let query_to_execute = if !prefixes.is_empty() {
142 &expanded_query
143 } else {
144 sparql
145 };
146
147 let result = if query_to_execute.to_uppercase().contains("SELECT") {
148 self.select_counter.inc();
149 self.execute_select_query(query_to_execute)
150 } else if query_to_execute.to_uppercase().starts_with("ASK") {
151 self.ask_counter.inc();
152 self.execute_ask_query(query_to_execute)
153 } else if query_to_execute.to_uppercase().starts_with("CONSTRUCT") {
154 self.construct_counter.inc();
155 self.execute_construct_query(query_to_execute)
156 } else if query_to_execute.to_uppercase().starts_with("DESCRIBE") {
157 self.describe_counter.inc();
158 self.execute_describe_query(query_to_execute)
159 } else {
160 return Err(OxirsError::Query(format!(
161 "Unsupported SPARQL query type: {sparql}"
162 )));
163 };
164
165 let duration = start.elapsed();
166 self.query_timer.observe(duration);
167
168 if let Ok(ref query_result) = result {
169 self.result_size_histogram
170 .observe(query_result.len() as f64);
171 }
172
173 result
174 }
175
176 fn extract_and_expand_prefixes(
178 &self,
179 sparql: &str,
180 ) -> Result<(HashMap<String, String>, String)> {
181 extract_and_expand_prefixes(sparql)
182 }
183}