sqlitegraph 2.2.2

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::backend::{BackendDirection, ChainStep};
use sqlitegraph::{GraphEdge, GraphEntity, SqliteGraph, multi_hop};

fn insert_node(graph: &SqliteGraph, name: &str) -> i64 {
    graph
        .insert_entity(&GraphEntity {
            id: 0,
            kind: "Item".into(),
            name: name.into(),
            file_path: None,
            data: json!({"name": name}),
        })
        .expect("insert node")
}

fn insert_edge(graph: &SqliteGraph, from: i64, to: i64, edge_type: &str) {
    graph
        .insert_edge(&GraphEdge {
            id: 0,
            from_id: from,
            to_id: to,
            edge_type: edge_type.into(),
            data: json!({}),
        })
        .expect("insert edge");
}

fn build_sample_graph() -> (SqliteGraph, Vec<i64>) {
    let graph = SqliteGraph::open_in_memory().expect("graph");
    let a = insert_node(&graph, "A");
    let b = insert_node(&graph, "B");
    let c = insert_node(&graph, "C");
    let d = insert_node(&graph, "D");
    let e = insert_node(&graph, "E");

    insert_edge(&graph, a, b, "CALLS");
    insert_edge(&graph, b, c, "CALLS");
    insert_edge(&graph, c, d, "USES");
    insert_edge(&graph, a, e, "USES");
    insert_edge(&graph, e, d, "CALLS");

    (graph, vec![a, b, c, d, e])
}

#[test]
fn test_k_hop_outgoing_depth_two() {
    let (graph, ids) = build_sample_graph();
    let hops = graph.query().k_hop_outgoing(ids[0], 2).expect("k-hop");
    assert_eq!(hops, vec![ids[1], ids[4], ids[2], ids[3]]);
}

#[test]
fn test_k_hop_filtered_by_type() {
    let (graph, ids) = build_sample_graph();
    let hops = graph
        .query()
        .k_hop_filtered(ids[0], 3, BackendDirection::Outgoing, &["CALLS"])
        .expect("k-hop filtered");
    assert_eq!(hops, vec![ids[1], ids[2]]);
}

#[test]
fn test_chain_query_with_direction_and_type() {
    let (graph, ids) = build_sample_graph();
    let query = graph.query();
    let chain = [
        ChainStep {
            direction: BackendDirection::Outgoing,
            edge_type: Some("CALLS".into()),
        },
        ChainStep {
            direction: BackendDirection::Outgoing,
            edge_type: Some("CALLS".into()),
        },
        ChainStep {
            direction: BackendDirection::Outgoing,
            edge_type: Some("USES".into()),
        },
    ];
    let matches = query.chain(ids[0], &chain).expect("chain query");
    assert_eq!(matches, vec![ids[3]]);
}

#[test]
fn multi_source_khop_matches_union_of_single_runs() {
    let (graph, ids) = build_sample_graph();
    let seeds = vec![ids[0], ids[1]];
    let mut expected = Vec::new();
    for &seed in &seeds {
        let hops = graph.query().k_hop_outgoing(seed, 2).expect("single hop");
        expected.extend(hops);
    }
    expected.sort();
    expected.dedup();
    expected.retain(|id| !seeds.contains(id));
    expected.sort();
    let mut combined =
        multi_hop::k_hop_multi(&graph, &seeds, 2, BackendDirection::Outgoing).expect("multi hop");
    combined.sort();
    assert_eq!(combined, expected);
}