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