jexidb 2.0.3 โ 2.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.babelrc +13 -0
- package/.gitattributes +2 -0
- package/CHANGELOG.md +132 -101
- package/LICENSE +21 -21
- package/README.md +301 -639
- package/babel.config.json +5 -0
- package/dist/Database.cjs +5204 -0
- package/docs/API.md +908 -241
- package/docs/EXAMPLES.md +701 -177
- package/docs/README.md +194 -184
- package/examples/iterate-usage-example.js +157 -0
- package/examples/simple-iterate-example.js +115 -0
- package/jest.config.js +24 -0
- package/package.json +63 -54
- package/scripts/README.md +47 -0
- package/scripts/benchmark-array-serialization.js +108 -0
- package/scripts/clean-test-files.js +75 -0
- package/scripts/prepare.js +31 -0
- package/scripts/run-tests.js +80 -0
- package/scripts/score-mode-demo.js +45 -0
- package/src/Database.mjs +5325 -0
- package/src/FileHandler.mjs +1140 -0
- package/src/OperationQueue.mjs +279 -0
- package/src/SchemaManager.mjs +268 -0
- package/src/Serializer.mjs +702 -0
- package/src/managers/ConcurrencyManager.mjs +257 -0
- package/src/managers/IndexManager.mjs +2094 -0
- package/src/managers/QueryManager.mjs +1490 -0
- package/src/managers/StatisticsManager.mjs +262 -0
- package/src/managers/StreamingProcessor.mjs +429 -0
- package/src/managers/TermManager.mjs +278 -0
- package/src/utils/operatorNormalizer.mjs +116 -0
- package/test/$not-operator-with-and.test.js +282 -0
- package/test/README.md +8 -0
- package/test/close-init-cycle.test.js +256 -0
- package/test/coverage-method.test.js +93 -0
- package/test/critical-bugs-fixes.test.js +1069 -0
- package/test/deserialize-corruption-fixes.test.js +296 -0
- package/test/exists-method.test.js +318 -0
- package/test/explicit-indexes-comparison.test.js +219 -0
- package/test/filehandler-non-adjacent-ranges-bug.test.js +175 -0
- package/test/index-line-number-regression.test.js +100 -0
- package/test/index-missing-index-data.test.js +91 -0
- package/test/index-persistence.test.js +491 -0
- package/test/index-serialization.test.js +314 -0
- package/test/indexed-query-mode.test.js +360 -0
- package/test/insert-session-auto-flush.test.js +353 -0
- package/test/iterate-method.test.js +272 -0
- package/test/legacy-operator-compat.test.js +154 -0
- package/test/query-operators.test.js +238 -0
- package/test/regex-array-fields.test.js +129 -0
- package/test/score-method.test.js +298 -0
- package/test/setup.js +17 -0
- package/test/term-mapping-minimal.test.js +154 -0
- package/test/term-mapping-simple.test.js +257 -0
- package/test/term-mapping.test.js +514 -0
- package/test/writebuffer-flush-resilience.test.js +204 -0
- package/dist/FileHandler.js +0 -688
- package/dist/IndexManager.js +0 -353
- package/dist/IntegrityChecker.js +0 -364
- package/dist/JSONLDatabase.js +0 -1333
- package/dist/index.js +0 -617
- package/docs/MIGRATION.md +0 -295
- package/examples/auto-save-example.js +0 -158
- package/examples/cjs-usage.cjs +0 -82
- package/examples/close-vs-delete-example.js +0 -71
- package/examples/esm-usage.js +0 -113
- package/examples/example-columns.idx.jdb +0 -0
- package/examples/example-columns.jdb +0 -9
- package/examples/example-options.idx.jdb +0 -0
- package/examples/example-options.jdb +0 -0
- package/examples/example-users.idx.jdb +0 -0
- package/examples/example-users.jdb +0 -5
- package/examples/simple-test.js +0 -55
- package/src/FileHandler.js +0 -674
- package/src/IndexManager.js +0 -363
- package/src/IntegrityChecker.js +0 -379
- package/src/JSONLDatabase.js +0 -1391
- package/src/index.js +0 -608
package/docs/README.md
CHANGED
|
@@ -1,184 +1,194 @@
|
|
|
1
|
-
# JexiDB
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
-
|
|
12
|
-
-
|
|
13
|
-
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
-
|
|
18
|
-
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
//
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
const
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
-
|
|
179
|
-
-
|
|
180
|
-
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
1
|
+
# JexiDB Documentation
|
|
2
|
+
|
|
3
|
+
Welcome to the JexiDB documentation! This directory contains comprehensive guides and references for using JexiDB effectively.
|
|
4
|
+
|
|
5
|
+
## ๐ Documentation Structure
|
|
6
|
+
|
|
7
|
+
### ๐ [API Reference](API.md)
|
|
8
|
+
Complete API documentation covering all methods, options, and features:
|
|
9
|
+
- Database constructor and configuration
|
|
10
|
+
- Core methods (insert, update, delete, find)
|
|
11
|
+
- Advanced features (term mapping, bulk operations)
|
|
12
|
+
- Query operators and complex queries
|
|
13
|
+
- Performance optimization tips
|
|
14
|
+
|
|
15
|
+
### ๐ก [Examples](EXAMPLES.md)
|
|
16
|
+
Practical examples and real-world use cases:
|
|
17
|
+
- Basic usage patterns
|
|
18
|
+
- User management systems
|
|
19
|
+
- Product catalogs
|
|
20
|
+
- Blog systems
|
|
21
|
+
- Analytics dashboards
|
|
22
|
+
- Performance optimization techniques
|
|
23
|
+
- Error handling strategies
|
|
24
|
+
|
|
25
|
+
## ๐ฏ Quick Start
|
|
26
|
+
|
|
27
|
+
If you're new to JexiDB, start with these resources:
|
|
28
|
+
|
|
29
|
+
1. **Installation**: See the main [README.md](../README.md) for installation instructions
|
|
30
|
+
2. **Basic Usage**: Check out the [Basic Usage](EXAMPLES.md#basic-usage) section in Examples
|
|
31
|
+
3. **API Reference**: Browse the [API Reference](API.md) for detailed method documentation
|
|
32
|
+
4. **Advanced Features**: Explore [Term Mapping](API.md#term-mapping) and [Bulk Operations](API.md#bulk-operations)
|
|
33
|
+
|
|
34
|
+
## ๐ What's New
|
|
35
|
+
|
|
36
|
+
### Recent Features
|
|
37
|
+
- **Term Mapping**: Reduce database size by up to 77% for repetitive data
|
|
38
|
+
- **Bulk Operations**: High-performance `iterate()` method for large datasets
|
|
39
|
+
- **Advanced Querying**: Support for complex queries with logical operators
|
|
40
|
+
- **Indexed Query Modes**: Strict and permissive query modes for performance control
|
|
41
|
+
|
|
42
|
+
### Performance Improvements
|
|
43
|
+
- Streaming operations for memory efficiency
|
|
44
|
+
- Automatic term cleanup
|
|
45
|
+
- Optimized indexing strategies
|
|
46
|
+
- Batch processing capabilities
|
|
47
|
+
|
|
48
|
+
## ๐ Key Concepts
|
|
49
|
+
|
|
50
|
+
### Indexes
|
|
51
|
+
Define which fields to keep in memory for fast queries:
|
|
52
|
+
```javascript
|
|
53
|
+
const db = new Database('app.jdb', {
|
|
54
|
+
fields: { // REQUIRED - Define schema
|
|
55
|
+
id: 'number',
|
|
56
|
+
name: 'string',
|
|
57
|
+
email: 'string'
|
|
58
|
+
},
|
|
59
|
+
indexes: { // OPTIONAL - Only fields you query frequently
|
|
60
|
+
email: 'string' // โ
Login queries
|
|
61
|
+
}
|
|
62
|
+
})
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
### Term Mapping
|
|
66
|
+
Optimize storage for repetitive string data:
|
|
67
|
+
```javascript
|
|
68
|
+
const db = new Database('app.jdb', {
|
|
69
|
+
fields: { // REQUIRED - Define schema
|
|
70
|
+
id: 'number',
|
|
71
|
+
name: 'string',
|
|
72
|
+
tags: 'array:string',
|
|
73
|
+
categories: 'array:string'
|
|
74
|
+
}
|
|
75
|
+
// termMapping is now auto-enabled for array:string fields
|
|
76
|
+
})
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
### Bulk Operations
|
|
80
|
+
Process large datasets efficiently:
|
|
81
|
+
```javascript
|
|
82
|
+
for await (const record of db.iterate({ status: 'pending' })) {
|
|
83
|
+
record.status = 'processed'
|
|
84
|
+
record.updatedAt = Date.now()
|
|
85
|
+
}
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
## ๐ ๏ธ Common Patterns
|
|
89
|
+
|
|
90
|
+
### Database Lifecycle
|
|
91
|
+
```javascript
|
|
92
|
+
const db = new Database('my-app.jdb', options)
|
|
93
|
+
await db.init() // Initialize
|
|
94
|
+
// ... use database ...
|
|
95
|
+
await db.save() // Save changes
|
|
96
|
+
await db.destroy() // Clean up
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
### Error Handling
|
|
100
|
+
```javascript
|
|
101
|
+
try {
|
|
102
|
+
await db.init()
|
|
103
|
+
await db.insert(data)
|
|
104
|
+
await db.save()
|
|
105
|
+
} catch (error) {
|
|
106
|
+
console.error('Database error:', error)
|
|
107
|
+
} finally {
|
|
108
|
+
await db.destroy()
|
|
109
|
+
}
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
### Query Patterns
|
|
113
|
+
```javascript
|
|
114
|
+
// Simple queries
|
|
115
|
+
const users = await db.find({ status: 'active' })
|
|
116
|
+
|
|
117
|
+
// Complex queries
|
|
118
|
+
const results = await db.find({
|
|
119
|
+
age: { '>': 18, '<': 65 },
|
|
120
|
+
$or: [
|
|
121
|
+
{ role: 'admin' },
|
|
122
|
+
{ role: 'moderator' }
|
|
123
|
+
]
|
|
124
|
+
})
|
|
125
|
+
|
|
126
|
+
// Case-insensitive search
|
|
127
|
+
const results = await db.find(
|
|
128
|
+
{ name: 'john doe' },
|
|
129
|
+
{ caseInsensitive: true }
|
|
130
|
+
)
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
## ๐ง Configuration Options
|
|
134
|
+
|
|
135
|
+
### Basic Configuration
|
|
136
|
+
```javascript
|
|
137
|
+
const db = new Database('database.jdb', {
|
|
138
|
+
create: true, // Create file if doesn't exist
|
|
139
|
+
clear: false, // Clear existing data
|
|
140
|
+
fields: { // REQUIRED - Define schema
|
|
141
|
+
id: 'number',
|
|
142
|
+
name: 'string'
|
|
143
|
+
},
|
|
144
|
+
indexes: { // OPTIONAL - Only fields you query frequently
|
|
145
|
+
name: 'string' // โ
Search by name
|
|
146
|
+
},
|
|
147
|
+
indexedQueryMode: 'permissive' // Query mode
|
|
148
|
+
})
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
### Advanced Configuration
|
|
152
|
+
```javascript
|
|
153
|
+
const db = new Database('database.jdb', {
|
|
154
|
+
fields: { // REQUIRED - Define schema
|
|
155
|
+
id: 'number',
|
|
156
|
+
name: 'string',
|
|
157
|
+
tags: 'array:string'
|
|
158
|
+
},
|
|
159
|
+
indexes: { // OPTIONAL - Only fields you query frequently
|
|
160
|
+
name: 'string', // โ
Search by name
|
|
161
|
+
tags: 'array:string' // โ
Search by tags
|
|
162
|
+
}
|
|
163
|
+
// termMapping is now auto-enabled for array:string fields
|
|
164
|
+
})
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
## ๐ Performance Tips
|
|
168
|
+
|
|
169
|
+
1. **Use indexed fields** in your queries for best performance
|
|
170
|
+
2. **Enable term mapping** for datasets with repetitive strings
|
|
171
|
+
3. **Use `iterate()`** for bulk operations on large datasets
|
|
172
|
+
4. **Specify fewer indexes** to reduce memory usage
|
|
173
|
+
5. **Use `save()`** strategically to persist changes
|
|
174
|
+
|
|
175
|
+
## ๐ Getting Help
|
|
176
|
+
|
|
177
|
+
- **Issues**: Report bugs and request features on [GitHub Issues](https://github.com/EdenwareApps/jexidb/issues)
|
|
178
|
+
- **Documentation**: Browse this documentation for detailed guides
|
|
179
|
+
- **Examples**: Check out the [Examples](EXAMPLES.md) for practical use cases
|
|
180
|
+
- **API Reference**: Consult the [API Reference](API.md) for method details
|
|
181
|
+
|
|
182
|
+
## ๐ Contributing
|
|
183
|
+
|
|
184
|
+
Found an issue with the documentation? Want to add an example?
|
|
185
|
+
|
|
186
|
+
1. Fork the repository
|
|
187
|
+
2. Make your changes
|
|
188
|
+
3. Submit a pull request
|
|
189
|
+
|
|
190
|
+
We welcome contributions to improve the documentation!
|
|
191
|
+
|
|
192
|
+
---
|
|
193
|
+
|
|
194
|
+
**Happy coding with JexiDB! ๐**
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Example usage of the new iterate() method
|
|
3
|
+
* Demonstrates bulk update capabilities with streaming performance
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { Database } from '../src/Database.mjs'
|
|
7
|
+
|
|
8
|
+
async function demonstrateIterate() {
|
|
9
|
+
console.log('๐ JexiDB iterate() Method Demo\n')
|
|
10
|
+
|
|
11
|
+
// Create database with indexing
|
|
12
|
+
const db = new Database('iterate-demo.jdb', {
|
|
13
|
+
debugMode: true,
|
|
14
|
+
termMapping: true,
|
|
15
|
+
indexedFields: ['category', 'price', 'status']
|
|
16
|
+
})
|
|
17
|
+
|
|
18
|
+
await db.init()
|
|
19
|
+
|
|
20
|
+
try {
|
|
21
|
+
// 1. Insert sample data
|
|
22
|
+
console.log('๐ Inserting sample data...')
|
|
23
|
+
const sampleData = [
|
|
24
|
+
{ id: 1, name: 'Apple', category: 'fruits', price: 1.50, status: 'active' },
|
|
25
|
+
{ id: 2, name: 'Banana', category: 'fruits', price: 0.80, status: 'active' },
|
|
26
|
+
{ id: 3, name: 'Orange', category: 'fruits', price: 1.20, status: 'inactive' },
|
|
27
|
+
{ id: 4, name: 'Carrot', category: 'vegetables', price: 0.60, status: 'active' },
|
|
28
|
+
{ id: 5, name: 'Broccoli', category: 'vegetables', price: 1.80, status: 'active' },
|
|
29
|
+
{ id: 6, name: 'Lettuce', category: 'vegetables', price: 0.90, status: 'inactive' },
|
|
30
|
+
{ id: 7, name: 'Milk', category: 'dairy', price: 2.50, status: 'active' },
|
|
31
|
+
{ id: 8, name: 'Cheese', category: 'dairy', price: 3.20, status: 'active' },
|
|
32
|
+
{ id: 9, name: 'Yogurt', category: 'dairy', price: 1.80, status: 'inactive' },
|
|
33
|
+
{ id: 10, name: 'Bread', category: 'bakery', price: 2.00, status: 'active' }
|
|
34
|
+
]
|
|
35
|
+
|
|
36
|
+
for (const item of sampleData) {
|
|
37
|
+
await db.insert(item)
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
console.log(`โ
Inserted ${sampleData.length} records\n`)
|
|
41
|
+
|
|
42
|
+
// 2. Basic iteration without modifications
|
|
43
|
+
console.log('๐ Basic iteration - listing all fruits:')
|
|
44
|
+
for await (const entry of db.iterate({ category: 'fruits' })) {
|
|
45
|
+
console.log(` - ${entry.name}: $${entry.price} (${entry.status})`)
|
|
46
|
+
}
|
|
47
|
+
console.log()
|
|
48
|
+
|
|
49
|
+
// 3. Bulk price update with progress tracking
|
|
50
|
+
console.log('๐ฐ Bulk price update - 10% increase for all active items:')
|
|
51
|
+
let processedCount = 0
|
|
52
|
+
let modifiedCount = 0
|
|
53
|
+
|
|
54
|
+
for await (const entry of db.iterate(
|
|
55
|
+
{ status: 'active' },
|
|
56
|
+
{
|
|
57
|
+
chunkSize: 5,
|
|
58
|
+
progressCallback: (progress) => {
|
|
59
|
+
if (progress.processed % 5 === 0) {
|
|
60
|
+
console.log(` ๐ Progress: ${progress.processed} processed, ${progress.modified} modified`)
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
)) {
|
|
65
|
+
const oldPrice = entry.price
|
|
66
|
+
entry.price = Math.round(entry.price * 1.1 * 100) / 100 // 10% increase, rounded to 2 decimals
|
|
67
|
+
entry.lastUpdated = new Date().toISOString()
|
|
68
|
+
|
|
69
|
+
if (oldPrice !== entry.price) {
|
|
70
|
+
console.log(` ๐ต ${entry.name}: $${oldPrice} โ $${entry.price}`)
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
console.log(`โ
Price update completed: ${processedCount} processed, ${modifiedCount} modified\n`)
|
|
75
|
+
|
|
76
|
+
// 4. Bulk status change with deletions
|
|
77
|
+
console.log('๐๏ธ Bulk operations - removing inactive items and updating status:')
|
|
78
|
+
const inactiveItems = await db.find({ status: 'inactive' })
|
|
79
|
+
for (const item of inactiveItems) {
|
|
80
|
+
console.log(` ๐๏ธ Deleting inactive item: ${item.name}`)
|
|
81
|
+
await db.delete({ id: item.id })
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// Update remaining items
|
|
85
|
+
for await (const entry of db.iterate({})) {
|
|
86
|
+
if (entry.status === 'active') {
|
|
87
|
+
entry.status = 'available'
|
|
88
|
+
entry.updatedAt = new Date().toISOString()
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
console.log('โ
Bulk operations completed\n')
|
|
93
|
+
|
|
94
|
+
// 5. Verify results
|
|
95
|
+
console.log('๐ Final results:')
|
|
96
|
+
const allItems = await db.find({})
|
|
97
|
+
console.log(`Total items remaining: ${allItems.length}`)
|
|
98
|
+
|
|
99
|
+
for (const item of allItems) {
|
|
100
|
+
console.log(` - ${item.name} (${item.category}): $${item.price} [${item.status}]`)
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// 6. Performance demonstration with larger dataset
|
|
104
|
+
console.log('\nโก Performance test with larger dataset...')
|
|
105
|
+
|
|
106
|
+
// Insert more data for performance test
|
|
107
|
+
const startTime = Date.now()
|
|
108
|
+
for (let i = 11; i <= 1000; i++) {
|
|
109
|
+
await db.insert({
|
|
110
|
+
id: i,
|
|
111
|
+
name: `Product${i}`,
|
|
112
|
+
category: ['electronics', 'clothing', 'books', 'home'][i % 4],
|
|
113
|
+
price: Math.random() * 100,
|
|
114
|
+
status: i % 3 === 0 ? 'inactive' : 'active'
|
|
115
|
+
})
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
const insertTime = Date.now() - startTime
|
|
119
|
+
console.log(`๐ Inserted 990 additional records in ${insertTime}ms`)
|
|
120
|
+
|
|
121
|
+
// Bulk update with performance tracking
|
|
122
|
+
const updateStartTime = Date.now()
|
|
123
|
+
let totalProcessed = 0
|
|
124
|
+
|
|
125
|
+
for await (const entry of db.iterate(
|
|
126
|
+
{ status: 'active' },
|
|
127
|
+
{
|
|
128
|
+
chunkSize: 100,
|
|
129
|
+
progressCallback: (progress) => {
|
|
130
|
+
if (progress.completed) {
|
|
131
|
+
totalProcessed = progress.processed
|
|
132
|
+
console.log(`๐ Final stats: ${progress.processed} processed, ${progress.modified} modified in ${progress.elapsed}ms`)
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
)) {
|
|
137
|
+
// Add a processing timestamp
|
|
138
|
+
entry.processedAt = Date.now()
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
const updateTime = Date.now() - updateStartTime
|
|
142
|
+
console.log(`โก Bulk update completed: ${totalProcessed} records processed in ${updateTime}ms`)
|
|
143
|
+
console.log(`๐ Performance: ${Math.round(totalProcessed / (updateTime / 1000))} records/second`)
|
|
144
|
+
|
|
145
|
+
} finally {
|
|
146
|
+
// Clean up
|
|
147
|
+
await db.close()
|
|
148
|
+
console.log('\n๐งน Database closed and cleaned up')
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
// Run the demonstration
|
|
153
|
+
if (import.meta.url === `file://${process.argv[1]}`) {
|
|
154
|
+
demonstrateIterate().catch(console.error)
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
export { demonstrateIterate }
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Simple example of the iterate() method
|
|
3
|
+
* Demonstrates bulk updates with streaming performance
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { Database } from '../src/Database.mjs'
|
|
7
|
+
|
|
8
|
+
async function simpleIterateExample() {
|
|
9
|
+
console.log('๐ JexiDB iterate() Method - Simple Example\n')
|
|
10
|
+
|
|
11
|
+
// Create database
|
|
12
|
+
const db = new Database('simple-iterate.jdb', {
|
|
13
|
+
debugMode: false,
|
|
14
|
+
termMapping: true,
|
|
15
|
+
indexedFields: ['category', 'price']
|
|
16
|
+
})
|
|
17
|
+
|
|
18
|
+
await db.init()
|
|
19
|
+
|
|
20
|
+
try {
|
|
21
|
+
// 1. Insert sample data
|
|
22
|
+
console.log('๐ Inserting sample data...')
|
|
23
|
+
const products = [
|
|
24
|
+
{ id: 1, name: 'Apple', category: 'fruits', price: 1.50, inStock: true },
|
|
25
|
+
{ id: 2, name: 'Banana', category: 'fruits', price: 0.80, inStock: true },
|
|
26
|
+
{ id: 3, name: 'Orange', category: 'fruits', price: 1.20, inStock: false },
|
|
27
|
+
{ id: 4, name: 'Carrot', category: 'vegetables', price: 0.60, inStock: true },
|
|
28
|
+
{ id: 5, name: 'Broccoli', category: 'vegetables', price: 1.80, inStock: true }
|
|
29
|
+
]
|
|
30
|
+
|
|
31
|
+
for (const product of products) {
|
|
32
|
+
await db.insert(product)
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
console.log(`โ
Inserted ${products.length} products\n`)
|
|
36
|
+
|
|
37
|
+
// 2. Basic iteration - list all fruits
|
|
38
|
+
console.log('๐ Listing all fruits:')
|
|
39
|
+
for await (const entry of db.iterate({ category: 'fruits' })) {
|
|
40
|
+
console.log(` - ${entry.name}: $${entry.price} (${entry.inStock ? 'in stock' : 'out of stock'})`)
|
|
41
|
+
}
|
|
42
|
+
console.log()
|
|
43
|
+
|
|
44
|
+
// 3. Bulk price update - 10% increase for in-stock items
|
|
45
|
+
console.log('๐ฐ Bulk price update - 10% increase for in-stock items:')
|
|
46
|
+
let updatedCount = 0
|
|
47
|
+
|
|
48
|
+
for await (const entry of db.iterate({ inStock: true })) {
|
|
49
|
+
const oldPrice = entry.price
|
|
50
|
+
entry.price = Math.round(entry.price * 1.1 * 100) / 100 // 10% increase
|
|
51
|
+
entry.lastUpdated = new Date().toISOString()
|
|
52
|
+
|
|
53
|
+
if (oldPrice !== entry.price) {
|
|
54
|
+
console.log(` ๐ต ${entry.name}: $${oldPrice} โ $${entry.price}`)
|
|
55
|
+
updatedCount++
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
console.log(`โ
Updated ${updatedCount} products\n`)
|
|
60
|
+
|
|
61
|
+
// 4. Bulk status update - mark all vegetables as organic
|
|
62
|
+
console.log('๐ฑ Marking all vegetables as organic:')
|
|
63
|
+
let organicCount = 0
|
|
64
|
+
|
|
65
|
+
for await (const entry of db.iterate({ category: 'vegetables' })) {
|
|
66
|
+
entry.organic = true
|
|
67
|
+
entry.certifiedAt = new Date().toISOString()
|
|
68
|
+
console.log(` ๐ฑ ${entry.name} is now organic`)
|
|
69
|
+
organicCount++
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
console.log(`โ
Marked ${organicCount} vegetables as organic\n`)
|
|
73
|
+
|
|
74
|
+
// 5. Performance test with progress tracking
|
|
75
|
+
console.log('โก Performance test with progress tracking:')
|
|
76
|
+
let processedCount = 0
|
|
77
|
+
|
|
78
|
+
for await (const entry of db.iterate(
|
|
79
|
+
{}, // All records
|
|
80
|
+
{
|
|
81
|
+
chunkSize: 2,
|
|
82
|
+
progressCallback: (progress) => {
|
|
83
|
+
if (progress.completed) {
|
|
84
|
+
console.log(`๐ Final stats: ${progress.processed} processed, ${progress.modified} modified in ${progress.elapsed}ms`)
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
)) {
|
|
89
|
+
// Add processing timestamp
|
|
90
|
+
entry.processedAt = Date.now()
|
|
91
|
+
processedCount++
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
console.log(`โก Processed ${processedCount} records\n`)
|
|
95
|
+
|
|
96
|
+
// 6. Verify all changes
|
|
97
|
+
console.log('๐ Final results:')
|
|
98
|
+
const allProducts = await db.find({})
|
|
99
|
+
for (const product of allProducts) {
|
|
100
|
+
console.log(` - ${product.name} (${product.category}): $${product.price} ${product.organic ? '๐ฑ' : ''} ${product.inStock ? 'โ
' : 'โ'}`)
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
} finally {
|
|
104
|
+
// Clean up
|
|
105
|
+
await db.close()
|
|
106
|
+
console.log('\n๐งน Database closed and cleaned up')
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
// Run the example
|
|
111
|
+
if (import.meta.url === `file://${process.argv[1]}`) {
|
|
112
|
+
simpleIterateExample().catch(console.error)
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
export { simpleIterateExample }
|