[Tree widget]: plan for child categories on next#1678
Conversation
There was a problem hiding this comment.
Pull request overview
Adds a design/implementation plan document for addressing incorrect element/category visibility handling in the tree widget when descendant elements belong to different categories (prep for #1100, #1561, #1563).
Changes:
- Added a new markdown plan describing intermediate category nodes, cache redesign/renames, and phased implementation steps.
Comments suppressed due to low confidence (1)
packages/itwin/tree-widget/child-categories-plan.md:260
getNestedChildren(...)is specified to return anId64Array, but existing caches in this codebase (includingElementChildrenCache) returnObservable<…>and batch DB work internally. Consider clarifying the async contract here (e.g.,Observable<Id64Array>/Promise<Id64Array>) so consumers don’t implement it as a blocking call.
Single method: `getNestedChildren(props: NestedChildrenCategoryRequest | NestedChildrenElementRequest)` → `Id64Array`
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
There was a problem hiding this comment.
Tree-Widget Next benchmark
| Benchmark suite | Current: 0c533bd | Previous: 075b3d4 | Deviation | Status |
|---|---|---|---|---|
models tree 50k 3D elements search > get search paths |
1223 ms |
1281 ms |
-4.53% |
〰️ |
models tree 50k 3D elements search > get search paths (P95 of main thread blocks) |
52 ms |
64 ms |
-18.75% |
〰️ |
models tree 50k 3D elements search > load hierarchy from search paths |
109775 ms |
120795 ms |
-9.12% |
〰️ |
models tree 50k 3D elements search > load hierarchy from search paths (P95 of main thread blocks) |
38 ms |
36 ms |
5.56% |
〰️ |
models tree 50k categories > collect nodes |
2921 ms |
3120 ms |
-6.38% |
〰️ |
models tree 50k categories > collect nodes (P95 of main thread blocks) |
124 ms |
147 ms |
-15.65% |
〰️ |
models tree 50k categories > validate initial visibility |
2525 ms |
2633 ms |
-4.10% |
〰️ |
models tree 50k categories > validate initial visibility (P95 of main thread blocks) |
0 ms |
0 ms |
0% |
🟰 |
models tree 50k categories > change visibility |
79 ms |
81 ms |
-2.47% |
〰️ |
models tree 50k categories > change visibility (P95 of main thread blocks) |
68 ms |
71 ms |
-4.23% |
〰️ |
models tree 50k categories > validate changed visibility |
4625 ms |
4718 ms |
-1.97% |
〰️ |
models tree 50k categories > validate changed visibility (P95 of main thread blocks) |
22 ms |
27 ms |
-18.52% |
〰️ |
models tree 50k 3D elements > collect nodes |
43981 ms |
47809 ms |
-8.01% |
〰️ |
models tree 50k 3D elements > collect nodes (P95 of main thread blocks) |
71 ms |
61 ms |
16.39% |
〰️ |
models tree 50k 3D elements > validate initial visibility |
2596 ms |
2620 ms |
-0.92% |
〰️ |
models tree 50k 3D elements > validate initial visibility (P95 of main thread blocks) |
29 ms |
0 ms |
2900% |
〰️ |
models tree 50k 3D elements > change model visibility |
19 ms |
24 ms |
-20.83% |
〰️ |
models tree 50k 3D elements > change model visibility (P95 of main thread blocks) |
0 ms |
0 ms |
0% |
🟰 |
models tree 50k 3D elements > validate changed model visibility |
2476 ms |
2600 ms |
-4.77% |
〰️ |
models tree 50k 3D elements > validate changed model visibility (P95 of main thread blocks) |
0 ms |
0 ms |
0% |
🟰 |
models tree 50k 3D elements > change category node visibility |
512 ms |
528 ms |
-3.03% |
〰️ |
models tree 50k 3D elements > change category node visibility (P95 of main thread blocks) |
67 ms |
65 ms |
3.08% |
〰️ |
models tree 50k 3D elements > validate changed category visibility |
2503 ms |
2608 ms |
-4.03% |
〰️ |
models tree 50k 3D elements > validate changed category visibility (P95 of main thread blocks) |
0 ms |
0 ms |
0% |
🟰 |
models tree 50k 3D elements > validate per-model category override |
2485 ms |
2578 ms |
-3.61% |
〰️ |
models tree 50k 3D elements > validate per-model category override (P95 of main thread blocks) |
0 ms |
0 ms |
0% |
🟰 |
models tree 50k 3D elements > change element visibility |
11 ms |
11 ms |
0% |
🟰 |
models tree 50k 3D elements > change element visibility (P95 of main thread blocks) |
0 ms |
0 ms |
0% |
🟰 |
models tree 50k 3D elements > validate changed element visibility |
3164 ms |
3366 ms |
-6.00% |
〰️ |
models tree 50k 3D elements > validate changed element visibility (P95 of main thread blocks) |
56 ms |
75 ms |
-25.33% |
〰️ |
models tree 50k 3D child elements with different categories > collect nodes |
43971 ms |
48007 ms |
-8.41% |
〰️ |
models tree 50k 3D child elements with different categories > collect nodes (P95 of main thread blocks) |
78 ms |
65 ms |
20% |
〰️ |
models tree 50k 3D child elements with different categories > validate initial visibility |
2443 ms |
2647 ms |
-7.71% |
〰️ |
models tree 50k 3D child elements with different categories > validate initial visibility (P95 of main thread blocks) |
0 ms |
32 ms |
-100% |
〰️ |
models tree 50k 3D child elements with different categories > change visibility |
10 ms |
10 ms |
0% |
🟰 |
models tree 50k 3D child elements with different categories > change visibility (P95 of main thread blocks) |
0 ms |
0 ms |
0% |
🟰 |
models tree 50k 3D child elements with different categories > validate changed visibility |
3258 ms |
3259 ms |
-0.03% |
〰️ |
models tree 50k 3D child elements with different categories > validate changed visibility (P95 of main thread blocks) |
78 ms |
74 ms |
5.41% |
〰️ |
categories tree 50k subCategories search > get search paths |
1819 ms |
1927 ms |
-5.60% |
〰️ |
categories tree 50k subCategories search > get search paths (P95 of main thread blocks) |
41 ms |
45 ms |
-8.89% |
〰️ |
categories tree 50k subCategories search > load hierarchy from search paths |
5405 ms |
5688 ms |
-4.98% |
〰️ |
categories tree 50k subCategories search > load hierarchy from search paths (P95 of main thread blocks) |
50 ms |
52 ms |
-3.85% |
〰️ |
categories tree 50k subCategories > collect nodes |
5910 ms |
6134 ms |
-3.65% |
〰️ |
categories tree 50k subCategories > collect nodes (P95 of main thread blocks) |
46 ms |
48 ms |
-4.17% |
〰️ |
categories tree 50k subCategories > validate initial visibility |
1670 ms |
1761 ms |
-5.17% |
〰️ |
categories tree 50k subCategories > validate initial visibility (P95 of main thread blocks) |
21 ms |
21 ms |
0% |
🟰 |
categories tree 50k subCategories > change visibility |
333 ms |
354 ms |
-5.93% |
〰️ |
categories tree 50k subCategories > change visibility (P95 of main thread blocks) |
0 ms |
23 ms |
-100% |
〰️ |
categories tree 50k subCategories > validate changed visibility |
1605 ms |
1659 ms |
-3.25% |
〰️ |
categories tree 50k subCategories > validate changed visibility (P95 of main thread blocks) |
26 ms |
28 ms |
-7.14% |
〰️ |
categories tree 50k categories > collect nodes |
2452 ms |
2664 ms |
-7.96% |
〰️ |
categories tree 50k categories > collect nodes (P95 of main thread blocks) |
123 ms |
131 ms |
-6.11% |
〰️ |
categories tree 50k categories > validate initial visibility |
7233 ms |
7557 ms |
-4.29% |
〰️ |
categories tree 50k categories > validate initial visibility (P95 of main thread blocks) |
86 ms |
95 ms |
-9.47% |
〰️ |
categories tree 50k categories > change visibility |
665 ms |
741 ms |
-10.26% |
✅ |
categories tree 50k categories > change visibility (P95 of main thread blocks) |
53 ms |
61 ms |
-13.11% |
〰️ |
categories tree 50k categories > validate changed visibility |
7226 ms |
7545 ms |
-4.23% |
〰️ |
categories tree 50k categories > validate changed visibility (P95 of main thread blocks) |
32 ms |
41 ms |
-21.95% |
〰️ |
classifications tree 50k classifications search > get search paths |
2177 ms |
2270 ms |
-4.10% |
〰️ |
classifications tree 50k classifications search > get search paths (P95 of main thread blocks) |
124 ms |
141 ms |
-12.06% |
〰️ |
classifications tree 50k classifications search > load hierarchy from search paths |
67249 ms |
71660 ms |
-6.16% |
〰️ |
classifications tree 50k classifications search > load hierarchy from search paths (P95 of main thread blocks) |
0 ms |
25 ms |
-100% |
〰️ |
classifications tree 50k classifications > collect nodes |
36486 ms |
38416 ms |
-5.02% |
〰️ |
classifications tree 50k classifications > collect nodes (P95 of main thread blocks) |
88 ms |
91 ms |
-3.30% |
〰️ |
classifications tree 50k classifications > validate initial visibility |
4807 ms |
4927 ms |
-2.44% |
〰️ |
classifications tree 50k classifications > validate initial visibility (P95 of main thread blocks) |
67 ms |
59 ms |
13.56% |
〰️ |
classifications tree 50k classifications > change visibility |
30 ms |
30 ms |
0% |
🟰 |
classifications tree 50k classifications > change visibility (P95 of main thread blocks) |
0 ms |
0 ms |
0% |
🟰 |
classifications tree 50k classifications > validate changed visibility |
4890 ms |
5207 ms |
-6.09% |
〰️ |
classifications tree 50k classifications > validate changed visibility (P95 of main thread blocks) |
94 ms |
73 ms |
28.77% |
〰️ |
This comment was automatically generated by workflow using github-action-benchmark.
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 1 out of 1 changed files in this pull request and generated 2 comments.
Comments suppressed due to low confidence (1)
packages/itwin/tree-widget/child-categories-plan.md:387
- This section hard-codes a specific line number in
SearchResultsTree.ts(“line 277”), but that file is likely to shift as changes land (and it’s already not stable across branches). Consider referencing the relevant code snippet/behavior instead of an exact line number to keep the plan accurate over time.
**What:**
- Currently: child element nodes inherit `categoryId` from parent element (line 277: `categoryId: parent.categoryId`)
- With intermediate category nodes in the tree, the search/filter path will naturally include the correct category node
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 1 out of 1 changed files in this pull request and generated 2 comments.
Comments suppressed due to low confidence (1)
packages/itwin/tree-widget/child-categories-plan.md:450
- Phase 5 says the CTE change requires adding a
topCategoryIdcolumn and usinge.topCategoryIdin recursion, but earlier in the AlwaysAndNeverDrawnElementInfoCache section the proposed CTE explicitly avoidstopCategoryId/rootCategoryIdcolumns (overwriting a singlecategoryIdinstead). Please update Phase 5 to match the chosen CTE design to avoid implementers following the wrong SQL recipe.
- Insert intermediate category nodes into the cache tree for **every** element (not just when category differs from parent). The cache tree always alternates element→category→element→category, so navigation is uniform.
- Unify request interface with base + category/element split (same pattern as other caches)
- Add `elementCategoryPath` to category requests (for intermediate categories under elements). This path alternates element→category→element→... and includes categories even when same as parent.
- Change return type from `Set<ElementId>` to `Map<CategoryId, Set<ElementId>>` — group by element's own category
- The existing CTE query is modified: add a `topCategoryId` column and use `e.topCategoryId` (the child's category) in the recursive step to interleave each element's own `Category.Id` into the `elementsPath` string (see Architecture section for full CTE and trace)
- Caller filters the returned map as needed
- Note: `getParentElementsIdsPath` no longer needs to filter out category IDs — the `elementCategoryPath` naturally includes them
In preparation for #1100, #1561, #1563. I'd like to agree on the plan before proceeding.