@itwin/tree-widget-react 3.16.1 → 3.17.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +24 -2
- package/lib/cjs/tree-widget-react/components/SelectableTree.js.map +1 -1
- package/lib/cjs/tree-widget-react/components/TreeSelector.js.map +1 -1
- package/lib/cjs/tree-widget-react/components/TreeWidgetUiItemsProvider.js +1 -1
- package/lib/cjs/tree-widget-react/components/TreeWidgetUiItemsProvider.js.map +1 -1
- package/lib/cjs/tree-widget-react/components/tree-header/TreeHeader.js.map +1 -1
- package/lib/cjs/tree-widget-react/components/tree-header/TreeWithHeader.js.map +1 -1
- package/lib/cjs/tree-widget-react/components/trees/categories-tree/CategoriesTree.d.ts +2 -2
- package/lib/cjs/tree-widget-react/components/trees/categories-tree/CategoriesTree.js +2 -1
- package/lib/cjs/tree-widget-react/components/trees/categories-tree/CategoriesTree.js.map +1 -1
- package/lib/cjs/tree-widget-react/components/trees/categories-tree/CategoriesTreeButtons.js.map +1 -1
- package/lib/cjs/tree-widget-react/components/trees/categories-tree/CategoriesTreeComponent.d.ts +1 -1
- package/lib/cjs/tree-widget-react/components/trees/categories-tree/CategoriesTreeComponent.js.map +1 -1
- package/lib/cjs/tree-widget-react/components/trees/categories-tree/CategoriesTreeDefinition.d.ts +13 -0
- package/lib/cjs/tree-widget-react/components/trees/categories-tree/CategoriesTreeDefinition.js +92 -74
- package/lib/cjs/tree-widget-react/components/trees/categories-tree/CategoriesTreeDefinition.js.map +1 -1
- package/lib/cjs/tree-widget-react/components/trees/categories-tree/UseCategoriesTree.d.ts +3 -1
- package/lib/cjs/tree-widget-react/components/trees/categories-tree/UseCategoriesTree.js +21 -8
- package/lib/cjs/tree-widget-react/components/trees/categories-tree/UseCategoriesTree.js.map +1 -1
- package/lib/cjs/tree-widget-react/components/trees/categories-tree/internal/CategoriesTreeIdsCache.d.ts +21 -9
- package/lib/cjs/tree-widget-react/components/trees/categories-tree/internal/CategoriesTreeIdsCache.js +300 -251
- package/lib/cjs/tree-widget-react/components/trees/categories-tree/internal/CategoriesTreeIdsCache.js.map +1 -1
- package/lib/cjs/tree-widget-react/components/trees/categories-tree/internal/CategoriesVisibilityHandler.d.ts +2 -0
- package/lib/cjs/tree-widget-react/components/trees/categories-tree/internal/CategoriesVisibilityHandler.js +66 -107
- package/lib/cjs/tree-widget-react/components/trees/categories-tree/internal/CategoriesVisibilityHandler.js.map +1 -1
- package/lib/cjs/tree-widget-react/components/trees/common/CategoriesVisibilityUtils.js +22 -12
- package/lib/cjs/tree-widget-react/components/trees/common/CategoriesVisibilityUtils.js.map +1 -1
- package/lib/cjs/tree-widget-react/components/trees/common/UseActiveViewport.js.map +1 -1
- package/lib/cjs/tree-widget-react/components/trees/common/UseHierarchyVisibility.js.map +1 -1
- package/lib/cjs/tree-widget-react/components/trees/common/components/ProgressOverlay.js.map +1 -1
- package/lib/cjs/tree-widget-react/components/trees/common/components/Tree.d.ts +2 -2
- package/lib/cjs/tree-widget-react/components/trees/common/components/Tree.js.map +1 -1
- package/lib/cjs/tree-widget-react/components/trees/common/components/TreeNodeCheckbox.d.ts +1 -1
- package/lib/cjs/tree-widget-react/components/trees/common/components/TreeNodeCheckbox.js.map +1 -1
- package/lib/cjs/tree-widget-react/components/trees/common/components/TreeRenderer.d.ts +1 -1
- package/lib/cjs/tree-widget-react/components/trees/common/components/TreeRenderer.js +2 -2
- package/lib/cjs/tree-widget-react/components/trees/common/components/TreeRenderer.js.map +1 -1
- package/lib/cjs/tree-widget-react/components/trees/common/components/VisibilityTree.d.ts +3 -3
- package/lib/cjs/tree-widget-react/components/trees/common/components/VisibilityTree.js.map +1 -1
- package/lib/cjs/tree-widget-react/components/trees/common/components/VisibilityTreeRenderer.d.ts +1 -1
- package/lib/cjs/tree-widget-react/components/trees/common/components/VisibilityTreeRenderer.js.map +1 -1
- package/lib/cjs/tree-widget-react/components/trees/common/internal/Utils.d.ts +25 -0
- package/lib/cjs/tree-widget-react/components/trees/common/internal/Utils.js +83 -0
- package/lib/cjs/tree-widget-react/components/trees/common/internal/Utils.js.map +1 -0
- package/lib/cjs/tree-widget-react/components/trees/models-tree/ModelsTree.d.ts +1 -1
- package/lib/cjs/tree-widget-react/components/trees/models-tree/ModelsTree.js.map +1 -1
- package/lib/cjs/tree-widget-react/components/trees/models-tree/ModelsTreeButtons.js.map +1 -1
- package/lib/cjs/tree-widget-react/components/trees/models-tree/ModelsTreeComponent.js.map +1 -1
- package/lib/cjs/tree-widget-react/components/trees/models-tree/ModelsTreeDefinition.d.ts +1 -0
- package/lib/cjs/tree-widget-react/components/trees/models-tree/ModelsTreeDefinition.js +86 -28
- package/lib/cjs/tree-widget-react/components/trees/models-tree/ModelsTreeDefinition.js.map +1 -1
- package/lib/cjs/tree-widget-react/components/trees/models-tree/UseModelsTree.js +2 -1
- package/lib/cjs/tree-widget-react/components/trees/models-tree/UseModelsTree.js.map +1 -1
- package/lib/cjs/tree-widget-react/components/trees/models-tree/Utils.d.ts +5 -6
- package/lib/cjs/tree-widget-react/components/trees/models-tree/Utils.js +5 -17
- package/lib/cjs/tree-widget-react/components/trees/models-tree/Utils.js.map +1 -1
- package/lib/cjs/tree-widget-react/components/trees/models-tree/internal/AlwaysAndNeverDrawnElementInfo.js +5 -4
- package/lib/cjs/tree-widget-react/components/trees/models-tree/internal/AlwaysAndNeverDrawnElementInfo.js.map +1 -1
- package/lib/cjs/tree-widget-react/components/trees/models-tree/internal/FilteredTree.d.ts +7 -1
- package/lib/cjs/tree-widget-react/components/trees/models-tree/internal/FilteredTree.js +52 -0
- package/lib/cjs/tree-widget-react/components/trees/models-tree/internal/FilteredTree.js.map +1 -1
- package/lib/cjs/tree-widget-react/components/trees/models-tree/internal/ModelsTreeIdsCache.d.ts +28 -12
- package/lib/cjs/tree-widget-react/components/trees/models-tree/internal/ModelsTreeIdsCache.js +382 -278
- package/lib/cjs/tree-widget-react/components/trees/models-tree/internal/ModelsTreeIdsCache.js.map +1 -1
- package/lib/cjs/tree-widget-react/components/trees/models-tree/internal/ModelsTreeNode.d.ts +1 -1
- package/lib/cjs/tree-widget-react/components/trees/models-tree/internal/ModelsTreeNode.js.map +1 -1
- package/lib/cjs/tree-widget-react/components/trees/models-tree/internal/ModelsTreeVisibilityHandler.js +272 -139
- package/lib/cjs/tree-widget-react/components/trees/models-tree/internal/ModelsTreeVisibilityHandler.js.map +1 -1
- package/lib/cjs/tree-widget-react-internal.d.ts +2 -1
- package/lib/cjs/tree-widget-react-internal.js +4 -1
- package/lib/cjs/tree-widget-react-internal.js.map +1 -1
- package/lib/esm/tree-widget-react/components/SelectableTree.js.map +1 -1
- package/lib/esm/tree-widget-react/components/TreeSelector.js.map +1 -1
- package/lib/esm/tree-widget-react/components/TreeWidgetUiItemsProvider.js +1 -1
- package/lib/esm/tree-widget-react/components/TreeWidgetUiItemsProvider.js.map +1 -1
- package/lib/esm/tree-widget-react/components/tree-header/TreeHeader.js.map +1 -1
- package/lib/esm/tree-widget-react/components/tree-header/TreeWithHeader.js.map +1 -1
- package/lib/esm/tree-widget-react/components/trees/categories-tree/CategoriesTree.d.ts +2 -2
- package/lib/esm/tree-widget-react/components/trees/categories-tree/CategoriesTree.js +2 -1
- package/lib/esm/tree-widget-react/components/trees/categories-tree/CategoriesTree.js.map +1 -1
- package/lib/esm/tree-widget-react/components/trees/categories-tree/CategoriesTreeButtons.js.map +1 -1
- package/lib/esm/tree-widget-react/components/trees/categories-tree/CategoriesTreeComponent.d.ts +1 -1
- package/lib/esm/tree-widget-react/components/trees/categories-tree/CategoriesTreeComponent.js.map +1 -1
- package/lib/esm/tree-widget-react/components/trees/categories-tree/CategoriesTreeDefinition.d.ts +13 -0
- package/lib/esm/tree-widget-react/components/trees/categories-tree/CategoriesTreeDefinition.js +92 -74
- package/lib/esm/tree-widget-react/components/trees/categories-tree/CategoriesTreeDefinition.js.map +1 -1
- package/lib/esm/tree-widget-react/components/trees/categories-tree/UseCategoriesTree.d.ts +3 -1
- package/lib/esm/tree-widget-react/components/trees/categories-tree/UseCategoriesTree.js +22 -9
- package/lib/esm/tree-widget-react/components/trees/categories-tree/UseCategoriesTree.js.map +1 -1
- package/lib/esm/tree-widget-react/components/trees/categories-tree/internal/CategoriesTreeIdsCache.d.ts +21 -9
- package/lib/esm/tree-widget-react/components/trees/categories-tree/internal/CategoriesTreeIdsCache.js +301 -252
- package/lib/esm/tree-widget-react/components/trees/categories-tree/internal/CategoriesTreeIdsCache.js.map +1 -1
- package/lib/esm/tree-widget-react/components/trees/categories-tree/internal/CategoriesVisibilityHandler.d.ts +2 -0
- package/lib/esm/tree-widget-react/components/trees/categories-tree/internal/CategoriesVisibilityHandler.js +67 -108
- package/lib/esm/tree-widget-react/components/trees/categories-tree/internal/CategoriesVisibilityHandler.js.map +1 -1
- package/lib/esm/tree-widget-react/components/trees/common/CategoriesVisibilityUtils.js +23 -13
- package/lib/esm/tree-widget-react/components/trees/common/CategoriesVisibilityUtils.js.map +1 -1
- package/lib/esm/tree-widget-react/components/trees/common/UseActiveViewport.js.map +1 -1
- package/lib/esm/tree-widget-react/components/trees/common/UseHierarchyVisibility.js.map +1 -1
- package/lib/esm/tree-widget-react/components/trees/common/components/ProgressOverlay.js.map +1 -1
- package/lib/esm/tree-widget-react/components/trees/common/components/Tree.d.ts +2 -2
- package/lib/esm/tree-widget-react/components/trees/common/components/Tree.js.map +1 -1
- package/lib/esm/tree-widget-react/components/trees/common/components/TreeNodeCheckbox.d.ts +1 -1
- package/lib/esm/tree-widget-react/components/trees/common/components/TreeNodeCheckbox.js.map +1 -1
- package/lib/esm/tree-widget-react/components/trees/common/components/TreeRenderer.d.ts +1 -1
- package/lib/esm/tree-widget-react/components/trees/common/components/TreeRenderer.js +2 -2
- package/lib/esm/tree-widget-react/components/trees/common/components/TreeRenderer.js.map +1 -1
- package/lib/esm/tree-widget-react/components/trees/common/components/VisibilityTree.d.ts +3 -3
- package/lib/esm/tree-widget-react/components/trees/common/components/VisibilityTree.js.map +1 -1
- package/lib/esm/tree-widget-react/components/trees/common/components/VisibilityTreeRenderer.d.ts +1 -1
- package/lib/esm/tree-widget-react/components/trees/common/components/VisibilityTreeRenderer.js.map +1 -1
- package/lib/esm/tree-widget-react/components/trees/common/internal/Utils.d.ts +25 -0
- package/lib/esm/tree-widget-react/components/trees/common/internal/Utils.js +74 -0
- package/lib/esm/tree-widget-react/components/trees/common/internal/Utils.js.map +1 -0
- package/lib/esm/tree-widget-react/components/trees/models-tree/ModelsTree.d.ts +1 -1
- package/lib/esm/tree-widget-react/components/trees/models-tree/ModelsTree.js.map +1 -1
- package/lib/esm/tree-widget-react/components/trees/models-tree/ModelsTreeButtons.js.map +1 -1
- package/lib/esm/tree-widget-react/components/trees/models-tree/ModelsTreeComponent.js.map +1 -1
- package/lib/esm/tree-widget-react/components/trees/models-tree/ModelsTreeDefinition.d.ts +1 -0
- package/lib/esm/tree-widget-react/components/trees/models-tree/ModelsTreeDefinition.js +83 -25
- package/lib/esm/tree-widget-react/components/trees/models-tree/ModelsTreeDefinition.js.map +1 -1
- package/lib/esm/tree-widget-react/components/trees/models-tree/UseModelsTree.js +2 -1
- package/lib/esm/tree-widget-react/components/trees/models-tree/UseModelsTree.js.map +1 -1
- package/lib/esm/tree-widget-react/components/trees/models-tree/Utils.d.ts +5 -6
- package/lib/esm/tree-widget-react/components/trees/models-tree/Utils.js +4 -16
- package/lib/esm/tree-widget-react/components/trees/models-tree/Utils.js.map +1 -1
- package/lib/esm/tree-widget-react/components/trees/models-tree/internal/AlwaysAndNeverDrawnElementInfo.js +4 -3
- package/lib/esm/tree-widget-react/components/trees/models-tree/internal/AlwaysAndNeverDrawnElementInfo.js.map +1 -1
- package/lib/esm/tree-widget-react/components/trees/models-tree/internal/FilteredTree.d.ts +7 -1
- package/lib/esm/tree-widget-react/components/trees/models-tree/internal/FilteredTree.js +52 -0
- package/lib/esm/tree-widget-react/components/trees/models-tree/internal/FilteredTree.js.map +1 -1
- package/lib/esm/tree-widget-react/components/trees/models-tree/internal/ModelsTreeIdsCache.d.ts +28 -12
- package/lib/esm/tree-widget-react/components/trees/models-tree/internal/ModelsTreeIdsCache.js +382 -278
- package/lib/esm/tree-widget-react/components/trees/models-tree/internal/ModelsTreeIdsCache.js.map +1 -1
- package/lib/esm/tree-widget-react/components/trees/models-tree/internal/ModelsTreeNode.d.ts +1 -1
- package/lib/esm/tree-widget-react/components/trees/models-tree/internal/ModelsTreeNode.js.map +1 -1
- package/lib/esm/tree-widget-react/components/trees/models-tree/internal/ModelsTreeVisibilityHandler.js +257 -124
- package/lib/esm/tree-widget-react/components/trees/models-tree/internal/ModelsTreeVisibilityHandler.js.map +1 -1
- package/lib/esm/tree-widget-react-internal.d.ts +2 -1
- package/lib/esm/tree-widget-react-internal.js +2 -1
- package/lib/esm/tree-widget-react-internal.js.map +1 -1
- package/package.json +1 -1
package/lib/esm/tree-widget-react/components/trees/models-tree/internal/ModelsTreeIdsCache.js
CHANGED
|
@@ -2,11 +2,12 @@
|
|
|
2
2
|
* Copyright (c) Bentley Systems, Incorporated. All rights reserved.
|
|
3
3
|
* See LICENSE.md in the project root for license terms and full copyright notice.
|
|
4
4
|
*--------------------------------------------------------------------------------------------*/
|
|
5
|
-
import { bufferCount, bufferTime, filter, firstValueFrom, from, map, mergeAll, mergeMap, reduce, ReplaySubject, Subject } from "rxjs";
|
|
5
|
+
import { bufferCount, bufferTime, defaultIfEmpty, defer, EMPTY, filter, firstValueFrom, forkJoin, from, map, mergeAll, mergeMap, reduce, ReplaySubject, shareReplay, Subject, take, tap, toArray, } from "rxjs";
|
|
6
6
|
import { assert, Guid, Id64 } from "@itwin/core-bentley";
|
|
7
7
|
import { IModel } from "@itwin/core-common";
|
|
8
|
-
import {
|
|
8
|
+
import { releaseMainThreadOnItemsCount } from "../../common/internal/Utils.js";
|
|
9
9
|
import { pushToMap } from "../../common/Utils.js";
|
|
10
|
+
import { getOptimalBatchSize } from "../Utils.js";
|
|
10
11
|
/** @internal */
|
|
11
12
|
export class ModelsTreeIdsCache {
|
|
12
13
|
#categoryElementCounts;
|
|
@@ -19,93 +20,221 @@ export class ModelsTreeIdsCache {
|
|
|
19
20
|
#categoryKeyPaths;
|
|
20
21
|
#queryExecutor;
|
|
21
22
|
#hierarchyConfig;
|
|
23
|
+
#childrenMap;
|
|
24
|
+
/** Stores element ids which have children query scheduled to execute. */
|
|
25
|
+
#childrenLoadingMap;
|
|
22
26
|
#componentId;
|
|
23
27
|
#componentName;
|
|
24
28
|
constructor(queryExecutor, hierarchyConfig, componentId) {
|
|
25
29
|
this.#hierarchyConfig = hierarchyConfig;
|
|
26
30
|
this.#queryExecutor = queryExecutor;
|
|
27
|
-
this.#categoryElementCounts = new ModelCategoryElementsCountCache(
|
|
31
|
+
this.#categoryElementCounts = new ModelCategoryElementsCountCache((input) => this.queryCategoryElementCounts(input));
|
|
28
32
|
this.#modelKeyPaths = new Map();
|
|
29
33
|
this.#subjectKeyPaths = new Map();
|
|
30
34
|
this.#categoryKeyPaths = new Map();
|
|
35
|
+
this.#childrenMap = new Map();
|
|
36
|
+
this.#childrenLoadingMap = new Map();
|
|
31
37
|
this.#componentId = componentId ?? Guid.createValue();
|
|
32
38
|
this.#componentName = "ModelsTreeIdsCache";
|
|
33
39
|
}
|
|
34
40
|
[Symbol.dispose]() {
|
|
35
41
|
this.#categoryElementCounts[Symbol.dispose]();
|
|
36
42
|
}
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
}
|
|
43
|
+
querySubjects() {
|
|
44
|
+
return defer(() => {
|
|
45
|
+
const subjectsQuery = `
|
|
46
|
+
SELECT
|
|
47
|
+
s.ECInstanceId id,
|
|
48
|
+
s.Parent.Id parentId,
|
|
49
|
+
(
|
|
50
|
+
SELECT m.ECInstanceId
|
|
51
|
+
FROM bis.GeometricModel3d m
|
|
52
|
+
WHERE m.ECInstanceId = HexToId(json_extract(s.JsonProperties, '$.Subject.Model.TargetPartition'))
|
|
53
|
+
AND NOT m.IsPrivate
|
|
54
|
+
AND EXISTS (SELECT 1 FROM ${this.#hierarchyConfig.elementClassSpecification} WHERE Model.Id = m.ECInstanceId)
|
|
55
|
+
) targetPartitionId,
|
|
56
|
+
CASE
|
|
57
|
+
WHEN (
|
|
58
|
+
json_extract(s.JsonProperties, '$.Subject.Job.Bridge') IS NOT NULL
|
|
59
|
+
OR json_extract(s.JsonProperties, '$.Subject.Model.Type') = 'Hierarchy'
|
|
60
|
+
) THEN 1
|
|
61
|
+
ELSE 0
|
|
62
|
+
END hideInHierarchy
|
|
63
|
+
FROM bis.Subject s
|
|
64
|
+
`;
|
|
65
|
+
return this.#queryExecutor.createQueryReader({ ecsql: subjectsQuery }, { rowFormat: "ECSqlPropertyNames", limit: "unbounded", restartToken: `${this.#componentName}/${this.#componentId}/subjects` });
|
|
66
|
+
}).pipe(map((row) => {
|
|
67
|
+
return { id: row.id, parentId: row.parentId, targetPartitionId: row.targetPartitionId, hideInHierarchy: !!row.hideInHierarchy };
|
|
68
|
+
}));
|
|
61
69
|
}
|
|
62
|
-
|
|
63
|
-
|
|
70
|
+
queryModels() {
|
|
71
|
+
return defer(() => {
|
|
72
|
+
const modelsQuery = `
|
|
64
73
|
SELECT p.ECInstanceId id, p.Parent.Id parentId
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
74
|
+
FROM bis.InformationPartitionElement p
|
|
75
|
+
INNER JOIN bis.GeometricModel3d m ON m.ModeledElement.Id = p.ECInstanceId
|
|
76
|
+
WHERE
|
|
77
|
+
NOT m.IsPrivate
|
|
78
|
+
${this.#hierarchyConfig.showEmptyModels ? "" : `AND EXISTS (SELECT 1 FROM ${this.#hierarchyConfig.elementClassSpecification} WHERE Model.Id = m.ECInstanceId)`}
|
|
79
|
+
`;
|
|
80
|
+
return this.#queryExecutor.createQueryReader({ ecsql: modelsQuery }, { rowFormat: "ECSqlPropertyNames", limit: "unbounded", restartToken: `${this.#componentName}/${this.#componentId}/models` });
|
|
81
|
+
}).pipe(map((row) => {
|
|
82
|
+
return { id: row.id, parentId: row.parentId };
|
|
83
|
+
}));
|
|
84
|
+
}
|
|
85
|
+
queryChildren({ elementIds }) {
|
|
86
|
+
if (elementIds.length === 0) {
|
|
87
|
+
return EMPTY;
|
|
73
88
|
}
|
|
89
|
+
return defer(() => {
|
|
90
|
+
const ctes = [
|
|
91
|
+
`
|
|
92
|
+
ElementChildren(id, parentId) AS (
|
|
93
|
+
SELECT ECInstanceId id, Parent.Id parentId
|
|
94
|
+
FROM ${this.#hierarchyConfig.elementClassSpecification}
|
|
95
|
+
WHERE Parent.Id IN (${elementIds.join(", ")})
|
|
96
|
+
|
|
97
|
+
UNION ALL
|
|
98
|
+
|
|
99
|
+
SELECT c.ECInstanceId id, c.Parent.Id
|
|
100
|
+
FROM ${this.#hierarchyConfig.elementClassSpecification} c
|
|
101
|
+
JOIN ElementChildren p ON c.Parent.Id = p.id
|
|
102
|
+
)
|
|
103
|
+
`,
|
|
104
|
+
];
|
|
105
|
+
const ecsql = `
|
|
106
|
+
SELECT id, parentId
|
|
107
|
+
FROM ElementChildren
|
|
108
|
+
`;
|
|
109
|
+
return this.#queryExecutor.createQueryReader({ ecsql, ctes }, { rowFormat: "ECSqlPropertyNames", limit: "unbounded", restartToken: `${this.#componentName}/${this.#componentId}/children/${Guid.createValue()}` });
|
|
110
|
+
}).pipe(map((row) => {
|
|
111
|
+
return { id: row.id, parentId: row.parentId };
|
|
112
|
+
}));
|
|
74
113
|
}
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
114
|
+
getChildrenTreeFromMap({ elementIds }) {
|
|
115
|
+
const result = new Map();
|
|
116
|
+
if (Id64.sizeOf(elementIds) === 0 || this.#childrenMap.size === 0) {
|
|
117
|
+
return result;
|
|
118
|
+
}
|
|
119
|
+
for (const elementId of Id64.iterable(elementIds)) {
|
|
120
|
+
const entry = this.#childrenMap.get(elementId);
|
|
121
|
+
if (!entry?.children) {
|
|
122
|
+
continue;
|
|
123
|
+
}
|
|
124
|
+
const elementChildrenTree = new Map();
|
|
125
|
+
result.set(elementId, { children: elementChildrenTree });
|
|
126
|
+
entry.children.forEach((childId) => {
|
|
127
|
+
const childrenTreeOfChild = this.getChildrenTreeFromMap({ elementIds: childId });
|
|
128
|
+
// Need to add children tree created from childId. This tree includes childId as root element
|
|
129
|
+
// If child does not have children, children tree won't be created. Need to add childId with undefined children
|
|
130
|
+
elementChildrenTree.set(childId, { children: childrenTreeOfChild.size > 0 ? childrenTreeOfChild : undefined });
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
return result;
|
|
134
|
+
}
|
|
135
|
+
getChildrenCountMap({ elementIds }) {
|
|
136
|
+
const result = new Map();
|
|
137
|
+
for (const elementId of Id64.iterable(elementIds)) {
|
|
138
|
+
const entry = this.#childrenMap.get(elementId);
|
|
139
|
+
if (entry?.children) {
|
|
140
|
+
let totalChildrenCount = entry.children.length;
|
|
141
|
+
this.getChildrenCountMap({ elementIds: entry.children }).forEach((childrenOfChildCount) => (totalChildrenCount += childrenOfChildCount));
|
|
142
|
+
result.set(elementId, totalChildrenCount);
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
return result;
|
|
146
|
+
}
|
|
147
|
+
/**
|
|
148
|
+
* Populates #childrenLoadingMap with promises. When these promises resolve, they will populate #childrenMap with values and delete entries from #childrenLoadingMap.
|
|
149
|
+
*/
|
|
150
|
+
createChildrenLoadingMapEntries({ elementsToQuery }) {
|
|
151
|
+
const getElementsToQueryPromise = async (batchedElementsToQuery) => firstValueFrom(this.queryChildren({ elementIds: batchedElementsToQuery }).pipe(
|
|
152
|
+
// Want to have void at the end instead of void[], so using reduce
|
|
153
|
+
reduce((acc, { parentId, id }) => {
|
|
154
|
+
let entry = this.#childrenMap.get(parentId);
|
|
155
|
+
if (!entry) {
|
|
156
|
+
entry = { children: [] };
|
|
157
|
+
this.#childrenMap.set(parentId, entry);
|
|
158
|
+
}
|
|
159
|
+
if (!entry.children) {
|
|
160
|
+
entry.children = [];
|
|
161
|
+
}
|
|
162
|
+
// Add child to parent's entry and add child to children map
|
|
163
|
+
entry.children.push(id);
|
|
164
|
+
if (!this.#childrenMap.has(id)) {
|
|
165
|
+
this.#childrenMap.set(id, { children: undefined });
|
|
166
|
+
}
|
|
167
|
+
return acc;
|
|
168
|
+
}, (() => { })()), tap({ complete: () => batchedElementsToQuery.forEach((elementId) => this.#childrenLoadingMap.delete(elementId)) }), defaultIfEmpty((() => { })())));
|
|
169
|
+
const maximumBatchSize = 1000;
|
|
170
|
+
const totalSize = elementsToQuery.length;
|
|
171
|
+
const optimalBatchSize = getOptimalBatchSize({ totalSize, maximumBatchSize });
|
|
172
|
+
const loadingMapEntries = new Array();
|
|
173
|
+
// Don't want to slice if its not necessary
|
|
174
|
+
if (totalSize <= maximumBatchSize) {
|
|
175
|
+
loadingMapEntries.push(getElementsToQueryPromise(elementsToQuery));
|
|
176
|
+
}
|
|
177
|
+
else {
|
|
178
|
+
for (let i = 0; i < elementsToQuery.length; i += optimalBatchSize) {
|
|
179
|
+
loadingMapEntries.push(getElementsToQueryPromise(elementsToQuery.slice(i, i + optimalBatchSize)));
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
elementsToQuery.forEach((elementId, index) => this.#childrenLoadingMap.set(elementId, loadingMapEntries[Math.floor(index / optimalBatchSize)]));
|
|
183
|
+
return { loadingMapEntries: Promise.all(loadingMapEntries).then(() => { }) };
|
|
184
|
+
}
|
|
185
|
+
createChildrenMapEntries({ elementIds }) {
|
|
186
|
+
return from(Id64.iterable(elementIds)).pipe(reduce((acc, elementId) => {
|
|
187
|
+
if (this.#childrenMap.has(elementId)) {
|
|
188
|
+
return acc;
|
|
189
|
+
}
|
|
190
|
+
const loadingPromise = this.#childrenLoadingMap.get(elementId);
|
|
191
|
+
if (loadingPromise) {
|
|
192
|
+
acc.existingPromises.push(loadingPromise);
|
|
193
|
+
}
|
|
194
|
+
else {
|
|
195
|
+
acc.elementsToQuery.push(elementId);
|
|
196
|
+
}
|
|
197
|
+
return acc;
|
|
198
|
+
}, { existingPromises: new Array(), elementsToQuery: new Array() }), mergeMap(async ({ elementsToQuery, existingPromises }) => {
|
|
199
|
+
existingPromises.push(this.createChildrenLoadingMapEntries({ elementsToQuery }).loadingMapEntries);
|
|
200
|
+
return Promise.all(existingPromises);
|
|
201
|
+
}));
|
|
202
|
+
}
|
|
203
|
+
getChildrenTree({ elementIds }) {
|
|
204
|
+
return this.createChildrenMapEntries({ elementIds }).pipe(map(() => this.getChildrenTreeFromMap({ elementIds })));
|
|
205
|
+
}
|
|
206
|
+
getAllChildrenCount({ elementIds }) {
|
|
207
|
+
return this.createChildrenMapEntries({ elementIds }).pipe(map(() => this.getChildrenCountMap({ elementIds })));
|
|
208
|
+
}
|
|
209
|
+
getSubjectInfos() {
|
|
210
|
+
this.#subjectInfos ??= forkJoin({
|
|
211
|
+
subjectInfos: this.querySubjects().pipe(reduce((acc, subject) => {
|
|
212
|
+
const subjectInfo = {
|
|
213
|
+
parentSubject: subject.parentId,
|
|
214
|
+
hideInHierarchy: subject.hideInHierarchy,
|
|
215
|
+
childSubjects: new Set(),
|
|
216
|
+
childModels: new Set(),
|
|
217
|
+
};
|
|
218
|
+
if (subject.targetPartitionId) {
|
|
219
|
+
subjectInfo.childModels.add(subject.targetPartitionId);
|
|
220
|
+
}
|
|
221
|
+
acc.set(subject.id, subjectInfo);
|
|
222
|
+
return acc;
|
|
223
|
+
}, new Map()), map((subjectInfos) => {
|
|
224
|
+
for (const [subjectId, { parentSubject: parentSubjectId }] of subjectInfos.entries()) {
|
|
225
|
+
if (parentSubjectId) {
|
|
226
|
+
const parentSubjectInfo = subjectInfos.get(parentSubjectId);
|
|
227
|
+
assert(!!parentSubjectInfo);
|
|
228
|
+
parentSubjectInfo.childSubjects.add(subjectId);
|
|
98
229
|
}
|
|
99
|
-
return result;
|
|
100
|
-
})(),
|
|
101
|
-
]);
|
|
102
|
-
for (const [subjectId, { parentSubject: parentSubjectId }] of subjectInfos.entries()) {
|
|
103
|
-
if (parentSubjectId) {
|
|
104
|
-
const parentSubjectInfo = subjectInfos.get(parentSubjectId);
|
|
105
|
-
assert(!!parentSubjectInfo);
|
|
106
|
-
parentSubjectInfo.childSubjects.add(subjectId);
|
|
107
230
|
}
|
|
108
|
-
|
|
231
|
+
return subjectInfos;
|
|
232
|
+
})),
|
|
233
|
+
targetPartitionSubjects: this.queryModels().pipe(reduce((acc, model) => {
|
|
234
|
+
pushToMap(acc, model.id, model.parentId);
|
|
235
|
+
return acc;
|
|
236
|
+
}, new Map())),
|
|
237
|
+
}).pipe(map(({ subjectInfos, targetPartitionSubjects }) => {
|
|
109
238
|
for (const [partitionId, subjectIds] of targetPartitionSubjects) {
|
|
110
239
|
subjectIds.forEach((subjectId) => {
|
|
111
240
|
const subjectInfo = subjectInfos.get(subjectId);
|
|
@@ -114,13 +243,12 @@ export class ModelsTreeIdsCache {
|
|
|
114
243
|
});
|
|
115
244
|
}
|
|
116
245
|
return subjectInfos;
|
|
117
|
-
})();
|
|
246
|
+
}), shareReplay());
|
|
118
247
|
return this.#subjectInfos;
|
|
119
248
|
}
|
|
120
249
|
/** Returns ECInstanceIDs of Subjects that either have direct Model or at least one child Subject with a Model. */
|
|
121
|
-
|
|
122
|
-
this.#parentSubjectIds ??= (
|
|
123
|
-
const subjectInfos = await this.getSubjectInfos();
|
|
250
|
+
getParentSubjectIds() {
|
|
251
|
+
this.#parentSubjectIds ??= this.getSubjectInfos().pipe(map((subjectInfos) => {
|
|
124
252
|
const parentSubjectIds = new Set();
|
|
125
253
|
subjectInfos.forEach((subjectInfo, subjectId) => {
|
|
126
254
|
if (subjectInfo.childModels.size > 0) {
|
|
@@ -133,71 +261,73 @@ export class ModelsTreeIdsCache {
|
|
|
133
261
|
}
|
|
134
262
|
});
|
|
135
263
|
return [...parentSubjectIds];
|
|
136
|
-
})();
|
|
264
|
+
}), shareReplay());
|
|
137
265
|
return this.#parentSubjectIds;
|
|
138
266
|
}
|
|
139
267
|
/**
|
|
140
268
|
* Returns child subjects of the specified parent subjects as they're displayed in the hierarchy - taking into
|
|
141
269
|
* account `hideInHierarchy` flag.
|
|
142
270
|
*/
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
271
|
+
getChildSubjectIds(parentSubjectIds) {
|
|
272
|
+
return this.getSubjectInfos().pipe(map((subjectInfos) => {
|
|
273
|
+
const childSubjectIds = new Array();
|
|
274
|
+
parentSubjectIds.forEach((subjectId) => {
|
|
275
|
+
forEachChildSubject(subjectInfos, subjectId, (childSubjectId, childSubjectInfo) => {
|
|
276
|
+
if (!childSubjectInfo.hideInHierarchy) {
|
|
277
|
+
childSubjectIds.push(childSubjectId);
|
|
278
|
+
return "break";
|
|
279
|
+
}
|
|
280
|
+
return "continue";
|
|
281
|
+
});
|
|
153
282
|
});
|
|
154
|
-
|
|
155
|
-
|
|
283
|
+
return childSubjectIds;
|
|
284
|
+
}));
|
|
156
285
|
}
|
|
157
286
|
/** Returns ECInstanceIDs of all Models under specific parent Subjects, including their child Subjects, etc. */
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
287
|
+
getSubjectModelIds(subjectIds) {
|
|
288
|
+
return this.getSubjectInfos().pipe(map((subjectInfos) => {
|
|
289
|
+
const subjectStack = [...subjectIds];
|
|
290
|
+
const result = new Array();
|
|
291
|
+
while (true) {
|
|
292
|
+
const subjectId = subjectStack.pop();
|
|
293
|
+
if (subjectId === undefined) {
|
|
294
|
+
break;
|
|
295
|
+
}
|
|
296
|
+
const subjectInfo = subjectInfos.get(subjectId);
|
|
297
|
+
if (!subjectInfo) {
|
|
298
|
+
continue;
|
|
299
|
+
}
|
|
300
|
+
result.push(...subjectInfo.childModels);
|
|
301
|
+
subjectStack.push(...subjectInfo.childSubjects);
|
|
170
302
|
}
|
|
171
|
-
result
|
|
172
|
-
|
|
173
|
-
}
|
|
174
|
-
return result;
|
|
303
|
+
return result;
|
|
304
|
+
}));
|
|
175
305
|
}
|
|
176
306
|
/** Returns ECInstanceIDs of Models under specific parent Subjects as they are displayed in the hierarchy. */
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
307
|
+
getChildSubjectModelIds(parentSubjectIds) {
|
|
308
|
+
return this.getSubjectInfos().pipe(map((subjectInfos) => {
|
|
309
|
+
const hiddenSubjectIds = new Array();
|
|
310
|
+
parentSubjectIds.forEach((subjectId) => {
|
|
311
|
+
forEachChildSubject(subjectInfos, subjectId, (childSubjectId, childSubjectInfo) => {
|
|
312
|
+
if (childSubjectInfo.hideInHierarchy) {
|
|
313
|
+
hiddenSubjectIds.push(childSubjectId);
|
|
314
|
+
return "continue";
|
|
315
|
+
}
|
|
316
|
+
return "break";
|
|
317
|
+
});
|
|
187
318
|
});
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
319
|
+
const modelIds = new Array();
|
|
320
|
+
[...parentSubjectIds, ...hiddenSubjectIds].forEach((subjectId) => {
|
|
321
|
+
const subjectInfo = subjectInfos.get(subjectId);
|
|
322
|
+
subjectInfo && modelIds.push(...subjectInfo.childModels);
|
|
323
|
+
});
|
|
324
|
+
return modelIds;
|
|
325
|
+
}));
|
|
195
326
|
}
|
|
196
|
-
|
|
327
|
+
createSubjectInstanceKeysPath(targetSubjectId) {
|
|
197
328
|
let entry = this.#subjectKeyPaths.get(targetSubjectId);
|
|
198
329
|
if (!entry) {
|
|
199
|
-
entry = (
|
|
200
|
-
const subjectInfos = await this.getSubjectInfos();
|
|
330
|
+
entry = this.getSubjectInfos().pipe(map((subjectInfos) => {
|
|
201
331
|
const result = new Array();
|
|
202
332
|
let currParentId = targetSubjectId;
|
|
203
333
|
while (currParentId) {
|
|
@@ -211,128 +341,111 @@ export class ModelsTreeIdsCache {
|
|
|
211
341
|
currParentId = parentInfo?.parentSubject;
|
|
212
342
|
}
|
|
213
343
|
return result.reverse();
|
|
214
|
-
})();
|
|
344
|
+
}), shareReplay());
|
|
215
345
|
this.#subjectKeyPaths.set(targetSubjectId, entry);
|
|
216
346
|
}
|
|
217
347
|
return entry;
|
|
218
348
|
}
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
}
|
|
349
|
+
queryModelCategories() {
|
|
350
|
+
return defer(() => {
|
|
351
|
+
const query = `
|
|
352
|
+
SELECT
|
|
353
|
+
this.Model.Id modelId,
|
|
354
|
+
this.Category.Id categoryId,
|
|
355
|
+
m.IsPrivate isModelPrivate,
|
|
356
|
+
MAX(IIF(this.Parent.Id IS NULL, 1, 0)) isRootElementCategory
|
|
357
|
+
FROM BisCore.Model m
|
|
358
|
+
JOIN ${this.#hierarchyConfig.elementClassSpecification} this ON m.ECInstanceId = this.Model.Id
|
|
359
|
+
GROUP BY modelId, categoryId, isModelPrivate
|
|
360
|
+
`;
|
|
361
|
+
return this.#queryExecutor.createQueryReader({ ecsql: query }, { rowFormat: "ECSqlPropertyNames", limit: "unbounded", restartToken: `${this.#componentName}/${this.#componentId}/model-categories` });
|
|
362
|
+
}).pipe(map((row) => {
|
|
363
|
+
return { modelId: row.modelId, categoryId: row.categoryId, isModelPrivate: !!row.isModelPrivate, isRootElementCategory: !!row.isRootElementCategory };
|
|
364
|
+
}));
|
|
233
365
|
}
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
}
|
|
366
|
+
queryModeledElements() {
|
|
367
|
+
return defer(() => {
|
|
368
|
+
const query = `
|
|
369
|
+
SELECT
|
|
370
|
+
pe.ECInstanceId modeledElementId,
|
|
371
|
+
pe.Category.Id categoryId,
|
|
372
|
+
pe.Model.Id modelId
|
|
373
|
+
FROM BisCore.Model m
|
|
374
|
+
JOIN ${this.#hierarchyConfig.elementClassSpecification} pe ON pe.ECInstanceId = m.ModeledElement.Id
|
|
375
|
+
WHERE
|
|
376
|
+
m.IsPrivate = false
|
|
377
|
+
AND m.ECInstanceId IN (SELECT Model.Id FROM ${this.#hierarchyConfig.elementClassSpecification})
|
|
378
|
+
`;
|
|
379
|
+
return this.#queryExecutor.createQueryReader({ ecsql: query }, { rowFormat: "ECSqlPropertyNames", limit: "unbounded", restartToken: `${this.#componentName}/${this.#componentId}/modeled-elements` });
|
|
380
|
+
}).pipe(map((row) => {
|
|
381
|
+
return { modelId: row.modelId, categoryId: row.categoryId, modeledElementId: row.modeledElementId };
|
|
382
|
+
}));
|
|
249
383
|
}
|
|
250
|
-
|
|
251
|
-
this.#modelWithCategoryModeledElements ??= (
|
|
252
|
-
const
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
if (entry === undefined) {
|
|
257
|
-
modelWithCategoryModeledElements.set(key, new Set([modeledElementId]));
|
|
258
|
-
}
|
|
259
|
-
else {
|
|
260
|
-
entry.add(modeledElementId);
|
|
261
|
-
}
|
|
384
|
+
getModelWithCategoryModeledElements() {
|
|
385
|
+
this.#modelWithCategoryModeledElements ??= this.queryModeledElements().pipe(reduce((acc, { modelId, categoryId, modeledElementId }) => {
|
|
386
|
+
const key = `${modelId}-${categoryId}`;
|
|
387
|
+
const entry = acc.get(key);
|
|
388
|
+
if (entry === undefined) {
|
|
389
|
+
acc.set(key, new Set([modeledElementId]));
|
|
262
390
|
}
|
|
263
|
-
|
|
264
|
-
|
|
391
|
+
else {
|
|
392
|
+
entry.add(modeledElementId);
|
|
393
|
+
}
|
|
394
|
+
return acc;
|
|
395
|
+
}, new Map()), shareReplay());
|
|
265
396
|
return this.#modelWithCategoryModeledElements;
|
|
266
397
|
}
|
|
267
|
-
|
|
268
|
-
this.#modelInfos ??= (
|
|
269
|
-
const
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
}
|
|
276
|
-
else {
|
|
277
|
-
modelInfos.set(modelId, { categories: new Map([[categoryId, { isRootElementCategory }]]), isModelPrivate });
|
|
278
|
-
}
|
|
398
|
+
getModelInfos() {
|
|
399
|
+
this.#modelInfos ??= this.queryModelCategories().pipe(reduce((acc, { modelId, categoryId, isModelPrivate, isRootElementCategory }) => {
|
|
400
|
+
const entry = acc.get(modelId);
|
|
401
|
+
if (entry) {
|
|
402
|
+
entry.categories.set(categoryId, { isRootElementCategory });
|
|
403
|
+
entry.isModelPrivate = isModelPrivate;
|
|
404
|
+
}
|
|
405
|
+
else {
|
|
406
|
+
acc.set(modelId, { categories: new Map([[categoryId, { isRootElementCategory }]]), isModelPrivate });
|
|
279
407
|
}
|
|
280
|
-
return
|
|
281
|
-
})();
|
|
408
|
+
return acc;
|
|
409
|
+
}, new Map()), shareReplay());
|
|
282
410
|
return this.#modelInfos;
|
|
283
411
|
}
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
});
|
|
290
|
-
return result;
|
|
412
|
+
getAllCategories() {
|
|
413
|
+
return this.getModelInfos().pipe(mergeMap((modelInfos) => modelInfos.values()), mergeMap(({ categories }) => categories.keys()), reduce((acc, categoryId) => {
|
|
414
|
+
acc.add(categoryId);
|
|
415
|
+
return acc;
|
|
416
|
+
}, new Set()));
|
|
291
417
|
}
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
const categories = modelInfos.get(modelId)?.categories.keys();
|
|
295
|
-
return categories ? [...categories] : [];
|
|
418
|
+
getModelCategories(modelId) {
|
|
419
|
+
return this.getModelInfos().pipe(mergeMap((modelInfos) => modelInfos.get(modelId)?.categories.keys() ?? []), toArray());
|
|
296
420
|
}
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
421
|
+
hasSubModel(elementId) {
|
|
422
|
+
return this.getModelInfos().pipe(map((modelInfos) => {
|
|
423
|
+
const modeledElementInfo = modelInfos.get(elementId);
|
|
424
|
+
if (!modeledElementInfo) {
|
|
425
|
+
return false;
|
|
426
|
+
}
|
|
427
|
+
return !modeledElementInfo.isModelPrivate;
|
|
428
|
+
}));
|
|
304
429
|
}
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
const result = new Array();
|
|
308
|
-
for (const categoryId of Id64.iterable(categoryIds)) {
|
|
430
|
+
getCategoriesModeledElements(modelId, categoryIds) {
|
|
431
|
+
return this.getModelWithCategoryModeledElements().pipe(mergeMap((modelWithCategoryModeledElements) => from(Id64.iterable(categoryIds)).pipe(reduce((acc, categoryId) => {
|
|
309
432
|
const entry = modelWithCategoryModeledElements.get(`${modelId}-${categoryId}`);
|
|
310
433
|
if (entry !== undefined) {
|
|
311
|
-
|
|
434
|
+
acc.push(...entry);
|
|
312
435
|
}
|
|
313
|
-
|
|
314
|
-
|
|
436
|
+
return acc;
|
|
437
|
+
}, new Array()))));
|
|
315
438
|
}
|
|
316
|
-
|
|
439
|
+
createModelInstanceKeyPaths(modelId) {
|
|
317
440
|
let entry = this.#modelKeyPaths.get(modelId);
|
|
318
441
|
if (!entry) {
|
|
319
|
-
entry = (
|
|
320
|
-
const result = new Array();
|
|
321
|
-
const subjectInfos = (await this.getSubjectInfos()).entries();
|
|
322
|
-
for (const [modelSubjectId, subjectInfo] of subjectInfos) {
|
|
323
|
-
if (subjectInfo.childModels.has(modelId)) {
|
|
324
|
-
const subjectPath = await this.createSubjectInstanceKeysPath(modelSubjectId);
|
|
325
|
-
result.push([...subjectPath, { className: "BisCore.GeometricModel3d", id: modelId }]);
|
|
326
|
-
}
|
|
327
|
-
}
|
|
328
|
-
return result;
|
|
329
|
-
})();
|
|
442
|
+
entry = this.getSubjectInfos().pipe(mergeMap((subjectInfos) => subjectInfos.entries()), filter(([_, subjectInfo]) => subjectInfo.childModels.has(modelId)), mergeMap(([modelSubjectId]) => this.createSubjectInstanceKeysPath(modelSubjectId).pipe(map((path) => [...path, { className: "BisCore.GeometricModel3d", id: modelId }]))), toArray(), shareReplay());
|
|
330
443
|
this.#modelKeyPaths.set(modelId, entry);
|
|
331
444
|
}
|
|
332
445
|
return entry;
|
|
333
446
|
}
|
|
334
|
-
|
|
335
|
-
return
|
|
447
|
+
queryCategoryElementCounts(input) {
|
|
448
|
+
return from(input).pipe(reduce((acc, { modelId, categoryId }) => {
|
|
336
449
|
const entry = acc.get(modelId);
|
|
337
450
|
if (!entry) {
|
|
338
451
|
acc.set(modelId, new Set([categoryId]));
|
|
@@ -344,73 +457,64 @@ export class ModelsTreeIdsCache {
|
|
|
344
457
|
}, new Map()), mergeMap((modelCategoryMap) => modelCategoryMap.entries()), map(([modelId, categoryIds]) => `Model.Id = ${modelId} AND Category.Id IN (${[...categoryIds].join(", ")})`),
|
|
345
458
|
// we may have thousands of where clauses here, and sending a single query with all of them could take a
|
|
346
459
|
// long time - instead, split it into smaller chunks
|
|
347
|
-
bufferCount(100), mergeMap(
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
)
|
|
460
|
+
bufferCount(100), mergeMap((whereClauses) => defer(() => this.#queryExecutor.createQueryReader({
|
|
461
|
+
ctes: [
|
|
462
|
+
`
|
|
463
|
+
CategoryElements(id, modelId, categoryId) AS (
|
|
464
|
+
SELECT ECInstanceId, Model.Id, Category.Id
|
|
465
|
+
FROM ${this.#hierarchyConfig.elementClassSpecification}
|
|
466
|
+
WHERE
|
|
467
|
+
Parent.Id IS NULL
|
|
468
|
+
AND (
|
|
469
|
+
${whereClauses.join(" OR ")}
|
|
470
|
+
)
|
|
359
471
|
|
|
360
|
-
|
|
472
|
+
UNION ALL
|
|
361
473
|
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
474
|
+
SELECT c.ECInstanceId, p.modelId, p.categoryId
|
|
475
|
+
FROM ${this.#hierarchyConfig.elementClassSpecification} c
|
|
476
|
+
JOIN CategoryElements p ON c.Parent.Id = p.id
|
|
477
|
+
)
|
|
478
|
+
`,
|
|
479
|
+
],
|
|
480
|
+
ecsql: `
|
|
481
|
+
SELECT modelId, categoryId, COUNT(id) elementsCount
|
|
482
|
+
FROM CategoryElements
|
|
483
|
+
GROUP BY modelId, categoryId
|
|
366
484
|
`,
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
restartToken: `${this.#componentName}/${this.#componentId}/category-element-counts/${Guid.createValue()}`,
|
|
485
|
+
}, {
|
|
486
|
+
rowFormat: "ECSqlPropertyNames",
|
|
487
|
+
limit: "unbounded",
|
|
488
|
+
restartToken: `${this.#componentName}/${this.#componentId}/category-element-counts/${Guid.createValue()}`,
|
|
489
|
+
}))), releaseMainThreadOnItemsCount(500), reduce(({ acc, createKey }, row) => {
|
|
490
|
+
acc.set(createKey({ modelId: row.modelId, categoryId: row.categoryId }), {
|
|
491
|
+
modelId: row.modelId,
|
|
492
|
+
categoryId: row.categoryId,
|
|
493
|
+
elementsCount: row.elementsCount,
|
|
377
494
|
});
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
}
|
|
495
|
+
return { acc, createKey };
|
|
496
|
+
}, {
|
|
497
|
+
acc: new Map(),
|
|
498
|
+
createKey: (keyProps) => `${keyProps.modelId}-${keyProps.categoryId}`,
|
|
499
|
+
}), mergeMap(({ acc: result, createKey }) => {
|
|
382
500
|
input.forEach(({ modelId, categoryId }) => {
|
|
383
|
-
if (!result.
|
|
384
|
-
result.
|
|
501
|
+
if (!result.has(createKey({ modelId, categoryId }))) {
|
|
502
|
+
result.set(createKey({ modelId, categoryId }), { categoryId, modelId, elementsCount: 0 });
|
|
385
503
|
}
|
|
386
504
|
});
|
|
387
|
-
return result;
|
|
388
|
-
}),
|
|
505
|
+
return from(result.values());
|
|
506
|
+
}), toArray());
|
|
389
507
|
}
|
|
390
|
-
|
|
508
|
+
getCategoryElementsCount(modelId, categoryId) {
|
|
391
509
|
return this.#categoryElementCounts.getCategoryElementsCount(modelId, categoryId);
|
|
392
510
|
}
|
|
393
|
-
|
|
511
|
+
createCategoryInstanceKeyPaths(categoryId) {
|
|
394
512
|
let entry = this.#categoryKeyPaths.get(categoryId);
|
|
395
513
|
if (!entry) {
|
|
396
|
-
entry = (
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
const categoryEntry = modelInfo.categories.get(categoryId);
|
|
401
|
-
if (categoryEntry?.isRootElementCategory) {
|
|
402
|
-
result.add(modelId);
|
|
403
|
-
}
|
|
404
|
-
});
|
|
405
|
-
const categoryPaths = new Array();
|
|
406
|
-
for (const categoryModelId of [...result]) {
|
|
407
|
-
const modelPaths = await this.createModelInstanceKeyPaths(categoryModelId);
|
|
408
|
-
for (const modelPath of modelPaths) {
|
|
409
|
-
categoryPaths.push([...modelPath, { className: "BisCore.SpatialCategory", id: categoryId }]);
|
|
410
|
-
}
|
|
411
|
-
}
|
|
412
|
-
return categoryPaths;
|
|
413
|
-
})();
|
|
514
|
+
entry = this.getModelInfos().pipe(mergeMap((modelInfos) => modelInfos.entries()), filter(([_, modelInfo]) => !!modelInfo.categories.get(categoryId)?.isRootElementCategory), mergeMap(([categoryModelId]) => this.createModelInstanceKeyPaths(categoryModelId)), mergeMap((modelPaths) => modelPaths), reduce((acc, modelPath) => {
|
|
515
|
+
acc.push([...modelPath, { className: "BisCore.SpatialCategory", id: categoryId }]);
|
|
516
|
+
return acc;
|
|
517
|
+
}, new Array()), shareReplay());
|
|
414
518
|
this.#categoryKeyPaths.set(categoryId, entry);
|
|
415
519
|
}
|
|
416
520
|
return entry;
|
|
@@ -433,7 +537,7 @@ class ModelCategoryElementsCountCache {
|
|
|
433
537
|
#subscription;
|
|
434
538
|
constructor(loader) {
|
|
435
539
|
this.#subscription = this.#requestsStream
|
|
436
|
-
.pipe(bufferTime(20), filter((requests) => requests.length > 0), mergeMap(
|
|
540
|
+
.pipe(bufferTime(20), filter((requests) => requests.length > 0), mergeMap((requests) => loader(requests)), mergeAll())
|
|
437
541
|
.subscribe({
|
|
438
542
|
next: ({ modelId, categoryId, elementsCount }) => {
|
|
439
543
|
const subject = this.#cache.get(`${modelId}${categoryId}`);
|
|
@@ -445,16 +549,16 @@ class ModelCategoryElementsCountCache {
|
|
|
445
549
|
[Symbol.dispose]() {
|
|
446
550
|
this.#subscription.unsubscribe();
|
|
447
551
|
}
|
|
448
|
-
|
|
552
|
+
getCategoryElementsCount(modelId, categoryId) {
|
|
449
553
|
const cacheKey = `${modelId}${categoryId}`;
|
|
450
554
|
let result = this.#cache.get(cacheKey);
|
|
451
555
|
if (result !== undefined) {
|
|
452
|
-
return
|
|
556
|
+
return from(result).pipe(take(1));
|
|
453
557
|
}
|
|
454
558
|
result = new ReplaySubject(1);
|
|
455
559
|
this.#cache.set(cacheKey, result);
|
|
456
560
|
this.#requestsStream.next({ modelId, categoryId });
|
|
457
|
-
return
|
|
561
|
+
return from(result).pipe(take(1));
|
|
458
562
|
}
|
|
459
563
|
}
|
|
460
564
|
//# sourceMappingURL=ModelsTreeIdsCache.js.map
|