Skip to main content

overgraph/
lib.rs

1//! # OverGraph
2//!
3//! An absurdly fast embedded graph database. Pure Rust, sub-microsecond reads.
4//!
5//! OverGraph stores labeled nodes and edges with schemaless properties (MessagePack),
6//! temporal validity windows, exponential decay scoring, and automatic retention
7//! policies. It runs inside your process with no separate server or network calls.
8//!
9//! ## Quick start
10//!
11//! ```no_run
12//! use overgraph::{DatabaseEngine, DbOptions, UpsertNodeOptions, NeighborOptions};
13//!
14//! use std::path::Path;
15//! let mut db = DatabaseEngine::open(Path::new("./my-db"), &DbOptions::default()).unwrap();
16//! let id = db.upsert_node("User", "alice", UpsertNodeOptions::default()).unwrap();
17//! let neighbors = db.neighbors(id, &NeighborOptions { limit: Some(50), ..Default::default() }).unwrap();
18//! db.close().unwrap();
19//! ```
20//!
21//! ## Storage engine
22//!
23//! Log-structured merge tree: WAL -> memtable -> immutable segments -> background
24//! compaction. Reads never block writes. Segments are memory-mapped for zero-copy
25//! access through the OS page cache.
26//!
27//! ## Language connectors
28//!
29//! Native bindings for Node.js (napi-rs) and Python (PyO3) with full API parity.
30
31// Public API: the types and engine that library users interact with.
32pub mod engine;
33pub mod error;
34pub mod types;
35
36// Internal modules stay crate-private so public Rust callers cannot depend on
37// storage, WAL, segment, or planner implementation details.
38#[doc(hidden)]
39pub(crate) mod degree_cache;
40#[doc(hidden)]
41pub(crate) mod dense_hnsw;
42#[doc(hidden)]
43pub(crate) mod edge_metadata;
44#[doc(hidden)]
45pub(crate) mod encoding;
46#[doc(hidden)]
47pub(crate) mod gql;
48#[doc(hidden)]
49pub(crate) mod graph_row;
50// Diagnostic exception: `overgraph-inspect` uses the read-only manifest loader,
51// and `DatabaseEngine::manifest()` remains an explicit introspection surface.
52#[doc(hidden)]
53pub mod manifest;
54#[doc(hidden)]
55pub(crate) mod memtable;
56#[doc(hidden)]
57pub(crate) mod parallel;
58#[doc(hidden)]
59pub(crate) mod planner_stats;
60#[doc(hidden)]
61pub(crate) mod property_value_semantics;
62#[doc(hidden)]
63pub(crate) mod row_projection;
64#[doc(hidden)]
65pub(crate) mod scrub;
66#[doc(hidden)]
67pub(crate) mod segment_components;
68#[doc(hidden)]
69pub(crate) mod segment_reader;
70#[doc(hidden)]
71pub(crate) mod segment_writer;
72#[doc(hidden)]
73pub(crate) mod source_list;
74#[doc(hidden)]
75pub(crate) mod sparse_postings;
76#[doc(hidden)]
77pub(crate) mod wal;
78#[doc(hidden)]
79pub(crate) mod wal_sync;
80
81pub use engine::{DatabaseEngine, WriteTxn};
82pub use error::EngineError;
83pub use types::{
84    canonicalize_sparse_vector, canonicalize_sparse_vector_owned, hash_prop_key, hash_prop_value,
85    validate_dense_vector, validate_dense_vector_config, AdjacencyExport, AllShortestPathsOptions,
86    CompactionPhase, CompactionProgress, CompactionStats, ComponentOptions, ComponentScrubFinding,
87    DbOptions, DbStats, DegreeOptions, DenseMetric, DenseVector, DenseVectorConfig, Direction,
88    EdgeFilterExpr, EdgeInput, EdgeLabelInfo, EdgePropertyIndexInfo, EdgeQuery, EdgeQueryOrder,
89    EdgeView, ExportEdge, ExportOptions, FusionMode, GqlCapSummary, GqlEdge,
90    GqlExecutionCapSummary, GqlExecutionExplain, GqlExecutionMode, GqlExecutionOptions,
91    GqlExecutionResult, GqlExecutionStats, GqlExplain, GqlLoweringTarget, GqlMutationExplain,
92    GqlMutationOperationExplain, GqlMutationReadPrefixExplain, GqlMutationReturnExplain,
93    GqlMutationStats, GqlNode, GqlParamValue, GqlParams, GqlRow, GqlRowOperation,
94    GqlSemanticErrorCode, GqlStatementKind, GqlValue, GraphBinaryOp, GraphCapExplain,
95    GraphCaseBranch, GraphCursorExplain, GraphEdgeField, GraphEdgePattern, GraphEdgeValue,
96    GraphElementProjection, GraphExecutionSummaries, GraphExplainNode, GraphExpr, GraphFunction,
97    GraphNodeField, GraphNodePattern, GraphNodeValue, GraphOptionalGroup, GraphOrderDirection,
98    GraphOrderExplain, GraphOrderItem, GraphOutputMode, GraphOutputOptions, GraphPageRequest,
99    GraphParamValue, GraphPatch, GraphPath, GraphPathField, GraphPathValue, GraphPatternPiece,
100    GraphPipelineCapExplain, GraphPipelineExplain, GraphPipelineMatchStage, GraphPipelineOptions,
101    GraphPipelineQuery, GraphPipelineResult, GraphPipelineStage, GraphPipelineStageExplain,
102    GraphPipelineStats, GraphProjectItem, GraphProjectKind, GraphProjectStage,
103    GraphProjectionExplain, GraphProjectionItems, GraphPropertySelection, GraphQueryOptions,
104    GraphReturnItem, GraphReturnProjection, GraphRow, GraphRowExplain, GraphRowOperationExplain,
105    GraphRowQuery, GraphRowResult, GraphRowStats, GraphSelectedEdgeProjection,
106    GraphSelectedNodeProjection, GraphSelectedPathProjection, GraphSelectedProjection,
107    GraphShortestPathEndpoint, GraphShortestPathMode, GraphShortestPathStage, GraphSubqueryStage,
108    GraphUnaryOp, GraphUnionStage, GraphValue, GraphVariableLengthPattern, GraphVectorSelection,
109    HnswConfig, IntoNodeLabels, IsConnectedOptions, LabelMatchMode, ManifestState, NeighborEntry,
110    NeighborOptions, NodeFilterExpr, NodeIdBuildHasher, NodeIdHasher, NodeIdMap, NodeIdSet,
111    NodeInput, NodeKeyQuery, NodeLabelFilter, NodeLabelInfo, NodePropertyIndexInfo, NodeQuery,
112    NodeQueryOrder, NodeView, PageRequest, PageResult, PatchResult, PprAlgorithm, PprApproxMeta,
113    PprOptions, PprResult, PropValue, PropertyRangeBound, PropertyRangeCursor,
114    PropertyRangePageRequest, PropertyRangePageResult, PrunePolicy, PrunePolicyInfo, PruneResult,
115    QueryEdgeIdsResult, QueryEdgesResult, QueryNodeIdsResult, QueryNodesResult, QueryPlan,
116    QueryPlanKind, QueryPlanNode, QueryPlanNote, QueryPlanPublicInputs, QueryPlanPublicName,
117    QueryPlanWarning, ScoringMode, ScrubFindingType, ScrubReport, SecondaryIndexKind,
118    SecondaryIndexManifestEntry, SecondaryIndexState, SecondaryIndexTarget, SegmentInfo,
119    SegmentScrubResult, ShortestPath, ShortestPathOptions, SourceSpan, SparseVector, Subgraph,
120    SubgraphOptions, TombstoneEntry, TopKOptions, TraversalCursor, TraversalHit,
121    TraversalPageResult, TraverseOptions, TxnCommitResult, TxnEdgeRef, TxnEdgeView, TxnIntent,
122    TxnLocalRef, TxnNodeRef, TxnNodeView, UpsertEdgeOptions, UpsertNodeOptions, VectorHit,
123    VectorSearchMode, VectorSearchRequest, VectorSearchScope, WalSyncMode, DEFAULT_DENSE_EF_SEARCH,
124};
125
126#[doc(hidden)]
127pub fn gql_referenced_param_names(
128    query: &str,
129    options: &GqlExecutionOptions,
130) -> Result<Vec<String>, EngineError> {
131    crate::gql::params::referenced_param_names_for_query(query, options)
132}
133
134#[cfg(test)]
135mod public_api_boundary_tests {
136    fn source(path: &str) -> String {
137        std::fs::read_to_string(std::path::Path::new(env!("CARGO_MANIFEST_DIR")).join(path))
138            .unwrap()
139    }
140
141    fn rust_source_paths() -> Vec<std::path::PathBuf> {
142        fn collect(dir: &std::path::Path, out: &mut Vec<std::path::PathBuf>) {
143            for entry in std::fs::read_dir(dir).unwrap() {
144                let path = entry.unwrap().path();
145                if path.is_dir() {
146                    collect(&path, out);
147                } else if path.extension().is_some_and(|ext| ext == "rs") {
148                    out.push(path);
149                }
150            }
151        }
152
153        let root = std::path::Path::new(env!("CARGO_MANIFEST_DIR"));
154        let mut paths = Vec::new();
155        for dir in ["src", "tests", "benches"] {
156            collect(&root.join(dir), &mut paths);
157        }
158        paths
159    }
160
161    fn assert_files_do_not_contain(paths: &[std::path::PathBuf], patterns: &[String]) {
162        let root = std::path::Path::new(env!("CARGO_MANIFEST_DIR"));
163        for path in paths {
164            let contents = std::fs::read_to_string(path).unwrap();
165            let display = path.strip_prefix(root).unwrap_or(path).display();
166            for pattern in patterns {
167                assert!(
168                    !contents.contains(pattern),
169                    "`{pattern}` must not remain in Rust active edge-label source ({display})"
170                );
171            }
172        }
173    }
174
175    #[test]
176    fn internal_numeric_records_are_not_publicly_exported() {
177        let lib = source("src/lib.rs");
178        let types = source("src/types.rs");
179
180        assert!(
181            !lib.contains(concat!("pub use types", "::*")),
182            "public API must explicitly re-export stable DTOs and not glob-export internal records"
183        );
184        for forbidden in [
185            concat!("pub struct ", "NodeRecord"),
186            concat!("pub struct ", "EdgeRecord"),
187        ] {
188            assert!(
189                !types.contains(forbidden),
190                "`{forbidden}` would expose internal numeric label/type records"
191            );
192        }
193        for required in [
194            concat!("pub(crate) struct ", "NodeRecord"),
195            concat!("pub(crate) struct ", "EdgeRecord"),
196        ] {
197            assert!(
198                types.contains(required),
199                "`{required}` must remain the internal storage/WAL record boundary"
200            );
201        }
202    }
203
204    #[test]
205    fn graph_row_replacement_names_do_not_use_v2_suffixes() {
206        let paths = rust_source_paths();
207        assert_files_do_not_contain(
208            &paths,
209            &[
210                concat!("GraphRow", "V2").to_string(),
211                concat!("GraphRowQuery", "V2").to_string(),
212                concat!("GraphPatternPiece", "V2").to_string(),
213                concat!("GraphNodePattern", "V2").to_string(),
214                concat!("GraphEdgePattern", "V2").to_string(),
215                concat!("GraphReturnItem", "V2").to_string(),
216                concat!("GraphOutputOptions", "V2").to_string(),
217                concat!("GraphQueryOptions", "V2").to_string(),
218                concat!("GraphRowResult", "V2").to_string(),
219                concat!("GraphRowExplain", "V2").to_string(),
220                concat!("NormalizedGraphRowQuery", "V2").to_string(),
221            ],
222        );
223    }
224
225    #[test]
226    fn old_graph_pattern_public_exports_are_removed() {
227        let lib = source("src/lib.rs");
228        let types = source("src/types.rs");
229
230        for forbidden in [
231            concat!("Graph", "Pattern", "Query"),
232            concat!("Pattern", "Order"),
233            concat!("Query", "Match"),
234            concat!("Query", "Pattern", "Result"),
235        ] {
236            assert!(
237                !lib.contains(forbidden),
238                "`{forbidden}` must not be re-exported from the Rust public API"
239            );
240        }
241
242        for forbidden in [
243            concat!("pub struct ", "Graph", "Pattern", "Query"),
244            concat!("pub struct ", "Node", "Pattern"),
245            concat!("pub struct ", "Edge", "Pattern"),
246            concat!("pub enum ", "Pattern", "Order"),
247            concat!("pub struct ", "Query", "Pattern", "Result"),
248            concat!("pub struct ", "Query", "Match"),
249            concat!("Pattern", "Query"),
250            concat!("Pattern", "Expand"),
251            concat!("Pattern", "Edge", "Anchor"),
252            concat!("Unbounded", "Pattern", "Rejected"),
253        ] {
254            assert!(
255                !types.contains(forbidden),
256                "`{forbidden}` must not remain as a public/core graph-pattern DTO"
257            );
258        }
259    }
260
261    #[test]
262    fn old_graph_pattern_engine_methods_are_removed() {
263        let engine_query = source("src/engine/query.rs");
264        for forbidden in [
265            concat!("pub fn ", "query", "_pattern"),
266            concat!("pub fn ", "explain", "_pattern", "_query"),
267        ] {
268            assert!(
269                !engine_query.contains(forbidden),
270                "`{forbidden}` must not remain on DatabaseEngine"
271            );
272        }
273
274        for path in [
275            "src/engine/query_ir.rs",
276            "src/engine/query_exec.rs",
277            "src/engine/query_plan.rs",
278            "src/engine/projection.rs",
279        ] {
280            let contents = source(path);
281            for forbidden in [
282                concat!("Graph", "Pattern", "Query"),
283                concat!("Pattern", "Order"),
284                concat!("Query", "Match"),
285                concat!("Query", "Pattern", "Result"),
286                concat!("Normalized", "Graph", "Pattern", "Query"),
287                concat!("Planned", "Pattern", "Query"),
288                concat!("Pattern", "Plan", "Cost"),
289                concat!("Pattern", "Query"),
290                concat!("Pattern", "Expand"),
291                concat!("Pattern", "Edge", "Anchor"),
292                concat!("Unbounded", "Pattern", "Rejected"),
293                concat!("project", "_pattern", "_rows"),
294            ] {
295                assert!(
296                    !contents.contains(forbidden),
297                    "`{forbidden}` must not remain in old graph-pattern core path ({path})"
298                );
299            }
300        }
301    }
302
303    #[test]
304    fn rust_active_edge_label_id_vocabulary_has_no_backend_type_terms() {
305        let paths = rust_source_paths();
306        assert_files_do_not_contain(
307            &paths,
308            &[
309                concat!("EDGE", "_TYPE").to_string(),
310                concat!("Edge", "Type").to_string(),
311                concat!("edge", "_type").to_string(),
312                concat!("edge ", "type").to_string(),
313                concat!("edge", "-", "type").to_string(),
314                concat!("edges_by", "_type").to_string(),
315                concat!("visible_edges_by", "_type").to_string(),
316                concat!("type", "_edge_index").to_string(),
317                concat!("type", "_ids").to_string(),
318                concat!("type", "Id").to_string(),
319                concat!("type", " IDs").to_string(),
320                concat!("distinct ", "type").to_string(),
321                concat!("these ", "types").to_string(),
322                concat!("filtered", "_types").to_string(),
323                concat!("filtered", "_type", "_labels").to_string(),
324                concat!("Type ", "filter works").to_string(),
325                concat!("let ", "typed").to_string(),
326                concat!(":", "type", ":").to_string(),
327                concat!(":", "types", ":{").to_string(),
328            ],
329        );
330
331        let segment_reader = source("src/segment_reader.rs");
332        for pattern in [
333            concat!("entry", "_type"),
334            concat!("let e", "_type"),
335            concat!("match e", "_type"),
336        ] {
337            assert!(
338                !segment_reader.contains(pattern),
339                "`{pattern}` must not remain in segment edge label readers"
340            );
341        }
342    }
343
344    #[test]
345    fn implementation_modules_are_not_public_api() {
346        let lib = source("src/lib.rs");
347        for forbidden in [
348            concat!("pub mod ", "dense_hnsw;"),
349            concat!("pub mod ", "encoding;"),
350            concat!("pub mod ", "memtable;"),
351            concat!("pub mod ", "segment_reader;"),
352            concat!("pub mod ", "segment_writer;"),
353            concat!("pub mod ", "source_list;"),
354            concat!("pub mod ", "sparse_postings;"),
355            concat!("pub mod ", "wal;"),
356            concat!("pub mod ", "wal_sync;"),
357        ] {
358            assert!(
359                !lib.contains(forbidden),
360                "`{forbidden}` would expose implementation internals as Rust public API"
361            );
362        }
363    }
364
365    #[test]
366    fn manifest_module_public_surface_stays_read_only_diagnostic_only() {
367        let manifest = source("src/manifest.rs");
368        for forbidden in [
369            concat!("pub fn ", "write_manifest"),
370            concat!("pub fn ", "load_manifest("),
371            concat!("pub fn ", "default_manifest"),
372        ] {
373            assert!(
374                !manifest.contains(forbidden),
375                "`{forbidden}` must stay crate-private; manifest diagnostics expose read-only loading only"
376            );
377        }
378        assert!(
379            manifest.contains(concat!("pub fn ", "load_manifest_readonly")),
380            "the inspect binary relies on the explicit read-only diagnostic manifest loader"
381        );
382    }
383
384    #[test]
385    fn rust_public_edge_vocabulary_uses_labels() {
386        let lib = source("src/lib.rs");
387        let types = source("src/types.rs");
388        let engine = source("src/engine/mod.rs");
389        let read = source("src/engine/read.rs");
390        let write = source("src/engine/write.rs");
391        let txn = source("src/engine/txn.rs");
392        let manifest = source("src/manifest.rs");
393
394        for forbidden in [
395            concat!("Edge", "TypeInfo"),
396            concat!("pub edge", "_", "type:"),
397            concat!("pub edge", "_", "type", "_filter:"),
398            concat!("pub edge", "_", "type", "_index:"),
399            concat!("pub fn ensure_edge", "_", "type"),
400            concat!("pub fn get_edge", "_", "type("),
401            concat!("pub fn list_edge", "_", "types"),
402            concat!("pub fn ", "edges_by_label_id"),
403            concat!("pub fn ", "get_edges_by_label_id"),
404            concat!("pub fn ", "count_edges_by_label_id"),
405        ] {
406            assert!(
407                !lib.contains(forbidden)
408                    && !types.contains(forbidden)
409                    && !engine.contains(forbidden),
410                "`{forbidden}` must not remain in the Rust public edge-label API"
411            );
412        }
413
414        for forbidden in [
415            concat!("edge ", "type"),
416            concat!("edge", "-", "type"),
417            concat!("edge ", "type token"),
418            concat!("edge ", "type catalog"),
419            concat!("resolved by edge ", "type"),
420            concat!("transaction edge ", "type"),
421        ] {
422            assert!(
423                !types.contains(forbidden)
424                    && !engine.contains(forbidden)
425                    && !read.contains(forbidden)
426                    && !write.contains(forbidden)
427                    && !txn.contains(forbidden)
428                    && !manifest.contains(forbidden),
429                "`{forbidden}` must not remain in public-facing Rust edge-label diagnostics or docs"
430            );
431        }
432
433        for required in [
434            "EdgeLabelInfo",
435            "pub label: String",
436            "pub edge_label_filter: Option<Vec<String>>",
437            "pub edge_label_index: u32",
438            "pub fn ensure_edge_label",
439            "pub fn get_edge_label_id",
440            "pub fn get_edge_label(",
441            "pub fn list_edge_labels",
442            "pub fn edges_by_label",
443            "pub fn get_edges_by_label",
444            "pub fn count_edges_by_label",
445        ] {
446            assert!(
447                lib.contains(required) || types.contains(required) || engine.contains(required),
448                "`{required}` should exist in the Rust public edge-label API"
449            );
450        }
451    }
452}