Repository navigation
Requirements
Stephan Zehrer edited this page Jun 28, 2026
·
13 revisions
Numbered requirements for mGraphDB. Each is verified by a method on the Test Plan page. Status: Done, Design, or Planned.
Requirement IDs are stable; if a requirement is dropped it is marked obsolete rather than reused.
| ID | Requirement | Status |
|---|---|---|
| REQ-01 | The model represents Nodes, Edges, and Properties. | Done |
| REQ-02 | Edges and Properties are specialisations of Node ("everything is a node"). | Done |
| REQ-03 | Every element is addressable by a compact integer ID. | Done |
| REQ-04 | A node may carry a type reference to a "class" node. The reference is optional; no type is required. | Design |
| REQ-05 | The database is schema-less: no schema or type definition is required before writing data; the storage engine enforces no structural constraints. Types, classes, and ontologies are represented as nodes and edges within the graph itself and constitute a descriptive viewpoint, not a write gate. | Design |
| ID | Requirement | Status |
|---|---|---|
| REQ-10 | Strings are interned and deduplicated; identical content is stored once. | Done |
| REQ-11 | Each string is addressable by a compact StrId (u64). |
Done |
| REQ-12 | Each string has a 128-bit content hash for dedup and cross-segment identity. | Done |
| REQ-13 | Storage is append-only: existing entries are never mutated. | Done |
| REQ-14 | An entry is encoded as [hash128][varint len][utf8][crc32]. |
Done |
| REQ-15 | Entries are grouped into blocks (~64 KiB) with an entry-offset trailer. | Done |
| REQ-16 | A store can be saved to and re-opened from a single segment file. Format v1 (uncompressed) and v2 (compression-capable) are both readable. | Done |
| REQ-17 | On open, every entry's CRC32 and UTF-8 validity are verified. | Done |
| REQ-18 | On open, a corrupt header/footer/directory is rejected with an error. | Done |
| REQ-19 | API: intern, resolve_id, resolve_hash, id_of, hash_of. |
Done |
| REQ-20 | Block payload compression is optional and signalled by header flags. Supported algorithms: None (default), LZ4 (lz4_flex, pure Rust), Zstd (level 3). Selected at store-creation time via Compression enum. |
Done |
| REQ-21 | A per-segment Bloom filter is optional and signalled by footer flags. | Planned |
| REQ-22 | Long values may be routed to a more space-efficient store. Implemented in Graph::set_str: strings > 14 bytes are interned into the String Store and referenced by StrId. |
Done |
| REQ-23 | Incremental on-disk append (without rewriting the whole segment). | Planned |
| REQ-24 |
Lazy decompression: blocks remain compressed on disk; individual entries are decompressed on demand by resolve_id/resolve_hash without loading or decompressing the full block. |
Planned |
| REQ-25 | FSST compression as an optional third algorithm (after REQ-24); enables random-access decompression of single entries via a per-block symbol table. Depends on a production-ready, Arrow-free Rust implementation. | Planned |
| ID | Requirement | Status |
|---|---|---|
| REQ-30 | Records are fixed length, giving direct access by ID (offset = id × record_size). |
Done |
| REQ-31 | Reference (ID) size is configurable at creation. Current: u32 (NodeId); NodeId::MAX is the null sentinel. |
Done |
| REQ-32 | Node storage has three options: not stored, dedicated file, or inline-with-data. Option 2 (dedicated file) implemented. | Done |
| REQ-33 | Node flags encode subtype (4 bits) plus tombstone, HAS_IN, HAS_OUT, HAS_PROP. | Done |
| REQ-34 | Edges and Properties share a fixed 16-byte record in the property store. | Done |
| REQ-35 | An edge stores start/end node references; properties store a typed value. | Done |
| REQ-36 | The 16-byte buffer supports a range of value types (see §4). | Done |
| REQ-37 | Values too large for 16 bytes are referenced into the String Store. | Done |
| REQ-38 | A node's owned edges/properties are resolved through a separate CSR adjacency index (offsets + neighbors), built from explicit owner→record associations. The index is a derived view: rebuildable, memory-only, or droppable. |
Done |
| ID | Requirement | Status |
|---|---|---|
| REQ-40 | STRING: short UTF-8 inline — up to INLINE_STR_MAX (10) bytes; longer strings via String Store. |
Done |
| REQ-41 | BOOL, INTEGER (i128), DECIMAL (f64/f128). | Done |
| REQ-42 | DATE / TIME / DATETIME / DURATION. | Done |
| REQ-43 | UUID (16 bytes), HASH, GEO (lat/long). | Done |
| REQ-44 | URL / TEXT / FILE referenced into a backing store. | Done |
| REQ-45 | VECTOR referenced into an optional vector store. | Planned |
| REQ-46 | The value type is encoded in the record flags. | Done |
| ID | Requirement | Status |
|---|---|---|
| REQ-60 | A single Graph facade owns the node, property/edge, string, and index layers and exposes node/property/edge creation. |
Done |
| REQ-61 | Edges are created as Edge-valued property records owned by the start node; the start gains HAS_OUT and the end gains HAS_IN. |
Done |
| REQ-62 | Traversal accessors return a node's owned records, outgoing edges, and properties; string values resolve transparently across inline / String-Store storage. | Done |
| REQ-63 | The whole graph can be persisted to and reloaded from a directory of segment files, rebuilding adjacency from the CSR index on load. | Done |
| REQ-64 | Incoming-edge traversal is O(1) via a reverse adjacency (in_edges / in_neighbors / in_degree), maintained on edge creation and rebuilt from the edges on load in insertion order. |
Done |
| REQ-65 | A node can be soft-deleted (tombstoned, id never reused) and is detached symmetrically: its outgoing and incoming edges are removed from the respective peers, and its records are orphaned. Writes targeting a deleted node are rejected. Deletion state persists. | Done |
| REQ-66 | Each property and edge carries a key: a NodeId naming the property or labelling the edge (RDF-predicate style; NO_KEY = unkeyed). It is stored in-record (bytes 2–5). Lookups by key (get_property, edges_of_type) resolve by name, not position. |
Done |
| REQ-67 | Nodes can be found by property value: find_by_property(key, value), find_by_str(key, s) (resolves inline/String-Store storage), and find_by_key(key). Results are ascending node order, each node once, excluding edges and deleted nodes. Currently a full scan; a secondary inverted index is a planned optimisation. |
Done |
| REQ-68 | A graph can be imported from a SNAP-style edge list (from/to integer pairs, # comments skipped) via from_edge_list; sparse source ids are remapped to contiguous NodeIds, each line becoming one directed edge. |
Done |
| ID | Requirement | Status |
|---|---|---|
| REQ-50 | Minimal footprint, embeddable like SQLite. | Design |
| REQ-51 | No unsafe in the project's own crates. |
Done |
| REQ-52 | Zero compiler and Clippy warnings. | Done |
| REQ-53 | Little-endian on-disk encoding across platforms. | Done |
| REQ-54 | MIT licensed. | Done |
| REQ-55 | Full ACID support is not required for now. | N/A |
(c) Stephan Zehrer (2025)
Planning & process
Design & architecture
Scratch