From 69241852c88cd2c160ccf66dfb27e030e0c63b46 Mon Sep 17 00:00:00 2001 From: Grigas <35135765+grigasp@users.noreply.github.com> Date: Tue, 24 May 2022 23:48:59 +0300 Subject: [PATCH] UI: Fix performance of getting subject models (#3666) --- ...t-models-performance_2022-05-24-08-55.json | 10 +++ .../models-tree/ModelsVisibilityHandler.ts | 67 ++++++++++++------- .../ModelsVisibilityHandler.test.ts | 4 +- 3 files changed, 53 insertions(+), 28 deletions(-) create mode 100644 common/changes/@itwin/appui-react/ui-models-tree-fix-determining-subject-models-performance_2022-05-24-08-55.json diff --git a/common/changes/@itwin/appui-react/ui-models-tree-fix-determining-subject-models-performance_2022-05-24-08-55.json b/common/changes/@itwin/appui-react/ui-models-tree-fix-determining-subject-models-performance_2022-05-24-08-55.json new file mode 100644 index 000000000000..34129ce779b0 --- /dev/null +++ b/common/changes/@itwin/appui-react/ui-models-tree-fix-determining-subject-models-performance_2022-05-24-08-55.json @@ -0,0 +1,10 @@ +{ + "changes": [ + { + "packageName": "@itwin/appui-react", + "comment": "Models Tree: Fix performance of determining Subject nodes' display state.", + "type": "none" + } + ], + "packageName": "@itwin/appui-react" +} diff --git a/ui/appui-react/src/appui-react/imodel-components/models-tree/ModelsVisibilityHandler.ts b/ui/appui-react/src/appui-react/imodel-components/models-tree/ModelsVisibilityHandler.ts index ff20fdc627cd..efe4fbc6fafc 100644 --- a/ui/appui-react/src/appui-react/imodel-components/models-tree/ModelsVisibilityHandler.ts +++ b/ui/appui-react/src/appui-react/imodel-components/models-tree/ModelsVisibilityHandler.ts @@ -419,43 +419,58 @@ class SubjectModelIdsCache { this._imodel = imodel; } - private async initSubjectsHierarchy() { - this._subjectsHierarchy = new Map(); - const ecsql = `SELECT ECInstanceId id, Parent.Id parentId FROM bis.Subject WHERE Parent IS NOT NULL`; - const result = this._imodel.query(ecsql, undefined, { rowFormat: QueryRowFormat.UseJsPropertyNames }); - for await (const row of result) { - let list = this._subjectsHierarchy.get(row.parentId); + private async initSubjectModels() { + const querySubjects = (): AsyncIterableIterator<{ id: Id64String, parentId?: Id64String, targetPartitionId?: Id64String }> => { + const subjectsQuery = ` + SELECT ECInstanceId id, Parent.Id parentId, json_extract(JsonProperties, '$.Subject.Model.TargetPartition') targetPartitionId + FROM bis.Subject + `; + return this._imodel.query(subjectsQuery, undefined, { rowFormat: QueryRowFormat.UseJsPropertyNames }); + }; + const queryModels = (): AsyncIterableIterator<{ id: Id64String, parentId: Id64String, content?: string }> => { + const modelsQuery = ` + SELECT p.ECInstanceId id, p.Parent.Id parentId, json_extract(p.JsonProperties, '$.PhysicalPartition.Model.Content') content + FROM bis.InformationPartitionElement p + INNER JOIN bis.GeometricModel3d m ON m.ModeledElement.Id = p.ECInstanceId + WHERE NOT m.IsPrivate + `; + return this._imodel.query(modelsQuery, undefined, { rowFormat: QueryRowFormat.UseJsPropertyNames }); + }; + + function pushToMap(map: Map, key: TKey, value: TValue) { + let list = map.get(key); if (!list) { list = []; - this._subjectsHierarchy.set(row.parentId, list); + map.set(key, list); } - list.push(row.id); + list.push(value); + } + + this._subjectsHierarchy = new Map(); + const targetPartitionSubjects = new Map(); + for await (const subject of querySubjects()) { + if (subject.parentId) + pushToMap(this._subjectsHierarchy, subject.parentId, subject.id); + if (subject.targetPartitionId) + pushToMap(targetPartitionSubjects, subject.targetPartitionId, subject.id); } - } - private async initSubjectModels() { this._subjectModels = new Map(); - const ecsql = ` - SELECT p.ECInstanceId id, s.ECInstanceId subjectId, json_extract(p.JsonProperties, '$.PhysicalPartition.Model.Content') content - FROM bis.InformationPartitionElement p - INNER JOIN bis.GeometricModel3d m ON m.ModeledElement.Id = p.ECInstanceId - INNER JOIN bis.Subject s ON (s.ECInstanceId = p.Parent.Id OR json_extract(s.JsonProperties, '$.Subject.Model.TargetPartition') = printf('0x%x', p.ECInstanceId)) - WHERE NOT m.IsPrivate`; - const result = this._imodel.query(ecsql, undefined, { rowFormat: QueryRowFormat.UseJsPropertyNames }); - for await (const row of result) { - let list = this._subjectModels.get(row.subjectId); - if (!list) { - list = []; - this._subjectModels.set(row.subjectId, list); - } - const isHidden = row.content !== undefined; - list.push({ id: row.id, isHidden }); + for await (const model of queryModels()) { + const subjectIds = targetPartitionSubjects.get(model.id) ?? []; + if (!subjectIds.includes(model.parentId)) + subjectIds.push(model.parentId); + + const v = { id: model.id, isHidden: (model.content !== undefined) }; + subjectIds.forEach((subjectId) => { + pushToMap(this._subjectModels!, subjectId, v); + }); } } private async initCache() { if (!this._init) { - this._init = Promise.all([this.initSubjectModels(), this.initSubjectsHierarchy()]).then(() => { }); + this._init = this.initSubjectModels().then(() => { }); } return this._init; } diff --git a/ui/appui-react/src/test/imodel-components/models-tree/ModelsVisibilityHandler.test.ts b/ui/appui-react/src/test/imodel-components/models-tree/ModelsVisibilityHandler.test.ts index 649acf2a5919..28497818ec0a 100644 --- a/ui/appui-react/src/test/imodel-components/models-tree/ModelsVisibilityHandler.test.ts +++ b/ui/appui-react/src/test/imodel-components/models-tree/ModelsVisibilityHandler.test.ts @@ -104,8 +104,8 @@ describe("ModelsVisibilityHandler", () => { }); props.imodelMock.setup((x) => x.query(moq.It.is((q: string) => (-1 !== q.indexOf("FROM bis.InformationPartitionElement"))), undefined, { rowFormat: QueryRowFormat.UseJsPropertyNames })) .returns(async function* () { - const list = new Array<{ id: Id64String, subjectId: Id64String, content?: string }>(); - props.subjectModels.forEach((modelInfos, subjectId) => modelInfos.forEach((modelInfo) => list.push({ id: modelInfo.id, subjectId, content: modelInfo.content }))); + const list = new Array<{ id: Id64String, parentId: Id64String, content?: string }>(); + props.subjectModels.forEach((modelInfos, subjectId) => modelInfos.forEach((modelInfo) => list.push({ id: modelInfo.id, parentId: subjectId, content: modelInfo.content }))); while (list.length) yield list.shift(); });