nx-mongo 3.6.0 → 3.8.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +239 -22
- package/dist/simpleMongoHelper.d.ts +68 -15
- package/dist/simpleMongoHelper.d.ts.map +1 -1
- package/dist/simpleMongoHelper.js +263 -83
- package/dist/simpleMongoHelper.js.map +1 -1
- package/package.json +1 -1
- package/src/simpleMongoHelper.ts +319 -84
- package/test-connection.ts +47 -0
|
@@ -195,13 +195,14 @@ class ProgressAPIImpl {
|
|
|
195
195
|
* Ensures the progress collection and unique index exist.
|
|
196
196
|
* Called lazily on first use.
|
|
197
197
|
*/
|
|
198
|
-
async ensureProgressIndex(session) {
|
|
198
|
+
async ensureProgressIndex(session, database, ref, type) {
|
|
199
199
|
if (this.indexEnsured) {
|
|
200
200
|
return;
|
|
201
201
|
}
|
|
202
202
|
this.helper.ensureInitialized();
|
|
203
203
|
try {
|
|
204
|
-
const
|
|
204
|
+
const db = this.helper.getDatabaseByName(database, ref, type);
|
|
205
|
+
const collection = db.collection(this.config.collection);
|
|
205
206
|
const indexes = await collection.indexes();
|
|
206
207
|
// Build index spec from uniqueIndexKeys
|
|
207
208
|
const indexSpec = {};
|
|
@@ -253,12 +254,16 @@ class ProgressAPIImpl {
|
|
|
253
254
|
return filter;
|
|
254
255
|
}
|
|
255
256
|
async isCompleted(key, options) {
|
|
256
|
-
|
|
257
|
+
const database = options?.database;
|
|
258
|
+
const ref = options?.ref;
|
|
259
|
+
const type = options?.type;
|
|
260
|
+
await this.ensureProgressIndex(options?.session, database, ref, type);
|
|
257
261
|
this.helper.ensureInitialized();
|
|
258
262
|
const provider = this.resolveProvider(options);
|
|
259
263
|
const process = options?.process;
|
|
260
264
|
const filter = this.buildFilter(key, process, provider);
|
|
261
|
-
const
|
|
265
|
+
const db = this.helper.getDatabaseByName(database, ref, type);
|
|
266
|
+
const collection = db.collection(this.config.collection);
|
|
262
267
|
const findOptions = {};
|
|
263
268
|
if (options?.session) {
|
|
264
269
|
findOptions.session = options.session;
|
|
@@ -267,12 +272,16 @@ class ProgressAPIImpl {
|
|
|
267
272
|
return record?.completed === true;
|
|
268
273
|
}
|
|
269
274
|
async start(identity, options) {
|
|
270
|
-
|
|
275
|
+
const database = options?.database;
|
|
276
|
+
const ref = options?.ref;
|
|
277
|
+
const type = options?.type;
|
|
278
|
+
await this.ensureProgressIndex(options?.session, database, ref, type);
|
|
271
279
|
this.helper.ensureInitialized();
|
|
272
280
|
const provider = this.resolveProvider({ provider: identity.provider });
|
|
273
281
|
const process = identity.process;
|
|
274
282
|
const filter = this.buildFilter(identity.key, process, provider);
|
|
275
|
-
const
|
|
283
|
+
const db = this.helper.getDatabaseByName(database, ref, type);
|
|
284
|
+
const collection = db.collection(this.config.collection);
|
|
276
285
|
const update = {
|
|
277
286
|
$set: {
|
|
278
287
|
key: identity.key,
|
|
@@ -296,12 +305,16 @@ class ProgressAPIImpl {
|
|
|
296
305
|
await collection.updateOne(filter, update, updateOptions);
|
|
297
306
|
}
|
|
298
307
|
async complete(identity, options) {
|
|
299
|
-
|
|
308
|
+
const database = options?.database;
|
|
309
|
+
const ref = options?.ref;
|
|
310
|
+
const type = options?.type;
|
|
311
|
+
await this.ensureProgressIndex(options?.session, database, ref, type);
|
|
300
312
|
this.helper.ensureInitialized();
|
|
301
313
|
const provider = this.resolveProvider({ provider: identity.provider });
|
|
302
314
|
const process = identity.process;
|
|
303
315
|
const filter = this.buildFilter(identity.key, process, provider);
|
|
304
|
-
const
|
|
316
|
+
const db = this.helper.getDatabaseByName(database, ref, type);
|
|
317
|
+
const collection = db.collection(this.config.collection);
|
|
305
318
|
const update = {
|
|
306
319
|
$set: {
|
|
307
320
|
key: identity.key,
|
|
@@ -329,7 +342,10 @@ class ProgressAPIImpl {
|
|
|
329
342
|
await collection.updateOne(filter, update, updateOptions);
|
|
330
343
|
}
|
|
331
344
|
async getCompleted(options) {
|
|
332
|
-
|
|
345
|
+
const database = options?.database;
|
|
346
|
+
const ref = options?.ref;
|
|
347
|
+
const type = options?.type;
|
|
348
|
+
await this.ensureProgressIndex(options?.session, database, ref, type);
|
|
333
349
|
this.helper.ensureInitialized();
|
|
334
350
|
const provider = this.resolveProvider(options);
|
|
335
351
|
const process = options?.process;
|
|
@@ -340,7 +356,8 @@ class ProgressAPIImpl {
|
|
|
340
356
|
if (provider !== undefined) {
|
|
341
357
|
filter.provider = provider;
|
|
342
358
|
}
|
|
343
|
-
const
|
|
359
|
+
const db = this.helper.getDatabaseByName(database, ref, type);
|
|
360
|
+
const collection = db.collection(this.config.collection);
|
|
344
361
|
const findOptions = { projection: { key: 1, name: 1, completedAt: 1 } };
|
|
345
362
|
if (options?.session) {
|
|
346
363
|
findOptions.session = options.session;
|
|
@@ -353,7 +370,10 @@ class ProgressAPIImpl {
|
|
|
353
370
|
}));
|
|
354
371
|
}
|
|
355
372
|
async getProgress(options) {
|
|
356
|
-
|
|
373
|
+
const database = options?.database;
|
|
374
|
+
const ref = options?.ref;
|
|
375
|
+
const type = options?.type;
|
|
376
|
+
await this.ensureProgressIndex(options?.session, database, ref, type);
|
|
357
377
|
this.helper.ensureInitialized();
|
|
358
378
|
const provider = this.resolveProvider(options);
|
|
359
379
|
const process = options?.process;
|
|
@@ -364,7 +384,8 @@ class ProgressAPIImpl {
|
|
|
364
384
|
if (provider !== undefined) {
|
|
365
385
|
filter.provider = provider;
|
|
366
386
|
}
|
|
367
|
-
const
|
|
387
|
+
const db = this.helper.getDatabaseByName(database, ref, type);
|
|
388
|
+
const collection = db.collection(this.config.collection);
|
|
368
389
|
const findOptions = {};
|
|
369
390
|
if (options?.session) {
|
|
370
391
|
findOptions.session = options.session;
|
|
@@ -372,12 +393,16 @@ class ProgressAPIImpl {
|
|
|
372
393
|
return await collection.find(filter, findOptions).toArray();
|
|
373
394
|
}
|
|
374
395
|
async reset(key, options) {
|
|
375
|
-
|
|
396
|
+
const database = options?.database;
|
|
397
|
+
const ref = options?.ref;
|
|
398
|
+
const type = options?.type;
|
|
399
|
+
await this.ensureProgressIndex(options?.session, database, ref, type);
|
|
376
400
|
this.helper.ensureInitialized();
|
|
377
401
|
const provider = this.resolveProvider(options);
|
|
378
402
|
const process = options?.process;
|
|
379
403
|
const filter = this.buildFilter(key, process, provider);
|
|
380
|
-
const
|
|
404
|
+
const db = this.helper.getDatabaseByName(database, ref, type);
|
|
405
|
+
const collection = db.collection(this.config.collection);
|
|
381
406
|
const update = {
|
|
382
407
|
$set: {
|
|
383
408
|
completed: false,
|
|
@@ -402,7 +427,8 @@ class SimpleMongoHelper {
|
|
|
402
427
|
this.config = null;
|
|
403
428
|
this.cleanupRegistered = false;
|
|
404
429
|
this.isDisconnecting = false;
|
|
405
|
-
|
|
430
|
+
// Strip database name from connection string if present
|
|
431
|
+
this.connectionString = this.stripDatabaseFromConnectionString(connectionString);
|
|
406
432
|
this.retryOptions = {
|
|
407
433
|
maxRetries: retryOptions?.maxRetries ?? 3,
|
|
408
434
|
retryDelay: retryOptions?.retryDelay ?? 1000,
|
|
@@ -505,9 +531,8 @@ class SimpleMongoHelper {
|
|
|
505
531
|
connectTimeoutMS: 5000
|
|
506
532
|
});
|
|
507
533
|
await testClient.connect();
|
|
508
|
-
// Try to ping the server
|
|
509
|
-
const
|
|
510
|
-
const testDb = testClient.db(dbName);
|
|
534
|
+
// Try to ping the server using 'admin' database (default)
|
|
535
|
+
const testDb = testClient.db('admin');
|
|
511
536
|
await testDb.admin().ping();
|
|
512
537
|
// Test basic operation (list collections)
|
|
513
538
|
try {
|
|
@@ -540,75 +565,115 @@ class SimpleMongoHelper {
|
|
|
540
565
|
// Ignore close errors
|
|
541
566
|
}
|
|
542
567
|
}
|
|
543
|
-
|
|
544
|
-
|
|
568
|
+
// Extract error information more comprehensively
|
|
569
|
+
let errorMessage = 'Unknown error';
|
|
570
|
+
let errorName = 'Unknown';
|
|
571
|
+
let errorCode;
|
|
572
|
+
if (error instanceof Error) {
|
|
573
|
+
errorMessage = error.message || 'Unknown error';
|
|
574
|
+
errorName = error.name || 'Error';
|
|
575
|
+
// Check for MongoDB-specific error properties
|
|
576
|
+
const mongoError = error;
|
|
577
|
+
if (mongoError.code) {
|
|
578
|
+
errorCode = mongoError.code;
|
|
579
|
+
}
|
|
580
|
+
if (mongoError.codeName) {
|
|
581
|
+
errorName = mongoError.codeName;
|
|
582
|
+
}
|
|
583
|
+
if (mongoError.errmsg) {
|
|
584
|
+
errorMessage = mongoError.errmsg;
|
|
585
|
+
}
|
|
586
|
+
}
|
|
587
|
+
else if (error && typeof error === 'object') {
|
|
588
|
+
// Try to extract meaningful information from error object
|
|
589
|
+
const errObj = error;
|
|
590
|
+
errorMessage = errObj.message || errObj.errmsg || errObj.error || JSON.stringify(error);
|
|
591
|
+
errorName = errObj.name || errObj.codeName || 'Error';
|
|
592
|
+
errorCode = errObj.code;
|
|
593
|
+
}
|
|
594
|
+
else {
|
|
595
|
+
errorMessage = String(error);
|
|
596
|
+
}
|
|
597
|
+
const errorMessageLower = errorMessage.toLowerCase();
|
|
598
|
+
// Check for Windows-specific localhost resolution issues
|
|
599
|
+
const isLocalhost = this.connectionString.includes('localhost');
|
|
600
|
+
const windowsHint = isLocalhost && process.platform === 'win32'
|
|
601
|
+
? ' On Windows, try using 127.0.0.1 instead of localhost to avoid IPv6 resolution issues.'
|
|
602
|
+
: '';
|
|
545
603
|
// Analyze error type
|
|
546
|
-
if (
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
604
|
+
if (errorMessageLower.includes('authentication failed') ||
|
|
605
|
+
errorMessageLower.includes('auth failed') ||
|
|
606
|
+
errorMessageLower.includes('invalid credentials') ||
|
|
607
|
+
errorMessageLower.includes('authentication failed') ||
|
|
608
|
+
errorCode === 18 || // MongoDB error code for authentication failed
|
|
609
|
+
errorName === 'MongoAuthenticationError') {
|
|
550
610
|
return {
|
|
551
611
|
success: false,
|
|
552
612
|
error: {
|
|
553
613
|
type: 'authentication_failed',
|
|
554
614
|
message: 'Authentication failed',
|
|
555
|
-
details: `Invalid username or password. Error: ${errorMessage}. Verify credentials in connection string.`
|
|
615
|
+
details: `Invalid username or password. Error: ${errorMessage}${errorCode ? ` (Code: ${errorCode})` : ''}. Verify credentials in connection string.`
|
|
556
616
|
}
|
|
557
617
|
};
|
|
558
618
|
}
|
|
559
|
-
if (
|
|
560
|
-
|
|
561
|
-
|
|
619
|
+
if (errorMessageLower.includes('timeout') ||
|
|
620
|
+
errorMessageLower.includes('etimedout') ||
|
|
621
|
+
errorCode === 'ETIMEDOUT' ||
|
|
622
|
+
errorName === 'MongoServerSelectionError') {
|
|
562
623
|
return {
|
|
563
624
|
success: false,
|
|
564
625
|
error: {
|
|
565
626
|
type: 'connection_failed',
|
|
566
627
|
message: 'Connection timeout',
|
|
567
|
-
details: `Cannot reach MongoDB server. Error: ${errorMessage}. Check if server is running, host/port is correct, and firewall allows connections
|
|
628
|
+
details: `Cannot reach MongoDB server. Error: ${errorMessage}${errorCode ? ` (Code: ${errorCode})` : ''}. Check if server is running, host/port is correct, and firewall allows connections.${windowsHint}`
|
|
568
629
|
}
|
|
569
630
|
};
|
|
570
631
|
}
|
|
571
|
-
if (
|
|
572
|
-
|
|
573
|
-
|
|
632
|
+
if (errorMessageLower.includes('dns') ||
|
|
633
|
+
errorMessageLower.includes('enotfound') ||
|
|
634
|
+
errorMessageLower.includes('getaddrinfo') ||
|
|
635
|
+
errorCode === 'ENOTFOUND') {
|
|
574
636
|
return {
|
|
575
637
|
success: false,
|
|
576
638
|
error: {
|
|
577
639
|
type: 'connection_failed',
|
|
578
640
|
message: 'DNS resolution failed',
|
|
579
|
-
details: `Cannot resolve hostname. Error: ${errorMessage}. Check if hostname is correct and DNS is configured properly
|
|
641
|
+
details: `Cannot resolve hostname. Error: ${errorMessage}${errorCode ? ` (Code: ${errorCode})` : ''}. Check if hostname is correct and DNS is configured properly.${windowsHint}`
|
|
580
642
|
}
|
|
581
643
|
};
|
|
582
644
|
}
|
|
583
|
-
if (
|
|
584
|
-
|
|
645
|
+
if (errorMessageLower.includes('refused') ||
|
|
646
|
+
errorMessageLower.includes('econnrefused') ||
|
|
647
|
+
errorCode === 'ECONNREFUSED') {
|
|
585
648
|
return {
|
|
586
649
|
success: false,
|
|
587
650
|
error: {
|
|
588
651
|
type: 'connection_failed',
|
|
589
652
|
message: 'Connection refused',
|
|
590
|
-
details: `MongoDB server refused connection. Error: ${errorMessage}. Check if MongoDB is running on the specified host and port
|
|
653
|
+
details: `MongoDB server refused connection. Error: ${errorMessage}${errorCode ? ` (Code: ${errorCode})` : ''}. Check if MongoDB is running on the specified host and port.${windowsHint}`
|
|
591
654
|
}
|
|
592
655
|
};
|
|
593
656
|
}
|
|
594
|
-
if (
|
|
595
|
-
|
|
657
|
+
if (errorMessageLower.includes('network') ||
|
|
658
|
+
errorMessageLower.includes('network') ||
|
|
659
|
+
errorName === 'MongoNetworkError') {
|
|
596
660
|
return {
|
|
597
661
|
success: false,
|
|
598
662
|
error: {
|
|
599
663
|
type: 'connection_failed',
|
|
600
664
|
message: 'Network error',
|
|
601
|
-
details: `Network connectivity issue. Error: ${errorMessage}. Check network connection and firewall settings
|
|
665
|
+
details: `Network connectivity issue. Error: ${errorMessage}${errorCode ? ` (Code: ${errorCode})` : ''}. Check network connection and firewall settings.${windowsHint}`
|
|
602
666
|
}
|
|
603
667
|
};
|
|
604
668
|
}
|
|
605
|
-
// Generic connection error
|
|
669
|
+
// Generic connection error with full details
|
|
670
|
+
const details = `Failed to connect to MongoDB. Error: ${errorMessage}${errorCode ? ` (Code: ${errorCode})` : ''}${errorName !== 'Error' ? ` (Type: ${errorName})` : ''}. Verify connection string, server status, and network connectivity.${windowsHint}`;
|
|
606
671
|
return {
|
|
607
672
|
success: false,
|
|
608
673
|
error: {
|
|
609
674
|
type: 'connection_failed',
|
|
610
675
|
message: 'Connection failed',
|
|
611
|
-
details
|
|
676
|
+
details
|
|
612
677
|
}
|
|
613
678
|
};
|
|
614
679
|
}
|
|
@@ -627,9 +692,8 @@ class SimpleMongoHelper {
|
|
|
627
692
|
try {
|
|
628
693
|
this.client = new mongodb_1.MongoClient(this.connectionString);
|
|
629
694
|
await this.client.connect();
|
|
630
|
-
//
|
|
631
|
-
|
|
632
|
-
this.db = this.client.db(dbName);
|
|
695
|
+
// Default to 'admin' database for initial connection (not used for operations)
|
|
696
|
+
this.db = this.client.db('admin');
|
|
633
697
|
this.isInitialized = true;
|
|
634
698
|
return;
|
|
635
699
|
}
|
|
@@ -655,10 +719,11 @@ class SimpleMongoHelper {
|
|
|
655
719
|
* @returns Array of documents matching the query or paginated result
|
|
656
720
|
* @throws Error if not initialized or if query fails
|
|
657
721
|
*/
|
|
658
|
-
async loadCollection(collectionName, query, options) {
|
|
722
|
+
async loadCollection(collectionName, query, options, database, ref, type) {
|
|
659
723
|
this.ensureInitialized();
|
|
660
724
|
try {
|
|
661
|
-
const
|
|
725
|
+
const db = this.getDatabase(this.resolveDatabase({ database, ref, type }));
|
|
726
|
+
const collection = db.collection(collectionName);
|
|
662
727
|
const filter = query || {};
|
|
663
728
|
let cursor = collection.find(filter);
|
|
664
729
|
// Apply sorting if provided
|
|
@@ -701,10 +766,11 @@ class SimpleMongoHelper {
|
|
|
701
766
|
* @returns Single document matching the query or null
|
|
702
767
|
* @throws Error if not initialized or if query fails
|
|
703
768
|
*/
|
|
704
|
-
async findOne(collectionName, query, options) {
|
|
769
|
+
async findOne(collectionName, query, options, database, ref, type) {
|
|
705
770
|
this.ensureInitialized();
|
|
706
771
|
try {
|
|
707
|
-
const
|
|
772
|
+
const db = this.getDatabase(this.resolveDatabase({ database, ref, type }));
|
|
773
|
+
const collection = db.collection(collectionName);
|
|
708
774
|
let findOptions = {};
|
|
709
775
|
if (options?.sort) {
|
|
710
776
|
findOptions.sort = options.sort;
|
|
@@ -727,10 +793,11 @@ class SimpleMongoHelper {
|
|
|
727
793
|
* @returns Insert result(s)
|
|
728
794
|
* @throws Error if not initialized or if insert fails
|
|
729
795
|
*/
|
|
730
|
-
async insert(collectionName, data, options) {
|
|
796
|
+
async insert(collectionName, data, options, database, ref, type) {
|
|
731
797
|
this.ensureInitialized();
|
|
732
798
|
try {
|
|
733
|
-
const
|
|
799
|
+
const db = this.getDatabase(this.resolveDatabase({ database, ref, type }));
|
|
800
|
+
const collection = db.collection(collectionName);
|
|
734
801
|
const insertOptions = options?.session ? { session: options.session } : {};
|
|
735
802
|
if (Array.isArray(data)) {
|
|
736
803
|
const result = await collection.insertMany(data, insertOptions);
|
|
@@ -754,10 +821,11 @@ class SimpleMongoHelper {
|
|
|
754
821
|
* @returns Update result
|
|
755
822
|
* @throws Error if not initialized or if update fails
|
|
756
823
|
*/
|
|
757
|
-
async update(collectionName, filter, updateData, options) {
|
|
824
|
+
async update(collectionName, filter, updateData, options, database, ref, type) {
|
|
758
825
|
this.ensureInitialized();
|
|
759
826
|
try {
|
|
760
|
-
const
|
|
827
|
+
const db = this.getDatabase(this.resolveDatabase({ database, ref, type }));
|
|
828
|
+
const collection = db.collection(collectionName);
|
|
761
829
|
const updateOptions = {};
|
|
762
830
|
if (options?.upsert !== undefined)
|
|
763
831
|
updateOptions.upsert = options.upsert;
|
|
@@ -784,10 +852,11 @@ class SimpleMongoHelper {
|
|
|
784
852
|
* @returns Delete result
|
|
785
853
|
* @throws Error if not initialized or if delete fails
|
|
786
854
|
*/
|
|
787
|
-
async delete(collectionName, filter, options) {
|
|
855
|
+
async delete(collectionName, filter, options, database, ref, type) {
|
|
788
856
|
this.ensureInitialized();
|
|
789
857
|
try {
|
|
790
|
-
const
|
|
858
|
+
const db = this.getDatabase(this.resolveDatabase({ database, ref, type }));
|
|
859
|
+
const collection = db.collection(collectionName);
|
|
791
860
|
if (options?.multi) {
|
|
792
861
|
const result = await collection.deleteMany(filter);
|
|
793
862
|
return result;
|
|
@@ -808,10 +877,11 @@ class SimpleMongoHelper {
|
|
|
808
877
|
* @returns Number of documents matching the query
|
|
809
878
|
* @throws Error if not initialized or if count fails
|
|
810
879
|
*/
|
|
811
|
-
async countDocuments(collectionName, query) {
|
|
880
|
+
async countDocuments(collectionName, query, database, ref, type) {
|
|
812
881
|
this.ensureInitialized();
|
|
813
882
|
try {
|
|
814
|
-
const
|
|
883
|
+
const db = this.getDatabase(this.resolveDatabase({ database, ref, type }));
|
|
884
|
+
const collection = db.collection(collectionName);
|
|
815
885
|
const count = await collection.countDocuments(query || {});
|
|
816
886
|
return count;
|
|
817
887
|
}
|
|
@@ -825,10 +895,11 @@ class SimpleMongoHelper {
|
|
|
825
895
|
* @returns Estimated number of documents
|
|
826
896
|
* @throws Error if not initialized or if count fails
|
|
827
897
|
*/
|
|
828
|
-
async estimatedDocumentCount(collectionName) {
|
|
898
|
+
async estimatedDocumentCount(collectionName, database, ref, type) {
|
|
829
899
|
this.ensureInitialized();
|
|
830
900
|
try {
|
|
831
|
-
const
|
|
901
|
+
const db = this.getDatabase(this.resolveDatabase({ database, ref, type }));
|
|
902
|
+
const collection = db.collection(collectionName);
|
|
832
903
|
const count = await collection.estimatedDocumentCount();
|
|
833
904
|
return count;
|
|
834
905
|
}
|
|
@@ -843,10 +914,11 @@ class SimpleMongoHelper {
|
|
|
843
914
|
* @returns Array of aggregated results
|
|
844
915
|
* @throws Error if not initialized or if aggregation fails
|
|
845
916
|
*/
|
|
846
|
-
async aggregate(collectionName, pipeline) {
|
|
917
|
+
async aggregate(collectionName, pipeline, database, ref, type) {
|
|
847
918
|
this.ensureInitialized();
|
|
848
919
|
try {
|
|
849
|
-
const
|
|
920
|
+
const db = this.getDatabase(this.resolveDatabase({ database, ref, type }));
|
|
921
|
+
const collection = db.collection(collectionName);
|
|
850
922
|
const results = await collection.aggregate(pipeline).toArray();
|
|
851
923
|
return results;
|
|
852
924
|
}
|
|
@@ -862,10 +934,11 @@ class SimpleMongoHelper {
|
|
|
862
934
|
* @returns Index name
|
|
863
935
|
* @throws Error if not initialized or if index creation fails
|
|
864
936
|
*/
|
|
865
|
-
async createIndex(collectionName, indexSpec, options) {
|
|
937
|
+
async createIndex(collectionName, indexSpec, options, database, ref, type) {
|
|
866
938
|
this.ensureInitialized();
|
|
867
939
|
try {
|
|
868
|
-
const
|
|
940
|
+
const db = this.getDatabase(this.resolveDatabase({ database, ref, type }));
|
|
941
|
+
const collection = db.collection(collectionName);
|
|
869
942
|
const indexName = await collection.createIndex(indexSpec, options);
|
|
870
943
|
return indexName;
|
|
871
944
|
}
|
|
@@ -880,10 +953,11 @@ class SimpleMongoHelper {
|
|
|
880
953
|
* @returns Result object
|
|
881
954
|
* @throws Error if not initialized or if index drop fails
|
|
882
955
|
*/
|
|
883
|
-
async dropIndex(collectionName, indexName) {
|
|
956
|
+
async dropIndex(collectionName, indexName, database, ref, type) {
|
|
884
957
|
this.ensureInitialized();
|
|
885
958
|
try {
|
|
886
|
-
const
|
|
959
|
+
const db = this.getDatabase(this.resolveDatabase({ database, ref, type }));
|
|
960
|
+
const collection = db.collection(collectionName);
|
|
887
961
|
const result = await collection.dropIndex(indexName);
|
|
888
962
|
return result;
|
|
889
963
|
}
|
|
@@ -897,10 +971,11 @@ class SimpleMongoHelper {
|
|
|
897
971
|
* @returns Array of index information
|
|
898
972
|
* @throws Error if not initialized or if listing fails
|
|
899
973
|
*/
|
|
900
|
-
async listIndexes(collectionName) {
|
|
974
|
+
async listIndexes(collectionName, database, ref, type) {
|
|
901
975
|
this.ensureInitialized();
|
|
902
976
|
try {
|
|
903
|
-
const
|
|
977
|
+
const db = this.getDatabase(this.resolveDatabase({ database, ref, type }));
|
|
978
|
+
const collection = db.collection(collectionName);
|
|
904
979
|
const indexes = await collection.indexes();
|
|
905
980
|
return indexes;
|
|
906
981
|
}
|
|
@@ -962,9 +1037,13 @@ class SimpleMongoHelper {
|
|
|
962
1037
|
if (!inputConfig) {
|
|
963
1038
|
throw new Error(`Ref '${ref}' not found in configuration inputs.`);
|
|
964
1039
|
}
|
|
1040
|
+
const database = options?.database;
|
|
1041
|
+
const dbRef = options?.ref;
|
|
1042
|
+
const dbType = options?.type;
|
|
1043
|
+
const db = this.getDatabase(this.resolveDatabase({ database, ref: dbRef, type: dbType }));
|
|
965
1044
|
// Note: loadCollection doesn't support session yet, but we'll pass it through options
|
|
966
1045
|
// For now, we'll use the collection directly with session support
|
|
967
|
-
const collection =
|
|
1046
|
+
const collection = db.collection(inputConfig.collection);
|
|
968
1047
|
const filter = inputConfig.query || {};
|
|
969
1048
|
const session = options?.session;
|
|
970
1049
|
try {
|
|
@@ -1019,8 +1098,12 @@ class SimpleMongoHelper {
|
|
|
1019
1098
|
this.ensureInitialized();
|
|
1020
1099
|
const fieldName = options?.fieldName || '_sig';
|
|
1021
1100
|
const unique = options?.unique !== false; // Default to true
|
|
1101
|
+
const database = options?.database;
|
|
1102
|
+
const ref = options?.ref;
|
|
1103
|
+
const type = options?.type;
|
|
1022
1104
|
try {
|
|
1023
|
-
const
|
|
1105
|
+
const db = this.getDatabase(this.resolveDatabase({ database, ref, type }));
|
|
1106
|
+
const collection = db.collection(collectionName);
|
|
1024
1107
|
const indexes = await collection.indexes();
|
|
1025
1108
|
// Find existing index on the signature field
|
|
1026
1109
|
const existingIndex = indexes.find(idx => {
|
|
@@ -1090,6 +1173,9 @@ class SimpleMongoHelper {
|
|
|
1090
1173
|
const mode = outputConfig.mode || this.config.output?.mode || 'append';
|
|
1091
1174
|
const ensureIndex = options?.ensureIndex !== false; // Default to true
|
|
1092
1175
|
const session = options?.session;
|
|
1176
|
+
const database = options?.database;
|
|
1177
|
+
const dbRef = options?.ref;
|
|
1178
|
+
const dbType = options?.type;
|
|
1093
1179
|
const result = {
|
|
1094
1180
|
inserted: 0,
|
|
1095
1181
|
updated: 0,
|
|
@@ -1097,7 +1183,8 @@ class SimpleMongoHelper {
|
|
|
1097
1183
|
indexCreated: false,
|
|
1098
1184
|
};
|
|
1099
1185
|
try {
|
|
1100
|
-
const
|
|
1186
|
+
const db = this.getDatabase(this.resolveDatabase({ database, ref: dbRef, type: dbType }));
|
|
1187
|
+
const collection = db.collection(collectionName);
|
|
1101
1188
|
// Handle replace mode: clear collection first
|
|
1102
1189
|
if (mode === 'replace') {
|
|
1103
1190
|
const deleteOptions = session ? { session } : {};
|
|
@@ -1251,6 +1338,9 @@ class SimpleMongoHelper {
|
|
|
1251
1338
|
const writeResult = await this.writeByRef(ref, documents, {
|
|
1252
1339
|
session: options?.session,
|
|
1253
1340
|
ensureIndex: options?.ensureIndex,
|
|
1341
|
+
database: options?.database,
|
|
1342
|
+
ref: options?.ref,
|
|
1343
|
+
type: options?.type,
|
|
1254
1344
|
});
|
|
1255
1345
|
const result = {
|
|
1256
1346
|
...writeResult,
|
|
@@ -1265,7 +1355,7 @@ class SimpleMongoHelper {
|
|
|
1265
1355
|
name: options.complete.name,
|
|
1266
1356
|
provider: options.complete.provider,
|
|
1267
1357
|
metadata: options.complete.metadata,
|
|
1268
|
-
}, { session: options.session });
|
|
1358
|
+
}, { session: options.session, database: options?.database, ref: options?.ref, type: options?.type });
|
|
1269
1359
|
result.completed = true;
|
|
1270
1360
|
}
|
|
1271
1361
|
catch (error) {
|
|
@@ -1286,7 +1376,7 @@ class SimpleMongoHelper {
|
|
|
1286
1376
|
*/
|
|
1287
1377
|
async mergeCollections(options) {
|
|
1288
1378
|
this.ensureInitialized();
|
|
1289
|
-
const { sourceCollection1, sourceCollection2, targetCollection, strategy, key, compositeKeys, joinType, fieldPrefix1 = 'record', fieldPrefix2 = 'assessment', includeIndex = strategy === 'index', onUnmatched1 = 'include', onUnmatched2 = 'include', session, } = options;
|
|
1379
|
+
const { sourceCollection1, sourceCollection2, targetCollection, strategy, key, compositeKeys, joinType, fieldPrefix1 = 'record', fieldPrefix2 = 'assessment', includeIndex = strategy === 'index', onUnmatched1 = 'include', onUnmatched2 = 'include', session, database, ref, type, } = options;
|
|
1290
1380
|
// Determine join behavior from joinType or fallback to onUnmatched flags
|
|
1291
1381
|
let includeUnmatched1;
|
|
1292
1382
|
let includeUnmatched2;
|
|
@@ -1326,9 +1416,10 @@ class SimpleMongoHelper {
|
|
|
1326
1416
|
errors: [],
|
|
1327
1417
|
};
|
|
1328
1418
|
try {
|
|
1329
|
-
const
|
|
1330
|
-
const
|
|
1331
|
-
const
|
|
1419
|
+
const db = this.getDatabase(this.resolveDatabase({ database, ref, type }));
|
|
1420
|
+
const coll1 = db.collection(sourceCollection1);
|
|
1421
|
+
const coll2 = db.collection(sourceCollection2);
|
|
1422
|
+
const targetColl = db.collection(targetCollection);
|
|
1332
1423
|
const findOptions = {};
|
|
1333
1424
|
if (session) {
|
|
1334
1425
|
findOptions.session = session;
|
|
@@ -1724,21 +1815,110 @@ class SimpleMongoHelper {
|
|
|
1724
1815
|
return this.db;
|
|
1725
1816
|
}
|
|
1726
1817
|
/**
|
|
1727
|
-
*
|
|
1728
|
-
* @param connectionString - MongoDB connection string
|
|
1729
|
-
* @returns
|
|
1818
|
+
* Strips database name from MongoDB connection string, returning base connection string.
|
|
1819
|
+
* @param connectionString - MongoDB connection string (may include database name)
|
|
1820
|
+
* @returns Base connection string without database name
|
|
1821
|
+
* @example
|
|
1822
|
+
* stripDatabaseFromConnectionString('mongodb://localhost:27017/admin')
|
|
1823
|
+
* // Returns: 'mongodb://localhost:27017/'
|
|
1730
1824
|
*/
|
|
1731
|
-
|
|
1825
|
+
stripDatabaseFromConnectionString(connectionString) {
|
|
1732
1826
|
try {
|
|
1733
1827
|
const url = new URL(connectionString);
|
|
1734
|
-
|
|
1735
|
-
|
|
1828
|
+
// Remove pathname (database name) but keep trailing slash
|
|
1829
|
+
url.pathname = '/';
|
|
1830
|
+
return url.toString();
|
|
1736
1831
|
}
|
|
1737
1832
|
catch {
|
|
1738
|
-
// If URL parsing fails, try
|
|
1739
|
-
|
|
1740
|
-
|
|
1833
|
+
// If URL parsing fails, try regex pattern matching
|
|
1834
|
+
// Match: mongodb://host:port/database?options or mongodb://host:port/database
|
|
1835
|
+
const match = connectionString.match(/^([^\/]+\/\/[^\/]+)\/([^\/\?]+)(\?.*)?$/);
|
|
1836
|
+
if (match) {
|
|
1837
|
+
// Return base URL with trailing slash
|
|
1838
|
+
return match[1] + '/';
|
|
1839
|
+
}
|
|
1840
|
+
// If no database found, return as-is (should already be base URL)
|
|
1841
|
+
return connectionString.endsWith('/') ? connectionString : connectionString + '/';
|
|
1842
|
+
}
|
|
1843
|
+
}
|
|
1844
|
+
/**
|
|
1845
|
+
* Resolves the database name from provided options using the databases config map.
|
|
1846
|
+
* Priority: database > ref+type > ref > type
|
|
1847
|
+
* @param options - Options containing database, ref, and/or type
|
|
1848
|
+
* @returns Resolved database name or undefined (will default to 'admin')
|
|
1849
|
+
* @throws Error if no match found or multiple matches found
|
|
1850
|
+
* @internal
|
|
1851
|
+
*/
|
|
1852
|
+
resolveDatabase(options) {
|
|
1853
|
+
// Priority 1: If database is provided directly, use it
|
|
1854
|
+
if (options?.database) {
|
|
1855
|
+
return options.database;
|
|
1856
|
+
}
|
|
1857
|
+
// If no config or no databases map, return undefined (will default to 'admin')
|
|
1858
|
+
if (!this.config || !this.config.databases || this.config.databases.length === 0) {
|
|
1859
|
+
return undefined;
|
|
1860
|
+
}
|
|
1861
|
+
const databases = this.config.databases;
|
|
1862
|
+
// Priority 2: If both ref and type are provided, find exact match
|
|
1863
|
+
if (options?.ref && options?.type) {
|
|
1864
|
+
const matches = databases.filter(db => db.ref === options.ref && db.type === options.type);
|
|
1865
|
+
if (matches.length === 0) {
|
|
1866
|
+
throw new Error(`No database found for ref: ${options.ref} and type: ${options.type}`);
|
|
1867
|
+
}
|
|
1868
|
+
if (matches.length > 1) {
|
|
1869
|
+
throw new Error(`Multiple databases found for ref: ${options.ref} and type: ${options.type}`);
|
|
1870
|
+
}
|
|
1871
|
+
return matches[0].database;
|
|
1741
1872
|
}
|
|
1873
|
+
// Priority 3: If ref is provided, find by ref
|
|
1874
|
+
if (options?.ref) {
|
|
1875
|
+
const matches = databases.filter(db => db.ref === options.ref);
|
|
1876
|
+
if (matches.length === 0) {
|
|
1877
|
+
throw new Error(`No database found for ref: ${options.ref}`);
|
|
1878
|
+
}
|
|
1879
|
+
if (matches.length > 1) {
|
|
1880
|
+
throw new Error(`Multiple databases found for ref: ${options.ref}. Use 'type' parameter to narrow down.`);
|
|
1881
|
+
}
|
|
1882
|
+
return matches[0].database;
|
|
1883
|
+
}
|
|
1884
|
+
// Priority 4: If type is provided, find by type
|
|
1885
|
+
if (options?.type) {
|
|
1886
|
+
const matches = databases.filter(db => db.type === options.type);
|
|
1887
|
+
if (matches.length === 0) {
|
|
1888
|
+
throw new Error(`No database found for type: ${options.type}`);
|
|
1889
|
+
}
|
|
1890
|
+
if (matches.length > 1) {
|
|
1891
|
+
throw new Error(`Multiple databases found for type: ${options.type}. Use 'ref' parameter to narrow down.`);
|
|
1892
|
+
}
|
|
1893
|
+
return matches[0].database;
|
|
1894
|
+
}
|
|
1895
|
+
// No options provided, return undefined (will default to 'admin')
|
|
1896
|
+
return undefined;
|
|
1897
|
+
}
|
|
1898
|
+
/**
|
|
1899
|
+
* Gets a database instance by name. Defaults to 'admin' if no name provided.
|
|
1900
|
+
* @param databaseName - Optional database name (defaults to 'admin')
|
|
1901
|
+
* @returns Database instance
|
|
1902
|
+
* @throws Error if client is not initialized
|
|
1903
|
+
* @internal
|
|
1904
|
+
*/
|
|
1905
|
+
getDatabase(databaseName) {
|
|
1906
|
+
if (!this.client) {
|
|
1907
|
+
throw new Error('MongoDB client not initialized. Call initialize() first.');
|
|
1908
|
+
}
|
|
1909
|
+
return this.client.db(databaseName || 'admin');
|
|
1910
|
+
}
|
|
1911
|
+
/**
|
|
1912
|
+
* Gets a database instance by name. Defaults to 'admin' if no name provided.
|
|
1913
|
+
* Public method for use by ProgressAPIImpl.
|
|
1914
|
+
* @param databaseName - Optional database name (defaults to 'admin')
|
|
1915
|
+
* @param ref - Optional ref for database resolution
|
|
1916
|
+
* @param type - Optional type for database resolution
|
|
1917
|
+
* @returns Database instance
|
|
1918
|
+
* @throws Error if client is not initialized
|
|
1919
|
+
*/
|
|
1920
|
+
getDatabaseByName(databaseName, ref, type) {
|
|
1921
|
+
return this.getDatabase(this.resolveDatabase({ database: databaseName, ref, type }));
|
|
1742
1922
|
}
|
|
1743
1923
|
}
|
|
1744
1924
|
exports.SimpleMongoHelper = SimpleMongoHelper;
|