Skip to main content

reddb_server/storage/query/executors/
mod.rs

1//! Multi-Mode Query Executors
2//!
3//! Specialized executors for each query language mode:
4//! - Gremlin: Traverser-based execution with path state tracking
5//! - SPARQL: Pattern matching with variable bindings
6//! - Natural: Translation + execution with explanation
7//! - Vector: HNSW-based similarity search with metadata filtering
8//! - Hybrid: Combined structured + vector search with fusion strategies
9//!
10//! Each executor converts its native AST to execution operations,
11//! leveraging the unified executor for common operations.
12
13pub mod agg_spill;
14pub mod bitmap_scan;
15pub mod cte;
16pub mod gremlin;
17pub mod hybrid;
18pub mod natural;
19pub mod parallel_scan;
20pub mod sparql;
21pub mod vector;
22
23// Native public paths alias the same portable modules and types (ADR 0077).
24pub(crate) use reddb_engine::query::executors::value_compare;
25pub use reddb_engine::query::executors::{aggregation, join, set_ops, subquery, window};
26
27pub use aggregation::{
28    create_aggregator, execute_group_by, execute_having, AggregationDef, Aggregator, AvgAggregator,
29    CountAggregator, CountDistinctAggregator, GroupConcatAggregator, MaxAggregator, MinAggregator,
30    PercentileAggregator, SampleAggregator, StdDevAggregator, SumAggregator, VarianceAggregator,
31};
32pub use cte::{inline_ctes, split_union_parts, CteContext, CteExecutor, CteStats};
33pub use gremlin::GremlinExecutor;
34pub use hybrid::{HybridExecutor, InMemoryHybridExecutor};
35pub use join::{
36    choose_strategy, execute_join, hash_join, merge_join, nested_loop_join, JoinCondition,
37    JoinStats, JoinStrategy, JoinType,
38};
39pub use natural::NaturalExecutor;
40pub use set_ops::{execute_set_op, set_except, set_intersect, set_union, SetOpStats, SetOpType};
41pub use sparql::SparqlExecutor;
42pub use subquery::{
43    bind_outer_refs, detect_correlation, CompareOp, SubqueryCache, SubqueryDef, SubqueryExecutor,
44    SubqueryType, ValueHash,
45};
46pub use vector::{InMemoryVectorExecutor, VectorExecutor};
47pub use window::{
48    FrameBound, FrameExclude, FrameSpec, FrameType, NullsOrder, SortDirection, WindowDef,
49    WindowExecutor, WindowFunc, WindowFuncType, WindowOrderBy,
50};
51
52use std::sync::Arc;
53
54use super::unified::{ExecutionError, UnifiedExecutor, UnifiedResult};
55use crate::storage::engine::graph_store::GraphStore;
56use crate::storage::engine::graph_table_index::GraphTableIndex;
57use reddb_rql::modes::{detect_mode, parse_multi, QueryMode};
58
59/// Multi-mode executor that routes to specialized executors
60pub struct MultiModeExecutor {
61    /// Underlying unified executor for common operations
62    unified: UnifiedExecutor,
63    /// Gremlin executor for traversal queries
64    gremlin: GremlinExecutor,
65    /// SPARQL executor for triple pattern queries
66    sparql: SparqlExecutor,
67    /// Natural language executor
68    natural: NaturalExecutor,
69}
70
71impl MultiModeExecutor {
72    /// Create a new multi-mode executor
73    pub fn new(graph: Arc<GraphStore>, index: Arc<GraphTableIndex>) -> Self {
74        let unified = UnifiedExecutor::new(Arc::clone(&graph), Arc::clone(&index));
75        let gremlin = GremlinExecutor::new(Arc::clone(&graph));
76        let sparql = SparqlExecutor::new(Arc::clone(&graph));
77        let natural = NaturalExecutor::new(Arc::clone(&graph));
78
79        Self {
80            unified,
81            gremlin,
82            sparql,
83            natural,
84        }
85    }
86
87    /// Execute a query string, auto-detecting the mode
88    pub fn execute(&self, query: &str) -> Result<ExecuteResult, ExecutionError> {
89        let mode = detect_mode(query);
90
91        match mode {
92            QueryMode::Sql | QueryMode::Cypher | QueryMode::Path => {
93                // Use unified executor for these modes
94                let ast = parse_multi(query).map_err(|e| ExecutionError::new(e.to_string()))?;
95                let result = self.unified.execute(&ast)?;
96                Ok(ExecuteResult {
97                    result,
98                    mode,
99                    explanation: None,
100                })
101            }
102            QueryMode::Gremlin => {
103                let result = self.gremlin.execute(query)?;
104                Ok(ExecuteResult {
105                    result,
106                    mode,
107                    explanation: None,
108                })
109            }
110            QueryMode::Sparql => {
111                let result = self.sparql.execute(query)?;
112                Ok(ExecuteResult {
113                    result,
114                    mode,
115                    explanation: None,
116                })
117            }
118            QueryMode::Natural => {
119                let (result, explanation) = self.natural.execute_with_explanation(query)?;
120                Ok(ExecuteResult {
121                    result,
122                    mode,
123                    explanation: Some(explanation),
124                })
125            }
126            QueryMode::Unknown => Err(ExecutionError::new(format!(
127                "Cannot determine query mode for: {}",
128                if query.len() > 50 {
129                    &query[..50]
130                } else {
131                    query
132                }
133            ))),
134        }
135    }
136
137    /// Execute with explicit mode
138    pub fn execute_as(
139        &self,
140        query: &str,
141        mode: QueryMode,
142    ) -> Result<ExecuteResult, ExecutionError> {
143        match mode {
144            QueryMode::Sql | QueryMode::Cypher | QueryMode::Path => {
145                let ast = parse_multi(query).map_err(|e| ExecutionError::new(e.to_string()))?;
146                let result = self.unified.execute(&ast)?;
147                Ok(ExecuteResult {
148                    result,
149                    mode,
150                    explanation: None,
151                })
152            }
153            QueryMode::Gremlin => {
154                let result = self.gremlin.execute(query)?;
155                Ok(ExecuteResult {
156                    result,
157                    mode,
158                    explanation: None,
159                })
160            }
161            QueryMode::Sparql => {
162                let result = self.sparql.execute(query)?;
163                Ok(ExecuteResult {
164                    result,
165                    mode,
166                    explanation: None,
167                })
168            }
169            QueryMode::Natural => {
170                let (result, explanation) = self.natural.execute_with_explanation(query)?;
171                Ok(ExecuteResult {
172                    result,
173                    mode,
174                    explanation: Some(explanation),
175                })
176            }
177            QueryMode::Unknown => Err(ExecutionError::new("Cannot execute unknown query mode")),
178        }
179    }
180}
181
182/// Result of executing a query with mode information
183#[derive(Debug)]
184pub struct ExecuteResult {
185    /// The query result
186    pub result: UnifiedResult,
187    /// The detected/used query mode
188    pub mode: QueryMode,
189    /// Explanation (for natural language queries)
190    pub explanation: Option<String>,
191}
192
193impl ExecuteResult {
194    /// Check if an explanation is available
195    pub fn has_explanation(&self) -> bool {
196        self.explanation.is_some()
197    }
198}
199
200#[cfg(test)]
201mod tests {
202    use super::*;
203
204    fn create_test_executor() -> MultiModeExecutor {
205        let graph = Arc::new(GraphStore::new());
206        let index = Arc::new(GraphTableIndex::new());
207        MultiModeExecutor::new(graph, index)
208    }
209
210    #[test]
211    fn test_gremlin_mode_detection() {
212        let executor = create_test_executor();
213        let result = executor.execute("g.V()");
214        assert!(result.is_ok());
215        assert_eq!(result.unwrap().mode, QueryMode::Gremlin);
216    }
217
218    #[test]
219    fn test_sparql_mode_detection() {
220        let executor = create_test_executor();
221        let result = executor.execute("SELECT ?x WHERE { ?x :type :Host }");
222        assert!(result.is_ok());
223        assert_eq!(result.unwrap().mode, QueryMode::Sparql);
224    }
225
226    #[test]
227    fn test_natural_mode_detection() {
228        let executor = create_test_executor();
229        let result = executor.execute("find all hosts with port 22");
230        assert!(result.is_ok());
231        let exec_result = result.unwrap();
232        assert_eq!(exec_result.mode, QueryMode::Natural);
233        assert!(exec_result.explanation.is_some());
234    }
235}