@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.
@@ -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
- ObjectHelper.propertyDelete(result, MongoDbEntityStorageConnector._PARTITION_KEY);
197
- return result;
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
- EntitySchemaHelper.validateEntity(entity, this.getSchema());
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 = entity[primaryKey.property];
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: ObjectHelper.removeEmptyProperties(finalEntity) }, { upsert: true });
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
- for (const entity of entities) {
250
- EntitySchemaHelper.validateEntity(entity, this.getSchema());
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(entities.map(entity => {
255
- const finalEntity = ObjectHelper.clone(entity);
264
+ await collection.bulkWrite(preparedEntities.map(prepared => {
256
265
  const filter = {
257
- [primaryKey.property]: entity[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: ObjectHelper.removeEmptyProperties(finalEntity)
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 finalConditions = {
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 entities = entitiesResult ?? [];
441
- for (const entity of entities) {
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
- ObjectHelper.propertyDelete(entity, MongoDbEntityStorageConnector._PARTITION_KEY);
437
+ entities[i] = EntityHelper.unPrepareEntity(entity, [
438
+ MongoDbEntityStorageConnector._PARTITION_KEY
439
+ ]);
444
440
  }
445
441
  return {
446
442
  entities,
447
- cursor: entities?.length === returnSize ? String(cursorValue + returnSize) : undefined
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 { IEntityStorageConnector } from "@twin.org/entity-storage-models";
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 IEntityStorageConnector<T> {
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
- - `IEntityStorageConnector`\<`T`\>
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
- `IEntityStorageConnector.bootstrap`
67
+ `IEntityStorageMigrationConnector.bootstrap`
68
68
 
69
69
  ***
70
70
 
@@ -90,7 +90,7 @@ Nothing.
90
90
 
91
91
  #### Implementation of
92
92
 
93
- `IEntityStorageConnector.stop`
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
- `IEntityStorageConnector.className`
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
- `IEntityStorageConnector.health`
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
- `IEntityStorageConnector.getSchema`
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
- `IEntityStorageConnector.get`
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
- `IEntityStorageConnector.set`
217
+ `IEntityStorageMigrationConnector.set`
218
218
 
219
219
  ***
220
220
 
@@ -240,7 +240,7 @@ Nothing.
240
240
 
241
241
  #### Implementation of
242
242
 
243
- `IEntityStorageConnector.setBatch`
243
+ `IEntityStorageMigrationConnector.setBatch`
244
244
 
245
245
  ***
246
246
 
@@ -258,7 +258,7 @@ Nothing.
258
258
 
259
259
  #### Implementation of
260
260
 
261
- `IEntityStorageConnector.empty`
261
+ `IEntityStorageMigrationConnector.empty`
262
262
 
263
263
  ***
264
264
 
@@ -290,7 +290,7 @@ Nothing.
290
290
 
291
291
  #### Implementation of
292
292
 
293
- `IEntityStorageConnector.remove`
293
+ `IEntityStorageMigrationConnector.remove`
294
294
 
295
295
  ***
296
296
 
@@ -316,7 +316,7 @@ Nothing.
316
316
 
317
317
  #### Implementation of
318
318
 
319
- `IEntityStorageConnector.removeBatch`
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
- `IEntityStorageConnector.teardown`
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
- `IEntityStorageConnector.query`
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
- `IEntityStorageConnector.count`
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.12",
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.12",
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"