@twin.org/entity-storage-connector-mongodb 0.0.3-next.12 → 0.0.3-next.14
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/es/mongoDbEntityStorageConnector.js +141 -43
- package/dist/es/mongoDbEntityStorageConnector.js.map +1 -1
- package/dist/types/mongoDbEntityStorageConnector.d.ts +32 -3
- package/docs/changelog.md +32 -0
- package/docs/reference/classes/MongoDbEntityStorageConnector.md +162 -16
- package/locales/en.json +3 -1
- package/package.json +2 -2
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
import { ContextIdHelper, ContextIdStore } from "@twin.org/context";
|
|
4
4
|
import { BaseError, ComponentFactory, GeneralError, Guards, HealthStatus, Is, ObjectHelper } from "@twin.org/core";
|
|
5
5
|
import { ComparisonOperator, EntitySchemaFactory, EntitySchemaHelper, EntitySchemaPropertyType, LogicalOperator } from "@twin.org/entity";
|
|
6
|
+
import { EntityHelper } from "@twin.org/entity-storage-models";
|
|
6
7
|
import { MongoClient } from "mongodb";
|
|
7
8
|
/**
|
|
8
9
|
* Class for performing entity storage operations using MongoDb.
|
|
@@ -22,6 +23,11 @@ export class MongoDbEntityStorageConnector {
|
|
|
22
23
|
* @internal
|
|
23
24
|
*/
|
|
24
25
|
static _PARTITION_KEY = "partitionId";
|
|
26
|
+
/**
|
|
27
|
+
* The name for the schema.
|
|
28
|
+
* @internal
|
|
29
|
+
*/
|
|
30
|
+
_entitySchemaName;
|
|
25
31
|
/**
|
|
26
32
|
* The schema for the entity.
|
|
27
33
|
* @internal
|
|
@@ -53,6 +59,7 @@ export class MongoDbEntityStorageConnector {
|
|
|
53
59
|
Guards.stringValue(MongoDbEntityStorageConnector.CLASS_NAME, "options.config.host", options.config.host);
|
|
54
60
|
Guards.stringValue(MongoDbEntityStorageConnector.CLASS_NAME, "options.config.database", options.config.database);
|
|
55
61
|
Guards.stringValue(MongoDbEntityStorageConnector.CLASS_NAME, "options.config.collection", options.config.collection);
|
|
62
|
+
this._entitySchemaName = options.entitySchema;
|
|
56
63
|
this._entitySchema = EntitySchemaFactory.get(options.entitySchema);
|
|
57
64
|
this._partitionContextIds = options.partitionContextIds;
|
|
58
65
|
this._config = options.config;
|
|
@@ -193,8 +200,11 @@ export class MongoDbEntityStorageConnector {
|
|
|
193
200
|
const collection = await this.getCollection();
|
|
194
201
|
const result = await collection.findOne(query);
|
|
195
202
|
ObjectHelper.propertyDelete(result, "_id");
|
|
196
|
-
|
|
197
|
-
|
|
203
|
+
return Is.objectValue(result)
|
|
204
|
+
? EntityHelper.unPrepareEntity(result, [
|
|
205
|
+
MongoDbEntityStorageConnector._PARTITION_KEY
|
|
206
|
+
])
|
|
207
|
+
: undefined;
|
|
198
208
|
}
|
|
199
209
|
catch (err) {
|
|
200
210
|
throw new GeneralError(MongoDbEntityStorageConnector.CLASS_NAME, "getFailed", {
|
|
@@ -212,15 +222,15 @@ export class MongoDbEntityStorageConnector {
|
|
|
212
222
|
Guards.object(MongoDbEntityStorageConnector.CLASS_NAME, "entity", entity);
|
|
213
223
|
const contextIds = await ContextIdStore.getContextIds();
|
|
214
224
|
const partitionKey = ContextIdHelper.combinedContextKey(contextIds, this._partitionContextIds);
|
|
215
|
-
|
|
225
|
+
const prepared = EntityHelper.prepareEntity(entity, this._entitySchema, Is.stringValue(partitionKey)
|
|
226
|
+
? [{ property: MongoDbEntityStorageConnector._PARTITION_KEY, value: partitionKey }]
|
|
227
|
+
: undefined);
|
|
216
228
|
const primaryKey = EntitySchemaHelper.getPrimaryKey(this.getSchema());
|
|
217
|
-
const id =
|
|
229
|
+
const id = prepared[primaryKey.property];
|
|
218
230
|
try {
|
|
219
231
|
const filter = { [primaryKey.property]: id };
|
|
220
|
-
const finalEntity = ObjectHelper.clone(entity);
|
|
221
232
|
if (Is.stringValue(partitionKey)) {
|
|
222
233
|
filter[MongoDbEntityStorageConnector._PARTITION_KEY] = partitionKey;
|
|
223
|
-
ObjectHelper.propertySet(finalEntity, MongoDbEntityStorageConnector._PARTITION_KEY, partitionKey);
|
|
224
234
|
}
|
|
225
235
|
if (Is.arrayValue(conditions)) {
|
|
226
236
|
for (const condition of conditions) {
|
|
@@ -228,7 +238,7 @@ export class MongoDbEntityStorageConnector {
|
|
|
228
238
|
}
|
|
229
239
|
}
|
|
230
240
|
const collection = await this.getCollection();
|
|
231
|
-
await collection.findOneAndUpdate(filter, { $set:
|
|
241
|
+
await collection.findOneAndUpdate(filter, { $set: prepared }, { upsert: true });
|
|
232
242
|
}
|
|
233
243
|
catch (err) {
|
|
234
244
|
throw new GeneralError(MongoDbEntityStorageConnector.CLASS_NAME, "setFailed", {
|
|
@@ -246,25 +256,23 @@ export class MongoDbEntityStorageConnector {
|
|
|
246
256
|
const contextIds = await ContextIdStore.getContextIds();
|
|
247
257
|
const partitionKey = ContextIdHelper.combinedContextKey(contextIds, this._partitionContextIds);
|
|
248
258
|
const primaryKey = EntitySchemaHelper.getPrimaryKey(this.getSchema());
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
259
|
+
const preparedEntities = entities.map(entity => EntityHelper.prepareEntity(entity, this._entitySchema, Is.stringValue(partitionKey)
|
|
260
|
+
? [{ property: MongoDbEntityStorageConnector._PARTITION_KEY, value: partitionKey }]
|
|
261
|
+
: undefined));
|
|
252
262
|
try {
|
|
253
263
|
const collection = await this.getCollection();
|
|
254
|
-
await collection.bulkWrite(
|
|
255
|
-
const finalEntity = ObjectHelper.clone(entity);
|
|
264
|
+
await collection.bulkWrite(preparedEntities.map(prepared => {
|
|
256
265
|
const filter = {
|
|
257
|
-
[primaryKey.property]:
|
|
266
|
+
[primaryKey.property]: prepared[primaryKey.property]
|
|
258
267
|
};
|
|
259
268
|
if (Is.stringValue(partitionKey)) {
|
|
260
269
|
filter[MongoDbEntityStorageConnector._PARTITION_KEY] = partitionKey;
|
|
261
|
-
ObjectHelper.propertySet(finalEntity, MongoDbEntityStorageConnector._PARTITION_KEY, partitionKey);
|
|
262
270
|
}
|
|
263
271
|
return {
|
|
264
272
|
updateOne: {
|
|
265
273
|
filter,
|
|
266
274
|
update: {
|
|
267
|
-
$set:
|
|
275
|
+
$set: prepared
|
|
268
276
|
},
|
|
269
277
|
upsert: true
|
|
270
278
|
}
|
|
@@ -397,24 +405,7 @@ export class MongoDbEntityStorageConnector {
|
|
|
397
405
|
const contextIds = await ContextIdStore.getContextIds();
|
|
398
406
|
const partitionKey = ContextIdHelper.combinedContextKey(contextIds, this._partitionContextIds);
|
|
399
407
|
const returnSize = limit ?? MongoDbEntityStorageConnector._DEFAULT_LIMIT;
|
|
400
|
-
const
|
|
401
|
-
conditions: [],
|
|
402
|
-
logicalOperator: LogicalOperator.And
|
|
403
|
-
};
|
|
404
|
-
if (Is.stringValue(partitionKey)) {
|
|
405
|
-
finalConditions.conditions.push({
|
|
406
|
-
property: MongoDbEntityStorageConnector._PARTITION_KEY,
|
|
407
|
-
comparison: ComparisonOperator.Equals,
|
|
408
|
-
value: partitionKey
|
|
409
|
-
});
|
|
410
|
-
}
|
|
411
|
-
if (!Is.empty(conditions)) {
|
|
412
|
-
finalConditions.conditions.push(conditions);
|
|
413
|
-
}
|
|
414
|
-
const filter = {};
|
|
415
|
-
if (finalConditions.conditions.length > 0) {
|
|
416
|
-
this.buildQueryParameters("", finalConditions, filter);
|
|
417
|
-
}
|
|
408
|
+
const filter = this.buildFilter(conditions, partitionKey);
|
|
418
409
|
const sort = new Map();
|
|
419
410
|
if (Array.isArray(sortProperties)) {
|
|
420
411
|
for (const sortProperty of sortProperties) {
|
|
@@ -435,30 +426,33 @@ export class MongoDbEntityStorageConnector {
|
|
|
435
426
|
?.find(filter, { projection })
|
|
436
427
|
.sort(sort)
|
|
437
428
|
.skip(cursorValue)
|
|
438
|
-
.limit(returnSize)
|
|
429
|
+
.limit(returnSize + 1)
|
|
439
430
|
.toArray();
|
|
440
|
-
const
|
|
441
|
-
|
|
431
|
+
const rawResults = entitiesResult ?? [];
|
|
432
|
+
const hasMore = rawResults.length > returnSize;
|
|
433
|
+
const entities = hasMore ? rawResults.slice(0, returnSize) : rawResults;
|
|
434
|
+
for (let i = 0; i < entities.length; i++) {
|
|
435
|
+
const entity = entities[i];
|
|
442
436
|
ObjectHelper.propertyDelete(entity, "_id");
|
|
443
|
-
|
|
437
|
+
entities[i] = EntityHelper.unPrepareEntity(entity, [
|
|
438
|
+
MongoDbEntityStorageConnector._PARTITION_KEY
|
|
439
|
+
]);
|
|
444
440
|
}
|
|
445
441
|
return {
|
|
446
442
|
entities,
|
|
447
|
-
cursor:
|
|
443
|
+
cursor: hasMore ? String(cursorValue + returnSize) : undefined
|
|
448
444
|
};
|
|
449
445
|
}
|
|
450
446
|
/**
|
|
451
447
|
* Count all the entities which match the conditions.
|
|
448
|
+
* @param conditions The optional conditions to match for the entities.
|
|
452
449
|
* @returns The total count of entities in the storage.
|
|
453
450
|
*/
|
|
454
|
-
async count() {
|
|
451
|
+
async count(conditions) {
|
|
455
452
|
try {
|
|
456
453
|
const contextIds = await ContextIdStore.getContextIds();
|
|
457
454
|
const partitionKey = ContextIdHelper.combinedContextKey(contextIds, this._partitionContextIds);
|
|
458
|
-
const filter =
|
|
459
|
-
if (Is.stringValue(partitionKey)) {
|
|
460
|
-
filter[MongoDbEntityStorageConnector._PARTITION_KEY] = partitionKey;
|
|
461
|
-
}
|
|
455
|
+
const filter = this.buildFilter(conditions, partitionKey);
|
|
462
456
|
return await this._client
|
|
463
457
|
.db(this._config.database)
|
|
464
458
|
.collection(this._config.collection)
|
|
@@ -468,6 +462,82 @@ export class MongoDbEntityStorageConnector {
|
|
|
468
462
|
throw new GeneralError(MongoDbEntityStorageConnector.CLASS_NAME, "countFailed", undefined, err);
|
|
469
463
|
}
|
|
470
464
|
}
|
|
465
|
+
/**
|
|
466
|
+
* Get all unique partition context ids present in the collection.
|
|
467
|
+
* @returns An array of context id objects, one per unique partition.
|
|
468
|
+
*/
|
|
469
|
+
async getPartitionContextIds() {
|
|
470
|
+
if (!Is.arrayValue(this._partitionContextIds)) {
|
|
471
|
+
return [];
|
|
472
|
+
}
|
|
473
|
+
try {
|
|
474
|
+
const collection = await this.getCollection();
|
|
475
|
+
const partitionIds = await collection.distinct(MongoDbEntityStorageConnector._PARTITION_KEY);
|
|
476
|
+
return partitionIds
|
|
477
|
+
.filter((id) => Is.stringValue(id))
|
|
478
|
+
.map(partitionId => ContextIdHelper.shortSplit(this._partitionContextIds ?? [], partitionId));
|
|
479
|
+
}
|
|
480
|
+
catch (err) {
|
|
481
|
+
throw new GeneralError(MongoDbEntityStorageConnector.CLASS_NAME, "getPartitionContextIdsFailed", undefined, err);
|
|
482
|
+
}
|
|
483
|
+
}
|
|
484
|
+
/**
|
|
485
|
+
* Create the target connector for performing the migration using a temporary collection.
|
|
486
|
+
* @param newEntitySchema The name of the new entity schema to create the connector for.
|
|
487
|
+
* @returns Connector for performing the migration.
|
|
488
|
+
*/
|
|
489
|
+
async createTargetConnector(newEntitySchema) {
|
|
490
|
+
const migrationCollectionName = `${this._config.collection}Migration${Date.now()}`;
|
|
491
|
+
return new MongoDbEntityStorageConnector({
|
|
492
|
+
entitySchema: newEntitySchema,
|
|
493
|
+
config: {
|
|
494
|
+
...this._config,
|
|
495
|
+
collection: migrationCollectionName
|
|
496
|
+
},
|
|
497
|
+
partitionContextIds: this._partitionContextIds
|
|
498
|
+
});
|
|
499
|
+
}
|
|
500
|
+
/**
|
|
501
|
+
* Finalize the migration by dropping the source collection and renaming the migration collection to the original name.
|
|
502
|
+
* @param targetConnector The connector holding the migrated data in a temporary collection.
|
|
503
|
+
* @param options The options to control how the migration is finalized.
|
|
504
|
+
* @param loggingComponentType The logging component type to use during finalization.
|
|
505
|
+
* @returns The final connector using the original collection name with the new schema.
|
|
506
|
+
*/
|
|
507
|
+
async finalizeMigration(targetConnector, options, loggingComponentType) {
|
|
508
|
+
// Only rename if the migration collection was actually created (it won't exist if no
|
|
509
|
+
// entities were written, since MongoDB creates collections lazily on first write).
|
|
510
|
+
const migrationCollection = await targetConnector.getCollection();
|
|
511
|
+
const collections = await targetConnector._client
|
|
512
|
+
.db(targetConnector._config.database)
|
|
513
|
+
.listCollections({ name: targetConnector._config.collection })
|
|
514
|
+
.toArray();
|
|
515
|
+
if (collections.length > 0) {
|
|
516
|
+
// Teardown the existing table with the original name to free up the name for the new table
|
|
517
|
+
await this.teardown(loggingComponentType);
|
|
518
|
+
await migrationCollection.rename(this._config.collection);
|
|
519
|
+
}
|
|
520
|
+
const finalConnector = new MongoDbEntityStorageConnector({
|
|
521
|
+
entitySchema: targetConnector._entitySchemaName,
|
|
522
|
+
config: this._config,
|
|
523
|
+
partitionContextIds: this._partitionContextIds
|
|
524
|
+
});
|
|
525
|
+
if (await finalConnector.bootstrap(loggingComponentType)) {
|
|
526
|
+
await targetConnector.stop?.();
|
|
527
|
+
return finalConnector;
|
|
528
|
+
}
|
|
529
|
+
throw new GeneralError(MongoDbEntityStorageConnector.CLASS_NAME, "finalizeMigrationFailedBootstrap", undefined);
|
|
530
|
+
}
|
|
531
|
+
/**
|
|
532
|
+
* Cleanup a failed or aborted migration by dropping the temporary migration collection.
|
|
533
|
+
* @param targetConnector The target connector to cleanup.
|
|
534
|
+
* @param options The options to control how the migration is cleaned up.
|
|
535
|
+
* @param loggingComponentType The optional component type to use for logging.
|
|
536
|
+
* @returns A promise that resolves when the cleanup is complete.
|
|
537
|
+
*/
|
|
538
|
+
async cleanupMigration(targetConnector, options, loggingComponentType) {
|
|
539
|
+
await targetConnector?.teardown?.(loggingComponentType);
|
|
540
|
+
}
|
|
471
541
|
/**
|
|
472
542
|
* Create a new DB connection configuration.
|
|
473
543
|
* @returns The MongoDb connection configuration.
|
|
@@ -490,6 +560,34 @@ export class MongoDbEntityStorageConnector {
|
|
|
490
560
|
const { database, collection } = this._config;
|
|
491
561
|
return this._client.db(database).collection(collection);
|
|
492
562
|
}
|
|
563
|
+
/**
|
|
564
|
+
* Build a MongoDB filter combining partition key and optional conditions.
|
|
565
|
+
* @param conditions The optional entity conditions to include.
|
|
566
|
+
* @param partitionKey The partition key value.
|
|
567
|
+
* @returns The MongoDB filter object.
|
|
568
|
+
* @internal
|
|
569
|
+
*/
|
|
570
|
+
buildFilter(conditions, partitionKey) {
|
|
571
|
+
const finalConditions = {
|
|
572
|
+
conditions: [],
|
|
573
|
+
logicalOperator: LogicalOperator.And
|
|
574
|
+
};
|
|
575
|
+
if (Is.stringValue(partitionKey)) {
|
|
576
|
+
finalConditions.conditions.push({
|
|
577
|
+
property: MongoDbEntityStorageConnector._PARTITION_KEY,
|
|
578
|
+
comparison: ComparisonOperator.Equals,
|
|
579
|
+
value: partitionKey
|
|
580
|
+
});
|
|
581
|
+
}
|
|
582
|
+
if (!Is.empty(conditions)) {
|
|
583
|
+
finalConditions.conditions.push(conditions);
|
|
584
|
+
}
|
|
585
|
+
const filter = {};
|
|
586
|
+
if (finalConditions.conditions.length > 0) {
|
|
587
|
+
this.buildQueryParameters("", finalConditions, filter);
|
|
588
|
+
}
|
|
589
|
+
return filter;
|
|
590
|
+
}
|
|
493
591
|
/**
|
|
494
592
|
* Create an MongoDB filter query.
|
|
495
593
|
* @param objectPath The path for the nested object.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mongoDbEntityStorageConnector.js","sourceRoot":"","sources":["../../src/mongoDbEntityStorageConnector.ts"],"names":[],"mappings":"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","sourcesContent":["// Copyright 2024 IOTA Stiftung.\n// SPDX-License-Identifier: Apache-2.0.\nimport { ContextIdHelper, ContextIdStore } from \"@twin.org/context\";\nimport {\n\tBaseError,\n\tComponentFactory,\n\tGeneralError,\n\tGuards,\n\tHealthStatus,\n\ttype IHealth,\n\tIs,\n\tObjectHelper\n} from \"@twin.org/core\";\nimport {\n\tComparisonOperator,\n\ttype EntityCondition,\n\tEntitySchemaFactory,\n\tEntitySchemaHelper,\n\tEntitySchemaPropertyType,\n\ttype IEntitySchema,\n\tLogicalOperator,\n\ttype SortDirection\n} from \"@twin.org/entity\";\nimport type { IEntityStorageConnector } from \"@twin.org/entity-storage-models\";\nimport type { ILoggingComponent } from \"@twin.org/logging-models\";\nimport { nameof } from \"@twin.org/nameof\";\nimport { type Collection, type Document, type Filter, MongoClient, type WithId } from \"mongodb\";\nimport type { IMongoDbEntityStorageConnectorConfig } from \"./models/IMongoDbEntityStorageConnectorConfig.js\";\nimport type { IMongoDbEntityStorageConnectorConstructorOptions } from \"./models/IMongoDbEntityStorageConnectorConstructorOptions.js\";\n\n/**\n * Class for performing entity storage operations using MongoDb.\n */\nexport class MongoDbEntityStorageConnector<T = unknown> implements IEntityStorageConnector<T> {\n\t/**\n\t * Runtime name for the class.\n\t */\n\tpublic static readonly CLASS_NAME: string = nameof<MongoDbEntityStorageConnector>();\n\n\t/**\n\t * Limit the number of entities when finding.\n\t * @internal\n\t */\n\tprivate static readonly _DEFAULT_LIMIT: number = 40;\n\n\t/**\n\t * Partition id field name.\n\t * @internal\n\t */\n\tprivate static readonly _PARTITION_KEY: string = \"partitionId\";\n\n\t/**\n\t * The schema for the entity.\n\t * @internal\n\t */\n\tprivate readonly _entitySchema: IEntitySchema<T>;\n\n\t/**\n\t * The keys to use from the context ids to create partitions.\n\t * @internal\n\t */\n\tprivate readonly _partitionContextIds?: string[];\n\n\t/**\n\t * The configuration for the connector.\n\t * @internal\n\t */\n\tprivate readonly _config: IMongoDbEntityStorageConnectorConfig;\n\n\t/**\n\t * The MongoDb client.\n\t * @internal\n\t */\n\tprivate readonly _client: MongoClient;\n\n\t/**\n\t * Create a new instance of MongoDbEntityStorageConnector.\n\t * @param options The options for the connector.\n\t */\n\tconstructor(options: IMongoDbEntityStorageConnectorConstructorOptions) {\n\t\tGuards.object(MongoDbEntityStorageConnector.CLASS_NAME, nameof(options), options);\n\t\tGuards.stringValue(\n\t\t\tMongoDbEntityStorageConnector.CLASS_NAME,\n\t\t\tnameof(options.entitySchema),\n\t\t\toptions.entitySchema\n\t\t);\n\t\tGuards.object<IMongoDbEntityStorageConnectorConfig>(\n\t\t\tMongoDbEntityStorageConnector.CLASS_NAME,\n\t\t\tnameof(options.config),\n\t\t\toptions.config\n\t\t);\n\t\tGuards.stringValue(\n\t\t\tMongoDbEntityStorageConnector.CLASS_NAME,\n\t\t\tnameof(options.config.host),\n\t\t\toptions.config.host\n\t\t);\n\t\tGuards.stringValue(\n\t\t\tMongoDbEntityStorageConnector.CLASS_NAME,\n\t\t\tnameof(options.config.database),\n\t\t\toptions.config.database\n\t\t);\n\t\tGuards.stringValue(\n\t\t\tMongoDbEntityStorageConnector.CLASS_NAME,\n\t\t\tnameof(options.config.collection),\n\t\t\toptions.config.collection\n\t\t);\n\n\t\tthis._entitySchema = EntitySchemaFactory.get(options.entitySchema);\n\t\tthis._partitionContextIds = options.partitionContextIds;\n\n\t\tthis._config = options.config;\n\n\t\tthis._client = new MongoClient(this.createConnectionConfig());\n\t}\n\n\t/**\n\t * Initialize the MongoDb environment.\n\t * @param nodeLoggingComponentType Optional type of the logging component.\n\t * @returns A promise that resolves to a boolean indicating success.\n\t */\n\tpublic async bootstrap(nodeLoggingComponentType?: string): Promise<boolean> {\n\t\tconst nodeLogging = ComponentFactory.getIfExists<ILoggingComponent>(nodeLoggingComponentType);\n\n\t\ttry {\n\t\t\tawait this._client.connect();\n\n\t\t\tawait nodeLogging?.log({\n\t\t\t\tlevel: \"info\",\n\t\t\t\tsource: MongoDbEntityStorageConnector.CLASS_NAME,\n\t\t\t\tts: Date.now(),\n\t\t\t\tmessage: \"databaseCreating\",\n\t\t\t\tdata: {\n\t\t\t\t\tdatabaseName: this._config.database\n\t\t\t\t}\n\t\t\t});\n\n\t\t\t// Create the database if it does not exist\n\t\t\tthis._client.db(this._config.database);\n\n\t\t\tawait nodeLogging?.log({\n\t\t\t\tlevel: \"info\",\n\t\t\t\tsource: MongoDbEntityStorageConnector.CLASS_NAME,\n\t\t\t\tts: Date.now(),\n\t\t\t\tmessage: \"databaseExists\",\n\t\t\t\tdata: {\n\t\t\t\t\tdatabaseName: this._config.database\n\t\t\t\t}\n\t\t\t});\n\n\t\t\tawait this.getCollection();\n\n\t\t\tawait nodeLogging?.log({\n\t\t\t\tlevel: \"info\",\n\t\t\t\tsource: MongoDbEntityStorageConnector.CLASS_NAME,\n\t\t\t\tts: Date.now(),\n\t\t\t\tmessage: \"collectionExists\",\n\t\t\t\tdata: {\n\t\t\t\t\tcollectionName: this._config.collection\n\t\t\t\t}\n\t\t\t});\n\t\t} catch (error) {\n\t\t\tawait nodeLogging?.log({\n\t\t\t\tlevel: \"error\",\n\t\t\t\tsource: MongoDbEntityStorageConnector.CLASS_NAME,\n\t\t\t\tts: Date.now(),\n\t\t\t\tmessage: \"databaseCreateFailed\",\n\t\t\t\terror: BaseError.fromError(error),\n\t\t\t\tdata: {\n\t\t\t\t\tdatabaseName: this._config.database\n\t\t\t\t}\n\t\t\t});\n\t\t\treturn false;\n\t\t}\n\n\t\treturn true;\n\t}\n\n\t/**\n\t * The component needs to be stopped when the node is closed.\n\t * @param nodeLoggingComponentType The node logging component type.\n\t * @returns Nothing.\n\t */\n\tpublic async stop?(nodeLoggingComponentType?: string): Promise<void> {\n\t\tawait this._client.close();\n\t}\n\n\t/**\n\t * Returns the class name of the component.\n\t * @returns The class name of the component.\n\t */\n\tpublic className(): string {\n\t\treturn MongoDbEntityStorageConnector.CLASS_NAME;\n\t}\n\n\t/**\n\t * Returns the health status of the component.\n\t * @returns The health status of the component.\n\t */\n\tpublic async health(): Promise<IHealth[]> {\n\t\ttry {\n\t\t\tawait this._client\n\t\t\t\t.db(this._config.database)\n\t\t\t\t.collection(this._config.collection)\n\t\t\t\t.estimatedDocumentCount();\n\t\t\treturn [\n\t\t\t\t{\n\t\t\t\t\tsource: MongoDbEntityStorageConnector.CLASS_NAME,\n\t\t\t\t\tstatus: HealthStatus.Ok,\n\t\t\t\t\tdescription: \"healthDescription\",\n\t\t\t\t\tdata: { database: this._config.database, collection: this._config.collection }\n\t\t\t\t}\n\t\t\t];\n\t\t} catch {\n\t\t\treturn [\n\t\t\t\t{\n\t\t\t\t\tsource: MongoDbEntityStorageConnector.CLASS_NAME,\n\t\t\t\t\tstatus: HealthStatus.Error,\n\t\t\t\t\tdescription: \"healthDescription\",\n\t\t\t\t\tmessage: \"connectionFailed\",\n\t\t\t\t\tdata: { database: this._config.database, collection: this._config.collection }\n\t\t\t\t}\n\t\t\t];\n\t\t}\n\t}\n\n\t/**\n\t * Get the schema for the entities.\n\t * @returns The schema for the entities.\n\t */\n\tpublic getSchema(): IEntitySchema {\n\t\treturn this._entitySchema as IEntitySchema;\n\t}\n\n\t/**\n\t * Get an entity from MongoDb.\n\t * @param id The id of the entity to get, or the index value if secondaryIndex is set.\n\t * @param secondaryIndex Get the item using a secondary index.\n\t * @param conditions The optional conditions to match for the entities.\n\t * @returns The object if it can be found or undefined.\n\t */\n\tpublic async get(\n\t\tid: string,\n\t\tsecondaryIndex?: keyof T,\n\t\tconditions?: { property: keyof T; value: unknown }[]\n\t): Promise<T | undefined> {\n\t\tGuards.stringValue(MongoDbEntityStorageConnector.CLASS_NAME, nameof(id), id);\n\n\t\tconst contextIds = await ContextIdStore.getContextIds();\n\t\tconst partitionKey = ContextIdHelper.combinedContextKey(contextIds, this._partitionContextIds);\n\n\t\ttry {\n\t\t\tconst primaryKey = EntitySchemaHelper.getPrimaryKey(this.getSchema());\n\t\t\tconst query: { [key: string]: unknown } = Is.empty(secondaryIndex)\n\t\t\t\t? { [primaryKey.property]: id }\n\t\t\t\t: { [secondaryIndex]: id };\n\n\t\t\tif (Is.stringValue(partitionKey)) {\n\t\t\t\tquery[MongoDbEntityStorageConnector._PARTITION_KEY] = partitionKey;\n\t\t\t}\n\n\t\t\tif (conditions) {\n\t\t\t\tfor (const condition of conditions) {\n\t\t\t\t\tquery[condition.property as string] = condition.value;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tconst collection = await this.getCollection();\n\t\t\tconst result = await collection.findOne(query);\n\t\t\tObjectHelper.propertyDelete(result, \"_id\");\n\t\t\tObjectHelper.propertyDelete(result, MongoDbEntityStorageConnector._PARTITION_KEY);\n\t\t\treturn result as T | undefined;\n\t\t} catch (err) {\n\t\t\tthrow new GeneralError(\n\t\t\t\tMongoDbEntityStorageConnector.CLASS_NAME,\n\t\t\t\t\"getFailed\",\n\t\t\t\t{\n\t\t\t\t\tid\n\t\t\t\t},\n\t\t\t\terr\n\t\t\t);\n\t\t}\n\t}\n\n\t/**\n\t * Set an entity.\n\t * @param entity The entity to set.\n\t * @param conditions The optional conditions to match for the entities.\n\t * @returns The id of the entity.\n\t */\n\tpublic async set(entity: T, conditions?: { property: keyof T; value: unknown }[]): Promise<void> {\n\t\tGuards.object<T>(MongoDbEntityStorageConnector.CLASS_NAME, nameof(entity), entity);\n\n\t\tconst contextIds = await ContextIdStore.getContextIds();\n\t\tconst partitionKey = ContextIdHelper.combinedContextKey(contextIds, this._partitionContextIds);\n\n\t\tEntitySchemaHelper.validateEntity(entity, this.getSchema());\n\n\t\tconst primaryKey = EntitySchemaHelper.getPrimaryKey(this.getSchema());\n\t\tconst id = entity[primaryKey.property];\n\n\t\ttry {\n\t\t\tconst filter: { [key in keyof T]?: unknown } = { [primaryKey.property]: id };\n\t\t\tconst finalEntity = ObjectHelper.clone(entity);\n\n\t\t\tif (Is.stringValue(partitionKey)) {\n\t\t\t\tfilter[MongoDbEntityStorageConnector._PARTITION_KEY as keyof T] = partitionKey;\n\t\t\t\tObjectHelper.propertySet(\n\t\t\t\t\tfinalEntity,\n\t\t\t\t\tMongoDbEntityStorageConnector._PARTITION_KEY,\n\t\t\t\t\tpartitionKey\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tif (Is.arrayValue(conditions)) {\n\t\t\t\tfor (const condition of conditions) {\n\t\t\t\t\tfilter[condition.property] = condition.value;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tconst collection = await this.getCollection();\n\t\t\tawait collection.findOneAndUpdate(\n\t\t\t\tfilter,\n\t\t\t\t{ $set: ObjectHelper.removeEmptyProperties(finalEntity) as Partial<Document> },\n\t\t\t\t{ upsert: true }\n\t\t\t);\n\t\t} catch (err) {\n\t\t\tthrow new GeneralError(\n\t\t\t\tMongoDbEntityStorageConnector.CLASS_NAME,\n\t\t\t\t\"setFailed\",\n\t\t\t\t{\n\t\t\t\t\tid\n\t\t\t\t},\n\t\t\t\terr\n\t\t\t);\n\t\t}\n\t}\n\n\t/**\n\t * Set multiple entities in a batch.\n\t * @param entities The entities to set.\n\t * @returns Nothing.\n\t */\n\tpublic async setBatch(entities: T[]): Promise<void> {\n\t\tGuards.arrayValue(MongoDbEntityStorageConnector.CLASS_NAME, nameof(entities), entities);\n\n\t\tconst contextIds = await ContextIdStore.getContextIds();\n\t\tconst partitionKey = ContextIdHelper.combinedContextKey(contextIds, this._partitionContextIds);\n\t\tconst primaryKey = EntitySchemaHelper.getPrimaryKey(this.getSchema());\n\n\t\tfor (const entity of entities) {\n\t\t\tEntitySchemaHelper.validateEntity(entity, this.getSchema());\n\t\t}\n\n\t\ttry {\n\t\t\tconst collection = await this.getCollection();\n\t\t\tawait collection.bulkWrite(\n\t\t\t\tentities.map(entity => {\n\t\t\t\t\tconst finalEntity = ObjectHelper.clone(entity);\n\t\t\t\t\tconst filter: { [key: string]: unknown } = {\n\t\t\t\t\t\t[primaryKey.property]: entity[primaryKey.property]\n\t\t\t\t\t};\n\t\t\t\t\tif (Is.stringValue(partitionKey)) {\n\t\t\t\t\t\tfilter[MongoDbEntityStorageConnector._PARTITION_KEY] = partitionKey;\n\t\t\t\t\t\tObjectHelper.propertySet(\n\t\t\t\t\t\t\tfinalEntity,\n\t\t\t\t\t\t\tMongoDbEntityStorageConnector._PARTITION_KEY,\n\t\t\t\t\t\t\tpartitionKey\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\t\t\t\t\treturn {\n\t\t\t\t\t\tupdateOne: {\n\t\t\t\t\t\t\tfilter,\n\t\t\t\t\t\t\tupdate: {\n\t\t\t\t\t\t\t\t$set: ObjectHelper.removeEmptyProperties(finalEntity) as Partial<Document>\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tupsert: true\n\t\t\t\t\t\t}\n\t\t\t\t\t};\n\t\t\t\t})\n\t\t\t);\n\t\t} catch (err) {\n\t\t\tthrow new GeneralError(\n\t\t\t\tMongoDbEntityStorageConnector.CLASS_NAME,\n\t\t\t\t\"setBatchFailed\",\n\t\t\t\tundefined,\n\t\t\t\terr\n\t\t\t);\n\t\t}\n\t}\n\n\t/**\n\t * Empty the entity storage.\n\t * @returns Nothing.\n\t */\n\tpublic async empty(): Promise<void> {\n\t\ttry {\n\t\t\tconst contextIds = await ContextIdStore.getContextIds();\n\t\t\tconst partitionKey = ContextIdHelper.combinedContextKey(\n\t\t\t\tcontextIds,\n\t\t\t\tthis._partitionContextIds\n\t\t\t);\n\n\t\t\tconst filter: { [key: string]: unknown } = {};\n\t\t\tif (Is.stringValue(partitionKey)) {\n\t\t\t\tfilter[MongoDbEntityStorageConnector._PARTITION_KEY] = partitionKey;\n\t\t\t}\n\n\t\t\tconst collection = await this.getCollection();\n\t\t\tawait collection.deleteMany(filter);\n\t\t} catch (err) {\n\t\t\tthrow new GeneralError(\n\t\t\t\tMongoDbEntityStorageConnector.CLASS_NAME,\n\t\t\t\t\"emptyFailed\",\n\t\t\t\tundefined,\n\t\t\t\terr\n\t\t\t);\n\t\t}\n\t}\n\n\t/**\n\t * Remove the entity.\n\t * @param id The id of the entity to remove.\n\t * @param conditions The optional conditions to match for the entities.\n\t * @returns Nothing.\n\t */\n\tpublic async remove(\n\t\tid: string,\n\t\tconditions?: { property: keyof T; value: unknown }[]\n\t): Promise<void> {\n\t\tGuards.stringValue(MongoDbEntityStorageConnector.CLASS_NAME, nameof(id), id);\n\n\t\tconst contextIds = await ContextIdStore.getContextIds();\n\t\tconst partitionKey = ContextIdHelper.combinedContextKey(contextIds, this._partitionContextIds);\n\n\t\ttry {\n\t\t\tconst primaryKey = EntitySchemaHelper.getPrimaryKey(this.getSchema());\n\t\t\tconst query: { [key in keyof T]?: unknown } = { [primaryKey.property]: id };\n\n\t\t\tif (Is.stringValue(partitionKey)) {\n\t\t\t\tquery[MongoDbEntityStorageConnector._PARTITION_KEY as keyof T] = partitionKey;\n\t\t\t}\n\n\t\t\tif (conditions) {\n\t\t\t\tfor (const condition of conditions) {\n\t\t\t\t\tquery[condition.property] = condition.value;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tconst collection = await this.getCollection();\n\t\t\tawait collection.deleteOne(query);\n\t\t} catch (err) {\n\t\t\tthrow new GeneralError(MongoDbEntityStorageConnector.CLASS_NAME, \"removeFailed\", { id }, err);\n\t\t}\n\t}\n\n\t/**\n\t * Remove multiple entities by id.\n\t * @param ids The ids of the entities to remove.\n\t * @returns Nothing.\n\t */\n\tpublic async removeBatch(ids: string[]): Promise<void> {\n\t\tGuards.arrayValue(MongoDbEntityStorageConnector.CLASS_NAME, nameof(ids), ids);\n\n\t\tconst contextIds = await ContextIdStore.getContextIds();\n\t\tconst partitionKey = ContextIdHelper.combinedContextKey(contextIds, this._partitionContextIds);\n\n\t\ttry {\n\t\t\tconst primaryKey = EntitySchemaHelper.getPrimaryKey(this.getSchema());\n\t\t\tconst filter: { [key: string]: unknown } = {\n\t\t\t\t[primaryKey.property]: { $in: ids }\n\t\t\t};\n\n\t\t\tif (Is.stringValue(partitionKey)) {\n\t\t\t\tfilter[MongoDbEntityStorageConnector._PARTITION_KEY] = partitionKey;\n\t\t\t}\n\n\t\t\tconst collection = await this.getCollection();\n\t\t\tawait collection.deleteMany(filter);\n\t\t} catch (err) {\n\t\t\tthrow new GeneralError(\n\t\t\t\tMongoDbEntityStorageConnector.CLASS_NAME,\n\t\t\t\t\"removeBatchFailed\",\n\t\t\t\tundefined,\n\t\t\t\terr\n\t\t\t);\n\t\t}\n\t}\n\n\t/**\n\t * Teardown the entity storage by dropping the collection.\n\t * @param nodeLoggingComponentType The node logging component type.\n\t * @returns True if the teardown process was successful.\n\t */\n\tpublic async teardown(nodeLoggingComponentType?: string): Promise<boolean> {\n\t\tconst nodeLogging = ComponentFactory.getIfExists<ILoggingComponent>(nodeLoggingComponentType);\n\n\t\tawait nodeLogging?.log({\n\t\t\tlevel: \"info\",\n\t\t\tsource: MongoDbEntityStorageConnector.CLASS_NAME,\n\t\t\tts: Date.now(),\n\t\t\tmessage: \"collectionDropping\",\n\t\t\tdata: { collection: this._config.collection }\n\t\t});\n\n\t\ttry {\n\t\t\tconst collection = await this.getCollection();\n\t\t\tawait collection.drop();\n\n\t\t\tawait nodeLogging?.log({\n\t\t\t\tlevel: \"info\",\n\t\t\t\tsource: MongoDbEntityStorageConnector.CLASS_NAME,\n\t\t\t\tts: Date.now(),\n\t\t\t\tmessage: \"collectionDropped\",\n\t\t\t\tdata: { collection: this._config.collection }\n\t\t\t});\n\n\t\t\treturn true;\n\t\t} catch (err) {\n\t\t\tawait nodeLogging?.log({\n\t\t\t\tlevel: \"error\",\n\t\t\t\tsource: MongoDbEntityStorageConnector.CLASS_NAME,\n\t\t\t\tts: Date.now(),\n\t\t\t\tmessage: \"teardownFailed\",\n\t\t\t\terror: BaseError.fromError(err)\n\t\t\t});\n\t\t\treturn false;\n\t\t}\n\t}\n\n\t/**\n\t * Find all the entities which match the conditions.\n\t * @param conditions The conditions to match for the entities.\n\t * @param sortProperties The optional sort order.\n\t * @param properties The optional properties to return, defaults to all.\n\t * @param cursor The cursor to request the next chunk of entities.\n\t * @param limit The suggested number of entities to return in each chunk, in some scenarios can return a different amount.\n\t * @returns All the entities for the storage matching the conditions,\n\t * and a cursor which can be used to request more entities.\n\t */\n\tpublic async query(\n\t\tconditions?: EntityCondition<T>,\n\t\tsortProperties?: { property: keyof T; sortDirection: SortDirection }[],\n\t\tproperties?: (keyof T)[],\n\t\tcursor?: string,\n\t\tlimit?: number\n\t): Promise<{ entities: Partial<T>[]; cursor?: string }> {\n\t\tconst contextIds = await ContextIdStore.getContextIds();\n\t\tconst partitionKey = ContextIdHelper.combinedContextKey(contextIds, this._partitionContextIds);\n\n\t\tconst returnSize = limit ?? MongoDbEntityStorageConnector._DEFAULT_LIMIT;\n\n\t\tconst finalConditions: EntityCondition<T> = {\n\t\t\tconditions: [],\n\t\t\tlogicalOperator: LogicalOperator.And\n\t\t};\n\n\t\tif (Is.stringValue(partitionKey)) {\n\t\t\tfinalConditions.conditions.push({\n\t\t\t\tproperty: MongoDbEntityStorageConnector._PARTITION_KEY,\n\t\t\t\tcomparison: ComparisonOperator.Equals,\n\t\t\t\tvalue: partitionKey\n\t\t\t});\n\t\t}\n\n\t\tif (!Is.empty(conditions)) {\n\t\t\tfinalConditions.conditions.push(conditions);\n\t\t}\n\n\t\tconst filter: Filter<T> = {};\n\t\tif (finalConditions.conditions.length > 0) {\n\t\t\tthis.buildQueryParameters(\"\", finalConditions, filter);\n\t\t}\n\n\t\tconst sort = new Map<string, SortDirection>();\n\t\tif (Array.isArray(sortProperties)) {\n\t\t\tfor (const sortProperty of sortProperties) {\n\t\t\t\tsort.set(sortProperty.property as string, sortProperty.sortDirection);\n\t\t\t}\n\t\t}\n\n\t\tconst projection: { [key: string]: number } = {};\n\t\tif (properties) {\n\t\t\tfor (const property of properties) {\n\t\t\t\tprojection[property as string] = 1;\n\t\t\t}\n\t\t}\n\n\t\tconst cursorValue = cursor ? Number(cursor) : 0;\n\n\t\tconst collection = await this.getCollection();\n\t\tconst entitiesResult = await collection\n\t\t\t// False positive, this is not an array find call\n\t\t\t// eslint-disable-next-line unicorn/no-array-callback-reference\n\t\t\t?.find(filter as Filter<Document>, { projection })\n\t\t\t.sort(sort)\n\t\t\t.skip(cursorValue)\n\t\t\t.limit(returnSize)\n\t\t\t.toArray();\n\n\t\tconst entities = (entitiesResult as unknown as Partial<T>[]) ?? [];\n\n\t\tfor (const entity of entities) {\n\t\t\tObjectHelper.propertyDelete(entity, \"_id\");\n\t\t\tObjectHelper.propertyDelete(entity, MongoDbEntityStorageConnector._PARTITION_KEY);\n\t\t}\n\n\t\treturn {\n\t\t\tentities,\n\t\t\tcursor: entities?.length === returnSize ? String(cursorValue + returnSize) : undefined\n\t\t};\n\t}\n\n\t/**\n\t * Count all the entities which match the conditions.\n\t * @returns The total count of entities in the storage.\n\t */\n\tpublic async count(): Promise<number> {\n\t\ttry {\n\t\t\tconst contextIds = await ContextIdStore.getContextIds();\n\t\t\tconst partitionKey = ContextIdHelper.combinedContextKey(\n\t\t\t\tcontextIds,\n\t\t\t\tthis._partitionContextIds\n\t\t\t);\n\n\t\t\tconst filter: { [key: string]: unknown } = {};\n\t\t\tif (Is.stringValue(partitionKey)) {\n\t\t\t\tfilter[MongoDbEntityStorageConnector._PARTITION_KEY] = partitionKey;\n\t\t\t}\n\n\t\t\treturn await this._client\n\t\t\t\t.db(this._config.database)\n\t\t\t\t.collection(this._config.collection)\n\t\t\t\t.countDocuments(filter);\n\t\t} catch (err) {\n\t\t\tthrow new GeneralError(\n\t\t\t\tMongoDbEntityStorageConnector.CLASS_NAME,\n\t\t\t\t\"countFailed\",\n\t\t\t\tundefined,\n\t\t\t\terr\n\t\t\t);\n\t\t}\n\t}\n\n\t/**\n\t * Create a new DB connection configuration.\n\t * @returns The MongoDb connection configuration.\n\t * @internal\n\t */\n\tprivate createConnectionConfig(): string {\n\t\tconst { host, port, user, password, database } = this._config;\n\t\tconst portPart = port ? `:${port}` : \"\";\n\t\tif (user && password) {\n\t\t\treturn `mongodb://${user}:${password}@${host}${portPart}/${database}`;\n\t\t}\n\t\treturn `mongodb://${host}${portPart}/${database}`;\n\t}\n\n\t/**\n\t * Return a Mongo DB collection.\n\t * @returns The MongoDb collection.\n\t * @internal\n\t */\n\tprivate async getCollection(): Promise<Collection> {\n\t\tconst { database, collection } = this._config;\n\t\treturn this._client.db(database).collection(collection);\n\t}\n\n\t/**\n\t * Create an MongoDB filter query.\n\t * @param objectPath The path for the nested object.\n\t * @param condition The conditions to create the query from.\n\t * @param filter The filter query to use.\n\t * @internal\n\t */\n\tprivate buildQueryParameters(\n\t\tobjectPath: string,\n\t\tcondition: EntityCondition<T>,\n\t\tfilter: Filter<T>\n\t): void {\n\t\tif (!condition) {\n\t\t\treturn;\n\t\t}\n\n\t\tif (\"conditions\" in condition) {\n\t\t\tconst subConditions: Filter<T>[] = condition.conditions.map(c => {\n\t\t\t\tconst subFilter: Filter<T> = {};\n\t\t\t\tthis.buildQueryParameters(objectPath, c, subFilter);\n\t\t\t\treturn subFilter;\n\t\t\t});\n\n\t\t\tif (condition.logicalOperator === LogicalOperator.And) {\n\t\t\t\tfilter.$and = subConditions as Filter<WithId<T>>[];\n\t\t\t} else if (condition.logicalOperator === LogicalOperator.Or) {\n\t\t\t\tfilter.$or = subConditions as Filter<WithId<T>>[];\n\t\t\t} else {\n\t\t\t\tObject.assign(filter, subConditions[0]);\n\t\t\t}\n\t\t} else {\n\t\t\tconst propertyPath = String(condition.property);\n\t\t\tconst prop = objectPath ? `${objectPath}.${propertyPath}` : propertyPath;\n\t\t\tconst propertyParts = propertyPath.split(\".\");\n\t\t\tconst schemaLookupName = propertyParts.length > 1 ? propertyParts[0] : propertyPath;\n\t\t\tconst propertySchema = this._entitySchema.properties?.find(\n\t\t\t\tp => p.property === schemaLookupName\n\t\t\t);\n\t\t\t// For dot-notation paths the leaf field is always a string value; using the root\n\t\t\t// type directly would send Includes into $elemMatch which does not work for nested\n\t\t\t// string fields. Keeping String here causes mapComparisonOperator to emit $regex,\n\t\t\t// which MongoDB handles correctly for both nested object and array traversal.\n\t\t\tconst propertyType =\n\t\t\t\tpropertyParts.length > 1 ? EntitySchemaPropertyType.String : propertySchema?.type;\n\t\t\tconst comparison = this.mapComparisonOperator(\n\t\t\t\tcondition.comparison,\n\t\t\t\tcondition.value,\n\t\t\t\tpropertyType\n\t\t\t);\n\n\t\t\t(filter as { [key: string]: unknown })[prop] = comparison;\n\t\t}\n\t}\n\n\t/**\n\t * Map the framework comparison operators to those in MongoDB.\n\t * @param comparison The comparison operator.\n\t * @param value The value to compare.\n\t * @param type The type of the property from the schema.\n\t * @returns The MongoDB comparison expression.\n\t * @internal\n\t */\n\tprivate mapComparisonOperator(\n\t\tcomparison: ComparisonOperator,\n\t\tvalue: unknown,\n\t\ttype?: EntitySchemaPropertyType\n\t): unknown {\n\t\tswitch (comparison) {\n\t\t\tcase ComparisonOperator.Equals:\n\t\t\t\treturn value;\n\t\t\tcase ComparisonOperator.NotEquals:\n\t\t\t\treturn { $ne: value };\n\t\t\tcase ComparisonOperator.GreaterThan:\n\t\t\t\treturn { $gt: value };\n\t\t\tcase ComparisonOperator.LessThan:\n\t\t\t\treturn { $lt: value };\n\t\t\tcase ComparisonOperator.GreaterThanOrEqual:\n\t\t\t\treturn { $gte: value };\n\t\t\tcase ComparisonOperator.LessThanOrEqual:\n\t\t\t\treturn { $lte: value };\n\t\t\tcase ComparisonOperator.In:\n\t\t\t\treturn { $in: Array.isArray(value) ? value : [value] };\n\t\t\tcase ComparisonOperator.Includes:\n\t\t\t\t// For string fields, use regex for substring matching\n\t\t\t\tif (type === EntitySchemaPropertyType.String) {\n\t\t\t\t\t// Escape special regex characters in the value\n\t\t\t\t\tconst escapedValue = String(value).replace(/[$()*+.?[\\\\\\]^{|}]/g, \"\\\\$&\");\n\t\t\t\t\treturn { $regex: escapedValue };\n\t\t\t\t}\n\t\t\t\t// For array and object fields, use $elemMatch\n\t\t\t\tif (type === EntitySchemaPropertyType.Array || type === EntitySchemaPropertyType.Object) {\n\t\t\t\t\treturn { $elemMatch: { $eq: value } };\n\t\t\t\t}\n\t\t\t\t// Fallback to $elemMatch for backwards compatibility\n\t\t\t\treturn { $elemMatch: { $eq: value } };\n\t\t\tcase ComparisonOperator.NotIncludes:\n\t\t\t\t// For string fields, use negated regex\n\t\t\t\tif (type === EntitySchemaPropertyType.String) {\n\t\t\t\t\tconst escapedValue = String(value).replace(/[$()*+.?[\\\\\\]^{|}]/g, \"\\\\$&\");\n\t\t\t\t\treturn { $not: { $regex: escapedValue } };\n\t\t\t\t}\n\t\t\t\t// For arrays, use $elemMatch with $ne\n\t\t\t\treturn { $elemMatch: { $ne: value } };\n\t\t\tdefault:\n\t\t\t\tthrow new GeneralError(\n\t\t\t\t\tMongoDbEntityStorageConnector.CLASS_NAME,\n\t\t\t\t\t\"unsupportedComparisonOperator\",\n\t\t\t\t\t{ comparison }\n\t\t\t\t);\n\t\t}\n\t}\n}\n"]}
|
|
1
|
+
{"version":3,"file":"mongoDbEntityStorageConnector.js","sourceRoot":"","sources":["../../src/mongoDbEntityStorageConnector.ts"],"names":[],"mappings":"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","sourcesContent":["// Copyright 2024 IOTA Stiftung.\n// SPDX-License-Identifier: Apache-2.0.\nimport { ContextIdHelper, ContextIdStore, type IContextIds } from \"@twin.org/context\";\nimport {\n\tBaseError,\n\tComponentFactory,\n\tGeneralError,\n\tGuards,\n\tHealthStatus,\n\ttype IHealth,\n\tIs,\n\tObjectHelper\n} from \"@twin.org/core\";\nimport {\n\tComparisonOperator,\n\ttype EntityCondition,\n\tEntitySchemaFactory,\n\tEntitySchemaHelper,\n\tEntitySchemaPropertyType,\n\ttype IEntitySchema,\n\tLogicalOperator,\n\ttype SortDirection\n} from \"@twin.org/entity\";\nimport {\n\tEntityHelper,\n\ttype IEntityStorageConnector,\n\ttype IEntityStorageMigrationConnector,\n\ttype IMigrationOptions\n} from \"@twin.org/entity-storage-models\";\nimport type { ILoggingComponent } from \"@twin.org/logging-models\";\nimport { nameof } from \"@twin.org/nameof\";\nimport { type Collection, type Document, type Filter, MongoClient, type WithId } from \"mongodb\";\nimport type { IMongoDbEntityStorageConnectorConfig } from \"./models/IMongoDbEntityStorageConnectorConfig.js\";\nimport type { IMongoDbEntityStorageConnectorConstructorOptions } from \"./models/IMongoDbEntityStorageConnectorConstructorOptions.js\";\n\n/**\n * Class for performing entity storage operations using MongoDb.\n */\nexport class MongoDbEntityStorageConnector<\n\tT = unknown\n> implements IEntityStorageMigrationConnector<T> {\n\t/**\n\t * Runtime name for the class.\n\t */\n\tpublic static readonly CLASS_NAME: string = nameof<MongoDbEntityStorageConnector>();\n\n\t/**\n\t * Limit the number of entities when finding.\n\t * @internal\n\t */\n\tprivate static readonly _DEFAULT_LIMIT: number = 40;\n\n\t/**\n\t * Partition id field name.\n\t * @internal\n\t */\n\tprivate static readonly _PARTITION_KEY: string = \"partitionId\";\n\n\t/**\n\t * The name for the schema.\n\t * @internal\n\t */\n\tprivate readonly _entitySchemaName: string;\n\n\t/**\n\t * The schema for the entity.\n\t * @internal\n\t */\n\tprivate readonly _entitySchema: IEntitySchema<T>;\n\n\t/**\n\t * The keys to use from the context ids to create partitions.\n\t * @internal\n\t */\n\tprivate readonly _partitionContextIds?: string[];\n\n\t/**\n\t * The configuration for the connector.\n\t * @internal\n\t */\n\tprivate readonly _config: IMongoDbEntityStorageConnectorConfig;\n\n\t/**\n\t * The MongoDb client.\n\t * @internal\n\t */\n\tprivate readonly _client: MongoClient;\n\n\t/**\n\t * Create a new instance of MongoDbEntityStorageConnector.\n\t * @param options The options for the connector.\n\t */\n\tconstructor(options: IMongoDbEntityStorageConnectorConstructorOptions) {\n\t\tGuards.object(MongoDbEntityStorageConnector.CLASS_NAME, nameof(options), options);\n\t\tGuards.stringValue(\n\t\t\tMongoDbEntityStorageConnector.CLASS_NAME,\n\t\t\tnameof(options.entitySchema),\n\t\t\toptions.entitySchema\n\t\t);\n\t\tGuards.object<IMongoDbEntityStorageConnectorConfig>(\n\t\t\tMongoDbEntityStorageConnector.CLASS_NAME,\n\t\t\tnameof(options.config),\n\t\t\toptions.config\n\t\t);\n\t\tGuards.stringValue(\n\t\t\tMongoDbEntityStorageConnector.CLASS_NAME,\n\t\t\tnameof(options.config.host),\n\t\t\toptions.config.host\n\t\t);\n\t\tGuards.stringValue(\n\t\t\tMongoDbEntityStorageConnector.CLASS_NAME,\n\t\t\tnameof(options.config.database),\n\t\t\toptions.config.database\n\t\t);\n\t\tGuards.stringValue(\n\t\t\tMongoDbEntityStorageConnector.CLASS_NAME,\n\t\t\tnameof(options.config.collection),\n\t\t\toptions.config.collection\n\t\t);\n\n\t\tthis._entitySchemaName = options.entitySchema;\n\t\tthis._entitySchema = EntitySchemaFactory.get(options.entitySchema);\n\t\tthis._partitionContextIds = options.partitionContextIds;\n\n\t\tthis._config = options.config;\n\n\t\tthis._client = new MongoClient(this.createConnectionConfig());\n\t}\n\n\t/**\n\t * Initialize the MongoDb environment.\n\t * @param nodeLoggingComponentType Optional type of the logging component.\n\t * @returns A promise that resolves to a boolean indicating success.\n\t */\n\tpublic async bootstrap(nodeLoggingComponentType?: string): Promise<boolean> {\n\t\tconst nodeLogging = ComponentFactory.getIfExists<ILoggingComponent>(nodeLoggingComponentType);\n\n\t\ttry {\n\t\t\tawait this._client.connect();\n\n\t\t\tawait nodeLogging?.log({\n\t\t\t\tlevel: \"info\",\n\t\t\t\tsource: MongoDbEntityStorageConnector.CLASS_NAME,\n\t\t\t\tts: Date.now(),\n\t\t\t\tmessage: \"databaseCreating\",\n\t\t\t\tdata: {\n\t\t\t\t\tdatabaseName: this._config.database\n\t\t\t\t}\n\t\t\t});\n\n\t\t\t// Create the database if it does not exist\n\t\t\tthis._client.db(this._config.database);\n\n\t\t\tawait nodeLogging?.log({\n\t\t\t\tlevel: \"info\",\n\t\t\t\tsource: MongoDbEntityStorageConnector.CLASS_NAME,\n\t\t\t\tts: Date.now(),\n\t\t\t\tmessage: \"databaseExists\",\n\t\t\t\tdata: {\n\t\t\t\t\tdatabaseName: this._config.database\n\t\t\t\t}\n\t\t\t});\n\n\t\t\tawait this.getCollection();\n\n\t\t\tawait nodeLogging?.log({\n\t\t\t\tlevel: \"info\",\n\t\t\t\tsource: MongoDbEntityStorageConnector.CLASS_NAME,\n\t\t\t\tts: Date.now(),\n\t\t\t\tmessage: \"collectionExists\",\n\t\t\t\tdata: {\n\t\t\t\t\tcollectionName: this._config.collection\n\t\t\t\t}\n\t\t\t});\n\t\t} catch (error) {\n\t\t\tawait nodeLogging?.log({\n\t\t\t\tlevel: \"error\",\n\t\t\t\tsource: MongoDbEntityStorageConnector.CLASS_NAME,\n\t\t\t\tts: Date.now(),\n\t\t\t\tmessage: \"databaseCreateFailed\",\n\t\t\t\terror: BaseError.fromError(error),\n\t\t\t\tdata: {\n\t\t\t\t\tdatabaseName: this._config.database\n\t\t\t\t}\n\t\t\t});\n\t\t\treturn false;\n\t\t}\n\n\t\treturn true;\n\t}\n\n\t/**\n\t * The component needs to be stopped when the node is closed.\n\t * @param nodeLoggingComponentType The node logging component type.\n\t * @returns Nothing.\n\t */\n\tpublic async stop?(nodeLoggingComponentType?: string): Promise<void> {\n\t\tawait this._client.close();\n\t}\n\n\t/**\n\t * Returns the class name of the component.\n\t * @returns The class name of the component.\n\t */\n\tpublic className(): string {\n\t\treturn MongoDbEntityStorageConnector.CLASS_NAME;\n\t}\n\n\t/**\n\t * Returns the health status of the component.\n\t * @returns The health status of the component.\n\t */\n\tpublic async health(): Promise<IHealth[]> {\n\t\ttry {\n\t\t\tawait this._client\n\t\t\t\t.db(this._config.database)\n\t\t\t\t.collection(this._config.collection)\n\t\t\t\t.estimatedDocumentCount();\n\t\t\treturn [\n\t\t\t\t{\n\t\t\t\t\tsource: MongoDbEntityStorageConnector.CLASS_NAME,\n\t\t\t\t\tstatus: HealthStatus.Ok,\n\t\t\t\t\tdescription: \"healthDescription\",\n\t\t\t\t\tdata: { database: this._config.database, collection: this._config.collection }\n\t\t\t\t}\n\t\t\t];\n\t\t} catch {\n\t\t\treturn [\n\t\t\t\t{\n\t\t\t\t\tsource: MongoDbEntityStorageConnector.CLASS_NAME,\n\t\t\t\t\tstatus: HealthStatus.Error,\n\t\t\t\t\tdescription: \"healthDescription\",\n\t\t\t\t\tmessage: \"connectionFailed\",\n\t\t\t\t\tdata: { database: this._config.database, collection: this._config.collection }\n\t\t\t\t}\n\t\t\t];\n\t\t}\n\t}\n\n\t/**\n\t * Get the schema for the entities.\n\t * @returns The schema for the entities.\n\t */\n\tpublic getSchema(): IEntitySchema {\n\t\treturn this._entitySchema as IEntitySchema;\n\t}\n\n\t/**\n\t * Get an entity from MongoDb.\n\t * @param id The id of the entity to get, or the index value if secondaryIndex is set.\n\t * @param secondaryIndex Get the item using a secondary index.\n\t * @param conditions The optional conditions to match for the entities.\n\t * @returns The object if it can be found or undefined.\n\t */\n\tpublic async get(\n\t\tid: string,\n\t\tsecondaryIndex?: keyof T,\n\t\tconditions?: { property: keyof T; value: unknown }[]\n\t): Promise<T | undefined> {\n\t\tGuards.stringValue(MongoDbEntityStorageConnector.CLASS_NAME, nameof(id), id);\n\n\t\tconst contextIds = await ContextIdStore.getContextIds();\n\t\tconst partitionKey = ContextIdHelper.combinedContextKey(contextIds, this._partitionContextIds);\n\n\t\ttry {\n\t\t\tconst primaryKey = EntitySchemaHelper.getPrimaryKey(this.getSchema());\n\t\t\tconst query: { [key: string]: unknown } = Is.empty(secondaryIndex)\n\t\t\t\t? { [primaryKey.property]: id }\n\t\t\t\t: { [secondaryIndex]: id };\n\n\t\t\tif (Is.stringValue(partitionKey)) {\n\t\t\t\tquery[MongoDbEntityStorageConnector._PARTITION_KEY] = partitionKey;\n\t\t\t}\n\n\t\t\tif (conditions) {\n\t\t\t\tfor (const condition of conditions) {\n\t\t\t\t\tquery[condition.property as string] = condition.value;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tconst collection = await this.getCollection();\n\t\t\tconst result = await collection.findOne(query);\n\t\t\tObjectHelper.propertyDelete(result, \"_id\");\n\t\t\treturn Is.objectValue(result)\n\t\t\t\t? EntityHelper.unPrepareEntity<T>(result as T, [\n\t\t\t\t\t\tMongoDbEntityStorageConnector._PARTITION_KEY\n\t\t\t\t\t])\n\t\t\t\t: undefined;\n\t\t} catch (err) {\n\t\t\tthrow new GeneralError(\n\t\t\t\tMongoDbEntityStorageConnector.CLASS_NAME,\n\t\t\t\t\"getFailed\",\n\t\t\t\t{\n\t\t\t\t\tid\n\t\t\t\t},\n\t\t\t\terr\n\t\t\t);\n\t\t}\n\t}\n\n\t/**\n\t * Set an entity.\n\t * @param entity The entity to set.\n\t * @param conditions The optional conditions to match for the entities.\n\t * @returns The id of the entity.\n\t */\n\tpublic async set(entity: T, conditions?: { property: keyof T; value: unknown }[]): Promise<void> {\n\t\tGuards.object<T>(MongoDbEntityStorageConnector.CLASS_NAME, nameof(entity), entity);\n\n\t\tconst contextIds = await ContextIdStore.getContextIds();\n\t\tconst partitionKey = ContextIdHelper.combinedContextKey(contextIds, this._partitionContextIds);\n\n\t\tconst prepared = EntityHelper.prepareEntity(\n\t\t\tentity,\n\t\t\tthis._entitySchema,\n\t\t\tIs.stringValue(partitionKey)\n\t\t\t\t? [{ property: MongoDbEntityStorageConnector._PARTITION_KEY, value: partitionKey }]\n\t\t\t\t: undefined\n\t\t);\n\n\t\tconst primaryKey = EntitySchemaHelper.getPrimaryKey(this.getSchema());\n\t\tconst id = prepared[primaryKey.property];\n\n\t\ttry {\n\t\t\tconst filter: { [key in keyof T]?: unknown } = { [primaryKey.property]: id };\n\n\t\t\tif (Is.stringValue(partitionKey)) {\n\t\t\t\tfilter[MongoDbEntityStorageConnector._PARTITION_KEY as keyof T] = partitionKey;\n\t\t\t}\n\n\t\t\tif (Is.arrayValue(conditions)) {\n\t\t\t\tfor (const condition of conditions) {\n\t\t\t\t\tfilter[condition.property] = condition.value;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tconst collection = await this.getCollection();\n\t\t\tawait collection.findOneAndUpdate(\n\t\t\t\tfilter,\n\t\t\t\t{ $set: prepared as Partial<Document> },\n\t\t\t\t{ upsert: true }\n\t\t\t);\n\t\t} catch (err) {\n\t\t\tthrow new GeneralError(\n\t\t\t\tMongoDbEntityStorageConnector.CLASS_NAME,\n\t\t\t\t\"setFailed\",\n\t\t\t\t{\n\t\t\t\t\tid\n\t\t\t\t},\n\t\t\t\terr\n\t\t\t);\n\t\t}\n\t}\n\n\t/**\n\t * Set multiple entities in a batch.\n\t * @param entities The entities to set.\n\t * @returns Nothing.\n\t */\n\tpublic async setBatch(entities: T[]): Promise<void> {\n\t\tGuards.arrayValue(MongoDbEntityStorageConnector.CLASS_NAME, nameof(entities), entities);\n\n\t\tconst contextIds = await ContextIdStore.getContextIds();\n\t\tconst partitionKey = ContextIdHelper.combinedContextKey(contextIds, this._partitionContextIds);\n\t\tconst primaryKey = EntitySchemaHelper.getPrimaryKey(this.getSchema());\n\n\t\tconst preparedEntities = entities.map(entity =>\n\t\t\tEntityHelper.prepareEntity(\n\t\t\t\tentity,\n\t\t\t\tthis._entitySchema,\n\t\t\t\tIs.stringValue(partitionKey)\n\t\t\t\t\t? [{ property: MongoDbEntityStorageConnector._PARTITION_KEY, value: partitionKey }]\n\t\t\t\t\t: undefined\n\t\t\t)\n\t\t);\n\n\t\ttry {\n\t\t\tconst collection = await this.getCollection();\n\t\t\tawait collection.bulkWrite(\n\t\t\t\tpreparedEntities.map(prepared => {\n\t\t\t\t\tconst filter: { [key: string]: unknown } = {\n\t\t\t\t\t\t[primaryKey.property]: prepared[primaryKey.property]\n\t\t\t\t\t};\n\t\t\t\t\tif (Is.stringValue(partitionKey)) {\n\t\t\t\t\t\tfilter[MongoDbEntityStorageConnector._PARTITION_KEY] = partitionKey;\n\t\t\t\t\t}\n\t\t\t\t\treturn {\n\t\t\t\t\t\tupdateOne: {\n\t\t\t\t\t\t\tfilter,\n\t\t\t\t\t\t\tupdate: {\n\t\t\t\t\t\t\t\t$set: prepared as Partial<Document>\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tupsert: true\n\t\t\t\t\t\t}\n\t\t\t\t\t};\n\t\t\t\t})\n\t\t\t);\n\t\t} catch (err) {\n\t\t\tthrow new GeneralError(\n\t\t\t\tMongoDbEntityStorageConnector.CLASS_NAME,\n\t\t\t\t\"setBatchFailed\",\n\t\t\t\tundefined,\n\t\t\t\terr\n\t\t\t);\n\t\t}\n\t}\n\n\t/**\n\t * Empty the entity storage.\n\t * @returns Nothing.\n\t */\n\tpublic async empty(): Promise<void> {\n\t\ttry {\n\t\t\tconst contextIds = await ContextIdStore.getContextIds();\n\t\t\tconst partitionKey = ContextIdHelper.combinedContextKey(\n\t\t\t\tcontextIds,\n\t\t\t\tthis._partitionContextIds\n\t\t\t);\n\n\t\t\tconst filter: { [key: string]: unknown } = {};\n\t\t\tif (Is.stringValue(partitionKey)) {\n\t\t\t\tfilter[MongoDbEntityStorageConnector._PARTITION_KEY] = partitionKey;\n\t\t\t}\n\n\t\t\tconst collection = await this.getCollection();\n\t\t\tawait collection.deleteMany(filter);\n\t\t} catch (err) {\n\t\t\tthrow new GeneralError(\n\t\t\t\tMongoDbEntityStorageConnector.CLASS_NAME,\n\t\t\t\t\"emptyFailed\",\n\t\t\t\tundefined,\n\t\t\t\terr\n\t\t\t);\n\t\t}\n\t}\n\n\t/**\n\t * Remove the entity.\n\t * @param id The id of the entity to remove.\n\t * @param conditions The optional conditions to match for the entities.\n\t * @returns Nothing.\n\t */\n\tpublic async remove(\n\t\tid: string,\n\t\tconditions?: { property: keyof T; value: unknown }[]\n\t): Promise<void> {\n\t\tGuards.stringValue(MongoDbEntityStorageConnector.CLASS_NAME, nameof(id), id);\n\n\t\tconst contextIds = await ContextIdStore.getContextIds();\n\t\tconst partitionKey = ContextIdHelper.combinedContextKey(contextIds, this._partitionContextIds);\n\n\t\ttry {\n\t\t\tconst primaryKey = EntitySchemaHelper.getPrimaryKey(this.getSchema());\n\t\t\tconst query: { [key in keyof T]?: unknown } = { [primaryKey.property]: id };\n\n\t\t\tif (Is.stringValue(partitionKey)) {\n\t\t\t\tquery[MongoDbEntityStorageConnector._PARTITION_KEY as keyof T] = partitionKey;\n\t\t\t}\n\n\t\t\tif (conditions) {\n\t\t\t\tfor (const condition of conditions) {\n\t\t\t\t\tquery[condition.property] = condition.value;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tconst collection = await this.getCollection();\n\t\t\tawait collection.deleteOne(query);\n\t\t} catch (err) {\n\t\t\tthrow new GeneralError(MongoDbEntityStorageConnector.CLASS_NAME, \"removeFailed\", { id }, err);\n\t\t}\n\t}\n\n\t/**\n\t * Remove multiple entities by id.\n\t * @param ids The ids of the entities to remove.\n\t * @returns Nothing.\n\t */\n\tpublic async removeBatch(ids: string[]): Promise<void> {\n\t\tGuards.arrayValue(MongoDbEntityStorageConnector.CLASS_NAME, nameof(ids), ids);\n\n\t\tconst contextIds = await ContextIdStore.getContextIds();\n\t\tconst partitionKey = ContextIdHelper.combinedContextKey(contextIds, this._partitionContextIds);\n\n\t\ttry {\n\t\t\tconst primaryKey = EntitySchemaHelper.getPrimaryKey(this.getSchema());\n\t\t\tconst filter: { [key: string]: unknown } = {\n\t\t\t\t[primaryKey.property]: { $in: ids }\n\t\t\t};\n\n\t\t\tif (Is.stringValue(partitionKey)) {\n\t\t\t\tfilter[MongoDbEntityStorageConnector._PARTITION_KEY] = partitionKey;\n\t\t\t}\n\n\t\t\tconst collection = await this.getCollection();\n\t\t\tawait collection.deleteMany(filter);\n\t\t} catch (err) {\n\t\t\tthrow new GeneralError(\n\t\t\t\tMongoDbEntityStorageConnector.CLASS_NAME,\n\t\t\t\t\"removeBatchFailed\",\n\t\t\t\tundefined,\n\t\t\t\terr\n\t\t\t);\n\t\t}\n\t}\n\n\t/**\n\t * Teardown the entity storage by dropping the collection.\n\t * @param nodeLoggingComponentType The node logging component type.\n\t * @returns True if the teardown process was successful.\n\t */\n\tpublic async teardown(nodeLoggingComponentType?: string): Promise<boolean> {\n\t\tconst nodeLogging = ComponentFactory.getIfExists<ILoggingComponent>(nodeLoggingComponentType);\n\n\t\tawait nodeLogging?.log({\n\t\t\tlevel: \"info\",\n\t\t\tsource: MongoDbEntityStorageConnector.CLASS_NAME,\n\t\t\tts: Date.now(),\n\t\t\tmessage: \"collectionDropping\",\n\t\t\tdata: { collection: this._config.collection }\n\t\t});\n\n\t\ttry {\n\t\t\tconst collection = await this.getCollection();\n\t\t\tawait collection.drop();\n\n\t\t\tawait nodeLogging?.log({\n\t\t\t\tlevel: \"info\",\n\t\t\t\tsource: MongoDbEntityStorageConnector.CLASS_NAME,\n\t\t\t\tts: Date.now(),\n\t\t\t\tmessage: \"collectionDropped\",\n\t\t\t\tdata: { collection: this._config.collection }\n\t\t\t});\n\n\t\t\treturn true;\n\t\t} catch (err) {\n\t\t\tawait nodeLogging?.log({\n\t\t\t\tlevel: \"error\",\n\t\t\t\tsource: MongoDbEntityStorageConnector.CLASS_NAME,\n\t\t\t\tts: Date.now(),\n\t\t\t\tmessage: \"teardownFailed\",\n\t\t\t\terror: BaseError.fromError(err)\n\t\t\t});\n\t\t\treturn false;\n\t\t}\n\t}\n\n\t/**\n\t * Find all the entities which match the conditions.\n\t * @param conditions The conditions to match for the entities.\n\t * @param sortProperties The optional sort order.\n\t * @param properties The optional properties to return, defaults to all.\n\t * @param cursor The cursor to request the next chunk of entities.\n\t * @param limit The suggested number of entities to return in each chunk, in some scenarios can return a different amount.\n\t * @returns All the entities for the storage matching the conditions,\n\t * and a cursor which can be used to request more entities.\n\t */\n\tpublic async query(\n\t\tconditions?: EntityCondition<T>,\n\t\tsortProperties?: { property: keyof T; sortDirection: SortDirection }[],\n\t\tproperties?: (keyof T)[],\n\t\tcursor?: string,\n\t\tlimit?: number\n\t): Promise<{ entities: Partial<T>[]; cursor?: string }> {\n\t\tconst contextIds = await ContextIdStore.getContextIds();\n\t\tconst partitionKey = ContextIdHelper.combinedContextKey(contextIds, this._partitionContextIds);\n\n\t\tconst returnSize = limit ?? MongoDbEntityStorageConnector._DEFAULT_LIMIT;\n\n\t\tconst filter = this.buildFilter(conditions, partitionKey);\n\n\t\tconst sort = new Map<string, SortDirection>();\n\t\tif (Array.isArray(sortProperties)) {\n\t\t\tfor (const sortProperty of sortProperties) {\n\t\t\t\tsort.set(sortProperty.property as string, sortProperty.sortDirection);\n\t\t\t}\n\t\t}\n\n\t\tconst projection: { [key: string]: number } = {};\n\t\tif (properties) {\n\t\t\tfor (const property of properties) {\n\t\t\t\tprojection[property as string] = 1;\n\t\t\t}\n\t\t}\n\n\t\tconst cursorValue = cursor ? Number(cursor) : 0;\n\n\t\tconst collection = await this.getCollection();\n\t\tconst entitiesResult = await collection\n\t\t\t// False positive, this is not an array find call\n\t\t\t// eslint-disable-next-line unicorn/no-array-callback-reference\n\t\t\t?.find(filter as Filter<Document>, { projection })\n\t\t\t.sort(sort)\n\t\t\t.skip(cursorValue)\n\t\t\t.limit(returnSize + 1)\n\t\t\t.toArray();\n\n\t\tconst rawResults = (entitiesResult as unknown as Partial<T>[]) ?? [];\n\t\tconst hasMore = rawResults.length > returnSize;\n\t\tconst entities = hasMore ? rawResults.slice(0, returnSize) : rawResults;\n\n\t\tfor (let i = 0; i < entities.length; i++) {\n\t\t\tconst entity = entities[i];\n\t\t\tObjectHelper.propertyDelete(entity, \"_id\");\n\t\t\tentities[i] = EntityHelper.unPrepareEntity(entity, [\n\t\t\t\tMongoDbEntityStorageConnector._PARTITION_KEY\n\t\t\t]);\n\t\t}\n\n\t\treturn {\n\t\t\tentities,\n\t\t\tcursor: hasMore ? String(cursorValue + returnSize) : undefined\n\t\t};\n\t}\n\n\t/**\n\t * Count all the entities which match the conditions.\n\t * @param conditions The optional conditions to match for the entities.\n\t * @returns The total count of entities in the storage.\n\t */\n\tpublic async count(conditions?: EntityCondition<T>): Promise<number> {\n\t\ttry {\n\t\t\tconst contextIds = await ContextIdStore.getContextIds();\n\t\t\tconst partitionKey = ContextIdHelper.combinedContextKey(\n\t\t\t\tcontextIds,\n\t\t\t\tthis._partitionContextIds\n\t\t\t);\n\n\t\t\tconst filter = this.buildFilter(conditions, partitionKey);\n\n\t\t\treturn await this._client\n\t\t\t\t.db(this._config.database)\n\t\t\t\t.collection(this._config.collection)\n\t\t\t\t.countDocuments(filter as Filter<Document>);\n\t\t} catch (err) {\n\t\t\tthrow new GeneralError(\n\t\t\t\tMongoDbEntityStorageConnector.CLASS_NAME,\n\t\t\t\t\"countFailed\",\n\t\t\t\tundefined,\n\t\t\t\terr\n\t\t\t);\n\t\t}\n\t}\n\n\t/**\n\t * Get all unique partition context ids present in the collection.\n\t * @returns An array of context id objects, one per unique partition.\n\t */\n\tpublic async getPartitionContextIds(): Promise<IContextIds[]> {\n\t\tif (!Is.arrayValue(this._partitionContextIds)) {\n\t\t\treturn [];\n\t\t}\n\n\t\ttry {\n\t\t\tconst collection = await this.getCollection();\n\t\t\tconst partitionIds = await collection.distinct(MongoDbEntityStorageConnector._PARTITION_KEY);\n\n\t\t\treturn partitionIds\n\t\t\t\t.filter((id): id is string => Is.stringValue(id))\n\t\t\t\t.map(partitionId =>\n\t\t\t\t\tContextIdHelper.shortSplit(this._partitionContextIds ?? [], partitionId)\n\t\t\t\t);\n\t\t} catch (err) {\n\t\t\tthrow new GeneralError(\n\t\t\t\tMongoDbEntityStorageConnector.CLASS_NAME,\n\t\t\t\t\"getPartitionContextIdsFailed\",\n\t\t\t\tundefined,\n\t\t\t\terr\n\t\t\t);\n\t\t}\n\t}\n\n\t/**\n\t * Create the target connector for performing the migration using a temporary collection.\n\t * @param newEntitySchema The name of the new entity schema to create the connector for.\n\t * @returns Connector for performing the migration.\n\t */\n\tpublic async createTargetConnector<U>(\n\t\tnewEntitySchema: string\n\t): Promise<IEntityStorageConnector<U>> {\n\t\tconst migrationCollectionName = `${this._config.collection}Migration${Date.now()}`;\n\t\treturn new MongoDbEntityStorageConnector<U>({\n\t\t\tentitySchema: newEntitySchema,\n\t\t\tconfig: {\n\t\t\t\t...this._config,\n\t\t\t\tcollection: migrationCollectionName\n\t\t\t},\n\t\t\tpartitionContextIds: this._partitionContextIds\n\t\t});\n\t}\n\n\t/**\n\t * Finalize the migration by dropping the source collection and renaming the migration collection to the original name.\n\t * @param targetConnector The connector holding the migrated data in a temporary collection.\n\t * @param options The options to control how the migration is finalized.\n\t * @param loggingComponentType The logging component type to use during finalization.\n\t * @returns The final connector using the original collection name with the new schema.\n\t */\n\tpublic async finalizeMigration<U>(\n\t\ttargetConnector: MongoDbEntityStorageConnector<U>,\n\t\toptions?: IMigrationOptions<T, U>,\n\t\tloggingComponentType?: string\n\t): Promise<MongoDbEntityStorageConnector<U>> {\n\t\t// Only rename if the migration collection was actually created (it won't exist if no\n\t\t// entities were written, since MongoDB creates collections lazily on first write).\n\t\tconst migrationCollection = await targetConnector.getCollection();\n\n\t\tconst collections = await targetConnector._client\n\t\t\t.db(targetConnector._config.database)\n\t\t\t.listCollections({ name: targetConnector._config.collection })\n\t\t\t.toArray();\n\n\t\tif (collections.length > 0) {\n\t\t\t// Teardown the existing table with the original name to free up the name for the new table\n\t\t\tawait this.teardown(loggingComponentType);\n\n\t\t\tawait migrationCollection.rename(this._config.collection);\n\t\t}\n\n\t\tconst finalConnector = new MongoDbEntityStorageConnector<U>({\n\t\t\tentitySchema: targetConnector._entitySchemaName,\n\t\t\tconfig: this._config,\n\t\t\tpartitionContextIds: this._partitionContextIds\n\t\t});\n\n\t\tif (await finalConnector.bootstrap(loggingComponentType)) {\n\t\t\tawait targetConnector.stop?.();\n\t\t\treturn finalConnector;\n\t\t}\n\n\t\tthrow new GeneralError(\n\t\t\tMongoDbEntityStorageConnector.CLASS_NAME,\n\t\t\t\"finalizeMigrationFailedBootstrap\",\n\t\t\tundefined\n\t\t);\n\t}\n\n\t/**\n\t * Cleanup a failed or aborted migration by dropping the temporary migration collection.\n\t * @param targetConnector The target connector to cleanup.\n\t * @param options The options to control how the migration is cleaned up.\n\t * @param loggingComponentType The optional component type to use for logging.\n\t * @returns A promise that resolves when the cleanup is complete.\n\t */\n\tpublic async cleanupMigration<U>(\n\t\ttargetConnector: IEntityStorageConnector<U> | undefined,\n\t\toptions?: IMigrationOptions<T, U>,\n\t\tloggingComponentType?: string\n\t): Promise<void> {\n\t\tawait targetConnector?.teardown?.(loggingComponentType);\n\t}\n\n\t/**\n\t * Create a new DB connection configuration.\n\t * @returns The MongoDb connection configuration.\n\t * @internal\n\t */\n\tprivate createConnectionConfig(): string {\n\t\tconst { host, port, user, password, database } = this._config;\n\t\tconst portPart = port ? `:${port}` : \"\";\n\t\tif (user && password) {\n\t\t\treturn `mongodb://${user}:${password}@${host}${portPart}/${database}`;\n\t\t}\n\t\treturn `mongodb://${host}${portPart}/${database}`;\n\t}\n\n\t/**\n\t * Return a Mongo DB collection.\n\t * @returns The MongoDb collection.\n\t * @internal\n\t */\n\tprivate async getCollection(): Promise<Collection> {\n\t\tconst { database, collection } = this._config;\n\t\treturn this._client.db(database).collection(collection);\n\t}\n\n\t/**\n\t * Build a MongoDB filter combining partition key and optional conditions.\n\t * @param conditions The optional entity conditions to include.\n\t * @param partitionKey The partition key value.\n\t * @returns The MongoDB filter object.\n\t * @internal\n\t */\n\tprivate buildFilter(\n\t\tconditions: EntityCondition<T> | undefined,\n\t\tpartitionKey: string | undefined\n\t): Filter<T> {\n\t\tconst finalConditions: EntityCondition<T> = {\n\t\t\tconditions: [],\n\t\t\tlogicalOperator: LogicalOperator.And\n\t\t};\n\n\t\tif (Is.stringValue(partitionKey)) {\n\t\t\tfinalConditions.conditions.push({\n\t\t\t\tproperty: MongoDbEntityStorageConnector._PARTITION_KEY,\n\t\t\t\tcomparison: ComparisonOperator.Equals,\n\t\t\t\tvalue: partitionKey\n\t\t\t});\n\t\t}\n\n\t\tif (!Is.empty(conditions)) {\n\t\t\tfinalConditions.conditions.push(conditions);\n\t\t}\n\n\t\tconst filter: Filter<T> = {};\n\t\tif (finalConditions.conditions.length > 0) {\n\t\t\tthis.buildQueryParameters(\"\", finalConditions, filter);\n\t\t}\n\n\t\treturn filter;\n\t}\n\n\t/**\n\t * Create an MongoDB filter query.\n\t * @param objectPath The path for the nested object.\n\t * @param condition The conditions to create the query from.\n\t * @param filter The filter query to use.\n\t * @internal\n\t */\n\tprivate buildQueryParameters(\n\t\tobjectPath: string,\n\t\tcondition: EntityCondition<T>,\n\t\tfilter: Filter<T>\n\t): void {\n\t\tif (!condition) {\n\t\t\treturn;\n\t\t}\n\n\t\tif (\"conditions\" in condition) {\n\t\t\tconst subConditions: Filter<T>[] = condition.conditions.map(c => {\n\t\t\t\tconst subFilter: Filter<T> = {};\n\t\t\t\tthis.buildQueryParameters(objectPath, c, subFilter);\n\t\t\t\treturn subFilter;\n\t\t\t});\n\n\t\t\tif (condition.logicalOperator === LogicalOperator.And) {\n\t\t\t\tfilter.$and = subConditions as Filter<WithId<T>>[];\n\t\t\t} else if (condition.logicalOperator === LogicalOperator.Or) {\n\t\t\t\tfilter.$or = subConditions as Filter<WithId<T>>[];\n\t\t\t} else {\n\t\t\t\tObject.assign(filter, subConditions[0]);\n\t\t\t}\n\t\t} else {\n\t\t\tconst propertyPath = String(condition.property);\n\t\t\tconst prop = objectPath ? `${objectPath}.${propertyPath}` : propertyPath;\n\t\t\tconst propertyParts = propertyPath.split(\".\");\n\t\t\tconst schemaLookupName = propertyParts.length > 1 ? propertyParts[0] : propertyPath;\n\t\t\tconst propertySchema = this._entitySchema.properties?.find(\n\t\t\t\tp => p.property === schemaLookupName\n\t\t\t);\n\t\t\t// For dot-notation paths the leaf field is always a string value; using the root\n\t\t\t// type directly would send Includes into $elemMatch which does not work for nested\n\t\t\t// string fields. Keeping String here causes mapComparisonOperator to emit $regex,\n\t\t\t// which MongoDB handles correctly for both nested object and array traversal.\n\t\t\tconst propertyType =\n\t\t\t\tpropertyParts.length > 1 ? EntitySchemaPropertyType.String : propertySchema?.type;\n\t\t\tconst comparison = this.mapComparisonOperator(\n\t\t\t\tcondition.comparison,\n\t\t\t\tcondition.value,\n\t\t\t\tpropertyType\n\t\t\t);\n\n\t\t\t(filter as { [key: string]: unknown })[prop] = comparison;\n\t\t}\n\t}\n\n\t/**\n\t * Map the framework comparison operators to those in MongoDB.\n\t * @param comparison The comparison operator.\n\t * @param value The value to compare.\n\t * @param type The type of the property from the schema.\n\t * @returns The MongoDB comparison expression.\n\t * @internal\n\t */\n\tprivate mapComparisonOperator(\n\t\tcomparison: ComparisonOperator,\n\t\tvalue: unknown,\n\t\ttype?: EntitySchemaPropertyType\n\t): unknown {\n\t\tswitch (comparison) {\n\t\t\tcase ComparisonOperator.Equals:\n\t\t\t\treturn value;\n\t\t\tcase ComparisonOperator.NotEquals:\n\t\t\t\treturn { $ne: value };\n\t\t\tcase ComparisonOperator.GreaterThan:\n\t\t\t\treturn { $gt: value };\n\t\t\tcase ComparisonOperator.LessThan:\n\t\t\t\treturn { $lt: value };\n\t\t\tcase ComparisonOperator.GreaterThanOrEqual:\n\t\t\t\treturn { $gte: value };\n\t\t\tcase ComparisonOperator.LessThanOrEqual:\n\t\t\t\treturn { $lte: value };\n\t\t\tcase ComparisonOperator.In:\n\t\t\t\treturn { $in: Array.isArray(value) ? value : [value] };\n\t\t\tcase ComparisonOperator.Includes:\n\t\t\t\t// For string fields, use regex for substring matching\n\t\t\t\tif (type === EntitySchemaPropertyType.String) {\n\t\t\t\t\t// Escape special regex characters in the value\n\t\t\t\t\tconst escapedValue = String(value).replace(/[$()*+.?[\\\\\\]^{|}]/g, \"\\\\$&\");\n\t\t\t\t\treturn { $regex: escapedValue };\n\t\t\t\t}\n\t\t\t\t// For array and object fields, use $elemMatch\n\t\t\t\tif (type === EntitySchemaPropertyType.Array || type === EntitySchemaPropertyType.Object) {\n\t\t\t\t\treturn { $elemMatch: { $eq: value } };\n\t\t\t\t}\n\t\t\t\t// Fallback to $elemMatch for backwards compatibility\n\t\t\t\treturn { $elemMatch: { $eq: value } };\n\t\t\tcase ComparisonOperator.NotIncludes:\n\t\t\t\t// For string fields, use negated regex\n\t\t\t\tif (type === EntitySchemaPropertyType.String) {\n\t\t\t\t\tconst escapedValue = String(value).replace(/[$()*+.?[\\\\\\]^{|}]/g, \"\\\\$&\");\n\t\t\t\t\treturn { $not: { $regex: escapedValue } };\n\t\t\t\t}\n\t\t\t\t// For arrays, use $elemMatch with $ne\n\t\t\t\treturn { $elemMatch: { $ne: value } };\n\t\t\tdefault:\n\t\t\t\tthrow new GeneralError(\n\t\t\t\t\tMongoDbEntityStorageConnector.CLASS_NAME,\n\t\t\t\t\t\"unsupportedComparisonOperator\",\n\t\t\t\t\t{ comparison }\n\t\t\t\t);\n\t\t}\n\t}\n}\n"]}
|
|
@@ -1,11 +1,12 @@
|
|
|
1
|
+
import { type IContextIds } from "@twin.org/context";
|
|
1
2
|
import { type IHealth } from "@twin.org/core";
|
|
2
3
|
import { type EntityCondition, type IEntitySchema, type SortDirection } from "@twin.org/entity";
|
|
3
|
-
import type
|
|
4
|
+
import { type IEntityStorageConnector, type IEntityStorageMigrationConnector, type IMigrationOptions } from "@twin.org/entity-storage-models";
|
|
4
5
|
import type { IMongoDbEntityStorageConnectorConstructorOptions } from "./models/IMongoDbEntityStorageConnectorConstructorOptions.js";
|
|
5
6
|
/**
|
|
6
7
|
* Class for performing entity storage operations using MongoDb.
|
|
7
8
|
*/
|
|
8
|
-
export declare class MongoDbEntityStorageConnector<T = unknown> implements
|
|
9
|
+
export declare class MongoDbEntityStorageConnector<T = unknown> implements IEntityStorageMigrationConnector<T> {
|
|
9
10
|
/**
|
|
10
11
|
* Runtime name for the class.
|
|
11
12
|
*/
|
|
@@ -115,7 +116,35 @@ export declare class MongoDbEntityStorageConnector<T = unknown> implements IEnti
|
|
|
115
116
|
}>;
|
|
116
117
|
/**
|
|
117
118
|
* Count all the entities which match the conditions.
|
|
119
|
+
* @param conditions The optional conditions to match for the entities.
|
|
118
120
|
* @returns The total count of entities in the storage.
|
|
119
121
|
*/
|
|
120
|
-
count(): Promise<number>;
|
|
122
|
+
count(conditions?: EntityCondition<T>): Promise<number>;
|
|
123
|
+
/**
|
|
124
|
+
* Get all unique partition context ids present in the collection.
|
|
125
|
+
* @returns An array of context id objects, one per unique partition.
|
|
126
|
+
*/
|
|
127
|
+
getPartitionContextIds(): Promise<IContextIds[]>;
|
|
128
|
+
/**
|
|
129
|
+
* Create the target connector for performing the migration using a temporary collection.
|
|
130
|
+
* @param newEntitySchema The name of the new entity schema to create the connector for.
|
|
131
|
+
* @returns Connector for performing the migration.
|
|
132
|
+
*/
|
|
133
|
+
createTargetConnector<U>(newEntitySchema: string): Promise<IEntityStorageConnector<U>>;
|
|
134
|
+
/**
|
|
135
|
+
* Finalize the migration by dropping the source collection and renaming the migration collection to the original name.
|
|
136
|
+
* @param targetConnector The connector holding the migrated data in a temporary collection.
|
|
137
|
+
* @param options The options to control how the migration is finalized.
|
|
138
|
+
* @param loggingComponentType The logging component type to use during finalization.
|
|
139
|
+
* @returns The final connector using the original collection name with the new schema.
|
|
140
|
+
*/
|
|
141
|
+
finalizeMigration<U>(targetConnector: MongoDbEntityStorageConnector<U>, options?: IMigrationOptions<T, U>, loggingComponentType?: string): Promise<MongoDbEntityStorageConnector<U>>;
|
|
142
|
+
/**
|
|
143
|
+
* Cleanup a failed or aborted migration by dropping the temporary migration collection.
|
|
144
|
+
* @param targetConnector The target connector to cleanup.
|
|
145
|
+
* @param options The options to control how the migration is cleaned up.
|
|
146
|
+
* @param loggingComponentType The optional component type to use for logging.
|
|
147
|
+
* @returns A promise that resolves when the cleanup is complete.
|
|
148
|
+
*/
|
|
149
|
+
cleanupMigration<U>(targetConnector: IEntityStorageConnector<U> | undefined, options?: IMigrationOptions<T, U>, loggingComponentType?: string): Promise<void>;
|
|
121
150
|
}
|
package/docs/changelog.md
CHANGED
|
@@ -1,5 +1,37 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [0.0.3-next.14](https://github.com/iotaledger/twin-entity-storage/compare/entity-storage-connector-mongodb-v0.0.3-next.13...entity-storage-connector-mongodb-v0.0.3-next.14) (2026-05-19)
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
### Features
|
|
7
|
+
|
|
8
|
+
* adding schema migration functionality to all the connectors ([#85](https://github.com/iotaledger/twin-entity-storage/issues/85)) ([fd1555a](https://github.com/iotaledger/twin-entity-storage/commit/fd1555a34380158214a577586dafae821e72a578))
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
### Dependencies
|
|
12
|
+
|
|
13
|
+
* The following workspace dependencies were updated
|
|
14
|
+
* dependencies
|
|
15
|
+
* @twin.org/entity-storage-models bumped from 0.0.3-next.13 to 0.0.3-next.14
|
|
16
|
+
* devDependencies
|
|
17
|
+
* @twin.org/entity-storage-connector-memory bumped from 0.0.3-next.13 to 0.0.3-next.14
|
|
18
|
+
|
|
19
|
+
## [0.0.3-next.13](https://github.com/iotaledger/twin-entity-storage/compare/entity-storage-connector-mongodb-v0.0.3-next.12...entity-storage-connector-mongodb-v0.0.3-next.13) (2026-05-13)
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
### Miscellaneous Chores
|
|
23
|
+
|
|
24
|
+
* **entity-storage-connector-mongodb:** Synchronize repo versions
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
### Dependencies
|
|
28
|
+
|
|
29
|
+
* The following workspace dependencies were updated
|
|
30
|
+
* dependencies
|
|
31
|
+
* @twin.org/entity-storage-models bumped from 0.0.3-next.12 to 0.0.3-next.13
|
|
32
|
+
* devDependencies
|
|
33
|
+
* @twin.org/entity-storage-connector-memory bumped from 0.0.3-next.12 to 0.0.3-next.13
|
|
34
|
+
|
|
3
35
|
## [0.0.3-next.12](https://github.com/iotaledger/twin-entity-storage/compare/entity-storage-connector-mongodb-v0.0.3-next.11...entity-storage-connector-mongodb-v0.0.3-next.12) (2026-05-11)
|
|
4
36
|
|
|
5
37
|
|
|
@@ -10,7 +10,7 @@ Class for performing entity storage operations using MongoDb.
|
|
|
10
10
|
|
|
11
11
|
## Implements
|
|
12
12
|
|
|
13
|
-
- `
|
|
13
|
+
- `IEntityStorageMigrationConnector`\<`T`\>
|
|
14
14
|
|
|
15
15
|
## Constructors
|
|
16
16
|
|
|
@@ -64,7 +64,7 @@ A promise that resolves to a boolean indicating success.
|
|
|
64
64
|
|
|
65
65
|
#### Implementation of
|
|
66
66
|
|
|
67
|
-
`
|
|
67
|
+
`IEntityStorageMigrationConnector.bootstrap`
|
|
68
68
|
|
|
69
69
|
***
|
|
70
70
|
|
|
@@ -90,7 +90,7 @@ Nothing.
|
|
|
90
90
|
|
|
91
91
|
#### Implementation of
|
|
92
92
|
|
|
93
|
-
`
|
|
93
|
+
`IEntityStorageMigrationConnector.stop`
|
|
94
94
|
|
|
95
95
|
***
|
|
96
96
|
|
|
@@ -108,7 +108,7 @@ The class name of the component.
|
|
|
108
108
|
|
|
109
109
|
#### Implementation of
|
|
110
110
|
|
|
111
|
-
`
|
|
111
|
+
`IEntityStorageMigrationConnector.className`
|
|
112
112
|
|
|
113
113
|
***
|
|
114
114
|
|
|
@@ -126,7 +126,7 @@ The health status of the component.
|
|
|
126
126
|
|
|
127
127
|
#### Implementation of
|
|
128
128
|
|
|
129
|
-
`
|
|
129
|
+
`IEntityStorageMigrationConnector.health`
|
|
130
130
|
|
|
131
131
|
***
|
|
132
132
|
|
|
@@ -144,7 +144,7 @@ The schema for the entities.
|
|
|
144
144
|
|
|
145
145
|
#### Implementation of
|
|
146
146
|
|
|
147
|
-
`
|
|
147
|
+
`IEntityStorageMigrationConnector.getSchema`
|
|
148
148
|
|
|
149
149
|
***
|
|
150
150
|
|
|
@@ -182,7 +182,7 @@ The object if it can be found or undefined.
|
|
|
182
182
|
|
|
183
183
|
#### Implementation of
|
|
184
184
|
|
|
185
|
-
`
|
|
185
|
+
`IEntityStorageMigrationConnector.get`
|
|
186
186
|
|
|
187
187
|
***
|
|
188
188
|
|
|
@@ -214,7 +214,7 @@ The id of the entity.
|
|
|
214
214
|
|
|
215
215
|
#### Implementation of
|
|
216
216
|
|
|
217
|
-
`
|
|
217
|
+
`IEntityStorageMigrationConnector.set`
|
|
218
218
|
|
|
219
219
|
***
|
|
220
220
|
|
|
@@ -240,7 +240,7 @@ Nothing.
|
|
|
240
240
|
|
|
241
241
|
#### Implementation of
|
|
242
242
|
|
|
243
|
-
`
|
|
243
|
+
`IEntityStorageMigrationConnector.setBatch`
|
|
244
244
|
|
|
245
245
|
***
|
|
246
246
|
|
|
@@ -258,7 +258,7 @@ Nothing.
|
|
|
258
258
|
|
|
259
259
|
#### Implementation of
|
|
260
260
|
|
|
261
|
-
`
|
|
261
|
+
`IEntityStorageMigrationConnector.empty`
|
|
262
262
|
|
|
263
263
|
***
|
|
264
264
|
|
|
@@ -290,7 +290,7 @@ Nothing.
|
|
|
290
290
|
|
|
291
291
|
#### Implementation of
|
|
292
292
|
|
|
293
|
-
`
|
|
293
|
+
`IEntityStorageMigrationConnector.remove`
|
|
294
294
|
|
|
295
295
|
***
|
|
296
296
|
|
|
@@ -316,7 +316,7 @@ Nothing.
|
|
|
316
316
|
|
|
317
317
|
#### Implementation of
|
|
318
318
|
|
|
319
|
-
`
|
|
319
|
+
`IEntityStorageMigrationConnector.removeBatch`
|
|
320
320
|
|
|
321
321
|
***
|
|
322
322
|
|
|
@@ -342,7 +342,7 @@ True if the teardown process was successful.
|
|
|
342
342
|
|
|
343
343
|
#### Implementation of
|
|
344
344
|
|
|
345
|
-
`
|
|
345
|
+
`IEntityStorageMigrationConnector.teardown`
|
|
346
346
|
|
|
347
347
|
***
|
|
348
348
|
|
|
@@ -393,16 +393,24 @@ and a cursor which can be used to request more entities.
|
|
|
393
393
|
|
|
394
394
|
#### Implementation of
|
|
395
395
|
|
|
396
|
-
`
|
|
396
|
+
`IEntityStorageMigrationConnector.query`
|
|
397
397
|
|
|
398
398
|
***
|
|
399
399
|
|
|
400
400
|
### count() {#count}
|
|
401
401
|
|
|
402
|
-
> **count**(): `Promise`\<`number`\>
|
|
402
|
+
> **count**(`conditions?`): `Promise`\<`number`\>
|
|
403
403
|
|
|
404
404
|
Count all the entities which match the conditions.
|
|
405
405
|
|
|
406
|
+
#### Parameters
|
|
407
|
+
|
|
408
|
+
##### conditions?
|
|
409
|
+
|
|
410
|
+
`EntityCondition`\<`T`\>
|
|
411
|
+
|
|
412
|
+
The optional conditions to match for the entities.
|
|
413
|
+
|
|
406
414
|
#### Returns
|
|
407
415
|
|
|
408
416
|
`Promise`\<`number`\>
|
|
@@ -411,4 +419,142 @@ The total count of entities in the storage.
|
|
|
411
419
|
|
|
412
420
|
#### Implementation of
|
|
413
421
|
|
|
414
|
-
`
|
|
422
|
+
`IEntityStorageMigrationConnector.count`
|
|
423
|
+
|
|
424
|
+
***
|
|
425
|
+
|
|
426
|
+
### getPartitionContextIds() {#getpartitioncontextids}
|
|
427
|
+
|
|
428
|
+
> **getPartitionContextIds**(): `Promise`\<`IContextIds`[]\>
|
|
429
|
+
|
|
430
|
+
Get all unique partition context ids present in the collection.
|
|
431
|
+
|
|
432
|
+
#### Returns
|
|
433
|
+
|
|
434
|
+
`Promise`\<`IContextIds`[]\>
|
|
435
|
+
|
|
436
|
+
An array of context id objects, one per unique partition.
|
|
437
|
+
|
|
438
|
+
#### Implementation of
|
|
439
|
+
|
|
440
|
+
`IEntityStorageMigrationConnector.getPartitionContextIds`
|
|
441
|
+
|
|
442
|
+
***
|
|
443
|
+
|
|
444
|
+
### createTargetConnector() {#createtargetconnector}
|
|
445
|
+
|
|
446
|
+
> **createTargetConnector**\<`U`\>(`newEntitySchema`): `Promise`\<`IEntityStorageConnector`\<`U`\>\>
|
|
447
|
+
|
|
448
|
+
Create the target connector for performing the migration using a temporary collection.
|
|
449
|
+
|
|
450
|
+
#### Type Parameters
|
|
451
|
+
|
|
452
|
+
##### U
|
|
453
|
+
|
|
454
|
+
`U`
|
|
455
|
+
|
|
456
|
+
#### Parameters
|
|
457
|
+
|
|
458
|
+
##### newEntitySchema
|
|
459
|
+
|
|
460
|
+
`string`
|
|
461
|
+
|
|
462
|
+
The name of the new entity schema to create the connector for.
|
|
463
|
+
|
|
464
|
+
#### Returns
|
|
465
|
+
|
|
466
|
+
`Promise`\<`IEntityStorageConnector`\<`U`\>\>
|
|
467
|
+
|
|
468
|
+
Connector for performing the migration.
|
|
469
|
+
|
|
470
|
+
#### Implementation of
|
|
471
|
+
|
|
472
|
+
`IEntityStorageMigrationConnector.createTargetConnector`
|
|
473
|
+
|
|
474
|
+
***
|
|
475
|
+
|
|
476
|
+
### finalizeMigration() {#finalizemigration}
|
|
477
|
+
|
|
478
|
+
> **finalizeMigration**\<`U`\>(`targetConnector`, `options?`, `loggingComponentType?`): `Promise`\<`MongoDbEntityStorageConnector`\<`U`\>\>
|
|
479
|
+
|
|
480
|
+
Finalize the migration by dropping the source collection and renaming the migration collection to the original name.
|
|
481
|
+
|
|
482
|
+
#### Type Parameters
|
|
483
|
+
|
|
484
|
+
##### U
|
|
485
|
+
|
|
486
|
+
`U`
|
|
487
|
+
|
|
488
|
+
#### Parameters
|
|
489
|
+
|
|
490
|
+
##### targetConnector
|
|
491
|
+
|
|
492
|
+
`MongoDbEntityStorageConnector`\<`U`\>
|
|
493
|
+
|
|
494
|
+
The connector holding the migrated data in a temporary collection.
|
|
495
|
+
|
|
496
|
+
##### options?
|
|
497
|
+
|
|
498
|
+
`IMigrationOptions`\<`T`, `U`\>
|
|
499
|
+
|
|
500
|
+
The options to control how the migration is finalized.
|
|
501
|
+
|
|
502
|
+
##### loggingComponentType?
|
|
503
|
+
|
|
504
|
+
`string`
|
|
505
|
+
|
|
506
|
+
The logging component type to use during finalization.
|
|
507
|
+
|
|
508
|
+
#### Returns
|
|
509
|
+
|
|
510
|
+
`Promise`\<`MongoDbEntityStorageConnector`\<`U`\>\>
|
|
511
|
+
|
|
512
|
+
The final connector using the original collection name with the new schema.
|
|
513
|
+
|
|
514
|
+
#### Implementation of
|
|
515
|
+
|
|
516
|
+
`IEntityStorageMigrationConnector.finalizeMigration`
|
|
517
|
+
|
|
518
|
+
***
|
|
519
|
+
|
|
520
|
+
### cleanupMigration() {#cleanupmigration}
|
|
521
|
+
|
|
522
|
+
> **cleanupMigration**\<`U`\>(`targetConnector`, `options?`, `loggingComponentType?`): `Promise`\<`void`\>
|
|
523
|
+
|
|
524
|
+
Cleanup a failed or aborted migration by dropping the temporary migration collection.
|
|
525
|
+
|
|
526
|
+
#### Type Parameters
|
|
527
|
+
|
|
528
|
+
##### U
|
|
529
|
+
|
|
530
|
+
`U`
|
|
531
|
+
|
|
532
|
+
#### Parameters
|
|
533
|
+
|
|
534
|
+
##### targetConnector
|
|
535
|
+
|
|
536
|
+
`IEntityStorageConnector`\<`U`\> \| `undefined`
|
|
537
|
+
|
|
538
|
+
The target connector to cleanup.
|
|
539
|
+
|
|
540
|
+
##### options?
|
|
541
|
+
|
|
542
|
+
`IMigrationOptions`\<`T`, `U`\>
|
|
543
|
+
|
|
544
|
+
The options to control how the migration is cleaned up.
|
|
545
|
+
|
|
546
|
+
##### loggingComponentType?
|
|
547
|
+
|
|
548
|
+
`string`
|
|
549
|
+
|
|
550
|
+
The optional component type to use for logging.
|
|
551
|
+
|
|
552
|
+
#### Returns
|
|
553
|
+
|
|
554
|
+
`Promise`\<`void`\>
|
|
555
|
+
|
|
556
|
+
A promise that resolves when the cleanup is complete.
|
|
557
|
+
|
|
558
|
+
#### Implementation of
|
|
559
|
+
|
|
560
|
+
`IEntityStorageMigrationConnector.cleanupMigration`
|
package/locales/en.json
CHANGED
|
@@ -19,7 +19,9 @@
|
|
|
19
19
|
"removeBatchFailed": "Unable to remove batch of entities",
|
|
20
20
|
"emptyFailed": "Unable to empty entity storage",
|
|
21
21
|
"countFailed": "Unable to count entities",
|
|
22
|
-
"teardownFailed": "Unable to teardown entity storage"
|
|
22
|
+
"teardownFailed": "Unable to teardown entity storage",
|
|
23
|
+
"getPartitionContextIdsFailed": "Unable to get partition context ids",
|
|
24
|
+
"finalizeMigrationFailedBootstrap": "Finalizing migration failed during bootstrap phase"
|
|
23
25
|
}
|
|
24
26
|
},
|
|
25
27
|
"health": {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@twin.org/entity-storage-connector-mongodb",
|
|
3
|
-
"version": "0.0.3-next.
|
|
3
|
+
"version": "0.0.3-next.14",
|
|
4
4
|
"description": "MongoDB connector for flexible document-oriented persistence.",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
"@twin.org/context": "next",
|
|
18
18
|
"@twin.org/core": "next",
|
|
19
19
|
"@twin.org/entity": "next",
|
|
20
|
-
"@twin.org/entity-storage-models": "0.0.3-next.
|
|
20
|
+
"@twin.org/entity-storage-models": "0.0.3-next.14",
|
|
21
21
|
"@twin.org/logging-models": "next",
|
|
22
22
|
"@twin.org/nameof": "next",
|
|
23
23
|
"mongodb": "7.2.0"
|