jexidb 2.0.3 → 2.1.0

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 (67) 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 +3896 -0
  8. package/docs/API.md +1051 -390
  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/clean-test-files.js +75 -0
  17. package/scripts/prepare.js +31 -0
  18. package/scripts/run-tests.js +80 -0
  19. package/src/Database.mjs +4130 -0
  20. package/src/FileHandler.mjs +1101 -0
  21. package/src/OperationQueue.mjs +279 -0
  22. package/src/SchemaManager.mjs +268 -0
  23. package/src/Serializer.mjs +511 -0
  24. package/src/managers/ConcurrencyManager.mjs +257 -0
  25. package/src/managers/IndexManager.mjs +1403 -0
  26. package/src/managers/QueryManager.mjs +1273 -0
  27. package/src/managers/StatisticsManager.mjs +262 -0
  28. package/src/managers/StreamingProcessor.mjs +429 -0
  29. package/src/managers/TermManager.mjs +278 -0
  30. package/test/$not-operator-with-and.test.js +282 -0
  31. package/test/README.md +8 -0
  32. package/test/close-init-cycle.test.js +256 -0
  33. package/test/critical-bugs-fixes.test.js +1069 -0
  34. package/test/index-persistence.test.js +306 -0
  35. package/test/index-serialization.test.js +314 -0
  36. package/test/indexed-query-mode.test.js +360 -0
  37. package/test/iterate-method.test.js +272 -0
  38. package/test/query-operators.test.js +238 -0
  39. package/test/regex-array-fields.test.js +129 -0
  40. package/test/score-method.test.js +238 -0
  41. package/test/setup.js +17 -0
  42. package/test/term-mapping-minimal.test.js +154 -0
  43. package/test/term-mapping-simple.test.js +257 -0
  44. package/test/term-mapping.test.js +514 -0
  45. package/test/writebuffer-flush-resilience.test.js +204 -0
  46. package/dist/FileHandler.js +0 -688
  47. package/dist/IndexManager.js +0 -353
  48. package/dist/IntegrityChecker.js +0 -364
  49. package/dist/JSONLDatabase.js +0 -1333
  50. package/dist/index.js +0 -617
  51. package/docs/MIGRATION.md +0 -295
  52. package/examples/auto-save-example.js +0 -158
  53. package/examples/cjs-usage.cjs +0 -82
  54. package/examples/close-vs-delete-example.js +0 -71
  55. package/examples/esm-usage.js +0 -113
  56. package/examples/example-columns.idx.jdb +0 -0
  57. package/examples/example-columns.jdb +0 -9
  58. package/examples/example-options.idx.jdb +0 -0
  59. package/examples/example-options.jdb +0 -0
  60. package/examples/example-users.idx.jdb +0 -0
  61. package/examples/example-users.jdb +0 -5
  62. package/examples/simple-test.js +0 -55
  63. package/src/FileHandler.js +0 -674
  64. package/src/IndexManager.js +0 -363
  65. package/src/IntegrityChecker.js +0 -379
  66. package/src/JSONLDatabase.js +0 -1391
  67. package/src/index.js +0 -608
package/docs/API.md CHANGED
@@ -1,390 +1,1051 @@
1
- # JexiDB API Reference
2
-
3
- ## Overview
4
-
5
- JexiDB is a high-performance, local JSONL database with intelligent optimizations, real compression, and comprehensive error handling.
6
-
7
- ## Core Classes
8
-
9
- ### Database
10
-
11
- The main database class that provides CRUD operations with intelligent optimizations.
12
-
13
- #### Constructor
14
-
15
- ```javascript
16
- new Database(filePath, options = {})
17
- ```
18
-
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)
42
-
43
- #### Methods
44
-
45
- ##### init()
46
- Initializes the database and loads existing data.
47
-
48
- ```javascript
49
- await db.init()
50
- ```
51
-
52
- ##### insert(data)
53
- Inserts a single record with adaptive optimization.
54
-
55
- ```javascript
56
- const record = await db.insert({ id: '1', name: 'John' })
57
- ```
58
-
59
- ##### insertMany(dataArray)
60
- Inserts multiple records with bulk optimization.
61
-
62
- ```javascript
63
- const records = await db.insertMany([
64
- { id: '1', name: 'John' },
65
- { id: '2', name: 'Jane' }
66
- ])
67
- ```
68
-
69
- ##### find(criteria, options)
70
- Finds records matching criteria with query optimization.
71
-
72
- ```javascript
73
- const results = await db.find(
74
- { age: { $gte: 25 } },
75
- { limit: 10, sort: { name: 1 } }
76
- )
77
- ```
78
-
79
- ##### findOne(criteria, options)
80
- Finds a single record matching criteria.
81
-
82
- ```javascript
83
- const user = await db.findOne({ id: '1' })
84
- ```
85
-
86
- ##### update(criteria, updates, options)
87
- Updates records matching criteria.
88
-
89
- ```javascript
90
- const updated = await db.update(
91
- { id: '1' },
92
- { name: 'John Updated', age: 30 }
93
- )
94
- ```
95
-
96
- ##### delete(criteria, options)
97
- Deletes records matching criteria.
98
-
99
- ```javascript
100
- const deleted = await db.delete({ id: '1' })
101
- ```
102
-
103
- ##### count(criteria)
104
- Counts records matching criteria.
105
-
106
- ```javascript
107
- const count = await db.count({ active: true })
108
- ```
109
-
110
- ##### save()
111
- Saves pending changes to disk.
112
-
113
- ```javascript
114
- await db.save()
115
- ```
116
-
117
- ##### flush()
118
- Flushes the insertion buffer to disk immediately.
119
-
120
- ```javascript
121
- const flushedCount = await db.flush()
122
- // Returns: number of records flushed
123
- ```
124
-
125
- ##### forceSave()
126
- Forces a save operation regardless of buffer size.
127
-
128
- ```javascript
129
- await db.forceSave()
130
- // Always saves, even with just 1 record in buffer
131
- ```
132
-
133
- ##### getBufferStatus()
134
- Gets information about the current buffer state.
135
-
136
- ```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
- // }
147
- ```
148
-
149
- ##### configurePerformance(settings)
150
- Dynamically configures performance settings.
151
-
152
- ```javascript
153
- db.configurePerformance({
154
- batchSize: 25,
155
- autoSaveThreshold: 30,
156
- autoSaveInterval: 4000
157
- })
158
- ```
159
-
160
- ##### getPerformanceConfig()
161
- Gets current performance configuration.
162
-
163
- ```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
- // }
173
- ```
174
-
175
- ##### validateIntegrity(options)
176
- Validates database integrity.
177
-
178
- ```javascript
179
- const integrity = await db.validateIntegrity({ verbose: true })
180
- ```
181
-
182
- ##### getStats()
183
- Gets comprehensive database statistics.
184
-
185
- ```javascript
186
- const stats = await db.getStats()
187
- ```
188
-
189
- ##### readColumnIndex(column)
190
- Gets unique values from a specific column (indexed columns only).
191
-
192
- ```javascript
193
- const categories = db.readColumnIndex('category')
194
- // Returns: Set(['Electronics', 'Books', 'Clothing'])
195
- // Throws error for non-indexed columns
196
- ```
197
-
198
- ##### close()
199
- Closes the database instance and saves pending changes.
200
-
201
- ```javascript
202
- await db.close()
203
- // Saves data and closes instance, but keeps the database file
204
- ```
205
-
206
- ##### destroy()
207
- Closes the database instance and saves pending changes (equivalent to close()).
208
-
209
- ```javascript
210
- await db.destroy()
211
- // Same as: await db.close()
212
- ```
213
-
214
- ##### deleteDatabase()
215
- **⚠️ WARNING: This permanently deletes the database file!**
216
-
217
- Deletes the database file from disk and closes the instance.
218
-
219
- ```javascript
220
- await db.deleteDatabase() // Deletes the database file permanently
221
- ```
222
-
223
- ##### removeDatabase()
224
- Removes the database file from disk (alias for deleteDatabase).
225
-
226
- ```javascript
227
- await db.removeDatabase() // Same as: await db.deleteDatabase()
228
- ```
229
-
230
- #### Properties
231
-
232
- - `length`: Number of records in the database
233
- - `indexStats`: Statistics about database indexes
234
-
235
- #### Events
236
-
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
243
-
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
252
-
253
- ### Query Operators
254
-
255
- The database supports MongoDB-style query operators:
256
-
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
266
-
267
- ### Nested Field Queries
268
-
269
- You can query nested fields using dot notation:
270
-
271
- ```javascript
272
- const results = await db.find({
273
- 'metadata.preferences.theme': 'dark',
274
- 'metadata.loginCount': { $gt: 10 }
275
- })
276
- ```
277
-
278
- ### Query Options
279
-
280
- - `limit`: Limit number of results
281
- - `skip`: Skip number of results
282
- - `sort`: Sort results by field
283
- - `caseInsensitive`: Case-insensitive string matching
284
-
285
- ## Auto-Save Intelligence
286
-
287
- JexiDB features intelligent auto-save capabilities that automatically manage data persistence without manual intervention.
288
-
289
- ### Auto-Save Modes
290
-
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
296
-
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
301
-
302
- ### Auto-Save Configuration
303
-
304
- ```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
311
-
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
- });
318
- ```
319
-
320
- ### Event-Driven Monitoring
321
-
322
- ```javascript
323
- // Monitor auto-save operations
324
- db.on('buffer-flush', (count) => {
325
- console.log(`Flushed ${count} records`);
326
- });
327
-
328
- db.on('buffer-full', () => {
329
- console.log('Buffer reached threshold');
330
- });
331
-
332
- db.on('auto-save-timer', () => {
333
- console.log('Time-based auto-save triggered');
334
- });
335
-
336
- db.on('save-complete', () => {
337
- console.log('Database saved successfully');
338
- });
339
- ```
340
-
341
- ### Buffer Status Monitoring
342
-
343
- ```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}`);
349
- ```
350
-
351
- ## Optimization Features
352
-
353
- ### Adaptive Mode Switching
354
-
355
- JexiDB automatically switches between insertion and query optimization modes based on usage patterns.
356
-
357
- ### Real Compression
358
-
359
- - **LZ4**: Fast compression for warm data
360
- - **Gzip**: High compression for cold data
361
- - **Automatic fallback**: Graceful degradation if compression fails
362
-
363
- ### Intelligent Caching
364
-
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
368
-
369
- ### Background Maintenance
370
-
371
- - **Automatic compression**: Compresses old data in the background
372
- - **Index optimization**: Optimizes indexes during idle time
373
- - **Integrity checks**: Performs periodic integrity validation
374
-
375
- ## Error Handling
376
-
377
- Comprehensive error handling with automatic recovery:
378
-
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
383
-
384
- ## Performance Characteristics
385
-
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
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
+