Skip to content

Rust Extensions

Extend Grafeo with custom Rust code for maximum performance.

Why Rust Extensions?

  • Performance - Native speed for computationally intensive operations
  • Type Safety - Compile-time guarantees
  • Direct Access - Access to internal Grafeo APIs
  • Integration - Use any Rust crate

Creating an Extension Crate

Cargo.toml

[package]
name = "my-grafeo-extension"
version = "0.1.0"
edition = "2024"

[dependencies]
grafeo-core = "0.6"
grafeo-engine = "0.6"

Extension Code

use grafeo_common::utils::error::Result;
use grafeo_engine::GrafeoDB;

/// Custom graph analysis function
pub fn analyze_connectivity(db: &GrafeoDB) -> Result<ConnectivityReport> {
    // A snapshot of the graph: its store and the view its reads take, one
    // pinned state for every read. Every read can fail (an external store's
    // own error), so it returns a Result.
    let snapshot = db.snapshot()?;
    let (store, view) = (snapshot.store(), snapshot.view());

    let node_count = store.node_count(view)?;
    let edge_count = store.edge_count(view)?;
    let avg_degree = (edge_count as f64 * 2.0) / node_count as f64;

    Ok(ConnectivityReport {
        nodes: node_count,
        edges: edge_count,
        average_degree: avg_degree,
    })
}

pub struct ConnectivityReport {
    pub nodes: usize,
    pub edges: usize,
    pub average_degree: f64,
}

Accessing Internal APIs

Graph Store

use grafeo_common::types::ReadView;
use grafeo_common::utils::error::Result;
use grafeo_core::graph::{Direction, GraphStore};

/// Every read names who reads (`view`): the nodes and edges that reader sees.
fn process_graph(store: &dyn GraphStore, view: ReadView) -> Result<()> {
    for id in store.node_ids(view)? {
        let Some(node) = store.node(id, view)? else { continue };
        println!("Node: {:?}", node.id);
        for label in &node.labels {
            println!("  Label: {label}");
        }
        // Its outgoing edges of any type (`&[]`): those the view sees, whose
        // other end it sees
        for (target, edge) in store.edges(id, Direction::Outgoing, &[], view)? {
            if let Some(edge_type) = store.edge_type(edge, view)? {
                println!("Edge: {:?} -[{edge_type}]-> {:?}", node.id, target);
            }
        }
    }
    Ok(())
}

Index Access

use grafeo_core::index::HashIndex;

fn query_index(index: &HashIndex<String, NodeId>) {
    // Point lookup
    if let Some(node_ids) = index.get("Alix") {
        for node_id in node_ids {
            println!("Found: {:?}", node_id);
        }
    }
}

Execution Engine

use grafeo_core::execution::{DataChunk, Pipeline};

fn custom_operator(chunk: DataChunk) -> DataChunk {
    // Process data chunk
    chunk.filter(|row| row.get("age").as_int() > 30)
}

Building and Using

Build

cargo build --release

Use in Application

use my_grafeo_extension::analyze_connectivity;

let db = GrafeoDB::open("my_graph.db")?;
let report = analyze_connectivity(&db);

println!("Nodes: {}", report.nodes);
println!("Edges: {}", report.edges);
println!("Avg Degree: {:.2}", report.average_degree);

Exposing to Python

Use PyO3 to expose Rust extensions to Python:

use pyo3::prelude::*;

#[pyfunction]
fn analyze_connectivity_py(db: &PyGrafeoDB) -> PyResult<PyDict> {
    let report = analyze_connectivity(db.inner());

    let dict = PyDict::new(py);
    dict.set_item("nodes", report.nodes)?;
    dict.set_item("edges", report.edges)?;
    dict.set_item("average_degree", report.average_degree)?;

    Ok(dict)
}

#[pymodule]
fn my_extension(_py: Python, m: &PyModule) -> PyResult<()> {
    m.add_function(wrap_pyfunction!(analyze_connectivity_py, m)?)?;
    Ok(())
}