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