velr 0.1.68

Velr embedded property-graph database (Rust driver, beta)
Documentation

Velr

Velr is an embedded property-graph database from Velr.ai, written in Rust, built on top of SQLite3 (persisting to a standard SQLite database file) and queried using the openCypher language.

Vector data and time-series support are actively in development and will ship after openCypher support has stabilized.

This crate provides the Rust binding for Velr. It links against a bundled native runtime with a C ABI, implemented in Rust.

Questions, feedback, or commercial licensing: tomas@velr.ai


Release status

This release is alpha.

  • The API and query support are still evolving.
  • openCypher coverage is already substantial, but some features are still missing.

If you hit a missing feature (see below), please reach out — it helps us prioritize.

Velr is already usable for real workflows and representative use cases, but rough edges remain and the API is not yet stable.


Installation

Add to Cargo.toml:

[dependencies]
velr = "0.1"

Enable Arrow IPC support (binding Arrow arrays + exporting result tables as Arrow IPC):

[dependencies]
velr = { version = "0.1", features = ["arrow-ipc"] }

Quick start

use velr::{Velr, CellRef};

fn main() -> velr::Result<()> {
    // Open in-memory DB (pass Some("path.db") for file-backed)
    let db = Velr::open(None)?;

    db.run("CREATE (:Person {name:'Keanu Reeves', born:1964})")?;

    let mut t = db.exec_one("MATCH (p:Person) RETURN p.name AS name, p.born AS born")?;

    println!("{:?}", t.column_names());

    t.for_each_row(|row| {
        match row[0] {
            CellRef::Text(bytes) => println!("name={}", std::str::from_utf8(bytes).unwrap()),
            _ => {}
        }
        match row[1] {
            CellRef::Integer(i) => println!("born={i}"),
            _ => {}
        }
        Ok(())
    })?;

    Ok(())
}

Query language support

Velr supports most of openCypher, but some features are not yet implemented.

Notable missing features:

  • REMOVE clause
  • Query parameters (for example $name)
  • The query planner does not yet use indexes in all cases where expected.

Streaming multiple result tables

A single exec() can yield multiple result tables (e.g. multiple statements):

let db = Velr::open(None)?;
let mut stream = db.exec(
    "MATCH (m:Movie {title:'The Matrix'}) RETURN m.title AS title;
     MATCH (m:Movie {title:'Inception'})  RETURN m.released AS year"
)?;

while let Some(mut table) = stream.next_table()? {
    println!("{:?}", table.column_names());
    table.for_each_row(|row| {
        println!("{row:?}");
        Ok(())
    })?;
}

Transactions and savepoints

Velr supports transactions together with two kinds of savepoints:

  • Scoped savepoints via savepoint(), which return a guard
  • Named savepoints via savepoint_named(name), which remain active in the transaction until released or the transaction ends

Calling rollback_to(name) rolls back to the named savepoint, discards any newer named savepoints, and keeps the target savepoint active.

Scoped savepoint

let db = Velr::open(None)?;

let tx = db.begin_tx()?;
tx.run("CREATE (:Temp {k:'outer'})")?;

{
    let sp = tx.savepoint()?;
    tx.run("CREATE (:Temp {k:'inner'})")?;
    sp.rollback()?; // rollback to the scoped savepoint
}

tx.commit()?;

Named savepoints

let db = Velr::open(None)?;

let tx = db.begin_tx()?;

tx.savepoint_named("before_write1")?;
tx.run("CREATE (:Temp {k:'a'})")?;

tx.savepoint_named("before_write2")?;
tx.run("CREATE (:Temp {k:'b'})")?;

tx.rollback_to("before_write1")?;
tx.run("CREATE (:Temp {k:'c'})")?;

tx.release_savepoint("before_write1")?;
tx.commit()?;

release_savepoint(name) currently releases the most recent active named savepoint.

Dropping an active transaction without commit() will roll it back automatically.


Arrow IPC (optional)

With features = ["arrow-ipc"] you can:

  • Bind Arrow arrays as a logical table (bind_arrow, bind_arrow_chunks)
  • Export a result table as an Arrow IPC file (to_arrow_ipc_file())
#[cfg(feature = "arrow-ipc")]
fn arrow_example() -> velr::Result<()> {
    use arrow2::array::{Array, Utf8Array};

    let db = Velr::open(None)?;

    let cols = vec!["name".to_string()];
    let arrays: Vec<Box<dyn Array>> = vec![
        Utf8Array::<i64>::from(vec![Some("Alice"), Some("Bob")]).boxed(),
    ];

    db.bind_arrow("_people", cols, arrays)?;
    db.run("UNWIND BIND('_people') AS r CREATE (:Person {name:r.name})")?;

    let mut t = db.exec_one("MATCH (p:Person) RETURN p.name AS name ORDER BY name")?;
    let ipc = t.to_arrow_ipc_file()?;

    println!("IPC bytes: {}", ipc.len());
    Ok(())
}

Supported functions

Velr currently supports these openCypher functions:

Scalars

  • id()
  • type()
  • length()
  • nodes()
  • relationships()
  • coalesce()
  • labels()
  • properties()
  • keys()

Aggregates

  • count()
  • sum()
  • avg()
  • min()
  • max()
  • collect()

Platform support

This crate links against a bundled native runtime.

Currently bundled targets:

* macOS universal (arm64 + x86_64)
* Linux x86_64
* Linux aarch64
* Windows x86_64

Licensing

  • The Rust binding source code in this package is licensed under MIT.
  • The bundled native runtime binaries may be used and freely redistributed in unmodified form under the terms of LICENSE.runtime.

See LICENSE and LICENSE.runtime for the full license texts.