Skip to main content

Grit

Struct Grit 

Source
pub struct Grit { /* private fields */ }
Expand description

Handle to one grit database: a writer actor plus a read connection.

All methods take &self; Grit is Send + Sync. Writes are serialized through the single writer thread (SQLite has one writer — embrace it); reads run on a separate WAL connection and never block behind writes.

Implementations§

Source§

impl Grit

Source

pub fn export_jsonl<W: Write>(&self, out: W) -> Result<()>

Stream the entire database (graph tables + oplog + embedding metadata, not the recomputable vectors) as JSONL. The inverse of import_jsonl.

§Example
let mut out = Vec::new();
g.export_jsonl(&mut out)?;
Source§

impl Grit

Source

pub fn stats(&self) -> Result<Stats>

Row counts across the whole file, from one consistent snapshot.

Source

pub fn node(&self, id: Uuid) -> Result<Option<Node>>

Fetch one node by id (regardless of validity — point lookups see everything; filtering is for traversal/search).

Source

pub fn edge(&self, id: Uuid) -> Result<Option<Edge>>

Fetch one edge by id.

Source

pub fn episode(&self, id: Uuid) -> Result<Option<Episode>>

Fetch one episode by id.

Source

pub fn nodes_in_group(&self, group_id: &str) -> Result<Vec<Node>>

Every node in a group, id-ordered (deterministic). Includes expired and merged-away rows — callers filter on Node::expired_at / Node::merged_into when they want only live entities.

§Example
let live: Vec<_> = g
    .nodes_in_group("algebra")?
    .into_iter()
    .filter(|n| n.expired_at.is_none())
    .collect();
Source

pub fn edges_in_group(&self, group_id: &str) -> Result<Vec<Edge>>

Every edge in a group, id-ordered (deterministic). Includes invalidated and expired rows — the full bi-temporal record, same view as Grit::export_jsonl.

§Example
let facts = g.edges_in_group("algebra")?.len();
Source

pub fn episodes_in_group(&self, group_id: &str) -> Result<Vec<Episode>>

Every episode in a group, ordered by event time then id (deterministic, chronological — the natural order for building previous-episode context windows).

§Example
let history = g.episodes_in_group("chat")?;
let latest = history.last();
Source

pub fn get_node_embedding(&self, id: Uuid) -> Result<Option<Vec<f32>>>

The embedding stored for a node via Grit::set_node_embedding, or None when the node has no vector (or no model is registered).

§Example
if g.get_node_embedding(id)?.is_none() {
    // embed and g.set_node_embedding(id, vector)?
}
Source

pub fn get_edge_embedding(&self, id: Uuid) -> Result<Option<Vec<f32>>>

The embedding stored for an edge via Grit::set_edge_embedding, or None when the edge has no vector (or no model is registered).

Source

pub fn mentions_of(&self, target: Uuid) -> Result<Vec<Uuid>>

Episode ids that mention the given node or edge (provenance lookup). Mention rows keep their original target ids, so this resolves target to its canonical node and gathers mentions across all merged aliases.

Source

pub fn traverse(&self, from: &[Uuid], spec: &Traversal) -> Result<Subgraph>

Expand from from along edges valid at the requested instant, up to spec.depth hops, and return the induced subgraph. Time-travel is the public API here (Design Invariant 4): pass as_at for “what did I believe at t?”, as_of for “what was true at t?”.

§Example
let believed_in_march = g.traverse(&[node_id], &Traversal::default().as_at(march))?;
Source

pub fn node_history(&self, id: Uuid) -> Result<NodeHistory>

Full bi-temporal audit for a node: the node row plus every incident edge ever believed, invalidated and expired included — one snapshot.

Source

pub fn find_merge_candidates( &self, id: Uuid, min_score: f64, ) -> Result<Vec<MergeCandidate>>

Score possible duplicates of id within its group: Jaro-Winkler on names, cosine over embeddings when present. Returns candidates with score >= min_score, best first. Grit never acts on these — executing a crate::GraphOp::MergeNodes is Layer 2’s decision.

Source§

impl Grit

Source

pub fn search(&self, query: Query) -> Result<Vec<SearchHit>>

Run hybrid retrieval: BM25 + vector cosine + graph expansion, RRF-fused, validity-filtered at the query’s as_of/as_at, trimmed to budget.

Source§

impl Grit

Source

pub fn open(path: impl AsRef<Path>, opts: Options) -> Result<Self>

Open (creating or migrating as needed) the database at path.

The file plus its WAL sidecars is the entire truth (Design Invariant 2). In-memory databases are not supported: grit uses two connections (writer + reader) that must see the same file.

§Example
use grit_core::{Grit, Options};
let g = Grit::open("memory.db", Options::new("laptop"))?;
Source

pub fn new_id(&self) -> Uuid

Mint a UUIDv7 from the injected clock. Use for the id fields of GraphOps.

Source

pub fn apply(&self, op: GraphOp) -> Result<OplogEntry>

Apply a local write: mint an OplogEntry (op id + HLC), append it to the oplog and update the graph tables in one transaction. Returns the entry — the unit a sync mechanism would ship to other devices.

§Example
let entry = g.apply(GraphOp::AddNode {
    id: g.new_id(),
    kind: "person".into(),
    name: "Ada Lovelace".into(),
    summary: String::new(),
    attrs: serde_json::json!({}),
    group_id: String::new(),
})?;
assert_eq!(entry.device_id, "laptop");
Source

pub fn apply_remote(&self, entry: OplogEntry) -> Result<bool>

Apply an op that originated on another device (future sync ingest, and the test seam for the oplog merge laws). Idempotent: returns false if this op id was already applied. Folds the remote HLC into the local generator so subsequent local ops sort after everything observed.

Unlike Grit::apply, structurally invalid ops (e.g. a self-merge) are recorded and applied as no-ops rather than erroring — one malformed entry from a buggy device must not wedge a sync loop. The one exception is a negative HLC wall time, which is rejected: it cannot be ordered, so nothing downstream of it can converge.

Source

pub fn oplog_since(&self, after_seq: i64) -> Result<Vec<(i64, OplogEntry)>>

Read the oplog from local sequence after_seq (exclusive; 0 = start). Returns (seq, entry) pairs in local apply order.

Source

pub fn register_embedding_model( &self, model_id: impl Into<String>, dim: usize, model_version: impl Into<String>, ) -> Result<()>

Register the embedding model whose vectors this file stores. Creates the vec_nodes/vec_edges tables sized to dim if they are missing (also after an import — vectors are never exported, the tables are rebuilt empty and rows re-embed). Vectors are partitioned by their row’s group_id, so vector search filters by group inside the KNN scan. Embeddings are recomputable local state — not part of the oplog (Design Invariant 5).

Source

pub fn set_node_embedding(&self, node_id: Uuid, vector: Vec<f32>) -> Result<()>

Store (or replace) the embedding for a node. The caller computed it; grit only stores it, in the node’s group partition. The node row must already exist (Error::NotFound otherwise) — apply the GraphOp::AddNode first, then embed. Embedding a purged id is a silent no-op (right to forget).

Source

pub fn set_edge_embedding(&self, edge_id: Uuid, vector: Vec<f32>) -> Result<()>

Store (or replace) the embedding for an edge (its fact sentence), in the edge’s group partition. The edge row must already exist (Error::NotFound otherwise); embedding a purged id is a no-op.

Trait Implementations§

Source§

impl Drop for Grit

Source§

fn drop(&mut self)

Executes the destructor for this type. Read more
Source§

fn pin_drop(self: Pin<&mut Self>)

🔬This is a nightly-only experimental API. (pin_ergonomics)
Execute the destructor for this type, but different to Drop::drop, it requires self to be pinned. Read more

Auto Trait Implementations§

§

impl !Freeze for Grit

§

impl !RefUnwindSafe for Grit

§

impl !UnwindSafe for Grit

§

impl Send for Grit

§

impl Sync for Grit

§

impl Unpin for Grit

§

impl UnsafeUnpin for Grit

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.