Skip to main content

SqliteGraph

Struct SqliteGraph 

Source
pub struct SqliteGraph {
    pub pool: PoolManager,
    pub hnsw_indexes: RwLock<HashMap<String, HnswIndex>>,
    /* private fields */
}
Expand description

Embedded SQLite-backed graph database.

Provides a lightweight, deterministic graph database with entity and edge storage, pattern matching, MVCC-lite snapshots, and deterministic indexing.

§Thread Safety

NOT thread-safe for concurrent writes. SqliteGraph uses interior mutability (RefCell) and is not Sync. However, the underlying connection pool allows multiple threads to read concurrently when using separate SqliteGraph instances.

§Connection Pooling

File-based databases use an r2d2 connection pool (default 5 connections) for concurrent access. In-memory databases skip pooling and use a single direct connection.

Fields§

§pool: PoolManager

Connection pool for file-based databases, or direct connection for in-memory (public for CLI access to underlying connection)

§hnsw_indexes: RwLock<HashMap<String, HnswIndex>>

HNSW vector indexes stored by name (public for CLI access)

Implementations§

Source§

impl SqliteGraph

Source§

impl SqliteGraph

Source

pub fn open_with_config<P: AsRef<Path>>( path: P, cfg: &SqliteConfig, ) -> Result<Self, SqliteGraphError>

Open a graph database with custom configuration.

§Arguments
  • path - Path to the SQLite database file
  • cfg - Configuration options (pool size, cache size, PRAGMAs, etc.)
§Example
use sqlitegraph::{SqliteGraph, SqliteConfig};

let cfg = SqliteConfig::new()
    .with_pool_size(10)
    .with_wal_mode();
let graph = SqliteGraph::open_with_config("my_graph.db", &cfg)?;
Source

pub fn open<P: AsRef<Path>>(path: P) -> Result<Self, SqliteGraphError>

Source

pub fn open_without_migrations<P: AsRef<Path>>( path: P, ) -> Result<Self, SqliteGraphError>

Source

pub fn open_in_memory_with_config( cfg: &SqliteConfig, ) -> Result<Self, SqliteGraphError>

Open an in-memory database with custom configuration.

Note: Pool size is ignored for in-memory databases since they use a single direct connection (each connection would have isolated data).

§Arguments
  • cfg - Configuration options (cache size, PRAGMAs, etc.)
§Example
use sqlitegraph::{SqliteGraph, SqliteConfig};

let cfg = SqliteConfig::new()
    .with_cache_size(256)
    .with_performance_mode();
let graph = SqliteGraph::open_in_memory_with_config(&cfg)?;
Source

pub fn open_in_memory() -> Result<Self, SqliteGraphError>

Source

pub fn open_in_memory_without_migrations() -> Result<Self, SqliteGraphError>

Source

pub fn introspect(&self) -> Result<GraphIntrospection, SqliteGraphError>

Get comprehensive introspection data for this graph instance.

This method provides a structured snapshot of the graph state, including node counts, edge counts, cache statistics, and file sizes. The result is JSON-serializable for both human debugging and LLM consumption.

§Example
use sqlitegraph::{open_graph, GraphConfig};

let graph = open_graph("my_graph.db", &GraphConfig::sqlite())?;
let intro = graph.introspect()?;

println!("Backend: {}", intro.backend_type);
println!("Nodes: {}", intro.node_count);
println!("Cache hit ratio: {:.2}%", intro.cache_stats.hit_ratio().unwrap_or(0.0));
Source

pub fn cache_stats(&self) -> CacheStats

Get adjacency cache statistics.

Returns combined statistics from both outgoing and incoming adjacency caches. This is useful for monitoring cache effectiveness and tuning cache size.

§Example
use sqlitegraph::{open_graph, GraphConfig};

let graph = open_graph("my_graph.db", &GraphConfig::sqlite())?;
let stats = graph.cache_stats();

println!("Cache hits: {}", stats.hits);
println!("Cache misses: {}", stats.misses);
println!("Hit ratio: {:.2}%", stats.hit_ratio().unwrap_or(0.0));
Source§

impl SqliteGraph

Source§

impl SqliteGraph

Source§

impl SqliteGraph

Source§

impl SqliteGraph

Source

pub fn match_triples( &self, pattern: &PatternTriple, ) -> Result<Vec<TripleMatch>, SqliteGraphError>

Match lightweight triple patterns using pattern engine.

This method provides a simple interface for matching single-hop patterns like (start_label)-[edge_type]->(end_label) with optional property filters.

§Arguments
  • pattern - The pattern triple to match
§Returns

A vector of triple matches in deterministic order

Source

pub fn match_triples_fast( &self, pattern: &PatternTriple, ) -> Result<Vec<TripleMatch>, SqliteGraphError>

Match lightweight triple patterns using cache-enabled fast-path.

This method provides an optimized version of pattern matching that:

  • Uses cache as a fast-path where safe
  • Falls back to SQL where pattern requires it
  • Returns IDENTICAL results to match_triples()
  • Maintains deterministic ordering
§Arguments
  • pattern - The pattern triple to match
§Returns

A vector of triple matches in deterministic order

Source§

impl SqliteGraph

Source

pub fn acquire_snapshot(&self) -> Result<GraphSnapshot, SqliteGraphError>

Acquire a deterministic snapshot of the current graph state

Returns a read-only snapshot that provides isolated access to graph data. The snapshot contains cloned adjacency maps and uses a read-only SQLite connection.

§MVCC-lite Snapshot Isolation

Snapshots provide MVCC-lite isolation guarantees:

  • Immutable: Snapshot state never changes after creation
  • Consistent: Snapshot sees a point-in-time view of the graph
  • Isolated: Snapshot unaffected by subsequent writes
  • Cloned Data: Adjacency maps are fully cloned (not shared)
§Cache Requirement

IMPORTANT: Snapshots read from the in-memory adjacency cache, not the database. For accurate snapshots, the cache must be warmed first:

use sqlitegraph::SqliteGraph;

let graph = SqliteGraph::open_in_memory()?;
// ... perform writes ...

// Warm cache before snapshot
let entity_ids = graph.list_entity_ids()?;
for &id in &entity_ids {
    let _ = graph.query().outgoing(id);
    let _ = graph.query().incoming(id);
}

// Now acquire snapshot
let snapshot = graph.acquire_snapshot()?;
assert!(snapshot.node_count() > 0);

Without cache warming, snapshots may appear empty even if the database has data.

§Thread Safety

The underlying SnapshotManager is thread-safe and uses lock-free ArcSwap. However, SqliteGraph itself is NOT thread-safe (contains RefCell, non-Sync types).

For concurrent snapshot acquisition, wrap SqliteGraph in a Mutex or RwLock:

use std::sync::{Arc, Mutex};
use sqlitegraph::SqliteGraph;

let graph = Arc::new(Mutex::new(SqliteGraph::open_in_memory()?));
// Multiple threads can now safely acquire snapshots
§Performance
  • Acquisition: < 1ms typical (Arc::clone overhead)
  • Memory: O(N + E) where N = nodes, E = edges (full copy)
  • Throughput: > 10,000 snapshots/second single-threaded
§Returns

Result containing GraphSnapshot or error

§Errors

Returns error if:

  • Read-only SQLite connection cannot be opened
  • Database connection fails
Source

pub fn snapshot(&self) -> Result<GraphSnapshot, SqliteGraphError>

Convenience alias for acquire_snapshot()

This is a shorter name for acquiring snapshots, equivalent to:

let snapshot = graph.snapshot()?;

See acquire_snapshot() for full documentation.

Source

pub fn snapshot_node_count(&self) -> usize

Get the number of nodes in the current snapshot

Note: This requires cache warming to return accurate results. See acquire_snapshot() documentation for details.

Source

pub fn snapshot_edge_count(&self) -> usize

Get the number of edges in the current snapshot

Note: This requires cache warming to return accurate results. See acquire_snapshot() documentation for details.

Source

pub fn snapshot_contains_node(&self, node_id: i64) -> bool

Check if a node exists in the current snapshot

Note: This requires cache warming to return accurate results. See acquire_snapshot() documentation for details.

Source§

impl SqliteGraph

Source

pub fn reasoner(&self) -> GraphReasoner<'_>

Source§

impl SqliteGraph

Source

pub fn query(&self) -> GraphQuery<'_>

§

impl SqliteGraph

SQLiteGraph extension for HNSW vector search

pub fn hnsw_index( &self, name: &str, config: HnswConfig, ) -> Result<RwLockWriteGuard<'_, HashMap<String, HnswIndex>>, SqliteGraphError>

Create or get an HNSW index with the specified name and configuration

§Arguments
  • name - Name to identify this index (for multi-index support)
  • config - HNSW configuration parameters
§Returns

Returns a mutable reference to the HnswIndex ready for vector operations

§Examples
use sqlitegraph::{SqliteGraph, hnsw::{HnswConfig, DistanceMetric}};

let graph = SqliteGraph::open_in_memory()?;
let config = HnswConfig::builder()
    .dimension(256)
    .distance_metric(DistanceMetric::Cosine)
    .build()?;

let hnsw = graph.hnsw_index("embeddings", config)?;

pub fn hnsw_index_persistent( &self, name: &str, config: HnswConfig, ) -> Result<RwLockWriteGuard<'_, HashMap<String, HnswIndex>>, SqliteGraphError>

Create or get an HNSW index with persistent storage (for file-based databases)

This method automatically detects if the database is file-based and creates the index with SQLiteVectorStorage for automatic vector persistence. For in-memory databases, falls back to in-memory storage.

§Arguments
  • name - Name to identify this index
  • config - HNSW configuration parameters
§Returns

Returns a mutable reference to the HnswIndex ready for vector operations

§Examples
use sqlitegraph::{SqliteGraph, hnsw::{HnswConfig, DistanceMetric}};

let graph = SqliteGraph::open("mydb.db")?;
let config = HnswConfig::builder()
    .dimension(256)
    .distance_metric(DistanceMetric::Cosine)
    .build()?;

let hnsw = graph.hnsw_index_persistent("embeddings", config)?;
// Vectors inserted into this index will persist to the database

pub fn get_hnsw_index( &self, name: &str, ) -> Result<Option<RwLockWriteGuard<'_, HashMap<String, HnswIndex>>>, SqliteGraphError>

Get an existing HNSW index by name

§Arguments
  • name - Name of the index to retrieve
§Returns

Returns a mutable reference to the HnswIndex if it exists

pub fn get_hnsw_index_ref<F, R>( &self, name: &str, f: F, ) -> Result<R, SqliteGraphError>
where F: FnOnce(&HnswIndex) -> R,

Get a reference to an HNSW index without locking for write

pub fn get_hnsw_index_mut<F, R>( &self, name: &str, f: F, ) -> Result<R, SqliteGraphError>
where F: FnOnce(&mut HnswIndex) -> R,

Get a mutable reference to an HNSW index for modifications

pub fn list_hnsw_indexes(&self) -> Result<Vec<String>, SqliteGraphError>

List all HNSW index names

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> if into_left is true. Converts self into a Right variant of Either<Self, Self> otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> if into_left(&self) returns true. Converts self into a Right variant of Either<Self, Self> otherwise. Read more
Source§

impl<T> Pointable for T

Source§

const ALIGN: usize

The alignment of pointer.
Source§

type Init = T

The type for initializers.
Source§

unsafe fn init(init: <T as Pointable>::Init) -> usize

Initializes a with the given initializer. Read more
Source§

unsafe fn deref<'a>(ptr: usize) -> &'a T

Dereferences the given pointer. Read more
Source§

unsafe fn deref_mut<'a>(ptr: usize) -> &'a mut T

Mutably dereferences the given pointer. Read more
Source§

unsafe fn drop(ptr: usize)

Drops the object pointed to by the given pointer. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
Source§

impl<V, T> VZip<V> for T
where V: MultiLane<T>,

Source§

fn vzip(self) -> V