Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

UI: Fix performance of getting subject models in Models Tree (backport #3666) #3678

Merged
merged 2 commits into from
May 26, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -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"
}
Original file line number Diff line number Diff line change
Expand Up @@ -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<TKey, TValue>(map: Map<TKey, TValue[]>, 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<Id64String, Id64String[]>();
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;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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();
});
Expand Down