@push.rocks/smartmongo 2.2.0 → 4.0.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/dist_ts/00_commitinfo_data.js +1 -1
- package/dist_ts/index.d.ts +1 -1
- package/dist_ts/index.js +3 -3
- package/dist_ts/tsmdb/engine/AggregationEngine.js +189 -0
- package/dist_ts/{congodb → tsmdb}/engine/IndexEngine.d.ts +23 -3
- package/dist_ts/tsmdb/engine/IndexEngine.js +678 -0
- package/dist_ts/tsmdb/engine/QueryEngine.js +271 -0
- package/dist_ts/tsmdb/engine/QueryPlanner.d.ts +64 -0
- package/dist_ts/tsmdb/engine/QueryPlanner.js +308 -0
- package/dist_ts/tsmdb/engine/SessionEngine.d.ts +117 -0
- package/dist_ts/tsmdb/engine/SessionEngine.js +232 -0
- package/dist_ts/{congodb → tsmdb}/engine/TransactionEngine.d.ts +1 -1
- package/dist_ts/tsmdb/engine/TransactionEngine.js +287 -0
- package/dist_ts/tsmdb/engine/UpdateEngine.js +461 -0
- package/dist_ts/{congodb/errors/CongoErrors.d.ts → tsmdb/errors/TsmdbErrors.d.ts} +16 -16
- package/dist_ts/tsmdb/errors/TsmdbErrors.js +155 -0
- package/dist_ts/{congodb → tsmdb}/index.d.ts +11 -4
- package/dist_ts/tsmdb/index.js +31 -0
- package/dist_ts/tsmdb/server/CommandRouter.d.ts +87 -0
- package/dist_ts/tsmdb/server/CommandRouter.js +222 -0
- package/dist_ts/{congodb/server/CongoServer.d.ts → tsmdb/server/TsmdbServer.d.ts} +6 -6
- package/dist_ts/tsmdb/server/TsmdbServer.js +229 -0
- package/dist_ts/{congodb → tsmdb}/server/WireProtocol.d.ts +1 -1
- package/dist_ts/tsmdb/server/WireProtocol.js +298 -0
- package/dist_ts/{congodb → tsmdb}/server/handlers/AdminHandler.d.ts +1 -1
- package/dist_ts/tsmdb/server/handlers/AdminHandler.js +668 -0
- package/dist_ts/{congodb → tsmdb}/server/handlers/AggregateHandler.d.ts +1 -1
- package/dist_ts/tsmdb/server/handlers/AggregateHandler.js +277 -0
- package/dist_ts/{congodb → tsmdb}/server/handlers/DeleteHandler.d.ts +1 -1
- package/dist_ts/tsmdb/server/handlers/DeleteHandler.js +95 -0
- package/dist_ts/{congodb → tsmdb}/server/handlers/FindHandler.d.ts +1 -1
- package/dist_ts/tsmdb/server/handlers/FindHandler.js +291 -0
- package/dist_ts/{congodb → tsmdb}/server/handlers/HelloHandler.d.ts +1 -1
- package/dist_ts/{congodb → tsmdb}/server/handlers/HelloHandler.js +2 -2
- package/dist_ts/{congodb → tsmdb}/server/handlers/IndexHandler.d.ts +1 -1
- package/dist_ts/tsmdb/server/handlers/IndexHandler.js +183 -0
- package/dist_ts/{congodb → tsmdb}/server/handlers/InsertHandler.d.ts +1 -1
- package/dist_ts/tsmdb/server/handlers/InsertHandler.js +79 -0
- package/dist_ts/{congodb → tsmdb}/server/handlers/UpdateHandler.d.ts +1 -1
- package/dist_ts/tsmdb/server/handlers/UpdateHandler.js +296 -0
- package/dist_ts/tsmdb/server/handlers/index.js +10 -0
- package/dist_ts/{congodb → tsmdb}/server/index.d.ts +2 -2
- package/dist_ts/tsmdb/server/index.js +7 -0
- package/dist_ts/{congodb → tsmdb}/storage/FileStorageAdapter.d.ts +27 -3
- package/dist_ts/tsmdb/storage/FileStorageAdapter.js +465 -0
- package/dist_ts/{congodb → tsmdb}/storage/IStorageAdapter.d.ts +7 -2
- package/dist_ts/{congodb → tsmdb}/storage/IStorageAdapter.js +1 -1
- package/dist_ts/{congodb → tsmdb}/storage/MemoryStorageAdapter.d.ts +3 -2
- package/dist_ts/tsmdb/storage/MemoryStorageAdapter.js +378 -0
- package/dist_ts/{congodb → tsmdb}/storage/OpLog.d.ts +1 -1
- package/dist_ts/tsmdb/storage/OpLog.js +221 -0
- package/dist_ts/tsmdb/storage/WAL.d.ts +117 -0
- package/dist_ts/tsmdb/storage/WAL.js +286 -0
- package/dist_ts/tsmdb/tsmdb.plugins.js +14 -0
- package/dist_ts/{congodb → tsmdb}/types/interfaces.d.ts +3 -3
- package/dist_ts/{congodb → tsmdb}/types/interfaces.js +1 -1
- package/dist_ts/tsmdb/utils/checksum.d.ts +30 -0
- package/dist_ts/tsmdb/utils/checksum.js +77 -0
- package/dist_ts/tsmdb/utils/index.d.ts +1 -0
- package/dist_ts/tsmdb/utils/index.js +2 -0
- package/package.json +1 -1
- package/readme.hints.md +7 -12
- package/readme.md +25 -25
- package/ts/00_commitinfo_data.ts +1 -1
- package/ts/index.ts +2 -2
- package/ts/{congodb → tsmdb}/engine/AggregationEngine.ts +1 -1
- package/ts/tsmdb/engine/IndexEngine.ts +798 -0
- package/ts/{congodb → tsmdb}/engine/QueryEngine.ts +1 -1
- package/ts/tsmdb/engine/QueryPlanner.ts +393 -0
- package/ts/tsmdb/engine/SessionEngine.ts +292 -0
- package/ts/{congodb → tsmdb}/engine/TransactionEngine.ts +12 -12
- package/ts/{congodb → tsmdb}/engine/UpdateEngine.ts +1 -1
- package/ts/{congodb/errors/CongoErrors.ts → tsmdb/errors/TsmdbErrors.ts} +34 -34
- package/ts/{congodb → tsmdb}/index.ts +16 -7
- package/ts/{congodb → tsmdb}/server/CommandRouter.ts +114 -5
- package/ts/{congodb/server/CongoServer.ts → tsmdb/server/TsmdbServer.ts} +11 -8
- package/ts/{congodb → tsmdb}/server/WireProtocol.ts +1 -1
- package/ts/{congodb → tsmdb}/server/handlers/AdminHandler.ts +116 -11
- package/ts/{congodb → tsmdb}/server/handlers/AggregateHandler.ts +1 -1
- package/ts/{congodb → tsmdb}/server/handlers/DeleteHandler.ts +18 -3
- package/ts/{congodb → tsmdb}/server/handlers/FindHandler.ts +43 -14
- package/ts/{congodb → tsmdb}/server/handlers/HelloHandler.ts +1 -1
- package/ts/{congodb → tsmdb}/server/handlers/IndexHandler.ts +1 -1
- package/ts/{congodb → tsmdb}/server/handlers/InsertHandler.ts +7 -1
- package/ts/{congodb → tsmdb}/server/handlers/UpdateHandler.ts +34 -5
- package/ts/{congodb → tsmdb}/server/index.ts +2 -2
- package/ts/{congodb → tsmdb}/storage/FileStorageAdapter.ts +90 -7
- package/ts/{congodb → tsmdb}/storage/IStorageAdapter.ts +8 -2
- package/ts/{congodb → tsmdb}/storage/MemoryStorageAdapter.ts +14 -2
- package/ts/{congodb → tsmdb}/storage/OpLog.ts +1 -1
- package/ts/tsmdb/storage/WAL.ts +375 -0
- package/ts/{congodb → tsmdb}/types/interfaces.ts +3 -3
- package/ts/tsmdb/utils/checksum.ts +88 -0
- package/ts/tsmdb/utils/index.ts +1 -0
- package/dist_ts/congodb/congodb.plugins.js +0 -14
- package/dist_ts/congodb/engine/AggregationEngine.js +0 -189
- package/dist_ts/congodb/engine/IndexEngine.js +0 -376
- package/dist_ts/congodb/engine/QueryEngine.js +0 -271
- package/dist_ts/congodb/engine/TransactionEngine.js +0 -287
- package/dist_ts/congodb/engine/UpdateEngine.js +0 -461
- package/dist_ts/congodb/errors/CongoErrors.js +0 -155
- package/dist_ts/congodb/index.js +0 -26
- package/dist_ts/congodb/server/CommandRouter.d.ts +0 -51
- package/dist_ts/congodb/server/CommandRouter.js +0 -132
- package/dist_ts/congodb/server/CongoServer.js +0 -227
- package/dist_ts/congodb/server/WireProtocol.js +0 -298
- package/dist_ts/congodb/server/handlers/AdminHandler.js +0 -568
- package/dist_ts/congodb/server/handlers/AggregateHandler.js +0 -277
- package/dist_ts/congodb/server/handlers/DeleteHandler.js +0 -83
- package/dist_ts/congodb/server/handlers/FindHandler.js +0 -261
- package/dist_ts/congodb/server/handlers/IndexHandler.js +0 -183
- package/dist_ts/congodb/server/handlers/InsertHandler.js +0 -76
- package/dist_ts/congodb/server/handlers/UpdateHandler.js +0 -270
- package/dist_ts/congodb/server/handlers/index.js +0 -10
- package/dist_ts/congodb/server/index.js +0 -7
- package/dist_ts/congodb/storage/FileStorageAdapter.js +0 -396
- package/dist_ts/congodb/storage/MemoryStorageAdapter.js +0 -367
- package/dist_ts/congodb/storage/OpLog.js +0 -221
- package/ts/congodb/engine/IndexEngine.ts +0 -479
- /package/dist_ts/{congodb → tsmdb}/engine/AggregationEngine.d.ts +0 -0
- /package/dist_ts/{congodb → tsmdb}/engine/QueryEngine.d.ts +0 -0
- /package/dist_ts/{congodb → tsmdb}/engine/UpdateEngine.d.ts +0 -0
- /package/dist_ts/{congodb → tsmdb}/server/handlers/index.d.ts +0 -0
- /package/dist_ts/{congodb/congodb.plugins.d.ts → tsmdb/tsmdb.plugins.d.ts} +0 -0
- /package/ts/{congodb → tsmdb}/server/handlers/index.ts +0 -0
- /package/ts/{congodb/congodb.plugins.ts → tsmdb/tsmdb.plugins.ts} +0 -0
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import * as plugins from '../../
|
|
1
|
+
import * as plugins from '../../tsmdb.plugins.js';
|
|
2
2
|
import type { ICommandHandler, IHandlerContext } from '../CommandRouter.js';
|
|
3
|
+
import { SessionEngine } from '../../engine/SessionEngine.js';
|
|
3
4
|
|
|
4
5
|
/**
|
|
5
6
|
* AdminHandler - Handles administrative commands
|
|
@@ -237,16 +238,18 @@ export class AdminHandler implements ICommandHandler {
|
|
|
237
238
|
* Handle serverStatus command
|
|
238
239
|
*/
|
|
239
240
|
private async handleServerStatus(context: IHandlerContext): Promise<plugins.bson.Document> {
|
|
240
|
-
const { server } = context;
|
|
241
|
+
const { server, sessionEngine } = context;
|
|
241
242
|
|
|
242
243
|
const uptime = server.getUptime();
|
|
243
244
|
const connections = server.getConnectionCount();
|
|
245
|
+
const sessions = sessionEngine.listSessions();
|
|
246
|
+
const sessionsWithTxn = sessionEngine.getSessionsWithTransactions();
|
|
244
247
|
|
|
245
248
|
return {
|
|
246
249
|
ok: 1,
|
|
247
250
|
host: `${server.host}:${server.port}`,
|
|
248
251
|
version: '7.0.0',
|
|
249
|
-
process: '
|
|
252
|
+
process: 'tsmdb',
|
|
250
253
|
pid: process.pid,
|
|
251
254
|
uptime,
|
|
252
255
|
uptimeMillis: uptime * 1000,
|
|
@@ -263,13 +266,33 @@ export class AdminHandler implements ICommandHandler {
|
|
|
263
266
|
totalCreated: connections,
|
|
264
267
|
active: connections,
|
|
265
268
|
},
|
|
269
|
+
logicalSessionRecordCache: {
|
|
270
|
+
activeSessionsCount: sessions.length,
|
|
271
|
+
sessionsCollectionJobCount: 0,
|
|
272
|
+
lastSessionsCollectionJobDurationMillis: 0,
|
|
273
|
+
lastSessionsCollectionJobTimestamp: new Date(),
|
|
274
|
+
transactionReaperJobCount: 0,
|
|
275
|
+
lastTransactionReaperJobDurationMillis: 0,
|
|
276
|
+
lastTransactionReaperJobTimestamp: new Date(),
|
|
277
|
+
},
|
|
278
|
+
transactions: {
|
|
279
|
+
retriedCommandsCount: 0,
|
|
280
|
+
retriedStatementsCount: 0,
|
|
281
|
+
transactionsCollectionWriteCount: 0,
|
|
282
|
+
currentActive: sessionsWithTxn.length,
|
|
283
|
+
currentInactive: 0,
|
|
284
|
+
currentOpen: sessionsWithTxn.length,
|
|
285
|
+
totalStarted: sessionsWithTxn.length,
|
|
286
|
+
totalCommitted: 0,
|
|
287
|
+
totalAborted: 0,
|
|
288
|
+
},
|
|
266
289
|
network: {
|
|
267
290
|
bytesIn: 0,
|
|
268
291
|
bytesOut: 0,
|
|
269
292
|
numRequests: 0,
|
|
270
293
|
},
|
|
271
294
|
storageEngine: {
|
|
272
|
-
name: '
|
|
295
|
+
name: 'tsmdb',
|
|
273
296
|
supportsCommittedReads: true,
|
|
274
297
|
persistent: false,
|
|
275
298
|
},
|
|
@@ -283,7 +306,7 @@ export class AdminHandler implements ICommandHandler {
|
|
|
283
306
|
return {
|
|
284
307
|
ok: 1,
|
|
285
308
|
version: '7.0.0',
|
|
286
|
-
gitVersion: '
|
|
309
|
+
gitVersion: 'tsmdb',
|
|
287
310
|
modules: [],
|
|
288
311
|
allocator: 'system',
|
|
289
312
|
javascriptEngine: 'none',
|
|
@@ -294,7 +317,7 @@ export class AdminHandler implements ICommandHandler {
|
|
|
294
317
|
compiled: 'disabled',
|
|
295
318
|
},
|
|
296
319
|
buildEnvironment: {
|
|
297
|
-
distmod: '
|
|
320
|
+
distmod: 'tsmdb',
|
|
298
321
|
distarch: process.arch,
|
|
299
322
|
cc: '',
|
|
300
323
|
ccflags: '',
|
|
@@ -307,7 +330,7 @@ export class AdminHandler implements ICommandHandler {
|
|
|
307
330
|
bits: 64,
|
|
308
331
|
debug: false,
|
|
309
332
|
maxBsonObjectSize: 16777216,
|
|
310
|
-
storageEngines: ['
|
|
333
|
+
storageEngines: ['tsmdb'],
|
|
311
334
|
};
|
|
312
335
|
}
|
|
313
336
|
|
|
@@ -409,6 +432,17 @@ export class AdminHandler implements ICommandHandler {
|
|
|
409
432
|
* Handle endSessions command
|
|
410
433
|
*/
|
|
411
434
|
private async handleEndSessions(context: IHandlerContext): Promise<plugins.bson.Document> {
|
|
435
|
+
const { command, sessionEngine } = context;
|
|
436
|
+
|
|
437
|
+
// End each session in the array
|
|
438
|
+
const sessions = command.endSessions || [];
|
|
439
|
+
for (const sessionSpec of sessions) {
|
|
440
|
+
const sessionId = SessionEngine.extractSessionId(sessionSpec);
|
|
441
|
+
if (sessionId) {
|
|
442
|
+
await sessionEngine.endSession(sessionId);
|
|
443
|
+
}
|
|
444
|
+
}
|
|
445
|
+
|
|
412
446
|
return { ok: 1 };
|
|
413
447
|
}
|
|
414
448
|
|
|
@@ -416,16 +450,87 @@ export class AdminHandler implements ICommandHandler {
|
|
|
416
450
|
* Handle abortTransaction command
|
|
417
451
|
*/
|
|
418
452
|
private async handleAbortTransaction(context: IHandlerContext): Promise<plugins.bson.Document> {
|
|
419
|
-
|
|
420
|
-
|
|
453
|
+
const { transactionEngine, sessionEngine, txnId, sessionId } = context;
|
|
454
|
+
|
|
455
|
+
if (!txnId) {
|
|
456
|
+
return {
|
|
457
|
+
ok: 0,
|
|
458
|
+
errmsg: 'No transaction started',
|
|
459
|
+
code: 251,
|
|
460
|
+
codeName: 'NoSuchTransaction',
|
|
461
|
+
};
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
try {
|
|
465
|
+
await transactionEngine.abortTransaction(txnId);
|
|
466
|
+
transactionEngine.endTransaction(txnId);
|
|
467
|
+
// Update session state
|
|
468
|
+
if (sessionId) {
|
|
469
|
+
sessionEngine.endTransaction(sessionId);
|
|
470
|
+
}
|
|
471
|
+
return { ok: 1 };
|
|
472
|
+
} catch (error: any) {
|
|
473
|
+
return {
|
|
474
|
+
ok: 0,
|
|
475
|
+
errmsg: error.message || 'Abort transaction failed',
|
|
476
|
+
code: error.code || 1,
|
|
477
|
+
codeName: error.codeName || 'UnknownError',
|
|
478
|
+
};
|
|
479
|
+
}
|
|
421
480
|
}
|
|
422
481
|
|
|
423
482
|
/**
|
|
424
483
|
* Handle commitTransaction command
|
|
425
484
|
*/
|
|
426
485
|
private async handleCommitTransaction(context: IHandlerContext): Promise<plugins.bson.Document> {
|
|
427
|
-
|
|
428
|
-
|
|
486
|
+
const { transactionEngine, sessionEngine, txnId, sessionId } = context;
|
|
487
|
+
|
|
488
|
+
if (!txnId) {
|
|
489
|
+
return {
|
|
490
|
+
ok: 0,
|
|
491
|
+
errmsg: 'No transaction started',
|
|
492
|
+
code: 251,
|
|
493
|
+
codeName: 'NoSuchTransaction',
|
|
494
|
+
};
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
try {
|
|
498
|
+
await transactionEngine.commitTransaction(txnId);
|
|
499
|
+
transactionEngine.endTransaction(txnId);
|
|
500
|
+
// Update session state
|
|
501
|
+
if (sessionId) {
|
|
502
|
+
sessionEngine.endTransaction(sessionId);
|
|
503
|
+
}
|
|
504
|
+
return { ok: 1 };
|
|
505
|
+
} catch (error: any) {
|
|
506
|
+
// If commit fails, transaction should be aborted
|
|
507
|
+
try {
|
|
508
|
+
await transactionEngine.abortTransaction(txnId);
|
|
509
|
+
transactionEngine.endTransaction(txnId);
|
|
510
|
+
if (sessionId) {
|
|
511
|
+
sessionEngine.endTransaction(sessionId);
|
|
512
|
+
}
|
|
513
|
+
} catch {
|
|
514
|
+
// Ignore abort errors
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
if (error.code === 112) {
|
|
518
|
+
// Write conflict
|
|
519
|
+
return {
|
|
520
|
+
ok: 0,
|
|
521
|
+
errmsg: error.message || 'Write conflict during commit',
|
|
522
|
+
code: 112,
|
|
523
|
+
codeName: 'WriteConflict',
|
|
524
|
+
};
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
return {
|
|
528
|
+
ok: 0,
|
|
529
|
+
errmsg: error.message || 'Commit transaction failed',
|
|
530
|
+
code: error.code || 1,
|
|
531
|
+
codeName: error.codeName || 'UnknownError',
|
|
532
|
+
};
|
|
533
|
+
}
|
|
429
534
|
}
|
|
430
535
|
|
|
431
536
|
/**
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import * as plugins from '../../
|
|
1
|
+
import * as plugins from '../../tsmdb.plugins.js';
|
|
2
2
|
import type { ICommandHandler, IHandlerContext, ICursorState } from '../CommandRouter.js';
|
|
3
3
|
import { AggregationEngine } from '../../engine/AggregationEngine.js';
|
|
4
4
|
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import * as plugins from '../../
|
|
1
|
+
import * as plugins from '../../tsmdb.plugins.js';
|
|
2
2
|
import type { ICommandHandler, IHandlerContext } from '../CommandRouter.js';
|
|
3
|
+
import type { IStoredDocument } from '../../types/interfaces.js';
|
|
3
4
|
import { QueryEngine } from '../../engine/QueryEngine.js';
|
|
4
5
|
|
|
5
6
|
/**
|
|
@@ -47,6 +48,8 @@ export class DeleteHandler implements ICommandHandler {
|
|
|
47
48
|
return { ok: 1, n: 0 };
|
|
48
49
|
}
|
|
49
50
|
|
|
51
|
+
const indexEngine = context.getIndexEngine(collection);
|
|
52
|
+
|
|
50
53
|
for (let i = 0; i < deletes.length; i++) {
|
|
51
54
|
const deleteSpec = deletes[i];
|
|
52
55
|
const filter = deleteSpec.q || deleteSpec.filter || {};
|
|
@@ -56,8 +59,15 @@ export class DeleteHandler implements ICommandHandler {
|
|
|
56
59
|
const deleteAll = limit === 0;
|
|
57
60
|
|
|
58
61
|
try {
|
|
59
|
-
//
|
|
60
|
-
const
|
|
62
|
+
// Try to use index-accelerated query
|
|
63
|
+
const candidateIds = await indexEngine.findCandidateIds(filter);
|
|
64
|
+
|
|
65
|
+
let documents: IStoredDocument[];
|
|
66
|
+
if (candidateIds !== null) {
|
|
67
|
+
documents = await storage.findByIds(database, collection, candidateIds);
|
|
68
|
+
} else {
|
|
69
|
+
documents = await storage.findAll(database, collection);
|
|
70
|
+
}
|
|
61
71
|
|
|
62
72
|
// Apply filter
|
|
63
73
|
const matchingDocs = QueryEngine.filter(documents, filter);
|
|
@@ -69,6 +79,11 @@ export class DeleteHandler implements ICommandHandler {
|
|
|
69
79
|
// Determine which documents to delete
|
|
70
80
|
const docsToDelete = deleteAll ? matchingDocs : matchingDocs.slice(0, 1);
|
|
71
81
|
|
|
82
|
+
// Update indexes for deleted documents
|
|
83
|
+
for (const doc of docsToDelete) {
|
|
84
|
+
await indexEngine.onDelete(doc as any);
|
|
85
|
+
}
|
|
86
|
+
|
|
72
87
|
// Delete the documents
|
|
73
88
|
const idsToDelete = docsToDelete.map(doc => doc._id);
|
|
74
89
|
const deleted = await storage.deleteByIds(database, collection, idsToDelete);
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import * as plugins from '../../
|
|
1
|
+
import * as plugins from '../../tsmdb.plugins.js';
|
|
2
2
|
import type { ICommandHandler, IHandlerContext, ICursorState } from '../CommandRouter.js';
|
|
3
|
+
import type { IStoredDocument } from '../../types/interfaces.js';
|
|
3
4
|
import { QueryEngine } from '../../engine/QueryEngine.js';
|
|
4
5
|
|
|
5
6
|
/**
|
|
@@ -45,7 +46,7 @@ export class FindHandler implements ICommandHandler {
|
|
|
45
46
|
* Handle find command
|
|
46
47
|
*/
|
|
47
48
|
private async handleFind(context: IHandlerContext): Promise<plugins.bson.Document> {
|
|
48
|
-
const { storage, database, command } = context;
|
|
49
|
+
const { storage, database, command, getIndexEngine } = context;
|
|
49
50
|
|
|
50
51
|
const collection = command.find;
|
|
51
52
|
const filter = command.filter || {};
|
|
@@ -70,11 +71,22 @@ export class FindHandler implements ICommandHandler {
|
|
|
70
71
|
};
|
|
71
72
|
}
|
|
72
73
|
|
|
73
|
-
//
|
|
74
|
-
|
|
74
|
+
// Try to use index-accelerated query
|
|
75
|
+
const indexEngine = getIndexEngine(collection);
|
|
76
|
+
const candidateIds = await indexEngine.findCandidateIds(filter);
|
|
75
77
|
|
|
76
|
-
|
|
77
|
-
|
|
78
|
+
let documents: IStoredDocument[];
|
|
79
|
+
if (candidateIds !== null) {
|
|
80
|
+
// Index hit - fetch only candidate documents
|
|
81
|
+
documents = await storage.findByIds(database, collection, candidateIds);
|
|
82
|
+
// Still apply filter for any conditions the index couldn't fully satisfy
|
|
83
|
+
documents = QueryEngine.filter(documents, filter);
|
|
84
|
+
} else {
|
|
85
|
+
// No suitable index - full collection scan
|
|
86
|
+
documents = await storage.findAll(database, collection);
|
|
87
|
+
// Apply filter
|
|
88
|
+
documents = QueryEngine.filter(documents, filter);
|
|
89
|
+
}
|
|
78
90
|
|
|
79
91
|
// Apply sort
|
|
80
92
|
if (sort) {
|
|
@@ -233,7 +245,7 @@ export class FindHandler implements ICommandHandler {
|
|
|
233
245
|
* Handle count command
|
|
234
246
|
*/
|
|
235
247
|
private async handleCount(context: IHandlerContext): Promise<plugins.bson.Document> {
|
|
236
|
-
const { storage, database, command } = context;
|
|
248
|
+
const { storage, database, command, getIndexEngine } = context;
|
|
237
249
|
|
|
238
250
|
const collection = command.count;
|
|
239
251
|
const query = command.query || {};
|
|
@@ -246,11 +258,20 @@ export class FindHandler implements ICommandHandler {
|
|
|
246
258
|
return { ok: 1, n: 0 };
|
|
247
259
|
}
|
|
248
260
|
|
|
249
|
-
//
|
|
250
|
-
|
|
261
|
+
// Try to use index-accelerated query
|
|
262
|
+
const indexEngine = getIndexEngine(collection);
|
|
263
|
+
const candidateIds = await indexEngine.findCandidateIds(query);
|
|
251
264
|
|
|
252
|
-
|
|
253
|
-
|
|
265
|
+
let documents: IStoredDocument[];
|
|
266
|
+
if (candidateIds !== null) {
|
|
267
|
+
// Index hit - fetch only candidate documents
|
|
268
|
+
documents = await storage.findByIds(database, collection, candidateIds);
|
|
269
|
+
documents = QueryEngine.filter(documents, query);
|
|
270
|
+
} else {
|
|
271
|
+
// No suitable index - full collection scan
|
|
272
|
+
documents = await storage.findAll(database, collection);
|
|
273
|
+
documents = QueryEngine.filter(documents, query);
|
|
274
|
+
}
|
|
254
275
|
|
|
255
276
|
// Apply skip
|
|
256
277
|
if (skip > 0) {
|
|
@@ -269,7 +290,7 @@ export class FindHandler implements ICommandHandler {
|
|
|
269
290
|
* Handle distinct command
|
|
270
291
|
*/
|
|
271
292
|
private async handleDistinct(context: IHandlerContext): Promise<plugins.bson.Document> {
|
|
272
|
-
const { storage, database, command } = context;
|
|
293
|
+
const { storage, database, command, getIndexEngine } = context;
|
|
273
294
|
|
|
274
295
|
const collection = command.distinct;
|
|
275
296
|
const key = command.key;
|
|
@@ -290,8 +311,16 @@ export class FindHandler implements ICommandHandler {
|
|
|
290
311
|
return { ok: 1, values: [] };
|
|
291
312
|
}
|
|
292
313
|
|
|
293
|
-
//
|
|
294
|
-
const
|
|
314
|
+
// Try to use index-accelerated query
|
|
315
|
+
const indexEngine = getIndexEngine(collection);
|
|
316
|
+
const candidateIds = await indexEngine.findCandidateIds(query);
|
|
317
|
+
|
|
318
|
+
let documents: IStoredDocument[];
|
|
319
|
+
if (candidateIds !== null) {
|
|
320
|
+
documents = await storage.findByIds(database, collection, candidateIds);
|
|
321
|
+
} else {
|
|
322
|
+
documents = await storage.findAll(database, collection);
|
|
323
|
+
}
|
|
295
324
|
|
|
296
325
|
// Get distinct values
|
|
297
326
|
const values = QueryEngine.distinct(documents, key, query);
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import * as plugins from '../../
|
|
1
|
+
import * as plugins from '../../tsmdb.plugins.js';
|
|
2
2
|
import type { ICommandHandler, IHandlerContext } from '../CommandRouter.js';
|
|
3
|
+
import type { IStoredDocument } from '../../types/interfaces.js';
|
|
3
4
|
|
|
4
5
|
/**
|
|
5
6
|
* InsertHandler - Handles insert commands
|
|
@@ -42,6 +43,8 @@ export class InsertHandler implements ICommandHandler {
|
|
|
42
43
|
// Ensure collection exists
|
|
43
44
|
await storage.createCollection(database, collection);
|
|
44
45
|
|
|
46
|
+
const indexEngine = context.getIndexEngine(collection);
|
|
47
|
+
|
|
45
48
|
// Insert documents
|
|
46
49
|
for (let i = 0; i < documents.length; i++) {
|
|
47
50
|
const doc = documents[i];
|
|
@@ -52,6 +55,9 @@ export class InsertHandler implements ICommandHandler {
|
|
|
52
55
|
doc._id = new plugins.bson.ObjectId();
|
|
53
56
|
}
|
|
54
57
|
|
|
58
|
+
// Check index constraints before insert (doc now has _id)
|
|
59
|
+
await indexEngine.onInsert(doc as IStoredDocument);
|
|
60
|
+
|
|
55
61
|
await storage.insertOne(database, collection, doc);
|
|
56
62
|
insertedCount++;
|
|
57
63
|
} catch (error: any) {
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import * as plugins from '../../
|
|
1
|
+
import * as plugins from '../../tsmdb.plugins.js';
|
|
2
2
|
import type { ICommandHandler, IHandlerContext } from '../CommandRouter.js';
|
|
3
|
+
import type { IStoredDocument } from '../../types/interfaces.js';
|
|
3
4
|
import { QueryEngine } from '../../engine/QueryEngine.js';
|
|
4
5
|
import { UpdateEngine } from '../../engine/UpdateEngine.js';
|
|
5
6
|
|
|
@@ -69,6 +70,8 @@ export class UpdateHandler implements ICommandHandler {
|
|
|
69
70
|
// Ensure collection exists
|
|
70
71
|
await storage.createCollection(database, collection);
|
|
71
72
|
|
|
73
|
+
const indexEngine = context.getIndexEngine(collection);
|
|
74
|
+
|
|
72
75
|
for (let i = 0; i < updates.length; i++) {
|
|
73
76
|
const updateSpec = updates[i];
|
|
74
77
|
const filter = updateSpec.q || updateSpec.filter || {};
|
|
@@ -78,8 +81,15 @@ export class UpdateHandler implements ICommandHandler {
|
|
|
78
81
|
const arrayFilters = updateSpec.arrayFilters;
|
|
79
82
|
|
|
80
83
|
try {
|
|
81
|
-
//
|
|
82
|
-
|
|
84
|
+
// Try to use index-accelerated query
|
|
85
|
+
const candidateIds = await indexEngine.findCandidateIds(filter);
|
|
86
|
+
|
|
87
|
+
let documents: IStoredDocument[];
|
|
88
|
+
if (candidateIds !== null) {
|
|
89
|
+
documents = await storage.findByIds(database, collection, candidateIds);
|
|
90
|
+
} else {
|
|
91
|
+
documents = await storage.findAll(database, collection);
|
|
92
|
+
}
|
|
83
93
|
|
|
84
94
|
// Apply filter
|
|
85
95
|
let matchingDocs = QueryEngine.filter(documents, filter);
|
|
@@ -99,6 +109,8 @@ export class UpdateHandler implements ICommandHandler {
|
|
|
99
109
|
Object.assign(updatedDoc, update.$setOnInsert);
|
|
100
110
|
}
|
|
101
111
|
|
|
112
|
+
// Update index for the new document
|
|
113
|
+
await indexEngine.onInsert(updatedDoc);
|
|
102
114
|
await storage.insertOne(database, collection, updatedDoc);
|
|
103
115
|
totalUpserted++;
|
|
104
116
|
upserted.push({ index: i, _id: updatedDoc._id });
|
|
@@ -113,6 +125,8 @@ export class UpdateHandler implements ICommandHandler {
|
|
|
113
125
|
// Check if document actually changed
|
|
114
126
|
const changed = JSON.stringify(doc) !== JSON.stringify(updatedDoc);
|
|
115
127
|
if (changed) {
|
|
128
|
+
// Update index
|
|
129
|
+
await indexEngine.onUpdate(doc as any, updatedDoc);
|
|
116
130
|
await storage.updateById(database, collection, doc._id, updatedDoc);
|
|
117
131
|
totalModified++;
|
|
118
132
|
}
|
|
@@ -186,8 +200,17 @@ export class UpdateHandler implements ICommandHandler {
|
|
|
186
200
|
// Ensure collection exists
|
|
187
201
|
await storage.createCollection(database, collection);
|
|
188
202
|
|
|
189
|
-
//
|
|
190
|
-
|
|
203
|
+
// Try to use index-accelerated query
|
|
204
|
+
const indexEngine = context.getIndexEngine(collection);
|
|
205
|
+
const candidateIds = await indexEngine.findCandidateIds(query);
|
|
206
|
+
|
|
207
|
+
let documents: IStoredDocument[];
|
|
208
|
+
if (candidateIds !== null) {
|
|
209
|
+
documents = await storage.findByIds(database, collection, candidateIds);
|
|
210
|
+
} else {
|
|
211
|
+
documents = await storage.findAll(database, collection);
|
|
212
|
+
}
|
|
213
|
+
|
|
191
214
|
let matchingDocs = QueryEngine.filter(documents, query);
|
|
192
215
|
|
|
193
216
|
// Apply sort if specified
|
|
@@ -203,6 +226,8 @@ export class UpdateHandler implements ICommandHandler {
|
|
|
203
226
|
return { ok: 1, value: null };
|
|
204
227
|
}
|
|
205
228
|
|
|
229
|
+
// Update index for delete
|
|
230
|
+
await indexEngine.onDelete(doc as any);
|
|
206
231
|
await storage.deleteById(database, collection, doc._id);
|
|
207
232
|
|
|
208
233
|
let result = doc;
|
|
@@ -231,6 +256,8 @@ export class UpdateHandler implements ICommandHandler {
|
|
|
231
256
|
// Update existing
|
|
232
257
|
originalDoc = { ...doc };
|
|
233
258
|
resultDoc = UpdateEngine.applyUpdate(doc, update, arrayFilters);
|
|
259
|
+
// Update index
|
|
260
|
+
await indexEngine.onUpdate(doc as any, resultDoc as any);
|
|
234
261
|
await storage.updateById(database, collection, doc._id, resultDoc as any);
|
|
235
262
|
} else {
|
|
236
263
|
// Upsert
|
|
@@ -243,6 +270,8 @@ export class UpdateHandler implements ICommandHandler {
|
|
|
243
270
|
Object.assign(resultDoc, update.$setOnInsert);
|
|
244
271
|
}
|
|
245
272
|
|
|
273
|
+
// Update index for insert
|
|
274
|
+
await indexEngine.onInsert(resultDoc as any);
|
|
246
275
|
await storage.insertOne(database, collection, resultDoc);
|
|
247
276
|
}
|
|
248
277
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// Server module exports
|
|
2
2
|
|
|
3
|
-
export {
|
|
4
|
-
export type {
|
|
3
|
+
export { TsmdbServer } from './TsmdbServer.js';
|
|
4
|
+
export type { ITsmdbServerOptions } from './TsmdbServer.js';
|
|
5
5
|
export { WireProtocol } from './WireProtocol.js';
|
|
6
6
|
export { CommandRouter } from './CommandRouter.js';
|
|
7
7
|
export type { ICommandHandler, IHandlerContext, ICursorState } from './CommandRouter.js';
|
|
@@ -1,9 +1,20 @@
|
|
|
1
|
-
import * as plugins from '../
|
|
1
|
+
import * as plugins from '../tsmdb.plugins.js';
|
|
2
2
|
import type { IStorageAdapter } from './IStorageAdapter.js';
|
|
3
3
|
import type { IStoredDocument, IOpLogEntry, Document } from '../types/interfaces.js';
|
|
4
|
+
import { calculateDocumentChecksum, verifyChecksum } from '../utils/checksum.js';
|
|
4
5
|
|
|
5
6
|
/**
|
|
6
|
-
* File
|
|
7
|
+
* File storage adapter options
|
|
8
|
+
*/
|
|
9
|
+
export interface IFileStorageAdapterOptions {
|
|
10
|
+
/** Enable checksum verification for data integrity */
|
|
11
|
+
enableChecksums?: boolean;
|
|
12
|
+
/** Throw error on checksum mismatch (default: false, just log warning) */
|
|
13
|
+
strictChecksums?: boolean;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* File-based storage adapter for TsmDB
|
|
7
18
|
* Stores data in JSON files on disk for persistence
|
|
8
19
|
*/
|
|
9
20
|
export class FileStorageAdapter implements IStorageAdapter {
|
|
@@ -11,9 +22,13 @@ export class FileStorageAdapter implements IStorageAdapter {
|
|
|
11
22
|
private opLogCounter = 0;
|
|
12
23
|
private initialized = false;
|
|
13
24
|
private fs = new plugins.smartfs.SmartFs(new plugins.smartfs.SmartFsProviderNode());
|
|
25
|
+
private enableChecksums: boolean;
|
|
26
|
+
private strictChecksums: boolean;
|
|
14
27
|
|
|
15
|
-
constructor(basePath: string) {
|
|
28
|
+
constructor(basePath: string, options?: IFileStorageAdapterOptions) {
|
|
16
29
|
this.basePath = basePath;
|
|
30
|
+
this.enableChecksums = options?.enableChecksums ?? false;
|
|
31
|
+
this.strictChecksums = options?.strictChecksums ?? false;
|
|
17
32
|
}
|
|
18
33
|
|
|
19
34
|
// ============================================================================
|
|
@@ -68,6 +83,45 @@ export class FileStorageAdapter implements IStorageAdapter {
|
|
|
68
83
|
return doc;
|
|
69
84
|
}
|
|
70
85
|
|
|
86
|
+
/**
|
|
87
|
+
* Verify document checksum and handle errors
|
|
88
|
+
*/
|
|
89
|
+
private verifyDocumentChecksum(doc: any): boolean {
|
|
90
|
+
if (!this.enableChecksums || !doc._checksum) {
|
|
91
|
+
return true;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
const isValid = verifyChecksum(doc);
|
|
95
|
+
if (!isValid) {
|
|
96
|
+
const errorMsg = `Checksum mismatch for document ${doc._id}`;
|
|
97
|
+
if (this.strictChecksums) {
|
|
98
|
+
throw new Error(errorMsg);
|
|
99
|
+
} else {
|
|
100
|
+
console.warn(`WARNING: ${errorMsg}`);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
return isValid;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Add checksum to document before storing
|
|
108
|
+
*/
|
|
109
|
+
private prepareDocumentForStorage(doc: any): any {
|
|
110
|
+
if (!this.enableChecksums) {
|
|
111
|
+
return doc;
|
|
112
|
+
}
|
|
113
|
+
const checksum = calculateDocumentChecksum(doc);
|
|
114
|
+
return { ...doc, _checksum: checksum };
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Remove internal checksum field before returning to user
|
|
119
|
+
*/
|
|
120
|
+
private cleanDocumentForReturn(doc: any): IStoredDocument {
|
|
121
|
+
const { _checksum, ...cleanDoc } = doc;
|
|
122
|
+
return this.restoreObjectIds(cleanDoc);
|
|
123
|
+
}
|
|
124
|
+
|
|
71
125
|
// ============================================================================
|
|
72
126
|
// Initialization
|
|
73
127
|
// ============================================================================
|
|
@@ -233,7 +287,9 @@ export class FileStorageAdapter implements IStorageAdapter {
|
|
|
233
287
|
throw new Error(`Duplicate key error: _id ${idStr}`);
|
|
234
288
|
}
|
|
235
289
|
|
|
236
|
-
|
|
290
|
+
// Add checksum if enabled
|
|
291
|
+
const docToStore = this.prepareDocumentForStorage(storedDoc);
|
|
292
|
+
docs.push(docToStore);
|
|
237
293
|
await this.writeJsonFile(collPath, docs);
|
|
238
294
|
return storedDoc;
|
|
239
295
|
}
|
|
@@ -258,7 +314,9 @@ export class FileStorageAdapter implements IStorageAdapter {
|
|
|
258
314
|
}
|
|
259
315
|
|
|
260
316
|
existingIds.add(idStr);
|
|
261
|
-
|
|
317
|
+
// Add checksum if enabled
|
|
318
|
+
const docToStore = this.prepareDocumentForStorage(storedDoc);
|
|
319
|
+
docs.push(docToStore);
|
|
262
320
|
results.push(storedDoc);
|
|
263
321
|
}
|
|
264
322
|
|
|
@@ -270,10 +328,33 @@ export class FileStorageAdapter implements IStorageAdapter {
|
|
|
270
328
|
await this.createCollection(dbName, collName);
|
|
271
329
|
const collPath = this.getCollectionPath(dbName, collName);
|
|
272
330
|
const docs = await this.readJsonFile<any[]>(collPath, []);
|
|
273
|
-
return docs.map(doc =>
|
|
331
|
+
return docs.map(doc => {
|
|
332
|
+
// Verify checksum if enabled
|
|
333
|
+
this.verifyDocumentChecksum(doc);
|
|
334
|
+
// Clean and return document without internal checksum field
|
|
335
|
+
return this.cleanDocumentForReturn(doc);
|
|
336
|
+
});
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
async findByIds(dbName: string, collName: string, ids: Set<string>): Promise<IStoredDocument[]> {
|
|
340
|
+
await this.createCollection(dbName, collName);
|
|
341
|
+
const collPath = this.getCollectionPath(dbName, collName);
|
|
342
|
+
const docs = await this.readJsonFile<any[]>(collPath, []);
|
|
343
|
+
const results: IStoredDocument[] = [];
|
|
344
|
+
for (const doc of docs) {
|
|
345
|
+
// Verify checksum if enabled
|
|
346
|
+
this.verifyDocumentChecksum(doc);
|
|
347
|
+
// Clean and restore document
|
|
348
|
+
const cleaned = this.cleanDocumentForReturn(doc);
|
|
349
|
+
if (ids.has(cleaned._id.toHexString())) {
|
|
350
|
+
results.push(cleaned);
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
return results;
|
|
274
354
|
}
|
|
275
355
|
|
|
276
356
|
async findById(dbName: string, collName: string, id: plugins.bson.ObjectId): Promise<IStoredDocument | null> {
|
|
357
|
+
// Use findAll which already handles checksum verification
|
|
277
358
|
const docs = await this.findAll(dbName, collName);
|
|
278
359
|
const idStr = id.toHexString();
|
|
279
360
|
return docs.find(d => d._id.toHexString() === idStr) || null;
|
|
@@ -291,7 +372,9 @@ export class FileStorageAdapter implements IStorageAdapter {
|
|
|
291
372
|
|
|
292
373
|
if (idx === -1) return false;
|
|
293
374
|
|
|
294
|
-
|
|
375
|
+
// Add checksum if enabled
|
|
376
|
+
const docToStore = this.prepareDocumentForStorage(doc);
|
|
377
|
+
docs[idx] = docToStore;
|
|
295
378
|
await this.writeJsonFile(collPath, docs);
|
|
296
379
|
return true;
|
|
297
380
|
}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import type * as plugins from '../
|
|
1
|
+
import type * as plugins from '../tsmdb.plugins.js';
|
|
2
2
|
import type { IStoredDocument, IOpLogEntry, Document } from '../types/interfaces.js';
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
|
-
* Storage adapter interface for
|
|
5
|
+
* Storage adapter interface for TsmDB
|
|
6
6
|
* Implementations can provide different storage backends (memory, file, etc.)
|
|
7
7
|
*/
|
|
8
8
|
export interface IStorageAdapter {
|
|
@@ -90,6 +90,12 @@ export interface IStorageAdapter {
|
|
|
90
90
|
*/
|
|
91
91
|
findAll(dbName: string, collName: string): Promise<IStoredDocument[]>;
|
|
92
92
|
|
|
93
|
+
/**
|
|
94
|
+
* Find documents by a set of _id strings (hex format)
|
|
95
|
+
* Used for index-accelerated queries
|
|
96
|
+
*/
|
|
97
|
+
findByIds(dbName: string, collName: string, ids: Set<string>): Promise<IStoredDocument[]>;
|
|
98
|
+
|
|
93
99
|
/**
|
|
94
100
|
* Find a document by _id
|
|
95
101
|
*/
|