Skip to main content

NodeDb

Trait NodeDb 

Source
pub trait NodeDb: Send + Sync {
Show 16 methods // Required methods fn vector_search<'life0, 'life1, 'life2, 'life3, 'async_trait>( &'life0 self, collection: &'life1 str, query: &'life2 [f32], k: usize, filter: Option<&'life3 MetadataFilter>, ) -> Pin<Box<dyn Future<Output = NodeDbResult<Vec<SearchResult>>> + Send + 'async_trait>> where Self: 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait, 'life2: 'async_trait, 'life3: 'async_trait; fn vector_insert<'life0, 'life1, 'life2, 'life3, 'async_trait>( &'life0 self, collection: &'life1 str, id: &'life2 str, embedding: &'life3 [f32], metadata: Option<Document>, ) -> Pin<Box<dyn Future<Output = NodeDbResult<()>> + Send + 'async_trait>> where Self: 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait, 'life2: 'async_trait, 'life3: 'async_trait; fn vector_delete<'life0, 'life1, 'life2, 'async_trait>( &'life0 self, collection: &'life1 str, id: &'life2 str, ) -> Pin<Box<dyn Future<Output = NodeDbResult<()>> + Send + 'async_trait>> where Self: 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait, 'life2: 'async_trait; fn graph_traverse<'life0, 'life1, 'life2, 'async_trait>( &'life0 self, start: &'life1 NodeId, depth: u8, edge_filter: Option<&'life2 EdgeFilter>, ) -> Pin<Box<dyn Future<Output = NodeDbResult<SubGraph>> + Send + 'async_trait>> where Self: 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait, 'life2: 'async_trait; fn graph_insert_edge<'life0, 'life1, 'life2, 'life3, 'async_trait>( &'life0 self, from: &'life1 NodeId, to: &'life2 NodeId, edge_type: &'life3 str, properties: Option<Document>, ) -> Pin<Box<dyn Future<Output = NodeDbResult<EdgeId>> + Send + 'async_trait>> where Self: 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait, 'life2: 'async_trait, 'life3: 'async_trait; fn graph_delete_edge<'life0, 'life1, 'async_trait>( &'life0 self, edge_id: &'life1 EdgeId, ) -> Pin<Box<dyn Future<Output = NodeDbResult<()>> + Send + 'async_trait>> where Self: 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait; fn document_get<'life0, 'life1, 'life2, 'async_trait>( &'life0 self, collection: &'life1 str, id: &'life2 str, ) -> Pin<Box<dyn Future<Output = NodeDbResult<Option<Document>>> + Send + 'async_trait>> where Self: 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait, 'life2: 'async_trait; fn document_put<'life0, 'life1, 'async_trait>( &'life0 self, collection: &'life1 str, doc: Document, ) -> Pin<Box<dyn Future<Output = NodeDbResult<()>> + Send + 'async_trait>> where Self: 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait; fn document_delete<'life0, 'life1, 'life2, 'async_trait>( &'life0 self, collection: &'life1 str, id: &'life2 str, ) -> Pin<Box<dyn Future<Output = NodeDbResult<()>> + Send + 'async_trait>> where Self: 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait, 'life2: 'async_trait; fn execute_sql<'life0, 'life1, 'life2, 'async_trait>( &'life0 self, query: &'life1 str, params: &'life2 [Value], ) -> Pin<Box<dyn Future<Output = NodeDbResult<QueryResult>> + Send + 'async_trait>> where Self: 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait, 'life2: 'async_trait; // Provided methods fn vector_insert_field<'life0, 'life1, 'life2, 'life3, 'life4, 'async_trait>( &'life0 self, collection: &'life1 str, field_name: &'life2 str, id: &'life3 str, embedding: &'life4 [f32], metadata: Option<Document>, ) -> Pin<Box<dyn Future<Output = NodeDbResult<()>> + Send + 'async_trait>> where Self: 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait, 'life2: 'async_trait, 'life3: 'async_trait, 'life4: 'async_trait { ... } fn vector_search_field<'life0, 'life1, 'life2, 'life3, 'life4, 'async_trait>( &'life0 self, collection: &'life1 str, field_name: &'life2 str, query: &'life3 [f32], k: usize, filter: Option<&'life4 MetadataFilter>, ) -> Pin<Box<dyn Future<Output = NodeDbResult<Vec<SearchResult>>> + Send + 'async_trait>> where Self: 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait, 'life2: 'async_trait, 'life3: 'async_trait, 'life4: 'async_trait { ... } fn graph_shortest_path<'life0, 'life1, 'life2, 'life3, 'async_trait>( &'life0 self, from: &'life1 NodeId, to: &'life2 NodeId, max_depth: u8, edge_filter: Option<&'life3 EdgeFilter>, ) -> Pin<Box<dyn Future<Output = NodeDbResult<Option<Vec<NodeId>>>> + Send + 'async_trait>> where Self: 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait, 'life2: 'async_trait, 'life3: 'async_trait { ... } fn text_search<'life0, 'life1, 'life2, 'async_trait>( &'life0 self, collection: &'life1 str, query: &'life2 str, top_k: usize, params: TextSearchParams, ) -> Pin<Box<dyn Future<Output = NodeDbResult<Vec<SearchResult>>> + Send + 'async_trait>> where Self: 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait, 'life2: 'async_trait { ... } fn batch_vector_insert<'life0, 'life1, 'life2, 'life3, 'life4, 'async_trait>( &'life0 self, collection: &'life1 str, vectors: &'life2 [(&'life3 str, &'life4 [f32])], ) -> Pin<Box<dyn Future<Output = NodeDbResult<()>> + Send + 'async_trait>> where Self: 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait, 'life2: 'async_trait, 'life3: 'async_trait, 'life4: 'async_trait { ... } fn batch_graph_insert_edges<'life0, 'life1, 'life2, 'life3, 'life4, 'async_trait>( &'life0 self, edges: &'life1 [(&'life2 str, &'life3 str, &'life4 str)], ) -> Pin<Box<dyn Future<Output = NodeDbResult<()>> + Send + 'async_trait>> where Self: 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait, 'life2: 'async_trait, 'life3: 'async_trait, 'life4: 'async_trait { ... }
}
Expand description

Unified database interface for NodeDB.

Two implementations:

  • NodeDbLite: executes queries against in-memory HNSW/CSR/Loro engines on the edge device. Writes produce CRDT deltas synced to Origin in background.
  • NodeDbRemote: translates trait calls into parameterized SQL and sends them over pgwire to the Origin cluster.

The developer writes agent logic once. Switching between local and cloud is a one-line configuration change.

Required Methods§

Search for the k nearest vectors to query in collection.

Returns results ordered by ascending distance. Optional metadata filter constrains which vectors are considered.

On Lite: direct in-memory HNSW search. Sub-millisecond. On Remote: translated to SELECT ... ORDER BY embedding <-> $1 LIMIT $2.

Source

fn vector_insert<'life0, 'life1, 'life2, 'life3, 'async_trait>( &'life0 self, collection: &'life1 str, id: &'life2 str, embedding: &'life3 [f32], metadata: Option<Document>, ) -> Pin<Box<dyn Future<Output = NodeDbResult<()>> + Send + 'async_trait>>
where Self: 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait, 'life2: 'async_trait, 'life3: 'async_trait,

Insert a vector with optional metadata into collection.

On Lite: inserts into in-memory HNSW + emits CRDT delta + persists to SQLite. On Remote: translated to INSERT INTO collection (id, embedding, metadata) VALUES (...).

Source

fn vector_delete<'life0, 'life1, 'life2, 'async_trait>( &'life0 self, collection: &'life1 str, id: &'life2 str, ) -> Pin<Box<dyn Future<Output = NodeDbResult<()>> + Send + 'async_trait>>
where Self: 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait, 'life2: 'async_trait,

Delete a vector by ID from collection.

On Lite: marks deleted in HNSW + emits CRDT tombstone. On Remote: DELETE FROM collection WHERE id = $1.

Source

fn graph_traverse<'life0, 'life1, 'life2, 'async_trait>( &'life0 self, start: &'life1 NodeId, depth: u8, edge_filter: Option<&'life2 EdgeFilter>, ) -> Pin<Box<dyn Future<Output = NodeDbResult<SubGraph>> + Send + 'async_trait>>
where Self: 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait, 'life2: 'async_trait,

Traverse the graph from start up to depth hops.

Returns the discovered subgraph (nodes + edges). Optional edge filter constrains which edges are followed during traversal.

On Lite: direct CSR pointer-chasing in contiguous memory. Microseconds. On Remote: SELECT * FROM graph_traverse($1, $2, $3).

Source

fn graph_insert_edge<'life0, 'life1, 'life2, 'life3, 'async_trait>( &'life0 self, from: &'life1 NodeId, to: &'life2 NodeId, edge_type: &'life3 str, properties: Option<Document>, ) -> Pin<Box<dyn Future<Output = NodeDbResult<EdgeId>> + Send + 'async_trait>>
where Self: 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait, 'life2: 'async_trait, 'life3: 'async_trait,

Insert a directed edge from from to to with the given label.

Returns the generated edge ID.

On Lite: appends to mutable adjacency buffer + CRDT delta + SQLite. On Remote: INSERT INTO edges (src, dst, label, properties) VALUES (...).

Source

fn graph_delete_edge<'life0, 'life1, 'async_trait>( &'life0 self, edge_id: &'life1 EdgeId, ) -> Pin<Box<dyn Future<Output = NodeDbResult<()>> + Send + 'async_trait>>
where Self: 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait,

Delete a graph edge by ID.

On Lite: marks deleted + CRDT tombstone. On Remote: DELETE FROM edges WHERE id = $1.

Source

fn document_get<'life0, 'life1, 'life2, 'async_trait>( &'life0 self, collection: &'life1 str, id: &'life2 str, ) -> Pin<Box<dyn Future<Output = NodeDbResult<Option<Document>>> + Send + 'async_trait>>
where Self: 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait, 'life2: 'async_trait,

Get a document by ID from collection.

On Lite: direct Loro state read. Sub-millisecond. On Remote: SELECT * FROM collection WHERE id = $1.

Source

fn document_put<'life0, 'life1, 'async_trait>( &'life0 self, collection: &'life1 str, doc: Document, ) -> Pin<Box<dyn Future<Output = NodeDbResult<()>> + Send + 'async_trait>>
where Self: 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait,

Put (insert or update) a document into collection.

The document’s id field determines the key. If a document with that ID already exists, it is overwritten (last-writer-wins locally; CRDT merge on sync).

On Lite: Loro apply + CRDT delta + SQLite persist. On Remote: INSERT ... ON CONFLICT (id) DO UPDATE SET ....

Source

fn document_delete<'life0, 'life1, 'life2, 'async_trait>( &'life0 self, collection: &'life1 str, id: &'life2 str, ) -> Pin<Box<dyn Future<Output = NodeDbResult<()>> + Send + 'async_trait>>
where Self: 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait, 'life2: 'async_trait,

Delete a document by ID from collection.

On Lite: Loro delete + CRDT tombstone. On Remote: DELETE FROM collection WHERE id = $1.

Source

fn execute_sql<'life0, 'life1, 'life2, 'async_trait>( &'life0 self, query: &'life1 str, params: &'life2 [Value], ) -> Pin<Box<dyn Future<Output = NodeDbResult<QueryResult>> + Send + 'async_trait>>
where Self: 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait, 'life2: 'async_trait,

Execute a raw SQL query with parameters.

On Lite: requires the sql feature flag (compiles in DataFusion parser). Returns NodeDbError::SqlNotEnabled if the feature is not compiled in. On Remote: pass-through to Origin via pgwire.

For most AI agent workloads, the typed methods above are sufficient and faster. Use this for BI tools, existing ORMs, or ad-hoc queries.

Provided Methods§

Source

fn vector_insert_field<'life0, 'life1, 'life2, 'life3, 'life4, 'async_trait>( &'life0 self, collection: &'life1 str, field_name: &'life2 str, id: &'life3 str, embedding: &'life4 [f32], metadata: Option<Document>, ) -> Pin<Box<dyn Future<Output = NodeDbResult<()>> + Send + 'async_trait>>
where Self: 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait, 'life2: 'async_trait, 'life3: 'async_trait, 'life4: 'async_trait,

Insert a vector into a named field within a collection.

Enables multiple embeddings per collection (e.g., “title_embedding”, “body_embedding”) with independent HNSW indexes. Default: delegates to vector_insert() ignoring field_name.

Source

fn vector_search_field<'life0, 'life1, 'life2, 'life3, 'life4, 'async_trait>( &'life0 self, collection: &'life1 str, field_name: &'life2 str, query: &'life3 [f32], k: usize, filter: Option<&'life4 MetadataFilter>, ) -> Pin<Box<dyn Future<Output = NodeDbResult<Vec<SearchResult>>> + Send + 'async_trait>>
where Self: 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait, 'life2: 'async_trait, 'life3: 'async_trait, 'life4: 'async_trait,

Search a named vector field.

Default: delegates to vector_search() ignoring field_name.

Source

fn graph_shortest_path<'life0, 'life1, 'life2, 'life3, 'async_trait>( &'life0 self, from: &'life1 NodeId, to: &'life2 NodeId, max_depth: u8, edge_filter: Option<&'life3 EdgeFilter>, ) -> Pin<Box<dyn Future<Output = NodeDbResult<Option<Vec<NodeId>>>> + Send + 'async_trait>>
where Self: 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait, 'life2: 'async_trait, 'life3: 'async_trait,

Find the shortest path between two nodes.

Returns the path as a list of node IDs, or None if no path exists within max_depth hops. Uses bidirectional BFS.

Full-text search with BM25 scoring.

Returns document IDs with relevance scores, ordered by descending score. Pass TextSearchParams::default() for standard OR-mode non-fuzzy search.

Source

fn batch_vector_insert<'life0, 'life1, 'life2, 'life3, 'life4, 'async_trait>( &'life0 self, collection: &'life1 str, vectors: &'life2 [(&'life3 str, &'life4 [f32])], ) -> Pin<Box<dyn Future<Output = NodeDbResult<()>> + Send + 'async_trait>>
where Self: 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait, 'life2: 'async_trait, 'life3: 'async_trait, 'life4: 'async_trait,

Batch insert vectors — amortizes CRDT delta export to O(1) per batch.

Source

fn batch_graph_insert_edges<'life0, 'life1, 'life2, 'life3, 'life4, 'async_trait>( &'life0 self, edges: &'life1 [(&'life2 str, &'life3 str, &'life4 str)], ) -> Pin<Box<dyn Future<Output = NodeDbResult<()>> + Send + 'async_trait>>
where Self: 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait, 'life2: 'async_trait, 'life3: 'async_trait, 'life4: 'async_trait,

Batch insert graph edges — amortizes CRDT delta export to O(1) per batch.

Implementors§