nx-mongo 3.6.0 → 3.8.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/README.md +206 -19
- package/dist/simpleMongoHelper.d.ts +68 -15
- package/dist/simpleMongoHelper.d.ts.map +1 -1
- package/dist/simpleMongoHelper.js +200 -60
- package/dist/simpleMongoHelper.js.map +1 -1
- package/package.json +1 -1
- package/src/simpleMongoHelper.ts +254 -61
package/src/simpleMongoHelper.ts
CHANGED
|
@@ -47,6 +47,11 @@ export interface HelperConfig {
|
|
|
47
47
|
uniqueIndexKeys?: string[];
|
|
48
48
|
provider?: string;
|
|
49
49
|
};
|
|
50
|
+
databases?: Array<{
|
|
51
|
+
ref: string;
|
|
52
|
+
type: string;
|
|
53
|
+
database: string;
|
|
54
|
+
}>;
|
|
50
55
|
}
|
|
51
56
|
|
|
52
57
|
export interface WriteByRefResult {
|
|
@@ -82,7 +87,7 @@ export interface StageRecord extends StageIdentity {
|
|
|
82
87
|
metadata?: StageMetadata;
|
|
83
88
|
}
|
|
84
89
|
|
|
85
|
-
export type ProgressSessionOptions = { session?: ClientSession };
|
|
90
|
+
export type ProgressSessionOptions = { session?: ClientSession; database?: string; ref?: string; type?: string };
|
|
86
91
|
|
|
87
92
|
export interface ProgressAPI {
|
|
88
93
|
isCompleted(key: string, options?: ProgressSessionOptions & { process?: string; provider?: string }): Promise<boolean>;
|
|
@@ -99,6 +104,9 @@ export interface ProgressAPI {
|
|
|
99
104
|
export interface WriteStageOptions {
|
|
100
105
|
ensureIndex?: boolean;
|
|
101
106
|
session?: ClientSession;
|
|
107
|
+
database?: string;
|
|
108
|
+
ref?: string;
|
|
109
|
+
type?: string;
|
|
102
110
|
complete?: {
|
|
103
111
|
key: string;
|
|
104
112
|
process?: string;
|
|
@@ -126,6 +134,9 @@ export interface MergeCollectionsOptions {
|
|
|
126
134
|
onUnmatched1?: 'include' | 'skip'; // What to do with unmatched records from collection 1 (default: 'include', deprecated: use joinType instead)
|
|
127
135
|
onUnmatched2?: 'include' | 'skip'; // What to do with unmatched records from collection 2 (default: 'include', deprecated: use joinType instead)
|
|
128
136
|
session?: ClientSession;
|
|
137
|
+
database?: string; // Database name (defaults to 'admin')
|
|
138
|
+
ref?: string; // Optional ref for database resolution
|
|
139
|
+
type?: string; // Optional type for database resolution
|
|
129
140
|
}
|
|
130
141
|
|
|
131
142
|
export interface MergeCollectionsResult {
|
|
@@ -361,7 +372,7 @@ class ProgressAPIImpl implements ProgressAPI {
|
|
|
361
372
|
* Ensures the progress collection and unique index exist.
|
|
362
373
|
* Called lazily on first use.
|
|
363
374
|
*/
|
|
364
|
-
private async ensureProgressIndex(session?: ClientSession): Promise<void> {
|
|
375
|
+
private async ensureProgressIndex(session?: ClientSession, database?: string, ref?: string, type?: string): Promise<void> {
|
|
365
376
|
if (this.indexEnsured) {
|
|
366
377
|
return;
|
|
367
378
|
}
|
|
@@ -369,7 +380,8 @@ class ProgressAPIImpl implements ProgressAPI {
|
|
|
369
380
|
this.helper.ensureInitialized();
|
|
370
381
|
|
|
371
382
|
try {
|
|
372
|
-
const
|
|
383
|
+
const db = this.helper.getDatabaseByName(database, ref, type);
|
|
384
|
+
const collection = db.collection(this.config.collection);
|
|
373
385
|
const indexes = await collection.indexes();
|
|
374
386
|
|
|
375
387
|
// Build index spec from uniqueIndexKeys
|
|
@@ -429,13 +441,17 @@ class ProgressAPIImpl implements ProgressAPI {
|
|
|
429
441
|
}
|
|
430
442
|
|
|
431
443
|
async isCompleted(key: string, options?: ProgressSessionOptions & { process?: string; provider?: string }): Promise<boolean> {
|
|
432
|
-
|
|
444
|
+
const database = options?.database;
|
|
445
|
+
const ref = options?.ref;
|
|
446
|
+
const type = options?.type;
|
|
447
|
+
await this.ensureProgressIndex(options?.session, database, ref, type);
|
|
433
448
|
this.helper.ensureInitialized();
|
|
434
449
|
|
|
435
450
|
const provider = this.resolveProvider(options);
|
|
436
451
|
const process = options?.process;
|
|
437
452
|
const filter = this.buildFilter(key, process, provider);
|
|
438
|
-
const
|
|
453
|
+
const db = this.helper.getDatabaseByName(database, ref, type);
|
|
454
|
+
const collection = db.collection<StageRecord>(this.config.collection);
|
|
439
455
|
|
|
440
456
|
const findOptions: any = {};
|
|
441
457
|
if (options?.session) {
|
|
@@ -447,13 +463,17 @@ class ProgressAPIImpl implements ProgressAPI {
|
|
|
447
463
|
}
|
|
448
464
|
|
|
449
465
|
async start(identity: StageIdentity, options?: ProgressSessionOptions): Promise<void> {
|
|
450
|
-
|
|
466
|
+
const database = options?.database;
|
|
467
|
+
const ref = options?.ref;
|
|
468
|
+
const type = options?.type;
|
|
469
|
+
await this.ensureProgressIndex(options?.session, database, ref, type);
|
|
451
470
|
this.helper.ensureInitialized();
|
|
452
471
|
|
|
453
472
|
const provider = this.resolveProvider({ provider: identity.provider });
|
|
454
473
|
const process = identity.process;
|
|
455
474
|
const filter = this.buildFilter(identity.key, process, provider);
|
|
456
|
-
const
|
|
475
|
+
const db = this.helper.getDatabaseByName(database, ref, type);
|
|
476
|
+
const collection = db.collection<StageRecord>(this.config.collection);
|
|
457
477
|
|
|
458
478
|
const update: UpdateFilter<StageRecord> = {
|
|
459
479
|
$set: {
|
|
@@ -485,13 +505,17 @@ class ProgressAPIImpl implements ProgressAPI {
|
|
|
485
505
|
identity: StageIdentity & { metadata?: StageMetadata },
|
|
486
506
|
options?: ProgressSessionOptions
|
|
487
507
|
): Promise<void> {
|
|
488
|
-
|
|
508
|
+
const database = options?.database;
|
|
509
|
+
const ref = options?.ref;
|
|
510
|
+
const type = options?.type;
|
|
511
|
+
await this.ensureProgressIndex(options?.session, database, ref, type);
|
|
489
512
|
this.helper.ensureInitialized();
|
|
490
513
|
|
|
491
514
|
const provider = this.resolveProvider({ provider: identity.provider });
|
|
492
515
|
const process = identity.process;
|
|
493
516
|
const filter = this.buildFilter(identity.key, process, provider);
|
|
494
|
-
const
|
|
517
|
+
const db = this.helper.getDatabaseByName(database, ref, type);
|
|
518
|
+
const collection = db.collection<StageRecord>(this.config.collection);
|
|
495
519
|
|
|
496
520
|
const update: UpdateFilter<StageRecord> = {
|
|
497
521
|
$set: {
|
|
@@ -526,7 +550,10 @@ class ProgressAPIImpl implements ProgressAPI {
|
|
|
526
550
|
}
|
|
527
551
|
|
|
528
552
|
async getCompleted(options?: ProgressSessionOptions & { process?: string; provider?: string }): Promise<Array<Pick<StageRecord, 'key' | 'name' | 'completedAt'>>> {
|
|
529
|
-
|
|
553
|
+
const database = options?.database;
|
|
554
|
+
const ref = options?.ref;
|
|
555
|
+
const type = options?.type;
|
|
556
|
+
await this.ensureProgressIndex(options?.session, database, ref, type);
|
|
530
557
|
this.helper.ensureInitialized();
|
|
531
558
|
|
|
532
559
|
const provider = this.resolveProvider(options);
|
|
@@ -539,7 +566,8 @@ class ProgressAPIImpl implements ProgressAPI {
|
|
|
539
566
|
filter.provider = provider;
|
|
540
567
|
}
|
|
541
568
|
|
|
542
|
-
const
|
|
569
|
+
const db = this.helper.getDatabaseByName(database, ref, type);
|
|
570
|
+
const collection = db.collection<StageRecord>(this.config.collection);
|
|
543
571
|
const findOptions: any = { projection: { key: 1, name: 1, completedAt: 1 } };
|
|
544
572
|
if (options?.session) {
|
|
545
573
|
findOptions.session = options.session;
|
|
@@ -554,7 +582,10 @@ class ProgressAPIImpl implements ProgressAPI {
|
|
|
554
582
|
}
|
|
555
583
|
|
|
556
584
|
async getProgress(options?: ProgressSessionOptions & { process?: string; provider?: string }): Promise<StageRecord[]> {
|
|
557
|
-
|
|
585
|
+
const database = options?.database;
|
|
586
|
+
const ref = options?.ref;
|
|
587
|
+
const type = options?.type;
|
|
588
|
+
await this.ensureProgressIndex(options?.session, database, ref, type);
|
|
558
589
|
this.helper.ensureInitialized();
|
|
559
590
|
|
|
560
591
|
const provider = this.resolveProvider(options);
|
|
@@ -567,7 +598,8 @@ class ProgressAPIImpl implements ProgressAPI {
|
|
|
567
598
|
filter.provider = provider;
|
|
568
599
|
}
|
|
569
600
|
|
|
570
|
-
const
|
|
601
|
+
const db = this.helper.getDatabaseByName(database, ref, type);
|
|
602
|
+
const collection = db.collection<StageRecord>(this.config.collection);
|
|
571
603
|
const findOptions: any = {};
|
|
572
604
|
if (options?.session) {
|
|
573
605
|
findOptions.session = options.session;
|
|
@@ -577,13 +609,17 @@ class ProgressAPIImpl implements ProgressAPI {
|
|
|
577
609
|
}
|
|
578
610
|
|
|
579
611
|
async reset(key: string, options?: ProgressSessionOptions & { process?: string; provider?: string }): Promise<void> {
|
|
580
|
-
|
|
612
|
+
const database = options?.database;
|
|
613
|
+
const ref = options?.ref;
|
|
614
|
+
const type = options?.type;
|
|
615
|
+
await this.ensureProgressIndex(options?.session, database, ref, type);
|
|
581
616
|
this.helper.ensureInitialized();
|
|
582
617
|
|
|
583
618
|
const provider = this.resolveProvider(options);
|
|
584
619
|
const process = options?.process;
|
|
585
620
|
const filter = this.buildFilter(key, process, provider);
|
|
586
|
-
const
|
|
621
|
+
const db = this.helper.getDatabaseByName(database, ref, type);
|
|
622
|
+
const collection = db.collection<StageRecord>(this.config.collection);
|
|
587
623
|
|
|
588
624
|
const update: UpdateFilter<StageRecord> = {
|
|
589
625
|
$set: {
|
|
@@ -616,7 +652,8 @@ export class SimpleMongoHelper {
|
|
|
616
652
|
private isDisconnecting: boolean = false;
|
|
617
653
|
|
|
618
654
|
constructor(connectionString: string, retryOptions?: RetryOptions, config?: HelperConfig) {
|
|
619
|
-
|
|
655
|
+
// Strip database name from connection string if present
|
|
656
|
+
this.connectionString = this.stripDatabaseFromConnectionString(connectionString);
|
|
620
657
|
this.retryOptions = {
|
|
621
658
|
maxRetries: retryOptions?.maxRetries ?? 3,
|
|
622
659
|
retryDelay: retryOptions?.retryDelay ?? 1000,
|
|
@@ -733,9 +770,8 @@ export class SimpleMongoHelper {
|
|
|
733
770
|
|
|
734
771
|
await testClient.connect();
|
|
735
772
|
|
|
736
|
-
// Try to ping the server
|
|
737
|
-
const
|
|
738
|
-
const testDb = testClient.db(dbName);
|
|
773
|
+
// Try to ping the server using 'admin' database (default)
|
|
774
|
+
const testDb = testClient.db('admin');
|
|
739
775
|
await testDb.admin().ping();
|
|
740
776
|
|
|
741
777
|
// Test basic operation (list collections)
|
|
@@ -864,9 +900,8 @@ export class SimpleMongoHelper {
|
|
|
864
900
|
try {
|
|
865
901
|
this.client = new MongoClient(this.connectionString);
|
|
866
902
|
await this.client.connect();
|
|
867
|
-
//
|
|
868
|
-
|
|
869
|
-
this.db = this.client.db(dbName);
|
|
903
|
+
// Default to 'admin' database for initial connection (not used for operations)
|
|
904
|
+
this.db = this.client.db('admin');
|
|
870
905
|
this.isInitialized = true;
|
|
871
906
|
return;
|
|
872
907
|
} catch (error) {
|
|
@@ -897,12 +932,16 @@ export class SimpleMongoHelper {
|
|
|
897
932
|
async loadCollection<T extends Document = Document>(
|
|
898
933
|
collectionName: string,
|
|
899
934
|
query?: Filter<T>,
|
|
900
|
-
options?: PaginationOptions
|
|
935
|
+
options?: PaginationOptions,
|
|
936
|
+
database?: string,
|
|
937
|
+
ref?: string,
|
|
938
|
+
type?: string
|
|
901
939
|
): Promise<WithId<T>[] | PaginatedResult<T>> {
|
|
902
940
|
this.ensureInitialized();
|
|
903
941
|
|
|
904
942
|
try {
|
|
905
|
-
const
|
|
943
|
+
const db = this.getDatabase(this.resolveDatabase({ database, ref, type }));
|
|
944
|
+
const collection: Collection<T> = db.collection<T>(collectionName);
|
|
906
945
|
const filter = query || {};
|
|
907
946
|
let cursor = collection.find(filter);
|
|
908
947
|
|
|
@@ -954,12 +993,16 @@ export class SimpleMongoHelper {
|
|
|
954
993
|
async findOne<T extends Document = Document>(
|
|
955
994
|
collectionName: string,
|
|
956
995
|
query: Filter<T>,
|
|
957
|
-
options?: { sort?: Sort; projection?: Document }
|
|
996
|
+
options?: { sort?: Sort; projection?: Document },
|
|
997
|
+
database?: string,
|
|
998
|
+
ref?: string,
|
|
999
|
+
type?: string
|
|
958
1000
|
): Promise<WithId<T> | null> {
|
|
959
1001
|
this.ensureInitialized();
|
|
960
1002
|
|
|
961
1003
|
try {
|
|
962
|
-
const
|
|
1004
|
+
const db = this.getDatabase(this.resolveDatabase({ database, ref, type }));
|
|
1005
|
+
const collection: Collection<T> = db.collection<T>(collectionName);
|
|
963
1006
|
let findOptions: any = {};
|
|
964
1007
|
|
|
965
1008
|
if (options?.sort) {
|
|
@@ -987,12 +1030,16 @@ export class SimpleMongoHelper {
|
|
|
987
1030
|
async insert<T extends Document = Document>(
|
|
988
1031
|
collectionName: string,
|
|
989
1032
|
data: OptionalUnlessRequiredId<T> | OptionalUnlessRequiredId<T>[],
|
|
990
|
-
options?: { session?: ClientSession }
|
|
1033
|
+
options?: { session?: ClientSession },
|
|
1034
|
+
database?: string,
|
|
1035
|
+
ref?: string,
|
|
1036
|
+
type?: string
|
|
991
1037
|
): Promise<any> {
|
|
992
1038
|
this.ensureInitialized();
|
|
993
1039
|
|
|
994
1040
|
try {
|
|
995
|
-
const
|
|
1041
|
+
const db = this.getDatabase(this.resolveDatabase({ database, ref, type }));
|
|
1042
|
+
const collection: Collection<T> = db.collection<T>(collectionName);
|
|
996
1043
|
const insertOptions = options?.session ? { session: options.session } : {};
|
|
997
1044
|
|
|
998
1045
|
if (Array.isArray(data)) {
|
|
@@ -1020,12 +1067,16 @@ export class SimpleMongoHelper {
|
|
|
1020
1067
|
collectionName: string,
|
|
1021
1068
|
filter: Filter<T>,
|
|
1022
1069
|
updateData: UpdateFilter<T>,
|
|
1023
|
-
options?: { upsert?: boolean; multi?: boolean; session?: ClientSession }
|
|
1070
|
+
options?: { upsert?: boolean; multi?: boolean; session?: ClientSession },
|
|
1071
|
+
database?: string,
|
|
1072
|
+
ref?: string,
|
|
1073
|
+
type?: string
|
|
1024
1074
|
): Promise<any> {
|
|
1025
1075
|
this.ensureInitialized();
|
|
1026
1076
|
|
|
1027
1077
|
try {
|
|
1028
|
-
const
|
|
1078
|
+
const db = this.getDatabase(this.resolveDatabase({ database, ref, type }));
|
|
1079
|
+
const collection: Collection<T> = db.collection<T>(collectionName);
|
|
1029
1080
|
const updateOptions: any = {};
|
|
1030
1081
|
if (options?.upsert !== undefined) updateOptions.upsert = options.upsert;
|
|
1031
1082
|
if (options?.session) updateOptions.session = options.session;
|
|
@@ -1053,12 +1104,16 @@ export class SimpleMongoHelper {
|
|
|
1053
1104
|
async delete<T extends Document = Document>(
|
|
1054
1105
|
collectionName: string,
|
|
1055
1106
|
filter: Filter<T>,
|
|
1056
|
-
options?: { multi?: boolean }
|
|
1107
|
+
options?: { multi?: boolean },
|
|
1108
|
+
database?: string,
|
|
1109
|
+
ref?: string,
|
|
1110
|
+
type?: string
|
|
1057
1111
|
): Promise<any> {
|
|
1058
1112
|
this.ensureInitialized();
|
|
1059
1113
|
|
|
1060
1114
|
try {
|
|
1061
|
-
const
|
|
1115
|
+
const db = this.getDatabase(this.resolveDatabase({ database, ref, type }));
|
|
1116
|
+
const collection: Collection<T> = db.collection<T>(collectionName);
|
|
1062
1117
|
|
|
1063
1118
|
if (options?.multi) {
|
|
1064
1119
|
const result = await collection.deleteMany(filter);
|
|
@@ -1081,12 +1136,16 @@ export class SimpleMongoHelper {
|
|
|
1081
1136
|
*/
|
|
1082
1137
|
async countDocuments<T extends Document = Document>(
|
|
1083
1138
|
collectionName: string,
|
|
1084
|
-
query?: Filter<T
|
|
1139
|
+
query?: Filter<T>,
|
|
1140
|
+
database?: string,
|
|
1141
|
+
ref?: string,
|
|
1142
|
+
type?: string
|
|
1085
1143
|
): Promise<number> {
|
|
1086
1144
|
this.ensureInitialized();
|
|
1087
1145
|
|
|
1088
1146
|
try {
|
|
1089
|
-
const
|
|
1147
|
+
const db = this.getDatabase(this.resolveDatabase({ database, ref, type }));
|
|
1148
|
+
const collection: Collection<T> = db.collection<T>(collectionName);
|
|
1090
1149
|
const count = await collection.countDocuments(query || {});
|
|
1091
1150
|
return count;
|
|
1092
1151
|
} catch (error) {
|
|
@@ -1100,11 +1159,12 @@ export class SimpleMongoHelper {
|
|
|
1100
1159
|
* @returns Estimated number of documents
|
|
1101
1160
|
* @throws Error if not initialized or if count fails
|
|
1102
1161
|
*/
|
|
1103
|
-
async estimatedDocumentCount(collectionName: string): Promise<number> {
|
|
1162
|
+
async estimatedDocumentCount(collectionName: string, database?: string, ref?: string, type?: string): Promise<number> {
|
|
1104
1163
|
this.ensureInitialized();
|
|
1105
1164
|
|
|
1106
1165
|
try {
|
|
1107
|
-
const
|
|
1166
|
+
const db = this.getDatabase(this.resolveDatabase({ database, ref, type }));
|
|
1167
|
+
const collection = db.collection(collectionName);
|
|
1108
1168
|
const count = await collection.estimatedDocumentCount();
|
|
1109
1169
|
return count;
|
|
1110
1170
|
} catch (error) {
|
|
@@ -1121,12 +1181,16 @@ export class SimpleMongoHelper {
|
|
|
1121
1181
|
*/
|
|
1122
1182
|
async aggregate<T extends Document = Document>(
|
|
1123
1183
|
collectionName: string,
|
|
1124
|
-
pipeline: Document[]
|
|
1184
|
+
pipeline: Document[],
|
|
1185
|
+
database?: string,
|
|
1186
|
+
ref?: string,
|
|
1187
|
+
type?: string
|
|
1125
1188
|
): Promise<T[]> {
|
|
1126
1189
|
this.ensureInitialized();
|
|
1127
1190
|
|
|
1128
1191
|
try {
|
|
1129
|
-
const
|
|
1192
|
+
const db = this.getDatabase(this.resolveDatabase({ database, ref, type }));
|
|
1193
|
+
const collection: Collection<T> = db.collection<T>(collectionName);
|
|
1130
1194
|
const results = await collection.aggregate<T>(pipeline).toArray();
|
|
1131
1195
|
return results;
|
|
1132
1196
|
} catch (error) {
|
|
@@ -1145,12 +1209,16 @@ export class SimpleMongoHelper {
|
|
|
1145
1209
|
async createIndex(
|
|
1146
1210
|
collectionName: string,
|
|
1147
1211
|
indexSpec: IndexSpecification,
|
|
1148
|
-
options?: CreateIndexesOptions
|
|
1212
|
+
options?: CreateIndexesOptions,
|
|
1213
|
+
database?: string,
|
|
1214
|
+
ref?: string,
|
|
1215
|
+
type?: string
|
|
1149
1216
|
): Promise<string> {
|
|
1150
1217
|
this.ensureInitialized();
|
|
1151
1218
|
|
|
1152
1219
|
try {
|
|
1153
|
-
const
|
|
1220
|
+
const db = this.getDatabase(this.resolveDatabase({ database, ref, type }));
|
|
1221
|
+
const collection = db.collection(collectionName);
|
|
1154
1222
|
const indexName = await collection.createIndex(indexSpec, options);
|
|
1155
1223
|
return indexName;
|
|
1156
1224
|
} catch (error) {
|
|
@@ -1165,11 +1233,12 @@ export class SimpleMongoHelper {
|
|
|
1165
1233
|
* @returns Result object
|
|
1166
1234
|
* @throws Error if not initialized or if index drop fails
|
|
1167
1235
|
*/
|
|
1168
|
-
async dropIndex(collectionName: string, indexName: string): Promise<any> {
|
|
1236
|
+
async dropIndex(collectionName: string, indexName: string, database?: string, ref?: string, type?: string): Promise<any> {
|
|
1169
1237
|
this.ensureInitialized();
|
|
1170
1238
|
|
|
1171
1239
|
try {
|
|
1172
|
-
const
|
|
1240
|
+
const db = this.getDatabase(this.resolveDatabase({ database, ref, type }));
|
|
1241
|
+
const collection = db.collection(collectionName);
|
|
1173
1242
|
const result = await collection.dropIndex(indexName);
|
|
1174
1243
|
return result;
|
|
1175
1244
|
} catch (error) {
|
|
@@ -1183,11 +1252,12 @@ export class SimpleMongoHelper {
|
|
|
1183
1252
|
* @returns Array of index information
|
|
1184
1253
|
* @throws Error if not initialized or if listing fails
|
|
1185
1254
|
*/
|
|
1186
|
-
async listIndexes(collectionName: string): Promise<Document[]> {
|
|
1255
|
+
async listIndexes(collectionName: string, database?: string, ref?: string, type?: string): Promise<Document[]> {
|
|
1187
1256
|
this.ensureInitialized();
|
|
1188
1257
|
|
|
1189
1258
|
try {
|
|
1190
|
-
const
|
|
1259
|
+
const db = this.getDatabase(this.resolveDatabase({ database, ref, type }));
|
|
1260
|
+
const collection = db.collection(collectionName);
|
|
1191
1261
|
const indexes = await collection.indexes();
|
|
1192
1262
|
return indexes;
|
|
1193
1263
|
} catch (error) {
|
|
@@ -1249,7 +1319,7 @@ export class SimpleMongoHelper {
|
|
|
1249
1319
|
*/
|
|
1250
1320
|
async loadByRef<T extends Document = Document>(
|
|
1251
1321
|
ref: string,
|
|
1252
|
-
options?: PaginationOptions & { session?: ClientSession }
|
|
1322
|
+
options?: PaginationOptions & { session?: ClientSession; database?: string; ref?: string; type?: string }
|
|
1253
1323
|
): Promise<WithId<T>[] | PaginatedResult<T>> {
|
|
1254
1324
|
this.ensureInitialized();
|
|
1255
1325
|
|
|
@@ -1262,9 +1332,13 @@ export class SimpleMongoHelper {
|
|
|
1262
1332
|
throw new Error(`Ref '${ref}' not found in configuration inputs.`);
|
|
1263
1333
|
}
|
|
1264
1334
|
|
|
1335
|
+
const database = options?.database;
|
|
1336
|
+
const dbRef = options?.ref;
|
|
1337
|
+
const dbType = options?.type;
|
|
1338
|
+
const db = this.getDatabase(this.resolveDatabase({ database, ref: dbRef, type: dbType }));
|
|
1265
1339
|
// Note: loadCollection doesn't support session yet, but we'll pass it through options
|
|
1266
1340
|
// For now, we'll use the collection directly with session support
|
|
1267
|
-
const collection: Collection<T> =
|
|
1341
|
+
const collection: Collection<T> = db.collection<T>(inputConfig.collection);
|
|
1268
1342
|
const filter = inputConfig.query || {};
|
|
1269
1343
|
const session = options?.session;
|
|
1270
1344
|
|
|
@@ -1328,15 +1402,22 @@ export class SimpleMongoHelper {
|
|
|
1328
1402
|
options?: {
|
|
1329
1403
|
fieldName?: string;
|
|
1330
1404
|
unique?: boolean;
|
|
1405
|
+
database?: string;
|
|
1406
|
+
ref?: string;
|
|
1407
|
+
type?: string;
|
|
1331
1408
|
}
|
|
1332
1409
|
): Promise<EnsureSignatureIndexResult> {
|
|
1333
1410
|
this.ensureInitialized();
|
|
1334
1411
|
|
|
1335
1412
|
const fieldName = options?.fieldName || '_sig';
|
|
1336
1413
|
const unique = options?.unique !== false; // Default to true
|
|
1414
|
+
const database = options?.database;
|
|
1415
|
+
const ref = options?.ref;
|
|
1416
|
+
const type = options?.type;
|
|
1337
1417
|
|
|
1338
1418
|
try {
|
|
1339
|
-
const
|
|
1419
|
+
const db = this.getDatabase(this.resolveDatabase({ database, ref, type }));
|
|
1420
|
+
const collection = db.collection(collectionName);
|
|
1340
1421
|
const indexes = await collection.indexes();
|
|
1341
1422
|
|
|
1342
1423
|
// Find existing index on the signature field
|
|
@@ -1402,6 +1483,9 @@ export class SimpleMongoHelper {
|
|
|
1402
1483
|
options?: {
|
|
1403
1484
|
session?: ClientSession;
|
|
1404
1485
|
ensureIndex?: boolean;
|
|
1486
|
+
database?: string;
|
|
1487
|
+
ref?: string;
|
|
1488
|
+
type?: string;
|
|
1405
1489
|
}
|
|
1406
1490
|
): Promise<WriteByRefResult> {
|
|
1407
1491
|
this.ensureInitialized();
|
|
@@ -1420,6 +1504,9 @@ export class SimpleMongoHelper {
|
|
|
1420
1504
|
const mode = outputConfig.mode || this.config.output?.mode || 'append';
|
|
1421
1505
|
const ensureIndex = options?.ensureIndex !== false; // Default to true
|
|
1422
1506
|
const session = options?.session;
|
|
1507
|
+
const database = options?.database;
|
|
1508
|
+
const dbRef = options?.ref;
|
|
1509
|
+
const dbType = options?.type;
|
|
1423
1510
|
|
|
1424
1511
|
const result: WriteByRefResult = {
|
|
1425
1512
|
inserted: 0,
|
|
@@ -1429,7 +1516,8 @@ export class SimpleMongoHelper {
|
|
|
1429
1516
|
};
|
|
1430
1517
|
|
|
1431
1518
|
try {
|
|
1432
|
-
const
|
|
1519
|
+
const db = this.getDatabase(this.resolveDatabase({ database, ref: dbRef, type: dbType }));
|
|
1520
|
+
const collection = db.collection(collectionName);
|
|
1433
1521
|
|
|
1434
1522
|
// Handle replace mode: clear collection first
|
|
1435
1523
|
if (mode === 'replace') {
|
|
@@ -1595,6 +1683,9 @@ export class SimpleMongoHelper {
|
|
|
1595
1683
|
const writeResult = await this.writeByRef(ref, documents, {
|
|
1596
1684
|
session: options?.session,
|
|
1597
1685
|
ensureIndex: options?.ensureIndex,
|
|
1686
|
+
database: options?.database,
|
|
1687
|
+
ref: options?.ref,
|
|
1688
|
+
type: options?.type,
|
|
1598
1689
|
});
|
|
1599
1690
|
|
|
1600
1691
|
const result: WriteStageResult = {
|
|
@@ -1613,7 +1704,7 @@ export class SimpleMongoHelper {
|
|
|
1613
1704
|
provider: options.complete.provider,
|
|
1614
1705
|
metadata: options.complete.metadata,
|
|
1615
1706
|
},
|
|
1616
|
-
{ session: options.session }
|
|
1707
|
+
{ session: options.session, database: options?.database, ref: options?.ref, type: options?.type }
|
|
1617
1708
|
);
|
|
1618
1709
|
result.completed = true;
|
|
1619
1710
|
} catch (error) {
|
|
@@ -1651,6 +1742,9 @@ export class SimpleMongoHelper {
|
|
|
1651
1742
|
onUnmatched1 = 'include',
|
|
1652
1743
|
onUnmatched2 = 'include',
|
|
1653
1744
|
session,
|
|
1745
|
+
database,
|
|
1746
|
+
ref,
|
|
1747
|
+
type,
|
|
1654
1748
|
} = options;
|
|
1655
1749
|
|
|
1656
1750
|
// Determine join behavior from joinType or fallback to onUnmatched flags
|
|
@@ -1694,9 +1788,10 @@ export class SimpleMongoHelper {
|
|
|
1694
1788
|
};
|
|
1695
1789
|
|
|
1696
1790
|
try {
|
|
1697
|
-
const
|
|
1698
|
-
const
|
|
1699
|
-
const
|
|
1791
|
+
const db = this.getDatabase(this.resolveDatabase({ database, ref, type }));
|
|
1792
|
+
const coll1 = db.collection(sourceCollection1);
|
|
1793
|
+
const coll2 = db.collection(sourceCollection2);
|
|
1794
|
+
const targetColl = db.collection(targetCollection);
|
|
1700
1795
|
|
|
1701
1796
|
const findOptions: any = {};
|
|
1702
1797
|
if (session) {
|
|
@@ -2116,20 +2211,118 @@ export class SimpleMongoHelper {
|
|
|
2116
2211
|
}
|
|
2117
2212
|
|
|
2118
2213
|
/**
|
|
2119
|
-
*
|
|
2120
|
-
* @param connectionString - MongoDB connection string
|
|
2121
|
-
* @returns
|
|
2214
|
+
* Strips database name from MongoDB connection string, returning base connection string.
|
|
2215
|
+
* @param connectionString - MongoDB connection string (may include database name)
|
|
2216
|
+
* @returns Base connection string without database name
|
|
2217
|
+
* @example
|
|
2218
|
+
* stripDatabaseFromConnectionString('mongodb://localhost:27017/admin')
|
|
2219
|
+
* // Returns: 'mongodb://localhost:27017/'
|
|
2122
2220
|
*/
|
|
2123
|
-
private
|
|
2221
|
+
private stripDatabaseFromConnectionString(connectionString: string): string {
|
|
2124
2222
|
try {
|
|
2125
2223
|
const url = new URL(connectionString);
|
|
2126
|
-
|
|
2127
|
-
|
|
2224
|
+
// Remove pathname (database name) but keep trailing slash
|
|
2225
|
+
url.pathname = '/';
|
|
2226
|
+
return url.toString();
|
|
2128
2227
|
} catch {
|
|
2129
|
-
// If URL parsing fails, try
|
|
2130
|
-
|
|
2131
|
-
|
|
2228
|
+
// If URL parsing fails, try regex pattern matching
|
|
2229
|
+
// Match: mongodb://host:port/database?options or mongodb://host:port/database
|
|
2230
|
+
const match = connectionString.match(/^([^\/]+\/\/[^\/]+)\/([^\/\?]+)(\?.*)?$/);
|
|
2231
|
+
if (match) {
|
|
2232
|
+
// Return base URL with trailing slash
|
|
2233
|
+
return match[1] + '/';
|
|
2234
|
+
}
|
|
2235
|
+
// If no database found, return as-is (should already be base URL)
|
|
2236
|
+
return connectionString.endsWith('/') ? connectionString : connectionString + '/';
|
|
2237
|
+
}
|
|
2238
|
+
}
|
|
2239
|
+
|
|
2240
|
+
/**
|
|
2241
|
+
* Resolves the database name from provided options using the databases config map.
|
|
2242
|
+
* Priority: database > ref+type > ref > type
|
|
2243
|
+
* @param options - Options containing database, ref, and/or type
|
|
2244
|
+
* @returns Resolved database name or undefined (will default to 'admin')
|
|
2245
|
+
* @throws Error if no match found or multiple matches found
|
|
2246
|
+
* @internal
|
|
2247
|
+
*/
|
|
2248
|
+
private resolveDatabase(options?: { database?: string; ref?: string; type?: string }): string | undefined {
|
|
2249
|
+
// Priority 1: If database is provided directly, use it
|
|
2250
|
+
if (options?.database) {
|
|
2251
|
+
return options.database;
|
|
2252
|
+
}
|
|
2253
|
+
|
|
2254
|
+
// If no config or no databases map, return undefined (will default to 'admin')
|
|
2255
|
+
if (!this.config || !this.config.databases || this.config.databases.length === 0) {
|
|
2256
|
+
return undefined;
|
|
2132
2257
|
}
|
|
2258
|
+
|
|
2259
|
+
const databases = this.config.databases;
|
|
2260
|
+
|
|
2261
|
+
// Priority 2: If both ref and type are provided, find exact match
|
|
2262
|
+
if (options?.ref && options?.type) {
|
|
2263
|
+
const matches = databases.filter(db => db.ref === options.ref && db.type === options.type);
|
|
2264
|
+
if (matches.length === 0) {
|
|
2265
|
+
throw new Error(`No database found for ref: ${options.ref} and type: ${options.type}`);
|
|
2266
|
+
}
|
|
2267
|
+
if (matches.length > 1) {
|
|
2268
|
+
throw new Error(`Multiple databases found for ref: ${options.ref} and type: ${options.type}`);
|
|
2269
|
+
}
|
|
2270
|
+
return matches[0].database;
|
|
2271
|
+
}
|
|
2272
|
+
|
|
2273
|
+
// Priority 3: If ref is provided, find by ref
|
|
2274
|
+
if (options?.ref) {
|
|
2275
|
+
const matches = databases.filter(db => db.ref === options.ref);
|
|
2276
|
+
if (matches.length === 0) {
|
|
2277
|
+
throw new Error(`No database found for ref: ${options.ref}`);
|
|
2278
|
+
}
|
|
2279
|
+
if (matches.length > 1) {
|
|
2280
|
+
throw new Error(`Multiple databases found for ref: ${options.ref}. Use 'type' parameter to narrow down.`);
|
|
2281
|
+
}
|
|
2282
|
+
return matches[0].database;
|
|
2283
|
+
}
|
|
2284
|
+
|
|
2285
|
+
// Priority 4: If type is provided, find by type
|
|
2286
|
+
if (options?.type) {
|
|
2287
|
+
const matches = databases.filter(db => db.type === options.type);
|
|
2288
|
+
if (matches.length === 0) {
|
|
2289
|
+
throw new Error(`No database found for type: ${options.type}`);
|
|
2290
|
+
}
|
|
2291
|
+
if (matches.length > 1) {
|
|
2292
|
+
throw new Error(`Multiple databases found for type: ${options.type}. Use 'ref' parameter to narrow down.`);
|
|
2293
|
+
}
|
|
2294
|
+
return matches[0].database;
|
|
2295
|
+
}
|
|
2296
|
+
|
|
2297
|
+
// No options provided, return undefined (will default to 'admin')
|
|
2298
|
+
return undefined;
|
|
2299
|
+
}
|
|
2300
|
+
|
|
2301
|
+
/**
|
|
2302
|
+
* Gets a database instance by name. Defaults to 'admin' if no name provided.
|
|
2303
|
+
* @param databaseName - Optional database name (defaults to 'admin')
|
|
2304
|
+
* @returns Database instance
|
|
2305
|
+
* @throws Error if client is not initialized
|
|
2306
|
+
* @internal
|
|
2307
|
+
*/
|
|
2308
|
+
private getDatabase(databaseName?: string): Db {
|
|
2309
|
+
if (!this.client) {
|
|
2310
|
+
throw new Error('MongoDB client not initialized. Call initialize() first.');
|
|
2311
|
+
}
|
|
2312
|
+
return this.client.db(databaseName || 'admin');
|
|
2313
|
+
}
|
|
2314
|
+
|
|
2315
|
+
/**
|
|
2316
|
+
* Gets a database instance by name. Defaults to 'admin' if no name provided.
|
|
2317
|
+
* Public method for use by ProgressAPIImpl.
|
|
2318
|
+
* @param databaseName - Optional database name (defaults to 'admin')
|
|
2319
|
+
* @param ref - Optional ref for database resolution
|
|
2320
|
+
* @param type - Optional type for database resolution
|
|
2321
|
+
* @returns Database instance
|
|
2322
|
+
* @throws Error if client is not initialized
|
|
2323
|
+
*/
|
|
2324
|
+
public getDatabaseByName(databaseName?: string, ref?: string, type?: string): Db {
|
|
2325
|
+
return this.getDatabase(this.resolveDatabase({ database: databaseName, ref, type }));
|
|
2133
2326
|
}
|
|
2134
2327
|
}
|
|
2135
2328
|
|