@itwin/imodel-transformer 2.0.0-dev.6 → 2.0.0-dev.60
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +13 -27
- package/lib/cjs/Algo.js.map +1 -1
- package/lib/cjs/BigMap.d.ts.map +1 -1
- package/lib/cjs/BigMap.js.map +1 -1
- package/lib/cjs/BranchProvenanceInitializer.d.ts.map +1 -1
- package/lib/cjs/BranchProvenanceInitializer.js +34 -34
- package/lib/cjs/BranchProvenanceInitializer.js.map +1 -1
- package/lib/cjs/ChangedElementForest.d.ts +14 -0
- package/lib/cjs/ChangedElementForest.d.ts.map +1 -0
- package/lib/cjs/ChangedElementForest.js +86 -0
- package/lib/cjs/ChangedElementForest.js.map +1 -0
- package/lib/cjs/ChangesetScanner.d.ts +61 -0
- package/lib/cjs/ChangesetScanner.d.ts.map +1 -0
- package/lib/cjs/ChangesetScanner.js +104 -0
- package/lib/cjs/ChangesetScanner.js.map +1 -0
- package/lib/cjs/ECReferenceTypesCache.d.ts +0 -3
- package/lib/cjs/ECReferenceTypesCache.d.ts.map +1 -1
- package/lib/cjs/ECReferenceTypesCache.js +61 -104
- package/lib/cjs/ECReferenceTypesCache.js.map +1 -1
- package/lib/cjs/ECSqlReaderAsyncIterableIteratorAdapter.d.ts.map +1 -1
- package/lib/cjs/ECSqlReaderAsyncIterableIteratorAdapter.js.map +1 -1
- package/lib/cjs/ElementAspectCleanup.d.ts +16 -0
- package/lib/cjs/ElementAspectCleanup.d.ts.map +1 -0
- package/lib/cjs/ElementAspectCleanup.js +87 -0
- package/lib/cjs/ElementAspectCleanup.js.map +1 -0
- package/lib/cjs/ElementAspectExportCoordinator.d.ts +44 -0
- package/lib/cjs/ElementAspectExportCoordinator.d.ts.map +1 -0
- package/lib/cjs/ElementAspectExportCoordinator.js +148 -0
- package/lib/cjs/ElementAspectExportCoordinator.js.map +1 -0
- package/lib/cjs/ElementAspectExportProcessor.d.ts +44 -0
- package/lib/cjs/ElementAspectExportProcessor.d.ts.map +1 -0
- package/lib/cjs/ElementAspectExportProcessor.js +152 -0
- package/lib/cjs/ElementAspectExportProcessor.js.map +1 -0
- package/lib/cjs/ElementBulkDelete.d.ts +11 -0
- package/lib/cjs/ElementBulkDelete.d.ts.map +1 -0
- package/lib/cjs/ElementBulkDelete.js +55 -0
- package/lib/cjs/ElementBulkDelete.js.map +1 -0
- package/lib/cjs/EntityExistenceCache.d.ts +43 -0
- package/lib/cjs/EntityExistenceCache.d.ts.map +1 -0
- package/lib/cjs/EntityExistenceCache.js +91 -0
- package/lib/cjs/EntityExistenceCache.js.map +1 -0
- package/lib/cjs/EntityMap.d.ts.map +1 -1
- package/lib/cjs/EntityMap.js.map +1 -1
- package/lib/cjs/EntityUnifier.d.ts +8 -8
- package/lib/cjs/EntityUnifier.d.ts.map +1 -1
- package/lib/cjs/EntityUnifier.js +56 -32
- package/lib/cjs/EntityUnifier.js.map +1 -1
- package/lib/cjs/IModelCloneContext.d.ts +9 -3
- package/lib/cjs/IModelCloneContext.d.ts.map +1 -1
- package/lib/cjs/IModelCloneContext.js +66 -31
- package/lib/cjs/IModelCloneContext.js.map +1 -1
- package/lib/cjs/IModelExporter.d.ts +143 -30
- package/lib/cjs/IModelExporter.d.ts.map +1 -1
- package/lib/cjs/IModelExporter.js +893 -178
- package/lib/cjs/IModelExporter.js.map +1 -1
- package/lib/cjs/IModelImporter.d.ts +71 -30
- package/lib/cjs/IModelImporter.d.ts.map +1 -1
- package/lib/cjs/IModelImporter.js +269 -132
- package/lib/cjs/IModelImporter.js.map +1 -1
- package/lib/cjs/IModelTransformContext.d.ts +54 -0
- package/lib/cjs/IModelTransformContext.d.ts.map +1 -0
- package/lib/cjs/IModelTransformContext.js +3 -0
- package/lib/cjs/IModelTransformContext.js.map +1 -0
- package/lib/cjs/IModelTransformer.d.ts +130 -196
- package/lib/cjs/IModelTransformer.d.ts.map +1 -1
- package/lib/cjs/IModelTransformer.js +697 -1155
- package/lib/cjs/IModelTransformer.js.map +1 -1
- package/lib/cjs/IModelTransformerError.d.ts +84 -0
- package/lib/cjs/IModelTransformerError.d.ts.map +1 -0
- package/lib/cjs/IModelTransformerError.js +93 -0
- package/lib/cjs/IModelTransformerError.js.map +1 -0
- package/lib/cjs/ProvenanceManager.d.ts +177 -0
- package/lib/cjs/ProvenanceManager.d.ts.map +1 -0
- package/lib/cjs/ProvenanceManager.js +719 -0
- package/lib/cjs/ProvenanceManager.js.map +1 -0
- package/lib/cjs/SyncTypeResolver.d.ts +34 -0
- package/lib/cjs/SyncTypeResolver.d.ts.map +1 -0
- package/lib/cjs/SyncTypeResolver.js +98 -0
- package/lib/cjs/SyncTypeResolver.js.map +1 -0
- package/lib/cjs/TransformerLoggerCategory.d.ts +1 -1
- package/lib/cjs/TransformerLoggerCategory.js +1 -1
- package/lib/cjs/TransformerLoggerCategory.js.map +1 -1
- package/lib/cjs/TransformerPackageMetadata.d.ts +8 -0
- package/lib/cjs/TransformerPackageMetadata.d.ts.map +1 -0
- package/lib/cjs/TransformerPackageMetadata.js +47 -0
- package/lib/cjs/TransformerPackageMetadata.js.map +1 -0
- package/lib/cjs/imodel-transformer.d.ts +27 -0
- package/lib/cjs/imodel-transformer.d.ts.map +1 -1
- package/lib/cjs/imodel-transformer.js +61 -34
- package/lib/cjs/imodel-transformer.js.map +1 -1
- package/lib/cjs/schema-processing/DynamicSchemaUnionStrategy.d.ts +29 -0
- package/lib/cjs/schema-processing/DynamicSchemaUnionStrategy.d.ts.map +1 -0
- package/lib/cjs/schema-processing/DynamicSchemaUnionStrategy.js +212 -0
- package/lib/cjs/schema-processing/DynamicSchemaUnionStrategy.js.map +1 -0
- package/lib/cjs/schema-processing/SchemaProcessingCoordinator.d.ts +25 -0
- package/lib/cjs/schema-processing/SchemaProcessingCoordinator.d.ts.map +1 -0
- package/lib/cjs/schema-processing/SchemaProcessingCoordinator.js +196 -0
- package/lib/cjs/schema-processing/SchemaProcessingCoordinator.js.map +1 -0
- package/lib/cjs/schema-processing/SchemaProcessingStrategy.d.ts +59 -0
- package/lib/cjs/schema-processing/SchemaProcessingStrategy.d.ts.map +1 -0
- package/lib/cjs/schema-processing/SchemaProcessingStrategy.js +28 -0
- package/lib/cjs/schema-processing/SchemaProcessingStrategy.js.map +1 -0
- package/lib/cjs/schema-processing.d.ts +7 -0
- package/lib/cjs/schema-processing.d.ts.map +1 -0
- package/lib/cjs/schema-processing.js +27 -0
- package/lib/cjs/schema-processing.js.map +1 -0
- package/package.json +65 -43
- package/lib/cjs/DetachedExportElementAspectsStrategy.d.ts +0 -16
- package/lib/cjs/DetachedExportElementAspectsStrategy.d.ts.map +0 -1
- package/lib/cjs/DetachedExportElementAspectsStrategy.js +0 -106
- package/lib/cjs/DetachedExportElementAspectsStrategy.js.map +0 -1
- package/lib/cjs/ElementCascadingDeleter.d.ts +0 -22
- package/lib/cjs/ElementCascadingDeleter.d.ts.map +0 -1
- package/lib/cjs/ElementCascadingDeleter.js +0 -57
- package/lib/cjs/ElementCascadingDeleter.js.map +0 -1
- package/lib/cjs/ExportElementAspectsStrategy.d.ts +0 -35
- package/lib/cjs/ExportElementAspectsStrategy.d.ts.map +0 -1
- package/lib/cjs/ExportElementAspectsStrategy.js +0 -52
- package/lib/cjs/ExportElementAspectsStrategy.js.map +0 -1
- package/lib/cjs/ExportElementAspectsWithElementsStrategy.d.ts +0 -12
- package/lib/cjs/ExportElementAspectsWithElementsStrategy.d.ts.map +0 -1
- package/lib/cjs/ExportElementAspectsWithElementsStrategy.js +0 -47
- package/lib/cjs/ExportElementAspectsWithElementsStrategy.js.map +0 -1
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/*---------------------------------------------------------------------------------------------
|
|
3
|
+
* Copyright (c) Bentley Systems, Incorporated. All rights reserved.
|
|
4
|
+
* See LICENSE.md in the project root for license terms and full copyright notice.
|
|
5
|
+
*--------------------------------------------------------------------------------------------*/
|
|
6
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
+
exports.ElementAspectCleanup = void 0;
|
|
8
|
+
const core_backend_1 = require("@itwin/core-backend");
|
|
9
|
+
const core_common_1 = require("@itwin/core-common");
|
|
10
|
+
/** Deletes replaceable ElementAspects for target owners while preserving excluded classes and transformer provenance aspects.
|
|
11
|
+
* @internal
|
|
12
|
+
*/
|
|
13
|
+
class ElementAspectCleanup {
|
|
14
|
+
_targetDb;
|
|
15
|
+
_editTxn;
|
|
16
|
+
_deleteAspect;
|
|
17
|
+
constructor(_targetDb, _editTxn, _deleteAspect) {
|
|
18
|
+
this._targetDb = _targetDb;
|
|
19
|
+
this._editTxn = _editTxn;
|
|
20
|
+
this._deleteAspect = _deleteAspect;
|
|
21
|
+
}
|
|
22
|
+
/** Deletes replaceable unique and multi-aspects owned by the supplied target elements.
|
|
23
|
+
* Excluded classes and transformer provenance aspects for `provenanceScopeId` are preserved. Each deletion is performed through the configured callback and requires the target `EditTxn` to be active.
|
|
24
|
+
*/
|
|
25
|
+
async delete(targetElementIds, excludedElementAspectClassFullNames, provenanceScopeId, pageSize = core_backend_1.IModelDb.maxLimit - 1) {
|
|
26
|
+
if (!this._editTxn.isActive) {
|
|
27
|
+
throw new Error("The target EditTxn must be active when deleting ElementAspects.");
|
|
28
|
+
}
|
|
29
|
+
if (pageSize <= 0 || !Number.isSafeInteger(pageSize)) {
|
|
30
|
+
throw new Error("ElementAspect deletion pageSize must be a positive integer.");
|
|
31
|
+
}
|
|
32
|
+
if (targetElementIds.size === 0)
|
|
33
|
+
return;
|
|
34
|
+
const targetExcludedElementAspectClassFullNames = [
|
|
35
|
+
...excludedElementAspectClassFullNames,
|
|
36
|
+
].filter((classFullName) => this._targetDb.containsClass(classFullName));
|
|
37
|
+
for (const aspectClassFullName of [
|
|
38
|
+
core_backend_1.ElementUniqueAspect.classFullName,
|
|
39
|
+
core_backend_1.ElementMultiAspect.classFullName,
|
|
40
|
+
]) {
|
|
41
|
+
const { ecsql, params } = replaceableAspectQuery(aspectClassFullName, targetElementIds, targetExcludedElementAspectClassFullNames, provenanceScopeId, pageSize);
|
|
42
|
+
while (true) {
|
|
43
|
+
// Drain the full page before deleting: mutating a table while a reader is still
|
|
44
|
+
// scanning it is unsafe.
|
|
45
|
+
const candidateIds = [];
|
|
46
|
+
for await (const row of this._targetDb.createQueryReader(ecsql, params, { usePrimaryConn: true })) {
|
|
47
|
+
candidateIds.push(row.id);
|
|
48
|
+
}
|
|
49
|
+
if (candidateIds.length === 0)
|
|
50
|
+
break;
|
|
51
|
+
for (const aspectId of candidateIds) {
|
|
52
|
+
const aspect = this._targetDb.elements.getAspect(aspectId);
|
|
53
|
+
await this._deleteAspect(aspect);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
exports.ElementAspectCleanup = ElementAspectCleanup;
|
|
60
|
+
/** Builds the page query for aspects of `aspectClassFullName` owned by `elementIds`, skipping excluded classes and provenance aspects for `provenanceScopeId`. */
|
|
61
|
+
function replaceableAspectQuery(aspectClassFullName, elementIds, excludedClassFullNames, provenanceScopeId, pageSize) {
|
|
62
|
+
const params = new core_common_1.QueryBinder().bindIdSet("elementIds", elementIds);
|
|
63
|
+
const conditions = [];
|
|
64
|
+
if (excludedClassFullNames.length > 0) {
|
|
65
|
+
conditions.push(`aspect.ECClassId IS NOT (${excludedClassFullNames.join(", ")})`);
|
|
66
|
+
}
|
|
67
|
+
// ExternalSourceAspect is a multi-aspect, so provenance aspects are only found
|
|
68
|
+
// when querying ElementMultiAspect. For each candidate, check whether it is a
|
|
69
|
+
// provenance aspect rather than listing every provenance aspect on each page.
|
|
70
|
+
if (provenanceScopeId !== undefined &&
|
|
71
|
+
aspectClassFullName === core_backend_1.ElementMultiAspect.classFullName) {
|
|
72
|
+
params.bindId("provenanceScopeId", provenanceScopeId);
|
|
73
|
+
conditions.push(`NOT EXISTS (
|
|
74
|
+
SELECT 1 FROM ${core_backend_1.ExternalSourceAspect.classFullName} esa
|
|
75
|
+
WHERE esa.ECInstanceId = aspect.ECInstanceId
|
|
76
|
+
AND (esa.Element.Id = :provenanceScopeId OR esa.Scope.Id = :provenanceScopeId)
|
|
77
|
+
)`);
|
|
78
|
+
}
|
|
79
|
+
const whereClause = conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : "";
|
|
80
|
+
const ecsql = `SELECT aspect.ECInstanceId as id
|
|
81
|
+
FROM ${aspectClassFullName} aspect
|
|
82
|
+
INNER JOIN IdSet(:elementIds) ids ON ids.id = aspect.Element.Id
|
|
83
|
+
${whereClause}
|
|
84
|
+
LIMIT ${pageSize}`;
|
|
85
|
+
return { ecsql, params };
|
|
86
|
+
}
|
|
87
|
+
//# sourceMappingURL=ElementAspectCleanup.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ElementAspectCleanup.js","sourceRoot":"","sources":["../../src/ElementAspectCleanup.ts"],"names":[],"mappings":"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","sourcesContent":["/*---------------------------------------------------------------------------------------------\n * Copyright (c) Bentley Systems, Incorporated. All rights reserved.\n * See LICENSE.md in the project root for license terms and full copyright notice.\n *--------------------------------------------------------------------------------------------*/\n\nimport {\n EditTxn,\n ElementAspect,\n ElementMultiAspect,\n ElementUniqueAspect,\n ExternalSourceAspect,\n IModelDb,\n} from \"@itwin/core-backend\";\nimport { Id64String } from \"@itwin/core-bentley\";\nimport { QueryBinder } from \"@itwin/core-common\";\n\n/** Deletes replaceable ElementAspects for target owners while preserving excluded classes and transformer provenance aspects.\n * @internal\n */\nexport class ElementAspectCleanup {\n public constructor(\n private readonly _targetDb: IModelDb,\n private readonly _editTxn: EditTxn,\n private readonly _deleteAspect: (aspect: ElementAspect) => Promise<void>\n ) {}\n\n /** Deletes replaceable unique and multi-aspects owned by the supplied target elements.\n * Excluded classes and transformer provenance aspects for `provenanceScopeId` are preserved. Each deletion is performed through the configured callback and requires the target `EditTxn` to be active.\n */\n public async delete(\n targetElementIds: ReadonlySet<Id64String>,\n excludedElementAspectClassFullNames: ReadonlySet<string>,\n provenanceScopeId?: Id64String,\n pageSize = IModelDb.maxLimit - 1\n ): Promise<void> {\n if (!this._editTxn.isActive) {\n throw new Error(\n \"The target EditTxn must be active when deleting ElementAspects.\"\n );\n }\n if (pageSize <= 0 || !Number.isSafeInteger(pageSize)) {\n throw new Error(\n \"ElementAspect deletion pageSize must be a positive integer.\"\n );\n }\n if (targetElementIds.size === 0) return;\n\n const targetExcludedElementAspectClassFullNames = [\n ...excludedElementAspectClassFullNames,\n ].filter((classFullName) => this._targetDb.containsClass(classFullName));\n for (const aspectClassFullName of [\n ElementUniqueAspect.classFullName,\n ElementMultiAspect.classFullName,\n ]) {\n const { ecsql, params } = replaceableAspectQuery(\n aspectClassFullName,\n targetElementIds,\n targetExcludedElementAspectClassFullNames,\n provenanceScopeId,\n pageSize\n );\n while (true) {\n // Drain the full page before deleting: mutating a table while a reader is still\n // scanning it is unsafe.\n const candidateIds: Id64String[] = [];\n for await (const row of this._targetDb.createQueryReader(\n ecsql,\n params,\n { usePrimaryConn: true }\n )) {\n candidateIds.push(row.id);\n }\n if (candidateIds.length === 0) break;\n\n for (const aspectId of candidateIds) {\n const aspect = this._targetDb.elements.getAspect(aspectId);\n await this._deleteAspect(aspect);\n }\n }\n }\n }\n}\n\n/** Builds the page query for aspects of `aspectClassFullName` owned by `elementIds`, skipping excluded classes and provenance aspects for `provenanceScopeId`. */\nfunction replaceableAspectQuery(\n aspectClassFullName: string,\n elementIds: ReadonlySet<Id64String>,\n excludedClassFullNames: readonly string[],\n provenanceScopeId: Id64String | undefined,\n pageSize: number\n): { ecsql: string; params: QueryBinder } {\n const params = new QueryBinder().bindIdSet(\"elementIds\", elementIds);\n const conditions: string[] = [];\n if (excludedClassFullNames.length > 0) {\n conditions.push(\n `aspect.ECClassId IS NOT (${excludedClassFullNames.join(\", \")})`\n );\n }\n // ExternalSourceAspect is a multi-aspect, so provenance aspects are only found\n // when querying ElementMultiAspect. For each candidate, check whether it is a\n // provenance aspect rather than listing every provenance aspect on each page.\n if (\n provenanceScopeId !== undefined &&\n aspectClassFullName === ElementMultiAspect.classFullName\n ) {\n params.bindId(\"provenanceScopeId\", provenanceScopeId);\n conditions.push(`NOT EXISTS (\n SELECT 1 FROM ${ExternalSourceAspect.classFullName} esa\n WHERE esa.ECInstanceId = aspect.ECInstanceId\n AND (esa.Element.Id = :provenanceScopeId OR esa.Scope.Id = :provenanceScopeId)\n )`);\n }\n const whereClause =\n conditions.length > 0 ? `WHERE ${conditions.join(\" AND \")}` : \"\";\n const ecsql = `SELECT aspect.ECInstanceId as id\n FROM ${aspectClassFullName} aspect\n INNER JOIN IdSet(:elementIds) ids ON ids.id = aspect.Element.Id\n ${whereClause}\n LIMIT ${pageSize}`;\n return { ecsql, params };\n}\n"]}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { Id64String } from "@itwin/core-bentley";
|
|
2
|
+
export type ElementAspectExportPreparation = (excludedElementAspectClassFullNames: ReadonlySet<string>, ownerElementIds: ReadonlySet<Id64String>) => Promise<void>;
|
|
3
|
+
/** Coordinates scoped batches of accepted ElementAspect owners, including preparation before aspect export.
|
|
4
|
+
* @internal
|
|
5
|
+
*/
|
|
6
|
+
export declare class ElementAspectExportCoordinator {
|
|
7
|
+
private readonly _defaultBatchSize;
|
|
8
|
+
private readonly _excludedClassFullNames;
|
|
9
|
+
private readonly _exportAspects;
|
|
10
|
+
private readonly _resetCaches;
|
|
11
|
+
private readonly _acceptedOwnerIds;
|
|
12
|
+
private readonly _processedOwnerIds;
|
|
13
|
+
private _batchSize;
|
|
14
|
+
private _depth;
|
|
15
|
+
private _prepare?;
|
|
16
|
+
constructor(_defaultBatchSize: number, _excludedClassFullNames: () => ReadonlySet<string>, _exportAspects: (ownerElementIds: ReadonlySet<Id64String>) => Promise<void>, _resetCaches?: () => void);
|
|
17
|
+
/** Whether an accepted-owner collection scope is active. */
|
|
18
|
+
get isActive(): boolean;
|
|
19
|
+
/** Sets the callback that prepares each accepted-owner group before its aspects are exported. */
|
|
20
|
+
setPreparation(prepare: ElementAspectExportPreparation): void;
|
|
21
|
+
/** Begins an accepted-owner collection scope.
|
|
22
|
+
* Nested scopes share the outer scope's state and batch size.
|
|
23
|
+
*/
|
|
24
|
+
begin(batchSize?: number): void;
|
|
25
|
+
/** Ends an accepted-owner collection scope.
|
|
26
|
+
* Ending the outermost scope exports pending owners and resets all scope state.
|
|
27
|
+
*/
|
|
28
|
+
end(): Promise<void>;
|
|
29
|
+
/** Discards the active scope and its pending owner state without exporting it. */
|
|
30
|
+
abort(): void;
|
|
31
|
+
/** Runs element export within an accepted-owner collection scope.
|
|
32
|
+
* A scope created by this call is aborted if element export fails; an existing outer scope remains responsible for its own failure handling.
|
|
33
|
+
*/
|
|
34
|
+
run(exportElements: () => Promise<void>): Promise<void>;
|
|
35
|
+
/** Adds an accepted source owner to the current group and exports the group when it reaches the configured batch size. */
|
|
36
|
+
addAcceptedOwner(elementId: Id64String): Promise<void>;
|
|
37
|
+
/** Runs preparation and aspect export for an explicit owner set in bounded groups. */
|
|
38
|
+
exportOwners(ownerElementIds: ReadonlySet<Id64String>): Promise<void>;
|
|
39
|
+
private exportOwnerBatch;
|
|
40
|
+
private flush;
|
|
41
|
+
private resetScopeState;
|
|
42
|
+
private reset;
|
|
43
|
+
}
|
|
44
|
+
//# sourceMappingURL=ElementAspectExportCoordinator.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ElementAspectExportCoordinator.d.ts","sourceRoot":"","sources":["../../src/ElementAspectExportCoordinator.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;AAEjD,MAAM,MAAM,8BAA8B,GAAG,CAC3C,mCAAmC,EAAE,WAAW,CAAC,MAAM,CAAC,EACxD,eAAe,EAAE,WAAW,CAAC,UAAU,CAAC,KACrC,OAAO,CAAC,IAAI,CAAC,CAAC;AAEnB;;GAEG;AACH,qBAAa,8BAA8B;IAQvC,OAAO,CAAC,QAAQ,CAAC,iBAAiB;IAClC,OAAO,CAAC,QAAQ,CAAC,uBAAuB;IACxC,OAAO,CAAC,QAAQ,CAAC,cAAc;IAG/B,OAAO,CAAC,QAAQ,CAAC,YAAY;IAZ/B,OAAO,CAAC,QAAQ,CAAC,iBAAiB,CAAyB;IAC3D,OAAO,CAAC,QAAQ,CAAC,kBAAkB,CAAyB;IAC5D,OAAO,CAAC,UAAU,CAAS;IAC3B,OAAO,CAAC,MAAM,CAAK;IACnB,OAAO,CAAC,QAAQ,CAAC,CAAiC;IAElD,YACmB,iBAAiB,EAAE,MAAM,EACzB,uBAAuB,EAAE,MAAM,WAAW,CAAC,MAAM,CAAC,EAClD,cAAc,EAAE,CAC/B,eAAe,EAAE,WAAW,CAAC,UAAU,CAAC,KACrC,OAAO,CAAC,IAAI,CAAC,EACD,YAAY,GAAE,MAAM,IAAe,EAGrD;IAED,4DAA4D;IAC5D,IAAW,QAAQ,IAAI,OAAO,CAE7B;IAED,iGAAiG;IAC1F,cAAc,CAAC,OAAO,EAAE,8BAA8B,GAAG,IAAI,CAEnE;IAED;;OAEG;IACI,KAAK,CAAC,SAAS,SAAyB,GAAG,IAAI,CAOrD;IAED;;OAEG;IACU,GAAG,IAAI,OAAO,CAAC,IAAI,CAAC,CAehC;IAED,kFAAkF;IAC3E,KAAK,IAAI,IAAI,CAEnB;IAED;;OAEG;IACU,GAAG,CAAC,cAAc,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,CAcnE;IAED,0HAA0H;IAC7G,gBAAgB,CAAC,SAAS,EAAE,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC,CAOlE;IAED,sFAAsF;IACzE,YAAY,CACvB,eAAe,EAAE,WAAW,CAAC,UAAU,CAAC,GACvC,OAAO,CAAC,IAAI,CAAC,CA8Bf;YAEa,gBAAgB;YAOhB,KAAK;IAMnB,OAAO,CAAC,eAAe;IAKvB,OAAO,CAAC,KAAK;CAKd"}
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/*---------------------------------------------------------------------------------------------
|
|
3
|
+
* Copyright (c) Bentley Systems, Incorporated. All rights reserved.
|
|
4
|
+
* See LICENSE.md in the project root for license terms and full copyright notice.
|
|
5
|
+
*--------------------------------------------------------------------------------------------*/
|
|
6
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
+
exports.ElementAspectExportCoordinator = void 0;
|
|
8
|
+
/** Coordinates scoped batches of accepted ElementAspect owners, including preparation before aspect export.
|
|
9
|
+
* @internal
|
|
10
|
+
*/
|
|
11
|
+
class ElementAspectExportCoordinator {
|
|
12
|
+
_defaultBatchSize;
|
|
13
|
+
_excludedClassFullNames;
|
|
14
|
+
_exportAspects;
|
|
15
|
+
_resetCaches;
|
|
16
|
+
_acceptedOwnerIds = new Set();
|
|
17
|
+
_processedOwnerIds = new Set();
|
|
18
|
+
_batchSize;
|
|
19
|
+
_depth = 0;
|
|
20
|
+
_prepare;
|
|
21
|
+
constructor(_defaultBatchSize, _excludedClassFullNames, _exportAspects, _resetCaches = () => { }) {
|
|
22
|
+
this._defaultBatchSize = _defaultBatchSize;
|
|
23
|
+
this._excludedClassFullNames = _excludedClassFullNames;
|
|
24
|
+
this._exportAspects = _exportAspects;
|
|
25
|
+
this._resetCaches = _resetCaches;
|
|
26
|
+
this._batchSize = _defaultBatchSize;
|
|
27
|
+
}
|
|
28
|
+
/** Whether an accepted-owner collection scope is active. */
|
|
29
|
+
get isActive() {
|
|
30
|
+
return this._depth > 0;
|
|
31
|
+
}
|
|
32
|
+
/** Sets the callback that prepares each accepted-owner group before its aspects are exported. */
|
|
33
|
+
setPreparation(prepare) {
|
|
34
|
+
this._prepare = prepare;
|
|
35
|
+
}
|
|
36
|
+
/** Begins an accepted-owner collection scope.
|
|
37
|
+
* Nested scopes share the outer scope's state and batch size.
|
|
38
|
+
*/
|
|
39
|
+
begin(batchSize = this._defaultBatchSize) {
|
|
40
|
+
if (this._depth === 0) {
|
|
41
|
+
this.resetScopeState();
|
|
42
|
+
this._resetCaches();
|
|
43
|
+
this._batchSize = batchSize;
|
|
44
|
+
}
|
|
45
|
+
this._depth++;
|
|
46
|
+
}
|
|
47
|
+
/** Ends an accepted-owner collection scope.
|
|
48
|
+
* Ending the outermost scope exports pending owners and resets all scope state.
|
|
49
|
+
*/
|
|
50
|
+
async end() {
|
|
51
|
+
if (this._depth === 0) {
|
|
52
|
+
throw new Error("No scoped element export is active.");
|
|
53
|
+
}
|
|
54
|
+
this._depth--;
|
|
55
|
+
if (this._depth > 0)
|
|
56
|
+
return;
|
|
57
|
+
try {
|
|
58
|
+
if (this._acceptedOwnerIds.size > 0) {
|
|
59
|
+
await this.flush();
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
finally {
|
|
63
|
+
this.reset();
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
/** Discards the active scope and its pending owner state without exporting it. */
|
|
67
|
+
abort() {
|
|
68
|
+
this.reset();
|
|
69
|
+
}
|
|
70
|
+
/** Runs element export within an accepted-owner collection scope.
|
|
71
|
+
* A scope created by this call is aborted if element export fails; an existing outer scope remains responsible for its own failure handling.
|
|
72
|
+
*/
|
|
73
|
+
async run(exportElements) {
|
|
74
|
+
if (this.isActive) {
|
|
75
|
+
await exportElements();
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
this.begin();
|
|
79
|
+
try {
|
|
80
|
+
await exportElements();
|
|
81
|
+
}
|
|
82
|
+
catch (error) {
|
|
83
|
+
this.abort();
|
|
84
|
+
throw error;
|
|
85
|
+
}
|
|
86
|
+
await this.end();
|
|
87
|
+
}
|
|
88
|
+
/** Adds an accepted source owner to the current group and exports the group when it reaches the configured batch size. */
|
|
89
|
+
async addAcceptedOwner(elementId) {
|
|
90
|
+
if (!this.isActive || this._processedOwnerIds.has(elementId))
|
|
91
|
+
return;
|
|
92
|
+
this._acceptedOwnerIds.add(elementId);
|
|
93
|
+
if (this._acceptedOwnerIds.size >= this._batchSize) {
|
|
94
|
+
await this.flush();
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
/** Runs preparation and aspect export for an explicit owner set in bounded groups. */
|
|
98
|
+
async exportOwners(ownerElementIds) {
|
|
99
|
+
let ownerIds = ownerElementIds;
|
|
100
|
+
if (this.isActive && ownerElementIds.size > 0) {
|
|
101
|
+
const unprocessedOwnerIds = new Set();
|
|
102
|
+
for (const elementId of ownerElementIds) {
|
|
103
|
+
if (!this._processedOwnerIds.has(elementId)) {
|
|
104
|
+
unprocessedOwnerIds.add(elementId);
|
|
105
|
+
this._processedOwnerIds.add(elementId);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
if (unprocessedOwnerIds.size === 0)
|
|
109
|
+
return;
|
|
110
|
+
ownerIds = unprocessedOwnerIds;
|
|
111
|
+
}
|
|
112
|
+
if (ownerIds.size <= this._defaultBatchSize) {
|
|
113
|
+
await this.exportOwnerBatch(ownerIds);
|
|
114
|
+
return;
|
|
115
|
+
}
|
|
116
|
+
let ownerBatch = new Set();
|
|
117
|
+
for (const elementId of ownerIds) {
|
|
118
|
+
ownerBatch.add(elementId);
|
|
119
|
+
if (ownerBatch.size === this._defaultBatchSize) {
|
|
120
|
+
await this.exportOwnerBatch(ownerBatch);
|
|
121
|
+
ownerBatch = new Set();
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
if (ownerBatch.size > 0) {
|
|
125
|
+
await this.exportOwnerBatch(ownerBatch);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
async exportOwnerBatch(ownerElementIds) {
|
|
129
|
+
await this._prepare?.(this._excludedClassFullNames(), ownerElementIds);
|
|
130
|
+
await this._exportAspects(ownerElementIds);
|
|
131
|
+
}
|
|
132
|
+
async flush() {
|
|
133
|
+
const ownerElementIds = new Set(this._acceptedOwnerIds);
|
|
134
|
+
this._acceptedOwnerIds.clear();
|
|
135
|
+
await this.exportOwners(ownerElementIds);
|
|
136
|
+
}
|
|
137
|
+
resetScopeState() {
|
|
138
|
+
this._acceptedOwnerIds.clear();
|
|
139
|
+
this._processedOwnerIds.clear();
|
|
140
|
+
}
|
|
141
|
+
reset() {
|
|
142
|
+
this._depth = 0;
|
|
143
|
+
this.resetScopeState();
|
|
144
|
+
this._batchSize = this._defaultBatchSize;
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
exports.ElementAspectExportCoordinator = ElementAspectExportCoordinator;
|
|
148
|
+
//# sourceMappingURL=ElementAspectExportCoordinator.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ElementAspectExportCoordinator.js","sourceRoot":"","sources":["../../src/ElementAspectExportCoordinator.ts"],"names":[],"mappings":"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","sourcesContent":["/*---------------------------------------------------------------------------------------------\n * Copyright (c) Bentley Systems, Incorporated. All rights reserved.\n * See LICENSE.md in the project root for license terms and full copyright notice.\n *--------------------------------------------------------------------------------------------*/\n\nimport { Id64String } from \"@itwin/core-bentley\";\n\nexport type ElementAspectExportPreparation = (\n excludedElementAspectClassFullNames: ReadonlySet<string>,\n ownerElementIds: ReadonlySet<Id64String>\n) => Promise<void>;\n\n/** Coordinates scoped batches of accepted ElementAspect owners, including preparation before aspect export.\n * @internal\n */\nexport class ElementAspectExportCoordinator {\n private readonly _acceptedOwnerIds = new Set<Id64String>();\n private readonly _processedOwnerIds = new Set<Id64String>();\n private _batchSize: number;\n private _depth = 0;\n private _prepare?: ElementAspectExportPreparation;\n\n public constructor(\n private readonly _defaultBatchSize: number,\n private readonly _excludedClassFullNames: () => ReadonlySet<string>,\n private readonly _exportAspects: (\n ownerElementIds: ReadonlySet<Id64String>\n ) => Promise<void>,\n private readonly _resetCaches: () => void = () => {}\n ) {\n this._batchSize = _defaultBatchSize;\n }\n\n /** Whether an accepted-owner collection scope is active. */\n public get isActive(): boolean {\n return this._depth > 0;\n }\n\n /** Sets the callback that prepares each accepted-owner group before its aspects are exported. */\n public setPreparation(prepare: ElementAspectExportPreparation): void {\n this._prepare = prepare;\n }\n\n /** Begins an accepted-owner collection scope.\n * Nested scopes share the outer scope's state and batch size.\n */\n public begin(batchSize = this._defaultBatchSize): void {\n if (this._depth === 0) {\n this.resetScopeState();\n this._resetCaches();\n this._batchSize = batchSize;\n }\n this._depth++;\n }\n\n /** Ends an accepted-owner collection scope.\n * Ending the outermost scope exports pending owners and resets all scope state.\n */\n public async end(): Promise<void> {\n if (this._depth === 0) {\n throw new Error(\"No scoped element export is active.\");\n }\n\n this._depth--;\n if (this._depth > 0) return;\n\n try {\n if (this._acceptedOwnerIds.size > 0) {\n await this.flush();\n }\n } finally {\n this.reset();\n }\n }\n\n /** Discards the active scope and its pending owner state without exporting it. */\n public abort(): void {\n this.reset();\n }\n\n /** Runs element export within an accepted-owner collection scope.\n * A scope created by this call is aborted if element export fails; an existing outer scope remains responsible for its own failure handling.\n */\n public async run(exportElements: () => Promise<void>): Promise<void> {\n if (this.isActive) {\n await exportElements();\n return;\n }\n\n this.begin();\n try {\n await exportElements();\n } catch (error) {\n this.abort();\n throw error;\n }\n await this.end();\n }\n\n /** Adds an accepted source owner to the current group and exports the group when it reaches the configured batch size. */\n public async addAcceptedOwner(elementId: Id64String): Promise<void> {\n if (!this.isActive || this._processedOwnerIds.has(elementId)) return;\n\n this._acceptedOwnerIds.add(elementId);\n if (this._acceptedOwnerIds.size >= this._batchSize) {\n await this.flush();\n }\n }\n\n /** Runs preparation and aspect export for an explicit owner set in bounded groups. */\n public async exportOwners(\n ownerElementIds: ReadonlySet<Id64String>\n ): Promise<void> {\n let ownerIds = ownerElementIds;\n if (this.isActive && ownerElementIds.size > 0) {\n const unprocessedOwnerIds = new Set<Id64String>();\n for (const elementId of ownerElementIds) {\n if (!this._processedOwnerIds.has(elementId)) {\n unprocessedOwnerIds.add(elementId);\n this._processedOwnerIds.add(elementId);\n }\n }\n if (unprocessedOwnerIds.size === 0) return;\n ownerIds = unprocessedOwnerIds;\n }\n\n if (ownerIds.size <= this._defaultBatchSize) {\n await this.exportOwnerBatch(ownerIds);\n return;\n }\n\n let ownerBatch = new Set<Id64String>();\n for (const elementId of ownerIds) {\n ownerBatch.add(elementId);\n if (ownerBatch.size === this._defaultBatchSize) {\n await this.exportOwnerBatch(ownerBatch);\n ownerBatch = new Set<Id64String>();\n }\n }\n if (ownerBatch.size > 0) {\n await this.exportOwnerBatch(ownerBatch);\n }\n }\n\n private async exportOwnerBatch(\n ownerElementIds: ReadonlySet<Id64String>\n ): Promise<void> {\n await this._prepare?.(this._excludedClassFullNames(), ownerElementIds);\n await this._exportAspects(ownerElementIds);\n }\n\n private async flush(): Promise<void> {\n const ownerElementIds = new Set(this._acceptedOwnerIds);\n this._acceptedOwnerIds.clear();\n await this.exportOwners(ownerElementIds);\n }\n\n private resetScopeState(): void {\n this._acceptedOwnerIds.clear();\n this._processedOwnerIds.clear();\n }\n\n private reset(): void {\n this._depth = 0;\n this.resetScopeState();\n this._batchSize = this._defaultBatchSize;\n }\n}\n"]}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { ElementAspect, ElementMultiAspect, ElementUniqueAspect, IModelDb } from "@itwin/core-backend";
|
|
2
|
+
import { Id64String } from "@itwin/core-bentley";
|
|
3
|
+
interface AspectChanges {
|
|
4
|
+
readonly insertIds: ReadonlySet<Id64String>;
|
|
5
|
+
readonly updateIds: ReadonlySet<Id64String>;
|
|
6
|
+
}
|
|
7
|
+
export interface ElementAspectExportProcessorHandler {
|
|
8
|
+
shouldExportElementAspect(aspect: ElementAspect): Promise<boolean>;
|
|
9
|
+
onExportElementUniqueAspect(uniqueAspect: ElementUniqueAspect, isUpdate?: boolean | undefined): Promise<void>;
|
|
10
|
+
onExportElementMultiAspects(multiAspects: ElementMultiAspect[]): Promise<void>;
|
|
11
|
+
trackProgress: () => Promise<void>;
|
|
12
|
+
}
|
|
13
|
+
/** Queries and exports ElementAspects for accepted source owners, applying class and handler filters and grouping multi-aspects by owner.
|
|
14
|
+
* @internal
|
|
15
|
+
*/
|
|
16
|
+
export declare class ElementAspectExportProcessor {
|
|
17
|
+
/** ElementAspect classes excluded from source queries. */
|
|
18
|
+
private readonly _excludedElementAspectClassFullNames;
|
|
19
|
+
private _hasExportableAspects;
|
|
20
|
+
private _expandedExcludedElementAspectClassFullNames;
|
|
21
|
+
private _aspectChanges;
|
|
22
|
+
/** ElementAspect class names excluded from source queries. */
|
|
23
|
+
get excludedElementAspectClassFullNames(): ReadonlySet<string>;
|
|
24
|
+
private readonly _sourceDb;
|
|
25
|
+
private readonly _handler;
|
|
26
|
+
constructor(sourceDb: IModelDb, handler: ElementAspectExportProcessorHandler);
|
|
27
|
+
/** Exports ElementAspects owned by the supplied elements.
|
|
28
|
+
* Multi-aspects are emitted in one callback group per owner.
|
|
29
|
+
*/
|
|
30
|
+
exportAllElementAspects(ownerElementIds: ReadonlySet<Id64String>): Promise<void>;
|
|
31
|
+
/** Sets the aspect changes used to distinguish inserted and updated unique aspects during change export. */
|
|
32
|
+
setAspectChanges(aspectChanges?: AspectChanges): void;
|
|
33
|
+
/** Clears source schema and content caches at an outer export-scope boundary. */
|
|
34
|
+
resetCaches(): void;
|
|
35
|
+
/** Excludes an ElementAspect class from subsequent queries and export callbacks. */
|
|
36
|
+
excludeElementAspectClass(classFullName: string): void;
|
|
37
|
+
private getExcludedClassFilter;
|
|
38
|
+
private getHasExportableAspects;
|
|
39
|
+
private queryHasExportableAspects;
|
|
40
|
+
private getExpandedExcludedElementAspectClassFullNames;
|
|
41
|
+
private queryExpandedExcludedElementAspectClassFullNames;
|
|
42
|
+
}
|
|
43
|
+
export {};
|
|
44
|
+
//# sourceMappingURL=ElementAspectExportProcessor.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ElementAspectExportProcessor.d.ts","sourceRoot":"","sources":["../../src/ElementAspectExportProcessor.ts"],"names":[],"mappings":"AAKA,OAAO,EACL,aAAa,EACb,kBAAkB,EAClB,mBAAmB,EACnB,QAAQ,EACT,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;AAIjD,UAAU,aAAa;IACrB,QAAQ,CAAC,SAAS,EAAE,WAAW,CAAC,UAAU,CAAC,CAAC;IAC5C,QAAQ,CAAC,SAAS,EAAE,WAAW,CAAC,UAAU,CAAC,CAAC;CAC7C;AAED,MAAM,WAAW,mCAAmC;IAClD,yBAAyB,CAAC,MAAM,EAAE,aAAa,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IACnE,2BAA2B,CACzB,YAAY,EAAE,mBAAmB,EACjC,QAAQ,CAAC,EAAE,OAAO,GAAG,SAAS,GAC7B,OAAO,CAAC,IAAI,CAAC,CAAC;IACjB,2BAA2B,CACzB,YAAY,EAAE,kBAAkB,EAAE,GACjC,OAAO,CAAC,IAAI,CAAC,CAAC;IACjB,aAAa,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;CACpC;AAED;;GAEG;AACH,qBAAa,4BAA4B;IACvC,0DAA0D;IAC1D,OAAO,CAAC,QAAQ,CAAC,oCAAoC,CAAqB;IAC1E,OAAO,CAAC,qBAAqB,CAA+B;IAC5D,OAAO,CAAC,4CAA4C,CAEtC;IACd,OAAO,CAAC,cAAc,CAA4B;IAElD,8DAA8D;IAC9D,IAAW,mCAAmC,IAAI,WAAW,CAAC,MAAM,CAAC,CAEpE;IACD,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAW;IACrC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAsC;IAE/D,YACE,QAAQ,EAAE,QAAQ,EAClB,OAAO,EAAE,mCAAmC,EAI7C;IAED;;OAEG;IACU,uBAAuB,CAClC,eAAe,EAAE,WAAW,CAAC,UAAU,CAAC,GACvC,OAAO,CAAC,IAAI,CAAC,CAkDf;IAED,4GAA4G;IACrG,gBAAgB,CAAC,aAAa,CAAC,EAAE,aAAa,GAAG,IAAI,CAE3D;IAED,iFAAiF;IAC1E,WAAW,IAAI,IAAI,CAGzB;IAED,oFAAoF;IAC7E,yBAAyB,CAAC,aAAa,EAAE,MAAM,GAAG,IAAI,CAM5D;IAED,OAAO,CAAC,sBAAsB;YAkBhB,uBAAuB;YAKvB,yBAAyB;YAuBzB,8CAA8C;YAQ9C,gDAAgD;CAmB/D"}
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/*---------------------------------------------------------------------------------------------
|
|
3
|
+
* Copyright (c) Bentley Systems, Incorporated. All rights reserved.
|
|
4
|
+
* See LICENSE.md in the project root for license terms and full copyright notice.
|
|
5
|
+
*--------------------------------------------------------------------------------------------*/
|
|
6
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
+
exports.ElementAspectExportProcessor = void 0;
|
|
8
|
+
const core_backend_1 = require("@itwin/core-backend");
|
|
9
|
+
const ECSqlReaderAsyncIterableIteratorAdapter_1 = require("./ECSqlReaderAsyncIterableIteratorAdapter");
|
|
10
|
+
const core_common_1 = require("@itwin/core-common");
|
|
11
|
+
/** Queries and exports ElementAspects for accepted source owners, applying class and handler filters and grouping multi-aspects by owner.
|
|
12
|
+
* @internal
|
|
13
|
+
*/
|
|
14
|
+
class ElementAspectExportProcessor {
|
|
15
|
+
/** ElementAspect classes excluded from source queries. */
|
|
16
|
+
_excludedElementAspectClassFullNames = new Set();
|
|
17
|
+
_hasExportableAspects;
|
|
18
|
+
_expandedExcludedElementAspectClassFullNames;
|
|
19
|
+
_aspectChanges;
|
|
20
|
+
/** ElementAspect class names excluded from source queries. */
|
|
21
|
+
get excludedElementAspectClassFullNames() {
|
|
22
|
+
return this._excludedElementAspectClassFullNames;
|
|
23
|
+
}
|
|
24
|
+
_sourceDb;
|
|
25
|
+
_handler;
|
|
26
|
+
constructor(sourceDb, handler) {
|
|
27
|
+
this._sourceDb = sourceDb;
|
|
28
|
+
this._handler = handler;
|
|
29
|
+
}
|
|
30
|
+
/** Exports ElementAspects owned by the supplied elements.
|
|
31
|
+
* Multi-aspects are emitted in one callback group per owner.
|
|
32
|
+
*/
|
|
33
|
+
async exportAllElementAspects(ownerElementIds) {
|
|
34
|
+
if (ownerElementIds.size === 0 || !(await this.getHasExportableAspects()))
|
|
35
|
+
return;
|
|
36
|
+
let multiAspectOwnerId;
|
|
37
|
+
let multiAspects = [];
|
|
38
|
+
const exportMultiAspects = async () => {
|
|
39
|
+
if (multiAspects.length === 0)
|
|
40
|
+
return;
|
|
41
|
+
await this._handler.onExportElementMultiAspects(multiAspects);
|
|
42
|
+
await this._handler.trackProgress();
|
|
43
|
+
multiAspects = [];
|
|
44
|
+
multiAspectOwnerId = undefined;
|
|
45
|
+
};
|
|
46
|
+
const excludedAspectClassFullNames = await this.getExpandedExcludedElementAspectClassFullNames();
|
|
47
|
+
for await (const aspect of this._sourceDb.elements.queryAspects({
|
|
48
|
+
elementIds: new Set(ownerElementIds),
|
|
49
|
+
excludedAspectClassFullNames,
|
|
50
|
+
groupByOwner: true,
|
|
51
|
+
usePrimaryConn: true,
|
|
52
|
+
})) {
|
|
53
|
+
if (!(await this._handler.shouldExportElementAspect(aspect)))
|
|
54
|
+
continue;
|
|
55
|
+
if (multiAspectOwnerId !== undefined &&
|
|
56
|
+
multiAspectOwnerId !== aspect.element.id) {
|
|
57
|
+
await exportMultiAspects();
|
|
58
|
+
}
|
|
59
|
+
if (aspect instanceof core_backend_1.ElementUniqueAspect) {
|
|
60
|
+
const isUpdate = this._aspectChanges?.updateIds.has(aspect.id)
|
|
61
|
+
? true
|
|
62
|
+
: this._aspectChanges?.insertIds.has(aspect.id)
|
|
63
|
+
? false
|
|
64
|
+
: undefined;
|
|
65
|
+
await this._handler.onExportElementUniqueAspect(aspect, isUpdate);
|
|
66
|
+
await this._handler.trackProgress();
|
|
67
|
+
}
|
|
68
|
+
else if (aspect instanceof core_backend_1.ElementMultiAspect) {
|
|
69
|
+
multiAspectOwnerId = aspect.element.id;
|
|
70
|
+
multiAspects.push(aspect);
|
|
71
|
+
}
|
|
72
|
+
else {
|
|
73
|
+
throw new Error(`Unexpected ElementAspect type ${aspect.classFullName}`);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
await exportMultiAspects();
|
|
77
|
+
}
|
|
78
|
+
/** Sets the aspect changes used to distinguish inserted and updated unique aspects during change export. */
|
|
79
|
+
setAspectChanges(aspectChanges) {
|
|
80
|
+
this._aspectChanges = aspectChanges;
|
|
81
|
+
}
|
|
82
|
+
/** Clears source schema and content caches at an outer export-scope boundary. */
|
|
83
|
+
resetCaches() {
|
|
84
|
+
this._hasExportableAspects = undefined;
|
|
85
|
+
this._expandedExcludedElementAspectClassFullNames = undefined;
|
|
86
|
+
}
|
|
87
|
+
/** Excludes an ElementAspect class from subsequent queries and export callbacks. */
|
|
88
|
+
excludeElementAspectClass(classFullName) {
|
|
89
|
+
const normalizedClassFullName = classFullName.replace(".", ":");
|
|
90
|
+
if (this._excludedElementAspectClassFullNames.has(normalizedClassFullName))
|
|
91
|
+
return;
|
|
92
|
+
this._excludedElementAspectClassFullNames.add(normalizedClassFullName);
|
|
93
|
+
this.resetCaches();
|
|
94
|
+
}
|
|
95
|
+
getExcludedClassFilter(queryParams) {
|
|
96
|
+
const excludedClassParameters = [];
|
|
97
|
+
let excludedClassIndex = 0;
|
|
98
|
+
for (const classFullName of this._excludedElementAspectClassFullNames) {
|
|
99
|
+
const parameterName = `excludedAspectClass${excludedClassIndex++}`;
|
|
100
|
+
excludedClassParameters.push(`ec_classid(:${parameterName})`);
|
|
101
|
+
queryParams.bindString(parameterName, classFullName);
|
|
102
|
+
}
|
|
103
|
+
return excludedClassParameters.length === 0
|
|
104
|
+
? ""
|
|
105
|
+
: `AND NOT EXISTS (
|
|
106
|
+
SELECT 1 FROM meta.ClassHasAllBaseClasses excluded
|
|
107
|
+
WHERE excluded.SourceECInstanceId = aspect.ECClassId
|
|
108
|
+
AND excluded.TargetECInstanceId IN (${excludedClassParameters.join(",")})
|
|
109
|
+
)`;
|
|
110
|
+
}
|
|
111
|
+
async getHasExportableAspects() {
|
|
112
|
+
this._hasExportableAspects ??= this.queryHasExportableAspects();
|
|
113
|
+
return this._hasExportableAspects;
|
|
114
|
+
}
|
|
115
|
+
async queryHasExportableAspects() {
|
|
116
|
+
const queryParams = new core_common_1.QueryBinder();
|
|
117
|
+
const excludedClassFilter = this.getExcludedClassFilter(queryParams);
|
|
118
|
+
const reader = this._sourceDb.createQueryReader(`SELECT ECInstanceId FROM (
|
|
119
|
+
SELECT aspect.ECInstanceId
|
|
120
|
+
FROM Bis.ElementMultiAspect aspect
|
|
121
|
+
WHERE TRUE ${excludedClassFilter}
|
|
122
|
+
UNION ALL
|
|
123
|
+
SELECT aspect.ECInstanceId
|
|
124
|
+
FROM Bis.ElementUniqueAspect aspect
|
|
125
|
+
WHERE TRUE ${excludedClassFilter}
|
|
126
|
+
)
|
|
127
|
+
LIMIT 1`, queryParams, { usePrimaryConn: true });
|
|
128
|
+
for await (const _row of (0, ECSqlReaderAsyncIterableIteratorAdapter_1.ensureECSqlReaderIsAsyncIterableIterator)(reader))
|
|
129
|
+
return true;
|
|
130
|
+
return false;
|
|
131
|
+
}
|
|
132
|
+
async getExpandedExcludedElementAspectClassFullNames() {
|
|
133
|
+
this._expandedExcludedElementAspectClassFullNames ??=
|
|
134
|
+
this.queryExpandedExcludedElementAspectClassFullNames();
|
|
135
|
+
return this._expandedExcludedElementAspectClassFullNames;
|
|
136
|
+
}
|
|
137
|
+
async queryExpandedExcludedElementAspectClassFullNames() {
|
|
138
|
+
const excludedClassFullNames = new Set();
|
|
139
|
+
for (const classFullName of this._excludedElementAspectClassFullNames) {
|
|
140
|
+
const reader = this._sourceDb.createQueryReader(`SELECT ec_classname(classes.ECInstanceId, 's:c') AS classFullName
|
|
141
|
+
FROM meta.ClassHasAllBaseClasses inheritance
|
|
142
|
+
JOIN meta.ECClassDef classes
|
|
143
|
+
ON classes.ECInstanceId = inheritance.SourceECInstanceId
|
|
144
|
+
WHERE inheritance.TargetECInstanceId = ec_classid(:excludedClassName)`, new core_common_1.QueryBinder().bindString("excludedClassName", classFullName), { usePrimaryConn: true });
|
|
145
|
+
for await (const row of (0, ECSqlReaderAsyncIterableIteratorAdapter_1.ensureECSqlReaderIsAsyncIterableIterator)(reader))
|
|
146
|
+
excludedClassFullNames.add(row.classFullName);
|
|
147
|
+
}
|
|
148
|
+
return excludedClassFullNames;
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
exports.ElementAspectExportProcessor = ElementAspectExportProcessor;
|
|
152
|
+
//# sourceMappingURL=ElementAspectExportProcessor.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ElementAspectExportProcessor.js","sourceRoot":"","sources":["../../src/ElementAspectExportProcessor.ts"],"names":[],"mappings":"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","sourcesContent":["/*---------------------------------------------------------------------------------------------\n * Copyright (c) Bentley Systems, Incorporated. All rights reserved.\n * See LICENSE.md in the project root for license terms and full copyright notice.\n *--------------------------------------------------------------------------------------------*/\n\nimport {\n ElementAspect,\n ElementMultiAspect,\n ElementUniqueAspect,\n IModelDb,\n} from \"@itwin/core-backend\";\nimport { Id64String } from \"@itwin/core-bentley\";\nimport { ensureECSqlReaderIsAsyncIterableIterator } from \"./ECSqlReaderAsyncIterableIteratorAdapter\";\nimport { QueryBinder } from \"@itwin/core-common\";\n\ninterface AspectChanges {\n readonly insertIds: ReadonlySet<Id64String>;\n readonly updateIds: ReadonlySet<Id64String>;\n}\n\nexport interface ElementAspectExportProcessorHandler {\n shouldExportElementAspect(aspect: ElementAspect): Promise<boolean>;\n onExportElementUniqueAspect(\n uniqueAspect: ElementUniqueAspect,\n isUpdate?: boolean | undefined\n ): Promise<void>;\n onExportElementMultiAspects(\n multiAspects: ElementMultiAspect[]\n ): Promise<void>;\n trackProgress: () => Promise<void>;\n}\n\n/** Queries and exports ElementAspects for accepted source owners, applying class and handler filters and grouping multi-aspects by owner.\n * @internal\n */\nexport class ElementAspectExportProcessor {\n /** ElementAspect classes excluded from source queries. */\n private readonly _excludedElementAspectClassFullNames = new Set<string>();\n private _hasExportableAspects: Promise<boolean> | undefined;\n private _expandedExcludedElementAspectClassFullNames:\n | Promise<ReadonlySet<string>>\n | undefined;\n private _aspectChanges: AspectChanges | undefined;\n\n /** ElementAspect class names excluded from source queries. */\n public get excludedElementAspectClassFullNames(): ReadonlySet<string> {\n return this._excludedElementAspectClassFullNames;\n }\n private readonly _sourceDb: IModelDb;\n private readonly _handler: ElementAspectExportProcessorHandler;\n\n public constructor(\n sourceDb: IModelDb,\n handler: ElementAspectExportProcessorHandler\n ) {\n this._sourceDb = sourceDb;\n this._handler = handler;\n }\n\n /** Exports ElementAspects owned by the supplied elements.\n * Multi-aspects are emitted in one callback group per owner.\n */\n public async exportAllElementAspects(\n ownerElementIds: ReadonlySet<Id64String>\n ): Promise<void> {\n if (ownerElementIds.size === 0 || !(await this.getHasExportableAspects()))\n return;\n\n let multiAspectOwnerId: Id64String | undefined;\n let multiAspects: ElementMultiAspect[] = [];\n const exportMultiAspects = async () => {\n if (multiAspects.length === 0) return;\n await this._handler.onExportElementMultiAspects(multiAspects);\n await this._handler.trackProgress();\n multiAspects = [];\n multiAspectOwnerId = undefined;\n };\n\n const excludedAspectClassFullNames =\n await this.getExpandedExcludedElementAspectClassFullNames();\n for await (const aspect of this._sourceDb.elements.queryAspects({\n elementIds: new Set(ownerElementIds),\n excludedAspectClassFullNames,\n groupByOwner: true,\n usePrimaryConn: true,\n })) {\n if (!(await this._handler.shouldExportElementAspect(aspect))) continue;\n\n if (\n multiAspectOwnerId !== undefined &&\n multiAspectOwnerId !== aspect.element.id\n ) {\n await exportMultiAspects();\n }\n\n if (aspect instanceof ElementUniqueAspect) {\n const isUpdate = this._aspectChanges?.updateIds.has(aspect.id)\n ? true\n : this._aspectChanges?.insertIds.has(aspect.id)\n ? false\n : undefined;\n await this._handler.onExportElementUniqueAspect(aspect, isUpdate);\n await this._handler.trackProgress();\n } else if (aspect instanceof ElementMultiAspect) {\n multiAspectOwnerId = aspect.element.id;\n multiAspects.push(aspect);\n } else {\n throw new Error(\n `Unexpected ElementAspect type ${aspect.classFullName}`\n );\n }\n }\n\n await exportMultiAspects();\n }\n\n /** Sets the aspect changes used to distinguish inserted and updated unique aspects during change export. */\n public setAspectChanges(aspectChanges?: AspectChanges): void {\n this._aspectChanges = aspectChanges;\n }\n\n /** Clears source schema and content caches at an outer export-scope boundary. */\n public resetCaches(): void {\n this._hasExportableAspects = undefined;\n this._expandedExcludedElementAspectClassFullNames = undefined;\n }\n\n /** Excludes an ElementAspect class from subsequent queries and export callbacks. */\n public excludeElementAspectClass(classFullName: string): void {\n const normalizedClassFullName = classFullName.replace(\".\", \":\");\n if (this._excludedElementAspectClassFullNames.has(normalizedClassFullName))\n return;\n this._excludedElementAspectClassFullNames.add(normalizedClassFullName);\n this.resetCaches();\n }\n\n private getExcludedClassFilter(queryParams: QueryBinder): string {\n const excludedClassParameters: string[] = [];\n let excludedClassIndex = 0;\n for (const classFullName of this._excludedElementAspectClassFullNames) {\n const parameterName = `excludedAspectClass${excludedClassIndex++}`;\n excludedClassParameters.push(`ec_classid(:${parameterName})`);\n queryParams.bindString(parameterName, classFullName);\n }\n\n return excludedClassParameters.length === 0\n ? \"\"\n : `AND NOT EXISTS (\n SELECT 1 FROM meta.ClassHasAllBaseClasses excluded\n WHERE excluded.SourceECInstanceId = aspect.ECClassId\n AND excluded.TargetECInstanceId IN (${excludedClassParameters.join(\",\")})\n )`;\n }\n\n private async getHasExportableAspects(): Promise<boolean> {\n this._hasExportableAspects ??= this.queryHasExportableAspects();\n return this._hasExportableAspects;\n }\n\n private async queryHasExportableAspects(): Promise<boolean> {\n const queryParams = new QueryBinder();\n const excludedClassFilter = this.getExcludedClassFilter(queryParams);\n const reader = this._sourceDb.createQueryReader(\n `SELECT ECInstanceId FROM (\n SELECT aspect.ECInstanceId\n FROM Bis.ElementMultiAspect aspect\n WHERE TRUE ${excludedClassFilter}\n UNION ALL\n SELECT aspect.ECInstanceId\n FROM Bis.ElementUniqueAspect aspect\n WHERE TRUE ${excludedClassFilter}\n )\n LIMIT 1`,\n queryParams,\n { usePrimaryConn: true }\n );\n\n for await (const _row of ensureECSqlReaderIsAsyncIterableIterator(reader))\n return true;\n return false;\n }\n\n private async getExpandedExcludedElementAspectClassFullNames(): Promise<\n ReadonlySet<string>\n > {\n this._expandedExcludedElementAspectClassFullNames ??=\n this.queryExpandedExcludedElementAspectClassFullNames();\n return this._expandedExcludedElementAspectClassFullNames;\n }\n\n private async queryExpandedExcludedElementAspectClassFullNames(): Promise<\n ReadonlySet<string>\n > {\n const excludedClassFullNames = new Set<string>();\n for (const classFullName of this._excludedElementAspectClassFullNames) {\n const reader = this._sourceDb.createQueryReader(\n `SELECT ec_classname(classes.ECInstanceId, 's:c') AS classFullName\n FROM meta.ClassHasAllBaseClasses inheritance\n JOIN meta.ECClassDef classes\n ON classes.ECInstanceId = inheritance.SourceECInstanceId\n WHERE inheritance.TargetECInstanceId = ec_classid(:excludedClassName)`,\n new QueryBinder().bindString(\"excludedClassName\", classFullName),\n { usePrimaryConn: true }\n );\n for await (const row of ensureECSqlReaderIsAsyncIterableIterator(reader))\n excludedClassFullNames.add(row.classFullName);\n }\n return excludedClassFullNames;\n }\n}\n"]}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { Id64Set, Id64String } from "@itwin/core-bentley";
|
|
2
|
+
import { IModelDb } from "@itwin/core-backend";
|
|
3
|
+
/** Finds the target roots required for native bulk deletion.
|
|
4
|
+
*
|
|
5
|
+
* Native deletion already cascades through child elements and modeled contents. This query follows
|
|
6
|
+
* those dependencies to find top-level elements whose codes are scoped by an element in a deleted
|
|
7
|
+
* tree. Each code-dependent element becomes another native deletion root.
|
|
8
|
+
* @internal
|
|
9
|
+
*/
|
|
10
|
+
export declare function findBulkDeleteRoots(targetDb: IModelDb, elementIds: ReadonlySet<Id64String>): Promise<Id64Set>;
|
|
11
|
+
//# sourceMappingURL=ElementBulkDelete.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ElementBulkDelete.d.ts","sourceRoot":"","sources":["../../src/ElementBulkDelete.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,OAAO,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;AAE1D,OAAO,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAC;AAE/C;;;;;;GAMG;AACH,wBAAsB,mBAAmB,CACvC,QAAQ,EAAE,QAAQ,EAClB,UAAU,EAAE,WAAW,CAAC,UAAU,CAAC,GAClC,OAAO,CAAC,OAAO,CAAC,CAsClB"}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/*---------------------------------------------------------------------------------------------
|
|
3
|
+
* Copyright (c) Bentley Systems, Incorporated. All rights reserved.
|
|
4
|
+
* See LICENSE.md in the project root for license terms and full copyright notice.
|
|
5
|
+
*--------------------------------------------------------------------------------------------*/
|
|
6
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
+
exports.findBulkDeleteRoots = findBulkDeleteRoots;
|
|
8
|
+
const core_common_1 = require("@itwin/core-common");
|
|
9
|
+
/** Finds the target roots required for native bulk deletion.
|
|
10
|
+
*
|
|
11
|
+
* Native deletion already cascades through child elements and modeled contents. This query follows
|
|
12
|
+
* those dependencies to find top-level elements whose codes are scoped by an element in a deleted
|
|
13
|
+
* tree. Each code-dependent element becomes another native deletion root.
|
|
14
|
+
* @internal
|
|
15
|
+
*/
|
|
16
|
+
async function findBulkDeleteRoots(targetDb, elementIds) {
|
|
17
|
+
const deleteRoots = new Set();
|
|
18
|
+
// CascadeIds pairs each traversed element with a root that the native API must delete. The anchor
|
|
19
|
+
// adds requested roots that still exist. Recursive branches walk child elements and modeled
|
|
20
|
+
// contents while preserving DeleteRootId. A top-level element whose code is scoped by anything
|
|
21
|
+
// already traversed becomes a new root, so recursion also covers its tree and code dependents.
|
|
22
|
+
// DeleteRoots returns only the roots because native deletion performs the child and model cascade.
|
|
23
|
+
const query = `
|
|
24
|
+
WITH RECURSIVE CascadeIds(Id, DeleteRootId) AS (
|
|
25
|
+
SELECT element.ECInstanceId, element.ECInstanceId
|
|
26
|
+
FROM bis.Element element
|
|
27
|
+
INNER JOIN IdSet(:elementIds) ids ON ids.id = element.ECInstanceId
|
|
28
|
+
UNION
|
|
29
|
+
SELECT child.ECInstanceId, deletionParent.DeleteRootId
|
|
30
|
+
FROM bis.Element child
|
|
31
|
+
INNER JOIN CascadeIds deletionParent ON child.Parent.Id = deletionParent.Id
|
|
32
|
+
UNION
|
|
33
|
+
SELECT modelElement.ECInstanceId, modeledElement.DeleteRootId
|
|
34
|
+
FROM bis.Element modelElement
|
|
35
|
+
INNER JOIN CascadeIds modeledElement ON modelElement.Model.Id = modeledElement.Id
|
|
36
|
+
UNION
|
|
37
|
+
SELECT codeDependent.ECInstanceId, codeDependent.ECInstanceId
|
|
38
|
+
FROM bis.Element codeDependent
|
|
39
|
+
INNER JOIN CascadeIds scope ON codeDependent.CodeScope.Id = scope.Id
|
|
40
|
+
WHERE codeDependent.Parent.Id IS NULL
|
|
41
|
+
),
|
|
42
|
+
DeleteRoots(Id) AS (
|
|
43
|
+
SELECT DISTINCT DeleteRootId FROM CascadeIds
|
|
44
|
+
)
|
|
45
|
+
SELECT Id AS id FROM DeleteRoots
|
|
46
|
+
`;
|
|
47
|
+
const params = new core_common_1.QueryBinder().bindIdSet("elementIds", elementIds);
|
|
48
|
+
for await (const row of targetDb.createQueryReader(query, params, {
|
|
49
|
+
usePrimaryConn: true,
|
|
50
|
+
})) {
|
|
51
|
+
deleteRoots.add(row.id);
|
|
52
|
+
}
|
|
53
|
+
return deleteRoots;
|
|
54
|
+
}
|
|
55
|
+
//# sourceMappingURL=ElementBulkDelete.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ElementBulkDelete.js","sourceRoot":"","sources":["../../src/ElementBulkDelete.ts"],"names":[],"mappings":";AAAA;;;gGAGgG;;;AAGhG,oDAAiD;AAGjD;;;;;;GAMG;AACI,KAAK,8BACV,QAAkB,EAClB,UAAmC;IAEnC,MAAM,WAAW,GAAY,IAAI,GAAG,EAAc,CAAC;IACnD,kGAAkG;IAClG,4FAA4F;IAC5F,+FAA+F;IAC/F,+FAA+F;IAC/F,mGAAmG;IACnG,MAAM,KAAK,GAAG;;;;;;;;;;;;;;;;;;;;;;;GAuBb,CAAC;IACF,MAAM,MAAM,GAAG,IAAI,yBAAW,EAAE,CAAC,SAAS,CAAC,YAAY,EAAE,UAAU,CAAC,CAAC;IACrE,IAAI,KAAK,EAAE,MAAM,GAAG,IAAI,QAAQ,CAAC,iBAAiB,CAAC,KAAK,EAAE,MAAM,EAAE;QAChE,cAAc,EAAE,IAAI;KACrB,CAAC,EAAE,CAAC;QACH,WAAW,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;IAC1B,CAAC;IACD,OAAO,WAAW,CAAC;AACrB,CAAC","sourcesContent":["/*---------------------------------------------------------------------------------------------\n * Copyright (c) Bentley Systems, Incorporated. All rights reserved.\n * See LICENSE.md in the project root for license terms and full copyright notice.\n *--------------------------------------------------------------------------------------------*/\n\nimport { Id64Set, Id64String } from \"@itwin/core-bentley\";\nimport { QueryBinder } from \"@itwin/core-common\";\nimport { IModelDb } from \"@itwin/core-backend\";\n\n/** Finds the target roots required for native bulk deletion.\n *\n * Native deletion already cascades through child elements and modeled contents. This query follows\n * those dependencies to find top-level elements whose codes are scoped by an element in a deleted\n * tree. Each code-dependent element becomes another native deletion root.\n * @internal\n */\nexport async function findBulkDeleteRoots(\n targetDb: IModelDb,\n elementIds: ReadonlySet<Id64String>\n): Promise<Id64Set> {\n const deleteRoots: Id64Set = new Set<Id64String>();\n // CascadeIds pairs each traversed element with a root that the native API must delete. The anchor\n // adds requested roots that still exist. Recursive branches walk child elements and modeled\n // contents while preserving DeleteRootId. A top-level element whose code is scoped by anything\n // already traversed becomes a new root, so recursion also covers its tree and code dependents.\n // DeleteRoots returns only the roots because native deletion performs the child and model cascade.\n const query = `\n WITH RECURSIVE CascadeIds(Id, DeleteRootId) AS (\n SELECT element.ECInstanceId, element.ECInstanceId\n FROM bis.Element element\n INNER JOIN IdSet(:elementIds) ids ON ids.id = element.ECInstanceId\n UNION\n SELECT child.ECInstanceId, deletionParent.DeleteRootId\n FROM bis.Element child\n INNER JOIN CascadeIds deletionParent ON child.Parent.Id = deletionParent.Id\n UNION\n SELECT modelElement.ECInstanceId, modeledElement.DeleteRootId\n FROM bis.Element modelElement\n INNER JOIN CascadeIds modeledElement ON modelElement.Model.Id = modeledElement.Id\n UNION\n SELECT codeDependent.ECInstanceId, codeDependent.ECInstanceId\n FROM bis.Element codeDependent\n INNER JOIN CascadeIds scope ON codeDependent.CodeScope.Id = scope.Id\n WHERE codeDependent.Parent.Id IS NULL\n ),\n DeleteRoots(Id) AS (\n SELECT DISTINCT DeleteRootId FROM CascadeIds\n )\n SELECT Id AS id FROM DeleteRoots\n `;\n const params = new QueryBinder().bindIdSet(\"elementIds\", elementIds);\n for await (const row of targetDb.createQueryReader(query, params, {\n usePrimaryConn: true,\n })) {\n deleteRoots.add(row.id);\n }\n return deleteRoots;\n}\n"]}
|