sqlitegraph/backend/sqlite/
impl_.rs

1//! SQLite backend implementation for GraphBackend trait.
2//!
3//! This module contains the SqliteGraphBackend implementation that wraps
4//! SqliteGraph and provides the concrete implementation of all GraphBackend methods.
5
6use rusqlite::params;
7
8use crate::{
9    SqliteGraphError,
10    backend::sqlite::types::{BackendDirection, EdgeSpec, NeighborQuery, NodeSpec},
11    bfs::{bfs_neighbors, shortest_path},
12    graph::{GraphEdge, GraphEntity, SqliteGraph},
13    multi_hop,
14    pattern::{self, PatternMatch, PatternQuery},
15};
16
17/// SQLite-backed implementation of the GraphBackend trait.
18///
19/// This struct wraps a SqliteGraph instance and implements all GraphBackend methods
20/// by delegating to the underlying SQLite-based graph operations.
21pub struct SqliteGraphBackend {
22    graph: SqliteGraph,
23}
24
25impl SqliteGraphBackend {
26    /// Create a new SQLite backend with an in-memory database.
27    pub fn in_memory() -> Result<Self, SqliteGraphError> {
28        Ok(Self {
29            graph: SqliteGraph::open_in_memory()?,
30        })
31    }
32
33    /// Create a new SQLite backend from an existing SqliteGraph instance.
34    pub fn from_graph(graph: SqliteGraph) -> Self {
35        Self { graph }
36    }
37
38    /// Get a reference to the underlying SqliteGraph instance.
39    pub fn graph(&self) -> &SqliteGraph {
40        &self.graph
41    }
42
43    /// Get all entity IDs from the graph.
44    pub fn entity_ids(&self) -> Result<Vec<i64>, SqliteGraphError> {
45        self.graph.all_entity_ids()
46    }
47
48    /// Execute optimized neighbor queries based on direction and edge type filtering.
49    fn query_neighbors(
50        &self,
51        node: i64,
52        direction: BackendDirection,
53        edge_type: &Option<String>,
54    ) -> Result<Vec<i64>, SqliteGraphError> {
55        match (direction, edge_type) {
56            (BackendDirection::Outgoing, None) => self.graph.fetch_outgoing(node),
57            (BackendDirection::Incoming, None) => self.graph.fetch_incoming(node),
58            (BackendDirection::Outgoing, Some(edge_type)) => {
59                let conn = self.graph.connection();
60                let mut stmt = conn
61                    .prepare_cached(
62                        "SELECT to_id FROM graph_edges WHERE from_id=?1 AND edge_type=?2 ORDER BY to_id, id",
63                    )
64                    .map_err(|e| SqliteGraphError::query(e.to_string()))?;
65                let rows = stmt
66                    .query_map(params![node, edge_type], |row| row.get(0))
67                    .map_err(|e| SqliteGraphError::query(e.to_string()))?;
68                let mut values = Vec::new();
69                for value in rows {
70                    values.push(value.map_err(|e| SqliteGraphError::query(e.to_string()))?);
71                }
72                Ok(values)
73            }
74            (BackendDirection::Incoming, Some(edge_type)) => {
75                let conn = self.graph.connection();
76                let mut stmt = conn
77                    .prepare_cached(
78                        "SELECT from_id FROM graph_edges WHERE to_id=?1 AND edge_type=?2 ORDER BY from_id, id",
79                    )
80                    .map_err(|e| SqliteGraphError::query(e.to_string()))?;
81                let rows = stmt
82                    .query_map(params![node, edge_type], |row| row.get(0))
83                    .map_err(|e| SqliteGraphError::query(e.to_string()))?;
84                let mut values = Vec::new();
85                for value in rows {
86                    values.push(value.map_err(|e| SqliteGraphError::query(e.to_string()))?);
87                }
88                Ok(values)
89            }
90        }
91    }
92}
93
94impl crate::backend::GraphBackend for SqliteGraphBackend {
95    fn insert_node(&self, node: NodeSpec) -> Result<i64, SqliteGraphError> {
96        self.graph.insert_entity(&GraphEntity {
97            id: 0,
98            kind: node.kind,
99            name: node.name,
100            file_path: node.file_path,
101            data: node.data,
102        })
103    }
104
105    fn get_node(&self, id: i64) -> Result<GraphEntity, SqliteGraphError> {
106        self.graph.get_entity(id)
107    }
108
109    fn insert_edge(&self, edge: EdgeSpec) -> Result<i64, SqliteGraphError> {
110        self.graph.insert_edge(&GraphEdge {
111            id: 0,
112            from_id: edge.from,
113            to_id: edge.to,
114            edge_type: edge.edge_type,
115            data: edge.data,
116        })
117    }
118
119    fn neighbors(&self, node: i64, query: NeighborQuery) -> Result<Vec<i64>, SqliteGraphError> {
120        self.query_neighbors(node, query.direction, &query.edge_type)
121    }
122
123    fn bfs(&self, start: i64, depth: u32) -> Result<Vec<i64>, SqliteGraphError> {
124        // Check query cache first
125        if let Some(cached_result) = self.graph.query_cache.get_bfs(start, depth) {
126            return Ok(cached_result);
127        }
128
129        // Cache miss - compute and cache the result
130        let result = bfs_neighbors(&self.graph, start, depth)?;
131        self.graph.query_cache.put_bfs(start, depth, result.clone());
132        Ok(result)
133    }
134
135    fn shortest_path(&self, start: i64, end: i64) -> Result<Option<Vec<i64>>, SqliteGraphError> {
136        // Check query cache first
137        if let Some(cached_result) = self.graph.query_cache.get_shortest_path(start, end) {
138            return Ok(cached_result);
139        }
140
141        // Cache miss - compute and cache the result
142        let result = shortest_path(&self.graph, start, end)?;
143        self.graph
144            .query_cache
145            .put_shortest_path(start, end, result.clone());
146        Ok(result)
147    }
148
149    fn node_degree(&self, node: i64) -> Result<(usize, usize), SqliteGraphError> {
150        let out = self.graph.fetch_outgoing(node)?.len();
151        let incoming = self.graph.fetch_incoming(node)?.len();
152        Ok((out, incoming))
153    }
154
155    fn k_hop(
156        &self,
157        start: i64,
158        depth: u32,
159        direction: BackendDirection,
160    ) -> Result<Vec<i64>, SqliteGraphError> {
161        // Check query cache first
162        if let Some(cached_result) = self.graph.query_cache.get_k_hop(start, depth, direction) {
163            return Ok(cached_result);
164        }
165
166        // Cache miss - compute and cache the result
167        let result = multi_hop::k_hop(&self.graph, start, depth, direction)?;
168        self.graph
169            .query_cache
170            .put_k_hop(start, depth, direction, result.clone());
171        Ok(result)
172    }
173
174    fn k_hop_filtered(
175        &self,
176        start: i64,
177        depth: u32,
178        direction: BackendDirection,
179        allowed_edge_types: &[&str],
180    ) -> Result<Vec<i64>, SqliteGraphError> {
181        // Check query cache first
182        if let Some(cached_result) =
183            self.graph
184                .query_cache
185                .get_k_hop_filtered(start, depth, direction, allowed_edge_types)
186        {
187            return Ok(cached_result);
188        }
189
190        // Cache miss - compute and cache the result
191        let result =
192            multi_hop::k_hop_filtered(&self.graph, start, depth, direction, allowed_edge_types)?;
193        self.graph.query_cache.put_k_hop_filtered(
194            start,
195            depth,
196            direction,
197            allowed_edge_types,
198            result.clone(),
199        );
200        Ok(result)
201    }
202
203    fn chain_query(
204        &self,
205        start: i64,
206        chain: &[crate::multi_hop::ChainStep],
207    ) -> Result<Vec<i64>, SqliteGraphError> {
208        multi_hop::chain_query(&self.graph, start, chain)
209    }
210
211    fn pattern_search(
212        &self,
213        start: i64,
214        pattern: &PatternQuery,
215    ) -> Result<Vec<PatternMatch>, SqliteGraphError> {
216        pattern::execute_pattern(&self.graph, start, pattern)
217    }
218}