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 query with parameters and returns the result.
28    ///
29    /// # Errors
30    ///
31    /// Returns an error if the query fails.
32    pub fn execute_with_params(
33        &self,
34        query: &str,
35        params: std::collections::HashMap<String, grafeo_common::types::Value>,
36    ) -> Result<QueryResult> {
37        let session = self.session();
38        session.execute_with_params(query, params)
39    }
40
41    /// Executes a Cypher query and returns the result.
42    ///
43    /// # Errors
44    ///
45    /// Returns an error if the query fails.
46    #[cfg(feature = "cypher")]
47    pub fn execute_cypher(&self, query: &str) -> Result<QueryResult> {
48        let session = self.session();
49        session.execute_cypher(query)
50    }
51
52    /// Executes a Cypher query with parameters and returns the result.
53    ///
54    /// # Errors
55    ///
56    /// Returns an error if the query fails.
57    #[cfg(feature = "cypher")]
58    pub fn execute_cypher_with_params(
59        &self,
60        query: &str,
61        params: std::collections::HashMap<String, grafeo_common::types::Value>,
62    ) -> Result<QueryResult> {
63        use crate::query::processor::{QueryLanguage, QueryProcessor};
64
65        // Create processor
66        let processor = QueryProcessor::for_lpg(Arc::clone(&self.store));
67        processor.process(query, QueryLanguage::Cypher, Some(&params))
68    }
69
70    /// Executes a Gremlin query and returns the result.
71    ///
72    /// # Errors
73    ///
74    /// Returns an error if the query fails.
75    #[cfg(feature = "gremlin")]
76    pub fn execute_gremlin(&self, query: &str) -> Result<QueryResult> {
77        let session = self.session();
78        session.execute_gremlin(query)
79    }
80
81    /// Executes a Gremlin query with parameters and returns the result.
82    ///
83    /// # Errors
84    ///
85    /// Returns an error if the query fails.
86    #[cfg(feature = "gremlin")]
87    pub fn execute_gremlin_with_params(
88        &self,
89        query: &str,
90        params: std::collections::HashMap<String, grafeo_common::types::Value>,
91    ) -> Result<QueryResult> {
92        let session = self.session();
93        session.execute_gremlin_with_params(query, params)
94    }
95
96    /// Executes a GraphQL query and returns the result.
97    ///
98    /// # Errors
99    ///
100    /// Returns an error if the query fails.
101    #[cfg(feature = "graphql")]
102    pub fn execute_graphql(&self, query: &str) -> Result<QueryResult> {
103        let session = self.session();
104        session.execute_graphql(query)
105    }
106
107    /// Executes a GraphQL query with parameters and returns the result.
108    ///
109    /// # Errors
110    ///
111    /// Returns an error if the query fails.
112    #[cfg(feature = "graphql")]
113    pub fn execute_graphql_with_params(
114        &self,
115        query: &str,
116        params: std::collections::HashMap<String, grafeo_common::types::Value>,
117    ) -> Result<QueryResult> {
118        let session = self.session();
119        session.execute_graphql_with_params(query, params)
120    }
121
122    /// Executes a SQL/PGQ query (SQL:2023 GRAPH_TABLE) and returns the result.
123    ///
124    /// # Errors
125    ///
126    /// Returns an error if the query fails.
127    #[cfg(feature = "sql-pgq")]
128    pub fn execute_sql(&self, query: &str) -> Result<QueryResult> {
129        let session = self.session();
130        session.execute_sql(query)
131    }
132
133    /// Executes a SQL/PGQ query with parameters and returns the result.
134    ///
135    /// # Errors
136    ///
137    /// Returns an error if the query fails.
138    #[cfg(feature = "sql-pgq")]
139    pub fn execute_sql_with_params(
140        &self,
141        query: &str,
142        params: std::collections::HashMap<String, grafeo_common::types::Value>,
143    ) -> Result<QueryResult> {
144        use crate::query::processor::{QueryLanguage, QueryProcessor};
145
146        // Create processor
147        let processor = QueryProcessor::for_lpg(Arc::clone(&self.store));
148        processor.process(query, QueryLanguage::SqlPgq, Some(&params))
149    }
150
151    /// Executes a SPARQL query and returns the result.
152    ///
153    /// SPARQL queries operate on the RDF triple store.
154    ///
155    /// # Errors
156    ///
157    /// Returns an error if the query fails.
158    ///
159    /// # Examples
160    ///
161    /// ```no_run
162    /// # fn main() -> Result<(), Box<dyn std::error::Error>> {
163    /// use grafeo_engine::GrafeoDB;
164    ///
165    /// let db = GrafeoDB::new_in_memory();
166    /// let result = db.execute_sparql("SELECT ?s ?p ?o WHERE { ?s ?p ?o }")?;
167    /// # Ok(())
168    /// # }
169    /// ```
170    #[cfg(all(feature = "sparql", feature = "rdf"))]
171    pub fn execute_sparql(&self, query: &str) -> Result<QueryResult> {
172        use crate::query::{
173            Executor, optimizer::Optimizer, planner_rdf::RdfPlanner, sparql_translator,
174        };
175
176        // Parse and translate the SPARQL query to a logical plan
177        let logical_plan = sparql_translator::translate(query)?;
178
179        // Optimize the plan
180        let optimizer = Optimizer::from_store(&self.store);
181        let optimized_plan = optimizer.optimize(logical_plan)?;
182
183        // Convert to physical plan using RDF planner
184        let planner = RdfPlanner::new(Arc::clone(&self.rdf_store));
185        let mut physical_plan = planner.plan(&optimized_plan)?;
186
187        // Execute the plan
188        let executor = Executor::with_columns(physical_plan.columns.clone());
189        executor.execute(physical_plan.operator.as_mut())
190    }
191
192    /// Returns the RDF store.
193    ///
194    /// This provides direct access to the RDF store for triple operations.
195    #[cfg(feature = "rdf")]
196    #[must_use]
197    pub fn rdf_store(&self) -> &Arc<RdfStore> {
198        &self.rdf_store
199    }
200
201    /// Executes a query and returns a single scalar value.
202    ///
203    /// # Errors
204    ///
205    /// Returns an error if the query fails or doesn't return exactly one row.
206    pub fn query_scalar<T: FromValue>(&self, query: &str) -> Result<T> {
207        let result = self.execute(query)?;
208        result.scalar()
209    }
210}