@push.rocks/smartmongo 3.0.0 → 4.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.
Files changed (48) hide show
  1. package/dist_ts/00_commitinfo_data.js +1 -1
  2. package/dist_ts/tsmdb/engine/IndexEngine.d.ts +23 -3
  3. package/dist_ts/tsmdb/engine/IndexEngine.js +357 -55
  4. package/dist_ts/tsmdb/engine/QueryPlanner.d.ts +64 -0
  5. package/dist_ts/tsmdb/engine/QueryPlanner.js +308 -0
  6. package/dist_ts/tsmdb/engine/SessionEngine.d.ts +117 -0
  7. package/dist_ts/tsmdb/engine/SessionEngine.js +232 -0
  8. package/dist_ts/tsmdb/index.d.ts +7 -0
  9. package/dist_ts/tsmdb/index.js +6 -1
  10. package/dist_ts/tsmdb/server/CommandRouter.d.ts +36 -0
  11. package/dist_ts/tsmdb/server/CommandRouter.js +91 -1
  12. package/dist_ts/tsmdb/server/TsmdbServer.js +3 -1
  13. package/dist_ts/tsmdb/server/handlers/AdminHandler.js +106 -6
  14. package/dist_ts/tsmdb/server/handlers/DeleteHandler.js +15 -3
  15. package/dist_ts/tsmdb/server/handlers/FindHandler.js +44 -14
  16. package/dist_ts/tsmdb/server/handlers/InsertHandler.js +4 -1
  17. package/dist_ts/tsmdb/server/handlers/UpdateHandler.js +31 -5
  18. package/dist_ts/tsmdb/storage/FileStorageAdapter.d.ts +25 -1
  19. package/dist_ts/tsmdb/storage/FileStorageAdapter.js +75 -6
  20. package/dist_ts/tsmdb/storage/IStorageAdapter.d.ts +5 -0
  21. package/dist_ts/tsmdb/storage/MemoryStorageAdapter.d.ts +1 -0
  22. package/dist_ts/tsmdb/storage/MemoryStorageAdapter.js +12 -1
  23. package/dist_ts/tsmdb/storage/WAL.d.ts +117 -0
  24. package/dist_ts/tsmdb/storage/WAL.js +286 -0
  25. package/dist_ts/tsmdb/utils/checksum.d.ts +30 -0
  26. package/dist_ts/tsmdb/utils/checksum.js +77 -0
  27. package/dist_ts/tsmdb/utils/index.d.ts +1 -0
  28. package/dist_ts/tsmdb/utils/index.js +2 -0
  29. package/package.json +2 -2
  30. package/readme.md +140 -17
  31. package/ts/00_commitinfo_data.ts +1 -1
  32. package/ts/tsmdb/engine/IndexEngine.ts +375 -56
  33. package/ts/tsmdb/engine/QueryPlanner.ts +393 -0
  34. package/ts/tsmdb/engine/SessionEngine.ts +292 -0
  35. package/ts/tsmdb/index.ts +9 -0
  36. package/ts/tsmdb/server/CommandRouter.ts +109 -0
  37. package/ts/tsmdb/server/TsmdbServer.ts +3 -0
  38. package/ts/tsmdb/server/handlers/AdminHandler.ts +110 -5
  39. package/ts/tsmdb/server/handlers/DeleteHandler.ts +17 -2
  40. package/ts/tsmdb/server/handlers/FindHandler.ts +42 -13
  41. package/ts/tsmdb/server/handlers/InsertHandler.ts +6 -0
  42. package/ts/tsmdb/server/handlers/UpdateHandler.ts +33 -4
  43. package/ts/tsmdb/storage/FileStorageAdapter.ts +88 -5
  44. package/ts/tsmdb/storage/IStorageAdapter.ts +6 -0
  45. package/ts/tsmdb/storage/MemoryStorageAdapter.ts +12 -0
  46. package/ts/tsmdb/storage/WAL.ts +375 -0
  47. package/ts/tsmdb/utils/checksum.ts +88 -0
  48. package/ts/tsmdb/utils/index.ts +1 -0
@@ -2,6 +2,9 @@ import * as plugins from '../tsmdb.plugins.js';
2
2
  import type { IStorageAdapter } from '../storage/IStorageAdapter.js';
3
3
  import type { IParsedCommand } from './WireProtocol.js';
4
4
  import type { TsmdbServer } from './TsmdbServer.js';
5
+ import { IndexEngine } from '../engine/IndexEngine.js';
6
+ import { TransactionEngine } from '../engine/TransactionEngine.js';
7
+ import { SessionEngine } from '../engine/SessionEngine.js';
5
8
 
6
9
  // Import handlers
7
10
  import { HelloHandler } from './handlers/HelloHandler.js';
@@ -22,6 +25,16 @@ export interface IHandlerContext {
22
25
  database: string;
23
26
  command: plugins.bson.Document;
24
27
  documentSequences?: Map<string, plugins.bson.Document[]>;
28
+ /** Get or create an IndexEngine for a collection */
29
+ getIndexEngine: (collName: string) => IndexEngine;
30
+ /** Transaction engine instance */
31
+ transactionEngine: TransactionEngine;
32
+ /** Current transaction ID (if in a transaction) */
33
+ txnId?: string;
34
+ /** Session ID (from lsid) */
35
+ sessionId?: string;
36
+ /** Session engine instance */
37
+ sessionEngine: SessionEngine;
25
38
  }
26
39
 
27
40
  /**
@@ -43,12 +56,54 @@ export class CommandRouter {
43
56
  private cursors: Map<bigint, ICursorState> = new Map();
44
57
  private cursorIdCounter: bigint = BigInt(1);
45
58
 
59
+ // Index engine cache: db.collection -> IndexEngine
60
+ private indexEngines: Map<string, IndexEngine> = new Map();
61
+
62
+ // Transaction engine (shared across all handlers)
63
+ private transactionEngine: TransactionEngine;
64
+
65
+ // Session engine (shared across all handlers)
66
+ private sessionEngine: SessionEngine;
67
+
46
68
  constructor(storage: IStorageAdapter, server: TsmdbServer) {
47
69
  this.storage = storage;
48
70
  this.server = server;
71
+ this.transactionEngine = new TransactionEngine(storage);
72
+ this.sessionEngine = new SessionEngine();
73
+ // Link session engine to transaction engine for auto-abort on session expiry
74
+ this.sessionEngine.setTransactionEngine(this.transactionEngine);
49
75
  this.registerHandlers();
50
76
  }
51
77
 
78
+ /**
79
+ * Get or create an IndexEngine for a database.collection
80
+ */
81
+ getIndexEngine(dbName: string, collName: string): IndexEngine {
82
+ const key = `${dbName}.${collName}`;
83
+ let engine = this.indexEngines.get(key);
84
+ if (!engine) {
85
+ engine = new IndexEngine(dbName, collName, this.storage);
86
+ this.indexEngines.set(key, engine);
87
+ }
88
+ return engine;
89
+ }
90
+
91
+ /**
92
+ * Clear index engine cache for a collection (used when collection is dropped)
93
+ */
94
+ clearIndexEngineCache(dbName: string, collName?: string): void {
95
+ if (collName) {
96
+ this.indexEngines.delete(`${dbName}.${collName}`);
97
+ } else {
98
+ // Clear all engines for the database
99
+ for (const key of this.indexEngines.keys()) {
100
+ if (key.startsWith(`${dbName}.`)) {
101
+ this.indexEngines.delete(key);
102
+ }
103
+ }
104
+ }
105
+ }
106
+
52
107
  /**
53
108
  * Register all command handlers
54
109
  */
@@ -120,6 +175,29 @@ export class CommandRouter {
120
175
  async route(parsedCommand: IParsedCommand): Promise<plugins.bson.Document> {
121
176
  const { commandName, command, database, documentSequences } = parsedCommand;
122
177
 
178
+ // Extract session ID from lsid using SessionEngine helper
179
+ let sessionId = SessionEngine.extractSessionId(command.lsid);
180
+ let txnId: string | undefined;
181
+
182
+ // If we have a session ID, register/touch the session
183
+ if (sessionId) {
184
+ this.sessionEngine.getOrCreateSession(sessionId);
185
+ }
186
+
187
+ // Check if this starts a new transaction
188
+ if (command.startTransaction && sessionId) {
189
+ txnId = this.transactionEngine.startTransaction(sessionId);
190
+ this.sessionEngine.startTransaction(sessionId, txnId, command.txnNumber);
191
+ } else if (sessionId && this.sessionEngine.isInTransaction(sessionId)) {
192
+ // Continue existing transaction
193
+ txnId = this.sessionEngine.getTransactionId(sessionId);
194
+ // Verify transaction is still active
195
+ if (txnId && !this.transactionEngine.isActive(txnId)) {
196
+ this.sessionEngine.endTransaction(sessionId);
197
+ txnId = undefined;
198
+ }
199
+ }
200
+
123
201
  // Create handler context
124
202
  const context: IHandlerContext = {
125
203
  storage: this.storage,
@@ -127,6 +205,11 @@ export class CommandRouter {
127
205
  database,
128
206
  command,
129
207
  documentSequences,
208
+ getIndexEngine: (collName: string) => this.getIndexEngine(database, collName),
209
+ transactionEngine: this.transactionEngine,
210
+ sessionEngine: this.sessionEngine,
211
+ txnId,
212
+ sessionId,
130
213
  };
131
214
 
132
215
  // Find handler
@@ -164,6 +247,32 @@ export class CommandRouter {
164
247
  };
165
248
  }
166
249
  }
250
+
251
+ /**
252
+ * Close the command router and cleanup resources
253
+ */
254
+ close(): void {
255
+ // Close session engine (stops cleanup interval, clears sessions)
256
+ this.sessionEngine.close();
257
+ // Clear cursors
258
+ this.cursors.clear();
259
+ // Clear index engine cache
260
+ this.indexEngines.clear();
261
+ }
262
+
263
+ /**
264
+ * Get session engine (for administrative purposes)
265
+ */
266
+ getSessionEngine(): SessionEngine {
267
+ return this.sessionEngine;
268
+ }
269
+
270
+ /**
271
+ * Get transaction engine (for administrative purposes)
272
+ */
273
+ getTransactionEngine(): TransactionEngine {
274
+ return this.transactionEngine;
275
+ }
167
276
  }
168
277
 
169
278
  /**
@@ -154,6 +154,9 @@ export class TsmdbServer {
154
154
  }
155
155
  this.connections.clear();
156
156
 
157
+ // Close command router (cleans up session engine, cursors, etc.)
158
+ this.commandRouter.close();
159
+
157
160
  // Close storage
158
161
  await this.storage.close();
159
162
 
@@ -1,5 +1,6 @@
1
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,10 +238,12 @@ 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,
@@ -263,6 +266,26 @@ 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,
@@ -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
- // Transactions are not fully supported, but acknowledge the command
420
- return { ok: 1 };
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
- // Transactions are not fully supported, but acknowledge the command
428
- return { ok: 1 };
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,5 +1,6 @@
1
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
- // Get all documents
60
- const documents = await storage.findAll(database, collection);
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
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
- // Get all documents
74
- let documents = await storage.findAll(database, collection);
74
+ // Try to use index-accelerated query
75
+ const indexEngine = getIndexEngine(collection);
76
+ const candidateIds = await indexEngine.findCandidateIds(filter);
75
77
 
76
- // Apply filter
77
- documents = QueryEngine.filter(documents, filter);
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
- // Get all documents
250
- let documents = await storage.findAll(database, collection);
261
+ // Try to use index-accelerated query
262
+ const indexEngine = getIndexEngine(collection);
263
+ const candidateIds = await indexEngine.findCandidateIds(query);
251
264
 
252
- // Apply filter
253
- documents = QueryEngine.filter(documents, query);
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
- // Get all documents
294
- const documents = await storage.findAll(database, collection);
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
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
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
- // Get all documents
82
- let documents = await storage.findAll(database, collection);
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
- // Get matching documents
190
- let documents = await storage.findAll(database, collection);
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