jexidb 2.1.0 → 2.1.2

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 (41) hide show
  1. package/dist/Database.cjs +9253 -437
  2. package/package.json +9 -2
  3. package/src/Database.mjs +1572 -212
  4. package/src/FileHandler.mjs +83 -44
  5. package/src/OperationQueue.mjs +23 -23
  6. package/src/SchemaManager.mjs +325 -268
  7. package/src/Serializer.mjs +234 -24
  8. package/src/managers/IndexManager.mjs +778 -87
  9. package/src/managers/QueryManager.mjs +340 -67
  10. package/src/managers/TermManager.mjs +7 -7
  11. package/src/utils/operatorNormalizer.mjs +116 -0
  12. package/.babelrc +0 -13
  13. package/.gitattributes +0 -2
  14. package/CHANGELOG.md +0 -140
  15. package/babel.config.json +0 -5
  16. package/docs/API.md +0 -1051
  17. package/docs/EXAMPLES.md +0 -701
  18. package/docs/README.md +0 -194
  19. package/examples/iterate-usage-example.js +0 -157
  20. package/examples/simple-iterate-example.js +0 -115
  21. package/jest.config.js +0 -24
  22. package/scripts/README.md +0 -47
  23. package/scripts/clean-test-files.js +0 -75
  24. package/scripts/prepare.js +0 -31
  25. package/scripts/run-tests.js +0 -80
  26. package/test/$not-operator-with-and.test.js +0 -282
  27. package/test/README.md +0 -8
  28. package/test/close-init-cycle.test.js +0 -256
  29. package/test/critical-bugs-fixes.test.js +0 -1069
  30. package/test/index-persistence.test.js +0 -306
  31. package/test/index-serialization.test.js +0 -314
  32. package/test/indexed-query-mode.test.js +0 -360
  33. package/test/iterate-method.test.js +0 -272
  34. package/test/query-operators.test.js +0 -238
  35. package/test/regex-array-fields.test.js +0 -129
  36. package/test/score-method.test.js +0 -238
  37. package/test/setup.js +0 -17
  38. package/test/term-mapping-minimal.test.js +0 -154
  39. package/test/term-mapping-simple.test.js +0 -257
  40. package/test/term-mapping.test.js +0 -514
  41. package/test/writebuffer-flush-resilience.test.js +0 -204
package/docs/API.md DELETED
@@ -1,1051 +0,0 @@
1
- # JexiDB API Documentation
2
-
3
- ## ⚠️ Version 2.1.0 - Breaking Changes
4
-
5
- **This version is NOT backward compatible with databases created with previous versions (1.x.x).**
6
-
7
- ### Major Changes:
8
- - **`fields` is now MANDATORY** - Define your schema structure
9
- - **Term mapping is auto-enabled** - No manual configuration needed
10
- - **Database files are incompatible** - Export/import required for migration
11
- - **Performance improvements** - Up to 77% size reduction for repetitive data
12
-
13
- ## Table of Contents
14
-
15
- 1. [Database Constructor](#database-constructor)
16
- 2. [Core Methods](#core-methods)
17
- 3. [Query Methods](#query-methods)
18
- 4. [Advanced Features](#advanced-features)
19
- 5. [Term Mapping](#term-mapping)
20
- 6. [Bulk Operations](#bulk-operations)
21
- 7. [Configuration Options](#configuration-options)
22
- 8. [Migration Guide](#migration-guide)
23
-
24
- ## Database Constructor
25
-
26
- ```javascript
27
- import { Database } from 'jexidb'
28
-
29
- const db = new Database('path/to/database.jdb', options)
30
- ```
31
-
32
- ### Constructor Options
33
-
34
- | Option | Type | Default | Description |
35
- |--------|------|---------|-------------|
36
- | `create` | boolean | `true` | Create the file if it doesn't exist |
37
- | `clear` | boolean | `false` | Clear existing files before loading |
38
- | `fields` | object | **Required** | **MANDATORY** - Define the data structure schema |
39
- | `indexes` | object | `{}` | **Optional** indexed fields for faster queries (not required) |
40
- | `termMapping` | boolean | `true` | Enable term mapping for optimal performance (auto-detected) |
41
- | `termMappingFields` | string[] | `[]` | Fields to apply term mapping to (auto-detected from indexes) |
42
- | `termMappingCleanup` | boolean | `true` | Automatically clean up orphaned terms |
43
-
44
- ### Fields vs Indexes - Important Distinction
45
-
46
- **Fields** (Schema - **MANDATORY**):
47
- - ✅ **Required** - Define the structure of your data
48
- - ✅ **Schema enforcement** - Controls which fields are allowed
49
- - ✅ **All fields** are queryable by default
50
- - ✅ **No performance impact** on memory usage
51
- - ✅ **Data validation** - Ensures data consistency
52
-
53
- **Indexes** (Performance Optimization - **Optional**):
54
- - ⚠️ **Optional** - Only for fields you query frequently
55
- - ⚠️ **Memory overhead** - Each index uses additional memory
56
- - ⚠️ **Use sparingly** - Only index fields you actually query
57
- - ⚠️ **Query performance** - Only affects query speed, not functionality
58
-
59
- ### When to Use Indexes
60
-
61
- ```javascript
62
- // ❌ DON'T: Index everything (wastes memory)
63
- const db = new Database('db.jdb', {
64
- fields: { // REQUIRED - Define schema
65
- id: 'number',
66
- name: 'string',
67
- email: 'string',
68
- phone: 'string',
69
- address: 'string',
70
- city: 'string',
71
- country: 'string',
72
- status: 'string',
73
- createdAt: 'number',
74
- updatedAt: 'number'
75
- },
76
- indexes: { // OPTIONAL - Only for performance
77
- id: 'number', // Maybe needed
78
- name: 'string', // Maybe needed
79
- email: 'string', // Maybe needed
80
- phone: 'string', // Maybe needed
81
- address: 'string', // Maybe needed
82
- city: 'string', // Maybe needed
83
- country: 'string', // Maybe needed
84
- status: 'string', // Maybe needed
85
- createdAt: 'number', // Maybe needed
86
- updatedAt: 'number' // Maybe needed
87
- }
88
- })
89
-
90
- // ✅ DO: Define schema + index only what you query frequently
91
- const db = new Database('db.jdb', {
92
- fields: { // REQUIRED - Define schema
93
- id: 'number',
94
- name: 'string',
95
- email: 'string',
96
- phone: 'string',
97
- address: 'string',
98
- city: 'string',
99
- country: 'string',
100
- status: 'string',
101
- createdAt: 'number',
102
- updatedAt: 'number'
103
- },
104
- indexes: { // OPTIONAL - Only for performance
105
- id: 'number', // Primary key - always index
106
- email: 'string', // Login queries - index this
107
- status: 'string' // Filter queries - index this
108
- }
109
- // Other fields (name, phone, address, etc.) are still queryable
110
- // but will use slower sequential search
111
- })
112
- ```
113
-
114
- ### Example
115
-
116
- ```javascript
117
- // Example: E-commerce product database
118
- const db = new Database('products.jdb', {
119
- create: true,
120
- clear: false,
121
- fields: { // REQUIRED - Define schema
122
- id: 'number',
123
- name: 'string',
124
- description: 'string',
125
- category: 'string',
126
- tags: 'array:string',
127
- price: 'number',
128
- imageUrl: 'string',
129
- inStock: 'boolean',
130
- createdAt: 'number'
131
- },
132
- indexes: { // OPTIONAL - Only for performance
133
- id: 'number', // Primary key - always index
134
- category: 'string', // Filter by category - index this
135
- tags: 'array:string', // Search by tags - index this
136
- price: 'number' // Price range queries - index this
137
- }
138
- // Fields like 'name', 'description', 'imageUrl' are still queryable
139
- // but will use slower sequential search (no index needed unless you query them frequently)
140
- })
141
-
142
- await db.init()
143
-
144
- // All these queries work, but some are faster:
145
- await db.find({ id: 1 }) // ✅ Fast (indexed)
146
- await db.find({ category: 'electronics' }) // ✅ Fast (indexed)
147
- await db.find({ tags: 'wireless' }) // ✅ Fast (indexed)
148
- await db.find({ price: { '>': 100 } }) // ✅ Fast (indexed)
149
- await db.find({ name: 'iPhone' }) // ⚠️ Slower (not indexed, but still works)
150
- await db.find({ description: 'wireless' }) // ⚠️ Slower (not indexed, but still works)
151
- ```
152
-
153
- ## Core Methods
154
-
155
- ### `init()`
156
-
157
- Initialize the database and load existing data.
158
-
159
- ```javascript
160
- await db.init()
161
- ```
162
-
163
- ### `insert(data)`
164
-
165
- Insert a new record into the database.
166
-
167
- ```javascript
168
- await db.insert({
169
- id: 1,
170
- name: 'John Doe',
171
- email: 'john@example.com'
172
- })
173
- ```
174
-
175
- ### `update(criteria, updates)`
176
-
177
- Update records matching the criteria.
178
-
179
- ```javascript
180
- await db.update(
181
- { id: 1 },
182
- { email: 'newemail@example.com', updated: true }
183
- )
184
- ```
185
-
186
- ### `delete(criteria)`
187
-
188
- Delete records matching the criteria.
189
-
190
- ```javascript
191
- await db.delete({ id: 1 })
192
- ```
193
-
194
- ### `save()`
195
-
196
- Save all pending changes to disk.
197
-
198
- ```javascript
199
- await db.save()
200
- ```
201
-
202
- ### `destroy()`
203
-
204
- Close the database and clean up resources.
205
-
206
- ```javascript
207
- await db.destroy()
208
- ```
209
-
210
- ## Query Methods
211
-
212
- ### `find(criteria, options)`
213
-
214
- Find records matching the criteria.
215
-
216
- ```javascript
217
- // Basic query
218
- const results = await db.find({ name: 'John Doe' })
219
-
220
- // Query with conditions
221
- const results = await db.find({
222
- age: { '>': 18, '<': 65 },
223
- status: 'active'
224
- })
225
-
226
- // Case insensitive search
227
- const results = await db.find(
228
- { name: 'john doe' },
229
- { caseInsensitive: true }
230
- )
231
- ```
232
-
233
- ### `findOne(criteria, options)`
234
-
235
- Find the first record matching the criteria.
236
-
237
- ```javascript
238
- const user = await db.findOne({ id: 1 })
239
- ```
240
-
241
- ### `count(criteria)`
242
-
243
- Count records matching the criteria.
244
-
245
- ```javascript
246
- const userCount = await db.count({ status: 'active' })
247
- ```
248
-
249
- ### `score(fieldName, scores, options)`
250
-
251
- Score and rank records based on weighted terms in an indexed `array:string` field. This method is optimized for in-memory operations and provides 10x+ performance improvement over equivalent `find()` queries.
252
-
253
- ```javascript
254
- // Basic scoring
255
- const results = await db.score('tags', {
256
- 'javascript': 1.0,
257
- 'node': 0.8,
258
- 'typescript': 0.9
259
- })
260
-
261
- // With options
262
- const results = await db.score('terms', {
263
- 'action': 1.0,
264
- 'comedy': 0.8
265
- }, {
266
- limit: 10,
267
- sort: 'desc',
268
- includeScore: true
269
- })
270
- ```
271
-
272
- **Parameters:**
273
-
274
- - `fieldName` (string, required): Name of the indexed `array:string` field to score
275
- - `scores` (object, required): Map of terms to numeric weights (e.g., `{ 'action': 1.0, 'comedy': 0.8 }`)
276
- - `options` (object, optional):
277
- - `limit` (number): Maximum results (default: 100)
278
- - `sort` (string): "desc" or "asc" (default: "desc")
279
- - `includeScore` (boolean): Include score in results (default: true)
280
-
281
- **Returns:**
282
-
283
- Array of records ordered by score, with optional score property:
284
-
285
- ```javascript
286
- // With includeScore: true (default)
287
- [
288
- { _: 123, score: 1.8, title: "Action Comedy", terms: ["action", "comedy"] },
289
- { _: 456, score: 1.0, title: "Action Movie", terms: ["action", "movie"] },
290
- { _: 789, score: 0.8, title: "Comedy Show", terms: ["comedy", "show"] }
291
- ]
292
-
293
- // With includeScore: false
294
- [
295
- { _: 123, title: "Action Comedy", terms: ["action", "comedy"] },
296
- { _: 456, title: "Action Movie", terms: ["action", "movie"] },
297
- { _: 789, title: "Comedy Show", terms: ["comedy", "show"] }
298
- ]
299
- ```
300
-
301
- **Score Calculation:**
302
-
303
- For each record, the score is the sum of weights for matching terms:
304
-
305
- ```javascript
306
- // Record: { title: "Action Comedy", terms: ["action", "comedy"] }
307
- // Scores: { 'action': 1.0, 'comedy': 0.8 }
308
- // Result: score = 1.0 + 0.8 = 1.8
309
- ```
310
-
311
- **Example Use Cases:**
312
-
313
- ```javascript
314
- // Content recommendation system
315
- const recommendations = await db.score('categories', {
316
- 'technology': 1.0,
317
- 'programming': 0.9,
318
- 'web': 0.8
319
- }, { limit: 20 })
320
-
321
- // Search with weighted keywords
322
- const searchResults = await db.score('keywords', {
323
- 'urgent': 2.0,
324
- 'important': 1.5,
325
- 'notable': 1.0
326
- }, { sort: 'desc', limit: 50 })
327
-
328
- // Product recommendations
329
- const productMatches = await db.score('tags', {
330
- 'wireless': 1.2,
331
- 'bluetooth': 1.0,
332
- 'rechargeable': 0.9,
333
- 'compact': 0.8
334
- }, { includeScore: false })
335
- ```
336
-
337
- **Performance Notes:**
338
-
339
- - ⚡ **Memory-only operations** - Uses in-memory indices exclusively
340
- - ⚡ **No physical I/O for scoring** - Only final record fetch requires disk access
341
- - ⚡ **10x+ faster** than equivalent `find()` + manual scoring
342
- - ⚡ **O(T × N) complexity** - T = terms in scores, N = avg records per term
343
- - ⚡ **Optimal for selective queries** - Best when scoring subset of total records
344
-
345
- **Requirements:**
346
-
347
- - Field must be indexed as `array:string` type
348
- - Field must be present in `indexes` configuration
349
- - Returns empty array if no terms match
350
- - Records with zero scores are excluded
351
-
352
- **Error Handling:**
353
-
354
- ```javascript
355
- try {
356
- const results = await db.score('tags', { 'javascript': 1.0 })
357
- } catch (error) {
358
- // Error: Field "tags" is not indexed
359
- // Error: Field "tags" must be of type "array:string"
360
- // Error: scores must be an object
361
- // Error: Score value for term "javascript" must be a number
362
- }
363
- ```
364
-
365
- ### Query Operators
366
-
367
- | Operator | Description | Example |
368
- |----------|-------------|---------|
369
- | `>` | Greater than | `{ age: { '>': 18 } }` |
370
- | `>=` | Greater than or equal | `{ score: { '>=': 80 } }` |
371
- | `<` | Less than | `{ price: { '<': 100 } }` |
372
- | `<=` | Less than or equal | `{ rating: { '<=': 5 } }` |
373
- | `!=` | Not equal | `{ status: { '!=': 'deleted' } }` |
374
- | `$in` | In array | `{ category: { '$in': ['A', 'B'] } }` |
375
- | `$not` | Not | `{ name: { '$not': 'John' } }` |
376
- | `$and` | Logical AND | `{ '$and': [{ age: { '>': 18 } }, { status: 'active' }] }` |
377
- | `$or` | Logical OR | `{ '$or': [{ type: 'admin' }, { type: 'moderator' }] }` |
378
-
379
- ### Complex Queries
380
-
381
- ```javascript
382
- // Multiple conditions (AND by default)
383
- const results = await db.find({
384
- age: { '>': 18 },
385
- status: 'active',
386
- category: { '$in': ['premium', 'vip'] }
387
- })
388
-
389
- // Using logical operators
390
- const results = await db.find({
391
- '$or': [
392
- { type: 'admin' },
393
- { '$and': [
394
- { type: 'user' },
395
- { verified: true }
396
- ]}
397
- ]
398
- })
399
-
400
- // Array field queries
401
- const results = await db.find({
402
- tags: 'javascript', // Contains 'javascript'
403
- tags: { '$all': ['js', 'node'] } // Contains all specified tags
404
- })
405
- ```
406
-
407
- ## Advanced Features
408
-
409
- ### Indexed Query Mode
410
-
411
- Control whether queries are restricted to indexed fields only.
412
-
413
- ```javascript
414
- // Strict mode - only indexed fields allowed in queries
415
- const db = new Database('db.jdb', {
416
- fields: { // REQUIRED - Define schema
417
- id: 'number',
418
- name: 'string',
419
- age: 'number',
420
- email: 'string'
421
- },
422
- indexes: { // OPTIONAL - Only fields you query frequently
423
- name: 'string', // ✅ Search by name
424
- age: 'number' // ✅ Filter by age
425
- },
426
- indexedQueryMode: 'strict'
427
- })
428
-
429
- // This will throw an error in strict mode
430
- await db.find({ email: 'test@example.com' }) // Error: email is not indexed
431
-
432
- // Permissive mode (default) - allows any field
433
- const db = new Database('db.jdb', {
434
- fields: { // REQUIRED - Define schema
435
- id: 'number',
436
- name: 'string',
437
- age: 'number',
438
- email: 'string'
439
- },
440
- indexes: { // OPTIONAL - Only fields you query frequently
441
- name: 'string', // ✅ Search by name
442
- age: 'number' // ✅ Filter by age
443
- },
444
- indexedQueryMode: 'permissive'
445
- })
446
-
447
- // This works in permissive mode, but will be slower than strict mode
448
- await db.find({ email: 'test@example.com' }) // OK
449
- ```
450
-
451
- ### Query Performance
452
-
453
- **Index Strategy Guidelines:**
454
-
455
- 1. **Always index primary keys** (usually `id`)
456
- 2. **Index frequently queried fields** (filters, searches)
457
- 3. **Don't index everything** - each index uses memory
458
- 4. **Use specific criteria** rather than broad queries
459
-
460
- **Performance Examples:**
461
-
462
- ```javascript
463
- // ✅ Good: Index only what you need
464
- const db = new Database('users.jdb', {
465
- fields: { // REQUIRED - Define schema
466
- id: 'number',
467
- email: 'string',
468
- status: 'string',
469
- name: 'string',
470
- phone: 'string'
471
- },
472
- indexes: { // OPTIONAL - Only fields you query frequently
473
- email: 'string', // ✅ Login queries
474
- status: 'string' // ✅ Filter active/inactive users
475
- }
476
- })
477
-
478
- // ❌ Bad: Over-indexing wastes memory
479
- const db = new Database('users.jdb', {
480
- fields: { // REQUIRED - Define schema
481
- id: 'number',
482
- name: 'string',
483
- email: 'string',
484
- phone: 'string',
485
- address: 'string',
486
- city: 'string',
487
- country: 'string',
488
- createdAt: 'number',
489
- updatedAt: 'number'
490
- },
491
- indexes: { // OPTIONAL - Performance optimization (too many!)
492
- name: 'string', // ❌ Only if you search by name frequently
493
- email: 'string', // ❌ Only if you search by email frequently
494
- phone: 'string', // ❌ Only if you search by phone frequently
495
- address: 'string', // ❌ Only if you search by address frequently
496
- city: 'string', // ❌ Only if you search by city frequently
497
- country: 'string', // ❌ Only if you search by country frequently
498
- createdAt: 'number', // ❌ Only if you filter by date frequently
499
- updatedAt: 'number' // ❌ Only if you filter by date frequently
500
- }
501
- })
502
-
503
- // Query performance comparison:
504
- await db.find({ id: 1 }) // ✅ Fast (indexed)
505
- await db.find({ email: 'user@example.com' }) // ✅ Fast (indexed)
506
- await db.find({ status: 'active' }) // ✅ Fast (indexed)
507
- await db.find({ name: 'John' }) // ⚠️ Slower (not indexed, but works)
508
- await db.find({ phone: '123-456-7890' }) // ⚠️ Slower (not indexed, but works)
509
- ```
510
-
511
- **Memory Impact:**
512
- - Each index uses additional memory
513
- - String indexes use more memory than number indexes
514
- - Array indexes use more memory than single-value indexes
515
- - **Rule of thumb**: Only index fields you query in 80%+ of your queries
516
-
517
- ## Term Mapping
518
-
519
- Term mapping is a powerful optimization that reduces database size by mapping repetitive string terms to numeric IDs. **It's now enabled by default** and automatically detects which fields benefit from term mapping.
520
-
521
- ### Benefits
522
-
523
- - **77% size reduction** in typical scenarios
524
- - **Faster queries** with numeric comparisons
525
- - **Automatic cleanup** of unused terms
526
- - **Transparent operation** - same API
527
- - **Auto-detection** of optimal fields
528
- - **Zero configuration** required
529
-
530
- ### Automatic Configuration
531
-
532
- Term mapping is now **automatically enabled** and detects fields that benefit from optimization:
533
-
534
- ```javascript
535
- const db = new Database('my-db.jdb', {
536
- fields: { // REQUIRED - Define schema
537
- id: 'number',
538
- name: 'string',
539
- tags: 'array:string',
540
- scores: 'array:number',
541
- price: 'number'
542
- },
543
- indexes: { // OPTIONAL - Only fields you query frequently
544
- name: 'string', // ✅ Search by name (auto term mapping)
545
- tags: 'array:string' // ✅ Search by tags (auto term mapping)
546
- }
547
- })
548
-
549
- // Term mapping is automatically enabled
550
- console.log(db.opts.termMapping) // true
551
- console.log(db.termManager.termMappingFields) // ['name', 'tags']
552
- ```
553
-
554
- ### How It Works
555
-
556
- Term mapping automatically detects and optimizes fields:
557
-
558
- 1. **Auto-detection**: Fields with `'string'` or `'array:string'` types are automatically optimized
559
- 2. **Term ID mapping**: Each unique string term gets a numeric ID
560
- 3. **Efficient storage**: Term IDs are stored in optimized structures
561
- 4. **Transparent queries**: Queries automatically convert search terms to IDs
562
- 5. **Automatic cleanup**: Unused terms are automatically cleaned up
563
-
564
- ### Field Type Behavior
565
-
566
- | Field Type | Term Mapping | Storage | Example |
567
- |------------|--------------|---------|---------|
568
- | `'string'` | ✅ Enabled | Term IDs | `"Brazil"` → `1` |
569
- | `'array:string'` | ✅ Enabled | Term IDs | `["Brazil", "Argentina"]` → `[1, 2]` |
570
- | `'number'` | ❌ Disabled | Direct values | `85` → `"85"` |
571
- | `'array:number'` | ❌ Disabled | Direct values | `[85, 92]` → `["85", "92"]` |
572
- | `'boolean'` | ❌ Disabled | Direct values | `true` → `"true"` |
573
- | `'array:boolean'` | ❌ Disabled | Direct values | `[true, false]` → `["true", "false"]` |
574
-
575
- ### Example Usage
576
-
577
- ```javascript
578
- // Create database with mixed field types
579
- const db = new Database('products.jdb', {
580
- fields: { // REQUIRED - Define schema
581
- id: 'number',
582
- name: 'string',
583
- tags: 'array:string',
584
- scores: 'array:number',
585
- price: 'number'
586
- },
587
- indexes: { // OPTIONAL - Only fields you query frequently
588
- name: 'string', // ✅ Search by name (auto term mapping)
589
- tags: 'array:string' // ✅ Search by tags (auto term mapping)
590
- }
591
- })
592
-
593
- // Insert data with repetitive terms
594
- await db.insert({
595
- name: 'Product A',
596
- tags: ['electronics', 'gadget', 'wireless'],
597
- scores: [85, 92, 78],
598
- price: 299.99
599
- })
600
-
601
- await db.insert({
602
- name: 'Product B',
603
- tags: ['electronics', 'accessory', 'wireless'],
604
- scores: [90, 88, 95],
605
- price: 199.99
606
- })
607
-
608
- // Queries work normally - term mapping is transparent
609
- const results = await db.find({ tags: 'electronics' })
610
- const expensive = await db.find({ price: { '>': 250 } })
611
-
612
- // Check term mapping status
613
- console.log(db.opts.termMapping) // true
614
- console.log(db.termManager.termMappingFields) // ['name', 'tags']
615
- console.log(db.termManager.getStats()) // { totalTerms: 6, nextId: 7, orphanedTerms: 0 }
616
- ```
617
-
618
- ### Term Manager API
619
-
620
- ```javascript
621
- // Get term ID (creates if doesn't exist)
622
- const termId = db.termManager.getTermId('electronics')
623
-
624
- // Get term by ID
625
- const term = db.termManager.getTerm(1)
626
-
627
- // Get statistics
628
- const stats = db.termManager.getStats()
629
-
630
- // Manual cleanup
631
- const removedCount = await db.termManager.cleanupOrphanedTerms()
632
- ```
633
-
634
- ## Bulk Operations
635
-
636
- ### `iterate(criteria, options)`
637
-
638
- High-performance bulk update method with streaming support.
639
-
640
- ```javascript
641
- // Basic iteration
642
- for await (const entry of db.iterate({ category: 'products' })) {
643
- console.log(`${entry.name}: $${entry.price}`)
644
- }
645
-
646
- // Bulk updates
647
- for await (const entry of db.iterate({ inStock: true })) {
648
- entry.price = Math.round(entry.price * 1.1 * 100) / 100
649
- entry.lastUpdated = new Date().toISOString()
650
- }
651
-
652
- // Advanced options
653
- for await (const entry of db.iterate(
654
- { category: 'electronics' },
655
- {
656
- chunkSize: 1000, // Process in batches
657
- autoSave: false, // Auto-save after each chunk
658
- detectChanges: true, // Auto-detect modifications
659
- progressCallback: (progress) => {
660
- console.log(`Processed: ${progress.processed}, Modified: ${progress.modified}`)
661
- }
662
- }
663
- )) {
664
- entry.processed = true
665
- }
666
- ```
667
-
668
- ### Iterate Options
669
-
670
- | Option | Type | Default | Description |
671
- |--------|------|---------|-------------|
672
- | `chunkSize` | number | 1000 | Batch size for processing |
673
- | `strategy` | string | 'streaming' | Processing strategy |
674
- | `autoSave` | boolean | false | Auto-save after each chunk |
675
- | `detectChanges` | boolean | true | Auto-detect modifications |
676
- | `progressCallback` | function | undefined | Progress callback function |
677
-
678
- ### Progress Callback
679
-
680
- ```javascript
681
- {
682
- processed: 1500, // Number of records processed
683
- modified: 120, // Number of records modified
684
- deleted: 5, // Number of records deleted
685
- elapsed: 2500, // Time elapsed in milliseconds
686
- completed: false // Whether the operation is complete
687
- }
688
- ```
689
-
690
- ### `beginInsertSession(options)`
691
-
692
- High-performance batch insertion method for inserting large amounts of data efficiently.
693
-
694
- ```javascript
695
- // Example: EPG (Electronic Program Guide) data insertion
696
- const session = db.beginInsertSession({
697
- batchSize: 500, // Process in batches of 500
698
- enableAutoSave: true // Auto-save after each batch
699
- })
700
-
701
- // Insert TV program data
702
- const programmes = [
703
- {
704
- id: 1,
705
- title: 'Breaking News',
706
- channel: 'CNN',
707
- startTime: 1640995200000,
708
- endTime: 1640998800000,
709
- terms: ['news', 'breaking', 'politics'],
710
- category: 'news'
711
- },
712
- {
713
- id: 2,
714
- title: 'Sports Center',
715
- channel: 'ESPN',
716
- startTime: 1640998800000,
717
- endTime: 1641002400000,
718
- terms: ['sports', 'football', 'highlights'],
719
- category: 'sports'
720
- }
721
- // ... thousands more programmes
722
- ]
723
-
724
- // Add all programmes to the session
725
- for (const programme of programmes) {
726
- await session.add(programme)
727
- }
728
-
729
- // Commit all records at once (much faster than individual inserts)
730
- await session.commit()
731
- await db.save()
732
-
733
- console.log(`Inserted ${session.totalInserted} programmes`)
734
- ```
735
-
736
- ### InsertSession Options
737
-
738
- | Option | Type | Default | Description |
739
- |--------|------|---------|-------------|
740
- | `batchSize` | number | 100 | Number of records to process in each batch |
741
- | `enableAutoSave` | boolean | false | Auto-save after each batch |
742
-
743
- ### InsertSession Methods
744
-
745
- ```javascript
746
- const session = db.beginInsertSession({ batchSize: 500 })
747
-
748
- // Add a single record
749
- await session.add({ id: 1, name: 'John', email: 'john@example.com' })
750
-
751
- // Add multiple records
752
- for (const record of records) {
753
- await session.add(record)
754
- }
755
-
756
- // Commit all pending records
757
- await session.commit()
758
-
759
- // Get statistics
760
- console.log(`Inserted ${session.totalInserted} records`)
761
- ```
762
-
763
- ### When to Use `beginInsertSession()`
764
-
765
- **Use `beginInsertSession()` for:**
766
- - ✅ **Bulk insertions** (1000+ new records)
767
- - ✅ **Data migration** from other databases
768
- - ✅ **Initial data loading** (EPG, product catalogs, etc.)
769
- - ✅ **Batch processing** of external data sources
770
-
771
- ### Performance Comparison
772
-
773
- ```javascript
774
- // ❌ SLOW: Individual inserts (1000 records = 1000 database operations)
775
- for (let i = 0; i < 1000; i++) {
776
- await db.insert({ id: i, name: `Record ${i}` })
777
- }
778
-
779
- // ✅ FAST: Batch insertion (1000 records = 1 database operation)
780
- const session = db.beginInsertSession({ batchSize: 1000 })
781
- for (let i = 0; i < 1000; i++) {
782
- await session.add({ id: i, name: `Record ${i}` })
783
- }
784
- await session.commit()
785
- ```
786
-
787
- ### Performance Tips
788
-
789
- 1. **Use `beginInsertSession()`** for bulk insertions of 1000+ records
790
- 2. **Set appropriate batchSize** based on available memory (500-2000 records)
791
- 3. **Enable autoSave** for critical operations to prevent data loss
792
- 4. **Use indexed fields** in your data for better query performance later
793
- 5. **Commit frequently** for very large datasets to avoid memory issues
794
-
795
- ## Configuration Options
796
-
797
- ### Database Options
798
-
799
- ```javascript
800
- const db = new Database('database.jdb', {
801
- // Basic options
802
- create: true, // Create file if doesn't exist
803
- clear: false, // Clear existing files
804
-
805
- // Schema (REQUIRED - define data structure)
806
- fields: { // MANDATORY - Define all possible fields
807
- id: 'number',
808
- name: 'string',
809
- email: 'string',
810
- phone: 'string',
811
- address: 'string',
812
- city: 'string',
813
- country: 'string',
814
- status: 'string',
815
- createdAt: 'number',
816
- tags: 'array:string'
817
- },
818
-
819
- // Indexing (OPTIONAL - only for performance optimization)
820
- indexes: { // Only index fields you query frequently
821
- id: 'number', // Primary key - always index
822
- email: 'string', // Login queries - index this
823
- status: 'string' // Filter queries - index this
824
- // Don't index: name, phone, address, etc. unless you query them frequently
825
- },
826
-
827
- // Query mode
828
- indexedQueryMode: 'permissive', // 'strict' or 'permissive'
829
-
830
- // Term mapping (enabled by default)
831
- termMapping: true, // Enable term mapping (auto-detected)
832
- termMappingFields: [], // Fields to map (auto-detected)
833
- termMappingCleanup: true, // Auto cleanup
834
-
835
- // Performance
836
- chunkSize: 1000, // Default chunk size
837
- autoSave: false, // Auto-save changes
838
-
839
- // Debug
840
- debugMode: false // Enable debug logging
841
- })
842
- ```
843
-
844
- ### Schema vs Indexes - Complete Guide
845
-
846
- **Understanding the Difference:**
847
-
848
- ```javascript
849
- // Your data structure (schema) - MUST be defined in fields option
850
- const userRecord = {
851
- id: 1,
852
- name: 'John Doe',
853
- email: 'john@example.com',
854
- phone: '123-456-7890',
855
- address: '123 Main St',
856
- city: 'New York',
857
- country: 'USA',
858
- status: 'active',
859
- createdAt: 1640995200000,
860
- tags: ['premium', 'verified']
861
- }
862
-
863
- // Database configuration
864
- const db = new Database('users.jdb', {
865
- fields: { // REQUIRED - Define schema structure
866
- id: 'number',
867
- name: 'string',
868
- email: 'string',
869
- phone: 'string',
870
- address: 'string',
871
- city: 'string',
872
- country: 'string',
873
- status: 'string',
874
- createdAt: 'number',
875
- tags: 'array:string'
876
- },
877
- indexes: { // OPTIONAL - Only for performance optimization
878
- id: 'number', // Primary key - always index
879
- email: 'string', // Login queries - index this
880
- status: 'string', // Filter queries - index this
881
- tags: 'array:string' // Search by tags - index this
882
- }
883
- // Fields like name, phone, address, city, country, createdAt
884
- // are still queryable but will use slower sequential search
885
- })
886
-
887
- // All these queries work:
888
- await db.find({ id: 1 }) // ✅ Fast (indexed)
889
- await db.find({ email: 'john@example.com' }) // ✅ Fast (indexed)
890
- await db.find({ status: 'active' }) // ✅ Fast (indexed)
891
- await db.find({ tags: 'premium' }) // ✅ Fast (indexed)
892
- await db.find({ name: 'John Doe' }) // ⚠️ Slower (not indexed, but works)
893
- await db.find({ phone: '123-456-7890' }) // ⚠️ Slower (not indexed, but works)
894
- await db.find({ city: 'New York' }) // ⚠️ Slower (not indexed, but works)
895
- await db.find({ createdAt: { '>': 1640000000000 } }) // ⚠️ Slower (not indexed, but works)
896
- ```
897
-
898
- **Key Points:**
899
- - ✅ **All fields are queryable** regardless of indexing
900
- - ✅ **Schema is auto-detected** from your data
901
- - ⚠️ **Indexes are optional** - only for performance
902
- - ⚠️ **Each index uses memory** - use sparingly
903
- - ⚠️ **Index only what you query frequently** (80%+ of queries)
904
-
905
- ### Field Types
906
-
907
- Supported field types for indexing:
908
-
909
- | Type | Term Mapping | Description | Example |
910
- |------|--------------|-------------|---------|
911
- | `'string'` | ✅ Auto-enabled | String values | `"Brazil"` |
912
- | `'array:string'` | ✅ Auto-enabled | Array of strings | `["Brazil", "Argentina"]` |
913
- | `'number'` | ❌ Disabled | Numeric values | `85` |
914
- | `'array:number'` | ❌ Disabled | Array of numbers | `[85, 92, 78]` |
915
- | `'boolean'` | ❌ Disabled | Boolean values | `true` |
916
- | `'array:boolean'` | ❌ Disabled | Array of booleans | `[true, false]` |
917
- | `'date'` | ❌ Disabled | Date objects (stored as timestamps) | `new Date()` |
918
-
919
- ## Error Handling
920
-
921
- ```javascript
922
- try {
923
- await db.init()
924
- await db.insert({ id: 1, name: 'Test' })
925
- await db.save()
926
- } catch (error) {
927
- console.error('Database error:', error.message)
928
- } finally {
929
- await db.destroy()
930
- }
931
- ```
932
-
933
- ## Best Practices
934
-
935
- ### Database Operations
936
- 1. **Always define `fields`** - This is mandatory for schema definition
937
- 2. **Always call `init()`** before using the database
938
- 3. **Use `save()`** to persist changes to disk
939
- 4. **Call `destroy()`** when done to clean up resources
940
- 5. **Handle errors** appropriately
941
-
942
- ### Performance Optimization
943
- 6. **Index strategically** - only fields you query frequently (80%+ of queries)
944
- 7. **Don't over-index** - each index uses additional memory
945
- 8. **Term mapping is automatically enabled** for optimal performance
946
- 9. **Use `iterate()`** for bulk operations on large datasets
947
-
948
- ### Index Strategy
949
- 10. **Always index primary keys** (usually `id`)
950
- 11. **Index frequently filtered fields** (status, category, type)
951
- 12. **Index search fields** (email, username, tags)
952
- 13. **Don't index descriptive fields** (name, description, comments) unless you search them frequently
953
- 14. **Monitor memory usage** - too many indexes can impact performance
954
-
955
- ## Migration Guide
956
-
957
- ### From Previous Versions
958
-
959
- If you're upgrading from an older version:
960
-
961
- 1. **Backup your data** before upgrading
962
- 2. **Check breaking changes** in the changelog
963
- 3. **Update your code** to use new APIs
964
- 4. **Test thoroughly** with your data
965
-
966
- ### Common Migration Tasks
967
-
968
- ```javascript
969
- // Old way
970
- const results = db.query({ name: 'John' })
971
-
972
- // New way
973
- const results = await db.find({ name: 'John' })
974
-
975
- // Old way
976
- db.insert({ id: 1, name: 'John' })
977
-
978
- // New way
979
- await db.insert({ id: 1, name: 'John' })
980
- ```
981
-
982
- ## Migration Guide
983
-
984
- ### ⚠️ Important: Database Files Are NOT Compatible
985
-
986
- **Existing `.jdb` files from version 1.x.x will NOT work with version 2.1.0.**
987
-
988
- ### Step 1: Export Data from 1.x.x
989
-
990
- ```javascript
991
- // In your 1.x.x application
992
- const oldDb = new Database('old-database.jdb', {
993
- indexes: { name: 'string', tags: 'array:string' }
994
- })
995
-
996
- await oldDb.init()
997
- const allData = await oldDb.find({}) // Export all data
998
- await oldDb.destroy()
999
- ```
1000
-
1001
- ### Step 2: Update Your Code
1002
-
1003
- ```javascript
1004
- // ❌ OLD (1.x.x)
1005
- const db = new Database('db.jdb', {
1006
- indexes: { name: 'string', tags: 'array:string' }
1007
- })
1008
-
1009
- // ✅ NEW (2.1.0)
1010
- const db = new Database('db.jdb', {
1011
- fields: { // REQUIRED - Define schema
1012
- id: 'number',
1013
- name: 'string',
1014
- tags: 'array:string'
1015
- },
1016
- indexes: { // OPTIONAL - Performance optimization
1017
- name: 'string', // ✅ Search by name
1018
- tags: 'array:string' // ✅ Search by tags
1019
- }
1020
- })
1021
- ```
1022
-
1023
- ### Step 3: Import Data to 2.1.0
1024
-
1025
- ```javascript
1026
- // In your 2.1.0 application
1027
- const newDb = new Database('new-database.jdb', {
1028
- fields: { /* your schema */ },
1029
- indexes: { /* your indexes */ }
1030
- })
1031
-
1032
- await newDb.init()
1033
-
1034
- // Import all data
1035
- for (const record of allData) {
1036
- await newDb.insert(record)
1037
- }
1038
-
1039
- await newDb.save()
1040
- ```
1041
-
1042
- ### Key Changes Summary
1043
-
1044
- | Feature | 1.x.x | 2.1.0 |
1045
- |---------|-------|-------|
1046
- | `fields` | Optional | **MANDATORY** |
1047
- | `termMapping` | `false` (default) | `true` (default) |
1048
- | `termMappingFields` | Manual config | Auto-detected |
1049
- | Database files | Compatible | **NOT compatible** |
1050
- | Performance | Basic | **77% size reduction** |
1051
-