@rljson/db 0.0.12 → 0.0.13

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1,859 @@
1
- # Architecture
1
+ # @rljson/db Architecture
2
+
3
+ This document provides a deep dive into the architecture, design patterns, and implementation details of @rljson/db.
4
+
5
+ ## Table of Contents
6
+
7
+ - [Overview](#overview)
8
+ - [Architecture Layers](#architecture-layers)
9
+ - [Core Components](#core-components)
10
+ - [Data Flow](#data-flow)
11
+ - [Controller Pattern](#controller-pattern)
12
+ - [Route Resolution](#route-resolution)
13
+ - [Caching Strategy](#caching-strategy)
14
+ - [Tree Processing](#tree-processing)
15
+ - [Join System](#join-system)
16
+ - [Multi-Edit System](#multi-edit-system)
17
+ - [Design Decisions](#design-decisions)
18
+
19
+ ## Overview
20
+
21
+ @rljson/db is a TypeScript-based database abstraction layer for content-addressed, hierarchical RLJSON data. It provides a high-level query interface on top of pluggable storage backends (`@rljson/io`).
22
+
23
+ ### Key Characteristics
24
+
25
+ - **Content-Addressed**: All data identified by SHA-based content hashes
26
+ - **Immutable**: Data is never modified in place; all changes create new versions
27
+ - **Hierarchical**: Native support for tree structures and nested relationships
28
+ - **Type-Safe**: Full TypeScript support with strong typing
29
+ - **Version-Tracked**: Built-in insert history for time-travel queries
30
+ - **Storage-Agnostic**: Works with any `Io` implementation (memory, file, network)
31
+
32
+ ## Architecture Layers
33
+
34
+ ```
35
+ ┌─────────────────────────────────────────────┐
36
+ │ Application Layer │
37
+ │ (User Queries & Mutations) │
38
+ └──────────────────┬──────────────────────────┘
39
+
40
+ ┌──────────────────▼──────────────────────────┐
41
+ │ Db Layer │
42
+ │ • Route Resolution │
43
+ │ • Query Planning │
44
+ │ • Caching │
45
+ │ • Notification │
46
+ └──────────────────┬──────────────────────────┘
47
+
48
+ ┌──────────────────▼──────────────────────────┐
49
+ │ Controller Layer │
50
+ │ • BaseController │
51
+ │ • TreeController (path-based expansion) │
52
+ │ • CakeController │
53
+ │ • LayerController │
54
+ │ • ComponentController │
55
+ │ • SliceIdController │
56
+ └──────────────────┬──────────────────────────┘
57
+
58
+ ┌──────────────────▼──────────────────────────┐
59
+ │ Core Layer │
60
+ │ • Table Management │
61
+ │ • Data Import/Export │
62
+ │ • Validation │
63
+ └──────────────────┬──────────────────────────┘
64
+
65
+ ┌──────────────────▼──────────────────────────┐
66
+ │ Io Layer │
67
+ │ • IoMem (in-memory) │
68
+ │ • IoFile (file system) │
69
+ │ • IoMulti (redundant storage) │
70
+ └─────────────────────────────────────────────┘
71
+ ```
72
+
73
+ ## Core Components
74
+
75
+ ### 1. Db Class
76
+
77
+ The main entry point providing high-level query and mutation operations.
78
+
79
+ **Location**: `src/db.ts`
80
+
81
+ **Key Responsibilities**:
82
+
83
+ - Route parsing and resolution
84
+ - Query coordination across controllers
85
+ - Caching query results
86
+ - Insert operation orchestration
87
+ - Notification broadcasting
88
+
89
+ **Key Methods**:
90
+
91
+ ```typescript
92
+ class Db {
93
+ async get(route, where, filter?, sliceIds?, options?): Promise<ContainerWithControllers>
94
+ async insert(route, data, origin?, refs?): Promise<InsertHistoryRow[]>
95
+ async getInsertHistory(table, options?): Promise<InsertHistoryTable>
96
+ // ... internal methods
97
+ async _get(route, where, controllers, filter, sliceIds, routeAccumulator, options)
98
+ async _getReferenceOfRouteSegment(segment): Promise<string>
99
+ async _resolveSliceIds(table, row): Promise<SliceId[]>
100
+ }
101
+ ```
102
+
103
+ **Data Structures**:
104
+
105
+ ```typescript
106
+ type Container = {
107
+ rljson: Rljson; // Table data indexed by table name
108
+ tree: Json; // Hierarchical representation
109
+ cell: Cell[]; // Path-value pairs for modifications
110
+ };
111
+
112
+ type Cell = {
113
+ route: Route; // Full route to this cell
114
+ value: JsonValue; // Current value
115
+ row: JsonValue; // Parent row data
116
+ path: Array<Array<string | number>>; // Path segments
117
+ };
118
+ ```
119
+
120
+ ### 2. Core Class
121
+
122
+ Low-level data management layer wrapping Io operations.
123
+
124
+ **Location**: `src/core.ts`
125
+
126
+ **Key Responsibilities**:
127
+
128
+ - Table creation and schema management
129
+ - Data import with validation
130
+ - Data export (dump operations)
131
+ - Reading/writing through Io layer
132
+
133
+ **Key Methods**:
134
+
135
+ ```typescript
136
+ class Core {
137
+ async createTableWithInsertHistory(tableCfg): Promise<void>
138
+ async createTable(tableCfg): Promise<void>
139
+ async import(data): Promise<void>
140
+ async dump(): Promise<Rljson>
141
+ async dumpTable(table): Promise<Rljson>
142
+ async tables(): Promise<Record<string, TableCfg>>
143
+ async readRows(params): Promise<Rljson>
144
+ async write(params): Promise<void>
145
+ }
146
+ ```
147
+
148
+ ### 3. Controller Pattern
149
+
150
+ Controllers abstract table-specific operations and provide a uniform interface.
151
+
152
+ **Base Controller** (`src/controller/base-controller.ts`):
153
+
154
+ ```typescript
155
+ abstract class Controller<N, R, T> {
156
+ abstract init(): Promise<void>
157
+ abstract get(where, filter?, path?): Promise<Rljson>
158
+ abstract insert(command, value, origin?, refs?): Promise<InsertHistoryRow[]>
159
+ abstract table(): Promise<T>
160
+ abstract tableCfg(): TableCfg
161
+ abstract getChildRefs(hash): Promise<Ref[]>
162
+ }
163
+ ```
164
+
165
+ **Specialized Controllers**:
166
+
167
+ #### TreeController
168
+
169
+ Handles hierarchical tree structures with conditional children expansion.
170
+
171
+ **Critical Fix**: The `path` parameter controls whether children are expanded:
172
+
173
+ - `path === undefined`: WHERE clause query → Returns only requested node
174
+ - `path !== undefined`: Route navigation → Expands children recursively
175
+
176
+ ```typescript
177
+ class TreeController extends Controller {
178
+ async get(where, filter?, path?): Promise<Rljson> {
179
+ // ... fetch tree node(s)
180
+
181
+ const shouldExpandChildren = path !== undefined;
182
+
183
+ if (!shouldExpandChildren) {
184
+ // Return only requested node (prevents heap crash)
185
+ return { [tableKey]: { _data: [tree], _type: 'trees' } };
186
+ }
187
+
188
+ // Expand children recursively
189
+ for (const childRef of tree.children ?? []) {
190
+ const child = await this.get(childRef, undefined, treeRoute.deeper().flat);
191
+ children.push(...child[tableKey]._data);
192
+ }
193
+
194
+ return { [tableKey]: { _data: [...children, tree], _type: 'trees' } };
195
+ }
196
+ }
197
+ ```
198
+
199
+ This design prevents infinite recursion when querying trees by hash while preserving navigation capabilities.
200
+
201
+ #### CakeController
202
+
203
+ Handles multi-dimensional cube data with slicing.
204
+
205
+ ```typescript
206
+ class CakeController extends Controller {
207
+ // Resolves sliceIds and components
208
+ // Handles dimension-based queries
209
+ }
210
+ ```
211
+
212
+ #### LayerController
213
+
214
+ Manages layered data with inheritance.
215
+
216
+ ```typescript
217
+ class LayerController extends Controller {
218
+ // Resolves base layers
219
+ // Handles layer composition
220
+ }
221
+ ```
222
+
223
+ ## Data Flow
224
+
225
+ ### Query Flow (db.get)
226
+
227
+ ```
228
+ 1. Parse Route
229
+ Route.fromFlat('/users/projects/tasks')
230
+
231
+ 2. Isolate Property Keys
232
+ Extract hash references from route segments
233
+
234
+ 3. Index Controllers
235
+ Create/cache controllers for each table in route
236
+
237
+ 4. Execute _get (Recursive)
238
+ For each route segment:
239
+ ├─ Fetch node data (via controller)
240
+ ├─ Apply filters
241
+ ├─ Resolve children (if not leaf)
242
+ ├─ Recurse to deeper route
243
+ └─ Merge results
244
+
245
+ 5. Build Container
246
+ rljson: { users: {...}, projects: {...}, tasks: {...} }
247
+ tree: nested structure
248
+ cell: path-value pairs
249
+
250
+ 6. Cache Result
251
+ Store in _cache map by route+where hash
252
+
253
+ 7. Return Container + Controllers
254
+ ```
255
+
256
+ ### Insert Flow (db.insert)
257
+
258
+ ```
259
+ 1. Validate Route
260
+
261
+ 2. Index Controllers
262
+
263
+ 3. Execute _insert (Recursive)
264
+ For each route segment:
265
+ ├─ Get target controller
266
+ ├─ Extract data for this level
267
+ ├─ Call controller.insert()
268
+ ├─ Get InsertHistoryRow
269
+ ├─ Recurse to children
270
+ └─ Collect history rows
271
+
272
+ 4. Notify Callbacks
273
+ Broadcast changes to registered listeners
274
+
275
+ 5. Return InsertHistoryRows
276
+ ```
277
+
278
+ ## Route Resolution
279
+
280
+ Routes define paths through related data. The route system supports:
281
+
282
+ ### Route Syntax
283
+
284
+ ```
285
+ /tableName # Root table
286
+ /tableName@hash # Specific row by hash
287
+ /tableName@timeId # Historic version
288
+ /tableName/childTable # Relationship traversal
289
+ /tableName@hash/childTable@hash2 # Nested navigation
290
+ ```
291
+
292
+ ### Route Segments
293
+
294
+ ```typescript
295
+ type RouteSegment = {
296
+ tableKey: string; // Table name
297
+ ref?: string; // Hash or timeId
298
+ propertyKey?: string; // For tree navigation
299
+ sliceIds?: SliceId[]; // Dimension filters
300
+ };
301
+ ```
302
+
303
+ ### Route Resolution Process
304
+
305
+ 1. **Parse**: Split flat route string into segments
306
+ 2. **Validate**: Check table existence and relationships
307
+ 3. **Resolve References**:
308
+ - Look up hash references in insert history
309
+ - Convert timeIds to current hashes
310
+ - Handle default refs (latest version)
311
+ 4. **Navigate**: Traverse from root to leaf segment
312
+
313
+ ### Example Resolution
314
+
315
+ Route: `/users@hash123/projects`
316
+
317
+ ```
318
+ Segment 1: users@hash123
319
+ ├─ Table: users
320
+ ├─ Ref: hash123
321
+ └─ Query: { _hash: 'hash123' }
322
+
323
+ Segment 2: projects
324
+ ├─ Table: projects
325
+ ├─ Parent: users (hash123)
326
+ └─ Query: WHERE projects references hash123
327
+ ```
328
+
329
+ ## Caching Strategy
330
+
331
+ ### Cache Key Generation
332
+
333
+ ```typescript
334
+ const cacheKey = hsh({
335
+ route: route.flat,
336
+ where: where,
337
+ filter: filter,
338
+ sliceIds: sliceIds,
339
+ routeAccumulator: routeAccumulator.flat,
340
+ options: options
341
+ })._hash;
342
+ ```
343
+
344
+ ### Cache Conditions
345
+
346
+ Caching is enabled when:
347
+
348
+ - Route contains hash references (`@hash`)
349
+ - Filters are applied
350
+ - SliceIds are specified
351
+
352
+ Caching is disabled for:
353
+
354
+ - Empty WHERE clauses
355
+ - Routes without references
356
+ - Time-based queries (to ensure freshness)
357
+
358
+ ### Cache Invalidation
359
+
360
+ - Manual: `db.clearCache()`
361
+ - Automatic: On insert operations (via notify callbacks)
362
+ - Size-based: LRU eviction (if implemented)
363
+
364
+ ## Tree Processing
365
+
366
+ ### Tree Structure
367
+
368
+ ```typescript
369
+ type Tree = {
370
+ id: string; // Node identifier
371
+ children: string[]; // Hash references to child nodes
372
+ meta: Json; // Node data
373
+ isParent: boolean; // Has children flag
374
+ _hash: string; // Content hash
375
+ };
376
+ ```
377
+
378
+ ### Tree Expansion Algorithm
379
+
380
+ **Problem**: Trees use content-addressed children (hash references). Naively expanding all children causes:
381
+
382
+ - Infinite recursion on circular refs
383
+ - Heap exhaustion on large trees
384
+ - O(n²) complexity on deep trees
385
+
386
+ **Solution**: Conditional expansion based on query context
387
+
388
+ ### Tree INSERT and Root Node Creation
389
+
390
+ **Problem**: The `treeFromObject` function (from `@rljson/rljson`) automatically creates an explicit root node with `id='root'`. During INSERT operations, if the tree object already represents an isolated subtree (e.g., from `isolate()`), this creates a double-root structure:
391
+ - Auto-root (id='root') → User-root (id='root') → actual data nodes
392
+
393
+ This causes navigation issues because `TreeController` stops at the first node matching `id='root'`.
394
+
395
+ **Solution**: The `treeFromObject` call in `db.ts` (line 1365) uses a `skipRootCreation` parameter:
396
+
397
+ ```typescript
398
+ const trees = treeFromObject(treeObject, true); // true = skip automatic root creation
399
+ ```
400
+
401
+ This prevents the extra root wrapper when inserting tree data, allowing the subtree to be inserted with its existing structure intact.
402
+
403
+ ```typescript
404
+ async get(where, filter?, path?): Promise<Rljson> {
405
+ // Fetch matching tree nodes
406
+ const trees = await this._core.readRows({...});
407
+
408
+ // Single node check
409
+ if (trees.length === 1) {
410
+ const tree = trees[0];
411
+
412
+ // KEY DECISION: Expand children only if navigating
413
+ const shouldExpandChildren = path !== undefined;
414
+
415
+ if (!shouldExpandChildren) {
416
+ // WHERE clause query - return only this node
417
+ return { [tableKey]: { _data: [tree], _type: 'trees' } };
418
+ }
419
+
420
+ // Route navigation - expand children
421
+ const children = [];
422
+ for (const childHash of tree.children ?? []) {
423
+ const child = await this.get(
424
+ childHash,
425
+ undefined,
426
+ treeRoute.deeper().flat // Pass path to trigger expansion
427
+ );
428
+ children.push(...child[tableKey]._data);
429
+ }
430
+
431
+ return { [tableKey]: { _data: [...children, tree], _type: 'trees' } };
432
+ }
433
+
434
+ // Multiple nodes or no nodes
435
+ return { [tableKey]: { _data: trees, _type: 'trees' } };
436
+ }
437
+ ```
438
+
439
+ ### Tree Memoization
440
+
441
+ The `buildTreeFromTrees` method uses memoization to avoid reprocessing nodes:
442
+
443
+ ```typescript
444
+ async buildTreeFromTrees(trees: Tree[]): Promise<Json> {
445
+ const memo = new Map<string, Json>();
446
+
447
+ const buildNode = async (hash: string): Promise<Json> => {
448
+ if (memo.has(hash)) return memo.get(hash)!;
449
+
450
+ const tree = trees.find(t => t._hash === hash);
451
+ if (!tree) throw new Error(`Tree node ${hash} not found`);
452
+
453
+ const node = { ...tree.meta };
454
+
455
+ if (tree.children && tree.children.length > 0) {
456
+ for (const childHash of tree.children) {
457
+ const childTree = trees.find(t => t._hash === childHash);
458
+ if (childTree) {
459
+ node[childTree.id] = await buildNode(childHash);
460
+ }
461
+ }
462
+ }
463
+
464
+ memo.set(hash, node);
465
+ return node;
466
+ };
467
+
468
+ // Build from root (last element)
469
+ return buildNode(trees[trees.length - 1]._hash);
470
+ }
471
+ ```
472
+
473
+ ### Safety Mechanisms
474
+
475
+ 1. **Recursion Depth Limit**: 100 levels max
476
+ 2. **Path-based Expansion**: Only expand when navigating
477
+ 3. **Memoization**: Prevent redundant processing
478
+ 4. **Early Returns**: Short-circuit on leaf nodes
479
+
480
+ ## Join System
481
+
482
+ The Join system provides SQL-like operations on query results.
483
+
484
+ **Location**: `src/join/join.ts`
485
+
486
+ ### Join Operations
487
+
488
+ ```typescript
489
+ class Join {
490
+ // Selection (columns)
491
+ select(columnSelection: ColumnSelection): void
492
+
493
+ // Filtering (rows)
494
+ filter(rowFilter: RowFilter): void
495
+
496
+ // Sorting
497
+ sort(rowSort: RowSort): void
498
+
499
+ // Value mutation
500
+ setValue(setValue: SetValue): void
501
+
502
+ // Result access
503
+ async rows(): Promise<JoinRows>
504
+ async columns(): Promise<JoinColumn[]>
505
+ }
506
+ ```
507
+
508
+ ### Join Processing Pipeline
509
+
510
+ ```
511
+ Container (rljson, tree, cell)
512
+
513
+ Initialize Join
514
+
515
+ ┌─────────────┐
516
+ │ Selection │ → Filter columns
517
+ └──────┬──────┘
518
+
519
+ ┌─────────────┐
520
+ │ Filter │ → Filter rows
521
+ └──────┬──────┘
522
+
523
+ ┌─────────────┐
524
+ │ Sort │ → Order rows
525
+ └──────┬──────┘
526
+
527
+ ┌─────────────┐
528
+ │ SetValue │ → Modify values
529
+ └──────┬──────┘
530
+
531
+ Result (JoinRows)
532
+ ```
533
+
534
+ ### Column Selection
535
+
536
+ ```typescript
537
+ class ColumnSelection {
538
+ constructor(
539
+ route: Route,
540
+ columns: ColumnInfo[],
541
+ selectionType: 'include' | 'exclude' = 'include'
542
+ )
543
+
544
+ process(rows: JoinRows): JoinRows
545
+ }
546
+ ```
547
+
548
+ ### Row Filtering
549
+
550
+ ```typescript
551
+ class RowFilter {
552
+ constructor(
553
+ route: Route,
554
+ filters: Record<string, FilterProcessor>
555
+ )
556
+
557
+ process(rows: JoinRows): JoinRows
558
+ }
559
+ ```
560
+
561
+ Supported filter types:
562
+
563
+ - String filters (equals, contains, startsWith, endsWith, regex)
564
+ - Number filters (equals, gt, gte, lt, lte, range)
565
+ - Boolean filters (equals, notEquals)
566
+ - Column filters (compare columns)
567
+
568
+ ## Multi-Edit System
569
+
570
+ The MultiEditManager provides transactional editing capabilities.
571
+
572
+ **Location**: `src/edit/multi-edit-manager.ts`
573
+
574
+ ### Architecture
575
+
576
+ ```
577
+ MultiEditManager
578
+ ├─ Tracks head MultiEditProcessor
579
+ ├─ Registers as Db notify callback
580
+ ├─ Manages edit history
581
+ └─ Publishes completed edits
582
+
583
+ MultiEditProcessor
584
+ ├─ Executes individual EditActions
585
+ ├─ Maintains intermediate state
586
+ ├─ Builds result Join
587
+ └─ Supports rollback
588
+ ```
589
+
590
+ ### Edit Actions
591
+
592
+ ```typescript
593
+ type EditAction = {
594
+ type: 'columnSelection' | 'rowFilter' | 'rowSort' | 'setValue';
595
+ params: {...};
596
+ };
597
+ ```
598
+
599
+ ### Transaction Flow
600
+
601
+ ```
602
+ 1. Create MultiEditManager
603
+
604
+ 2. Start multiEdit()
605
+ ├─ Get or create head processor
606
+ ├─ Execute edit callback
607
+ └─ Return updated head
608
+
609
+ 3. Apply EditActions
610
+ head.edit(action1)
611
+ head.edit(action2)
612
+
613
+ 4. Publish Head
614
+ manager.publishHead()
615
+ ├─ Get result Join
616
+ ├─ Insert into Db
617
+ └─ Notify observers
618
+ ```
619
+
620
+ ## Design Decisions
621
+
622
+ ### 1. Content-Addressed Immutability
623
+
624
+ **Decision**: All data is immutable and identified by content hash.
625
+
626
+ **Rationale**:
627
+
628
+ - Eliminates update conflicts
629
+ - Enables perfect caching
630
+ - Supports version history naturally
631
+ - Allows secure data sharing
632
+
633
+ **Trade-offs**:
634
+
635
+ - Higher storage requirements
636
+ - More complex mutation patterns
637
+ - Requires hash computation overhead
638
+
639
+ ### 2. Controller Abstraction
640
+
641
+ **Decision**: Each table type has a specialized controller.
642
+
643
+ **Rationale**:
644
+
645
+ - Encapsulates type-specific logic
646
+ - Enables polymorphic operations
647
+ - Simplifies testing and maintenance
648
+ - Supports future table types
649
+
650
+ **Trade-offs**:
651
+
652
+ - More complex class hierarchy
653
+ - Requires careful interface design
654
+
655
+ ### 3. Recursive Route Resolution
656
+
657
+ **Decision**: Routes are resolved recursively, segment by segment.
658
+
659
+ **Rationale**:
660
+
661
+ - Handles arbitrary nesting depth
662
+ - Natural fit for hierarchical data
663
+ - Enables lazy loading
664
+ - Supports circular references
665
+
666
+ **Trade-offs**:
667
+
668
+ - Risk of stack overflow (mitigated by depth limit)
669
+ - Complex debugging
670
+ - Performance overhead
671
+
672
+ ### 4. Path-Based Tree Expansion
673
+
674
+ **Decision**: TreeController uses `path` parameter to control expansion.
675
+
676
+ **Rationale**:
677
+
678
+ - Prevents heap crashes on large trees
679
+ - Preserves navigation functionality
680
+ - Simple, clear semantics
681
+ - Minimal API changes
682
+
683
+ **Trade-offs**:
684
+
685
+ - Subtle behavior difference between query types
686
+ - Requires understanding of path parameter
687
+ - Documentation burden
688
+
689
+ ### 5. Caching by Query Signature
690
+
691
+ **Decision**: Cache results by hash of route+where+filter+options.
692
+
693
+ **Rationale**:
694
+
695
+ - Optimal cache hit rate
696
+ - Automatic cache key generation
697
+ - No manual cache management
698
+ - Supports complex queries
699
+
700
+ **Trade-offs**:
701
+
702
+ - Memory growth on diverse queries
703
+ - No automatic invalidation
704
+ - Hash computation overhead
705
+
706
+ ### 6. Join System Design
707
+
708
+ **Decision**: Separate Join class for data transformation.
709
+
710
+ **Rationale**:
711
+
712
+ - Separation of concerns
713
+ - Composable operations
714
+ - Testable in isolation
715
+ - Reusable across contexts
716
+
717
+ **Trade-offs**:
718
+
719
+ - Additional abstraction layer
720
+ - More object creation
721
+ - Steeper learning curve
722
+
723
+ ## Performance Considerations
724
+
725
+ ### Query Optimization
726
+
727
+ 1. **Batch SliceId Resolution**: Resolve all sliceIds in parallel
728
+ 2. **Controller Memoization**: Cache controller instances
729
+ 3. **Result Caching**: Cache query results by signature
730
+ 4. **Lazy Loading**: Only fetch data as needed
731
+ 5. **Parallel Fetches**: Fetch independent data concurrently
732
+
733
+ ### Memory Management
734
+
735
+ 1. **Streaming**: Use streaming for large data exports
736
+ 2. **Pagination**: Support paginated queries (future)
737
+ 3. **Cache Eviction**: Implement LRU cache policy (future)
738
+ 4. **Weak References**: Use WeakMap for temporary data (future)
739
+
740
+ ### Recursion Safety
741
+
742
+ 1. **Depth Limiting**: Max 100 recursion levels
743
+ 2. **Path Checking**: Detect navigation context
744
+ 3. **Memoization**: Avoid redundant processing
745
+ 4. **Early Exits**: Short-circuit when possible
746
+
747
+ ## Testing Strategy
748
+
749
+ ### Unit Tests
750
+
751
+ - Controller operations (get, insert, table)
752
+ - Route parsing and validation
753
+ - Filter processing
754
+ - Cache behavior
755
+
756
+ ### Integration Tests
757
+
758
+ - Full query flows (db.get)
759
+ - Insert operations (db.insert)
760
+ - Version history
761
+ - Multi-edit transactions
762
+
763
+ ### Performance Tests
764
+
765
+ - Large tree queries
766
+ - Deep recursion
767
+ - Cache hit rates
768
+ - Memory usage
769
+
770
+ ### Critical Test Cases
771
+
772
+ ```typescript
773
+ describe('Tree WHERE clause fix', () => {
774
+ it('should return ONLY ONE NODE when querying by _hash');
775
+ it('should prevent heap crash with large trees');
776
+ it('should expand children when path is provided');
777
+ });
778
+ ```
779
+
780
+ ## Future Enhancements
781
+
782
+ ### Planned Features
783
+
784
+ 1. **Query Optimization**
785
+ - Query planner
786
+ - Index support
787
+ - Parallel execution
788
+
789
+ 2. **Advanced Caching**
790
+ - LRU eviction
791
+ - Cache warming
792
+ - Distributed caching
793
+
794
+ 3. **Pagination**
795
+ - Cursor-based pagination
796
+ - Offset-limit pagination
797
+ - Stream processing
798
+
799
+ 4. **Transactions**
800
+ - ACID guarantees
801
+ - Rollback support
802
+ - Conflict resolution
803
+
804
+ 5. **Replication**
805
+ - Master-slave replication
806
+ - Multi-master replication
807
+ - Conflict-free replicated data types (CRDTs)
808
+
809
+ ## Debugging Tips
810
+
811
+ ### Enable Query Logging
812
+
813
+ ```typescript
814
+ // Add to Db._get for debugging
815
+ console.log('Route:', route.flat);
816
+ console.log('Where:', JSON.stringify(where));
817
+ console.log('Depth:', depth);
818
+ ```
819
+
820
+ ### Trace Tree Expansion
821
+
822
+ ```typescript
823
+ // Add to TreeController.get
824
+ console.log(`Tree expansion: ${tree.id}, path=${path}`);
825
+ console.log(`Should expand: ${path !== undefined}`);
826
+ ```
827
+
828
+ ### Cache Analysis
829
+
830
+ ```typescript
831
+ console.log('Cache size:', db.cache.size);
832
+ console.log('Cache keys:', Array.from(db.cache.keys()));
833
+ ```
834
+
835
+ ### Route Inspection
836
+
837
+ ```typescript
838
+ console.log('Route segments:', route.segments);
839
+ console.log('Property key:', route.propertyKey);
840
+ console.log('Is valid:', route.isValid);
841
+ ```
842
+
843
+ ## Contributing
844
+
845
+ When contributing to @rljson/db:
846
+
847
+ 1. **Understand the layers**: Know which layer owns which responsibility
848
+ 2. **Preserve immutability**: Never modify data in place
849
+ 3. **Test tree operations**: Always test with large/deep trees
850
+ 4. **Document behavior**: Explain non-obvious design choices
851
+ 5. **Benchmark changes**: Profile performance-critical paths
852
+ 6. **Update tests**: Add tests for new features and bug fixes
853
+
854
+ ## References
855
+
856
+ - [RLJSON Specification](https://github.com/rljson/rljson)
857
+ - [Content-Addressed Storage](https://en.wikipedia.org/wiki/Content-addressable_storage)
858
+ - [Merkle Trees](https://en.wikipedia.org/wiki/Merkle_tree)
859
+ - [Immutable Data Structures](https://en.wikipedia.org/wiki/Persistent_data_structure)