@gravito/dark-matter 1.0.0 → 1.1.2
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/README.md +235 -15
- package/dist/index.cjs +678 -20
- package/dist/index.d.cts +463 -2
- package/dist/index.d.ts +463 -2
- package/dist/index.js +675 -18
- package/package.json +16 -4
package/README.md
CHANGED
|
@@ -35,17 +35,30 @@ const users = await Mongo.collection('users')
|
|
|
35
35
|
await Mongo.disconnect()
|
|
36
36
|
```
|
|
37
37
|
|
|
38
|
-
## Features
|
|
39
|
-
|
|
40
|
-
- 🚀 **Bun Native
|
|
41
|
-
-
|
|
42
|
-
-
|
|
43
|
-
-
|
|
44
|
-
-
|
|
45
|
-
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
38
|
+
## ✨ Features
|
|
39
|
+
|
|
40
|
+
- 🚀 **Bun Native Performance**: Optimized for the Bun runtime with zero-copy data handling.
|
|
41
|
+
- 🌌 **Galaxy-Ready Persistence**: Native integration with PlanetCore for universal NoSQL data management.
|
|
42
|
+
- 🎯 **Laravel-style API**: Familiar, fluent interface for MongoDB collections and aggregation pipelines.
|
|
43
|
+
- 🛡️ **Distributed Document State**: ACID transactions and robust multi-connection management across the Galaxy.
|
|
44
|
+
- 🗑️ **Soft Deletes**: Built-in logic for logical record deletion with restoration support.
|
|
45
|
+
- 📦 **GridFS & Streams**: Specialized support for large-scale file storage and real-time change listening.
|
|
46
|
+
|
|
47
|
+
## 🌌 Role in Galaxy Architecture
|
|
48
|
+
|
|
49
|
+
In the **Gravito Galaxy Architecture**, Dark Matter acts as the **MongoDB Gravity Core (NoSQL Persistence)**.
|
|
50
|
+
|
|
51
|
+
- **Document Focus**: Provides an alternative gravity core for Satellites that require schema-less flexibility or massive-scale document storage.
|
|
52
|
+
- **Micro-State Bridge**: Enables real-time reactive logic via MongoDB Change Streams, allowing one Satellite to react instantly to data changes in another.
|
|
53
|
+
- **File Distribution**: Leverages GridFS to provide a distributed file storage layer that works alongside `@gravito/nebula`.
|
|
54
|
+
|
|
55
|
+
```mermaid
|
|
56
|
+
graph TD
|
|
57
|
+
S1[Satellite: Analytics] -- "Save" --> DM{Dark Matter}
|
|
58
|
+
S2[Satellite: Logs] -- "Query" --> DM
|
|
59
|
+
DM -->|Native Driver| MongoDB[(MongoDB Atlas)]
|
|
60
|
+
DM -.->|Watch| Realtime[Real-time Events]
|
|
61
|
+
```
|
|
49
62
|
|
|
50
63
|
## API Reference
|
|
51
64
|
|
|
@@ -123,6 +136,52 @@ await Mongo.collection('logs').bulkWrite([
|
|
|
123
136
|
])
|
|
124
137
|
```
|
|
125
138
|
|
|
139
|
+
### Soft Deletes
|
|
140
|
+
|
|
141
|
+
Dark Matter 支援開箱即用的軟刪除功能:
|
|
142
|
+
|
|
143
|
+
```typescript
|
|
144
|
+
// 軟刪除一筆記錄(設置 deletedAt)
|
|
145
|
+
await Mongo.collection('users')
|
|
146
|
+
.where('_id', userId)
|
|
147
|
+
.softDelete()
|
|
148
|
+
|
|
149
|
+
// 查詢時自動排除已軟刪除的記錄
|
|
150
|
+
const activeUsers = await Mongo.collection('users').get()
|
|
151
|
+
|
|
152
|
+
// 包含已軟刪除的記錄
|
|
153
|
+
const allUsers = await Mongo.collection('users')
|
|
154
|
+
.withTrashed()
|
|
155
|
+
.get()
|
|
156
|
+
|
|
157
|
+
// 只查詢已軟刪除的記錄
|
|
158
|
+
const trashedUsers = await Mongo.collection('users')
|
|
159
|
+
.onlyTrashed()
|
|
160
|
+
.get()
|
|
161
|
+
|
|
162
|
+
// 恢復軟刪除的記錄
|
|
163
|
+
await Mongo.collection('users')
|
|
164
|
+
.where('_id', userId)
|
|
165
|
+
.restore()
|
|
166
|
+
|
|
167
|
+
// 批次軟刪除
|
|
168
|
+
await Mongo.collection('users')
|
|
169
|
+
.where('status', 'inactive')
|
|
170
|
+
.softDeleteMany()
|
|
171
|
+
|
|
172
|
+
// 批次恢復
|
|
173
|
+
await Mongo.collection('users')
|
|
174
|
+
.onlyTrashed()
|
|
175
|
+
.restoreMany()
|
|
176
|
+
|
|
177
|
+
// 永久刪除記錄
|
|
178
|
+
await Mongo.collection('users')
|
|
179
|
+
.where('_id', userId)
|
|
180
|
+
.forceDelete()
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
**注意**:軟刪除使用 `deletedAt` 欄位(`Date | null`)。請確保在文檔中加入此欄位。
|
|
184
|
+
|
|
126
185
|
### Aggregation Pipeline
|
|
127
186
|
|
|
128
187
|
```typescript
|
|
@@ -152,6 +211,54 @@ const ordersWithCustomers = await Mongo.collection('orders')
|
|
|
152
211
|
.get()
|
|
153
212
|
```
|
|
154
213
|
|
|
214
|
+
### Schema Validation
|
|
215
|
+
|
|
216
|
+
使用友善的 Schema Builder API 建立型別安全的 MongoDB Schema 驗證:
|
|
217
|
+
|
|
218
|
+
```typescript
|
|
219
|
+
import { schema } from '@gravito/dark-matter'
|
|
220
|
+
|
|
221
|
+
// 建構 Schema
|
|
222
|
+
const userSchema = schema()
|
|
223
|
+
.required('username', 'email', 'createdAt')
|
|
224
|
+
.string('username', { minLength: 3, maxLength: 50 })
|
|
225
|
+
.string('email', { pattern: '^.+@.+$' })
|
|
226
|
+
.integer('age', { minimum: 0, maximum: 150 })
|
|
227
|
+
.boolean('isActive')
|
|
228
|
+
.date('createdAt')
|
|
229
|
+
.array('roles', 'string', { minItems: 1 })
|
|
230
|
+
.object('profile', (s) =>
|
|
231
|
+
s
|
|
232
|
+
.string('bio', { maxLength: 500 })
|
|
233
|
+
.string('avatar')
|
|
234
|
+
.integer('followers')
|
|
235
|
+
)
|
|
236
|
+
|
|
237
|
+
// 建立帶有 Schema 驗證的 Collection
|
|
238
|
+
await Mongo.database().createCollectionWithSchema('users', userSchema)
|
|
239
|
+
|
|
240
|
+
// 或使用原生 API
|
|
241
|
+
await Mongo.database().createCollection('users', {
|
|
242
|
+
schema: userSchema.toValidationOptions({
|
|
243
|
+
validationLevel: 'strict',
|
|
244
|
+
validationAction: 'error'
|
|
245
|
+
})
|
|
246
|
+
})
|
|
247
|
+
```
|
|
248
|
+
|
|
249
|
+
#### 支援的欄位類型
|
|
250
|
+
|
|
251
|
+
```typescript
|
|
252
|
+
schema()
|
|
253
|
+
.string('field', { minLength, maxLength, pattern, enum })
|
|
254
|
+
.number('field', { minimum, maximum, exclusiveMinimum, exclusiveMaximum })
|
|
255
|
+
.integer('field', { minimum, maximum })
|
|
256
|
+
.boolean('field')
|
|
257
|
+
.date('field')
|
|
258
|
+
.array('field', 'string', { minItems, maxItems, uniqueItems })
|
|
259
|
+
.object('field', (s) => s.string('nested'))
|
|
260
|
+
```
|
|
261
|
+
|
|
155
262
|
### Advanced Features
|
|
156
263
|
|
|
157
264
|
#### Transactions
|
|
@@ -184,14 +291,53 @@ for await (const event of stream) {
|
|
|
184
291
|
|
|
185
292
|
#### GridFS (File Storage)
|
|
186
293
|
|
|
294
|
+
GridFS 支援大檔案(>16MB)的儲存與串流處理:
|
|
295
|
+
|
|
187
296
|
```typescript
|
|
188
|
-
|
|
297
|
+
import { MongoGridFS } from '@gravito/dark-matter'
|
|
189
298
|
|
|
190
|
-
|
|
191
|
-
const fileId = await grid.upload(Buffer.from('Hello'), { filename: 'hello.txt' })
|
|
299
|
+
const grid = new MongoGridFS(Mongo.database())
|
|
192
300
|
|
|
193
|
-
//
|
|
301
|
+
// 基本上傳下載
|
|
302
|
+
const fileId = await grid.upload(Buffer.from('Hello'), {
|
|
303
|
+
filename: 'hello.txt',
|
|
304
|
+
contentType: 'text/plain',
|
|
305
|
+
metadata: { author: 'John' }
|
|
306
|
+
})
|
|
194
307
|
const content = await grid.download(fileId)
|
|
308
|
+
|
|
309
|
+
// 串流上傳(適合大檔案)
|
|
310
|
+
const stream = file.stream()
|
|
311
|
+
const fileId = await grid.uploadStream(stream, {
|
|
312
|
+
filename: 'large-video.mp4'
|
|
313
|
+
}, (progress) => {
|
|
314
|
+
console.log(`上傳進度: ${progress.bytesWritten} bytes`)
|
|
315
|
+
})
|
|
316
|
+
|
|
317
|
+
// 串流下載
|
|
318
|
+
const downloadStream = grid.downloadStream(fileId)
|
|
319
|
+
const reader = downloadStream.getReader()
|
|
320
|
+
while (true) {
|
|
321
|
+
const { done, value } = await reader.read()
|
|
322
|
+
if (done) break
|
|
323
|
+
// 處理 chunk
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
// 大檔案分片上傳(帶進度追蹤)
|
|
327
|
+
const fileId = await grid.uploadLargeFile(largeFile, {
|
|
328
|
+
filename: 'movie.mp4',
|
|
329
|
+
chunkSizeBytes: 255 * 1024 // 255 KB
|
|
330
|
+
}, (progress) => {
|
|
331
|
+
console.log(`進度: ${progress.percentage}%`)
|
|
332
|
+
console.log(`已上傳: ${progress.bytesWritten} / ${progress.totalBytes}`)
|
|
333
|
+
})
|
|
334
|
+
|
|
335
|
+
// 取得檔案中繼資料
|
|
336
|
+
const fileInfo = await grid.findById(fileId)
|
|
337
|
+
console.log(fileInfo.filename, fileInfo.length, fileInfo.uploadDate)
|
|
338
|
+
|
|
339
|
+
// 刪除檔案
|
|
340
|
+
await grid.delete(fileId)
|
|
195
341
|
```
|
|
196
342
|
|
|
197
343
|
#### Schema Validation
|
|
@@ -227,6 +373,77 @@ const health = await Mongo.connection().getHealthStatus()
|
|
|
227
373
|
console.log(health.latencyMs) // e.g. 15
|
|
228
374
|
```
|
|
229
375
|
|
|
376
|
+
## Performance Optimization
|
|
377
|
+
|
|
378
|
+
### Indexing
|
|
379
|
+
|
|
380
|
+
```typescript
|
|
381
|
+
// Create indexes for frequently queried fields
|
|
382
|
+
await Mongo.collection('users').createIndex({ email: 1 }, { unique: true })
|
|
383
|
+
await Mongo.collection('users').createIndex({ status: 1, createdAt: -1 })
|
|
384
|
+
```
|
|
385
|
+
|
|
386
|
+
### Query Optimization
|
|
387
|
+
|
|
388
|
+
```typescript
|
|
389
|
+
// Use projection to reduce data transfer
|
|
390
|
+
const users = await Mongo.collection('users')
|
|
391
|
+
.select('name', 'email') // Only fetch needed fields
|
|
392
|
+
.where('status', 'active')
|
|
393
|
+
.get()
|
|
394
|
+
|
|
395
|
+
// Use limit for large result sets
|
|
396
|
+
const recentPosts = await Mongo.collection('posts')
|
|
397
|
+
.orderBy('createdAt', 'desc')
|
|
398
|
+
.limit(20) // Prevent fetching too much data
|
|
399
|
+
.get()
|
|
400
|
+
```
|
|
401
|
+
|
|
402
|
+
### Connection Pool
|
|
403
|
+
|
|
404
|
+
```typescript
|
|
405
|
+
Mongo.configure({
|
|
406
|
+
default: 'main',
|
|
407
|
+
connections: {
|
|
408
|
+
main: {
|
|
409
|
+
uri: 'mongodb://localhost:27017',
|
|
410
|
+
database: 'myapp',
|
|
411
|
+
maxPoolSize: 50, // Adjust based on load
|
|
412
|
+
minPoolSize: 10, // Keep minimum connections
|
|
413
|
+
maxIdleTimeMS: 30000 // Idle connection timeout
|
|
414
|
+
}
|
|
415
|
+
}
|
|
416
|
+
})
|
|
417
|
+
```
|
|
418
|
+
|
|
419
|
+
### Batch Operations
|
|
420
|
+
|
|
421
|
+
```typescript
|
|
422
|
+
// Use bulkWrite to reduce round trips
|
|
423
|
+
await Mongo.collection('logs').bulkWrite([
|
|
424
|
+
{ insertOne: { document: { event: 'login', userId: 1 } } },
|
|
425
|
+
{ updateOne: { filter: { _id: 2 }, update: { $set: { status: 'active' } } } },
|
|
426
|
+
{ deleteOne: { filter: { _id: 3 } } }
|
|
427
|
+
])
|
|
428
|
+
```
|
|
429
|
+
|
|
430
|
+
### Monitoring
|
|
431
|
+
|
|
432
|
+
```typescript
|
|
433
|
+
import { MongoPoolMonitor } from '@gravito/dark-matter'
|
|
434
|
+
|
|
435
|
+
const monitor = new MongoPoolMonitor(Mongo.connection())
|
|
436
|
+
const metrics = monitor.getMetrics()
|
|
437
|
+
|
|
438
|
+
console.log('Pool status:', {
|
|
439
|
+
totalConnections: metrics.totalConnections,
|
|
440
|
+
availableConnections: metrics.availableConnections,
|
|
441
|
+
waitQueueSize: metrics.waitQueueSize
|
|
442
|
+
})
|
|
443
|
+
```
|
|
444
|
+
|
|
445
|
+
詳細的效能調優指南請參考 [docs/performance-analysis.md](./docs/performance-analysis.md)
|
|
446
|
+
|
|
230
447
|
## Roadmap
|
|
231
448
|
|
|
232
449
|
- [x] Connection retry & health check
|
|
@@ -234,6 +451,9 @@ console.log(health.latencyMs) // e.g. 15
|
|
|
234
451
|
- [x] Schema validation
|
|
235
452
|
- [x] Change streams
|
|
236
453
|
- [x] GridFS support
|
|
454
|
+
- [x] Soft deletes
|
|
455
|
+
- [x] Schema Builder API
|
|
456
|
+
- [x] GridFS streaming & progress tracking
|
|
237
457
|
|
|
238
458
|
## License
|
|
239
459
|
|