Skip to main content

sqlitegraph/
backend.rs

1//! Backend trait bridging sqlitegraph with higher-level graph consumers.
2//!
3//! This module contains the core GraphBackend trait and redirects to modular
4//! backend implementations. SQLite-specific implementations are in the sqlite submodule.
5
6// Include the modular backend structure
7mod sqlite;
8
9// Include native backend storage layer (no GraphBackend implementation yet)
10pub mod native;
11
12// Re-export from sqlite submodule
13pub use sqlite::SqliteGraphBackend;
14
15// Re-export from native submodule
16pub use native::NativeGraphBackend;
17
18// Re-export types for external users
19pub use crate::multi_hop::ChainStep;
20#[allow(unused_imports)] // Backend trait API types for future GraphBackend implementations
21pub use sqlite::types::{BackendDirection, EdgeSpec, NeighborQuery, NodeSpec};
22
23// KV store types (re-exported for public API)
24pub use crate::backend::native::types::{KvStoreError, KvValue};
25
26/// Types of pub/sub events
27#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
28pub enum PubSubEventType {
29    /// Node created or modified
30    NodeChanged = 1,
31    /// Edge created or modified
32    EdgeChanged = 2,
33    /// KV entry created, modified, or deleted
34    KvChanged = 3,
35    /// Transaction committed
36    SnapshotCommitted = 4,
37    /// All event types
38    All = 255,
39}
40
41/// Event delivered to subscribers
42#[derive(Debug, Clone, PartialEq, Eq)]
43pub enum PubSubEvent {
44    /// Node created or modified
45    NodeChanged {
46        /// Node ID that changed
47        node_id: i64,
48        /// Snapshot ID when change was committed
49        snapshot_id: u64,
50    },
51    /// Edge created or modified
52    EdgeChanged {
53        /// Edge ID that changed
54        edge_id: i64,
55        /// Source node ID
56        from_node: i64,
57        /// Target node ID
58        to_node: i64,
59        /// Snapshot ID when change was committed
60        snapshot_id: u64,
61    },
62    /// KV entry changed
63    KvChanged {
64        /// Key hash for the KV entry
65        key_hash: u64,
66        /// Snapshot ID when change was committed
67        snapshot_id: u64,
68    },
69    /// Transaction committed
70    SnapshotCommitted {
71        /// Snapshot ID that was committed
72        snapshot_id: u64,
73    },
74}
75
76/// Filter for subscriptions
77#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
78pub struct SubscriptionFilter {
79    /// Subscribe to NodeChanged events
80    pub node_changes: bool,
81    /// Subscribe to EdgeChanged events
82    pub edge_changes: bool,
83    /// Subscribe to KvChanged events
84    pub kv_changes: bool,
85    /// Subscribe to SnapshotCommitted events
86    pub snapshot_commits: bool,
87}
88
89impl SubscriptionFilter {
90    /// Create a filter that receives all event types
91    pub fn all() -> Self {
92        Self {
93            node_changes: true,
94            edge_changes: true,
95            kv_changes: true,
96            snapshot_commits: true,
97        }
98    }
99}
100
101use crate::{
102    SqliteGraphError,
103    graph::GraphEntity,
104    pattern::{PatternMatch, PatternQuery},
105    pattern_engine::{PatternTriple, TripleMatch},
106    snapshot::SnapshotId,
107};
108///
109/// Each trait method delegates to backend-specific primitives while ensuring
110/// deterministic behavior and a single integration surface for consumers.
111///
112/// # Snapshot Isolation
113///
114/// All read operations require a `snapshot_id: SnapshotId` parameter to enforce
115/// ACID compliance. Reads only observe data committed at or before the snapshot.
116pub trait GraphBackend {
117    // Write operations (unchanged - commit returns SnapshotId in future)
118    fn insert_node(&self, node: NodeSpec) -> Result<i64, SqliteGraphError>;
119
120    /// Insert a node with a manually specified ID.
121    ///
122    /// Backends that support manual ID assignment (e.g. V3Backend, SqliteGraphBackend)
123    /// will respect this ID. Default implementation injects "id" into the node spec data.
124    fn insert_node_with_id(
125        &self,
126        mut node: NodeSpec,
127        node_id: i64,
128    ) -> Result<i64, SqliteGraphError> {
129        if let Some(obj) = node.data.as_object_mut() {
130            obj.insert("id".to_string(), serde_json::Value::Number(node_id.into()));
131        } else {
132            let mut obj = serde_json::Map::new();
133            obj.insert("id".to_string(), serde_json::Value::Number(node_id.into()));
134            node.data = serde_json::Value::Object(obj);
135        }
136        self.insert_node(node)
137    }
138
139    fn insert_edge(&self, edge: EdgeSpec) -> Result<i64, SqliteGraphError>;
140
141    /// Insert many nodes atomically.
142    ///
143    /// Default implementation loops single-insert for each item; backends
144    /// override with a single-transaction path. Returns the new node IDs in
145    /// the same order as `nodes`. All-or-nothing: if any insert fails the
146    /// caller should assume nothing was committed.
147    fn insert_nodes_bulk(&self, nodes: &[NodeSpec]) -> Result<Vec<i64>, SqliteGraphError> {
148        let mut ids = Vec::with_capacity(nodes.len());
149        for n in nodes {
150            ids.push(self.insert_node(n.clone())?);
151        }
152        Ok(ids)
153    }
154
155    /// Insert many edges atomically.
156    ///
157    /// Default implementation loops single-insert for each item; backends
158    /// override with a single-transaction path. Returns the new edge IDs in
159    /// the same order as `edges`. All-or-nothing semantics on the bulk path;
160    /// the default fallback inherits whatever atomicity the single
161    /// `insert_edge` provides.
162    fn insert_edges_bulk(&self, edges: &[EdgeSpec]) -> Result<Vec<i64>, SqliteGraphError> {
163        let mut ids = Vec::with_capacity(edges.len());
164        for e in edges {
165            ids.push(self.insert_edge(e.clone())?);
166        }
167        Ok(ids)
168    }
169
170    /// Update an existing node in place without allocating a new node ID
171    ///
172    /// This modifies the data associated with an existing node while preserving
173    /// its node_id.
174    ///
175    /// # Arguments
176    /// * `node_id` - The ID of the node to update (must exist)
177    /// * `node` - New node specification (kind, name, file_path, data)
178    ///
179    /// # Returns
180    /// The same `node_id` that was passed in, if update succeeded
181    ///
182    /// # Errors
183    /// Returns `SqliteGraphError` if:
184    /// - The node_id doesn't exist
185    /// - The update operation fails
186    /// - The backend doesn't support in-place updates
187    ///
188    /// # Behavior by Backend
189    /// - **Native**: Uses WAL to update node data in place, preserving node_id
190    /// - **SQLite**: Uses UPDATE SQL query on entities table
191    ///
192    /// # Example
193    /// ```ignore
194    /// // Create a node
195    /// let node_id = backend.insert_node(NodeSpec {
196    ///     kind: "File".to_string(),
197    ///     name: "main.rs".to_string(),
198    ///     file_path: Some("main.rs".to_string()),
199    ///     data: serde_json::json!({"hash": "abc123"}),
200    /// })?;
201    ///
202    /// // Update it - this does NOT allocate a new node_id
203    /// let updated_id = backend.update_node(node_id, NodeSpec {
204    ///     kind: "File".to_string(),
205    ///     name: "main.rs".to_string(),
206    ///     file_path: Some("main.rs".to_string()),
207    ///     data: serde_json::json!({"hash": "def456", "modified": true}),
208    /// })?;
209    ///
210    /// assert_eq!(updated_id, node_id); // Same ID!
211    /// ```
212    fn update_node(&self, node_id: i64, node: NodeSpec) -> Result<i64, SqliteGraphError>;
213
214    /// Delete an entity (node) from the graph by ID
215    ///
216    /// This removes the entity and all associated edges from the graph.
217    /// For SQLite backend: deletes from entities table and cascades to edges
218    /// For Native backend: marks node as deleted and updates adjacency indexes
219    fn delete_entity(&self, id: i64) -> Result<(), SqliteGraphError>;
220
221    /// Get all entity IDs from the graph
222    ///
223    /// Returns a vector of all node IDs currently stored in the graph.
224    /// For SQLite backend: queries all IDs from entities table
225    /// For Native backend: iterates over node store
226    fn entity_ids(&self) -> Result<Vec<i64>, SqliteGraphError>;
227
228    /// Alias for `entity_ids()` for backward compatibility with algorithms
229    ///
230    /// Default implementation delegates to `entity_ids()`.
231    fn all_entity_ids(&self) -> Result<Vec<i64>, SqliteGraphError> {
232        self.entity_ids()
233    }
234
235    /// Get outgoing neighbors for a node (convenience method)
236    ///
237    /// Default implementation uses `neighbors` with Outgoing direction.
238    fn fetch_outgoing(&self, node: i64) -> Result<Vec<i64>, SqliteGraphError> {
239        self.neighbors(
240            SnapshotId::current(),
241            node,
242            NeighborQuery {
243                direction: BackendDirection::Outgoing,
244                edge_type: None,
245            },
246        )
247    }
248
249    /// Get incoming neighbors for a node (convenience method)
250    ///
251    /// Default implementation uses `neighbors` with Incoming direction.
252    fn fetch_incoming(&self, node: i64) -> Result<Vec<i64>, SqliteGraphError> {
253        self.neighbors(
254            SnapshotId::current(),
255            node,
256            NeighborQuery {
257                direction: BackendDirection::Incoming,
258                edge_type: None,
259            },
260        )
261    }
262
263    // Read operations (require snapshot_id parameter)
264    fn get_node(&self, snapshot_id: SnapshotId, id: i64) -> Result<GraphEntity, SqliteGraphError>;
265    fn neighbors(
266        &self,
267        snapshot_id: SnapshotId,
268        node: i64,
269        query: NeighborQuery,
270    ) -> Result<Vec<i64>, SqliteGraphError>;
271    fn bfs(
272        &self,
273        snapshot_id: SnapshotId,
274        start: i64,
275        depth: u32,
276    ) -> Result<Vec<i64>, SqliteGraphError>;
277    fn shortest_path(
278        &self,
279        snapshot_id: SnapshotId,
280        start: i64,
281        end: i64,
282    ) -> Result<Option<Vec<i64>>, SqliteGraphError>;
283    fn node_degree(
284        &self,
285        snapshot_id: SnapshotId,
286        node: i64,
287    ) -> Result<(usize, usize), SqliteGraphError>;
288    fn k_hop(
289        &self,
290        snapshot_id: SnapshotId,
291        start: i64,
292        depth: u32,
293        direction: BackendDirection,
294    ) -> Result<Vec<i64>, SqliteGraphError>;
295    fn k_hop_filtered(
296        &self,
297        snapshot_id: SnapshotId,
298        start: i64,
299        depth: u32,
300        direction: BackendDirection,
301        allowed_edge_types: &[&str],
302    ) -> Result<Vec<i64>, SqliteGraphError>;
303    fn bfs_filtered(
304        &self,
305        snapshot_id: SnapshotId,
306        start: i64,
307        depth: u32,
308        direction: BackendDirection,
309        allowed_edge_types: &[&str],
310    ) -> Result<Vec<i64>, SqliteGraphError>;
311    fn shortest_path_filtered(
312        &self,
313        snapshot_id: SnapshotId,
314        start: i64,
315        end: i64,
316        allowed_edge_types: &[&str],
317    ) -> Result<Option<Vec<i64>>, SqliteGraphError>;
318    fn chain_query(
319        &self,
320        snapshot_id: SnapshotId,
321        start: i64,
322        chain: &[crate::multi_hop::ChainStep],
323    ) -> Result<Vec<i64>, SqliteGraphError>;
324    fn pattern_search(
325        &self,
326        snapshot_id: SnapshotId,
327        start: i64,
328        pattern: &PatternQuery,
329    ) -> Result<Vec<PatternMatch>, SqliteGraphError>;
330
331    /// Match triples based on a pattern triple
332    ///
333    /// Provides pattern matching capability for Cypher queries.
334    /// Default implementation returns unsupported operation error.
335    ///
336    /// # Arguments
337    /// * `pattern` - The pattern triple to match
338    ///
339    /// # Returns
340    /// Vector of triple matches with start_id, end_id, and edge_id
341    fn match_triples(&self, pattern: &PatternTriple) -> Result<Vec<TripleMatch>, SqliteGraphError> {
342        Err(SqliteGraphError::unsupported(format!(
343            "match_triples not implemented for pattern: {:?}",
344            pattern
345        )))
346    }
347
348    /// Search HNSW vector index for nearest neighbors
349    ///
350    /// Provides vector similarity search capability for Cypher queries.
351    /// Default implementation returns unsupported operation error.
352    ///
353    /// # Arguments
354    /// * `_snapshot` - Snapshot ID for read isolation
355    /// * `index_name` - Name of the HNSW index to search
356    /// * `_vector` - Query vector
357    /// * `_k` - Number of nearest neighbors to return
358    ///
359    /// # Returns
360    /// Vector of (node_id, distance) tuples for the k nearest neighbors
361    fn vector_search(
362        &self,
363        _snapshot: SnapshotId,
364        index_name: &str,
365        _vector: &[f32],
366        _k: usize,
367    ) -> Result<Vec<(i64, f32)>, SqliteGraphError> {
368        Err(SqliteGraphError::unsupported(format!(
369            "vector_search not implemented for index '{}'",
370            index_name
371        )))
372    }
373
374    /// Get reference to underlying SqliteGraph (if available)
375    ///
376    /// Returns None for non-SQLite backends (e.g., native-v3).
377    /// This method enables backend-agnostic introspection while
378    /// maintaining compatibility with SQLite-specific features.
379    ///
380    /// # Returns
381    /// Some(&SqliteGraph) for SQLite backends, None otherwise
382    fn get_graph_ref(&self) -> Option<&crate::graph::SqliteGraph> {
383        None
384    }
385
386    /// Trigger WAL checkpoint for backends that support write-ahead logging
387    ///
388    /// For Native backend with WAL: flushes WAL to graph file
389    /// For SQLite backend: executes PRAGMA wal_checkpoint(TRUNCATE)
390    /// For backends without WAL: returns Ok(()) as no-op
391    fn checkpoint(&self) -> Result<(), SqliteGraphError>;
392
393    /// Force immediate flush of WAL buffer to disk
394    ///
395    /// Ensures all buffered WAL records (including KV writes) are persisted
396    /// immediately, making them visible to other processes.
397    ///
398    /// For Native backend with WAL: flushes WAL buffer to disk
399    /// For SQLite backend: returns Ok(()) as no-op (SQLite handles sync)
400    /// For backends without WAL: returns Ok(()) as no-op
401    fn flush(&self) -> Result<(), SqliteGraphError>;
402
403    /// Create a backup of the database
404    ///
405    /// Creates a consistent snapshot of the database including all data pages.
406    ///
407    /// # Arguments
408    /// * `backup_dir` - Destination directory for backup files
409    ///
410    /// # Returns
411    /// Backup result with paths, checksum, and metadata
412    fn backup(&self, backup_dir: &std::path::Path) -> Result<BackupResult, SqliteGraphError>;
413
414    /// Export database snapshot to the specified directory
415    ///
416    /// Creates a consistent snapshot of the current database state.
417    /// For Native backend: uses snapshot format
418    /// For SQLite backend: uses JSON dump format
419    ///
420    /// # Arguments
421    /// * `export_dir` - Directory path where snapshot will be written
422    ///
423    /// # Returns
424    /// Snapshot metadata including file paths and size information
425    fn snapshot_export(
426        &self,
427        export_dir: &std::path::Path,
428    ) -> Result<SnapshotMetadata, SqliteGraphError>;
429
430    /// Import database snapshot from the specified directory
431    ///
432    /// Restores database state from a previously created snapshot.
433    /// For Native backend: loads snapshot format
434    /// For SQLite backend: loads JSON dump format
435    ///
436    /// # Arguments
437    /// * `import_dir` - Directory path containing snapshot files
438    ///
439    /// # Returns
440    /// Import metadata including number of records imported
441    fn snapshot_import(
442        &self,
443        import_dir: &std::path::Path,
444    ) -> Result<ImportMetadata, SqliteGraphError>;
445
446    /// Get a value from the KV store at the given snapshot
447    ///
448    /// # Arguments
449    /// * `snapshot_id` - Only return data committed at or before this snapshot
450    /// * `key` - Key to retrieve (arbitrary bytes)
451    ///
452    /// # Returns
453    /// The value if found and visible at snapshot, or None if not found
454    fn kv_get(
455        &self,
456        snapshot_id: SnapshotId,
457        key: &[u8],
458    ) -> Result<Option<crate::backend::native::types::KvValue>, SqliteGraphError>;
459
460    /// Set a value in the KV store
461    ///
462    /// This operation participates in the current transaction and will
463    /// be committed atomically with other graph operations.
464    ///
465    /// # Arguments
466    /// * `key` - Key to set (arbitrary bytes)
467    /// * `value` - Value to store
468    /// * `ttl_seconds` - Optional TTL in seconds (None = no expiration)
469    fn kv_set(
470        &self,
471        key: Vec<u8>,
472        value: crate::backend::native::types::KvValue,
473        ttl_seconds: Option<u64>,
474    ) -> Result<(), SqliteGraphError>;
475
476    /// Delete a value from the KV store
477    ///
478    /// This operation participates in the current transaction and will
479    /// be committed atomically with other graph operations.
480    ///
481    /// # Arguments
482    /// * `key` - Key to delete
483    fn kv_delete(&self, key: &[u8]) -> Result<(), SqliteGraphError>;
484
485    // Pub/Sub operations (in-process event notification)
486
487    /// Subscribe to graph change events
488    ///
489    /// Returns a subscriber ID and a receiver channel for events.
490    /// The receiver will receive events that match the given filter.
491    ///
492    /// # Events
493    ///
494    /// Events are emitted ONLY on transaction commit:
495    /// - `NodeChanged` - node created or modified
496    /// - `EdgeChanged` - edge created or modified
497    /// - `KvChanged` - KV entry created, modified, or deleted
498    /// - `SnapshotCommitted` - transaction committed
499    ///
500    /// # Best-Effort Delivery
501    ///
502    /// - No persistence of events
503    /// - If receiver is dropped, events are silently dropped
504    /// - If channel is full, events are silently dropped
505    /// - No delivery guarantees
506    ///
507    /// # Example
508    ///
509    /// ```ignore
510    /// let (sub_id, rx) = graph.subscribe(SubscriptionFilter::all());
511    /// // In another thread/task:
512    /// for event in rx {
513    ///     match event {
514    ///         PubSubEvent::NodeChanged { node_id, snapshot_id } => {
515    ///             // Read node state at snapshot_id
516    ///         }
517    ///         _ => {}
518    ///     }
519    /// }
520    /// }
521    /// ```
522    fn subscribe(
523        &self,
524        filter: SubscriptionFilter,
525    ) -> Result<(u64, std::sync::mpsc::Receiver<PubSubEvent>), SqliteGraphError>;
526
527    /// Unsubscribe from events
528    ///
529    /// Cancels the subscription and stops receiving events.
530    /// Returns true if subscription existed and was removed.
531    ///
532    /// # Arguments
533    /// * `subscriber_id` - The subscriber ID returned by subscribe()
534    fn unsubscribe(&self, subscriber_id: u64) -> Result<bool, SqliteGraphError>;
535
536    // ========== Pub/Sub Enhancement APIs (v1.4.0) ==========
537
538    /// Scan all KV entries with a given prefix
539    ///
540    /// Returns all keys that start with the given prefix, along with their values.
541    /// Results are in lexicographic order by key.
542    ///
543    /// # Arguments
544    /// * `snapshot_id` - Only return data committed at or before this snapshot
545    /// * `prefix` - Prefix to match (empty prefix returns all keys)
546    ///
547    /// # Returns
548    /// Vector of (key, value) pairs for all matching keys
549    fn kv_prefix_scan(
550        &self,
551        snapshot_id: SnapshotId,
552        prefix: &[u8],
553    ) -> Result<Vec<(Vec<u8>, crate::backend::native::types::KvValue)>, SqliteGraphError>;
554
555    /// Query all nodes with a given kind
556    ///
557    /// Returns all node IDs where the node's kind equals the given string.
558    /// Results are sorted by node ID for deterministic output.
559    ///
560    /// # Arguments
561    /// * `snapshot_id` - Only return data committed at or before this snapshot
562    /// * `kind` - Kind string to match (case-sensitive)
563    ///
564    /// # Returns
565    /// Vector of node IDs with matching kind
566    fn query_nodes_by_kind(
567        &self,
568        snapshot_id: SnapshotId,
569        kind: &str,
570    ) -> Result<Vec<i64>, SqliteGraphError>;
571
572    /// Query nodes by name pattern using glob matching
573    ///
574    /// Returns all node IDs where the node's label matches the glob pattern.
575    /// Pattern syntax:
576    /// - `*` matches any sequence of characters
577    /// - `?` matches exactly one character
578    ///
579    /// # Arguments
580    /// * `snapshot_id` - Only return data committed at or before this snapshot
581    /// * `pattern` - Glob pattern to match against node labels
582    ///
583    /// # Returns
584    /// Vector of node IDs with matching labels
585    fn query_nodes_by_name_pattern(
586        &self,
587        snapshot_id: SnapshotId,
588        pattern: &str,
589    ) -> Result<Vec<i64>, SqliteGraphError>;
590}
591
592/// Metadata returned by snapshot export operations
593#[derive(Debug, Clone)]
594pub struct SnapshotMetadata {
595    /// Path to the snapshot file
596    pub snapshot_path: std::path::PathBuf,
597    /// Snapshot size in bytes
598    pub size_bytes: u64,
599    /// Number of entities in snapshot
600    pub entity_count: u64,
601    /// Number of edges in snapshot
602    pub edge_count: u64,
603}
604
605/// Metadata returned by snapshot import operations
606#[derive(Debug, Clone)]
607pub struct ImportMetadata {
608    /// Path to the imported snapshot
609    pub snapshot_path: std::path::PathBuf,
610    /// Number of entities imported
611    pub entities_imported: u64,
612    /// Number of edges imported
613    pub edges_imported: u64,
614}
615
616/// Result returned by backup operations
617#[derive(Debug, Clone)]
618pub struct BackupResult {
619    /// Path to backup snapshot file
620    pub snapshot_path: std::path::PathBuf,
621
622    /// Path to backup manifest file
623    pub manifest_path: std::path::PathBuf,
624
625    /// Backup size in bytes
626    pub size_bytes: u64,
627
628    /// Backup checksum
629    pub checksum: u64,
630
631    /// Number of records in backup
632    pub record_count: u64,
633
634    /// Backup duration in seconds
635    pub duration_secs: f64,
636
637    /// Backup timestamp (Unix epoch)
638    pub timestamp: u64,
639
640    /// Whether checkpoint was performed before backup
641    pub checkpoint_performed: bool,
642}
643
644/// Reference implementation for GraphBackend trait that works with references.
645impl<B> GraphBackend for &B
646where
647    B: GraphBackend + ?Sized,
648{
649    fn insert_node(&self, node: NodeSpec) -> Result<i64, SqliteGraphError> {
650        (*self).insert_node(node)
651    }
652
653    fn insert_node_with_id(&self, node: NodeSpec, node_id: i64) -> Result<i64, SqliteGraphError> {
654        (*self).insert_node_with_id(node, node_id)
655    }
656
657    fn get_node(&self, snapshot_id: SnapshotId, id: i64) -> Result<GraphEntity, SqliteGraphError> {
658        (*self).get_node(snapshot_id, id)
659    }
660
661    fn insert_edge(&self, edge: EdgeSpec) -> Result<i64, SqliteGraphError> {
662        (*self).insert_edge(edge)
663    }
664
665    fn update_node(&self, node_id: i64, node: NodeSpec) -> Result<i64, SqliteGraphError> {
666        (*self).update_node(node_id, node)
667    }
668
669    fn delete_entity(&self, id: i64) -> Result<(), SqliteGraphError> {
670        (*self).delete_entity(id)
671    }
672
673    fn entity_ids(&self) -> Result<Vec<i64>, SqliteGraphError> {
674        (*self).entity_ids()
675    }
676
677    fn insert_nodes_bulk(&self, nodes: &[NodeSpec]) -> Result<Vec<i64>, SqliteGraphError> {
678        (*self).insert_nodes_bulk(nodes)
679    }
680
681    fn insert_edges_bulk(&self, edges: &[EdgeSpec]) -> Result<Vec<i64>, SqliteGraphError> {
682        (*self).insert_edges_bulk(edges)
683    }
684
685    fn neighbors(
686        &self,
687        snapshot_id: SnapshotId,
688        node: i64,
689        query: NeighborQuery,
690    ) -> Result<Vec<i64>, SqliteGraphError> {
691        (*self).neighbors(snapshot_id, node, query)
692    }
693
694    fn bfs(
695        &self,
696        snapshot_id: SnapshotId,
697        start: i64,
698        depth: u32,
699    ) -> Result<Vec<i64>, SqliteGraphError> {
700        (*self).bfs(snapshot_id, start, depth)
701    }
702
703    fn shortest_path(
704        &self,
705        snapshot_id: SnapshotId,
706        start: i64,
707        end: i64,
708    ) -> Result<Option<Vec<i64>>, SqliteGraphError> {
709        (*self).shortest_path(snapshot_id, start, end)
710    }
711
712    fn node_degree(
713        &self,
714        snapshot_id: SnapshotId,
715        node: i64,
716    ) -> Result<(usize, usize), SqliteGraphError> {
717        (*self).node_degree(snapshot_id, node)
718    }
719
720    fn k_hop(
721        &self,
722        snapshot_id: SnapshotId,
723        start: i64,
724        depth: u32,
725        direction: BackendDirection,
726    ) -> Result<Vec<i64>, SqliteGraphError> {
727        (*self).k_hop(snapshot_id, start, depth, direction)
728    }
729
730    fn k_hop_filtered(
731        &self,
732        snapshot_id: SnapshotId,
733        start: i64,
734        depth: u32,
735        direction: BackendDirection,
736        allowed_edge_types: &[&str],
737    ) -> Result<Vec<i64>, SqliteGraphError> {
738        (*self).k_hop_filtered(snapshot_id, start, depth, direction, allowed_edge_types)
739    }
740
741    fn bfs_filtered(
742        &self,
743        snapshot_id: SnapshotId,
744        start: i64,
745        depth: u32,
746        direction: BackendDirection,
747        allowed_edge_types: &[&str],
748    ) -> Result<Vec<i64>, SqliteGraphError> {
749        (*self).bfs_filtered(snapshot_id, start, depth, direction, allowed_edge_types)
750    }
751
752    fn shortest_path_filtered(
753        &self,
754        snapshot_id: SnapshotId,
755        start: i64,
756        end: i64,
757        allowed_edge_types: &[&str],
758    ) -> Result<Option<Vec<i64>>, SqliteGraphError> {
759        (*self).shortest_path_filtered(snapshot_id, start, end, allowed_edge_types)
760    }
761
762    fn chain_query(
763        &self,
764        snapshot_id: SnapshotId,
765        start: i64,
766        chain: &[crate::multi_hop::ChainStep],
767    ) -> Result<Vec<i64>, SqliteGraphError> {
768        (*self).chain_query(snapshot_id, start, chain)
769    }
770
771    fn pattern_search(
772        &self,
773        snapshot_id: SnapshotId,
774        start: i64,
775        pattern: &PatternQuery,
776    ) -> Result<Vec<PatternMatch>, SqliteGraphError> {
777        (*self).pattern_search(snapshot_id, start, pattern)
778    }
779
780    fn checkpoint(&self) -> Result<(), SqliteGraphError> {
781        (*self).checkpoint()
782    }
783
784    fn flush(&self) -> Result<(), SqliteGraphError> {
785        (*self).flush()
786    }
787
788    fn backup(&self, backup_dir: &std::path::Path) -> Result<BackupResult, SqliteGraphError> {
789        (*self).backup(backup_dir)
790    }
791
792    fn snapshot_export(
793        &self,
794        export_dir: &std::path::Path,
795    ) -> Result<SnapshotMetadata, SqliteGraphError> {
796        (*self).snapshot_export(export_dir)
797    }
798
799    fn snapshot_import(
800        &self,
801        import_dir: &std::path::Path,
802    ) -> Result<ImportMetadata, SqliteGraphError> {
803        (*self).snapshot_import(import_dir)
804    }
805
806    fn kv_get(
807        &self,
808        snapshot_id: SnapshotId,
809        key: &[u8],
810    ) -> Result<Option<crate::backend::native::types::KvValue>, SqliteGraphError> {
811        (*self).kv_get(snapshot_id, key)
812    }
813
814    fn kv_set(
815        &self,
816        key: Vec<u8>,
817        value: crate::backend::native::types::KvValue,
818        ttl_seconds: Option<u64>,
819    ) -> Result<(), SqliteGraphError> {
820        (*self).kv_set(key, value, ttl_seconds)
821    }
822
823    fn kv_delete(&self, key: &[u8]) -> Result<(), SqliteGraphError> {
824        (*self).kv_delete(key)
825    }
826
827    fn subscribe(
828        &self,
829        filter: SubscriptionFilter,
830    ) -> Result<(u64, std::sync::mpsc::Receiver<PubSubEvent>), SqliteGraphError> {
831        (*self).subscribe(filter)
832    }
833
834    fn unsubscribe(&self, subscriber_id: u64) -> Result<bool, SqliteGraphError> {
835        (*self).unsubscribe(subscriber_id)
836    }
837
838    fn kv_prefix_scan(
839        &self,
840        snapshot_id: SnapshotId,
841        prefix: &[u8],
842    ) -> Result<Vec<(Vec<u8>, crate::backend::native::types::KvValue)>, SqliteGraphError> {
843        (*self).kv_prefix_scan(snapshot_id, prefix)
844    }
845
846    fn query_nodes_by_kind(
847        &self,
848        snapshot_id: SnapshotId,
849        kind: &str,
850    ) -> Result<Vec<i64>, SqliteGraphError> {
851        (*self).query_nodes_by_kind(snapshot_id, kind)
852    }
853
854    fn query_nodes_by_name_pattern(
855        &self,
856        snapshot_id: SnapshotId,
857        pattern: &str,
858    ) -> Result<Vec<i64>, SqliteGraphError> {
859        (*self).query_nodes_by_name_pattern(snapshot_id, pattern)
860    }
861
862    fn match_triples(&self, pattern: &PatternTriple) -> Result<Vec<TripleMatch>, SqliteGraphError> {
863        (*self).match_triples(pattern)
864    }
865
866    fn vector_search(
867        &self,
868        snapshot: SnapshotId,
869        index_name: &str,
870        vector: &[f32],
871        k: usize,
872    ) -> Result<Vec<(i64, f32)>, SqliteGraphError> {
873        (*self).vector_search(snapshot, index_name, vector, k)
874    }
875
876    fn get_graph_ref(&self) -> Option<&crate::graph::SqliteGraph> {
877        (*self).get_graph_ref()
878    }
879}
880
881/// AsyncGraphBackend defines non-blocking graph operations.
882/// Supports async/await natively using Rust 2024 edition features.
883pub trait AsyncGraphBackend: Send + Sync {
884    /// Retrieve a node asynchronously from the snapshot.
885    fn get_node(
886        &self,
887        snapshot_id: SnapshotId,
888        id: i64,
889    ) -> impl std::future::Future<Output = Result<GraphEntity, SqliteGraphError>> + Send;
890
891    /// Retrieve neighbor node IDs for a given source node asynchronously.
892    fn neighbors(
893        &self,
894        snapshot_id: SnapshotId,
895        node: i64,
896        query: NeighborQuery,
897    ) -> impl std::future::Future<Output = Result<Vec<i64>, SqliteGraphError>> + Send;
898}