jexidb 2.1.1 → 2.1.3
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.
- package/dist/Database.cjs +7981 -231
- package/package.json +9 -2
- package/src/Database.mjs +372 -154
- package/src/SchemaManager.mjs +325 -268
- package/src/Serializer.mjs +20 -1
- package/src/managers/QueryManager.mjs +74 -18
- package/.babelrc +0 -13
- package/.gitattributes +0 -2
- package/CHANGELOG.md +0 -140
- package/babel.config.json +0 -5
- package/docs/API.md +0 -1057
- package/docs/EXAMPLES.md +0 -701
- package/docs/README.md +0 -194
- package/examples/iterate-usage-example.js +0 -157
- package/examples/simple-iterate-example.js +0 -115
- package/jest.config.js +0 -24
- package/scripts/README.md +0 -47
- package/scripts/benchmark-array-serialization.js +0 -108
- package/scripts/clean-test-files.js +0 -75
- package/scripts/prepare.js +0 -31
- package/scripts/run-tests.js +0 -80
- package/scripts/score-mode-demo.js +0 -45
- package/test/$not-operator-with-and.test.js +0 -282
- package/test/README.md +0 -8
- package/test/close-init-cycle.test.js +0 -256
- package/test/coverage-method.test.js +0 -93
- package/test/critical-bugs-fixes.test.js +0 -1069
- package/test/deserialize-corruption-fixes.test.js +0 -296
- package/test/exists-method.test.js +0 -318
- package/test/explicit-indexes-comparison.test.js +0 -219
- package/test/filehandler-non-adjacent-ranges-bug.test.js +0 -175
- package/test/index-line-number-regression.test.js +0 -100
- package/test/index-missing-index-data.test.js +0 -91
- package/test/index-persistence.test.js +0 -491
- package/test/index-serialization.test.js +0 -314
- package/test/indexed-query-mode.test.js +0 -360
- package/test/insert-session-auto-flush.test.js +0 -353
- package/test/iterate-method.test.js +0 -272
- package/test/legacy-operator-compat.test.js +0 -154
- package/test/query-operators.test.js +0 -238
- package/test/regex-array-fields.test.js +0 -129
- package/test/score-method.test.js +0 -298
- package/test/setup.js +0 -17
- package/test/term-mapping-minimal.test.js +0 -154
- package/test/term-mapping-simple.test.js +0 -257
- package/test/term-mapping.test.js +0 -514
- package/test/writebuffer-flush-resilience.test.js +0 -204
package/docs/API.md
DELETED
|
@@ -1,1057 +0,0 @@
|
|
|
1
|
-
# JexiDB API Documentation
|
|
2
|
-
|
|
3
|
-
## ⚠️ Version 2.1.0 - Breaking Changes
|
|
4
|
-
|
|
5
|
-
**This version is NOT backward compatible with databases created with previous versions (1.x.x).**
|
|
6
|
-
|
|
7
|
-
### Major Changes:
|
|
8
|
-
- **`fields` is now MANDATORY** - Define your schema structure
|
|
9
|
-
- **Term mapping is auto-enabled** - No manual configuration needed
|
|
10
|
-
- **Database files are incompatible** - Export/import required for migration
|
|
11
|
-
- **Performance improvements** - Up to 77% size reduction for repetitive data
|
|
12
|
-
|
|
13
|
-
## Table of Contents
|
|
14
|
-
|
|
15
|
-
1. [Database Constructor](#database-constructor)
|
|
16
|
-
2. [Core Methods](#core-methods)
|
|
17
|
-
3. [Query Methods](#query-methods)
|
|
18
|
-
4. [Advanced Features](#advanced-features)
|
|
19
|
-
5. [Term Mapping](#term-mapping)
|
|
20
|
-
6. [Bulk Operations](#bulk-operations)
|
|
21
|
-
7. [Configuration Options](#configuration-options)
|
|
22
|
-
8. [Migration Guide](#migration-guide)
|
|
23
|
-
|
|
24
|
-
## Database Constructor
|
|
25
|
-
|
|
26
|
-
```javascript
|
|
27
|
-
import { Database } from 'jexidb'
|
|
28
|
-
|
|
29
|
-
const db = new Database('path/to/database.jdb', options)
|
|
30
|
-
```
|
|
31
|
-
|
|
32
|
-
### Constructor Options
|
|
33
|
-
|
|
34
|
-
| Option | Type | Default | Description |
|
|
35
|
-
|--------|------|---------|-------------|
|
|
36
|
-
| `create` | boolean | `true` | Create the file if it doesn't exist |
|
|
37
|
-
| `clear` | boolean | `false` | Clear existing files before loading |
|
|
38
|
-
| `fields` | object | **Required** | **MANDATORY** - Define the data structure schema |
|
|
39
|
-
| `indexes` | object | `{}` | **Optional** indexed fields for faster queries (not required) |
|
|
40
|
-
| `termMapping` | boolean | `true` | Enable term mapping for optimal performance (auto-detected) |
|
|
41
|
-
| `termMappingFields` | string[] | `[]` | Fields to apply term mapping to (auto-detected from indexes) |
|
|
42
|
-
| `termMappingCleanup` | boolean | `true` | Automatically clean up orphaned terms |
|
|
43
|
-
|
|
44
|
-
### Fields vs Indexes - Important Distinction
|
|
45
|
-
|
|
46
|
-
**Fields** (Schema - **MANDATORY**):
|
|
47
|
-
- ✅ **Required** - Define the structure of your data
|
|
48
|
-
- ✅ **Schema enforcement** - Controls which fields are allowed
|
|
49
|
-
- ✅ **All fields** are queryable by default
|
|
50
|
-
- ✅ **No performance impact** on memory usage
|
|
51
|
-
- ✅ **Data validation** - Ensures data consistency
|
|
52
|
-
|
|
53
|
-
**Indexes** (Performance Optimization - **Optional**):
|
|
54
|
-
- ⚠️ **Optional** - Only for fields you query frequently
|
|
55
|
-
- ⚠️ **Memory overhead** - Each index uses additional memory
|
|
56
|
-
- ⚠️ **Use sparingly** - Only index fields you actually query
|
|
57
|
-
- ⚠️ **Query performance** - Only affects query speed, not functionality
|
|
58
|
-
|
|
59
|
-
### When to Use Indexes
|
|
60
|
-
|
|
61
|
-
```javascript
|
|
62
|
-
// ❌ DON'T: Index everything (wastes memory)
|
|
63
|
-
const db = new Database('db.jdb', {
|
|
64
|
-
fields: { // REQUIRED - Define schema
|
|
65
|
-
id: 'number',
|
|
66
|
-
name: 'string',
|
|
67
|
-
email: 'string',
|
|
68
|
-
phone: 'string',
|
|
69
|
-
address: 'string',
|
|
70
|
-
city: 'string',
|
|
71
|
-
country: 'string',
|
|
72
|
-
status: 'string',
|
|
73
|
-
createdAt: 'number',
|
|
74
|
-
updatedAt: 'number'
|
|
75
|
-
},
|
|
76
|
-
indexes: { // OPTIONAL - Only for performance
|
|
77
|
-
id: 'number', // Maybe needed
|
|
78
|
-
name: 'string', // Maybe needed
|
|
79
|
-
email: 'string', // Maybe needed
|
|
80
|
-
phone: 'string', // Maybe needed
|
|
81
|
-
address: 'string', // Maybe needed
|
|
82
|
-
city: 'string', // Maybe needed
|
|
83
|
-
country: 'string', // Maybe needed
|
|
84
|
-
status: 'string', // Maybe needed
|
|
85
|
-
createdAt: 'number', // Maybe needed
|
|
86
|
-
updatedAt: 'number' // Maybe needed
|
|
87
|
-
}
|
|
88
|
-
})
|
|
89
|
-
|
|
90
|
-
// ✅ DO: Define schema + index only what you query frequently
|
|
91
|
-
const db = new Database('db.jdb', {
|
|
92
|
-
fields: { // REQUIRED - Define schema
|
|
93
|
-
id: 'number',
|
|
94
|
-
name: 'string',
|
|
95
|
-
email: 'string',
|
|
96
|
-
phone: 'string',
|
|
97
|
-
address: 'string',
|
|
98
|
-
city: 'string',
|
|
99
|
-
country: 'string',
|
|
100
|
-
status: 'string',
|
|
101
|
-
createdAt: 'number',
|
|
102
|
-
updatedAt: 'number'
|
|
103
|
-
},
|
|
104
|
-
indexes: { // OPTIONAL - Only for performance
|
|
105
|
-
id: 'number', // Primary key - always index
|
|
106
|
-
email: 'string', // Login queries - index this
|
|
107
|
-
status: 'string' // Filter queries - index this
|
|
108
|
-
}
|
|
109
|
-
// Other fields (name, phone, address, etc.) are still queryable
|
|
110
|
-
// but will use slower sequential search
|
|
111
|
-
})
|
|
112
|
-
```
|
|
113
|
-
|
|
114
|
-
### Example
|
|
115
|
-
|
|
116
|
-
```javascript
|
|
117
|
-
// Example: E-commerce product database
|
|
118
|
-
const db = new Database('products.jdb', {
|
|
119
|
-
create: true,
|
|
120
|
-
clear: false,
|
|
121
|
-
fields: { // REQUIRED - Define schema
|
|
122
|
-
id: 'number',
|
|
123
|
-
name: 'string',
|
|
124
|
-
description: 'string',
|
|
125
|
-
category: 'string',
|
|
126
|
-
tags: 'array:string',
|
|
127
|
-
price: 'number',
|
|
128
|
-
imageUrl: 'string',
|
|
129
|
-
inStock: 'boolean',
|
|
130
|
-
createdAt: 'number'
|
|
131
|
-
},
|
|
132
|
-
indexes: { // OPTIONAL - Only for performance
|
|
133
|
-
id: 'number', // Primary key - always index
|
|
134
|
-
category: 'string', // Filter by category - index this
|
|
135
|
-
tags: 'array:string', // Search by tags - index this
|
|
136
|
-
price: 'number' // Price range queries - index this
|
|
137
|
-
}
|
|
138
|
-
// Fields like 'name', 'description', 'imageUrl' are still queryable
|
|
139
|
-
// but will use slower sequential search (no index needed unless you query them frequently)
|
|
140
|
-
})
|
|
141
|
-
|
|
142
|
-
await db.init()
|
|
143
|
-
|
|
144
|
-
// All these queries work, but some are faster:
|
|
145
|
-
await db.find({ id: 1 }) // ✅ Fast (indexed)
|
|
146
|
-
await db.find({ category: 'electronics' }) // ✅ Fast (indexed)
|
|
147
|
-
await db.find({ tags: 'wireless' }) // ✅ Fast (indexed)
|
|
148
|
-
await db.find({ price: { '>': 100 } }) // ✅ Fast (indexed)
|
|
149
|
-
await db.find({ name: 'iPhone' }) // ⚠️ Slower (not indexed, but still works)
|
|
150
|
-
await db.find({ description: 'wireless' }) // ⚠️ Slower (not indexed, but still works)
|
|
151
|
-
```
|
|
152
|
-
|
|
153
|
-
## Core Methods
|
|
154
|
-
|
|
155
|
-
### `init()`
|
|
156
|
-
|
|
157
|
-
Initialize the database and load existing data.
|
|
158
|
-
|
|
159
|
-
```javascript
|
|
160
|
-
await db.init()
|
|
161
|
-
```
|
|
162
|
-
|
|
163
|
-
### `insert(data)`
|
|
164
|
-
|
|
165
|
-
Insert a new record into the database.
|
|
166
|
-
|
|
167
|
-
```javascript
|
|
168
|
-
await db.insert({
|
|
169
|
-
id: 1,
|
|
170
|
-
name: 'John Doe',
|
|
171
|
-
email: 'john@example.com'
|
|
172
|
-
})
|
|
173
|
-
```
|
|
174
|
-
|
|
175
|
-
### `update(criteria, updates)`
|
|
176
|
-
|
|
177
|
-
Update records matching the criteria.
|
|
178
|
-
|
|
179
|
-
```javascript
|
|
180
|
-
await db.update(
|
|
181
|
-
{ id: 1 },
|
|
182
|
-
{ email: 'newemail@example.com', updated: true }
|
|
183
|
-
)
|
|
184
|
-
```
|
|
185
|
-
|
|
186
|
-
### `delete(criteria)`
|
|
187
|
-
|
|
188
|
-
Delete records matching the criteria.
|
|
189
|
-
|
|
190
|
-
```javascript
|
|
191
|
-
await db.delete({ id: 1 })
|
|
192
|
-
```
|
|
193
|
-
|
|
194
|
-
### `save()`
|
|
195
|
-
|
|
196
|
-
Save all pending changes to disk.
|
|
197
|
-
|
|
198
|
-
```javascript
|
|
199
|
-
await db.save()
|
|
200
|
-
```
|
|
201
|
-
|
|
202
|
-
### `destroy()`
|
|
203
|
-
|
|
204
|
-
Close the database and clean up resources.
|
|
205
|
-
|
|
206
|
-
```javascript
|
|
207
|
-
await db.destroy()
|
|
208
|
-
```
|
|
209
|
-
|
|
210
|
-
## Query Methods
|
|
211
|
-
|
|
212
|
-
### `find(criteria, options)`
|
|
213
|
-
|
|
214
|
-
Find records matching the criteria.
|
|
215
|
-
|
|
216
|
-
```javascript
|
|
217
|
-
// Basic query
|
|
218
|
-
const results = await db.find({ name: 'John Doe' })
|
|
219
|
-
|
|
220
|
-
// Query with conditions
|
|
221
|
-
const results = await db.find({
|
|
222
|
-
age: { '>': 18, '<': 65 },
|
|
223
|
-
status: 'active'
|
|
224
|
-
})
|
|
225
|
-
|
|
226
|
-
// Case insensitive search
|
|
227
|
-
const results = await db.find(
|
|
228
|
-
{ name: 'john doe' },
|
|
229
|
-
{ caseInsensitive: true }
|
|
230
|
-
)
|
|
231
|
-
```
|
|
232
|
-
|
|
233
|
-
### `findOne(criteria, options)`
|
|
234
|
-
|
|
235
|
-
Find the first record matching the criteria.
|
|
236
|
-
|
|
237
|
-
```javascript
|
|
238
|
-
const user = await db.findOne({ id: 1 })
|
|
239
|
-
```
|
|
240
|
-
|
|
241
|
-
### `count(criteria)`
|
|
242
|
-
|
|
243
|
-
Count records matching the criteria.
|
|
244
|
-
|
|
245
|
-
```javascript
|
|
246
|
-
const userCount = await db.count({ status: 'active' })
|
|
247
|
-
```
|
|
248
|
-
|
|
249
|
-
### `score(fieldName, scores, options)`
|
|
250
|
-
|
|
251
|
-
Score and rank records based on weighted terms in an indexed `array:string` field. This method is optimized for in-memory operations and provides 10x+ performance improvement over equivalent `find()` queries.
|
|
252
|
-
|
|
253
|
-
```javascript
|
|
254
|
-
// Basic scoring
|
|
255
|
-
const results = await db.score('tags', {
|
|
256
|
-
'javascript': 1.0,
|
|
257
|
-
'node': 0.8,
|
|
258
|
-
'typescript': 0.9
|
|
259
|
-
})
|
|
260
|
-
|
|
261
|
-
// With options
|
|
262
|
-
const results = await db.score('terms', {
|
|
263
|
-
'action': 1.0,
|
|
264
|
-
'comedy': 0.8
|
|
265
|
-
}, {
|
|
266
|
-
limit: 10,
|
|
267
|
-
sort: 'desc',
|
|
268
|
-
includeScore: true
|
|
269
|
-
})
|
|
270
|
-
```
|
|
271
|
-
|
|
272
|
-
**Parameters:**
|
|
273
|
-
|
|
274
|
-
- `fieldName` (string, required): Name of the indexed `array:string` field to score
|
|
275
|
-
- `scores` (object, required): Map of terms to numeric weights (e.g., `{ 'action': 1.0, 'comedy': 0.8 }`)
|
|
276
|
-
- `options` (object, optional):
|
|
277
|
-
- `limit` (number): Maximum results (default: 100)
|
|
278
|
-
- `sort` (string): "desc" or "asc" (default: "desc")
|
|
279
|
-
- `includeScore` (boolean): Include score in results (default: true)
|
|
280
|
-
- `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:
|
|
285
|
-
|
|
286
|
-
```javascript
|
|
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
|
-
]
|
|
300
|
-
```
|
|
301
|
-
|
|
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).
|
|
308
|
-
|
|
309
|
-
```javascript
|
|
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
|
|
314
|
-
```
|
|
315
|
-
|
|
316
|
-
**Example Use Cases:**
|
|
317
|
-
|
|
318
|
-
```javascript
|
|
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 })
|
|
340
|
-
```
|
|
341
|
-
|
|
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:**
|
|
358
|
-
|
|
359
|
-
```javascript
|
|
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
|
-
}
|
|
368
|
-
```
|
|
369
|
-
|
|
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
|
|
385
|
-
|
|
386
|
-
```javascript
|
|
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
|
-
})
|
|
410
|
-
```
|
|
411
|
-
|
|
412
|
-
## Advanced Features
|
|
413
|
-
|
|
414
|
-
### Indexed Query Mode
|
|
415
|
-
|
|
416
|
-
Control whether queries are restricted to indexed fields only.
|
|
417
|
-
|
|
418
|
-
```javascript
|
|
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
|
|
454
|
-
```
|
|
455
|
-
|
|
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:**
|
|
466
|
-
|
|
467
|
-
```javascript
|
|
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)
|
|
514
|
-
```
|
|
515
|
-
|
|
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:
|
|
538
|
-
|
|
539
|
-
```javascript
|
|
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']
|
|
557
|
-
```
|
|
558
|
-
|
|
559
|
-
### How It Works
|
|
560
|
-
|
|
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
|
|
581
|
-
|
|
582
|
-
```javascript
|
|
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 }
|
|
621
|
-
```
|
|
622
|
-
|
|
623
|
-
### Term Manager API
|
|
624
|
-
|
|
625
|
-
```javascript
|
|
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()
|
|
637
|
-
```
|
|
638
|
-
|
|
639
|
-
## Bulk Operations
|
|
640
|
-
|
|
641
|
-
### `iterate(criteria, options)`
|
|
642
|
-
|
|
643
|
-
High-performance bulk update method with streaming support.
|
|
644
|
-
|
|
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
|
-
```
|
|
672
|
-
|
|
673
|
-
### Iterate Options
|
|
674
|
-
|
|
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 |
|
|
682
|
-
|
|
683
|
-
### Progress Callback
|
|
684
|
-
|
|
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
|
-
```
|
|
694
|
-
|
|
695
|
-
### `beginInsertSession(options)`
|
|
696
|
-
|
|
697
|
-
High-performance batch insertion method for inserting large amounts of data efficiently.
|
|
698
|
-
|
|
699
|
-
```javascript
|
|
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
|
|
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`)
|
|
766
|
-
```
|
|
767
|
-
|
|
768
|
-
### When to Use `beginInsertSession()`
|
|
769
|
-
|
|
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
|
|
775
|
-
|
|
776
|
-
### Performance Comparison
|
|
777
|
-
|
|
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
|
-
```
|
|
791
|
-
|
|
792
|
-
### Performance Tips
|
|
793
|
-
|
|
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
|
|
800
|
-
|
|
801
|
-
## Configuration Options
|
|
802
|
-
|
|
803
|
-
### Database Options
|
|
804
|
-
|
|
805
|
-
```javascript
|
|
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
|
|
810
|
-
|
|
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
|
-
})
|
|
848
|
-
```
|
|
849
|
-
|
|
850
|
-
### Schema vs Indexes - Complete Guide
|
|
851
|
-
|
|
852
|
-
**Understanding the Difference:**
|
|
853
|
-
|
|
854
|
-
```javascript
|
|
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
|
-
```
|
|
903
|
-
|
|
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)
|
|
910
|
-
|
|
911
|
-
### Field Types
|
|
912
|
-
|
|
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
|
-
}
|
|
937
|
-
```
|
|
938
|
-
|
|
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
|
|
973
|
-
|
|
974
|
-
```javascript
|
|
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' })
|
|
986
|
-
```
|
|
987
|
-
|
|
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.**
|
|
993
|
-
|
|
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
|
-
})
|
|
1001
|
-
|
|
1002
|
-
await oldDb.init()
|
|
1003
|
-
const allData = await oldDb.find({}) // Export all data
|
|
1004
|
-
await oldDb.destroy()
|
|
1005
|
-
```
|
|
1006
|
-
|
|
1007
|
-
### Step 2: Update Your Code
|
|
1008
|
-
|
|
1009
|
-
```javascript
|
|
1010
|
-
// ❌ OLD (1.x.x)
|
|
1011
|
-
const db = new Database('db.jdb', {
|
|
1012
|
-
indexes: { name: 'string', tags: 'array:string' }
|
|
1013
|
-
})
|
|
1014
|
-
|
|
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
|
-
```
|
|
1028
|
-
|
|
1029
|
-
### Step 3: Import Data to 2.1.0
|
|
1030
|
-
|
|
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
|
-
})
|
|
1037
|
-
|
|
1038
|
-
await newDb.init()
|
|
1039
|
-
|
|
1040
|
-
// Import all data
|
|
1041
|
-
for (const record of allData) {
|
|
1042
|
-
await newDb.insert(record)
|
|
1043
|
-
}
|
|
1044
|
-
|
|
1045
|
-
await newDb.save()
|
|
1046
|
-
```
|
|
1047
|
-
|
|
1048
|
-
### Key Changes Summary
|
|
1049
|
-
|
|
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** |
|
|
1057
|
-
|