@itwin/presentation-backend 5.14.0-dev.9 → 5.15.0-dev.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 CHANGED
@@ -1,6 +1,36 @@
1
1
  # Change Log - @itwin/presentation-backend
2
2
 
3
- This log was last generated on Thu, 03 Sep 2026 18:51:17 GMT and should not be manually modified.
3
+ This log was last generated on Fri, 25 Sep 2026 22:28:38 GMT and should not be manually modified.
4
+
5
+ ## 5.13.6
6
+ Fri, 25 Sep 2026 22:27:22 GMT
7
+
8
+ _Version update only_
9
+
10
+ ## 5.13.5
11
+ Tue, 22 Sep 2026 22:38:08 GMT
12
+
13
+ _Version update only_
14
+
15
+ ## 5.13.4
16
+ Fri, 18 Sep 2026 15:11:19 GMT
17
+
18
+ _Version update only_
19
+
20
+ ## 5.13.3
21
+ Tue, 15 Sep 2026 20:25:28 GMT
22
+
23
+ _Version update only_
24
+
25
+ ## 5.13.2
26
+ Mon, 14 Sep 2026 20:22:20 GMT
27
+
28
+ _Version update only_
29
+
30
+ ## 5.13.1
31
+ Tue, 08 Sep 2026 18:43:29 GMT
32
+
33
+ _Version update only_
4
34
 
5
35
  ## 5.13.0
6
36
  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,EAAmB,UAAU,EAAuB,MAAM,qBAAqB,CAAC;AAEvF,OAAO,EACL,+BAA+B,EAC/B,qBAAqB,EACrB,UAAU,EACV,IAAI,EACJ,MAAM,EAIN,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,CAiBnG;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,CAiBnG;AA4ID;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,SAAS,EAAE,UAAU,EAAE,EAAE,SAAS,EAAE,MAAM,GAAG,UAAU,CAAC,KAAK,CAAC;IAAE,IAAI,EAAE,UAAU,CAAC;IAAC,EAAE,EAAE,UAAU,CAAA;CAAE,CAAC,CAAC,CAuBnI;AAED;;;GAGG;AACH,wBAAgB,yBAAyB,CAAC,MAAM,EAAE,QAAQ,EAAE,aAAa,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,UAAU,CAAC,KAAK,CAAC;IAAE,IAAI,EAAE,UAAU,CAAC;IAAC,EAAE,EAAE,UAAU,CAAA;CAAE,CAAC,CAAC,CAM7J;AAED,gBAAgB;AAChB,wBAAsB,gBAAgB,CAAC,EAAE,EAAE,QAAQ,EAAE,UAAU,EAAE,MAAM,EAAE,gBA0BxE"}
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(core_bentley_1.OrderedId64Iterable.sortArray(ids), batchSize), batchesParallelism), classParallelism)),
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
- return { descriptor, keys, ruleset };
46
- }).pipe(
47
- // create elements' id batches
48
- (0, rxjs_1.mergeMap)((x) => batcher().pipe((0, rxjs_1.map)((batch) => ({ ...x, batch })))),
49
- // request content for each batch, filter by IDs for performance
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: createElementIdsECExpressionFilter(batch),
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
- return contentSetGetter({
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: filteringDescriptor,
60
+ descriptor: batchDescriptor,
60
61
  });
61
- }).pipe((0, rxjs_1.map)((items) => ({ descriptor, items }))), batchesParallelism));
62
+ return { descriptor: batchDescriptor, items };
63
+ }), batchesParallelism));
62
64
  }
63
- function createElementIdsECExpressionFilter(batch) {
64
- let filter = "";
65
- function appendCondition(cond) {
66
- if (filter.length > 0) {
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
- for (const item of batch) {
72
- if (item.from === item.to) {
73
- appendCondition(`this.ECInstanceId = ${item.from}`);
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
- /** Given a list of element ids, group them by class name. */
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
- return (0, rxjs_1.range)(0, elementIds.length / elementIdsBatchSize).pipe((0, rxjs_1.mergeMap)((batchIndex) => {
107
- const idsFrom = batchIndex * elementIdsBatchSize;
108
- const idsTo = Math.min(idsFrom + elementIdsBatchSize, elementIds.length);
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 ec_classname(e.ECClassId) className, GROUP_CONCAT(IdToHex(e.ECInstanceId)) ids
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
- WHERE e.ECInstanceId IN (${elementIds.slice(idsFrom, idsTo).join(",")})
157
+ JOIN IdSet(:elementIds) elementIds ON elementIds.id = e.ECInstanceId
113
158
  GROUP BY e.ECClassId
114
- `));
115
- }), (0, rxjs_1.map)((row) => ({ className: row.className, ids: row.ids.split(",") })), (0, rxjs_1.groupBy)(({ className }) => className), (0, rxjs_1.mergeMap)((groups) => groups.pipe((0, rxjs_1.reduce)((acc, g) => {
116
- g.ids.forEach((id) => acc.ids.push(id));
117
- return {
118
- classFullName: g.className,
119
- ids: acc.ids,
120
- };
121
- }, { classFullName: "", ids: [] }))));
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 a stream of actual class names that have instances. */
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 sorted list of ECInstanceIds and a batch size, create a stream of batches. Because the IDs won't necessarily
133
- * be sequential, a batch is defined a list of from-to pairs.
134
- * @internal
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 sequences = new Array();
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
- const currLocalId = core_bentley_1.Id64.getLocalId(sortedIds[i]);
148
- if (currLocalId !== to.localId + 1) {
149
- sequences.push({ from: fromId, to: sortedIds[i - 1] });
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
- sequences.push({ from: fromId, to: sortedIds[endIndex] });
155
- return sequences;
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)((batch) => [{ from: batch[0], to: batch[batch.length - 1] }]));
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,EAAmB,UAAU,EAAuB,MAAM,qBAAqB,CAAC;AAEvF,OAAO,EACL,+BAA+B,EAC/B,qBAAqB,EACrB,UAAU,EACV,IAAI,EACJ,MAAM,EAIN,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,CAiBnG;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,CAiBnG;AA4ID;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,SAAS,EAAE,UAAU,EAAE,EAAE,SAAS,EAAE,MAAM,GAAG,UAAU,CAAC,KAAK,CAAC;IAAE,IAAI,EAAE,UAAU,CAAC;IAAC,EAAE,EAAE,UAAU,CAAA;CAAE,CAAC,CAAC,CAuBnI;AAED;;;GAGG;AACH,wBAAgB,yBAAyB,CAAC,MAAM,EAAE,QAAQ,EAAE,aAAa,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,UAAU,CAAC,KAAK,CAAC;IAAE,IAAI,EAAE,UAAU,CAAC;IAAC,EAAE,EAAE,UAAU,CAAA;CAAE,CAAC,CAAC,CAM7J;AAED,gBAAgB;AAChB,wBAAsB,gBAAgB,CAAC,EAAE,EAAE,QAAQ,EAAE,UAAU,EAAE,MAAM,EAAE,gBA0BxE"}
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 { Id64, OrderedId64Iterable } from "@itwin/core-bentley";
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(OrderedId64Iterable.sortArray(ids), batchSize), batchesParallelism), classParallelism)),
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
- return { descriptor, keys, ruleset };
39
- }).pipe(
40
- // create elements' id batches
41
- mergeMap((x) => batcher().pipe(map((batch) => ({ ...x, batch })))),
42
- // request content for each batch, filter by IDs for performance
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: createElementIdsECExpressionFilter(batch),
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
- return contentSetGetter({
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: filteringDescriptor,
55
+ descriptor: batchDescriptor,
53
56
  });
54
- }).pipe(map((items) => ({ descriptor, items }))), batchesParallelism));
57
+ return { descriptor: batchDescriptor, items };
58
+ }), batchesParallelism));
55
59
  }
56
- function createElementIdsECExpressionFilter(batch) {
57
- let filter = "";
58
- function appendCondition(cond) {
59
- if (filter.length > 0) {
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
- for (const item of batch) {
65
- if (item.from === item.to) {
66
- appendCondition(`this.ECInstanceId = ${item.from}`);
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
- /** Given a list of element ids, group them by class name. */
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
- return range(0, elementIds.length / elementIdsBatchSize).pipe(mergeMap((batchIndex) => {
100
- const idsFrom = batchIndex * elementIdsBatchSize;
101
- const idsTo = Math.min(idsFrom + elementIdsBatchSize, elementIds.length);
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 ec_classname(e.ECClassId) className, GROUP_CONCAT(IdToHex(e.ECInstanceId)) ids
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
- WHERE e.ECInstanceId IN (${elementIds.slice(idsFrom, idsTo).join(",")})
152
+ JOIN IdSet(:elementIds) elementIds ON elementIds.id = e.ECInstanceId
106
153
  GROUP BY e.ECClassId
107
- `));
108
- }), map((row) => ({ className: row.className, ids: row.ids.split(",") })), groupBy(({ className }) => className), mergeMap((groups) => groups.pipe(reduce((acc, g) => {
109
- g.ids.forEach((id) => acc.ids.push(id));
110
- return {
111
- classFullName: g.className,
112
- ids: acc.ids,
113
- };
114
- }, { classFullName: "", ids: [] }))));
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 a stream of actual class names that have instances. */
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 sorted list of ECInstanceIds and a batch size, create a stream of batches. Because the IDs won't necessarily
126
- * be sequential, a batch is defined a list of from-to pairs.
127
- * @internal
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
- export function createIdBatches(sortedIds, batchSize) {
191
+ function createIdBatches(sortedIds, batchSize) {
130
192
  return range(0, sortedIds.length / batchSize).pipe(map((batchIndex) => {
131
- const sequences = new Array();
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
- const currLocalId = Id64.getLocalId(sortedIds[i]);
141
- if (currLocalId !== to.localId + 1) {
142
- sequences.push({ from: fromId, to: sortedIds[i - 1] });
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
- sequences.push({ from: fromId, to: sortedIds[endIndex] });
148
- return sequences;
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
- * Query all ECInstanceIds from given class and stream from-to pairs that batch the items into batches of `batchSize` size.
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.14.0-dev.9",
3
+ "version": "5.15.0-dev.1",
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-backend": "5.14.0-dev.9",
40
- "@itwin/core-quantity": "5.14.0-dev.9",
41
- "@itwin/core-common": "5.14.0-dev.9",
42
- "@itwin/core-bentley": "5.14.0-dev.9",
43
- "@itwin/ecschema-metadata": "5.14.0-dev.9",
44
- "@itwin/presentation-common": "5.14.0-dev.9"
39
+ "@itwin/core-backend": "5.15.0-dev.1",
40
+ "@itwin/core-bentley": "5.15.0-dev.1",
41
+ "@itwin/core-common": "5.15.0-dev.1",
42
+ "@itwin/core-quantity": "5.15.0-dev.1",
43
+ "@itwin/ecschema-metadata": "5.15.0-dev.1",
44
+ "@itwin/presentation-common": "5.15.0-dev.1"
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/build-tools": "5.14.0-dev.9",
79
- "@itwin/core-backend": "5.14.0-dev.9",
80
- "@itwin/ecschema-metadata": "5.14.0-dev.9",
81
- "@itwin/presentation-common": "5.14.0-dev.9",
82
- "@itwin/core-bentley": "5.14.0-dev.9",
83
- "@itwin/core-quantity": "5.14.0-dev.9",
84
- "@itwin/core-common": "5.14.0-dev.9",
85
- "internal-tools": "3.0.0-dev.69"
78
+ "@itwin/core-quantity": "5.15.0-dev.1",
79
+ "@itwin/core-backend": "5.15.0-dev.1",
80
+ "@itwin/build-tools": "5.15.0-dev.1",
81
+ "@itwin/ecschema-metadata": "5.15.0-dev.1",
82
+ "@itwin/presentation-common": "5.15.0-dev.1",
83
+ "@itwin/core-bentley": "5.15.0-dev.1",
84
+ "internal-tools": "3.0.0-dev.69",
85
+ "@itwin/core-common": "5.15.0-dev.1"
86
86
  },
87
87
  "dependencies": {
88
88
  "@itwin/presentation-shared": "^1.2.2",