Skip to main content

grafeo_engine/database/
query.rs

1//! Query execution methods for GrafeoDB.
2
3#[cfg(any(feature = "cypher", feature = "sql-pgq", feature = "rdf"))]
4use std::sync::Arc;
5
6use grafeo_common::utils::error::Result;
7#[cfg(feature = "rdf")]
8use grafeo_core::graph::rdf::RdfStore;
9
10use super::{FromValue, QueryResult};
11
12impl super::GrafeoDB {
13    /// Runs a query directly on the database.
14    ///
15    /// A convenience method that creates a temporary session behind the
16    /// scenes. If you're running multiple queries, grab a
17    /// [`session()`](Self::session) instead to avoid the overhead.
18    ///
19    /// # Errors
20    ///
21    /// Returns an error if parsing or execution fails.
22    pub fn execute(&self, query: &str) -> Result<QueryResult> {
23        let session = self.session();
24        session.execute(query)
25    }
26
27    /// Executes a GQL query with visibility at the specified epoch.
28    ///
29    /// This enables time-travel queries: the query sees the database
30    /// as it existed at the given epoch.
31    ///
32    /// # Errors
33    ///
34    /// Returns an error if parsing or execution fails.
35    pub fn execute_at_epoch(
36        &self,
37        query: &str,
38        epoch: grafeo_common::types::EpochId,
39    ) -> Result<QueryResult> {
40        let session = self.session();
41        session.execute_at_epoch(query, epoch)
42    }
43
44    /// Executes a query with parameters and returns the result.
45    ///
46    /// # Errors
47    ///
48    /// Returns an error if the query fails.
49    pub fn execute_with_params(
50        &self,
51        query: &str,
52        params: std::collections::HashMap<String, grafeo_common::types::Value>,
53    ) -> Result<QueryResult> {
54        let session = self.session();
55        session.execute_with_params(query, params)
56    }
57
58    /// Executes a Cypher query and returns the result.
59    ///
60    /// # Errors
61    ///
62    /// Returns an error if the query fails.
63    #[cfg(feature = "cypher")]
64    pub fn execute_cypher(&self, query: &str) -> Result<QueryResult> {
65        let session = self.session();
66        session.execute_cypher(query)
67    }
68
69    /// Executes a Cypher query with parameters and returns the result.
70    ///
71    /// # Errors
72    ///
73    /// Returns an error if the query fails.
74    #[cfg(feature = "cypher")]
75    pub fn execute_cypher_with_params(
76        &self,
77        query: &str,
78        params: std::collections::HashMap<String, grafeo_common::types::Value>,
79    ) -> Result<QueryResult> {
80        use crate::query::processor::{QueryLanguage, QueryProcessor};
81
82        // Create processor
83        let processor = QueryProcessor::for_lpg(Arc::clone(&self.store));
84        processor.process(query, QueryLanguage::Cypher, Some(&params))
85    }
86
87    /// Executes a Gremlin query and returns the result.
88    ///
89    /// # Errors
90    ///
91    /// Returns an error if the query fails.
92    #[cfg(feature = "gremlin")]
93    pub fn execute_gremlin(&self, query: &str) -> Result<QueryResult> {
94        let session = self.session();
95        session.execute_gremlin(query)
96    }
97
98    /// Executes a Gremlin query with parameters and returns the result.
99    ///
100    /// # Errors
101    ///
102    /// Returns an error if the query fails.
103    #[cfg(feature = "gremlin")]
104    pub fn execute_gremlin_with_params(
105        &self,
106        query: &str,
107        params: std::collections::HashMap<String, grafeo_common::types::Value>,
108    ) -> Result<QueryResult> {
109        let session = self.session();
110        session.execute_gremlin_with_params(query, params)
111    }
112
113    /// Executes a GraphQL query and returns the result.
114    ///
115    /// # Errors
116    ///
117    /// Returns an error if the query fails.
118    #[cfg(feature = "graphql")]
119    pub fn execute_graphql(&self, query: &str) -> Result<QueryResult> {
120        let session = self.session();
121        session.execute_graphql(query)
122    }
123
124    /// Executes a GraphQL query with parameters and returns the result.
125    ///
126    /// # Errors
127    ///
128    /// Returns an error if the query fails.
129    #[cfg(feature = "graphql")]
130    pub fn execute_graphql_with_params(
131        &self,
132        query: &str,
133        params: std::collections::HashMap<String, grafeo_common::types::Value>,
134    ) -> Result<QueryResult> {
135        let session = self.session();
136        session.execute_graphql_with_params(query, params)
137    }
138
139    /// Executes a SQL/PGQ query (SQL:2023 GRAPH_TABLE) and returns the result.
140    ///
141    /// # Errors
142    ///
143    /// Returns an error if the query fails.
144    #[cfg(feature = "sql-pgq")]
145    pub fn execute_sql(&self, query: &str) -> Result<QueryResult> {
146        let session = self.session();
147        session.execute_sql(query)
148    }
149
150    /// Executes a SQL/PGQ query with parameters and returns the result.
151    ///
152    /// # Errors
153    ///
154    /// Returns an error if the query fails.
155    #[cfg(feature = "sql-pgq")]
156    pub fn execute_sql_with_params(
157        &self,
158        query: &str,
159        params: std::collections::HashMap<String, grafeo_common::types::Value>,
160    ) -> Result<QueryResult> {
161        use crate::query::processor::{QueryLanguage, QueryProcessor};
162
163        // Create processor
164        let processor = QueryProcessor::for_lpg(Arc::clone(&self.store));
165        processor.process(query, QueryLanguage::SqlPgq, Some(&params))
166    }
167
168    /// Executes a SPARQL query and returns the result.
169    ///
170    /// SPARQL queries operate on the RDF triple store.
171    ///
172    /// # Errors
173    ///
174    /// Returns an error if the query fails.
175    ///
176    /// # Examples
177    ///
178    /// ```no_run
179    /// # fn main() -> Result<(), Box<dyn std::error::Error>> {
180    /// use grafeo_engine::GrafeoDB;
181    ///
182    /// let db = GrafeoDB::new_in_memory();
183    /// let result = db.execute_sparql("SELECT ?s ?p ?o WHERE { ?s ?p ?o }")?;
184    /// # Ok(())
185    /// # }
186    /// ```
187    #[cfg(all(feature = "sparql", feature = "rdf"))]
188    pub fn execute_sparql(&self, query: &str) -> Result<QueryResult> {
189        use crate::query::{
190            Executor, optimizer::Optimizer, planner::rdf::RdfPlanner, translators::sparql,
191        };
192
193        // Parse and translate the SPARQL query to a logical plan
194        let logical_plan = sparql::translate(query)?;
195
196        // Optimize the plan
197        let optimizer = Optimizer::from_store(&self.store);
198        let optimized_plan = optimizer.optimize(logical_plan)?;
199
200        // Convert to physical plan using RDF planner
201        let planner = RdfPlanner::new(Arc::clone(&self.rdf_store));
202        let mut physical_plan = planner.plan(&optimized_plan)?;
203
204        // Execute the plan
205        let executor = Executor::with_columns(physical_plan.columns.clone());
206        executor.execute(physical_plan.operator.as_mut())
207    }
208
209    /// Executes a query in the specified language by name.
210    ///
211    /// Supported language names: `"gql"`, `"cypher"`, `"gremlin"`, `"graphql"`,
212    /// `"sparql"`, `"sql"`. Each requires the corresponding feature flag.
213    ///
214    /// # Errors
215    ///
216    /// Returns an error if the language is unknown/disabled, or if the query
217    /// fails.
218    pub fn execute_language(
219        &self,
220        query: &str,
221        language: &str,
222        params: Option<std::collections::HashMap<String, grafeo_common::types::Value>>,
223    ) -> Result<QueryResult> {
224        let session = self.session();
225        session.execute_language(query, language, params)
226    }
227
228    /// Returns the RDF store.
229    ///
230    /// This provides direct access to the RDF store for triple operations.
231    #[cfg(feature = "rdf")]
232    #[must_use]
233    pub fn rdf_store(&self) -> &Arc<RdfStore> {
234        &self.rdf_store
235    }
236
237    /// Executes a query and returns a single scalar value.
238    ///
239    /// # Errors
240    ///
241    /// Returns an error if the query fails or doesn't return exactly one row.
242    pub fn query_scalar<T: FromValue>(&self, query: &str) -> Result<T> {
243        let result = self.execute(query)?;
244        result.scalar()
245    }
246}