@itwin/presentation-backend 5.14.0-dev.9 → 5.15.0-dev.2
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 +36 -1
- package/lib/cjs/presentation-backend/ElementPropertiesHelper.d.ts +0 -17
- package/lib/cjs/presentation-backend/ElementPropertiesHelper.d.ts.map +1 -1
- package/lib/cjs/presentation-backend/ElementPropertiesHelper.js +117 -67
- package/lib/cjs/presentation-backend/ElementPropertiesHelper.js.map +1 -1
- package/lib/esm/presentation-backend/ElementPropertiesHelper.d.ts +0 -17
- package/lib/esm/presentation-backend/ElementPropertiesHelper.d.ts.map +1 -1
- package/lib/esm/presentation-backend/ElementPropertiesHelper.js +120 -68
- package/lib/esm/presentation-backend/ElementPropertiesHelper.js.map +1 -1
- package/package.json +15 -15
package/CHANGELOG.md
CHANGED
|
@@ -1,6 +1,41 @@
|
|
|
1
1
|
# Change Log - @itwin/presentation-backend
|
|
2
2
|
|
|
3
|
-
This log was last generated on Thu,
|
|
3
|
+
This log was last generated on Thu, 01 Oct 2026 15:17:55 GMT and should not be manually modified.
|
|
4
|
+
|
|
5
|
+
## 5.13.7
|
|
6
|
+
Thu, 01 Oct 2026 15:15:30 GMT
|
|
7
|
+
|
|
8
|
+
_Version update only_
|
|
9
|
+
|
|
10
|
+
## 5.13.6
|
|
11
|
+
Fri, 25 Sep 2026 22:27:22 GMT
|
|
12
|
+
|
|
13
|
+
_Version update only_
|
|
14
|
+
|
|
15
|
+
## 5.13.5
|
|
16
|
+
Tue, 22 Sep 2026 22:38:08 GMT
|
|
17
|
+
|
|
18
|
+
_Version update only_
|
|
19
|
+
|
|
20
|
+
## 5.13.4
|
|
21
|
+
Fri, 18 Sep 2026 15:11:19 GMT
|
|
22
|
+
|
|
23
|
+
_Version update only_
|
|
24
|
+
|
|
25
|
+
## 5.13.3
|
|
26
|
+
Tue, 15 Sep 2026 20:25:28 GMT
|
|
27
|
+
|
|
28
|
+
_Version update only_
|
|
29
|
+
|
|
30
|
+
## 5.13.2
|
|
31
|
+
Mon, 14 Sep 2026 20:22:20 GMT
|
|
32
|
+
|
|
33
|
+
_Version update only_
|
|
34
|
+
|
|
35
|
+
## 5.13.1
|
|
36
|
+
Tue, 08 Sep 2026 18:43:29 GMT
|
|
37
|
+
|
|
38
|
+
_Version update only_
|
|
4
39
|
|
|
5
40
|
## 5.13.0
|
|
6
41
|
Thu, 03 Sep 2026 18:49:56 GMT
|
|
@@ -21,23 +21,6 @@ export declare function getContentItemsObservableFromClassNames(imodel: IModelDb
|
|
|
21
21
|
}>;
|
|
22
22
|
count: Observable<number>;
|
|
23
23
|
};
|
|
24
|
-
/**
|
|
25
|
-
* Given a sorted list of ECInstanceIds and a batch size, create a stream of batches. Because the IDs won't necessarily
|
|
26
|
-
* be sequential, a batch is defined a list of from-to pairs.
|
|
27
|
-
* @internal
|
|
28
|
-
*/
|
|
29
|
-
export declare function createIdBatches(sortedIds: Id64String[], batchSize: number): Observable<Array<{
|
|
30
|
-
from: Id64String;
|
|
31
|
-
to: Id64String;
|
|
32
|
-
}>>;
|
|
33
|
-
/**
|
|
34
|
-
* Query all ECInstanceIds from given class and stream from-to pairs that batch the items into batches of `batchSize` size.
|
|
35
|
-
* @internal
|
|
36
|
-
*/
|
|
37
|
-
export declare function getBatchedClassElementIds(imodel: IModelDb, fullClassName: string, batchSize: number): Observable<Array<{
|
|
38
|
-
from: Id64String;
|
|
39
|
-
to: Id64String;
|
|
40
|
-
}>>;
|
|
41
24
|
/** @internal */
|
|
42
25
|
export declare function getElementsCount(db: IModelDb, classNames: string[]): Promise<any>;
|
|
43
26
|
//# sourceMappingURL=ElementPropertiesHelper.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ElementPropertiesHelper.d.ts","sourceRoot":"","sources":["../../../src/presentation-backend/ElementPropertiesHelper.ts"],"names":[],"mappings":"AAIA;;GAEG;AAEH,OAAO,EAAoD,UAAU,EAAsC,MAAM,MAAM,CAAC;AACxH,OAAO,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAC;AAC/C,OAAO,
|
|
1
|
+
{"version":3,"file":"ElementPropertiesHelper.d.ts","sourceRoot":"","sources":["../../../src/presentation-backend/ElementPropertiesHelper.ts"],"names":[],"mappings":"AAIA;;GAEG;AAEH,OAAO,EAAoD,UAAU,EAAsC,MAAM,MAAM,CAAC;AACxH,OAAO,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAC;AAC/C,OAAO,EAAa,UAAU,EAAuB,MAAM,qBAAqB,CAAC;AAEjF,OAAO,EACL,+BAA+B,EAC/B,qBAAqB,EACrB,UAAU,EAEV,IAAI,EACJ,MAAM,EAKN,eAAe,EAChB,MAAM,4BAA4B,CAAC;AAQpC,gBAAgB;AAChB,wBAAgB,uCAAuC,CACrD,MAAM,EAAE,QAAQ,EAChB,uBAAuB,EAAE,CACvB,YAAY,EAAE,IAAI,CAAC,+BAA+B,CAAC,QAAQ,EAAE,MAAM,EAAE,eAAe,CAAC,EAAE,aAAa,GAAG,MAAM,CAAC,KAC3G,OAAO,CAAC,UAAU,GAAG,SAAS,CAAC,EACpC,gBAAgB,EAAE,CAChB,YAAY,EAAE,IAAI,CAAC,qBAAqB,CAAC,QAAQ,EAAE,UAAU,EAAE,MAAM,EAAE,eAAe,CAAC,EAAE,aAAa,GAAG,MAAM,GAAG,YAAY,CAAC,KAC5H,OAAO,CAAC,IAAI,EAAE,CAAC,EACpB,UAAU,EAAE,UAAU,EAAE,EACxB,gBAAgB,EAAE,MAAM,EACxB,kBAAkB,EAAE,MAAM,EAC1B,SAAS,EAAE,MAAM,GAChB;IAAE,WAAW,EAAE,UAAU,CAAC;QAAE,UAAU,EAAE,UAAU,CAAC;QAAC,KAAK,EAAE,IAAI,EAAE,CAAA;KAAE,CAAC,CAAC;IAAC,KAAK,EAAE,UAAU,CAAC,MAAM,CAAC,CAAA;CAAE,CAkBnG;AAED,gBAAgB;AAChB,wBAAgB,uCAAuC,CACrD,MAAM,EAAE,QAAQ,EAChB,uBAAuB,EAAE,CACvB,YAAY,EAAE,IAAI,CAAC,+BAA+B,CAAC,QAAQ,EAAE,MAAM,EAAE,eAAe,CAAC,EAAE,aAAa,GAAG,MAAM,CAAC,KAC3G,OAAO,CAAC,UAAU,GAAG,SAAS,CAAC,EACpC,gBAAgB,EAAE,CAChB,YAAY,EAAE,IAAI,CAAC,qBAAqB,CAAC,QAAQ,EAAE,UAAU,EAAE,MAAM,EAAE,eAAe,CAAC,EAAE,aAAa,GAAG,MAAM,GAAG,YAAY,CAAC,KAC5H,OAAO,CAAC,IAAI,EAAE,CAAC,EACpB,cAAc,EAAE,MAAM,EAAE,EACxB,gBAAgB,EAAE,MAAM,EACxB,kBAAkB,EAAE,MAAM,EAC1B,SAAS,EAAE,MAAM,GAChB;IAAE,WAAW,EAAE,UAAU,CAAC;QAAE,UAAU,EAAE,UAAU,CAAC;QAAC,KAAK,EAAE,IAAI,EAAE,CAAA;KAAE,CAAC,CAAC;IAAC,KAAK,EAAE,UAAU,CAAC,MAAM,CAAC,CAAA;CAAE,CAkBnG;AAsQD,gBAAgB;AAChB,wBAAsB,gBAAgB,CAAC,EAAE,EAAE,QAAQ,EAAE,UAAU,EAAE,MAAM,EAAE,gBA0BxE"}
|
|
@@ -9,11 +9,10 @@
|
|
|
9
9
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
10
10
|
exports.getContentItemsObservableFromElementIds = getContentItemsObservableFromElementIds;
|
|
11
11
|
exports.getContentItemsObservableFromClassNames = getContentItemsObservableFromClassNames;
|
|
12
|
-
exports.createIdBatches = createIdBatches;
|
|
13
|
-
exports.getBatchedClassElementIds = getBatchedClassElementIds;
|
|
14
12
|
exports.getElementsCount = getElementsCount;
|
|
15
13
|
const rxjs_1 = require("rxjs");
|
|
16
14
|
const core_bentley_1 = require("@itwin/core-bentley");
|
|
15
|
+
const core_common_1 = require("@itwin/core-common");
|
|
17
16
|
const presentation_common_1 = require("@itwin/presentation-common");
|
|
18
17
|
const presentation_shared_1 = require("@itwin/presentation-shared");
|
|
19
18
|
function getECSqlName(fullClassName) {
|
|
@@ -23,18 +22,18 @@ function getECSqlName(fullClassName) {
|
|
|
23
22
|
/** @internal */
|
|
24
23
|
function getContentItemsObservableFromElementIds(imodel, contentDescriptorGetter, contentSetGetter, elementIds, classParallelism, batchesParallelism, batchSize) {
|
|
25
24
|
return {
|
|
26
|
-
itemBatches: getElementClassesFromIds(imodel, elementIds).pipe((0, rxjs_1.mergeMap)(({ classFullName, ids }) => getBatchedClassContentItems(classFullName, contentDescriptorGetter, contentSetGetter, () => createIdBatches(
|
|
25
|
+
itemBatches: getElementClassesFromIds(imodel, elementIds).pipe((0, rxjs_1.mergeMap)(({ classFullName, ids }) => getBatchedClassContentItems(imodel, classFullName, contentDescriptorGetter, contentSetGetter, () => createIdBatches(ids, batchSize), batchesParallelism), classParallelism)),
|
|
27
26
|
count: (0, rxjs_1.of)(elementIds.length),
|
|
28
27
|
};
|
|
29
28
|
}
|
|
30
29
|
/** @internal */
|
|
31
30
|
function getContentItemsObservableFromClassNames(imodel, contentDescriptorGetter, contentSetGetter, elementClasses, classParallelism, batchesParallelism, batchSize) {
|
|
32
31
|
return {
|
|
33
|
-
itemBatches: getClassesWithInstances(imodel, elementClasses).pipe((0, rxjs_1.mergeMap)((classFullName) => getBatchedClassContentItems(classFullName, contentDescriptorGetter, contentSetGetter, () => getBatchedClassElementIds(imodel, classFullName, batchSize), batchesParallelism), classParallelism)),
|
|
32
|
+
itemBatches: getClassesWithInstances(imodel, elementClasses).pipe((0, rxjs_1.mergeMap)((classFullName) => getBatchedClassContentItems(imodel, classFullName, contentDescriptorGetter, contentSetGetter, () => getBatchedClassElementIds(imodel, classFullName, batchSize), batchesParallelism), classParallelism)),
|
|
34
33
|
count: (0, rxjs_1.from)(getElementsCount(imodel, elementClasses)),
|
|
35
34
|
};
|
|
36
35
|
}
|
|
37
|
-
function getBatchedClassContentItems(classFullName, contentDescriptorGetter, contentSetGetter, batcher, batchesParallelism) {
|
|
36
|
+
function getBatchedClassContentItems(imodel, classFullName, contentDescriptorGetter, contentSetGetter, batcher, batchesParallelism) {
|
|
38
37
|
return (0, rxjs_1.defer)(async () => {
|
|
39
38
|
const ruleset = createClassContentRuleset(classFullName);
|
|
40
39
|
const keys = new presentation_common_1.KeySet();
|
|
@@ -42,41 +41,70 @@ function getBatchedClassContentItems(classFullName, contentDescriptorGetter, con
|
|
|
42
41
|
if (!descriptor) {
|
|
43
42
|
throw new presentation_common_1.PresentationError(presentation_common_1.PresentationStatus.Error, `Failed to get descriptor for class ${classFullName}`);
|
|
44
43
|
}
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
(0, rxjs_1.mergeMap)(({ descriptor, keys, ruleset, batch }) => (0, rxjs_1.defer)(async () => {
|
|
51
|
-
const filteringDescriptor = new presentation_common_1.Descriptor(descriptor);
|
|
52
|
-
filteringDescriptor.instanceFilter = {
|
|
44
|
+
const aspectFields = getPrunableAspectFields(descriptor, classFullName);
|
|
45
|
+
return { descriptor, keys, ruleset, aspectFields };
|
|
46
|
+
}).pipe((0, rxjs_1.mergeMap)((x) => batcher().pipe((0, rxjs_1.map)((batch) => ({ ...x, batch })))), (0, rxjs_1.mergeMap)(({ descriptor, keys, ruleset, batch, aspectFields }) => (0, rxjs_1.defer)(async () => {
|
|
47
|
+
const batchDescriptor = new presentation_common_1.Descriptor(descriptor);
|
|
48
|
+
batchDescriptor.instanceFilter = {
|
|
53
49
|
selectClassName: classFullName,
|
|
54
|
-
expression:
|
|
50
|
+
expression: batch.ranges
|
|
51
|
+
.map(({ from: fromId, to }) => fromId === to ? `this.ECInstanceId = ${fromId}` : `this.ECInstanceId >= ${fromId} AND this.ECInstanceId <= ${to}`)
|
|
52
|
+
.join(" OR "),
|
|
55
53
|
};
|
|
56
|
-
|
|
54
|
+
if (aspectFields.length) {
|
|
55
|
+
await selectBatchAspectFields(imodel, batchDescriptor, aspectFields, batch.ids);
|
|
56
|
+
}
|
|
57
|
+
const items = await contentSetGetter({
|
|
57
58
|
rulesetOrId: ruleset,
|
|
58
59
|
keys,
|
|
59
|
-
descriptor:
|
|
60
|
+
descriptor: batchDescriptor,
|
|
60
61
|
});
|
|
61
|
-
|
|
62
|
+
return { descriptor: batchDescriptor, items };
|
|
63
|
+
}), batchesParallelism));
|
|
62
64
|
}
|
|
63
|
-
function
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
filter += " OR ";
|
|
68
|
-
}
|
|
69
|
-
filter += cond;
|
|
65
|
+
function getPrunableAspectFields(descriptor, classFullName) {
|
|
66
|
+
// Caller selections take precedence; don't replace an include/exclude selector with our own.
|
|
67
|
+
if (descriptor.fieldsSelector || countFields(descriptor.fields) <= 1000) {
|
|
68
|
+
return [];
|
|
70
69
|
}
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
else {
|
|
76
|
-
appendCondition(`this.ECInstanceId >= ${item.from} AND this.ECInstanceId <= ${item.to}`);
|
|
70
|
+
const { schemaName, className } = (0, presentation_shared_1.parseFullClassName)(classFullName);
|
|
71
|
+
return descriptor.fields.filter((field) => {
|
|
72
|
+
if (!field.isNestedContentField() || field.pathToPrimaryClass.length !== 1) {
|
|
73
|
+
return false;
|
|
77
74
|
}
|
|
75
|
+
const step = field.pathToPrimaryClass[0];
|
|
76
|
+
return (!step.isForwardRelationship &&
|
|
77
|
+
(step.relationshipInfo.name === "BisCore:ElementOwnsMultiAspects" || step.relationshipInfo.name === "BisCore:ElementOwnsUniqueAspect") &&
|
|
78
|
+
step.sourceClassInfo.id === field.contentClassInfo.id &&
|
|
79
|
+
step.targetClassInfo.name === `${schemaName}:${className}`);
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
function countFields(fields) {
|
|
83
|
+
return fields.reduce((count, field) => count + 1 + (field.isNestedContentField() ? countFields(field.nestedFields) : 0), 0);
|
|
84
|
+
}
|
|
85
|
+
async function selectBatchAspectFields(imodel, descriptor, aspectFields, batch) {
|
|
86
|
+
const query = `
|
|
87
|
+
WITH aspectClasses(ClassId) AS (
|
|
88
|
+
SELECT DISTINCT a.ECClassId
|
|
89
|
+
FROM bis.ElementMultiAspect a
|
|
90
|
+
JOIN IdSet(:elementIds) e ON e.id = a.Element.Id
|
|
91
|
+
UNION ALL
|
|
92
|
+
SELECT DISTINCT a.ECClassId
|
|
93
|
+
FROM bis.ElementUniqueAspect a
|
|
94
|
+
JOIN IdSet(:elementIds) e ON e.id = a.Element.Id
|
|
95
|
+
)
|
|
96
|
+
SELECT IdToHex(b.TargetECInstanceId) classId
|
|
97
|
+
FROM meta.ClassHasAllBaseClasses b
|
|
98
|
+
JOIN aspectClasses a ON a.ClassId = b.SourceECInstanceId
|
|
99
|
+
`;
|
|
100
|
+
const aspectClasses = new Set();
|
|
101
|
+
for await (const row of imodel.createQueryReader(query, core_common_1.QueryBinder.from({ elementIds: batch }))) {
|
|
102
|
+
aspectClasses.add(row.classId);
|
|
103
|
+
}
|
|
104
|
+
const excludedFields = aspectFields.filter((field) => !aspectClasses.has(field.contentClassInfo.id));
|
|
105
|
+
if (excludedFields.length > 0) {
|
|
106
|
+
descriptor.fieldsSelector = { type: "exclude", fields: excludedFields.map((field) => field.getFieldDescriptor()) };
|
|
78
107
|
}
|
|
79
|
-
return filter;
|
|
80
108
|
}
|
|
81
109
|
function createClassContentRuleset(fullClassName) {
|
|
82
110
|
const { schemaName, className } = (0, presentation_shared_1.parseFullClassName)(fullClassName);
|
|
@@ -100,27 +128,59 @@ function createClassContentRuleset(fullClassName) {
|
|
|
100
128
|
],
|
|
101
129
|
};
|
|
102
130
|
}
|
|
103
|
-
/**
|
|
131
|
+
/**
|
|
132
|
+
* Given a list of element ids, group them by class name. In addition, for every element, get ID of the previous
|
|
133
|
+
* element of the same class, which lets us put requested IDs into ranges without other instances of the same class.
|
|
134
|
+
*
|
|
135
|
+
* The query reader re-executes the query for every page, so querying all IDs at once is much slower than querying
|
|
136
|
+
* them in chunks. Chunks are made of the sorted IDs list and run in parallel. `GROUP_CONCAT` order isn't guaranteed
|
|
137
|
+
* by SQLite, so each chunk's IDs are sorted individually (usually a no-op check), then stored at the chunk's index.
|
|
138
|
+
* Concatenating them gives each class its IDs sorted.
|
|
139
|
+
*/
|
|
104
140
|
function getElementClassesFromIds(imodel, elementIds) {
|
|
105
141
|
const elementIdsBatchSize = 5000;
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
const
|
|
142
|
+
const sortedElementIds = core_bentley_1.OrderedId64Iterable.sortArray([...elementIds]);
|
|
143
|
+
return (0, rxjs_1.range)(0, sortedElementIds.length / elementIdsBatchSize).pipe((0, rxjs_1.mergeMap)((_, chunkIndex) => {
|
|
144
|
+
const idsFrom = chunkIndex * elementIdsBatchSize;
|
|
145
|
+
const idsTo = Math.min(idsFrom + elementIdsBatchSize, sortedElementIds.length);
|
|
109
146
|
return (0, rxjs_1.from)(imodel.createQueryReader(`
|
|
110
|
-
SELECT
|
|
147
|
+
SELECT
|
|
148
|
+
ec_classname(e.ECClassId) className,
|
|
149
|
+
GROUP_CONCAT(
|
|
150
|
+
IdToHex(e.ECInstanceId) || ':' || IFNULL((
|
|
151
|
+
SELECT IdToHex(MAX(p.ECInstanceId))
|
|
152
|
+
FROM bis.Element p
|
|
153
|
+
WHERE p.ECClassId = e.ECClassId AND p.ECInstanceId < e.ECInstanceId
|
|
154
|
+
), '')
|
|
155
|
+
) ids
|
|
111
156
|
FROM bis.Element e
|
|
112
|
-
|
|
157
|
+
JOIN IdSet(:elementIds) elementIds ON elementIds.id = e.ECInstanceId
|
|
113
158
|
GROUP BY e.ECClassId
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
159
|
+
`, new core_common_1.QueryBinder().bindIdSet("elementIds", sortedElementIds.slice(idsFrom, idsTo)))).pipe((0, rxjs_1.map)((row) => ({
|
|
160
|
+
chunkIndex,
|
|
161
|
+
className: row.className,
|
|
162
|
+
ids: ensureSortedById(row.ids.split(",").map((pair) => {
|
|
163
|
+
const [id, prevClassInstanceId] = pair.split(":");
|
|
164
|
+
return { id, ...(prevClassInstanceId ? { prevClassInstanceId } : undefined) };
|
|
165
|
+
})),
|
|
166
|
+
})));
|
|
167
|
+
}), (0, rxjs_1.groupBy)(({ className }) => className), (0, rxjs_1.mergeMap)((groups) => groups.pipe((0, rxjs_1.reduce)((acc, g) => {
|
|
168
|
+
acc.chunks[g.chunkIndex] = g.ids;
|
|
169
|
+
return { classFullName: g.className, chunks: acc.chunks };
|
|
170
|
+
}, { classFullName: "", chunks: [] }))),
|
|
171
|
+
// chunks cover consecutive ID ranges and are sorted individually, so their concatenation is sorted as well;
|
|
172
|
+
// `flat` skips holes left by chunks that had no elements of the class
|
|
173
|
+
(0, rxjs_1.map)(({ classFullName, chunks }) => ({ classFullName, ids: chunks.flat() })));
|
|
174
|
+
}
|
|
175
|
+
function ensureSortedById(ids) {
|
|
176
|
+
for (let i = 1; i < ids.length; ++i) {
|
|
177
|
+
if (core_bentley_1.OrderedId64Iterable.compare(ids[i - 1].id, ids[i].id) > 0) {
|
|
178
|
+
return ids.sort((a, b) => core_bentley_1.OrderedId64Iterable.compare(a.id, b.id));
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
return ids;
|
|
122
182
|
}
|
|
123
|
-
/** Given a list of full class names, get
|
|
183
|
+
/** Given a list of full class names, get concrete class names with instances. */
|
|
124
184
|
function getClassesWithInstances(imodel, fullClassNames) {
|
|
125
185
|
return (0, rxjs_1.from)(fullClassNames).pipe((0, rxjs_1.mergeMap)((fullClassName) => (0, rxjs_1.from)(imodel.createQueryReader(`
|
|
126
186
|
SELECT ec_classname(e.ECClassId, 's.c') className
|
|
@@ -129,38 +189,28 @@ function getClassesWithInstances(imodel, fullClassNames) {
|
|
|
129
189
|
`))), (0, rxjs_1.map)((row) => row.className));
|
|
130
190
|
}
|
|
131
191
|
/**
|
|
132
|
-
* Given a
|
|
133
|
-
* be sequential, a batch is defined a list of from-to pairs.
|
|
134
|
-
*
|
|
192
|
+
* Given a list of same-class ECInstanceIds, sorted by ID, and a batch size, create a stream of batches. Because the IDs won't
|
|
193
|
+
* necessarily be sequential, a batch is defined as a list of from-to pairs. An ID extends the current range if there are no other
|
|
194
|
+
* instances of the same class between it and the previous requested ID.
|
|
135
195
|
*/
|
|
136
196
|
function createIdBatches(sortedIds, batchSize) {
|
|
137
197
|
return (0, rxjs_1.range)(0, sortedIds.length / batchSize).pipe((0, rxjs_1.map)((batchIndex) => {
|
|
138
|
-
const
|
|
198
|
+
const ranges = new Array();
|
|
139
199
|
const startIndex = batchIndex * batchSize;
|
|
140
200
|
const endIndex = Math.min((batchIndex + 1) * batchSize, sortedIds.length) - 1;
|
|
141
|
-
let fromId = sortedIds[startIndex];
|
|
142
|
-
let to = {
|
|
143
|
-
id: sortedIds[startIndex],
|
|
144
|
-
localId: core_bentley_1.Id64.getLocalId(sortedIds[startIndex]),
|
|
145
|
-
};
|
|
201
|
+
let fromId = sortedIds[startIndex].id;
|
|
146
202
|
for (let i = startIndex + 1; i <= endIndex; ++i) {
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
fromId = sortedIds[i];
|
|
203
|
+
if (sortedIds[i].prevClassInstanceId !== sortedIds[i - 1].id) {
|
|
204
|
+
ranges.push({ from: fromId, to: sortedIds[i - 1].id });
|
|
205
|
+
fromId = sortedIds[i].id;
|
|
151
206
|
}
|
|
152
|
-
to = { id: sortedIds[i], localId: currLocalId };
|
|
153
207
|
}
|
|
154
|
-
|
|
155
|
-
return
|
|
208
|
+
ranges.push({ from: fromId, to: sortedIds[endIndex].id });
|
|
209
|
+
return { ids: sortedIds.slice(startIndex, endIndex + 1).map(({ id }) => id), ranges };
|
|
156
210
|
}));
|
|
157
211
|
}
|
|
158
|
-
/**
|
|
159
|
-
* Query all ECInstanceIds from given class and stream from-to pairs that batch the items into batches of `batchSize` size.
|
|
160
|
-
* @internal
|
|
161
|
-
*/
|
|
162
212
|
function getBatchedClassElementIds(imodel, fullClassName, batchSize) {
|
|
163
|
-
return (0, rxjs_1.from)(imodel.createQueryReader(`SELECT IdToHex(ECInstanceId) id FROM ${getECSqlName(fullClassName)} ORDER BY ECInstanceId`)).pipe((0, rxjs_1.map)((row) => row.id), (0, rxjs_1.bufferCount)(batchSize), (0, rxjs_1.map)((
|
|
213
|
+
return (0, rxjs_1.from)(imodel.createQueryReader(`SELECT IdToHex(ECInstanceId) id FROM ONLY ${getECSqlName(fullClassName)} ORDER BY ECInstanceId`)).pipe((0, rxjs_1.map)((row) => row.id), (0, rxjs_1.bufferCount)(batchSize), (0, rxjs_1.map)((ids) => ({ ids, ranges: [{ from: ids[0], to: ids[ids.length - 1] }] })));
|
|
164
214
|
}
|
|
165
215
|
/** @internal */
|
|
166
216
|
async function getElementsCount(db, classNames) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ElementPropertiesHelper.js","sourceRoot":"","sources":["../../../src/presentation-backend/ElementPropertiesHelper.ts"],"names":[],"mappings":"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","sourcesContent":["/*---------------------------------------------------------------------------------------------\r\n * Copyright (c) Bentley Systems, Incorporated. All rights reserved.\r\n * See LICENSE.md in the project root for license terms and full copyright notice.\r\n *--------------------------------------------------------------------------------------------*/\r\n/** @packageDocumentation\r\n * @module Core\r\n */\r\n\r\nimport { bufferCount, defer, from, groupBy, map, mergeMap, Observable, ObservedValueOf, of, range, reduce } from \"rxjs\";\r\nimport { IModelDb } from \"@itwin/core-backend\";\r\nimport { Id64, Id64Array, Id64String, OrderedId64Iterable } from \"@itwin/core-bentley\";\r\nimport { QueryRowProxy } from \"@itwin/core-common\";\r\nimport {\r\n ContentDescriptorRequestOptions,\r\n ContentRequestOptions,\r\n Descriptor,\r\n Item,\r\n KeySet,\r\n PresentationError,\r\n PresentationStatus,\r\n Ruleset,\r\n RulesetVariable,\r\n} from \"@itwin/presentation-common\";\r\nimport { parseFullClassName } from \"@itwin/presentation-shared\";\r\n\r\nfunction getECSqlName(fullClassName: string) {\r\n const { schemaName, className } = parseFullClassName(fullClassName);\r\n return `[${schemaName}].[${className}]`;\r\n}\r\n\r\n/** @internal */\r\nexport function getContentItemsObservableFromElementIds(\r\n imodel: IModelDb,\r\n contentDescriptorGetter: (\r\n partialProps: Pick<ContentDescriptorRequestOptions<IModelDb, KeySet, RulesetVariable>, \"rulesetOrId\" | \"keys\">,\r\n ) => Promise<Descriptor | undefined>,\r\n contentSetGetter: (\r\n partialProps: Pick<ContentRequestOptions<IModelDb, Descriptor, KeySet, RulesetVariable>, \"rulesetOrId\" | \"keys\" | \"descriptor\">,\r\n ) => Promise<Item[]>,\r\n elementIds: Id64String[],\r\n classParallelism: number,\r\n batchesParallelism: number,\r\n batchSize: number,\r\n): { itemBatches: Observable<{ descriptor: Descriptor; items: Item[] }>; count: Observable<number> } {\r\n return {\r\n itemBatches: getElementClassesFromIds(imodel, elementIds).pipe(\r\n mergeMap(\r\n ({ classFullName, ids }) =>\r\n getBatchedClassContentItems(\r\n classFullName,\r\n contentDescriptorGetter,\r\n contentSetGetter,\r\n () => createIdBatches(OrderedId64Iterable.sortArray(ids), batchSize),\r\n batchesParallelism,\r\n ),\r\n classParallelism,\r\n ),\r\n ),\r\n count: of(elementIds.length),\r\n };\r\n}\r\n\r\n/** @internal */\r\nexport function getContentItemsObservableFromClassNames(\r\n imodel: IModelDb,\r\n contentDescriptorGetter: (\r\n partialProps: Pick<ContentDescriptorRequestOptions<IModelDb, KeySet, RulesetVariable>, \"rulesetOrId\" | \"keys\">,\r\n ) => Promise<Descriptor | undefined>,\r\n contentSetGetter: (\r\n partialProps: Pick<ContentRequestOptions<IModelDb, Descriptor, KeySet, RulesetVariable>, \"rulesetOrId\" | \"keys\" | \"descriptor\">,\r\n ) => Promise<Item[]>,\r\n elementClasses: string[],\r\n classParallelism: number,\r\n batchesParallelism: number,\r\n batchSize: number,\r\n): { itemBatches: Observable<{ descriptor: Descriptor; items: Item[] }>; count: Observable<number> } {\r\n return {\r\n itemBatches: getClassesWithInstances(imodel, elementClasses).pipe(\r\n mergeMap(\r\n (classFullName) =>\r\n getBatchedClassContentItems(\r\n classFullName,\r\n contentDescriptorGetter,\r\n contentSetGetter,\r\n () => getBatchedClassElementIds(imodel, classFullName, batchSize),\r\n batchesParallelism,\r\n ),\r\n classParallelism,\r\n ),\r\n ),\r\n count: from(getElementsCount(imodel, elementClasses)),\r\n };\r\n}\r\n\r\nfunction getBatchedClassContentItems(\r\n classFullName: string,\r\n contentDescriptorGetter: (\r\n partialProps: Pick<ContentDescriptorRequestOptions<IModelDb, KeySet, RulesetVariable>, \"rulesetOrId\" | \"keys\">,\r\n ) => Promise<Descriptor | undefined>,\r\n contentSetGetter: (\r\n partialProps: Pick<ContentRequestOptions<IModelDb, Descriptor, KeySet, RulesetVariable>, \"rulesetOrId\" | \"keys\" | \"descriptor\">,\r\n ) => Promise<Item[]>,\r\n batcher: () => Observable<Array<{ from: Id64String; to: Id64String }>>,\r\n batchesParallelism: number,\r\n): Observable<{ descriptor: Descriptor; items: Item[] }> {\r\n return defer(async () => {\r\n const ruleset = createClassContentRuleset(classFullName);\r\n const keys = new KeySet();\r\n const descriptor = await contentDescriptorGetter({ rulesetOrId: ruleset, keys });\r\n if (!descriptor) {\r\n throw new PresentationError(PresentationStatus.Error, `Failed to get descriptor for class ${classFullName}`);\r\n }\r\n return { descriptor, keys, ruleset };\r\n }).pipe(\r\n // create elements' id batches\r\n mergeMap((x) => batcher().pipe(map((batch) => ({ ...x, batch })))),\r\n // request content for each batch, filter by IDs for performance\r\n mergeMap(\r\n ({ descriptor, keys, ruleset, batch }) =>\r\n defer(async () => {\r\n const filteringDescriptor = new Descriptor(descriptor);\r\n filteringDescriptor.instanceFilter = {\r\n selectClassName: classFullName,\r\n expression: createElementIdsECExpressionFilter(batch),\r\n };\r\n return contentSetGetter({\r\n rulesetOrId: ruleset,\r\n keys,\r\n descriptor: filteringDescriptor,\r\n });\r\n }).pipe(map((items) => ({ descriptor, items }))),\r\n batchesParallelism,\r\n ),\r\n );\r\n}\r\n\r\nfunction createElementIdsECExpressionFilter(batch: Array<{ from: Id64String; to: Id64String }>): string {\r\n let filter = \"\";\r\n function appendCondition(cond: string) {\r\n if (filter.length > 0) {\r\n filter += \" OR \";\r\n }\r\n filter += cond;\r\n }\r\n for (const item of batch) {\r\n if (item.from === item.to) {\r\n appendCondition(`this.ECInstanceId = ${item.from}`);\r\n } else {\r\n appendCondition(`this.ECInstanceId >= ${item.from} AND this.ECInstanceId <= ${item.to}`);\r\n }\r\n }\r\n return filter;\r\n}\r\n\r\nfunction createClassContentRuleset(fullClassName: string): Ruleset {\r\n const { schemaName, className } = parseFullClassName(fullClassName);\r\n return {\r\n id: `content/class-descriptor/${fullClassName}`,\r\n rules: [\r\n {\r\n ruleType: \"Content\",\r\n specifications: [\r\n {\r\n specType: \"ContentInstancesOfSpecificClasses\",\r\n classes: {\r\n schemaName,\r\n classNames: [className],\r\n arePolymorphic: false,\r\n },\r\n handlePropertiesPolymorphically: true,\r\n },\r\n ],\r\n },\r\n ],\r\n };\r\n}\r\n\r\n/** Given a list of element ids, group them by class name. */\r\nfunction getElementClassesFromIds(imodel: IModelDb, elementIds: string[]): Observable<{ classFullName: string; ids: Id64Array }> {\r\n const elementIdsBatchSize = 5000;\r\n return range(0, elementIds.length / elementIdsBatchSize).pipe(\r\n mergeMap((batchIndex) => {\r\n const idsFrom = batchIndex * elementIdsBatchSize;\r\n const idsTo = Math.min(idsFrom + elementIdsBatchSize, elementIds.length);\r\n return from(\r\n imodel.createQueryReader(\r\n `\r\n SELECT ec_classname(e.ECClassId) className, GROUP_CONCAT(IdToHex(e.ECInstanceId)) ids\r\n FROM bis.Element e\r\n WHERE e.ECInstanceId IN (${elementIds.slice(idsFrom, idsTo).join(\",\")})\r\n GROUP BY e.ECClassId\r\n `,\r\n ),\r\n );\r\n }),\r\n map((row: QueryRowProxy): { className: string; ids: Id64Array } => ({ className: row.className, ids: row.ids.split(\",\") })),\r\n groupBy(({ className }) => className),\r\n mergeMap((groups) =>\r\n groups.pipe(\r\n reduce<ObservedValueOf<typeof groups>, { classFullName: string; ids: Id64Array }>(\r\n (acc, g) => {\r\n g.ids.forEach((id) => acc.ids.push(id));\r\n return {\r\n classFullName: g.className,\r\n ids: acc.ids,\r\n };\r\n },\r\n { classFullName: \"\", ids: [] },\r\n ),\r\n ),\r\n ),\r\n );\r\n}\r\n\r\n/** Given a list of full class names, get a stream of actual class names that have instances. */\r\nfunction getClassesWithInstances(imodel: IModelDb, fullClassNames: string[]): Observable<string> {\r\n return from(fullClassNames).pipe(\r\n mergeMap((fullClassName) =>\r\n from(\r\n imodel.createQueryReader(\r\n `\r\n SELECT ec_classname(e.ECClassId, 's.c') className\r\n FROM ${getECSqlName(fullClassName)} e\r\n GROUP BY e.ECClassId\r\n `,\r\n ),\r\n ),\r\n ),\r\n map((row: QueryRowProxy): string => row.className),\r\n );\r\n}\r\n\r\n/**\r\n * Given a sorted list of ECInstanceIds and a batch size, create a stream of batches. Because the IDs won't necessarily\r\n * be sequential, a batch is defined a list of from-to pairs.\r\n * @internal\r\n */\r\nexport function createIdBatches(sortedIds: Id64String[], batchSize: number): Observable<Array<{ from: Id64String; to: Id64String }>> {\r\n return range(0, sortedIds.length / batchSize).pipe(\r\n map((batchIndex) => {\r\n const sequences = new Array<{ from: Id64String; to: Id64String }>();\r\n const startIndex = batchIndex * batchSize;\r\n const endIndex = Math.min((batchIndex + 1) * batchSize, sortedIds.length) - 1;\r\n let fromId = sortedIds[startIndex];\r\n let to = {\r\n id: sortedIds[startIndex],\r\n localId: Id64.getLocalId(sortedIds[startIndex]),\r\n };\r\n for (let i = startIndex + 1; i <= endIndex; ++i) {\r\n const currLocalId = Id64.getLocalId(sortedIds[i]);\r\n if (currLocalId !== to.localId + 1) {\r\n sequences.push({ from: fromId, to: sortedIds[i - 1] });\r\n fromId = sortedIds[i];\r\n }\r\n to = { id: sortedIds[i], localId: currLocalId };\r\n }\r\n sequences.push({ from: fromId, to: sortedIds[endIndex] });\r\n return sequences;\r\n }),\r\n );\r\n}\r\n\r\n/**\r\n * Query all ECInstanceIds from given class and stream from-to pairs that batch the items into batches of `batchSize` size.\r\n * @internal\r\n */\r\nexport function getBatchedClassElementIds(imodel: IModelDb, fullClassName: string, batchSize: number): Observable<Array<{ from: Id64String; to: Id64String }>> {\r\n return from(imodel.createQueryReader(`SELECT IdToHex(ECInstanceId) id FROM ${getECSqlName(fullClassName)} ORDER BY ECInstanceId`)).pipe(\r\n map((row): Id64String => row.id),\r\n bufferCount(batchSize),\r\n map((batch) => [{ from: batch[0], to: batch[batch.length - 1] }]),\r\n );\r\n}\r\n\r\n/** @internal */\r\nexport async function getElementsCount(db: IModelDb, classNames: string[]) {\r\n const whereClause = (() => {\r\n if (classNames === undefined || classNames.length === 0) {\r\n return undefined;\r\n }\r\n // check if list contains only valid class names\r\n const classNameRegExp = new RegExp(/^[\\w]+[.:][\\w]+$/);\r\n const invalidName = classNames.find((name) => !name.match(classNameRegExp));\r\n if (invalidName) {\r\n throw new PresentationError(\r\n PresentationStatus.InvalidArgument,\r\n `Encountered invalid class name - ${invalidName}.\r\n Valid class name formats: \"<schema name or alias>.<class name>\", \"<schema name or alias>:<class name>\"`,\r\n );\r\n }\r\n return `e.ECClassId IS (${classNames.map(getECSqlName).join(\",\")})`;\r\n })();\r\n const query = `\r\n SELECT COUNT(e.ECInstanceId) AS elementCount\r\n FROM bis.Element e\r\n ${whereClause ? `WHERE ${whereClause}` : \"\"}\r\n `;\r\n for await (const row of db.createQueryReader(query)) {\r\n return row.elementCount;\r\n }\r\n return 0;\r\n}\r\n"]}
|
|
1
|
+
{"version":3,"file":"ElementPropertiesHelper.js","sourceRoot":"","sources":["../../../src/presentation-backend/ElementPropertiesHelper.ts"],"names":[],"mappings":"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","sourcesContent":["/*---------------------------------------------------------------------------------------------\r\n * Copyright (c) Bentley Systems, Incorporated. All rights reserved.\r\n * See LICENSE.md in the project root for license terms and full copyright notice.\r\n *--------------------------------------------------------------------------------------------*/\r\n/** @packageDocumentation\r\n * @module Core\r\n */\r\n\r\nimport { bufferCount, defer, from, groupBy, map, mergeMap, Observable, ObservedValueOf, of, range, reduce } from \"rxjs\";\r\nimport { IModelDb } from \"@itwin/core-backend\";\r\nimport { Id64Array, Id64String, OrderedId64Iterable } from \"@itwin/core-bentley\";\r\nimport { QueryBinder, QueryRowProxy } from \"@itwin/core-common\";\r\nimport {\r\n ContentDescriptorRequestOptions,\r\n ContentRequestOptions,\r\n Descriptor,\r\n Field,\r\n Item,\r\n KeySet,\r\n NestedContentField,\r\n PresentationError,\r\n PresentationStatus,\r\n Ruleset,\r\n RulesetVariable,\r\n} from \"@itwin/presentation-common\";\r\nimport { parseFullClassName } from \"@itwin/presentation-shared\";\r\n\r\nfunction getECSqlName(fullClassName: string) {\r\n const { schemaName, className } = parseFullClassName(fullClassName);\r\n return `[${schemaName}].[${className}]`;\r\n}\r\n\r\n/** @internal */\r\nexport function getContentItemsObservableFromElementIds(\r\n imodel: IModelDb,\r\n contentDescriptorGetter: (\r\n partialProps: Pick<ContentDescriptorRequestOptions<IModelDb, KeySet, RulesetVariable>, \"rulesetOrId\" | \"keys\">,\r\n ) => Promise<Descriptor | undefined>,\r\n contentSetGetter: (\r\n partialProps: Pick<ContentRequestOptions<IModelDb, Descriptor, KeySet, RulesetVariable>, \"rulesetOrId\" | \"keys\" | \"descriptor\">,\r\n ) => Promise<Item[]>,\r\n elementIds: Id64String[],\r\n classParallelism: number,\r\n batchesParallelism: number,\r\n batchSize: number,\r\n): { itemBatches: Observable<{ descriptor: Descriptor; items: Item[] }>; count: Observable<number> } {\r\n return {\r\n itemBatches: getElementClassesFromIds(imodel, elementIds).pipe(\r\n mergeMap(\r\n ({ classFullName, ids }) =>\r\n getBatchedClassContentItems(\r\n imodel,\r\n classFullName,\r\n contentDescriptorGetter,\r\n contentSetGetter,\r\n () => createIdBatches(ids, batchSize),\r\n batchesParallelism,\r\n ),\r\n classParallelism,\r\n ),\r\n ),\r\n count: of(elementIds.length),\r\n };\r\n}\r\n\r\n/** @internal */\r\nexport function getContentItemsObservableFromClassNames(\r\n imodel: IModelDb,\r\n contentDescriptorGetter: (\r\n partialProps: Pick<ContentDescriptorRequestOptions<IModelDb, KeySet, RulesetVariable>, \"rulesetOrId\" | \"keys\">,\r\n ) => Promise<Descriptor | undefined>,\r\n contentSetGetter: (\r\n partialProps: Pick<ContentRequestOptions<IModelDb, Descriptor, KeySet, RulesetVariable>, \"rulesetOrId\" | \"keys\" | \"descriptor\">,\r\n ) => Promise<Item[]>,\r\n elementClasses: string[],\r\n classParallelism: number,\r\n batchesParallelism: number,\r\n batchSize: number,\r\n): { itemBatches: Observable<{ descriptor: Descriptor; items: Item[] }>; count: Observable<number> } {\r\n return {\r\n itemBatches: getClassesWithInstances(imodel, elementClasses).pipe(\r\n mergeMap(\r\n (classFullName) =>\r\n getBatchedClassContentItems(\r\n imodel,\r\n classFullName,\r\n contentDescriptorGetter,\r\n contentSetGetter,\r\n () => getBatchedClassElementIds(imodel, classFullName, batchSize),\r\n batchesParallelism,\r\n ),\r\n classParallelism,\r\n ),\r\n ),\r\n count: from(getElementsCount(imodel, elementClasses)),\r\n };\r\n}\r\n\r\nfunction getBatchedClassContentItems(\r\n imodel: IModelDb,\r\n classFullName: string,\r\n contentDescriptorGetter: (\r\n partialProps: Pick<ContentDescriptorRequestOptions<IModelDb, KeySet, RulesetVariable>, \"rulesetOrId\" | \"keys\">,\r\n ) => Promise<Descriptor | undefined>,\r\n contentSetGetter: (\r\n partialProps: Pick<ContentRequestOptions<IModelDb, Descriptor, KeySet, RulesetVariable>, \"rulesetOrId\" | \"keys\" | \"descriptor\">,\r\n ) => Promise<Item[]>,\r\n batcher: () => Observable<ElementIdBatch>,\r\n batchesParallelism: number,\r\n): Observable<{ descriptor: Descriptor; items: Item[] }> {\r\n return defer(async () => {\r\n const ruleset = createClassContentRuleset(classFullName);\r\n const keys = new KeySet();\r\n const descriptor = await contentDescriptorGetter({ rulesetOrId: ruleset, keys });\r\n if (!descriptor) {\r\n throw new PresentationError(PresentationStatus.Error, `Failed to get descriptor for class ${classFullName}`);\r\n }\r\n const aspectFields = getPrunableAspectFields(descriptor, classFullName);\r\n return { descriptor, keys, ruleset, aspectFields };\r\n }).pipe(\r\n mergeMap((x) => batcher().pipe(map((batch) => ({ ...x, batch })))),\r\n mergeMap(\r\n ({ descriptor, keys, ruleset, batch, aspectFields }) =>\r\n defer(async () => {\r\n const batchDescriptor = new Descriptor(descriptor);\r\n batchDescriptor.instanceFilter = {\r\n selectClassName: classFullName,\r\n expression: batch.ranges\r\n .map(({ from: fromId, to }) =>\r\n fromId === to ? `this.ECInstanceId = ${fromId}` : `this.ECInstanceId >= ${fromId} AND this.ECInstanceId <= ${to}`,\r\n )\r\n .join(\" OR \"),\r\n };\r\n if (aspectFields.length) {\r\n await selectBatchAspectFields(imodel, batchDescriptor, aspectFields, batch.ids);\r\n }\r\n const items = await contentSetGetter({\r\n rulesetOrId: ruleset,\r\n keys,\r\n descriptor: batchDescriptor,\r\n });\r\n return { descriptor: batchDescriptor, items };\r\n }),\r\n batchesParallelism,\r\n ),\r\n );\r\n}\r\n\r\nfunction getPrunableAspectFields(descriptor: Descriptor, classFullName: string): NestedContentField[] {\r\n // Caller selections take precedence; don't replace an include/exclude selector with our own.\r\n if (descriptor.fieldsSelector || countFields(descriptor.fields) <= 1000) {\r\n return [];\r\n }\r\n const { schemaName, className } = parseFullClassName(classFullName);\r\n return descriptor.fields.filter((field): field is NestedContentField => {\r\n if (!field.isNestedContentField() || field.pathToPrimaryClass.length !== 1) {\r\n return false;\r\n }\r\n const step = field.pathToPrimaryClass[0];\r\n return (\r\n !step.isForwardRelationship &&\r\n (step.relationshipInfo.name === \"BisCore:ElementOwnsMultiAspects\" || step.relationshipInfo.name === \"BisCore:ElementOwnsUniqueAspect\") &&\r\n step.sourceClassInfo.id === field.contentClassInfo.id &&\r\n step.targetClassInfo.name === `${schemaName}:${className}`\r\n );\r\n });\r\n}\r\n\r\nfunction countFields(fields: Field[]): number {\r\n return fields.reduce((count, field) => count + 1 + (field.isNestedContentField() ? countFields(field.nestedFields) : 0), 0);\r\n}\r\n\r\nasync function selectBatchAspectFields(imodel: IModelDb, descriptor: Descriptor, aspectFields: NestedContentField[], batch: Id64Array): Promise<void> {\r\n const query = `\r\n WITH aspectClasses(ClassId) AS (\r\n SELECT DISTINCT a.ECClassId\r\n FROM bis.ElementMultiAspect a\r\n JOIN IdSet(:elementIds) e ON e.id = a.Element.Id\r\n UNION ALL\r\n SELECT DISTINCT a.ECClassId\r\n FROM bis.ElementUniqueAspect a\r\n JOIN IdSet(:elementIds) e ON e.id = a.Element.Id\r\n )\r\n SELECT IdToHex(b.TargetECInstanceId) classId\r\n FROM meta.ClassHasAllBaseClasses b\r\n JOIN aspectClasses a ON a.ClassId = b.SourceECInstanceId\r\n `;\r\n const aspectClasses = new Set<Id64String>();\r\n for await (const row of imodel.createQueryReader(query, QueryBinder.from({ elementIds: batch }))) {\r\n aspectClasses.add(row.classId);\r\n }\r\n const excludedFields = aspectFields.filter((field) => !aspectClasses.has(field.contentClassInfo.id));\r\n if (excludedFields.length > 0) {\r\n descriptor.fieldsSelector = { type: \"exclude\", fields: excludedFields.map((field) => field.getFieldDescriptor()) };\r\n }\r\n}\r\n\r\nfunction createClassContentRuleset(fullClassName: string): Ruleset {\r\n const { schemaName, className } = parseFullClassName(fullClassName);\r\n return {\r\n id: `content/class-descriptor/${fullClassName}`,\r\n rules: [\r\n {\r\n ruleType: \"Content\",\r\n specifications: [\r\n {\r\n specType: \"ContentInstancesOfSpecificClasses\",\r\n classes: {\r\n schemaName,\r\n classNames: [className],\r\n arePolymorphic: false,\r\n },\r\n handlePropertiesPolymorphically: true,\r\n },\r\n ],\r\n },\r\n ],\r\n };\r\n}\r\n\r\ninterface ElementIdWithPrevClassInstance {\r\n id: Id64String;\r\n /** ID of the previous (by ECInstanceId) element of the same class, if any. */\r\n prevClassInstanceId?: Id64String;\r\n}\r\n\r\n/**\r\n * Given a list of element ids, group them by class name. In addition, for every element, get ID of the previous\r\n * element of the same class, which lets us put requested IDs into ranges without other instances of the same class.\r\n *\r\n * The query reader re-executes the query for every page, so querying all IDs at once is much slower than querying\r\n * them in chunks. Chunks are made of the sorted IDs list and run in parallel. `GROUP_CONCAT` order isn't guaranteed\r\n * by SQLite, so each chunk's IDs are sorted individually (usually a no-op check), then stored at the chunk's index.\r\n * Concatenating them gives each class its IDs sorted.\r\n */\r\nfunction getElementClassesFromIds(\r\n imodel: IModelDb,\r\n elementIds: Id64Array,\r\n): Observable<{ classFullName: string; ids: ElementIdWithPrevClassInstance[] }> {\r\n const elementIdsBatchSize = 5000;\r\n const sortedElementIds = OrderedId64Iterable.sortArray([...elementIds]);\r\n return range(0, sortedElementIds.length / elementIdsBatchSize).pipe(\r\n mergeMap((_, chunkIndex) => {\r\n const idsFrom = chunkIndex * elementIdsBatchSize;\r\n const idsTo = Math.min(idsFrom + elementIdsBatchSize, sortedElementIds.length);\r\n return from(\r\n imodel.createQueryReader(\r\n `\r\n SELECT\r\n ec_classname(e.ECClassId) className,\r\n GROUP_CONCAT(\r\n IdToHex(e.ECInstanceId) || ':' || IFNULL((\r\n SELECT IdToHex(MAX(p.ECInstanceId))\r\n FROM bis.Element p\r\n WHERE p.ECClassId = e.ECClassId AND p.ECInstanceId < e.ECInstanceId\r\n ), '')\r\n ) ids\r\n FROM bis.Element e\r\n JOIN IdSet(:elementIds) elementIds ON elementIds.id = e.ECInstanceId\r\n GROUP BY e.ECClassId\r\n `,\r\n new QueryBinder().bindIdSet(\"elementIds\", sortedElementIds.slice(idsFrom, idsTo)),\r\n ),\r\n ).pipe(\r\n map((row: QueryRowProxy) => ({\r\n chunkIndex,\r\n className: row.className as string,\r\n ids: ensureSortedById(\r\n (row.ids as string).split(\",\").map((pair): ElementIdWithPrevClassInstance => {\r\n const [id, prevClassInstanceId] = pair.split(\":\");\r\n return { id, ...(prevClassInstanceId ? { prevClassInstanceId } : undefined) };\r\n }),\r\n ),\r\n })),\r\n );\r\n }),\r\n groupBy(({ className }) => className),\r\n mergeMap((groups) =>\r\n groups.pipe(\r\n reduce<ObservedValueOf<typeof groups>, { classFullName: string; chunks: ElementIdWithPrevClassInstance[][] }>(\r\n (acc, g) => {\r\n acc.chunks[g.chunkIndex] = g.ids;\r\n return { classFullName: g.className, chunks: acc.chunks };\r\n },\r\n { classFullName: \"\", chunks: [] },\r\n ),\r\n ),\r\n ),\r\n // chunks cover consecutive ID ranges and are sorted individually, so their concatenation is sorted as well;\r\n // `flat` skips holes left by chunks that had no elements of the class\r\n map(({ classFullName, chunks }) => ({ classFullName, ids: chunks.flat() })),\r\n );\r\n}\r\n\r\nfunction ensureSortedById(ids: ElementIdWithPrevClassInstance[]): ElementIdWithPrevClassInstance[] {\r\n for (let i = 1; i < ids.length; ++i) {\r\n if (OrderedId64Iterable.compare(ids[i - 1].id, ids[i].id) > 0) {\r\n return ids.sort((a, b) => OrderedId64Iterable.compare(a.id, b.id));\r\n }\r\n }\r\n return ids;\r\n}\r\n\r\n/** Given a list of full class names, get concrete class names with instances. */\r\nfunction getClassesWithInstances(imodel: IModelDb, fullClassNames: string[]): Observable<string> {\r\n return from(fullClassNames).pipe(\r\n mergeMap((fullClassName) =>\r\n from(\r\n imodel.createQueryReader(\r\n `\r\n SELECT ec_classname(e.ECClassId, 's.c') className\r\n FROM ${getECSqlName(fullClassName)} e\r\n GROUP BY e.ECClassId\r\n `,\r\n ),\r\n ),\r\n ),\r\n map((row: QueryRowProxy): string => row.className),\r\n );\r\n}\r\n\r\ninterface ElementIdBatch {\r\n ids: Id64Array;\r\n ranges: Array<{ from: Id64String; to: Id64String }>;\r\n}\r\n\r\n/**\r\n * Given a list of same-class ECInstanceIds, sorted by ID, and a batch size, create a stream of batches. Because the IDs won't\r\n * necessarily be sequential, a batch is defined as a list of from-to pairs. An ID extends the current range if there are no other\r\n * instances of the same class between it and the previous requested ID.\r\n */\r\nfunction createIdBatches(sortedIds: ElementIdWithPrevClassInstance[], batchSize: number): Observable<ElementIdBatch> {\r\n return range(0, sortedIds.length / batchSize).pipe(\r\n map((batchIndex) => {\r\n const ranges = new Array<{ from: Id64String; to: Id64String }>();\r\n const startIndex = batchIndex * batchSize;\r\n const endIndex = Math.min((batchIndex + 1) * batchSize, sortedIds.length) - 1;\r\n let fromId = sortedIds[startIndex].id;\r\n for (let i = startIndex + 1; i <= endIndex; ++i) {\r\n if (sortedIds[i].prevClassInstanceId !== sortedIds[i - 1].id) {\r\n ranges.push({ from: fromId, to: sortedIds[i - 1].id });\r\n fromId = sortedIds[i].id;\r\n }\r\n }\r\n ranges.push({ from: fromId, to: sortedIds[endIndex].id });\r\n return { ids: sortedIds.slice(startIndex, endIndex + 1).map(({ id }) => id), ranges };\r\n }),\r\n );\r\n}\r\n\r\nfunction getBatchedClassElementIds(imodel: IModelDb, fullClassName: string, batchSize: number): Observable<ElementIdBatch> {\r\n return from(imodel.createQueryReader(`SELECT IdToHex(ECInstanceId) id FROM ONLY ${getECSqlName(fullClassName)} ORDER BY ECInstanceId`)).pipe(\r\n map((row): Id64String => row.id),\r\n bufferCount(batchSize),\r\n map((ids) => ({ ids, ranges: [{ from: ids[0], to: ids[ids.length - 1] }] })),\r\n );\r\n}\r\n\r\n/** @internal */\r\nexport async function getElementsCount(db: IModelDb, classNames: string[]) {\r\n const whereClause = (() => {\r\n if (classNames === undefined || classNames.length === 0) {\r\n return undefined;\r\n }\r\n // check if list contains only valid class names\r\n const classNameRegExp = new RegExp(/^[\\w]+[.:][\\w]+$/);\r\n const invalidName = classNames.find((name) => !name.match(classNameRegExp));\r\n if (invalidName) {\r\n throw new PresentationError(\r\n PresentationStatus.InvalidArgument,\r\n `Encountered invalid class name - ${invalidName}.\r\n Valid class name formats: \"<schema name or alias>.<class name>\", \"<schema name or alias>:<class name>\"`,\r\n );\r\n }\r\n return `e.ECClassId IS (${classNames.map(getECSqlName).join(\",\")})`;\r\n })();\r\n const query = `\r\n SELECT COUNT(e.ECInstanceId) AS elementCount\r\n FROM bis.Element e\r\n ${whereClause ? `WHERE ${whereClause}` : \"\"}\r\n `;\r\n for await (const row of db.createQueryReader(query)) {\r\n return row.elementCount;\r\n }\r\n return 0;\r\n}\r\n"]}
|
|
@@ -21,23 +21,6 @@ export declare function getContentItemsObservableFromClassNames(imodel: IModelDb
|
|
|
21
21
|
}>;
|
|
22
22
|
count: Observable<number>;
|
|
23
23
|
};
|
|
24
|
-
/**
|
|
25
|
-
* Given a sorted list of ECInstanceIds and a batch size, create a stream of batches. Because the IDs won't necessarily
|
|
26
|
-
* be sequential, a batch is defined a list of from-to pairs.
|
|
27
|
-
* @internal
|
|
28
|
-
*/
|
|
29
|
-
export declare function createIdBatches(sortedIds: Id64String[], batchSize: number): Observable<Array<{
|
|
30
|
-
from: Id64String;
|
|
31
|
-
to: Id64String;
|
|
32
|
-
}>>;
|
|
33
|
-
/**
|
|
34
|
-
* Query all ECInstanceIds from given class and stream from-to pairs that batch the items into batches of `batchSize` size.
|
|
35
|
-
* @internal
|
|
36
|
-
*/
|
|
37
|
-
export declare function getBatchedClassElementIds(imodel: IModelDb, fullClassName: string, batchSize: number): Observable<Array<{
|
|
38
|
-
from: Id64String;
|
|
39
|
-
to: Id64String;
|
|
40
|
-
}>>;
|
|
41
24
|
/** @internal */
|
|
42
25
|
export declare function getElementsCount(db: IModelDb, classNames: string[]): Promise<any>;
|
|
43
26
|
//# sourceMappingURL=ElementPropertiesHelper.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ElementPropertiesHelper.d.ts","sourceRoot":"","sources":["../../../src/presentation-backend/ElementPropertiesHelper.ts"],"names":[],"mappings":"AAIA;;GAEG;AAEH,OAAO,EAAoD,UAAU,EAAsC,MAAM,MAAM,CAAC;AACxH,OAAO,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAC;AAC/C,OAAO,
|
|
1
|
+
{"version":3,"file":"ElementPropertiesHelper.d.ts","sourceRoot":"","sources":["../../../src/presentation-backend/ElementPropertiesHelper.ts"],"names":[],"mappings":"AAIA;;GAEG;AAEH,OAAO,EAAoD,UAAU,EAAsC,MAAM,MAAM,CAAC;AACxH,OAAO,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAC;AAC/C,OAAO,EAAa,UAAU,EAAuB,MAAM,qBAAqB,CAAC;AAEjF,OAAO,EACL,+BAA+B,EAC/B,qBAAqB,EACrB,UAAU,EAEV,IAAI,EACJ,MAAM,EAKN,eAAe,EAChB,MAAM,4BAA4B,CAAC;AAQpC,gBAAgB;AAChB,wBAAgB,uCAAuC,CACrD,MAAM,EAAE,QAAQ,EAChB,uBAAuB,EAAE,CACvB,YAAY,EAAE,IAAI,CAAC,+BAA+B,CAAC,QAAQ,EAAE,MAAM,EAAE,eAAe,CAAC,EAAE,aAAa,GAAG,MAAM,CAAC,KAC3G,OAAO,CAAC,UAAU,GAAG,SAAS,CAAC,EACpC,gBAAgB,EAAE,CAChB,YAAY,EAAE,IAAI,CAAC,qBAAqB,CAAC,QAAQ,EAAE,UAAU,EAAE,MAAM,EAAE,eAAe,CAAC,EAAE,aAAa,GAAG,MAAM,GAAG,YAAY,CAAC,KAC5H,OAAO,CAAC,IAAI,EAAE,CAAC,EACpB,UAAU,EAAE,UAAU,EAAE,EACxB,gBAAgB,EAAE,MAAM,EACxB,kBAAkB,EAAE,MAAM,EAC1B,SAAS,EAAE,MAAM,GAChB;IAAE,WAAW,EAAE,UAAU,CAAC;QAAE,UAAU,EAAE,UAAU,CAAC;QAAC,KAAK,EAAE,IAAI,EAAE,CAAA;KAAE,CAAC,CAAC;IAAC,KAAK,EAAE,UAAU,CAAC,MAAM,CAAC,CAAA;CAAE,CAkBnG;AAED,gBAAgB;AAChB,wBAAgB,uCAAuC,CACrD,MAAM,EAAE,QAAQ,EAChB,uBAAuB,EAAE,CACvB,YAAY,EAAE,IAAI,CAAC,+BAA+B,CAAC,QAAQ,EAAE,MAAM,EAAE,eAAe,CAAC,EAAE,aAAa,GAAG,MAAM,CAAC,KAC3G,OAAO,CAAC,UAAU,GAAG,SAAS,CAAC,EACpC,gBAAgB,EAAE,CAChB,YAAY,EAAE,IAAI,CAAC,qBAAqB,CAAC,QAAQ,EAAE,UAAU,EAAE,MAAM,EAAE,eAAe,CAAC,EAAE,aAAa,GAAG,MAAM,GAAG,YAAY,CAAC,KAC5H,OAAO,CAAC,IAAI,EAAE,CAAC,EACpB,cAAc,EAAE,MAAM,EAAE,EACxB,gBAAgB,EAAE,MAAM,EACxB,kBAAkB,EAAE,MAAM,EAC1B,SAAS,EAAE,MAAM,GAChB;IAAE,WAAW,EAAE,UAAU,CAAC;QAAE,UAAU,EAAE,UAAU,CAAC;QAAC,KAAK,EAAE,IAAI,EAAE,CAAA;KAAE,CAAC,CAAC;IAAC,KAAK,EAAE,UAAU,CAAC,MAAM,CAAC,CAAA;CAAE,CAkBnG;AAsQD,gBAAgB;AAChB,wBAAsB,gBAAgB,CAAC,EAAE,EAAE,QAAQ,EAAE,UAAU,EAAE,MAAM,EAAE,gBA0BxE"}
|
|
@@ -6,7 +6,8 @@
|
|
|
6
6
|
* @module Core
|
|
7
7
|
*/
|
|
8
8
|
import { bufferCount, defer, from, groupBy, map, mergeMap, of, range, reduce } from "rxjs";
|
|
9
|
-
import {
|
|
9
|
+
import { OrderedId64Iterable } from "@itwin/core-bentley";
|
|
10
|
+
import { QueryBinder } from "@itwin/core-common";
|
|
10
11
|
import { Descriptor, KeySet, PresentationError, PresentationStatus, } from "@itwin/presentation-common";
|
|
11
12
|
import { parseFullClassName } from "@itwin/presentation-shared";
|
|
12
13
|
function getECSqlName(fullClassName) {
|
|
@@ -16,18 +17,18 @@ function getECSqlName(fullClassName) {
|
|
|
16
17
|
/** @internal */
|
|
17
18
|
export function getContentItemsObservableFromElementIds(imodel, contentDescriptorGetter, contentSetGetter, elementIds, classParallelism, batchesParallelism, batchSize) {
|
|
18
19
|
return {
|
|
19
|
-
itemBatches: getElementClassesFromIds(imodel, elementIds).pipe(mergeMap(({ classFullName, ids }) => getBatchedClassContentItems(classFullName, contentDescriptorGetter, contentSetGetter, () => createIdBatches(
|
|
20
|
+
itemBatches: getElementClassesFromIds(imodel, elementIds).pipe(mergeMap(({ classFullName, ids }) => getBatchedClassContentItems(imodel, classFullName, contentDescriptorGetter, contentSetGetter, () => createIdBatches(ids, batchSize), batchesParallelism), classParallelism)),
|
|
20
21
|
count: of(elementIds.length),
|
|
21
22
|
};
|
|
22
23
|
}
|
|
23
24
|
/** @internal */
|
|
24
25
|
export function getContentItemsObservableFromClassNames(imodel, contentDescriptorGetter, contentSetGetter, elementClasses, classParallelism, batchesParallelism, batchSize) {
|
|
25
26
|
return {
|
|
26
|
-
itemBatches: getClassesWithInstances(imodel, elementClasses).pipe(mergeMap((classFullName) => getBatchedClassContentItems(classFullName, contentDescriptorGetter, contentSetGetter, () => getBatchedClassElementIds(imodel, classFullName, batchSize), batchesParallelism), classParallelism)),
|
|
27
|
+
itemBatches: getClassesWithInstances(imodel, elementClasses).pipe(mergeMap((classFullName) => getBatchedClassContentItems(imodel, classFullName, contentDescriptorGetter, contentSetGetter, () => getBatchedClassElementIds(imodel, classFullName, batchSize), batchesParallelism), classParallelism)),
|
|
27
28
|
count: from(getElementsCount(imodel, elementClasses)),
|
|
28
29
|
};
|
|
29
30
|
}
|
|
30
|
-
function getBatchedClassContentItems(classFullName, contentDescriptorGetter, contentSetGetter, batcher, batchesParallelism) {
|
|
31
|
+
function getBatchedClassContentItems(imodel, classFullName, contentDescriptorGetter, contentSetGetter, batcher, batchesParallelism) {
|
|
31
32
|
return defer(async () => {
|
|
32
33
|
const ruleset = createClassContentRuleset(classFullName);
|
|
33
34
|
const keys = new KeySet();
|
|
@@ -35,41 +36,70 @@ function getBatchedClassContentItems(classFullName, contentDescriptorGetter, con
|
|
|
35
36
|
if (!descriptor) {
|
|
36
37
|
throw new PresentationError(PresentationStatus.Error, `Failed to get descriptor for class ${classFullName}`);
|
|
37
38
|
}
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
mergeMap(({ descriptor, keys, ruleset, batch }) => defer(async () => {
|
|
44
|
-
const filteringDescriptor = new Descriptor(descriptor);
|
|
45
|
-
filteringDescriptor.instanceFilter = {
|
|
39
|
+
const aspectFields = getPrunableAspectFields(descriptor, classFullName);
|
|
40
|
+
return { descriptor, keys, ruleset, aspectFields };
|
|
41
|
+
}).pipe(mergeMap((x) => batcher().pipe(map((batch) => ({ ...x, batch })))), mergeMap(({ descriptor, keys, ruleset, batch, aspectFields }) => defer(async () => {
|
|
42
|
+
const batchDescriptor = new Descriptor(descriptor);
|
|
43
|
+
batchDescriptor.instanceFilter = {
|
|
46
44
|
selectClassName: classFullName,
|
|
47
|
-
expression:
|
|
45
|
+
expression: batch.ranges
|
|
46
|
+
.map(({ from: fromId, to }) => fromId === to ? `this.ECInstanceId = ${fromId}` : `this.ECInstanceId >= ${fromId} AND this.ECInstanceId <= ${to}`)
|
|
47
|
+
.join(" OR "),
|
|
48
48
|
};
|
|
49
|
-
|
|
49
|
+
if (aspectFields.length) {
|
|
50
|
+
await selectBatchAspectFields(imodel, batchDescriptor, aspectFields, batch.ids);
|
|
51
|
+
}
|
|
52
|
+
const items = await contentSetGetter({
|
|
50
53
|
rulesetOrId: ruleset,
|
|
51
54
|
keys,
|
|
52
|
-
descriptor:
|
|
55
|
+
descriptor: batchDescriptor,
|
|
53
56
|
});
|
|
54
|
-
|
|
57
|
+
return { descriptor: batchDescriptor, items };
|
|
58
|
+
}), batchesParallelism));
|
|
55
59
|
}
|
|
56
|
-
function
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
filter += " OR ";
|
|
61
|
-
}
|
|
62
|
-
filter += cond;
|
|
60
|
+
function getPrunableAspectFields(descriptor, classFullName) {
|
|
61
|
+
// Caller selections take precedence; don't replace an include/exclude selector with our own.
|
|
62
|
+
if (descriptor.fieldsSelector || countFields(descriptor.fields) <= 1000) {
|
|
63
|
+
return [];
|
|
63
64
|
}
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
else {
|
|
69
|
-
appendCondition(`this.ECInstanceId >= ${item.from} AND this.ECInstanceId <= ${item.to}`);
|
|
65
|
+
const { schemaName, className } = parseFullClassName(classFullName);
|
|
66
|
+
return descriptor.fields.filter((field) => {
|
|
67
|
+
if (!field.isNestedContentField() || field.pathToPrimaryClass.length !== 1) {
|
|
68
|
+
return false;
|
|
70
69
|
}
|
|
70
|
+
const step = field.pathToPrimaryClass[0];
|
|
71
|
+
return (!step.isForwardRelationship &&
|
|
72
|
+
(step.relationshipInfo.name === "BisCore:ElementOwnsMultiAspects" || step.relationshipInfo.name === "BisCore:ElementOwnsUniqueAspect") &&
|
|
73
|
+
step.sourceClassInfo.id === field.contentClassInfo.id &&
|
|
74
|
+
step.targetClassInfo.name === `${schemaName}:${className}`);
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
function countFields(fields) {
|
|
78
|
+
return fields.reduce((count, field) => count + 1 + (field.isNestedContentField() ? countFields(field.nestedFields) : 0), 0);
|
|
79
|
+
}
|
|
80
|
+
async function selectBatchAspectFields(imodel, descriptor, aspectFields, batch) {
|
|
81
|
+
const query = `
|
|
82
|
+
WITH aspectClasses(ClassId) AS (
|
|
83
|
+
SELECT DISTINCT a.ECClassId
|
|
84
|
+
FROM bis.ElementMultiAspect a
|
|
85
|
+
JOIN IdSet(:elementIds) e ON e.id = a.Element.Id
|
|
86
|
+
UNION ALL
|
|
87
|
+
SELECT DISTINCT a.ECClassId
|
|
88
|
+
FROM bis.ElementUniqueAspect a
|
|
89
|
+
JOIN IdSet(:elementIds) e ON e.id = a.Element.Id
|
|
90
|
+
)
|
|
91
|
+
SELECT IdToHex(b.TargetECInstanceId) classId
|
|
92
|
+
FROM meta.ClassHasAllBaseClasses b
|
|
93
|
+
JOIN aspectClasses a ON a.ClassId = b.SourceECInstanceId
|
|
94
|
+
`;
|
|
95
|
+
const aspectClasses = new Set();
|
|
96
|
+
for await (const row of imodel.createQueryReader(query, QueryBinder.from({ elementIds: batch }))) {
|
|
97
|
+
aspectClasses.add(row.classId);
|
|
98
|
+
}
|
|
99
|
+
const excludedFields = aspectFields.filter((field) => !aspectClasses.has(field.contentClassInfo.id));
|
|
100
|
+
if (excludedFields.length > 0) {
|
|
101
|
+
descriptor.fieldsSelector = { type: "exclude", fields: excludedFields.map((field) => field.getFieldDescriptor()) };
|
|
71
102
|
}
|
|
72
|
-
return filter;
|
|
73
103
|
}
|
|
74
104
|
function createClassContentRuleset(fullClassName) {
|
|
75
105
|
const { schemaName, className } = parseFullClassName(fullClassName);
|
|
@@ -93,27 +123,59 @@ function createClassContentRuleset(fullClassName) {
|
|
|
93
123
|
],
|
|
94
124
|
};
|
|
95
125
|
}
|
|
96
|
-
/**
|
|
126
|
+
/**
|
|
127
|
+
* Given a list of element ids, group them by class name. In addition, for every element, get ID of the previous
|
|
128
|
+
* element of the same class, which lets us put requested IDs into ranges without other instances of the same class.
|
|
129
|
+
*
|
|
130
|
+
* The query reader re-executes the query for every page, so querying all IDs at once is much slower than querying
|
|
131
|
+
* them in chunks. Chunks are made of the sorted IDs list and run in parallel. `GROUP_CONCAT` order isn't guaranteed
|
|
132
|
+
* by SQLite, so each chunk's IDs are sorted individually (usually a no-op check), then stored at the chunk's index.
|
|
133
|
+
* Concatenating them gives each class its IDs sorted.
|
|
134
|
+
*/
|
|
97
135
|
function getElementClassesFromIds(imodel, elementIds) {
|
|
98
136
|
const elementIdsBatchSize = 5000;
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
const
|
|
137
|
+
const sortedElementIds = OrderedId64Iterable.sortArray([...elementIds]);
|
|
138
|
+
return range(0, sortedElementIds.length / elementIdsBatchSize).pipe(mergeMap((_, chunkIndex) => {
|
|
139
|
+
const idsFrom = chunkIndex * elementIdsBatchSize;
|
|
140
|
+
const idsTo = Math.min(idsFrom + elementIdsBatchSize, sortedElementIds.length);
|
|
102
141
|
return from(imodel.createQueryReader(`
|
|
103
|
-
SELECT
|
|
142
|
+
SELECT
|
|
143
|
+
ec_classname(e.ECClassId) className,
|
|
144
|
+
GROUP_CONCAT(
|
|
145
|
+
IdToHex(e.ECInstanceId) || ':' || IFNULL((
|
|
146
|
+
SELECT IdToHex(MAX(p.ECInstanceId))
|
|
147
|
+
FROM bis.Element p
|
|
148
|
+
WHERE p.ECClassId = e.ECClassId AND p.ECInstanceId < e.ECInstanceId
|
|
149
|
+
), '')
|
|
150
|
+
) ids
|
|
104
151
|
FROM bis.Element e
|
|
105
|
-
|
|
152
|
+
JOIN IdSet(:elementIds) elementIds ON elementIds.id = e.ECInstanceId
|
|
106
153
|
GROUP BY e.ECClassId
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
154
|
+
`, new QueryBinder().bindIdSet("elementIds", sortedElementIds.slice(idsFrom, idsTo)))).pipe(map((row) => ({
|
|
155
|
+
chunkIndex,
|
|
156
|
+
className: row.className,
|
|
157
|
+
ids: ensureSortedById(row.ids.split(",").map((pair) => {
|
|
158
|
+
const [id, prevClassInstanceId] = pair.split(":");
|
|
159
|
+
return { id, ...(prevClassInstanceId ? { prevClassInstanceId } : undefined) };
|
|
160
|
+
})),
|
|
161
|
+
})));
|
|
162
|
+
}), groupBy(({ className }) => className), mergeMap((groups) => groups.pipe(reduce((acc, g) => {
|
|
163
|
+
acc.chunks[g.chunkIndex] = g.ids;
|
|
164
|
+
return { classFullName: g.className, chunks: acc.chunks };
|
|
165
|
+
}, { classFullName: "", chunks: [] }))),
|
|
166
|
+
// chunks cover consecutive ID ranges and are sorted individually, so their concatenation is sorted as well;
|
|
167
|
+
// `flat` skips holes left by chunks that had no elements of the class
|
|
168
|
+
map(({ classFullName, chunks }) => ({ classFullName, ids: chunks.flat() })));
|
|
169
|
+
}
|
|
170
|
+
function ensureSortedById(ids) {
|
|
171
|
+
for (let i = 1; i < ids.length; ++i) {
|
|
172
|
+
if (OrderedId64Iterable.compare(ids[i - 1].id, ids[i].id) > 0) {
|
|
173
|
+
return ids.sort((a, b) => OrderedId64Iterable.compare(a.id, b.id));
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
return ids;
|
|
115
177
|
}
|
|
116
|
-
/** Given a list of full class names, get
|
|
178
|
+
/** Given a list of full class names, get concrete class names with instances. */
|
|
117
179
|
function getClassesWithInstances(imodel, fullClassNames) {
|
|
118
180
|
return from(fullClassNames).pipe(mergeMap((fullClassName) => from(imodel.createQueryReader(`
|
|
119
181
|
SELECT ec_classname(e.ECClassId, 's.c') className
|
|
@@ -122,38 +184,28 @@ function getClassesWithInstances(imodel, fullClassNames) {
|
|
|
122
184
|
`))), map((row) => row.className));
|
|
123
185
|
}
|
|
124
186
|
/**
|
|
125
|
-
* Given a
|
|
126
|
-
* be sequential, a batch is defined a list of from-to pairs.
|
|
127
|
-
*
|
|
187
|
+
* Given a list of same-class ECInstanceIds, sorted by ID, and a batch size, create a stream of batches. Because the IDs won't
|
|
188
|
+
* necessarily be sequential, a batch is defined as a list of from-to pairs. An ID extends the current range if there are no other
|
|
189
|
+
* instances of the same class between it and the previous requested ID.
|
|
128
190
|
*/
|
|
129
|
-
|
|
191
|
+
function createIdBatches(sortedIds, batchSize) {
|
|
130
192
|
return range(0, sortedIds.length / batchSize).pipe(map((batchIndex) => {
|
|
131
|
-
const
|
|
193
|
+
const ranges = new Array();
|
|
132
194
|
const startIndex = batchIndex * batchSize;
|
|
133
195
|
const endIndex = Math.min((batchIndex + 1) * batchSize, sortedIds.length) - 1;
|
|
134
|
-
let fromId = sortedIds[startIndex];
|
|
135
|
-
let to = {
|
|
136
|
-
id: sortedIds[startIndex],
|
|
137
|
-
localId: Id64.getLocalId(sortedIds[startIndex]),
|
|
138
|
-
};
|
|
196
|
+
let fromId = sortedIds[startIndex].id;
|
|
139
197
|
for (let i = startIndex + 1; i <= endIndex; ++i) {
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
fromId = sortedIds[i];
|
|
198
|
+
if (sortedIds[i].prevClassInstanceId !== sortedIds[i - 1].id) {
|
|
199
|
+
ranges.push({ from: fromId, to: sortedIds[i - 1].id });
|
|
200
|
+
fromId = sortedIds[i].id;
|
|
144
201
|
}
|
|
145
|
-
to = { id: sortedIds[i], localId: currLocalId };
|
|
146
202
|
}
|
|
147
|
-
|
|
148
|
-
return
|
|
203
|
+
ranges.push({ from: fromId, to: sortedIds[endIndex].id });
|
|
204
|
+
return { ids: sortedIds.slice(startIndex, endIndex + 1).map(({ id }) => id), ranges };
|
|
149
205
|
}));
|
|
150
206
|
}
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
* @internal
|
|
154
|
-
*/
|
|
155
|
-
export function getBatchedClassElementIds(imodel, fullClassName, batchSize) {
|
|
156
|
-
return from(imodel.createQueryReader(`SELECT IdToHex(ECInstanceId) id FROM ${getECSqlName(fullClassName)} ORDER BY ECInstanceId`)).pipe(map((row) => row.id), bufferCount(batchSize), map((batch) => [{ from: batch[0], to: batch[batch.length - 1] }]));
|
|
207
|
+
function getBatchedClassElementIds(imodel, fullClassName, batchSize) {
|
|
208
|
+
return from(imodel.createQueryReader(`SELECT IdToHex(ECInstanceId) id FROM ONLY ${getECSqlName(fullClassName)} ORDER BY ECInstanceId`)).pipe(map((row) => row.id), bufferCount(batchSize), map((ids) => ({ ids, ranges: [{ from: ids[0], to: ids[ids.length - 1] }] })));
|
|
157
209
|
}
|
|
158
210
|
/** @internal */
|
|
159
211
|
export async function getElementsCount(db, classNames) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ElementPropertiesHelper.js","sourceRoot":"","sources":["../../../src/presentation-backend/ElementPropertiesHelper.ts"],"names":[],"mappings":"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","sourcesContent":["/*---------------------------------------------------------------------------------------------\r\n * Copyright (c) Bentley Systems, Incorporated. All rights reserved.\r\n * See LICENSE.md in the project root for license terms and full copyright notice.\r\n *--------------------------------------------------------------------------------------------*/\r\n/** @packageDocumentation\r\n * @module Core\r\n */\r\n\r\nimport { bufferCount, defer, from, groupBy, map, mergeMap, Observable, ObservedValueOf, of, range, reduce } from \"rxjs\";\r\nimport { IModelDb } from \"@itwin/core-backend\";\r\nimport { Id64, Id64Array, Id64String, OrderedId64Iterable } from \"@itwin/core-bentley\";\r\nimport { QueryRowProxy } from \"@itwin/core-common\";\r\nimport {\r\n ContentDescriptorRequestOptions,\r\n ContentRequestOptions,\r\n Descriptor,\r\n Item,\r\n KeySet,\r\n PresentationError,\r\n PresentationStatus,\r\n Ruleset,\r\n RulesetVariable,\r\n} from \"@itwin/presentation-common\";\r\nimport { parseFullClassName } from \"@itwin/presentation-shared\";\r\n\r\nfunction getECSqlName(fullClassName: string) {\r\n const { schemaName, className } = parseFullClassName(fullClassName);\r\n return `[${schemaName}].[${className}]`;\r\n}\r\n\r\n/** @internal */\r\nexport function getContentItemsObservableFromElementIds(\r\n imodel: IModelDb,\r\n contentDescriptorGetter: (\r\n partialProps: Pick<ContentDescriptorRequestOptions<IModelDb, KeySet, RulesetVariable>, \"rulesetOrId\" | \"keys\">,\r\n ) => Promise<Descriptor | undefined>,\r\n contentSetGetter: (\r\n partialProps: Pick<ContentRequestOptions<IModelDb, Descriptor, KeySet, RulesetVariable>, \"rulesetOrId\" | \"keys\" | \"descriptor\">,\r\n ) => Promise<Item[]>,\r\n elementIds: Id64String[],\r\n classParallelism: number,\r\n batchesParallelism: number,\r\n batchSize: number,\r\n): { itemBatches: Observable<{ descriptor: Descriptor; items: Item[] }>; count: Observable<number> } {\r\n return {\r\n itemBatches: getElementClassesFromIds(imodel, elementIds).pipe(\r\n mergeMap(\r\n ({ classFullName, ids }) =>\r\n getBatchedClassContentItems(\r\n classFullName,\r\n contentDescriptorGetter,\r\n contentSetGetter,\r\n () => createIdBatches(OrderedId64Iterable.sortArray(ids), batchSize),\r\n batchesParallelism,\r\n ),\r\n classParallelism,\r\n ),\r\n ),\r\n count: of(elementIds.length),\r\n };\r\n}\r\n\r\n/** @internal */\r\nexport function getContentItemsObservableFromClassNames(\r\n imodel: IModelDb,\r\n contentDescriptorGetter: (\r\n partialProps: Pick<ContentDescriptorRequestOptions<IModelDb, KeySet, RulesetVariable>, \"rulesetOrId\" | \"keys\">,\r\n ) => Promise<Descriptor | undefined>,\r\n contentSetGetter: (\r\n partialProps: Pick<ContentRequestOptions<IModelDb, Descriptor, KeySet, RulesetVariable>, \"rulesetOrId\" | \"keys\" | \"descriptor\">,\r\n ) => Promise<Item[]>,\r\n elementClasses: string[],\r\n classParallelism: number,\r\n batchesParallelism: number,\r\n batchSize: number,\r\n): { itemBatches: Observable<{ descriptor: Descriptor; items: Item[] }>; count: Observable<number> } {\r\n return {\r\n itemBatches: getClassesWithInstances(imodel, elementClasses).pipe(\r\n mergeMap(\r\n (classFullName) =>\r\n getBatchedClassContentItems(\r\n classFullName,\r\n contentDescriptorGetter,\r\n contentSetGetter,\r\n () => getBatchedClassElementIds(imodel, classFullName, batchSize),\r\n batchesParallelism,\r\n ),\r\n classParallelism,\r\n ),\r\n ),\r\n count: from(getElementsCount(imodel, elementClasses)),\r\n };\r\n}\r\n\r\nfunction getBatchedClassContentItems(\r\n classFullName: string,\r\n contentDescriptorGetter: (\r\n partialProps: Pick<ContentDescriptorRequestOptions<IModelDb, KeySet, RulesetVariable>, \"rulesetOrId\" | \"keys\">,\r\n ) => Promise<Descriptor | undefined>,\r\n contentSetGetter: (\r\n partialProps: Pick<ContentRequestOptions<IModelDb, Descriptor, KeySet, RulesetVariable>, \"rulesetOrId\" | \"keys\" | \"descriptor\">,\r\n ) => Promise<Item[]>,\r\n batcher: () => Observable<Array<{ from: Id64String; to: Id64String }>>,\r\n batchesParallelism: number,\r\n): Observable<{ descriptor: Descriptor; items: Item[] }> {\r\n return defer(async () => {\r\n const ruleset = createClassContentRuleset(classFullName);\r\n const keys = new KeySet();\r\n const descriptor = await contentDescriptorGetter({ rulesetOrId: ruleset, keys });\r\n if (!descriptor) {\r\n throw new PresentationError(PresentationStatus.Error, `Failed to get descriptor for class ${classFullName}`);\r\n }\r\n return { descriptor, keys, ruleset };\r\n }).pipe(\r\n // create elements' id batches\r\n mergeMap((x) => batcher().pipe(map((batch) => ({ ...x, batch })))),\r\n // request content for each batch, filter by IDs for performance\r\n mergeMap(\r\n ({ descriptor, keys, ruleset, batch }) =>\r\n defer(async () => {\r\n const filteringDescriptor = new Descriptor(descriptor);\r\n filteringDescriptor.instanceFilter = {\r\n selectClassName: classFullName,\r\n expression: createElementIdsECExpressionFilter(batch),\r\n };\r\n return contentSetGetter({\r\n rulesetOrId: ruleset,\r\n keys,\r\n descriptor: filteringDescriptor,\r\n });\r\n }).pipe(map((items) => ({ descriptor, items }))),\r\n batchesParallelism,\r\n ),\r\n );\r\n}\r\n\r\nfunction createElementIdsECExpressionFilter(batch: Array<{ from: Id64String; to: Id64String }>): string {\r\n let filter = \"\";\r\n function appendCondition(cond: string) {\r\n if (filter.length > 0) {\r\n filter += \" OR \";\r\n }\r\n filter += cond;\r\n }\r\n for (const item of batch) {\r\n if (item.from === item.to) {\r\n appendCondition(`this.ECInstanceId = ${item.from}`);\r\n } else {\r\n appendCondition(`this.ECInstanceId >= ${item.from} AND this.ECInstanceId <= ${item.to}`);\r\n }\r\n }\r\n return filter;\r\n}\r\n\r\nfunction createClassContentRuleset(fullClassName: string): Ruleset {\r\n const { schemaName, className } = parseFullClassName(fullClassName);\r\n return {\r\n id: `content/class-descriptor/${fullClassName}`,\r\n rules: [\r\n {\r\n ruleType: \"Content\",\r\n specifications: [\r\n {\r\n specType: \"ContentInstancesOfSpecificClasses\",\r\n classes: {\r\n schemaName,\r\n classNames: [className],\r\n arePolymorphic: false,\r\n },\r\n handlePropertiesPolymorphically: true,\r\n },\r\n ],\r\n },\r\n ],\r\n };\r\n}\r\n\r\n/** Given a list of element ids, group them by class name. */\r\nfunction getElementClassesFromIds(imodel: IModelDb, elementIds: string[]): Observable<{ classFullName: string; ids: Id64Array }> {\r\n const elementIdsBatchSize = 5000;\r\n return range(0, elementIds.length / elementIdsBatchSize).pipe(\r\n mergeMap((batchIndex) => {\r\n const idsFrom = batchIndex * elementIdsBatchSize;\r\n const idsTo = Math.min(idsFrom + elementIdsBatchSize, elementIds.length);\r\n return from(\r\n imodel.createQueryReader(\r\n `\r\n SELECT ec_classname(e.ECClassId) className, GROUP_CONCAT(IdToHex(e.ECInstanceId)) ids\r\n FROM bis.Element e\r\n WHERE e.ECInstanceId IN (${elementIds.slice(idsFrom, idsTo).join(\",\")})\r\n GROUP BY e.ECClassId\r\n `,\r\n ),\r\n );\r\n }),\r\n map((row: QueryRowProxy): { className: string; ids: Id64Array } => ({ className: row.className, ids: row.ids.split(\",\") })),\r\n groupBy(({ className }) => className),\r\n mergeMap((groups) =>\r\n groups.pipe(\r\n reduce<ObservedValueOf<typeof groups>, { classFullName: string; ids: Id64Array }>(\r\n (acc, g) => {\r\n g.ids.forEach((id) => acc.ids.push(id));\r\n return {\r\n classFullName: g.className,\r\n ids: acc.ids,\r\n };\r\n },\r\n { classFullName: \"\", ids: [] },\r\n ),\r\n ),\r\n ),\r\n );\r\n}\r\n\r\n/** Given a list of full class names, get a stream of actual class names that have instances. */\r\nfunction getClassesWithInstances(imodel: IModelDb, fullClassNames: string[]): Observable<string> {\r\n return from(fullClassNames).pipe(\r\n mergeMap((fullClassName) =>\r\n from(\r\n imodel.createQueryReader(\r\n `\r\n SELECT ec_classname(e.ECClassId, 's.c') className\r\n FROM ${getECSqlName(fullClassName)} e\r\n GROUP BY e.ECClassId\r\n `,\r\n ),\r\n ),\r\n ),\r\n map((row: QueryRowProxy): string => row.className),\r\n );\r\n}\r\n\r\n/**\r\n * Given a sorted list of ECInstanceIds and a batch size, create a stream of batches. Because the IDs won't necessarily\r\n * be sequential, a batch is defined a list of from-to pairs.\r\n * @internal\r\n */\r\nexport function createIdBatches(sortedIds: Id64String[], batchSize: number): Observable<Array<{ from: Id64String; to: Id64String }>> {\r\n return range(0, sortedIds.length / batchSize).pipe(\r\n map((batchIndex) => {\r\n const sequences = new Array<{ from: Id64String; to: Id64String }>();\r\n const startIndex = batchIndex * batchSize;\r\n const endIndex = Math.min((batchIndex + 1) * batchSize, sortedIds.length) - 1;\r\n let fromId = sortedIds[startIndex];\r\n let to = {\r\n id: sortedIds[startIndex],\r\n localId: Id64.getLocalId(sortedIds[startIndex]),\r\n };\r\n for (let i = startIndex + 1; i <= endIndex; ++i) {\r\n const currLocalId = Id64.getLocalId(sortedIds[i]);\r\n if (currLocalId !== to.localId + 1) {\r\n sequences.push({ from: fromId, to: sortedIds[i - 1] });\r\n fromId = sortedIds[i];\r\n }\r\n to = { id: sortedIds[i], localId: currLocalId };\r\n }\r\n sequences.push({ from: fromId, to: sortedIds[endIndex] });\r\n return sequences;\r\n }),\r\n );\r\n}\r\n\r\n/**\r\n * Query all ECInstanceIds from given class and stream from-to pairs that batch the items into batches of `batchSize` size.\r\n * @internal\r\n */\r\nexport function getBatchedClassElementIds(imodel: IModelDb, fullClassName: string, batchSize: number): Observable<Array<{ from: Id64String; to: Id64String }>> {\r\n return from(imodel.createQueryReader(`SELECT IdToHex(ECInstanceId) id FROM ${getECSqlName(fullClassName)} ORDER BY ECInstanceId`)).pipe(\r\n map((row): Id64String => row.id),\r\n bufferCount(batchSize),\r\n map((batch) => [{ from: batch[0], to: batch[batch.length - 1] }]),\r\n );\r\n}\r\n\r\n/** @internal */\r\nexport async function getElementsCount(db: IModelDb, classNames: string[]) {\r\n const whereClause = (() => {\r\n if (classNames === undefined || classNames.length === 0) {\r\n return undefined;\r\n }\r\n // check if list contains only valid class names\r\n const classNameRegExp = new RegExp(/^[\\w]+[.:][\\w]+$/);\r\n const invalidName = classNames.find((name) => !name.match(classNameRegExp));\r\n if (invalidName) {\r\n throw new PresentationError(\r\n PresentationStatus.InvalidArgument,\r\n `Encountered invalid class name - ${invalidName}.\r\n Valid class name formats: \"<schema name or alias>.<class name>\", \"<schema name or alias>:<class name>\"`,\r\n );\r\n }\r\n return `e.ECClassId IS (${classNames.map(getECSqlName).join(\",\")})`;\r\n })();\r\n const query = `\r\n SELECT COUNT(e.ECInstanceId) AS elementCount\r\n FROM bis.Element e\r\n ${whereClause ? `WHERE ${whereClause}` : \"\"}\r\n `;\r\n for await (const row of db.createQueryReader(query)) {\r\n return row.elementCount;\r\n }\r\n return 0;\r\n}\r\n"]}
|
|
1
|
+
{"version":3,"file":"ElementPropertiesHelper.js","sourceRoot":"","sources":["../../../src/presentation-backend/ElementPropertiesHelper.ts"],"names":[],"mappings":"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","sourcesContent":["/*---------------------------------------------------------------------------------------------\r\n * Copyright (c) Bentley Systems, Incorporated. All rights reserved.\r\n * See LICENSE.md in the project root for license terms and full copyright notice.\r\n *--------------------------------------------------------------------------------------------*/\r\n/** @packageDocumentation\r\n * @module Core\r\n */\r\n\r\nimport { bufferCount, defer, from, groupBy, map, mergeMap, Observable, ObservedValueOf, of, range, reduce } from \"rxjs\";\r\nimport { IModelDb } from \"@itwin/core-backend\";\r\nimport { Id64Array, Id64String, OrderedId64Iterable } from \"@itwin/core-bentley\";\r\nimport { QueryBinder, QueryRowProxy } from \"@itwin/core-common\";\r\nimport {\r\n ContentDescriptorRequestOptions,\r\n ContentRequestOptions,\r\n Descriptor,\r\n Field,\r\n Item,\r\n KeySet,\r\n NestedContentField,\r\n PresentationError,\r\n PresentationStatus,\r\n Ruleset,\r\n RulesetVariable,\r\n} from \"@itwin/presentation-common\";\r\nimport { parseFullClassName } from \"@itwin/presentation-shared\";\r\n\r\nfunction getECSqlName(fullClassName: string) {\r\n const { schemaName, className } = parseFullClassName(fullClassName);\r\n return `[${schemaName}].[${className}]`;\r\n}\r\n\r\n/** @internal */\r\nexport function getContentItemsObservableFromElementIds(\r\n imodel: IModelDb,\r\n contentDescriptorGetter: (\r\n partialProps: Pick<ContentDescriptorRequestOptions<IModelDb, KeySet, RulesetVariable>, \"rulesetOrId\" | \"keys\">,\r\n ) => Promise<Descriptor | undefined>,\r\n contentSetGetter: (\r\n partialProps: Pick<ContentRequestOptions<IModelDb, Descriptor, KeySet, RulesetVariable>, \"rulesetOrId\" | \"keys\" | \"descriptor\">,\r\n ) => Promise<Item[]>,\r\n elementIds: Id64String[],\r\n classParallelism: number,\r\n batchesParallelism: number,\r\n batchSize: number,\r\n): { itemBatches: Observable<{ descriptor: Descriptor; items: Item[] }>; count: Observable<number> } {\r\n return {\r\n itemBatches: getElementClassesFromIds(imodel, elementIds).pipe(\r\n mergeMap(\r\n ({ classFullName, ids }) =>\r\n getBatchedClassContentItems(\r\n imodel,\r\n classFullName,\r\n contentDescriptorGetter,\r\n contentSetGetter,\r\n () => createIdBatches(ids, batchSize),\r\n batchesParallelism,\r\n ),\r\n classParallelism,\r\n ),\r\n ),\r\n count: of(elementIds.length),\r\n };\r\n}\r\n\r\n/** @internal */\r\nexport function getContentItemsObservableFromClassNames(\r\n imodel: IModelDb,\r\n contentDescriptorGetter: (\r\n partialProps: Pick<ContentDescriptorRequestOptions<IModelDb, KeySet, RulesetVariable>, \"rulesetOrId\" | \"keys\">,\r\n ) => Promise<Descriptor | undefined>,\r\n contentSetGetter: (\r\n partialProps: Pick<ContentRequestOptions<IModelDb, Descriptor, KeySet, RulesetVariable>, \"rulesetOrId\" | \"keys\" | \"descriptor\">,\r\n ) => Promise<Item[]>,\r\n elementClasses: string[],\r\n classParallelism: number,\r\n batchesParallelism: number,\r\n batchSize: number,\r\n): { itemBatches: Observable<{ descriptor: Descriptor; items: Item[] }>; count: Observable<number> } {\r\n return {\r\n itemBatches: getClassesWithInstances(imodel, elementClasses).pipe(\r\n mergeMap(\r\n (classFullName) =>\r\n getBatchedClassContentItems(\r\n imodel,\r\n classFullName,\r\n contentDescriptorGetter,\r\n contentSetGetter,\r\n () => getBatchedClassElementIds(imodel, classFullName, batchSize),\r\n batchesParallelism,\r\n ),\r\n classParallelism,\r\n ),\r\n ),\r\n count: from(getElementsCount(imodel, elementClasses)),\r\n };\r\n}\r\n\r\nfunction getBatchedClassContentItems(\r\n imodel: IModelDb,\r\n classFullName: string,\r\n contentDescriptorGetter: (\r\n partialProps: Pick<ContentDescriptorRequestOptions<IModelDb, KeySet, RulesetVariable>, \"rulesetOrId\" | \"keys\">,\r\n ) => Promise<Descriptor | undefined>,\r\n contentSetGetter: (\r\n partialProps: Pick<ContentRequestOptions<IModelDb, Descriptor, KeySet, RulesetVariable>, \"rulesetOrId\" | \"keys\" | \"descriptor\">,\r\n ) => Promise<Item[]>,\r\n batcher: () => Observable<ElementIdBatch>,\r\n batchesParallelism: number,\r\n): Observable<{ descriptor: Descriptor; items: Item[] }> {\r\n return defer(async () => {\r\n const ruleset = createClassContentRuleset(classFullName);\r\n const keys = new KeySet();\r\n const descriptor = await contentDescriptorGetter({ rulesetOrId: ruleset, keys });\r\n if (!descriptor) {\r\n throw new PresentationError(PresentationStatus.Error, `Failed to get descriptor for class ${classFullName}`);\r\n }\r\n const aspectFields = getPrunableAspectFields(descriptor, classFullName);\r\n return { descriptor, keys, ruleset, aspectFields };\r\n }).pipe(\r\n mergeMap((x) => batcher().pipe(map((batch) => ({ ...x, batch })))),\r\n mergeMap(\r\n ({ descriptor, keys, ruleset, batch, aspectFields }) =>\r\n defer(async () => {\r\n const batchDescriptor = new Descriptor(descriptor);\r\n batchDescriptor.instanceFilter = {\r\n selectClassName: classFullName,\r\n expression: batch.ranges\r\n .map(({ from: fromId, to }) =>\r\n fromId === to ? `this.ECInstanceId = ${fromId}` : `this.ECInstanceId >= ${fromId} AND this.ECInstanceId <= ${to}`,\r\n )\r\n .join(\" OR \"),\r\n };\r\n if (aspectFields.length) {\r\n await selectBatchAspectFields(imodel, batchDescriptor, aspectFields, batch.ids);\r\n }\r\n const items = await contentSetGetter({\r\n rulesetOrId: ruleset,\r\n keys,\r\n descriptor: batchDescriptor,\r\n });\r\n return { descriptor: batchDescriptor, items };\r\n }),\r\n batchesParallelism,\r\n ),\r\n );\r\n}\r\n\r\nfunction getPrunableAspectFields(descriptor: Descriptor, classFullName: string): NestedContentField[] {\r\n // Caller selections take precedence; don't replace an include/exclude selector with our own.\r\n if (descriptor.fieldsSelector || countFields(descriptor.fields) <= 1000) {\r\n return [];\r\n }\r\n const { schemaName, className } = parseFullClassName(classFullName);\r\n return descriptor.fields.filter((field): field is NestedContentField => {\r\n if (!field.isNestedContentField() || field.pathToPrimaryClass.length !== 1) {\r\n return false;\r\n }\r\n const step = field.pathToPrimaryClass[0];\r\n return (\r\n !step.isForwardRelationship &&\r\n (step.relationshipInfo.name === \"BisCore:ElementOwnsMultiAspects\" || step.relationshipInfo.name === \"BisCore:ElementOwnsUniqueAspect\") &&\r\n step.sourceClassInfo.id === field.contentClassInfo.id &&\r\n step.targetClassInfo.name === `${schemaName}:${className}`\r\n );\r\n });\r\n}\r\n\r\nfunction countFields(fields: Field[]): number {\r\n return fields.reduce((count, field) => count + 1 + (field.isNestedContentField() ? countFields(field.nestedFields) : 0), 0);\r\n}\r\n\r\nasync function selectBatchAspectFields(imodel: IModelDb, descriptor: Descriptor, aspectFields: NestedContentField[], batch: Id64Array): Promise<void> {\r\n const query = `\r\n WITH aspectClasses(ClassId) AS (\r\n SELECT DISTINCT a.ECClassId\r\n FROM bis.ElementMultiAspect a\r\n JOIN IdSet(:elementIds) e ON e.id = a.Element.Id\r\n UNION ALL\r\n SELECT DISTINCT a.ECClassId\r\n FROM bis.ElementUniqueAspect a\r\n JOIN IdSet(:elementIds) e ON e.id = a.Element.Id\r\n )\r\n SELECT IdToHex(b.TargetECInstanceId) classId\r\n FROM meta.ClassHasAllBaseClasses b\r\n JOIN aspectClasses a ON a.ClassId = b.SourceECInstanceId\r\n `;\r\n const aspectClasses = new Set<Id64String>();\r\n for await (const row of imodel.createQueryReader(query, QueryBinder.from({ elementIds: batch }))) {\r\n aspectClasses.add(row.classId);\r\n }\r\n const excludedFields = aspectFields.filter((field) => !aspectClasses.has(field.contentClassInfo.id));\r\n if (excludedFields.length > 0) {\r\n descriptor.fieldsSelector = { type: \"exclude\", fields: excludedFields.map((field) => field.getFieldDescriptor()) };\r\n }\r\n}\r\n\r\nfunction createClassContentRuleset(fullClassName: string): Ruleset {\r\n const { schemaName, className } = parseFullClassName(fullClassName);\r\n return {\r\n id: `content/class-descriptor/${fullClassName}`,\r\n rules: [\r\n {\r\n ruleType: \"Content\",\r\n specifications: [\r\n {\r\n specType: \"ContentInstancesOfSpecificClasses\",\r\n classes: {\r\n schemaName,\r\n classNames: [className],\r\n arePolymorphic: false,\r\n },\r\n handlePropertiesPolymorphically: true,\r\n },\r\n ],\r\n },\r\n ],\r\n };\r\n}\r\n\r\ninterface ElementIdWithPrevClassInstance {\r\n id: Id64String;\r\n /** ID of the previous (by ECInstanceId) element of the same class, if any. */\r\n prevClassInstanceId?: Id64String;\r\n}\r\n\r\n/**\r\n * Given a list of element ids, group them by class name. In addition, for every element, get ID of the previous\r\n * element of the same class, which lets us put requested IDs into ranges without other instances of the same class.\r\n *\r\n * The query reader re-executes the query for every page, so querying all IDs at once is much slower than querying\r\n * them in chunks. Chunks are made of the sorted IDs list and run in parallel. `GROUP_CONCAT` order isn't guaranteed\r\n * by SQLite, so each chunk's IDs are sorted individually (usually a no-op check), then stored at the chunk's index.\r\n * Concatenating them gives each class its IDs sorted.\r\n */\r\nfunction getElementClassesFromIds(\r\n imodel: IModelDb,\r\n elementIds: Id64Array,\r\n): Observable<{ classFullName: string; ids: ElementIdWithPrevClassInstance[] }> {\r\n const elementIdsBatchSize = 5000;\r\n const sortedElementIds = OrderedId64Iterable.sortArray([...elementIds]);\r\n return range(0, sortedElementIds.length / elementIdsBatchSize).pipe(\r\n mergeMap((_, chunkIndex) => {\r\n const idsFrom = chunkIndex * elementIdsBatchSize;\r\n const idsTo = Math.min(idsFrom + elementIdsBatchSize, sortedElementIds.length);\r\n return from(\r\n imodel.createQueryReader(\r\n `\r\n SELECT\r\n ec_classname(e.ECClassId) className,\r\n GROUP_CONCAT(\r\n IdToHex(e.ECInstanceId) || ':' || IFNULL((\r\n SELECT IdToHex(MAX(p.ECInstanceId))\r\n FROM bis.Element p\r\n WHERE p.ECClassId = e.ECClassId AND p.ECInstanceId < e.ECInstanceId\r\n ), '')\r\n ) ids\r\n FROM bis.Element e\r\n JOIN IdSet(:elementIds) elementIds ON elementIds.id = e.ECInstanceId\r\n GROUP BY e.ECClassId\r\n `,\r\n new QueryBinder().bindIdSet(\"elementIds\", sortedElementIds.slice(idsFrom, idsTo)),\r\n ),\r\n ).pipe(\r\n map((row: QueryRowProxy) => ({\r\n chunkIndex,\r\n className: row.className as string,\r\n ids: ensureSortedById(\r\n (row.ids as string).split(\",\").map((pair): ElementIdWithPrevClassInstance => {\r\n const [id, prevClassInstanceId] = pair.split(\":\");\r\n return { id, ...(prevClassInstanceId ? { prevClassInstanceId } : undefined) };\r\n }),\r\n ),\r\n })),\r\n );\r\n }),\r\n groupBy(({ className }) => className),\r\n mergeMap((groups) =>\r\n groups.pipe(\r\n reduce<ObservedValueOf<typeof groups>, { classFullName: string; chunks: ElementIdWithPrevClassInstance[][] }>(\r\n (acc, g) => {\r\n acc.chunks[g.chunkIndex] = g.ids;\r\n return { classFullName: g.className, chunks: acc.chunks };\r\n },\r\n { classFullName: \"\", chunks: [] },\r\n ),\r\n ),\r\n ),\r\n // chunks cover consecutive ID ranges and are sorted individually, so their concatenation is sorted as well;\r\n // `flat` skips holes left by chunks that had no elements of the class\r\n map(({ classFullName, chunks }) => ({ classFullName, ids: chunks.flat() })),\r\n );\r\n}\r\n\r\nfunction ensureSortedById(ids: ElementIdWithPrevClassInstance[]): ElementIdWithPrevClassInstance[] {\r\n for (let i = 1; i < ids.length; ++i) {\r\n if (OrderedId64Iterable.compare(ids[i - 1].id, ids[i].id) > 0) {\r\n return ids.sort((a, b) => OrderedId64Iterable.compare(a.id, b.id));\r\n }\r\n }\r\n return ids;\r\n}\r\n\r\n/** Given a list of full class names, get concrete class names with instances. */\r\nfunction getClassesWithInstances(imodel: IModelDb, fullClassNames: string[]): Observable<string> {\r\n return from(fullClassNames).pipe(\r\n mergeMap((fullClassName) =>\r\n from(\r\n imodel.createQueryReader(\r\n `\r\n SELECT ec_classname(e.ECClassId, 's.c') className\r\n FROM ${getECSqlName(fullClassName)} e\r\n GROUP BY e.ECClassId\r\n `,\r\n ),\r\n ),\r\n ),\r\n map((row: QueryRowProxy): string => row.className),\r\n );\r\n}\r\n\r\ninterface ElementIdBatch {\r\n ids: Id64Array;\r\n ranges: Array<{ from: Id64String; to: Id64String }>;\r\n}\r\n\r\n/**\r\n * Given a list of same-class ECInstanceIds, sorted by ID, and a batch size, create a stream of batches. Because the IDs won't\r\n * necessarily be sequential, a batch is defined as a list of from-to pairs. An ID extends the current range if there are no other\r\n * instances of the same class between it and the previous requested ID.\r\n */\r\nfunction createIdBatches(sortedIds: ElementIdWithPrevClassInstance[], batchSize: number): Observable<ElementIdBatch> {\r\n return range(0, sortedIds.length / batchSize).pipe(\r\n map((batchIndex) => {\r\n const ranges = new Array<{ from: Id64String; to: Id64String }>();\r\n const startIndex = batchIndex * batchSize;\r\n const endIndex = Math.min((batchIndex + 1) * batchSize, sortedIds.length) - 1;\r\n let fromId = sortedIds[startIndex].id;\r\n for (let i = startIndex + 1; i <= endIndex; ++i) {\r\n if (sortedIds[i].prevClassInstanceId !== sortedIds[i - 1].id) {\r\n ranges.push({ from: fromId, to: sortedIds[i - 1].id });\r\n fromId = sortedIds[i].id;\r\n }\r\n }\r\n ranges.push({ from: fromId, to: sortedIds[endIndex].id });\r\n return { ids: sortedIds.slice(startIndex, endIndex + 1).map(({ id }) => id), ranges };\r\n }),\r\n );\r\n}\r\n\r\nfunction getBatchedClassElementIds(imodel: IModelDb, fullClassName: string, batchSize: number): Observable<ElementIdBatch> {\r\n return from(imodel.createQueryReader(`SELECT IdToHex(ECInstanceId) id FROM ONLY ${getECSqlName(fullClassName)} ORDER BY ECInstanceId`)).pipe(\r\n map((row): Id64String => row.id),\r\n bufferCount(batchSize),\r\n map((ids) => ({ ids, ranges: [{ from: ids[0], to: ids[ids.length - 1] }] })),\r\n );\r\n}\r\n\r\n/** @internal */\r\nexport async function getElementsCount(db: IModelDb, classNames: string[]) {\r\n const whereClause = (() => {\r\n if (classNames === undefined || classNames.length === 0) {\r\n return undefined;\r\n }\r\n // check if list contains only valid class names\r\n const classNameRegExp = new RegExp(/^[\\w]+[.:][\\w]+$/);\r\n const invalidName = classNames.find((name) => !name.match(classNameRegExp));\r\n if (invalidName) {\r\n throw new PresentationError(\r\n PresentationStatus.InvalidArgument,\r\n `Encountered invalid class name - ${invalidName}.\r\n Valid class name formats: \"<schema name or alias>.<class name>\", \"<schema name or alias>:<class name>\"`,\r\n );\r\n }\r\n return `e.ECClassId IS (${classNames.map(getECSqlName).join(\",\")})`;\r\n })();\r\n const query = `\r\n SELECT COUNT(e.ECInstanceId) AS elementCount\r\n FROM bis.Element e\r\n ${whereClause ? `WHERE ${whereClause}` : \"\"}\r\n `;\r\n for await (const row of db.createQueryReader(query)) {\r\n return row.elementCount;\r\n }\r\n return 0;\r\n}\r\n"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@itwin/presentation-backend",
|
|
3
|
-
"version": "5.
|
|
3
|
+
"version": "5.15.0-dev.2",
|
|
4
4
|
"description": "Backend of iTwin.js Presentation library",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -36,12 +36,12 @@
|
|
|
36
36
|
"./package.json": "./package.json"
|
|
37
37
|
},
|
|
38
38
|
"peerDependencies": {
|
|
39
|
-
"@itwin/core-
|
|
40
|
-
"@itwin/core-
|
|
41
|
-
"@itwin/core-
|
|
42
|
-
"@itwin/
|
|
43
|
-
"@itwin/ecschema-metadata": "5.
|
|
44
|
-
"@itwin/
|
|
39
|
+
"@itwin/core-bentley": "5.15.0-dev.2",
|
|
40
|
+
"@itwin/core-backend": "5.15.0-dev.2",
|
|
41
|
+
"@itwin/core-quantity": "5.15.0-dev.2",
|
|
42
|
+
"@itwin/presentation-common": "5.15.0-dev.2",
|
|
43
|
+
"@itwin/ecschema-metadata": "5.15.0-dev.2",
|
|
44
|
+
"@itwin/core-common": "5.15.0-dev.2"
|
|
45
45
|
},
|
|
46
46
|
"devDependencies": {
|
|
47
47
|
"@itwin/eslint-plugin": "^6.0.0",
|
|
@@ -75,14 +75,14 @@
|
|
|
75
75
|
"sinon": "^17.0.2",
|
|
76
76
|
"sinon-chai": "^3.7.0",
|
|
77
77
|
"typescript": "~5.6.2",
|
|
78
|
-
"@itwin/
|
|
79
|
-
"@itwin/
|
|
80
|
-
"@itwin/
|
|
81
|
-
"@itwin/
|
|
82
|
-
"@itwin/
|
|
83
|
-
"@itwin/core-
|
|
84
|
-
"
|
|
85
|
-
"
|
|
78
|
+
"@itwin/core-quantity": "5.15.0-dev.2",
|
|
79
|
+
"@itwin/build-tools": "5.15.0-dev.2",
|
|
80
|
+
"@itwin/core-backend": "5.15.0-dev.2",
|
|
81
|
+
"@itwin/ecschema-metadata": "5.15.0-dev.2",
|
|
82
|
+
"@itwin/presentation-common": "5.15.0-dev.2",
|
|
83
|
+
"@itwin/core-bentley": "5.15.0-dev.2",
|
|
84
|
+
"internal-tools": "3.0.0-dev.69",
|
|
85
|
+
"@itwin/core-common": "5.15.0-dev.2"
|
|
86
86
|
},
|
|
87
87
|
"dependencies": {
|
|
88
88
|
"@itwin/presentation-shared": "^1.2.2",
|