Skip to content

Commit 31ff122

Browse files
konardclaude
andcommitted
Add separate implementation files for Neo4j and Doublets
Per user feedback, create separate documentation files for easier comparison: - doublets_impl.rs: Documents how Doublets implements the common interface - neo4j_impl.rs: Documents how Neo4j implements the common interface with Cypher Each file shows the same operations side-by-side for easy structure comparison. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <[email protected]>
1 parent fc7fe61 commit 31ff122

File tree

3 files changed

+435
-12
lines changed

3 files changed

+435
-12
lines changed

rust/src/doublets_impl.rs

Lines changed: 246 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,246 @@
1+
//! # Doublets Implementation
2+
//!
3+
//! This module documents how Doublets implements the common `Doublets<T>` interface.
4+
//! Each operation is implemented using direct memory access to specialized data structures.
5+
//!
6+
//! ## Storage Types
7+
//!
8+
//! ### United (Unit) Store
9+
//!
10+
//! ```rust,ignore
11+
//! // Volatile (in-memory):
12+
//! let store = unit::Store::new(Alloc::new(Global))?;
13+
//!
14+
//! // Non-volatile (file-mapped):
15+
//! let store = unit::Store::new(FileMapped::new(file)?)?;
16+
//! ```
17+
//!
18+
//! Each link is stored as a contiguous `LinkPart<T>`:
19+
//! ```text
20+
//! +--------+--------+--------+
21+
//! | id | source | target |
22+
//! +--------+--------+--------+
23+
//! ```
24+
//!
25+
//! ### Split Store
26+
//!
27+
//! ```rust,ignore
28+
//! // Volatile (in-memory):
29+
//! let store = split::Store::new(
30+
//! Alloc::new(Global), // data
31+
//! Alloc::new(Global), // index
32+
//! )?;
33+
//!
34+
//! // Non-volatile (file-mapped):
35+
//! let store = split::Store::new(
36+
//! FileMapped::new(data_file)?,
37+
//! FileMapped::new(index_file)?,
38+
//! )?;
39+
//! ```
40+
//!
41+
//! Separates data and indexes for better cache efficiency:
42+
//! ```text
43+
//! DataPart: IndexPart:
44+
//! +--------+--------+ +----------------+
45+
//! | source | target | | source_tree |
46+
//! +--------+--------+ | target_tree |
47+
//! +----------------+
48+
//! ```
49+
//!
50+
//! ## Operations
51+
//!
52+
//! ### Create Point Link
53+
//!
54+
//! Interface method: `store.create_point()`
55+
//!
56+
//! ```rust,ignore
57+
//! // Implementation (conceptual):
58+
//! let id = self.allocate_next_id();
59+
//! self.links[id] = Link { id, source: id, target: id };
60+
//! self.source_index.insert(id, id); // index by source
61+
//! self.target_index.insert(id, id); // index by target
62+
//! ```
63+
//!
64+
//! - Allocates next available ID from internal counter
65+
//! - Writes (id, id, id) tuple directly to memory/file
66+
//! - Updates source and target index trees
67+
//! - Time complexity: O(log n) for index updates
68+
//!
69+
//! ### Update Link
70+
//!
71+
//! Interface method: `store.update(id, source, target)`
72+
//!
73+
//! ```rust,ignore
74+
//! // Implementation (conceptual):
75+
//! let old = self.links[id];
76+
//! self.source_index.remove(old.source, id);
77+
//! self.target_index.remove(old.target, id);
78+
//! self.links[id] = Link { id, source, target };
79+
//! self.source_index.insert(source, id);
80+
//! self.target_index.insert(target, id);
81+
//! ```
82+
//!
83+
//! - Direct memory access to read old values: O(1)
84+
//! - Updates index trees: O(log n)
85+
//! - Writes new values: O(1)
86+
//!
87+
//! ### Delete Link
88+
//!
89+
//! Interface method: `store.delete(id)`
90+
//!
91+
//! ```rust,ignore
92+
//! // Implementation (conceptual):
93+
//! let old = self.links[id];
94+
//! self.source_index.remove(old.source, id);
95+
//! self.target_index.remove(old.target, id);
96+
//! self.links[id] = EMPTY;
97+
//! self.free_list.push(id); // reuse slot later
98+
//! ```
99+
//!
100+
//! - Direct memory access: O(1)
101+
//! - Index tree updates: O(log n)
102+
//! - Marks slot for reuse
103+
//!
104+
//! ### Query All Links (Each All)
105+
//!
106+
//! Interface method: `store.each(handler)` or `store.each_by([any, any, any], handler)`
107+
//!
108+
//! ```rust,ignore
109+
//! // Implementation (conceptual):
110+
//! for id in 1..=self.count {
111+
//! if self.links[id].is_valid() {
112+
//! handler(self.links[id]);
113+
//! }
114+
//! }
115+
//! ```
116+
//!
117+
//! - Iterates through internal link array sequentially
118+
//! - Skips empty/deleted slots
119+
//! - Time complexity: O(n) where n = total allocated slots
120+
//!
121+
//! ### Query by ID (Each Identity)
122+
//!
123+
//! Interface method: `store.each_by([id, any, any], handler)`
124+
//!
125+
//! ```rust,ignore
126+
//! // Implementation (conceptual):
127+
//! if let Some(link) = self.links.get(id) {
128+
//! if link.is_valid() {
129+
//! handler(link);
130+
//! }
131+
//! }
132+
//! ```
133+
//!
134+
//! - Direct array index access: O(1)
135+
//! - Returns link at `links[id]` if it exists
136+
//! - Fastest possible lookup
137+
//!
138+
//! ### Query by Source (Each Outgoing)
139+
//!
140+
//! Interface method: `store.each_by([any, source, any], handler)`
141+
//!
142+
//! ```rust,ignore
143+
//! // Implementation (conceptual):
144+
//! for id in self.source_index.get_all(source) {
145+
//! handler(self.links[id]);
146+
//! }
147+
//! ```
148+
//!
149+
//! - Uses source index tree to find all links with given source
150+
//! - Time complexity: O(log n + k) where k = matching links
151+
//! - Finds all outgoing edges from a node
152+
//!
153+
//! ### Query by Target (Each Incoming)
154+
//!
155+
//! Interface method: `store.each_by([any, any, target], handler)`
156+
//!
157+
//! ```rust,ignore
158+
//! // Implementation (conceptual):
159+
//! for id in self.target_index.get_all(target) {
160+
//! handler(self.links[id]);
161+
//! }
162+
//! ```
163+
//!
164+
//! - Uses target index tree to find all links with given target
165+
//! - Time complexity: O(log n + k) where k = matching links
166+
//! - Finds all incoming edges to a node
167+
//!
168+
//! ### Query by Source AND Target (Each Concrete)
169+
//!
170+
//! Interface method: `store.each_by([any, source, target], handler)`
171+
//!
172+
//! ```rust,ignore
173+
//! // Implementation (conceptual):
174+
//! // Uses whichever index has fewer entries, then filters
175+
//! let candidates = self.source_index.get_all(source); // or target_index
176+
//! for id in candidates {
177+
//! if self.links[id].target == target { // or source check
178+
//! handler(self.links[id]);
179+
//! }
180+
//! }
181+
//! ```
182+
//!
183+
//! - Uses one index tree, then filters by the other field
184+
//! - Time complexity: O(log n + k) for tree traversal
185+
//!
186+
//! ### Get Link by ID
187+
//!
188+
//! Interface method: `store.get_link(id)`
189+
//!
190+
//! ```rust,ignore
191+
//! // Implementation (conceptual):
192+
//! self.links.get(id).filter(|l| l.is_valid()).copied()
193+
//! ```
194+
//!
195+
//! - Direct array access: O(1)
196+
//!
197+
//! ## Index Structure
198+
//!
199+
//! Doublets uses balanced trees for source and target indexes:
200+
//!
201+
//! ```text
202+
//! Source Index Tree:
203+
//! [5]
204+
//! / \
205+
//! [3] [7]
206+
//! / \
207+
//! [1] [9]
208+
//!
209+
//! Each node contains: (source_value -> list of link IDs)
210+
//! ```
211+
//!
212+
//! ## Performance Characteristics
213+
//!
214+
//! | Operation | Time Complexity | Notes |
215+
//! |-----------------|-----------------|---------------------------------------|
216+
//! | Create | O(log n) | Index tree insertions |
217+
//! | Update | O(log n) | Index tree remove + insert |
218+
//! | Delete | O(log n) | Index tree removals |
219+
//! | Each All | O(n) | Full array scan |
220+
//! | Each Identity | O(1) | Direct array access |
221+
//! | Each Outgoing | O(log n + k) | Tree lookup + k results |
222+
//! | Each Incoming | O(log n + k) | Tree lookup + k results |
223+
//! | Each Concrete | O(log n + k) | Tree lookup + filter |
224+
//!
225+
//! ## Memory Layout
226+
//!
227+
//! ### United Store
228+
//! ```text
229+
//! [Header][Link 1][Link 2][Link 3]...[Link n]
230+
//! ^ ^
231+
//! | +-- Each link: (id, source, target)
232+
//! +-- Contains: count, free_list_head, etc.
233+
//! ```
234+
//!
235+
//! ### Split Store
236+
//! ```text
237+
//! Data File:
238+
//! [Header][Data 1][Data 2]...[Data n]
239+
//! ^
240+
//! +-- Each data: (source, target)
241+
//!
242+
//! Index File:
243+
//! [Header][Source Tree Nodes][Target Tree Nodes]
244+
//! ```
245+
246+
// This is a documentation-only module

rust/src/lib.rs

Lines changed: 13 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,17 @@
66
//! database operations. It defines a common trait that both databases implement,
77
//! making it crystal clear what logic is being benchmarked against what.
88
//!
9+
//! ## Detailed Implementation Documentation
10+
//!
11+
//! For detailed documentation on how each database implements the common interface,
12+
//! see the separate implementation modules:
13+
//!
14+
//! - **[`doublets_impl`]** - How Doublets implements each operation with direct memory access
15+
//! - **[`neo4j_impl`]** - How Neo4j implements each operation with Cypher queries
16+
//!
17+
//! These modules provide side-by-side comparison of the same logic implemented
18+
//! in both databases.
19+
//!
920
//! ## The Common Interface
1021
//!
1122
//! Both Neo4j and Doublets implement the same operations through the [`Doublets<T>`]
@@ -23,18 +34,6 @@
2334
//! | Each Outgoing | `each_by([*,s,*], h)` | Find links by source (outgoing edges) |
2435
//! | Each Incoming | `each_by([*,*,t], h)` | Find links by target (incoming edges) |
2536
//!
26-
//! ## Implementation Comparison
27-
//!
28-
//! ### Doublets Implementation
29-
//! - Uses in-memory or file-mapped storage with specialized link data structures
30-
//! - Direct memory access for O(1) link retrieval by ID
31-
//! - Maintains source/target indexes for fast queries
32-
//!
33-
//! ### Neo4j Implementation
34-
//! - Uses HTTP API to execute Cypher queries
35-
//! - Links stored as Neo4j nodes with `id`, `source`, `target` properties
36-
//! - Indexes created on `id`, `source`, and `target` for query performance
37-
//!
3837
//! ## Benchmarked Implementations
3938
//!
4039
//! | Implementation | Backend Type | Description |
@@ -103,8 +102,10 @@ pub use transaction::Transaction;
103102

104103
mod benched;
105104
mod client;
105+
pub mod doublets_impl;
106106
mod exclusive;
107107
mod fork;
108+
pub mod neo4j_impl;
108109
mod transaction;
109110

110111
pub type Result<T, E = Box<dyn error::Error + Sync + Send>> = result::Result<T, E>;

0 commit comments

Comments
 (0)