Skip to main content

nodedb_lite/nodedb/trait_impl/
dispatch.rs

1// SPDX-License-Identifier: Apache-2.0
2
3//! The single `impl NodeDb for NodeDbLite<S>` block.
4//!
5//! Each method delegates to a domain-specific inherent helper defined
6//! in a sibling module (`vector`, `graph`, `document`, `sql_lifecycle`).
7//! This keeps the trait surface in one place while the implementations
8//! stay split by concern.
9
10use std::collections::HashSet;
11
12use async_trait::async_trait;
13
14use nodedb_client::NodeDb;
15use nodedb_types::document::Document;
16use nodedb_types::dropped_collection::DroppedCollection;
17use nodedb_types::error::NodeDbResult;
18use nodedb_types::filter::{EdgeFilter, MetadataFilter};
19use nodedb_types::graph::{Direction, GraphStats};
20use nodedb_types::id::{EdgeId, NodeId};
21use nodedb_types::result::{QueryResult, SearchResult, SubGraph};
22use nodedb_types::text_search::TextSearchParams;
23use nodedb_types::value::Value;
24
25use crate::nodedb::NodeDbLite;
26use crate::storage::engine::StorageEngine;
27
28use super::vector::{INTERNAL_FIELDS_BASE, INTERNAL_FIELDS_NAMED, TraitVectorSearch};
29
30#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
31#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
32impl<S: StorageEngine> NodeDb for NodeDbLite<S> {
33    // ─── Vector Operations ───────────────────────────────────────────
34
35    async fn vector_search(
36        &self,
37        collection: &str,
38        query: &[f32],
39        k: usize,
40        filter: Option<&MetadataFilter>,
41        allowed_ids: Option<&HashSet<String>>,
42    ) -> NodeDbResult<Vec<SearchResult>> {
43        self.vector_search_internal(TraitVectorSearch {
44            index_key: collection,
45            collection,
46            query,
47            k,
48            filter,
49            exclude_fields: INTERNAL_FIELDS_BASE,
50            allowed_ids,
51        })
52        .await
53    }
54
55    async fn vector_insert(
56        &self,
57        collection: &str,
58        id: &str,
59        embedding: &[f32],
60        metadata: Option<Document>,
61    ) -> NodeDbResult<()> {
62        self.vector_insert_impl(collection, id, embedding, metadata)
63            .await
64    }
65
66    async fn vector_delete(&self, collection: &str, id: &str) -> NodeDbResult<()> {
67        self.vector_delete_impl(collection, id).await
68    }
69
70    async fn vector_insert_field(
71        &self,
72        collection: &str,
73        field_name: &str,
74        id: &str,
75        embedding: &[f32],
76        metadata: Option<Document>,
77    ) -> NodeDbResult<()> {
78        self.vector_insert_field_impl(collection, field_name, id, embedding, metadata)
79            .await
80    }
81
82    async fn vector_search_field(
83        &self,
84        collection: &str,
85        field_name: &str,
86        query: &[f32],
87        k: usize,
88        filter: Option<&MetadataFilter>,
89    ) -> NodeDbResult<Vec<SearchResult>> {
90        let index_key = if field_name.is_empty() {
91            collection.to_string()
92        } else {
93            format!("{collection}:{field_name}")
94        };
95        self.vector_search_internal(TraitVectorSearch {
96            index_key: &index_key,
97            collection,
98            query,
99            k,
100            filter,
101            exclude_fields: INTERNAL_FIELDS_NAMED,
102            allowed_ids: None,
103        })
104        .await
105    }
106
107    // ─── Graph Operations ────────────────────────────────────────────
108
109    async fn graph_traverse(
110        &self,
111        collection: &str,
112        start: &NodeId,
113        depth: u8,
114        direction: Direction,
115        edge_filter: Option<&EdgeFilter>,
116    ) -> NodeDbResult<SubGraph> {
117        self.graph_traverse_impl(collection, start, depth, direction, edge_filter)
118            .await
119    }
120
121    async fn graph_insert_edge(
122        &self,
123        collection: &str,
124        from: &NodeId,
125        to: &NodeId,
126        edge_type: &str,
127        properties: Option<Document>,
128    ) -> NodeDbResult<EdgeId> {
129        self.graph_insert_edge_impl(collection, from, to, edge_type, properties)
130            .await
131    }
132
133    async fn graph_delete_edge(&self, collection: &str, edge_id: &EdgeId) -> NodeDbResult<()> {
134        self.graph_delete_edge_impl(collection, edge_id).await
135    }
136
137    async fn graph_stats(
138        &self,
139        collection: Option<&str>,
140        as_of: Option<i64>,
141    ) -> NodeDbResult<Vec<GraphStats>> {
142        self.graph_stats_impl(collection, as_of).await
143    }
144
145    async fn graph_pagerank(
146        &self,
147        collection: &str,
148        personalization: Option<std::collections::HashMap<String, f64>>,
149        damping: Option<f64>,
150        max_iterations: Option<u32>,
151    ) -> NodeDbResult<Vec<(String, f64)>> {
152        self.graph_pagerank_impl(collection, personalization, damping, max_iterations)
153            .await
154    }
155
156    async fn graph_shortest_path(
157        &self,
158        collection: &str,
159        from: &NodeId,
160        to: &NodeId,
161        max_depth: u8,
162        edge_filter: Option<&EdgeFilter>,
163    ) -> NodeDbResult<Option<Vec<NodeId>>> {
164        self.graph_shortest_path_impl(collection, from, to, max_depth, edge_filter)
165            .await
166    }
167
168    /// Overrides the trait's default (which loops `graph_insert_edge`) with
169    /// the batched CSR-lock-once, single-CRDT-write path. `edge_type` and
170    /// `properties` are not part of this trait signature, so every edge is
171    /// inserted with no properties, matching what looping `graph_insert_edge`
172    /// with `properties: None` would have written.
173    async fn batch_graph_insert_edges(
174        &self,
175        collection: &str,
176        edges: &[(&str, &str, &str)],
177    ) -> NodeDbResult<()> {
178        let owned: Vec<(NodeId, NodeId, &str, Option<Document>)> = edges
179            .iter()
180            .map(|&(src, dst, label)| {
181                (
182                    NodeId::from_validated(src.to_string()),
183                    NodeId::from_validated(dst.to_string()),
184                    label,
185                    None,
186                )
187            })
188            .collect();
189        self.batch_graph_insert_edges(collection, &owned).await?;
190        Ok(())
191    }
192
193    // ─── CRDT List Operations (Movable List) ───────────────────────────
194
195    async fn list_insert(
196        &self,
197        collection: &str,
198        document_id: &str,
199        list_path: &str,
200        index: usize,
201        fields: &Value,
202    ) -> NodeDbResult<()> {
203        self.list_insert_impl(collection, document_id, list_path, index, fields)
204            .await
205    }
206
207    async fn list_delete(
208        &self,
209        collection: &str,
210        document_id: &str,
211        list_path: &str,
212        index: usize,
213    ) -> NodeDbResult<()> {
214        self.list_delete_impl(collection, document_id, list_path, index)
215            .await
216    }
217
218    async fn list_move(
219        &self,
220        collection: &str,
221        document_id: &str,
222        list_path: &str,
223        from_index: usize,
224        to_index: usize,
225    ) -> NodeDbResult<()> {
226        self.list_move_impl(collection, document_id, list_path, from_index, to_index)
227            .await
228    }
229
230    // ─── Document Operations ─────────────────────────────────────────
231
232    async fn document_get(&self, collection: &str, id: &str) -> NodeDbResult<Option<Document>> {
233        self.document_get_impl(collection, id).await
234    }
235
236    async fn document_put(&self, collection: &str, doc: Document) -> NodeDbResult<()> {
237        self.document_put_impl(collection, doc).await
238    }
239
240    async fn document_delete(&self, collection: &str, id: &str) -> NodeDbResult<()> {
241        self.document_delete_impl(collection, id).await
242    }
243
244    async fn document_put_with_vector(
245        &self,
246        doc_collection: &str,
247        doc: Document,
248        vector_collection: &str,
249        id: &str,
250        embedding: &[f32],
251    ) -> NodeDbResult<()> {
252        self.document_put_with_vector_impl(doc_collection, doc, vector_collection, id, embedding)
253            .await
254    }
255
256    async fn document_get_as_of(
257        &self,
258        collection: &str,
259        id: &str,
260        as_of_ms: Option<i64>,
261        valid_time_ms: Option<i64>,
262    ) -> NodeDbResult<Option<Document>> {
263        self.document_get_as_of_impl(collection, id, as_of_ms, valid_time_ms)
264            .await
265    }
266
267    async fn document_put_with_valid_time(
268        &self,
269        collection: &str,
270        doc: Document,
271        valid_from_ms: Option<i64>,
272        valid_until_ms: Option<i64>,
273    ) -> NodeDbResult<()> {
274        self.document_put_with_valid_time_impl(collection, doc, valid_from_ms, valid_until_ms)
275            .await
276    }
277
278    // ─── SQL and Text Search ─────────────────────────────────────────
279
280    async fn execute_sql(&self, query: &str, params: &[Value]) -> NodeDbResult<QueryResult> {
281        self.execute_sql_impl(query, params).await
282    }
283
284    async fn text_search(
285        &self,
286        collection: &str,
287        field: &str,
288        query: &str,
289        top_k: usize,
290        params: TextSearchParams,
291        allowed_ids: Option<&HashSet<String>>,
292    ) -> NodeDbResult<Vec<SearchResult>> {
293        self.text_search_impl(collection, field, query, top_k, params, allowed_ids)
294            .await
295    }
296
297    // ─── Collection Lifecycle ─────────────────────────────────────────
298
299    async fn list_dropped_collections(&self) -> NodeDbResult<Vec<DroppedCollection>> {
300        // Lite has no soft-delete or retention layer, so the list is
301        // always empty. Routing through `execute_sql` would hit the
302        // catalog-shaped query the Origin trait default expects, which
303        // Lite's executor does not implement.
304        Ok(Vec::new())
305    }
306}