jexidb 2.0.3 → 2.1.1

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.
Files changed (79) hide show
  1. package/.babelrc +13 -0
  2. package/.gitattributes +2 -0
  3. package/CHANGELOG.md +132 -101
  4. package/LICENSE +21 -21
  5. package/README.md +301 -639
  6. package/babel.config.json +5 -0
  7. package/dist/Database.cjs +5204 -0
  8. package/docs/API.md +908 -241
  9. package/docs/EXAMPLES.md +701 -177
  10. package/docs/README.md +194 -184
  11. package/examples/iterate-usage-example.js +157 -0
  12. package/examples/simple-iterate-example.js +115 -0
  13. package/jest.config.js +24 -0
  14. package/package.json +63 -54
  15. package/scripts/README.md +47 -0
  16. package/scripts/benchmark-array-serialization.js +108 -0
  17. package/scripts/clean-test-files.js +75 -0
  18. package/scripts/prepare.js +31 -0
  19. package/scripts/run-tests.js +80 -0
  20. package/scripts/score-mode-demo.js +45 -0
  21. package/src/Database.mjs +5325 -0
  22. package/src/FileHandler.mjs +1140 -0
  23. package/src/OperationQueue.mjs +279 -0
  24. package/src/SchemaManager.mjs +268 -0
  25. package/src/Serializer.mjs +702 -0
  26. package/src/managers/ConcurrencyManager.mjs +257 -0
  27. package/src/managers/IndexManager.mjs +2094 -0
  28. package/src/managers/QueryManager.mjs +1490 -0
  29. package/src/managers/StatisticsManager.mjs +262 -0
  30. package/src/managers/StreamingProcessor.mjs +429 -0
  31. package/src/managers/TermManager.mjs +278 -0
  32. package/src/utils/operatorNormalizer.mjs +116 -0
  33. package/test/$not-operator-with-and.test.js +282 -0
  34. package/test/README.md +8 -0
  35. package/test/close-init-cycle.test.js +256 -0
  36. package/test/coverage-method.test.js +93 -0
  37. package/test/critical-bugs-fixes.test.js +1069 -0
  38. package/test/deserialize-corruption-fixes.test.js +296 -0
  39. package/test/exists-method.test.js +318 -0
  40. package/test/explicit-indexes-comparison.test.js +219 -0
  41. package/test/filehandler-non-adjacent-ranges-bug.test.js +175 -0
  42. package/test/index-line-number-regression.test.js +100 -0
  43. package/test/index-missing-index-data.test.js +91 -0
  44. package/test/index-persistence.test.js +491 -0
  45. package/test/index-serialization.test.js +314 -0
  46. package/test/indexed-query-mode.test.js +360 -0
  47. package/test/insert-session-auto-flush.test.js +353 -0
  48. package/test/iterate-method.test.js +272 -0
  49. package/test/legacy-operator-compat.test.js +154 -0
  50. package/test/query-operators.test.js +238 -0
  51. package/test/regex-array-fields.test.js +129 -0
  52. package/test/score-method.test.js +298 -0
  53. package/test/setup.js +17 -0
  54. package/test/term-mapping-minimal.test.js +154 -0
  55. package/test/term-mapping-simple.test.js +257 -0
  56. package/test/term-mapping.test.js +514 -0
  57. package/test/writebuffer-flush-resilience.test.js +204 -0
  58. package/dist/FileHandler.js +0 -688
  59. package/dist/IndexManager.js +0 -353
  60. package/dist/IntegrityChecker.js +0 -364
  61. package/dist/JSONLDatabase.js +0 -1333
  62. package/dist/index.js +0 -617
  63. package/docs/MIGRATION.md +0 -295
  64. package/examples/auto-save-example.js +0 -158
  65. package/examples/cjs-usage.cjs +0 -82
  66. package/examples/close-vs-delete-example.js +0 -71
  67. package/examples/esm-usage.js +0 -113
  68. package/examples/example-columns.idx.jdb +0 -0
  69. package/examples/example-columns.jdb +0 -9
  70. package/examples/example-options.idx.jdb +0 -0
  71. package/examples/example-options.jdb +0 -0
  72. package/examples/example-users.idx.jdb +0 -0
  73. package/examples/example-users.jdb +0 -5
  74. package/examples/simple-test.js +0 -55
  75. package/src/FileHandler.js +0 -674
  76. package/src/IndexManager.js +0 -363
  77. package/src/IntegrityChecker.js +0 -379
  78. package/src/JSONLDatabase.js +0 -1391
  79. package/src/index.js +0 -608
package/README.md CHANGED
@@ -1,639 +1,301 @@
1
- # JexiDB - Pure JavaScript JSONL Database
2
-
3
- **JexiDB** is a lightweight, high-performance JSONL (JSON Lines) database for Node.js built in pure JavaScript that provides fast data storage and retrieval with persistent indexing.
4
-
5
- ## 🚀 Features
6
-
7
- - **JSONL Architecture**: Each database is a single JSONL file for simplicity and portability
8
- - **Persistent Indexes**: Fast searches with disk-persisted indexes that don't need rebuilding
9
- - **Point Reading**: Efficient memory usage - only reads necessary data
10
- - **Rich Query API**: Support for complex queries with operators, sorting, and pagination
11
- - **Intelligent Auto-Save**: Automatic data persistence with configurable thresholds and intervals
12
- - **Event-Driven Monitoring**: Real-time notifications for all database operations
13
- - **Performance Optimization**: Adaptive batch sizes and memory management
14
- - **Automatic Integrity Validation**: Built-in data integrity checking and repair
15
- - **Legacy Compatibility**: Automatic migration from JexiDB 1.x databases
16
- - **Pure JavaScript**: No native dependencies, works everywhere, easy to deploy
17
-
18
- ## 📦 Installation
19
-
20
- ```bash
21
- npm install jexidb
22
- ```
23
-
24
- ## 🚀 Quick Start
25
-
26
- ### ESM
27
-
28
- ```javascript
29
- import Database from 'jexidb';
30
-
31
- // Prefer default import aliased as Database for clarity
32
- const db = new Database('./users.jdb', {
33
- indexes: { id: 'number', email: 'string', age: 'number' },
34
- autoSave: true,
35
- validateOnInit: true
36
- });
37
-
38
- await db.init();
39
-
40
- // Event listeners
41
- db.on('insert', (record, index) => console.log(`Record inserted at index ${index}`));
42
- db.on('update', (record, index) => console.log(`Record updated at index ${index}`));
43
- db.on('save', () => console.log('Changes saved'));
44
-
45
- // Insert data
46
- await db.insert({ id: 1, name: 'John Doe', email: 'john@example.com', age: 30 });
47
-
48
- // Search data (both methods work)
49
- const john = await db.findOne({ id: 1 });
50
- const youngUsers = await db.find({ age: { '<': 30 } });
51
-
52
- // JexiDB 1.x compatible query
53
- const results = await db.query({ name: 'john doe' }, { caseInsensitive: true });
54
-
55
- // Update / Delete / Save
56
- await db.update({ id: 1 }, { age: 31 });
57
- await db.delete({ id: 1 });
58
- await db.save();
59
- await db.close(); // or await db.destroy()
60
- ```
61
-
62
- ### CommonJS
63
-
64
- ```javascript
65
- const Database = require('jexidb');
66
- // Alternatively (backward compatible): const { Database } = require('jexidb');
67
-
68
- const db = new Database('./users.jdb', {
69
- indexes: { id: 'number', email: 'string', age: 'number' }
70
- });
71
-
72
- (async () => {
73
- await db.init();
74
- await db.insert({ id: 1, name: 'John' });
75
- console.log(await db.findOne({ id: 1 }));
76
- await db.close(); // or await db.destroy()
77
- })();
78
- ```
79
-
80
- ## 📚 API Reference
81
-
82
- ### Constructor
83
-
84
- ```javascript
85
- const db = new Database(filePath, options);
86
- ```
87
-
88
- **Parameters:**
89
- - `filePath` (string): Path to the main file (.jdb)
90
- - `options` (object): Configuration options
91
-
92
- **Options:**
93
- ```javascript
94
- {
95
- // Core options
96
- indexes: {}, // Indexes for fields
97
- create: true, // Create database if it doesn't exist
98
- clear: false, // Clear database on load
99
-
100
- // Auto-save configuration
101
- autoSave: true, // Enable intelligent auto-save (default: true)
102
- autoSaveThreshold: 50, // Flush buffer when it reaches this many records
103
- autoSaveInterval: 5000, // Flush buffer every N milliseconds
104
- forceSaveOnClose: true, // Always save when closing database
105
-
106
- // Performance configuration
107
- batchSize: 50, // Batch size for inserts (reduced for faster response)
108
- adaptiveBatchSize: true, // Adjust batch size based on usage
109
- minBatchSize: 10, // Minimum batch size for flush
110
- maxBatchSize: 200 // Maximum batch size for performance
111
- }
112
- ```
113
-
114
- ### Auto-Save Intelligence
115
-
116
- JexiDB features intelligent auto-save capabilities that automatically manage data persistence without manual intervention.
117
-
118
- #### Auto-Save Modes
119
-
120
- **Intelligent Auto-Save (Default):**
121
- - Automatically flushes buffer when it reaches the threshold (default: 50 records)
122
- - Automatically flushes buffer every N milliseconds (default: 5000ms)
123
- - Always saves when closing the database
124
- - Provides real-time feedback through events
125
-
126
- **Manual Mode:**
127
- - Disable auto-save with `autoSave: false`
128
- - Manually call `flush()` and `save()` when needed
129
- - Useful for applications requiring precise control over persistence timing
130
-
131
- #### Event-Driven Monitoring
132
-
133
- ```javascript
134
- // Monitor auto-save operations
135
- db.on('buffer-flush', (count) => {
136
- console.log(`Flushed ${count} records`);
137
- });
138
-
139
- db.on('buffer-full', () => {
140
- console.log('Buffer reached threshold');
141
- });
142
-
143
- db.on('auto-save-timer', () => {
144
- console.log('Time-based auto-save triggered');
145
- });
146
-
147
- db.on('save-complete', () => {
148
- console.log('Database saved successfully');
149
- });
150
- ```
151
-
152
- #### Buffer Status Monitoring
153
-
154
- ```javascript
155
- // Check buffer status anytime
156
- const status = db.getBufferStatus();
157
- console.log(`Pending: ${status.pendingCount}/${status.bufferSize}`);
158
- console.log(`Should flush: ${status.shouldFlush}`);
159
- console.log(`Auto-save enabled: ${status.autoSaveEnabled}`);
160
- ```
161
-
162
- ### Main Methods
163
-
164
- #### `init()`
165
- Initializes the database.
166
-
167
- #### `insert(data)`
168
- Inserts a record.
169
-
170
- #### `insertMany(dataArray)`
171
- Inserts multiple records.
172
-
173
- #### `find(criteria, options)` / `query(criteria, options)`
174
- Searches records with optional criteria. Both methods work identically.
175
-
176
- **Supported operators:**
177
- ```javascript
178
- // Comparison
179
- { age: { '>': 25 } }
180
- { age: { '>=': 25 } }
181
- { age: { '<': 30 } }
182
- { age: { '<=': 30 } }
183
- { age: { '!=': 25 } }
184
-
185
- // Arrays
186
- { tags: { in: ['developer', 'admin'] } }
187
- { tags: { nin: ['designer'] } }
188
-
189
- // Strings
190
- { name: { regex: 'john' } }
191
- { name: { contains: 'john' } }
192
- ```
193
-
194
- **Options:**
195
- ```javascript
196
- {
197
- limit: 10, // Limit results
198
- skip: 5, // Skip records
199
- sort: { age: 1 }, // Sorting (1 = ascending, -1 = descending)
200
- caseInsensitive: false, // Case insensitive matching (query() only)
201
- matchAny: false // OR instead of AND
202
- }
203
- ```
204
-
205
- **JexiDB 1.x Compatibility:**
206
- ```javascript
207
- // Both work identically
208
- const results1 = await db.find({ name: 'John' });
209
- const results2 = await db.query({ name: 'John' });
210
-
211
- // Case insensitive query (JexiDB 1.x style)
212
- const results = await db.query({ name: 'john' }, { caseInsensitive: true });
213
- ```
214
-
215
- #### `findOne(criteria, options)`
216
- Searches for one record.
217
-
218
- #### `update(criteria, updateData, options)`
219
- Updates records.
220
-
221
- #### `delete(criteria, options)`
222
- Removes records.
223
-
224
- **Delete options:**
225
- ```javascript
226
- {
227
- physical: false, // Physically remove instead of marking as deleted
228
- limit: 1 // Limit number of records to delete
229
- }
230
- ```
231
-
232
- #### `count(criteria, options)`
233
- Counts records.
234
-
235
- #### `save()`
236
- Saves pending changes.
237
-
238
- #### `flush()`
239
- Flushes the insertion buffer to disk immediately.
240
-
241
- #### `forceSave()`
242
- Forces a save operation regardless of buffer size.
243
-
244
- #### `getBufferStatus()`
245
- Gets information about the current buffer state.
246
-
247
- #### `configurePerformance(settings)`
248
- Dynamically configures performance settings.
249
-
250
- #### `getPerformanceConfig()`
251
- Gets current performance configuration.
252
-
253
- #### `close()`
254
- Closes the database instance and saves pending changes.
255
-
256
- #### `destroy()`
257
- Closes the database instance and saves pending changes (equivalent to close()).
258
-
259
- ```javascript
260
- await db.destroy() // Same as: await db.close()
261
- ```
262
-
263
- #### `deleteDatabase()`
264
- **⚠️ WARNING: This permanently deletes the database file!**
265
-
266
- Deletes the database file from disk and closes the instance.
267
-
268
- ```javascript
269
- await db.deleteDatabase() // Deletes the database file permanently
270
- ```
271
-
272
- #### `removeDatabase()`
273
- Removes the database file from disk (alias for deleteDatabase).
274
-
275
- ```javascript
276
- await db.removeDatabase() // Same as: await db.deleteDatabase()
277
- ```
278
-
279
- #### `validateIntegrity(options)`
280
- Validates database integrity.
281
-
282
- #### `rebuildIndexes(options)`
283
- Rebuilds indexes.
284
-
285
- #### `getStats()`
286
- Gets detailed statistics.
287
-
288
- ### `walk()` Iterator
289
-
290
- For traversing large volumes of data:
291
-
292
- ```javascript
293
- // Traverse all records
294
- for await (const record of db.walk()) {
295
- console.log(record.name);
296
- }
297
-
298
- // With options
299
- for await (const record of db.walk({
300
- limit: 100,
301
- skip: 50,
302
- includeDeleted: false
303
- })) {
304
- console.log(record.name);
305
- }
306
- ```
307
-
308
- ### Properties
309
-
310
- #### `length`
311
- Total number of records.
312
-
313
- #### `indexStats`
314
- Index statistics.
315
-
316
- ### Events
317
-
318
- ```javascript
319
- // Core events
320
- db.on('init', () => console.log('Database initialized'));
321
- db.on('insert', (record, index) => console.log('Record inserted'));
322
- db.on('update', (record, index) => console.log('Record updated'));
323
- db.on('delete', (record, index) => console.log('Record deleted'));
324
- db.on('before-save', () => console.log('Before save'));
325
- db.on('save', () => console.log('Save completed'));
326
- db.on('close', () => console.log('Database closed'));
327
- db.on('destroy', () => console.log('Database destroyed'));
328
- db.on('delete-database', () => console.log('Database file deleted'));
329
-
330
- // Auto-save events
331
- db.on('buffer-flush', (count) => console.log(`Buffer flushed: ${count} records`));
332
- db.on('buffer-full', () => console.log('Buffer reached threshold'));
333
- db.on('auto-save-timer', () => console.log('Time-based auto-save triggered'));
334
- db.on('save-complete', () => console.log('Save operation completed'));
335
- db.on('close-save-complete', () => console.log('Database closed with final save'));
336
- db.on('close', () => console.log('Database closed'));
337
- db.on('performance-configured', (config) => console.log('Performance reconfigured'));
338
- ```
339
-
340
- ## 📁 File Structure
341
-
342
- For each database, 2 files are created:
343
-
344
- ```
345
- users.jdb # Data (JSON Lines format)
346
- users.idx.jdb # Compressed persistent indexes
347
- ```
348
-
349
- ### 🔄 Legacy Compatibility
350
-
351
- JexiDB automatically detects and migrates JexiDB 1.x files:
352
-
353
- **Legacy Format (JexiDB 1.x):**
354
- ```
355
- users.jsonl # Data + indexes + offsets in single file
356
- ```
357
-
358
- **New Format (JexiDB):**
359
- ```
360
- users.jdb # Data + offsets
361
- users.idx.jdb # Compressed indexes
362
- ```
363
-
364
-
365
-
366
- ### 🚀 Persistent Indexes
367
-
368
- JexiDB implements **persistent indexes** that are saved to disk:
369
-
370
- **Benefits:**
371
- - **Fast startup**: No need to read all data to rebuild indexes
372
- - **Scalable**: Works well with large databases (100k+ records)
373
- - **Consistent**: Indexes synchronized with data
374
- - **Portable**: Only 2 files to manage
375
- - **Compressed**: Indexes compressed using gzip
376
-
377
- **🔧 How it works:**
378
- 1. **First open**: Indexes are built by reading data
379
- 2. **Save**: Indexes are persisted and compressed to `users.idx.jdb`
380
- 3. **Reopen**: Indexes are loaded instantly from disk
381
- 4. **Fallback**: If index file is corrupted, rebuilds automatically
382
-
383
- ### JSONL Format
384
-
385
- Each line is a valid JSON record:
386
-
387
- ```json
388
- {"id":1,"name":"John","email":"john@example.com","_created":"2024-12-19T10:00:00.000Z","_updated":"2024-12-19T10:00:00.000Z"}
389
- {"id":2,"name":"Jane","email":"jane@example.com","_created":"2024-12-19T10:01:00.000Z","_updated":"2024-12-19T10:01:00.000Z"}
390
- ```
391
-
392
- ## 🔍 Advanced Examples
393
-
394
- ### Complex Search
395
-
396
- ```javascript
397
- // Young users from New York who are developers
398
- const users = await db.find({
399
- age: { '<': 30 },
400
- 'profile.city': 'New York',
401
- tags: { in: ['developer'] }
402
- }, {
403
- sort: { age: 1 },
404
- limit: 10
405
- });
406
- ```
407
-
408
- ### Batch Update
409
-
410
- ```javascript
411
- // Update age of all users from a city
412
- const updated = await db.update(
413
- { 'profile.city': 'New York' },
414
- { 'profile.country': 'USA' }
415
- );
416
- ```
417
-
418
- ### Integrity Validation
419
-
420
- ```javascript
421
- // Validate integrity with details
422
- const integrity = await db.validateIntegrity({
423
- checkData: true,
424
- checkIndexes: true,
425
- checkOffsets: true,
426
- verbose: true
427
- });
428
-
429
- if (!integrity.isValid) {
430
- console.log('Errors:', integrity.errors);
431
- console.log('Warnings:', integrity.warnings);
432
- }
433
- ```
434
-
435
- ### Detailed Statistics
436
-
437
- ```javascript
438
- const stats = await db.getStats();
439
- console.log('File size:', stats.file.size);
440
- console.log('Total records:', stats.summary.totalRecords);
441
- console.log('Indexes:', stats.indexes.indexCount);
442
- ```
443
-
444
- ## 🧪 Tests
445
-
446
- ```bash
447
- npm test
448
- ```
449
-
450
- **Automatic Cleanup**: The test script automatically removes all test files after execution to keep the project directory clean.
451
-
452
- **Manual Cleanup**: If you need to clean up test files manually:
453
- ```bash
454
- npm run test:clean
455
- ```
456
-
457
- **Available Test Scripts**:
458
- - `npm test` - Run all tests with automatic cleanup
459
- - `npm run test:watch` - Run tests in watch mode
460
- - `npm run test:clean` - Clean up test files manually
461
- - `npm run test:optimized` - Run optimized performance tests
462
- - `npm run test:parallel` - Run tests in parallel
463
- - `npm run test:fast` - Run fast tests without isolation
464
-
465
- ## 📈 Performance
466
-
467
- ### JSONL Features
468
-
469
- - **Point reading**: Only reads necessary lines
470
- - **In-memory indexes**: Fast search by indexed fields
471
- - **No complete parsing**: Doesn't load entire file into memory
472
- - **Large volume support**: Scales with millions of records
473
-
474
- ### Comparison: JexiDB vs 1.x
475
-
476
- | Feature | JexiDB | JexiDB 1.x |
477
- |---------|---------------|------------|
478
- | Safe truncation | ✅ | ❌ |
479
- | Consistent offsets | ✅ | ❌ |
480
- | Integrity validation | ✅ | ❌ |
481
- | Isolated tests | ✅ | ❌ |
482
- | No V8 dependency | ✅ | ❌ |
483
- | Similar API | ✅ | ✅ |
484
-
485
- ## 🔧 Utilities
486
-
487
- ```javascript
488
- const { utils } = require('jexidb');
489
-
490
- // Validate JSONL file
491
- const validation = await utils.validateJSONLFile('./data.jsonl');
492
-
493
- // Convert JSON to JSONL (basic)
494
- await utils.convertJSONToJSONL('./data.json', './data.jsonl');
495
-
496
- // Convert JSONL to JSON
497
- await utils.convertJSONLToJSON('./data.jsonl', './data.json');
498
-
499
- // Create JexiDB database with automatic indexes
500
- const result = await utils.createDatabaseFromJSON('./users.json', './users.jsonl', {
501
- autoDetectIndexes: true,
502
- autoIndexFields: ['id', 'email', 'name', 'username']
503
- });
504
-
505
- // Analyze JSON and suggest optimal indexes
506
- const analysis = await utils.analyzeJSONForIndexes('./users.json', 100);
507
- console.log('Recommended indexes:', analysis.suggestions.recommended);
508
-
509
- // Migrate from JexiDB 1.x to JexiDB
510
- await utils.migrateFromJexiDB('./jexidb-v1-database', './users.jsonl');
511
- ```
512
-
513
- ### 🔍 **How Utilities Work**
514
-
515
- #### **1. Basic Conversion (No Indexes)**
516
- ```javascript
517
- // Only converts format - DOES NOT add indexes
518
- await utils.convertJSONToJSONL('./data.json', './data.jsonl');
519
- ```
520
- - ✅ Converts JSON to JSONL
521
- - ❌ **DOES NOT create indexes**
522
- - ❌ **DOES NOT create JexiDB database**
523
- - ✅ Pure JSONL file
524
-
525
- #### **2. Database Creation with Automatic Indexes**
526
- ```javascript
527
- // Create complete JexiDB database with indexes
528
- const result = await utils.createDatabaseFromJSON('./users.json', './users.jsonl', {
529
- autoDetectIndexes: true,
530
- autoIndexFields: ['id', 'email', 'name']
531
- });
532
-
533
- console.log(result);
534
- // {
535
- // success: true,
536
- // recordCount: 1000,
537
- // indexes: ['id', 'email', 'name'],
538
- // dbPath: './users.jsonl'
539
- // }
540
- ```
541
- - ✅ Converts JSON to JSONL
542
- - ✅ **Creates indexes automatically**
543
- - ✅ **Creates complete JexiDB database**
544
- - ✅ File ready for use
545
-
546
- #### **3. Intelligent Index Analysis**
547
- ```javascript
548
- // Analyze data and suggest optimal indexes
549
- const analysis = await utils.analyzeJSONForIndexes('./users.json');
550
-
551
- console.log('Recommended:', analysis.suggestions.recommended);
552
- // [
553
- // { field: 'id', type: 'number', coverage: 100, uniqueness: 100 },
554
- // { field: 'email', type: 'string', coverage: 95, uniqueness: 98 }
555
- // ]
556
- ```
557
-
558
-
559
-
560
- ## 🔄 Migration from JexiDB 1.x
561
-
562
- ### Seamless Migration
563
-
564
- JexiDB is **fully backward compatible** with JexiDB 1.x! You can use the same API:
565
-
566
- ```javascript
567
- // JexiDB 1.x code works unchanged in JexiDB
568
- import { Database } from 'jexidb';
569
-
570
- const db = new Database('./database.jdb', {
571
- indexes: { id: 'number', name: 'string' }
572
- });
573
- await db.init();
574
-
575
- // All JexiDB 1.x methods work:
576
- await db.insert({ id: 1, name: 'John Doe' });
577
- const results = await db.query({ name: 'John Doe' }, { caseInsensitive: true });
578
- await db.update({ id: 1 }, { name: 'John Smith' });
579
- await db.delete({ id: 1 });
580
- await db.save();
581
- ```
582
-
583
- ### File Format Support
584
-
585
- JexiDB supports both file formats:
586
- - **`.jdb`** (preferred) - JexiDB's branded extension
587
- - **`.jsonl`** (standard) - JSON Lines format
588
-
589
- ```javascript
590
- // Both work identically:
591
- const db1 = new Database('./users.jdb', { indexes: { id: 'number' } });
592
- const db2 = new Database('./users.jsonl', { indexes: { id: 'number' } });
593
- ```
594
-
595
- ### Key Improvements
596
-
597
- | Feature | JexiDB 1.x | JexiDB |
598
- |---------|------------|--------------|
599
- | **API Compatibility** | Original | ✅ **100% Backward Compatible** |
600
- | **Query Methods** | `db.query()` | ✅ `db.query()` + `db.find()` |
601
- | **File Format** | `.jdb` (proprietary) | ✅ `.jdb` + `.jsonl` support |
602
- | **Performance** | Basic | ✅ **10-100x faster** |
603
- | **Memory Usage** | Higher | ✅ **25% less memory** |
604
- | **Data Integrity** | Basic | ✅ **Advanced validation** |
605
-
606
- ## 📝 Changelog
607
-
608
- See [CHANGELOG.md](CHANGELOG.md) for complete change history.
609
-
610
- ## 🤝 Contributing
611
-
612
- 1. Fork the project
613
- 2. Create a branch for your feature
614
- 3. Commit your changes
615
- 4. Push to the branch
616
- 5. Open a Pull Request
617
-
618
- ## 📄 License
619
-
620
- MIT License - see [LICENSE](LICENSE) for details.
621
-
622
- ---
623
-
624
- ## 🎯 About JexiDB
625
-
626
- JexiDB maintains the original JexiDB philosophy while fixing bugs and implementing a more robust architecture.
627
-
628
- ### 🚀 Performance
629
-
630
- **JexiDB** performance compared to version 1.x:
631
-
632
- - **Find operations**: 103x faster
633
- - **Update operations**: 26x faster
634
- - **Insert operations**: 6-11x faster
635
- - **Memory usage**: 25% less memory
636
-
637
- <p align="center">
638
- <img width="420" src="https://edenware.app/jexidb/images/jexi-mascot.webp" alt="JexiDB mascot" title="JexiDB mascot" />
639
- </p>
1
+ # JexiDB
2
+
3
+ <p align="center">
4
+ <img width="270" src="https://edenware.app/jexidb/images/jexidb-logo-icon.jpg" alt="JexiDB logo" title="JexiDB logo" />
5
+ </p>
6
+
7
+ ## Overview
8
+
9
+ JexiDB is a high-performance, in-memory database with persistence capabilities. It provides advanced indexing, querying, and term mapping features for efficient data operations. Ideal for local Node.js projects as well as apps built with Electron or NW.js. Written in pure JavaScript, it requires no compilation and is compatible with both CommonJS and ESM modules.
10
+
11
+ **Key Features:**
12
+ - In-memory storage with optional persistence
13
+ - Advanced indexing and querying capabilities
14
+ - Term mapping for efficient storage and querying
15
+ - Bulk update operations with `iterate()` method
16
+ - Manual save functionality (auto-save removed for better control)
17
+ - Transaction support with operation queuing
18
+ - Recovery mechanisms for data integrity
19
+
20
+ ## Installation
21
+
22
+ To install JexiDB, you can use npm:
23
+
24
+ ```bash
25
+ npm install EdenwareApps/jexidb
26
+ ```
27
+
28
+ ## Usage
29
+
30
+ ### Creating a Database Instance
31
+
32
+ To create a new instance of the database, you need to provide a file path where the database will be stored and an optional configuration object for indexes.
33
+
34
+ ```javascript
35
+ // const { Database } = require('jexidb'); // commonjs
36
+
37
+ import { Database } from 'jexidb'; // ESM
38
+
39
+ const db = new Database('path/to/database.jdb', {
40
+ create: true, // Create file if doesn't exist (default: true)
41
+ clear: false, // Clear existing files before loading (default: false)
42
+
43
+ // REQUIRED - Define your schema structure
44
+ fields: {
45
+ id: 'number',
46
+ name: 'string',
47
+ email: 'string',
48
+ tags: 'array:string'
49
+ },
50
+
51
+ // OPTIONAL - Performance optimization (only fields you query frequently)
52
+ indexes: {
53
+ name: 'string', // Search by name
54
+ tags: 'array:string' // ✅ Search by tags
55
+ }
56
+
57
+ // termMapping is now auto-enabled for array:string fields
58
+ });
59
+ ```
60
+ #### Constructor Options
61
+
62
+ - **create** (boolean, default: `true`): Controls whether the database file should be created if it doesn't exist.
63
+ - `true`: Creates the file if it doesn't exist (default behavior)
64
+ - `false`: Throws an error if the file doesn't exist
65
+
66
+ - **clear** (boolean, default: `false`): Controls whether to clear existing database files before loading.
67
+ - `true`: Deletes both the main database file (.jdb) and index file (.idx.jdb) if they exist, then starts with a clean database
68
+ - `false`: Loads existing data from files (default behavior)
69
+
70
+ You can [learn a bit more about these options at this link](https://github.com/EdenwareApps/jexidb/tree/main/test#readme).
71
+
72
+
73
+ ### Initializing the Database
74
+
75
+ Before using the database, you need to initialize it. This will load the existing data and indexes from the file.
76
+
77
+ ```javascript
78
+ await db.init();
79
+ ```
80
+ Only the values ​​specified as indexes are kept in memory for faster queries. JexiDB will never load the entire file into memory.
81
+
82
+
83
+ ### Inserting Data
84
+
85
+ You can insert data into the database by using the `insert` method. The data should be an object that contains the defined indexes. All object values will be saved into database.
86
+
87
+ ```javascript
88
+ await db.insert({ id: 1, name: 'John Doe' });
89
+ await db.insert({ id: 2, name: 'Jane Doe', anyArbitraryField: '1' });
90
+ ```
91
+
92
+ ### Querying Data
93
+
94
+ The `query` method allows you to retrieve data based on specific criteria. You can specify criteria for multiple fields.
95
+
96
+ ```javascript
97
+ const results = await db.query({ name: 'John Doe' }, { caseInsensitive: true });
98
+ console.log(results); // [{ id: 1, name: 'John Doe' }]
99
+ ```
100
+
101
+ Note: For now the query should be limited to using the fields specified as 'indexes' when instantiating the class.
102
+
103
+ #### Querying with Conditions
104
+
105
+ You can use conditions to perform more complex queries:
106
+
107
+ ```javascript
108
+ const results = await db.query({ id: { '>': 1 } });
109
+ console.log(results); // [{ id: 2, name: 'Jane Doe' }]
110
+ ```
111
+
112
+ ### Updating Data
113
+
114
+ To update existing records, use the `update` method with the criteria to find the records and the new data.
115
+
116
+ ```javascript
117
+ await db.update({ id: 1 }, { name: 'John Smith' });
118
+ ```
119
+
120
+ ### Deleting Data
121
+
122
+ You can delete records that match certain criteria using the `delete` method.
123
+
124
+ ```javascript
125
+ const deletedCount = await db.delete({ name: 'Jane Doe' });
126
+ console.log(`Deleted ${deletedCount} record(s).`);
127
+ ```
128
+
129
+ ### Iterating Through Records
130
+
131
+ You can iterate through records in the database using the `walk` method, which returns an async generator.
132
+
133
+ ```javascript
134
+ for await (const record of db.walk()) {
135
+ console.log(record);
136
+ }
137
+ ```
138
+
139
+ ### Saving Changes
140
+
141
+ After making any changes to the database, you need to save them using the `save` method. This will persist the changes to disk.
142
+
143
+ ```javascript
144
+ await db.save();
145
+ ```
146
+
147
+ ## Testing
148
+
149
+ JexiDB includes a comprehensive test suite built with Jest. To run the tests:
150
+
151
+ ```bash
152
+ # Run all tests
153
+ npm test
154
+
155
+ # Run tests in watch mode (for development)
156
+ npm test:watch
157
+
158
+ # Run tests with coverage report
159
+ npm test:coverage
160
+
161
+ # Run legacy test suite (original Mortal Kombat themed tests)
162
+ npm run test:legacy
163
+ ```
164
+
165
+ The test suite includes:
166
+ - **Database Tests**: Complete CRUD operations, querying, indexing, and persistence
167
+ - **IndexManager Tests**: Index creation, querying with various operators, and data management
168
+ - **Serializer Tests**: JSON serialization/deserialization with various data types
169
+
170
+ All database operations ensure that the `_` property (position index) is always included in returned results.
171
+
172
+ ## Bulk Operations with `iterate()`
173
+
174
+ The `iterate()` method provides high-performance bulk update capabilities with streaming support:
175
+
176
+ ```javascript
177
+ // Basic bulk update
178
+ for await (const entry of db.iterate({ category: 'fruits' })) {
179
+ entry.price = entry.price * 1.1 // 10% price increase
180
+ entry.lastUpdated = new Date().toISOString()
181
+ }
182
+
183
+ // Advanced options with progress tracking
184
+ for await (const entry of db.iterate(
185
+ { status: 'active' },
186
+ {
187
+ chunkSize: 1000,
188
+ progressCallback: (progress) => {
189
+ console.log(`Processed: ${progress.processed}, Modified: ${progress.modified}`)
190
+ }
191
+ }
192
+ )) {
193
+ entry.processed = true
194
+ }
195
+ ```
196
+
197
+ **Key Benefits:**
198
+ - **Streaming Performance**: Process large datasets without loading everything into memory
199
+ - **Bulk Updates**: Modify multiple records in a single operation
200
+ - **Automatic Change Detection**: Automatically detects which records were modified
201
+ - **Progress Tracking**: Optional progress callbacks for long-running operations
202
+
203
+ ## 🔄 Migration Guide (1.x.x → 2.1.0)
204
+
205
+ ### ⚠️ Important: Database Files Are NOT Compatible
206
+
207
+ **Existing `.jdb` files from version 1.x.x will NOT work with version 2.1.0.**
208
+
209
+ ### Step 1: Export Data from 1.x.x
210
+
211
+ ```javascript
212
+ // In your 1.x.x application
213
+ const oldDb = new Database('old-database.jdb', {
214
+ indexes: { name: 'string', tags: 'array:string' }
215
+ })
216
+
217
+ await oldDb.init()
218
+ const allData = await oldDb.find({}) // Export all data
219
+ await oldDb.destroy()
220
+ ```
221
+
222
+ ### Step 2: Update Your Code
223
+
224
+ ```javascript
225
+ // ❌ OLD (1.x.x)
226
+ const db = new Database('db.jdb', {
227
+ indexes: { name: 'string', tags: 'array:string' }
228
+ })
229
+
230
+ // ✅ NEW (2.1.0)
231
+ const db = new Database('db.jdb', {
232
+ fields: { // REQUIRED - Define schema
233
+ id: 'number',
234
+ name: 'string',
235
+ tags: 'array:string'
236
+ },
237
+ indexes: { // OPTIONAL - Performance optimization
238
+ name: 'string', // ✅ Search by name
239
+ tags: 'array:string' // Search by tags
240
+ }
241
+ })
242
+ ```
243
+
244
+ ### Step 3: Import Data to 2.1.0
245
+
246
+ ```javascript
247
+ // In your 2.1.0 application
248
+ const newDb = new Database('new-database.jdb', {
249
+ fields: { /* your schema */ },
250
+ indexes: { /* your indexes */ }
251
+ })
252
+
253
+ await newDb.init()
254
+
255
+ // Import all data
256
+ for (const record of allData) {
257
+ await newDb.insert(record)
258
+ }
259
+
260
+ await newDb.save()
261
+ ```
262
+
263
+ ### Key Changes Summary
264
+
265
+ | Feature | 1.x.x | 2.1.0 |
266
+ |---------|-------|-------|
267
+ | `fields` | Optional | **MANDATORY** |
268
+ | `termMapping` | `false` (default) | `true` (default) |
269
+ | `termMappingFields` | Manual config | Auto-detected |
270
+ | Database files | Compatible | **NOT compatible** |
271
+ | Performance | Basic | **77% size reduction** |
272
+
273
+ ## 📚 Documentation
274
+
275
+ For comprehensive documentation and examples:
276
+
277
+ - **[📖 Full Documentation](docs/README.md)** - Complete documentation index
278
+ - **[🔧 API Reference](docs/API.md)** - Detailed API documentation
279
+ - **[💡 Examples](docs/EXAMPLES.md)** - Practical examples and use cases
280
+ - **[🚀 Getting Started](docs/README.md#quick-start)** - Quick start guide
281
+
282
+ ### Key Features Documentation
283
+
284
+ - **[Bulk Operations](docs/API.md#bulk-operations)** - High-performance `iterate()` method
285
+ - **[Term Mapping](docs/API.md#term-mapping)** - Optimize storage for repetitive data
286
+ - **[Query Operators](docs/API.md#query-operators)** - Advanced querying capabilities
287
+ - **[Performance Tips](docs/API.md#best-practices)** - Optimization strategies
288
+
289
+ ## Conclusion
290
+
291
+ JexiDB provides a simple yet powerful way to store and manage data in JavaScript applications. With its indexing and querying features, you can build efficient data-driven applications.
292
+
293
+ <p align="center">
294
+ <img width="380" src="https://edenware.app/jexidb/images/jexidb-mascot3.jpg" alt="JexiDB mascot" title="JexiDB mascot" />
295
+ </p>
296
+
297
+ # Contributing
298
+
299
+ Please, feel free to contribute to the project by opening a discussion under Issues section or sending your PR.
300
+
301
+ If you find this library useful, please consider making a donation of any amount via [PayPal by clicking here](https://www.paypal.com/donate/?item_name=megacubo.tv&cmd=_donations&business=efox.web%40gmail.com) to help the developer continue to dedicate himself to the project. ❤