Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Embedding in Rust

cargo add marsdb
#![allow(unused)]
fn main() {
let db = marsdb::Database::in_memory()?; // or Database::open("path/to.db")
db.execute("CREATE (a:Person {name: 'Alice'})")?;
let result = db.execute("MATCH (n:Person) RETURN n.name")?;

// Bound work from untrusted callers. A CancellationToken can also be
// cloned and cancelled from another thread.
let options = marsdb::ExecutionOptions {
    max_intermediate_rows: Some(100_000),
    max_result_rows: Some(10_000),
    max_relationship_expansions: Some(1_000_000),
    timeout: Some(std::time::Duration::from_secs(5)),
    ..Default::default()
};
let result = db.execute_with_options("MATCH (n) RETURN n", &options)?;

// Group statements into one atomic unit. Reads through `tx` see its earlier
// writes; any statement error aborts and closes the whole transaction.
let mut tx = db.begin_transaction()?;
tx.execute("CREATE (:Person {name: 'Bob'})")?;
tx.execute("CREATE (:Person {name: 'Carol'})")?;
tx.commit()?;

// Or run a `;`-separated batch, one transaction per statement, one
// QueryResult per statement back:
let results = db.execute_batch("CREATE (a:Person {name: 'Alice'}); CREATE (b:Person {name: 'Bob'})")?;
}

ExecutionOptions::observer accepts an ExecutionObserver callback for dependency-free telemetry. Events contain duration, outcome category, read/write classification, result-row count, and relationship expansions; they deliberately exclude query text and error messages. Syntax and missing-parameter rejections are reported too, and observer panics are contained.

Backup, integrity checks, and other operational concerns that apply regardless of which language you’re calling from are covered in Operations.

Stored procedures (CALL)

MarsDB ships no built-in procedures — CALL proc(args) [YIELD ...] resolves against a marsdb_query::ProcedureProvider you supply via ExecutionOptions::procedures:

#![allow(unused)]
fn main() {
use std::sync::Arc;
use marsdb::{ExecutionOptions, Procedures, ProcedureProvider, ProcedureSignature, Value};
// `ProcedureProvider::call`'s error type -- not re-exported by `marsdb`
// itself, so implementing the trait needs a direct `marsdb-query`
// dependency too (`cargo add marsdb-query`).
use marsdb_query::QueryError;

struct MyProcedures;

impl ProcedureProvider for MyProcedures {
    fn signature(&self, name: &str) -> Option<ProcedureSignature> {
        // Look up `name`'s declared inputs/outputs, or `None` if unknown.
        None
    }
    fn call(&self, name: &str, args: &[Value]) -> Result<Vec<Vec<Value>>, QueryError> {
        // Run the procedure, return its output rows.
        Ok(vec![])
    }
}

let options = ExecutionOptions {
    procedures: Some(Procedures(Arc::new(MyProcedures))),
    ..Default::default()
};
}

More examples

cargo run -p marsdb --example task_tracker    # CRUD + aggregation
cargo run -p marsdb --example social_graph    # variable-length traversal, MATCH...CREATE
cargo run -p marsdb --example params_and_batch # $parameters, execute_batch

Full source in marsdb/examples/. Each also writes an SVG chart of its query result (via plotters) to the current directory.

Natural language → Cypher

marsdb-nl2cypher translates an English question into Cypher against a database’s actual schema (labels/relationship-types/properties in use, introspected automatically), validates its syntax and variable/type binding, and retries once with the exact validation error fed back if the first attempt is invalid. No HTTP/LLM-SDK dependency in the core crate — bring your own LlmClient:

#![allow(unused)]
fn main() {
use marsdb::Database;
use marsdb_nl2cypher::{translate_and_run, LlmClient};

let db = Database::in_memory()?;
db.execute("CREATE (:Person {name: 'Alice'})-[:KNOWS]->(:Person {name: 'Bob'})")?;

let (cypher, result) = translate_and_run(&db, &my_llm_client, "who does Alice know?")?;
}

translate_and_run enforces read-only generated Cypher. Model-generated writes are rejected before execution unless the caller explicitly uses translate_and_run_with_policy(..., ExecutionPolicy::AllowWrites) after performing its own authentication and authorization.

MarsDB’s narrower Cypher subset (vs. full Neo4j Cypher) is a deliberate fit for this — a smaller grammar means fewer ways an LLM can generate something unparseable. The prompt tells the model what’s supported and what to avoid (no bare --> shorthand, MERGE capped at one hop, etc.) — see marsdb-nl2cypher/src/lib.rs’s CAPABILITIES constant.

A runnable example against a local Ollama instance:

ollama serve &
ollama pull llama3.2
cargo run -p marsdb-nl2cypher --example ollama_demo