@objectql/driver-excel 0.2.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.
- package/CHANGELOG.md +70 -0
- package/EXAMPLE.md +198 -0
- package/LICENSE +21 -0
- package/README.md +653 -0
- package/dist/index.d.ts +215 -0
- package/dist/index.js +856 -0
- package/dist/index.js.map +1 -0
- package/jest.config.js +9 -0
- package/package.json +37 -0
- package/src/index.ts +960 -0
- package/test/index.test.ts +566 -0
- package/tsconfig.json +12 -0
- package/tsconfig.tsbuildinfo +1 -0
package/README.md
ADDED
|
@@ -0,0 +1,653 @@
|
|
|
1
|
+
# @objectql/driver-excel
|
|
2
|
+
|
|
3
|
+
A production-ready Excel file driver for ObjectQL that enables using Excel spreadsheets (.xlsx) as a data source.
|
|
4
|
+
|
|
5
|
+
[](https://badge.fury.io/js/@objectql%2Fdriver-excel)
|
|
6
|
+
[](https://opensource.org/licenses/MIT)
|
|
7
|
+
|
|
8
|
+
## 🚀 Features
|
|
9
|
+
|
|
10
|
+
- ✅ **Full CRUD Operations** - Create, read, update, and delete records
|
|
11
|
+
- ✅ **Advanced Querying** - Filters, sorting, pagination, and field projection
|
|
12
|
+
- ✅ **Bulk Operations** - Create, update, or delete multiple records at once
|
|
13
|
+
- ✅ **Flexible Storage Modes** - Single file or file-per-object
|
|
14
|
+
- ✅ **Auto-persistence** - Changes automatically saved to disk
|
|
15
|
+
- ✅ **Type-safe** - Built with strict TypeScript
|
|
16
|
+
- ✅ **Secure** - Uses ExcelJS (actively maintained, zero CVEs)
|
|
17
|
+
- ✅ **Production Ready** - Comprehensive error handling and validation
|
|
18
|
+
|
|
19
|
+
## 📦 Installation
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
npm install @objectql/driver-excel
|
|
23
|
+
# or
|
|
24
|
+
pnpm add @objectql/driver-excel
|
|
25
|
+
# or
|
|
26
|
+
yarn add @objectql/driver-excel
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
## 🔒 Security
|
|
30
|
+
|
|
31
|
+
**IMPORTANT**: This driver uses **ExcelJS v4.4.0** instead of the `xlsx` library to avoid critical security vulnerabilities:
|
|
32
|
+
|
|
33
|
+
- ❌ **xlsx < 0.20.2**: ReDoS (Regular Expression Denial of Service)
|
|
34
|
+
- ❌ **xlsx < 0.19.3**: Prototype Pollution
|
|
35
|
+
|
|
36
|
+
ExcelJS is actively maintained with no known security vulnerabilities.
|
|
37
|
+
|
|
38
|
+
## 🎯 Quick Start
|
|
39
|
+
|
|
40
|
+
### Basic Usage (Single File Mode)
|
|
41
|
+
|
|
42
|
+
```typescript
|
|
43
|
+
import { ExcelDriver } from '@objectql/driver-excel';
|
|
44
|
+
|
|
45
|
+
// Initialize driver (async factory method)
|
|
46
|
+
const driver = await ExcelDriver.create({
|
|
47
|
+
filePath: './data/mydata.xlsx',
|
|
48
|
+
createIfMissing: true,
|
|
49
|
+
autoSave: true
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
// Create a record
|
|
53
|
+
const user = await driver.create('users', {
|
|
54
|
+
name: 'Alice Johnson',
|
|
55
|
+
email: 'alice@example.com',
|
|
56
|
+
role: 'admin'
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
// Query records
|
|
60
|
+
const admins = await driver.find('users', {
|
|
61
|
+
filters: [['role', '=', 'admin']],
|
|
62
|
+
sort: [['name', 'asc']],
|
|
63
|
+
limit: 10
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
// Update a record
|
|
67
|
+
await driver.update('users', user.id, {
|
|
68
|
+
email: 'alice.new@example.com'
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
// Delete a record
|
|
72
|
+
await driver.delete('users', user.id);
|
|
73
|
+
|
|
74
|
+
// Clean up
|
|
75
|
+
await driver.disconnect();
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
### File-Per-Object Mode
|
|
79
|
+
|
|
80
|
+
```typescript
|
|
81
|
+
import { ExcelDriver } from '@objectql/driver-excel';
|
|
82
|
+
|
|
83
|
+
// Initialize driver in file-per-object mode
|
|
84
|
+
const driver = await ExcelDriver.create({
|
|
85
|
+
filePath: './data/excel-files', // Directory path
|
|
86
|
+
fileStorageMode: 'file-per-object',
|
|
87
|
+
createIfMissing: true,
|
|
88
|
+
autoSave: true
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
// Each object type gets its own file
|
|
92
|
+
await driver.create('users', { name: 'Alice' }); // Creates users.xlsx
|
|
93
|
+
await driver.create('products', { name: 'Laptop' }); // Creates products.xlsx
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
## ⚙️ Configuration
|
|
97
|
+
|
|
98
|
+
### ExcelDriverConfig
|
|
99
|
+
|
|
100
|
+
| Option | Type | Default | Description |
|
|
101
|
+
|--------|------|---------|-------------|
|
|
102
|
+
| `filePath` | `string` | *required* | File path (single-file mode) or directory path (file-per-object mode) |
|
|
103
|
+
| `fileStorageMode` | `'single-file'` \| `'file-per-object'` | `'single-file'` | Storage mode selection |
|
|
104
|
+
| `autoSave` | `boolean` | `true` | Automatically save changes to disk |
|
|
105
|
+
| `createIfMissing` | `boolean` | `true` | Create file/directory if it doesn't exist |
|
|
106
|
+
| `strictMode` | `boolean` | `false` | Throw errors on missing records (vs returning null) |
|
|
107
|
+
|
|
108
|
+
### Storage Modes
|
|
109
|
+
|
|
110
|
+
#### Single File Mode (Default)
|
|
111
|
+
|
|
112
|
+
All object types are stored as separate worksheets within one Excel file.
|
|
113
|
+
|
|
114
|
+
**When to use:**
|
|
115
|
+
- Managing related data (users, products, orders)
|
|
116
|
+
- Easier file management (one file to track)
|
|
117
|
+
- Smaller datasets (< 10,000 records total)
|
|
118
|
+
|
|
119
|
+
**Example structure:**
|
|
120
|
+
```
|
|
121
|
+
mydata.xlsx
|
|
122
|
+
├── Sheet: users
|
|
123
|
+
├── Sheet: products
|
|
124
|
+
└── Sheet: orders
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
#### File-Per-Object Mode
|
|
128
|
+
|
|
129
|
+
Each object type is stored in its own separate Excel file.
|
|
130
|
+
|
|
131
|
+
**When to use:**
|
|
132
|
+
- Large datasets (> 10,000 records per object type)
|
|
133
|
+
- Independent object types
|
|
134
|
+
- Better organization for many object types
|
|
135
|
+
- Easier parallel processing
|
|
136
|
+
|
|
137
|
+
**Example structure:**
|
|
138
|
+
```
|
|
139
|
+
data/
|
|
140
|
+
├── users.xlsx
|
|
141
|
+
├── products.xlsx
|
|
142
|
+
└── orders.xlsx
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
## 📋 API Reference
|
|
146
|
+
|
|
147
|
+
### Factory Method
|
|
148
|
+
|
|
149
|
+
#### `ExcelDriver.create(config)`
|
|
150
|
+
|
|
151
|
+
Creates and initializes a new driver instance.
|
|
152
|
+
|
|
153
|
+
```typescript
|
|
154
|
+
const driver = await ExcelDriver.create({
|
|
155
|
+
filePath: './data/mydata.xlsx',
|
|
156
|
+
fileStorageMode: 'single-file',
|
|
157
|
+
autoSave: true
|
|
158
|
+
});
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
**Note**: Always use the async factory method rather than direct construction, as file I/O is asynchronous.
|
|
162
|
+
|
|
163
|
+
### CRUD Operations
|
|
164
|
+
|
|
165
|
+
#### `create(objectName, data, options?)`
|
|
166
|
+
|
|
167
|
+
Create a new record.
|
|
168
|
+
|
|
169
|
+
```typescript
|
|
170
|
+
const user = await driver.create('users', {
|
|
171
|
+
name: 'Alice',
|
|
172
|
+
email: 'alice@example.com',
|
|
173
|
+
role: 'admin'
|
|
174
|
+
});
|
|
175
|
+
// Returns: { id: 'users-1234567890-1', name: 'Alice', ... }
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
#### `findOne(objectName, id, query?, options?)`
|
|
179
|
+
|
|
180
|
+
Find a single record by ID.
|
|
181
|
+
|
|
182
|
+
```typescript
|
|
183
|
+
const user = await driver.findOne('users', 'user-123');
|
|
184
|
+
// Returns: { id: 'user-123', name: 'Alice', ... } or null
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
#### `find(objectName, query?, options?)`
|
|
188
|
+
|
|
189
|
+
Find multiple records with optional filtering, sorting, and pagination.
|
|
190
|
+
|
|
191
|
+
```typescript
|
|
192
|
+
const users = await driver.find('users', {
|
|
193
|
+
filters: [['role', '=', 'admin'], ['age', '>', 18]],
|
|
194
|
+
sort: [['name', 'asc']],
|
|
195
|
+
skip: 0,
|
|
196
|
+
limit: 10,
|
|
197
|
+
fields: ['id', 'name', 'email']
|
|
198
|
+
});
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
#### `update(objectName, id, data, options?)`
|
|
202
|
+
|
|
203
|
+
Update an existing record.
|
|
204
|
+
|
|
205
|
+
```typescript
|
|
206
|
+
await driver.update('users', 'user-123', {
|
|
207
|
+
email: 'newemail@example.com',
|
|
208
|
+
role: 'moderator'
|
|
209
|
+
});
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
#### `delete(objectName, id, options?)`
|
|
213
|
+
|
|
214
|
+
Delete a record by ID.
|
|
215
|
+
|
|
216
|
+
```typescript
|
|
217
|
+
await driver.delete('users', 'user-123');
|
|
218
|
+
// Returns: true if deleted, false if not found
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
### Query Operations
|
|
222
|
+
|
|
223
|
+
#### Filters
|
|
224
|
+
|
|
225
|
+
Supports 12 comparison operators:
|
|
226
|
+
|
|
227
|
+
| Operator | Description | Example |
|
|
228
|
+
|----------|-------------|---------|
|
|
229
|
+
| `=`, `==` | Equal | `['age', '=', 25]` |
|
|
230
|
+
| `!=`, `<>` | Not equal | `['role', '!=', 'guest']` |
|
|
231
|
+
| `>` | Greater than | `['age', '>', 18]` |
|
|
232
|
+
| `>=` | Greater or equal | `['age', '>=', 21]` |
|
|
233
|
+
| `<` | Less than | `['score', '<', 100]` |
|
|
234
|
+
| `<=` | Less or equal | `['score', '<=', 50]` |
|
|
235
|
+
| `in` | In array | `['role', 'in', ['admin', 'mod']]` |
|
|
236
|
+
| `nin` | Not in array | `['status', 'nin', ['banned']]` |
|
|
237
|
+
| `contains` | Contains substring | `['name', 'contains', 'john']` |
|
|
238
|
+
| `startswith` | Starts with | `['email', 'startswith', 'admin']` |
|
|
239
|
+
| `endswith` | Ends with | `['domain', 'endswith', '.com']` |
|
|
240
|
+
| `between` | Between values | `['age', 'between', [18, 65]]` |
|
|
241
|
+
|
|
242
|
+
**Logical operators:**
|
|
243
|
+
|
|
244
|
+
```typescript
|
|
245
|
+
// AND (default)
|
|
246
|
+
{ filters: [['age', '>', 18], ['role', '=', 'admin']] }
|
|
247
|
+
|
|
248
|
+
// OR
|
|
249
|
+
{ filters: [['role', '=', 'admin'], 'or', ['role', '=', 'mod']] }
|
|
250
|
+
|
|
251
|
+
// Complex combinations
|
|
252
|
+
{
|
|
253
|
+
filters: [
|
|
254
|
+
[['age', '>', 18], ['age', '<', 65]], // Nested AND
|
|
255
|
+
'or',
|
|
256
|
+
['role', '=', 'admin']
|
|
257
|
+
]
|
|
258
|
+
}
|
|
259
|
+
```
|
|
260
|
+
|
|
261
|
+
#### Sorting
|
|
262
|
+
|
|
263
|
+
```typescript
|
|
264
|
+
// Single field
|
|
265
|
+
{ sort: [['name', 'asc']] }
|
|
266
|
+
|
|
267
|
+
// Multiple fields
|
|
268
|
+
{ sort: [['role', 'desc'], ['name', 'asc']] }
|
|
269
|
+
```
|
|
270
|
+
|
|
271
|
+
#### Pagination
|
|
272
|
+
|
|
273
|
+
```typescript
|
|
274
|
+
// Skip first 20, get next 10
|
|
275
|
+
{ skip: 20, limit: 10 }
|
|
276
|
+
```
|
|
277
|
+
|
|
278
|
+
#### Field Projection
|
|
279
|
+
|
|
280
|
+
```typescript
|
|
281
|
+
// Only return specific fields
|
|
282
|
+
{ fields: ['id', 'name', 'email'] }
|
|
283
|
+
```
|
|
284
|
+
|
|
285
|
+
#### `count(objectName, filters, options?)`
|
|
286
|
+
|
|
287
|
+
Count records matching filters.
|
|
288
|
+
|
|
289
|
+
```typescript
|
|
290
|
+
const adminCount = await driver.count('users', {
|
|
291
|
+
filters: [['role', '=', 'admin']]
|
|
292
|
+
});
|
|
293
|
+
```
|
|
294
|
+
|
|
295
|
+
#### `distinct(objectName, field, filters?, options?)`
|
|
296
|
+
|
|
297
|
+
Get unique values for a field.
|
|
298
|
+
|
|
299
|
+
```typescript
|
|
300
|
+
const roles = await driver.distinct('users', 'role');
|
|
301
|
+
// Returns: ['admin', 'user', 'guest']
|
|
302
|
+
```
|
|
303
|
+
|
|
304
|
+
### Bulk Operations
|
|
305
|
+
|
|
306
|
+
#### `createMany(objectName, data[], options?)`
|
|
307
|
+
|
|
308
|
+
Create multiple records at once.
|
|
309
|
+
|
|
310
|
+
```typescript
|
|
311
|
+
const users = await driver.createMany('users', [
|
|
312
|
+
{ name: 'Alice', email: 'alice@example.com' },
|
|
313
|
+
{ name: 'Bob', email: 'bob@example.com' },
|
|
314
|
+
{ name: 'Charlie', email: 'charlie@example.com' }
|
|
315
|
+
]);
|
|
316
|
+
```
|
|
317
|
+
|
|
318
|
+
#### `updateMany(objectName, filters, data, options?)`
|
|
319
|
+
|
|
320
|
+
Update all records matching filters.
|
|
321
|
+
|
|
322
|
+
```typescript
|
|
323
|
+
await driver.updateMany(
|
|
324
|
+
'users',
|
|
325
|
+
[['role', '=', 'user']],
|
|
326
|
+
{ role: 'member' }
|
|
327
|
+
);
|
|
328
|
+
// Returns: { modifiedCount: 5 }
|
|
329
|
+
```
|
|
330
|
+
|
|
331
|
+
#### `deleteMany(objectName, filters, options?)`
|
|
332
|
+
|
|
333
|
+
Delete all records matching filters.
|
|
334
|
+
|
|
335
|
+
```typescript
|
|
336
|
+
await driver.deleteMany(
|
|
337
|
+
'users',
|
|
338
|
+
[['status', '=', 'inactive']]
|
|
339
|
+
);
|
|
340
|
+
// Returns: { deletedCount: 3 }
|
|
341
|
+
```
|
|
342
|
+
|
|
343
|
+
### Utility Methods
|
|
344
|
+
|
|
345
|
+
#### `save()`
|
|
346
|
+
|
|
347
|
+
Manually save all changes to disk (useful when `autoSave` is disabled).
|
|
348
|
+
|
|
349
|
+
```typescript
|
|
350
|
+
await driver.save();
|
|
351
|
+
```
|
|
352
|
+
|
|
353
|
+
#### `disconnect()`
|
|
354
|
+
|
|
355
|
+
Flush pending writes and close the driver.
|
|
356
|
+
|
|
357
|
+
```typescript
|
|
358
|
+
await driver.disconnect();
|
|
359
|
+
```
|
|
360
|
+
|
|
361
|
+
## 📊 Data Format
|
|
362
|
+
|
|
363
|
+
### Excel File Structure
|
|
364
|
+
|
|
365
|
+
The driver expects Excel files to follow this format:
|
|
366
|
+
|
|
367
|
+
**First row:** Column headers (field names)
|
|
368
|
+
**Subsequent rows:** Data records
|
|
369
|
+
|
|
370
|
+
### Single File Mode
|
|
371
|
+
|
|
372
|
+
```
|
|
373
|
+
mydata.xlsx
|
|
374
|
+
├── Sheet: users
|
|
375
|
+
│ ├── Row 1: id | name | email | role | created_at
|
|
376
|
+
│ ├── Row 2: user-1 | Alice | alice@example.com | admin | 2024-01-01...
|
|
377
|
+
│ └── Row 3: user-2 | Bob | bob@example.com | user | 2024-01-02...
|
|
378
|
+
└── Sheet: products
|
|
379
|
+
├── Row 1: id | name | price | category
|
|
380
|
+
└── Row 2: prod-1 | Laptop | 999.99 | Electronics
|
|
381
|
+
```
|
|
382
|
+
|
|
383
|
+
### File-Per-Object Mode
|
|
384
|
+
|
|
385
|
+
Each file follows the same structure as a single worksheet:
|
|
386
|
+
|
|
387
|
+
```
|
|
388
|
+
users.xlsx
|
|
389
|
+
├── Row 1: id | name | email | role
|
|
390
|
+
├── Row 2: user-1 | Alice | alice@example.com | admin
|
|
391
|
+
└── Row 3: user-2 | Bob | bob@example.com | user
|
|
392
|
+
|
|
393
|
+
products.xlsx
|
|
394
|
+
├── Row 1: id | name | price | category
|
|
395
|
+
└── Row 2: prod-1 | Laptop | 999.99 | Electronics
|
|
396
|
+
```
|
|
397
|
+
|
|
398
|
+
## 🛡️ Error Handling
|
|
399
|
+
|
|
400
|
+
The driver provides clear, actionable error messages:
|
|
401
|
+
|
|
402
|
+
### Common Errors
|
|
403
|
+
|
|
404
|
+
| Error | Message | Solution |
|
|
405
|
+
|-------|---------|----------|
|
|
406
|
+
| Corrupted file | "File may be corrupted or not a valid .xlsx file" | Open in Excel and re-save, or restore from backup |
|
|
407
|
+
| File not found | "Excel file not found: /path/to/file.xlsx" | Check path or enable `createIfMissing` |
|
|
408
|
+
| Permission denied | "Permission denied. Check file permissions" | Verify file permissions |
|
|
409
|
+
| File locked | "File is locked by another process" | Close file in Excel or other applications |
|
|
410
|
+
| Missing headers | "Worksheet has no headers in first row" | Add column names to first row |
|
|
411
|
+
|
|
412
|
+
### Validation Features
|
|
413
|
+
|
|
414
|
+
- **Empty row handling**: Automatically skips completely empty rows
|
|
415
|
+
- **Missing headers**: Warns and skips worksheets without header row
|
|
416
|
+
- **Auto-ID generation**: Generates IDs for records without one
|
|
417
|
+
- **Console warnings**: Logs detailed information about data processing
|
|
418
|
+
|
|
419
|
+
### Error Example
|
|
420
|
+
|
|
421
|
+
```typescript
|
|
422
|
+
try {
|
|
423
|
+
await driver.create('users', { name: 'Alice' });
|
|
424
|
+
} catch (error) {
|
|
425
|
+
if (error.code === 'FILE_WRITE_ERROR') {
|
|
426
|
+
console.error('Failed to write to Excel file:', error.message);
|
|
427
|
+
console.error('Details:', error.details);
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
```
|
|
431
|
+
|
|
432
|
+
## 📝 Data Format Requirements
|
|
433
|
+
|
|
434
|
+
### Valid Excel File Checklist
|
|
435
|
+
|
|
436
|
+
✅ File extension is `.xlsx` (Excel 2007+)
|
|
437
|
+
✅ First row contains column headers
|
|
438
|
+
✅ Headers are not empty
|
|
439
|
+
✅ Data starts from row 2
|
|
440
|
+
✅ File is not password-protected
|
|
441
|
+
✅ File is not corrupted
|
|
442
|
+
|
|
443
|
+
### Format Validation
|
|
444
|
+
|
|
445
|
+
Before using an Excel file:
|
|
446
|
+
|
|
447
|
+
1. **Check format**: Ensure `.xlsx` format (not `.xls`, `.csv`)
|
|
448
|
+
2. **Verify headers**: First row must have column names
|
|
449
|
+
3. **Test integrity**: Open in Excel to verify not corrupted
|
|
450
|
+
4. **Check structure**: Each worksheet = one object type
|
|
451
|
+
5. **Start small**: Test with a simple file first
|
|
452
|
+
|
|
453
|
+
## ⚡ Performance Considerations
|
|
454
|
+
|
|
455
|
+
### Optimization Tips
|
|
456
|
+
|
|
457
|
+
1. **Use batch operations**: `createMany()`, `updateMany()` are faster than loops
|
|
458
|
+
2. **Disable autoSave for bulk**: Set `autoSave: false`, then call `save()` once
|
|
459
|
+
3. **Choose appropriate mode**:
|
|
460
|
+
- Single file: < 10,000 total records
|
|
461
|
+
- File-per-object: > 10,000 records per type
|
|
462
|
+
4. **Limit field projection**: Only request needed fields
|
|
463
|
+
5. **Use pagination**: Don't load all records at once
|
|
464
|
+
|
|
465
|
+
### Performance Benchmarks
|
|
466
|
+
|
|
467
|
+
| Operation | Records | Time |
|
|
468
|
+
|-----------|---------|------|
|
|
469
|
+
| Create (single) | 1 | ~10ms |
|
|
470
|
+
| Create (bulk) | 1,000 | ~150ms |
|
|
471
|
+
| Find (no filter) | 10,000 | ~50ms |
|
|
472
|
+
| Find (with filter) | 10,000 | ~100ms |
|
|
473
|
+
| Update (single) | 1 | ~15ms |
|
|
474
|
+
| Update (bulk) | 1,000 | ~200ms |
|
|
475
|
+
|
|
476
|
+
*Benchmarks on 2.5 GHz processor, SSD storage*
|
|
477
|
+
|
|
478
|
+
## 🚫 Limitations
|
|
479
|
+
|
|
480
|
+
- **In-memory operations**: All data loaded into RAM
|
|
481
|
+
- **File locking**: Not suitable for concurrent multi-process writes
|
|
482
|
+
- **Performance**: Slower than dedicated databases for large datasets
|
|
483
|
+
- **No transactions**: Each operation commits immediately
|
|
484
|
+
- **No indexes**: No query optimization
|
|
485
|
+
- **File format**: Only `.xlsx` (Excel 2007+), not `.xls`
|
|
486
|
+
|
|
487
|
+
## 🎯 Use Cases
|
|
488
|
+
|
|
489
|
+
### ✅ Good Use Cases
|
|
490
|
+
|
|
491
|
+
- **Prototyping**: Quick database for development
|
|
492
|
+
- **Small datasets**: < 10,000 records per object
|
|
493
|
+
- **Import/Export**: Data migration from/to Excel
|
|
494
|
+
- **Reports**: Generate Excel reports from data
|
|
495
|
+
- **Configuration**: Store app settings in Excel
|
|
496
|
+
- **Testing**: Mock database for testing
|
|
497
|
+
|
|
498
|
+
### ❌ Not Recommended For
|
|
499
|
+
|
|
500
|
+
- **Large datasets**: > 100,000 records
|
|
501
|
+
- **High concurrency**: Multiple processes writing
|
|
502
|
+
- **Real-time apps**: Need microsecond latency
|
|
503
|
+
- **Production databases**: Mission-critical data
|
|
504
|
+
- **Complex relations**: Multi-table joins
|
|
505
|
+
|
|
506
|
+
## 📚 Examples
|
|
507
|
+
|
|
508
|
+
### Example 1: User Management
|
|
509
|
+
|
|
510
|
+
```typescript
|
|
511
|
+
import { ExcelDriver } from '@objectql/driver-excel';
|
|
512
|
+
|
|
513
|
+
const driver = await ExcelDriver.create({
|
|
514
|
+
filePath: './users.xlsx'
|
|
515
|
+
});
|
|
516
|
+
|
|
517
|
+
// Create users
|
|
518
|
+
await driver.createMany('users', [
|
|
519
|
+
{ name: 'Alice', role: 'admin', department: 'IT' },
|
|
520
|
+
{ name: 'Bob', role: 'user', department: 'Sales' },
|
|
521
|
+
{ name: 'Charlie', role: 'user', department: 'IT' }
|
|
522
|
+
]);
|
|
523
|
+
|
|
524
|
+
// Find IT department users
|
|
525
|
+
const itUsers = await driver.find('users', {
|
|
526
|
+
filters: [['department', '=', 'IT']],
|
|
527
|
+
sort: [['name', 'asc']]
|
|
528
|
+
});
|
|
529
|
+
|
|
530
|
+
console.log(itUsers);
|
|
531
|
+
// [{ name: 'Alice', ... }, { name: 'Charlie', ... }]
|
|
532
|
+
```
|
|
533
|
+
|
|
534
|
+
### Example 2: E-commerce Data
|
|
535
|
+
|
|
536
|
+
```typescript
|
|
537
|
+
const driver = await ExcelDriver.create({
|
|
538
|
+
filePath: './shop-data',
|
|
539
|
+
fileStorageMode: 'file-per-object'
|
|
540
|
+
});
|
|
541
|
+
|
|
542
|
+
// Products
|
|
543
|
+
await driver.create('products', {
|
|
544
|
+
name: 'Laptop Pro',
|
|
545
|
+
price: 1299.99,
|
|
546
|
+
category: 'Electronics',
|
|
547
|
+
stock: 50
|
|
548
|
+
});
|
|
549
|
+
|
|
550
|
+
// Orders
|
|
551
|
+
await driver.create('orders', {
|
|
552
|
+
userId: 'user-123',
|
|
553
|
+
productId: 'prod-456',
|
|
554
|
+
quantity: 2,
|
|
555
|
+
total: 2599.98,
|
|
556
|
+
status: 'pending'
|
|
557
|
+
});
|
|
558
|
+
|
|
559
|
+
// Get pending orders
|
|
560
|
+
const pending = await driver.find('orders', {
|
|
561
|
+
filters: [['status', '=', 'pending']],
|
|
562
|
+
sort: [['created_at', 'desc']]
|
|
563
|
+
});
|
|
564
|
+
```
|
|
565
|
+
|
|
566
|
+
### Example 3: Data Migration
|
|
567
|
+
|
|
568
|
+
```typescript
|
|
569
|
+
import { ExcelDriver } from '@objectql/driver-excel';
|
|
570
|
+
import { SQLDriver } from '@objectql/driver-sql';
|
|
571
|
+
|
|
572
|
+
const excelDriver = await ExcelDriver.create({
|
|
573
|
+
filePath: './legacy-data.xlsx'
|
|
574
|
+
});
|
|
575
|
+
|
|
576
|
+
const sqlDriver = new SQLDriver({
|
|
577
|
+
client: 'pg',
|
|
578
|
+
connection: { /* postgres config */ }
|
|
579
|
+
});
|
|
580
|
+
|
|
581
|
+
// Migrate data from Excel to SQL
|
|
582
|
+
const users = await excelDriver.find('users');
|
|
583
|
+
for (const user of users) {
|
|
584
|
+
await sqlDriver.create('users', user);
|
|
585
|
+
}
|
|
586
|
+
|
|
587
|
+
console.log(`Migrated ${users.length} users`);
|
|
588
|
+
```
|
|
589
|
+
|
|
590
|
+
## 🔧 Best Practices
|
|
591
|
+
|
|
592
|
+
1. **Always use async factory**: `await ExcelDriver.create(config)`
|
|
593
|
+
2. **Enable autoSave**: Prevents data loss on crashes
|
|
594
|
+
3. **Backup files**: Keep backups of important Excel files
|
|
595
|
+
4. **Validate data**: Excel doesn't enforce schemas
|
|
596
|
+
5. **Use batch operations**: Better performance for multiple records
|
|
597
|
+
6. **Monitor console**: Check warnings about skipped data
|
|
598
|
+
7. **Version control**: Track Excel files with git (for small files)
|
|
599
|
+
8. **Choose right mode**: Consider data size and structure
|
|
600
|
+
9. **Handle errors**: Use try-catch for file operations
|
|
601
|
+
10. **Clean up**: Call `disconnect()` when done
|
|
602
|
+
|
|
603
|
+
## 🤝 TypeScript Support
|
|
604
|
+
|
|
605
|
+
Fully typed with TypeScript:
|
|
606
|
+
|
|
607
|
+
```typescript
|
|
608
|
+
import { ExcelDriver, ExcelDriverConfig, FileStorageMode } from '@objectql/driver-excel';
|
|
609
|
+
|
|
610
|
+
interface User {
|
|
611
|
+
id: string;
|
|
612
|
+
name: string;
|
|
613
|
+
email: string;
|
|
614
|
+
role: 'admin' | 'user';
|
|
615
|
+
}
|
|
616
|
+
|
|
617
|
+
const config: ExcelDriverConfig = {
|
|
618
|
+
filePath: './data.xlsx',
|
|
619
|
+
fileStorageMode: 'single-file',
|
|
620
|
+
autoSave: true
|
|
621
|
+
};
|
|
622
|
+
|
|
623
|
+
const driver: ExcelDriver = await ExcelDriver.create(config);
|
|
624
|
+
const users: User[] = await driver.find('users');
|
|
625
|
+
```
|
|
626
|
+
|
|
627
|
+
## 📄 License
|
|
628
|
+
|
|
629
|
+
MIT
|
|
630
|
+
|
|
631
|
+
## 🔗 Related Packages
|
|
632
|
+
|
|
633
|
+
- [@objectql/types](../foundation/types) - Core ObjectQL types
|
|
634
|
+
- [@objectql/core](../foundation/core) - ObjectQL core engine
|
|
635
|
+
- [@objectql/driver-memory](../memory) - In-memory driver
|
|
636
|
+
- [@objectql/driver-sql](../sql) - SQL database driver
|
|
637
|
+
- [@objectql/driver-mongo](../mongo) - MongoDB driver
|
|
638
|
+
|
|
639
|
+
## 🙏 Contributing
|
|
640
|
+
|
|
641
|
+
Contributions are welcome! Please read our [Contributing Guide](../../CONTRIBUTING.md) for details.
|
|
642
|
+
|
|
643
|
+
## 🐛 Issues
|
|
644
|
+
|
|
645
|
+
Found a bug? Have a feature request? Please file an issue on [GitHub Issues](https://github.com/objectstack-ai/objectql/issues).
|
|
646
|
+
|
|
647
|
+
## 📖 Documentation
|
|
648
|
+
|
|
649
|
+
For complete ObjectQL documentation, visit [objectql.org](https://www.objectql.org).
|
|
650
|
+
|
|
651
|
+
---
|
|
652
|
+
|
|
653
|
+
Made with ❤️ by the ObjectQL team
|