sqlitegraph 2.2.0

Embedded graph database with full ACID transactions, HNSW vector search, dual backend support, and comprehensive graph algorithms library
Documentation
use serde_json::json;
use sqlitegraph::{
    graph::SqliteGraph,
    graph_opt::{
        GraphEdgeCreate, GraphEntityCreate, adjacency_fetch_outgoing_batch, bulk_insert_edges,
        bulk_insert_entities, cache_clear_ranges, cache_stats,
    },
};

fn graph() -> SqliteGraph {
    SqliteGraph::open_in_memory().expect("graph")
}

#[test]
fn test_bulk_insert_vs_single_insert_equivalence() {
    let graph = graph();
    let expected = graph
        .insert_entity(&sqlitegraph::graph::GraphEntity {
            id: 0,
            kind: "Fn".into(),
            name: "single".into(),
            file_path: None,
            data: json!({}),
        })
        .unwrap();
    let ids = bulk_insert_entities(
        &graph,
        &[GraphEntityCreate {
            kind: "Fn".into(),
            name: "single".into(),
            file_path: None,
            data: json!({}),
        }],
    )
    .expect("bulk");
    assert_eq!(ids.len(), 1);
    let manual = graph.get_entity(ids[0]).unwrap();
    assert_eq!(manual.kind, "Fn");
    assert!(manual.id > expected);
}

#[test]
fn test_batch_outgoing_matches_scalar_fetch() {
    let graph = graph();
    let a = graph
        .insert_entity(&sqlitegraph::graph::GraphEntity {
            id: 0,
            kind: "Fn".into(),
            name: "a".into(),
            file_path: None,
            data: json!({}),
        })
        .unwrap();
    let b = graph
        .insert_entity(&sqlitegraph::graph::GraphEntity {
            id: 0,
            kind: "Fn".into(),
            name: "b".into(),
            file_path: None,
            data: json!({}),
        })
        .unwrap();
    graph
        .insert_edge(&sqlitegraph::graph::GraphEdge {
            id: 0,
            from_id: a,
            to_id: b,
            edge_type: "CALLS".into(),
            data: json!({}),
        })
        .unwrap();
    let batch = adjacency_fetch_outgoing_batch(&graph, &[a]).expect("batch");
    assert_eq!(batch[0].1, vec![b]);
}

#[test]
fn test_batch_order_is_deterministic() {
    let graph = graph();
    let ids = (0..3)
        .map(|i| {
            graph
                .insert_entity(&sqlitegraph::graph::GraphEntity {
                    id: 0,
                    kind: "Fn".into(),
                    name: format!("n{i}"),
                    file_path: None,
                    data: json!({}),
                })
                .unwrap()
        })
        .collect::<Vec<_>>();
    let first = adjacency_fetch_outgoing_batch(&graph, &ids).unwrap();
    let second = adjacency_fetch_outgoing_batch(&graph, &ids).unwrap();
    assert_eq!(first, second);
}

#[test]
fn test_cache_stats_changes_on_hits_and_misses() {
    let graph = graph();
    let ids = (0..2)
        .map(|i| {
            graph
                .insert_entity(&sqlitegraph::graph::GraphEntity {
                    id: 0,
                    kind: "Fn".into(),
                    name: format!("n{i}"),
                    file_path: None,
                    data: json!({}),
                })
                .unwrap()
        })
        .collect::<Vec<_>>();
    let start = cache_stats(&graph);
    let query = graph.query();
    query.neighbors(ids[0]).unwrap();
    query.neighbors(ids[0]).unwrap();
    let after = cache_stats(&graph);
    assert!(after.misses > start.misses);
    assert!(after.hits > start.hits);
    cache_clear_ranges(&graph, &ids);
    let cleared = cache_stats(&graph);
    assert_eq!(cleared.entries, 0);
}

#[test]
fn test_bulk_insert_edges_skips_duplicates() {
    let graph = graph();
    let from = graph
        .insert_entity(&sqlitegraph::graph::GraphEntity {
            id: 0,
            kind: "Fn".into(),
            name: "from".into(),
            file_path: None,
            data: json!({}),
        })
        .unwrap();
    let to = graph
        .insert_entity(&sqlitegraph::graph::GraphEntity {
            id: 0,
            kind: "Fn".into(),
            name: "to".into(),
            file_path: None,
            data: json!({}),
        })
        .unwrap();
    let ids = bulk_insert_edges(
        &graph,
        &[
            GraphEdgeCreate {
                from_id: from,
                to_id: to,
                edge_type: "CALLS".into(),
                data: json!({}),
            },
            GraphEdgeCreate {
                from_id: from,
                to_id: to,
                edge_type: "CALLS".into(),
                data: json!({}),
            },
        ],
    )
    .expect("bulk edges");
    assert_eq!(ids.len(), 1);
    let neighbors = graph.query().neighbors(from).unwrap();
    assert_eq!(neighbors, vec![to]);
}