Skip to main content

NodeDb

Trait NodeDb 

Source
pub trait NodeDb: Send + Sync {
Show 20 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 { ... } fn undrop_collection<'life0, 'life1, 'async_trait>( &'life0 self, name: &'life1 str, ) -> Pin<Box<dyn Future<Output = NodeDbResult<()>> + Send + 'async_trait>> where Self: 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait { ... } fn drop_collection_purge<'life0, 'life1, 'async_trait>( &'life0 self, name: &'life1 str, ) -> Pin<Box<dyn Future<Output = NodeDbResult<()>> + Send + 'async_trait>> where Self: 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait { ... } fn list_dropped_collections<'life0, 'async_trait>( &'life0 self, ) -> Pin<Box<dyn Future<Output = NodeDbResult<Vec<DroppedCollection>>> + Send + 'async_trait>> where Self: 'async_trait, 'life0: 'async_trait { ... } fn on_collection_purged<'life0, 'async_trait>( &'life0 self, _handler: CollectionPurgedHandler, ) -> Pin<Box<dyn Future<Output = NodeDbResult<()>> + Send + 'async_trait>> where Self: 'async_trait, 'life0: '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.

Source

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

Restore a soft-deleted collection within its retention window.

Equivalent to UNDROP COLLECTION <name>. Fails with 42P01 if the retention window has elapsed and the row is gone, or with 42501 if the caller is neither preserved owner nor admin.

Default impl routes through execute_sql so any implementation that can execute SQL inherits the correct behavior for free.

Source

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

Hard-delete a collection, skipping soft-delete and retention.

Equivalent to DROP COLLECTION <name> PURGE. Admin-only on the server; the server rejects non-admin callers with 42501. Bypasses the retention safety net — data is unrecoverable.

Source

fn list_dropped_collections<'life0, 'async_trait>( &'life0 self, ) -> Pin<Box<dyn Future<Output = NodeDbResult<Vec<DroppedCollection>>> + Send + 'async_trait>>
where Self: 'async_trait, 'life0: 'async_trait,

List every soft-deleted collection in the current tenant that is still within its retention window.

Equivalent to SELECT tenant_id, name, owner, deactivated_at_ns, retention_expires_at_ns FROM _system.dropped_collections. Returns Vec<DroppedCollection> — empty if no soft-deleted rows exist for the caller’s tenant.

Source

fn on_collection_purged<'life0, 'async_trait>( &'life0 self, _handler: CollectionPurgedHandler, ) -> Pin<Box<dyn Future<Output = NodeDbResult<()>> + Send + 'async_trait>>
where Self: 'async_trait, 'life0: 'async_trait,

Register a handler fired when a collection the caller has synced is purged on Origin and the local copy is removed.

Default impl returns NodeDbError::storage with a "not supported" detail — implementations that maintain a sync client (Lite, any future push-capable remote client) override with registration into their internal handler list. Stateless clients (pgwire-only NodeDbRemote) have nothing to push, so the default rejection is the correct behavior.

Implementors§