@itwin/presentation-hierarchies 2.0.0-alpha.5 → 2.0.0-alpha.6

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.
Files changed (82) hide show
  1. package/CHANGELOG.md +36 -0
  2. package/lib/cjs/hierarchies/HierarchyFiltering.d.ts +77 -36
  3. package/lib/cjs/hierarchies/HierarchyFiltering.d.ts.map +1 -1
  4. package/lib/cjs/hierarchies/HierarchyFiltering.js +126 -98
  5. package/lib/cjs/hierarchies/HierarchyFiltering.js.map +1 -1
  6. package/lib/cjs/hierarchies/HierarchyMerge.d.ts +19 -0
  7. package/lib/cjs/hierarchies/HierarchyMerge.d.ts.map +1 -0
  8. package/lib/cjs/hierarchies/HierarchyMerge.js +43 -0
  9. package/lib/cjs/hierarchies/HierarchyMerge.js.map +1 -0
  10. package/lib/cjs/hierarchies/HierarchyNode.d.ts +2 -2
  11. package/lib/cjs/hierarchies/HierarchyNode.d.ts.map +1 -1
  12. package/lib/cjs/hierarchies/HierarchyNode.js.map +1 -1
  13. package/lib/cjs/hierarchies/HierarchyNodeKey.d.ts.map +1 -1
  14. package/lib/cjs/hierarchies/HierarchyNodeKey.js +8 -8
  15. package/lib/cjs/hierarchies/HierarchyNodeKey.js.map +1 -1
  16. package/lib/cjs/hierarchies/HierarchyProvider.d.ts +0 -18
  17. package/lib/cjs/hierarchies/HierarchyProvider.d.ts.map +1 -1
  18. package/lib/cjs/hierarchies/HierarchyProvider.js +0 -37
  19. package/lib/cjs/hierarchies/HierarchyProvider.js.map +1 -1
  20. package/lib/cjs/hierarchies/imodel/FilteringHierarchyDefinition.d.ts +2 -0
  21. package/lib/cjs/hierarchies/imodel/FilteringHierarchyDefinition.d.ts.map +1 -1
  22. package/lib/cjs/hierarchies/imodel/FilteringHierarchyDefinition.js +45 -30
  23. package/lib/cjs/hierarchies/imodel/FilteringHierarchyDefinition.js.map +1 -1
  24. package/lib/cjs/hierarchies/imodel/IModelHierarchyProvider.d.ts +42 -7
  25. package/lib/cjs/hierarchies/imodel/IModelHierarchyProvider.d.ts.map +1 -1
  26. package/lib/cjs/hierarchies/imodel/IModelHierarchyProvider.js +136 -45
  27. package/lib/cjs/hierarchies/imodel/IModelHierarchyProvider.js.map +1 -1
  28. package/lib/cjs/hierarchies/imodel/TreeNodesReader.d.ts.map +1 -1
  29. package/lib/cjs/hierarchies/imodel/TreeNodesReader.js +8 -1
  30. package/lib/cjs/hierarchies/imodel/TreeNodesReader.js.map +1 -1
  31. package/lib/cjs/hierarchies/internal/Common.d.ts +3 -0
  32. package/lib/cjs/hierarchies/internal/Common.d.ts.map +1 -1
  33. package/lib/cjs/hierarchies/internal/Common.js +12 -1
  34. package/lib/cjs/hierarchies/internal/Common.js.map +1 -1
  35. package/lib/cjs/hierarchies/internal/RxjsHierarchyDefinition.d.ts.map +1 -1
  36. package/lib/cjs/hierarchies/internal/RxjsHierarchyDefinition.js +2 -1
  37. package/lib/cjs/hierarchies/internal/RxjsHierarchyDefinition.js.map +1 -1
  38. package/lib/cjs/presentation-hierarchies.d.ts +3 -2
  39. package/lib/cjs/presentation-hierarchies.d.ts.map +1 -1
  40. package/lib/cjs/presentation-hierarchies.js +4 -3
  41. package/lib/cjs/presentation-hierarchies.js.map +1 -1
  42. package/lib/esm/hierarchies/HierarchyFiltering.d.ts +77 -36
  43. package/lib/esm/hierarchies/HierarchyFiltering.d.ts.map +1 -1
  44. package/lib/esm/hierarchies/HierarchyFiltering.js +125 -98
  45. package/lib/esm/hierarchies/HierarchyFiltering.js.map +1 -1
  46. package/lib/esm/hierarchies/HierarchyMerge.d.ts +19 -0
  47. package/lib/esm/hierarchies/HierarchyMerge.d.ts.map +1 -0
  48. package/lib/esm/hierarchies/HierarchyMerge.js +40 -0
  49. package/lib/esm/hierarchies/HierarchyMerge.js.map +1 -0
  50. package/lib/esm/hierarchies/HierarchyNode.d.ts +2 -2
  51. package/lib/esm/hierarchies/HierarchyNode.d.ts.map +1 -1
  52. package/lib/esm/hierarchies/HierarchyNode.js.map +1 -1
  53. package/lib/esm/hierarchies/HierarchyNodeKey.d.ts.map +1 -1
  54. package/lib/esm/hierarchies/HierarchyNodeKey.js +9 -9
  55. package/lib/esm/hierarchies/HierarchyNodeKey.js.map +1 -1
  56. package/lib/esm/hierarchies/HierarchyProvider.d.ts +0 -18
  57. package/lib/esm/hierarchies/HierarchyProvider.d.ts.map +1 -1
  58. package/lib/esm/hierarchies/HierarchyProvider.js +1 -36
  59. package/lib/esm/hierarchies/HierarchyProvider.js.map +1 -1
  60. package/lib/esm/hierarchies/imodel/FilteringHierarchyDefinition.d.ts +2 -0
  61. package/lib/esm/hierarchies/imodel/FilteringHierarchyDefinition.d.ts.map +1 -1
  62. package/lib/esm/hierarchies/imodel/FilteringHierarchyDefinition.js +47 -32
  63. package/lib/esm/hierarchies/imodel/FilteringHierarchyDefinition.js.map +1 -1
  64. package/lib/esm/hierarchies/imodel/IModelHierarchyProvider.d.ts +42 -7
  65. package/lib/esm/hierarchies/imodel/IModelHierarchyProvider.d.ts.map +1 -1
  66. package/lib/esm/hierarchies/imodel/IModelHierarchyProvider.js +138 -48
  67. package/lib/esm/hierarchies/imodel/IModelHierarchyProvider.js.map +1 -1
  68. package/lib/esm/hierarchies/imodel/TreeNodesReader.d.ts.map +1 -1
  69. package/lib/esm/hierarchies/imodel/TreeNodesReader.js +8 -1
  70. package/lib/esm/hierarchies/imodel/TreeNodesReader.js.map +1 -1
  71. package/lib/esm/hierarchies/internal/Common.d.ts +3 -0
  72. package/lib/esm/hierarchies/internal/Common.d.ts.map +1 -1
  73. package/lib/esm/hierarchies/internal/Common.js +11 -1
  74. package/lib/esm/hierarchies/internal/Common.js.map +1 -1
  75. package/lib/esm/hierarchies/internal/RxjsHierarchyDefinition.d.ts.map +1 -1
  76. package/lib/esm/hierarchies/internal/RxjsHierarchyDefinition.js +3 -2
  77. package/lib/esm/hierarchies/internal/RxjsHierarchyDefinition.js.map +1 -1
  78. package/lib/esm/presentation-hierarchies.d.ts +3 -2
  79. package/lib/esm/presentation-hierarchies.d.ts.map +1 -1
  80. package/lib/esm/presentation-hierarchies.js +2 -2
  81. package/lib/esm/presentation-hierarchies.js.map +1 -1
  82. package/package.json +7 -7
@@ -37,7 +37,10 @@ interface IModelHierarchyProviderProps {
37
37
  * @see `ECClassHierarchyInspector`
38
38
  */
39
39
  imodelAccess: IModelAccess;
40
- /** An event that is raised when iModel data, that may affect the hierarchy, changes. */
40
+ /**
41
+ * An event that is raised when iModel data, that may affect the hierarchy, changes. The created provider
42
+ * subscribes to the event to know that it needs to reload the data, and unsubscribes on disposal.
43
+ */
41
44
  imodelChanged?: Event<() => void>;
42
45
  /**
43
46
  * A function that returns a hierarchy definition, describing how the hierarchy that the provider should be create. The
@@ -66,15 +69,47 @@ interface IModelHierarchyProviderProps {
66
69
  };
67
70
  }
68
71
  /**
69
- * Creates an instance of `HierarchyProvider` that creates a hierarchy based on given iModel and
72
+ * Creates an instance of `HierarchyProvider` that creates a hierarchy based on given the iModel and
70
73
  * a hierarchy definition, which defines each hierarchy level through ECSQL queries.
71
74
  *
72
75
  * @public
73
76
  */
74
- export declare function createIModelHierarchyProvider(props: IModelHierarchyProviderProps): HierarchyProvider & {
75
- /** @deprecated in 1.4. Use `[Symbol.dispose]` instead. */
76
- dispose: () => void;
77
- [Symbol.dispose]: () => void;
78
- };
77
+ export declare function createIModelHierarchyProvider(props: IModelHierarchyProviderProps): HierarchyProvider & Disposable;
78
+ /**
79
+ * Props for `createMergedIModelHierarchyProvider`.
80
+ * @alpha
81
+ */
82
+ interface MergedIModelHierarchyProviderProps extends Omit<IModelHierarchyProviderProps, "imodelAccess" | "imodelChanged"> {
83
+ /**
84
+ * A list of iModels to create merged hierarchy for.
85
+ *
86
+ * **Warning:** These **must** all be different versions of the same iModel, ordered from the earliest to the
87
+ * latest version. Not obeying this rule may result in undefined behavior.
88
+ */
89
+ imodels: Array<{
90
+ /**
91
+ * An object that provides access to iModel's data and metadata.
92
+ *
93
+ * @see `ECSchemaProvider`
94
+ * @see `LimitingECSqlQueryExecutor`
95
+ * @see `ECClassHierarchyInspector`
96
+ */
97
+ imodelAccess: IModelAccess;
98
+ /**
99
+ * An event that is raised when iModel data, that may affect the hierarchy, changes. The created provider
100
+ * subscribes to the event to know that it needs to reload the data, and unsubscribes on disposal.
101
+ */
102
+ imodelChanged?: Event<() => void>;
103
+ }>;
104
+ }
105
+ /**
106
+ * Creates an instance of `HierarchyProvider` that creates a hierarchy based on multiple version of **the same** iModel
107
+ * and a hierarchy definition, which defines each hierarchy level through ECSQL queries.
108
+ *
109
+ * **Warning:** This is an experimental API that **should not be used in production**.
110
+ *
111
+ * @alpha
112
+ */
113
+ export declare function createMergedIModelHierarchyProvider(props: MergedIModelHierarchyProviderProps): HierarchyProvider & Disposable;
79
114
  export {};
80
115
  //# sourceMappingURL=IModelHierarchyProvider.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"IModelHierarchyProvider.d.ts","sourceRoot":"","sources":["../../../../src/hierarchies/imodel/IModelHierarchyProvider.ts"],"names":[],"mappings":"AAKA,OAAO,gCAAgC,CAAC;AAwBxC,OAAO,EAGL,yBAAyB,EACzB,gBAAgB,EAGhB,KAAK,EAIL,wBAAwB,EAEzB,MAAM,4BAA4B,CAAC;AAEpC,OAAO,EAAE,sBAAsB,EAAE,MAAM,0BAA0B,CAAC;AAGlE,OAAO,EAA0B,iBAAiB,EAAE,MAAM,yBAAyB,CAAC;AAkBpF,OAAO,EAA6B,mBAAmB,EAA4B,MAAM,gCAAgC,CAAC;AAE1H,OAAO,EAAE,0BAA0B,EAAE,MAAM,iCAAiC,CAAC;AAe7E;;;GAGG;AACH,UAAU,uCAAuC;IAC/C;;;OAGG;IACH,WAAW,EAAE,MAAM,CAAC;IAEpB;;;OAGG;IACH,KAAK,EAAE,MAAM,CAAC;CACf;AAED,cAAc;AACd,KAAK,YAAY,GAAG,gBAAgB,GAAG,0BAA0B,GAAG,yBAAyB,GAAG;IAAE,SAAS,EAAE,MAAM,CAAA;CAAE,CAAC;AAEtH;;;GAGG;AACH,UAAU,4BAA4B;IACpC;;;;;;OAMG;IACH,YAAY,EAAE,YAAY,CAAC;IAE3B,wFAAwF;IACxF,aAAa,CAAC,EAAE,KAAK,CAAC,MAAM,IAAI,CAAC,CAAC;IAElC;;;OAGG;IACH,mBAAmB,EAAE,mBAAmB,CAAC;IAEzC,qGAAqG;IACrG,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B;;;OAGG;IACH,cAAc,CAAC,EAAE,MAAM,CAAC;IAExB,sEAAsE;IACtE,gBAAgB,CAAC,EAAE,OAAO,CAAC,uCAAuC,CAAC,CAAC;IAEpE;;;;OAIG;IACH,SAAS,CAAC,EAAE,wBAAwB,CAAC;IACrC,yCAAyC;IACzC,SAAS,CAAC,EAAE;QACV,2DAA2D;QAC3D,KAAK,EAAE,sBAAsB,EAAE,CAAC;KACjC,CAAC;CACH;AAED;;;;;GAKG;AACH,wBAAgB,6BAA6B,CAAC,KAAK,EAAE,4BAA4B,GAAG,iBAAiB,GAAG;IACtG,0DAA0D;IAC1D,OAAO,EAAE,MAAM,IAAI,CAAC;IACpB,CAAC,MAAM,CAAC,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;CAC9B,CAEA"}
1
+ {"version":3,"file":"IModelHierarchyProvider.d.ts","sourceRoot":"","sources":["../../../../src/hierarchies/imodel/IModelHierarchyProvider.ts"],"names":[],"mappings":"AAKA,OAAO,gCAAgC,CAAC;AA4BxC,OAAO,EAGL,yBAAyB,EACzB,gBAAgB,EAGhB,KAAK,EAIL,wBAAwB,EAEzB,MAAM,4BAA4B,CAAC;AAEpC,OAAO,EAAE,sBAAsB,EAAE,MAAM,0BAA0B,CAAC;AAGlE,OAAO,EAA0B,iBAAiB,EAAE,MAAM,yBAAyB,CAAC;AAkBpF,OAAO,EAGL,mBAAmB,EAGpB,MAAM,gCAAgC,CAAC;AAExC,OAAO,EAAE,0BAA0B,EAAE,MAAM,iCAAiC,CAAC;AAe7E;;;GAGG;AACH,UAAU,uCAAuC;IAC/C;;;OAGG;IACH,WAAW,EAAE,MAAM,CAAC;IAEpB;;;OAGG;IACH,KAAK,EAAE,MAAM,CAAC;CACf;AAED,cAAc;AACd,KAAK,YAAY,GAAG,gBAAgB,GAAG,0BAA0B,GAAG,yBAAyB,GAAG;IAAE,SAAS,EAAE,MAAM,CAAA;CAAE,CAAC;AAEtH;;;GAGG;AACH,UAAU,4BAA4B;IACpC;;;;;;OAMG;IACH,YAAY,EAAE,YAAY,CAAC;IAE3B;;;OAGG;IACH,aAAa,CAAC,EAAE,KAAK,CAAC,MAAM,IAAI,CAAC,CAAC;IAElC;;;OAGG;IACH,mBAAmB,EAAE,mBAAmB,CAAC;IAEzC,qGAAqG;IACrG,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAE1B;;;OAGG;IACH,cAAc,CAAC,EAAE,MAAM,CAAC;IAExB,sEAAsE;IACtE,gBAAgB,CAAC,EAAE,OAAO,CAAC,uCAAuC,CAAC,CAAC;IAEpE;;;;OAIG;IACH,SAAS,CAAC,EAAE,wBAAwB,CAAC;IAErC,yCAAyC;IACzC,SAAS,CAAC,EAAE;QACV,2DAA2D;QAC3D,KAAK,EAAE,sBAAsB,EAAE,CAAC;KACjC,CAAC;CACH;AAED;;;;;GAKG;AACH,wBAAgB,6BAA6B,CAAC,KAAK,EAAE,4BAA4B,GAAG,iBAAiB,GAAG,UAAU,CAGjH;AAED;;;GAGG;AACH,UAAU,kCAAmC,SAAQ,IAAI,CAAC,4BAA4B,EAAE,cAAc,GAAG,eAAe,CAAC;IACvH;;;;;OAKG;IACH,OAAO,EAAE,KAAK,CAAC;QACb;;;;;;WAMG;QACH,YAAY,EAAE,YAAY,CAAC;QAE3B;;;WAGG;QACH,aAAa,CAAC,EAAE,KAAK,CAAC,MAAM,IAAI,CAAC,CAAC;KACnC,CAAC,CAAC;CACJ;AAED;;;;;;;GAOG;AACH,wBAAgB,mCAAmC,CAAC,KAAK,EAAE,kCAAkC,GAAG,iBAAiB,GAAG,UAAU,CAE7H"}
@@ -3,9 +3,9 @@
3
3
  * See LICENSE.md in the project root for license terms and full copyright notice.
4
4
  *--------------------------------------------------------------------------------------------*/
5
5
  import "../internal/DisposePolyfill.js";
6
- import { catchError, concat, defaultIfEmpty, EMPTY, filter, finalize, from, identity, map, merge, mergeAll, mergeMap, of, Subject, take, takeUntil, tap, } from "rxjs";
6
+ import { catchError, concat, defaultIfEmpty, defer, EMPTY, filter, finalize, from, fromEventPattern, identity, map, merge, mergeAll, mergeMap, of, reduce, Subject, take, takeUntil, tap, } from "rxjs";
7
7
  import { assert, BeEvent, Guid, StopWatch } from "@itwin/core-bentley";
8
- import { createDefaultValueFormatter, formatConcatenatedValue, normalizeFullClassName, } from "@itwin/presentation-shared";
8
+ import { createDefaultValueFormatter, formatConcatenatedValue, InstanceKey, normalizeFullClassName, } from "@itwin/presentation-shared";
9
9
  import { RowsLimitExceededError } from "../HierarchyErrors.js";
10
10
  import { HierarchyNode } from "../HierarchyNode.js";
11
11
  import { HierarchyNodeKey } from "../HierarchyNodeKey.js";
@@ -20,7 +20,7 @@ import { getRxjsHierarchyDefinition } from "../internal/RxjsHierarchyDefinition.
20
20
  import { SubscriptionScheduler } from "../internal/SubscriptionScheduler.js";
21
21
  import { FilteringHierarchyDefinition } from "./FilteringHierarchyDefinition.js";
22
22
  import { HierarchyCache } from "./HierarchyCache.js";
23
- import { HierarchyNodesDefinition } from "./IModelHierarchyDefinition.js";
23
+ import { HierarchyNodesDefinition, } from "./IModelHierarchyDefinition.js";
24
24
  import { NodeSelectClauseColumnNames } from "./NodeSelectQueryFactory.js";
25
25
  import { createDetermineChildrenOperator } from "./operators/DetermineChildren.js";
26
26
  import { createGroupingOperator } from "./operators/Grouping.js";
@@ -33,36 +33,56 @@ const LOGGING_NAMESPACE_PERFORMANCE = `${BASE_LOGGING_NAMESPACE_PERFORMANCE}.IMo
33
33
  const DEFAULT_QUERY_CONCURRENCY = 10;
34
34
  const DEFAULT_QUERY_CACHE_SIZE = 1;
35
35
  /**
36
- * Creates an instance of `HierarchyProvider` that creates a hierarchy based on given iModel and
36
+ * Creates an instance of `HierarchyProvider` that creates a hierarchy based on given the iModel and
37
37
  * a hierarchy definition, which defines each hierarchy level through ECSQL queries.
38
38
  *
39
39
  * @public
40
40
  */
41
41
  export function createIModelHierarchyProvider(props) {
42
+ const { imodelAccess, imodelChanged, ...restProps } = props;
43
+ return createMergedIModelHierarchyProvider({ ...restProps, imodels: [{ imodelAccess, imodelChanged }] });
44
+ }
45
+ /**
46
+ * Creates an instance of `HierarchyProvider` that creates a hierarchy based on multiple version of **the same** iModel
47
+ * and a hierarchy definition, which defines each hierarchy level through ECSQL queries.
48
+ *
49
+ * **Warning:** This is an experimental API that **should not be used in production**.
50
+ *
51
+ * @alpha
52
+ */
53
+ export function createMergedIModelHierarchyProvider(props) {
42
54
  return new IModelHierarchyProviderImpl(props);
43
55
  }
44
56
  class IModelHierarchyProviderImpl {
45
- _imodelAccess;
57
+ _imodels;
46
58
  _hierarchyChanged;
47
59
  _valuesFormatter;
48
60
  _sourceHierarchyDefinition;
49
61
  _activeHierarchyDefinition;
50
62
  _localizedStrings;
51
- _queryScheduler;
63
+ _queryConcurrency;
64
+ _querySchedulers;
52
65
  _nodesCache;
53
66
  _unsubscribe;
54
67
  _dispose = new Subject();
55
68
  #componentId;
56
69
  #componentName;
70
+ #sourceName;
57
71
  constructor(props) {
72
+ if (props.imodels.length === 0) {
73
+ throw new Error(`Creating an iModel hierarchy provider requires at least one iModel.`);
74
+ }
58
75
  this.#componentId = Guid.createValue();
59
76
  this.#componentName = "IModelHierarchyProviderImpl";
60
- this._imodelAccess = props.imodelAccess;
77
+ /* c8 ignore next */
78
+ this.#sourceName = props.sourceName ?? `${this.#componentName}:${this.#componentId}`;
79
+ this._imodels = props.imodels;
61
80
  this._hierarchyChanged = new BeEvent();
62
81
  this._activeHierarchyDefinition = this._sourceHierarchyDefinition = getRxjsHierarchyDefinition(props.hierarchyDefinition);
63
82
  this._valuesFormatter = props?.formatter ?? createDefaultValueFormatter();
64
83
  this._localizedStrings = { other: "Other", unspecified: "Not specified", ...props?.localizedStrings };
65
- this._queryScheduler = new SubscriptionScheduler(props.queryConcurrency ?? DEFAULT_QUERY_CONCURRENCY);
84
+ this._queryConcurrency = props.queryConcurrency ?? DEFAULT_QUERY_CONCURRENCY;
85
+ this._querySchedulers = new Map();
66
86
  this.setHierarchyFilter(props.filtering);
67
87
  const queryCacheSize = props.queryCacheSize ?? DEFAULT_QUERY_CACHE_SIZE;
68
88
  if (queryCacheSize !== 0) {
@@ -72,11 +92,18 @@ class IModelHierarchyProviderImpl {
72
92
  variationsCount: 1,
73
93
  });
74
94
  }
75
- this._unsubscribe = props.imodelChanged?.addListener(() => {
76
- this.invalidateHierarchyCache("Data source changed");
95
+ const imodelChangeSubscription = from(this._imodels)
96
+ .pipe(mergeMap(({ imodelAccess, imodelChanged }) => imodelChanged
97
+ ? fromEventPattern((handler) => imodelChanged.addListener(handler), (handler) => imodelChanged.removeListener(handler), () => imodelAccess)
98
+ : EMPTY))
99
+ .subscribe(({ imodelKey }) => {
100
+ this.invalidateHierarchyCache(`Data source changed: "${imodelKey}"`);
77
101
  this._dispose.next();
78
102
  this._hierarchyChanged.raiseEvent();
79
103
  });
104
+ this._unsubscribe = () => {
105
+ imodelChangeSubscription.unsubscribe();
106
+ };
80
107
  }
81
108
  [Symbol.dispose]() {
82
109
  this._dispose.next();
@@ -89,6 +116,17 @@ class IModelHierarchyProviderImpl {
89
116
  get hierarchyChanged() {
90
117
  return this._hierarchyChanged;
91
118
  }
119
+ getQueryScheduler(imodelKey) {
120
+ let scheduler = this._querySchedulers.get(imodelKey);
121
+ if (!scheduler) {
122
+ scheduler = new SubscriptionScheduler(this._queryConcurrency);
123
+ this._querySchedulers.set(imodelKey, scheduler);
124
+ }
125
+ return scheduler;
126
+ }
127
+ getPrimaryIModelAccess() {
128
+ return this._imodels[this._imodels.length - 1].imodelAccess;
129
+ }
92
130
  invalidateHierarchyCache(reason) {
93
131
  doLog({
94
132
  category: `${LOGGING_NAMESPACE}.Events`,
@@ -114,8 +152,9 @@ class IModelHierarchyProviderImpl {
114
152
  return;
115
153
  }
116
154
  this._activeHierarchyDefinition = new FilteringHierarchyDefinition({
117
- imodelAccess: this._imodelAccess,
155
+ imodelAccess: this.getPrimaryIModelAccess(),
118
156
  source: this._sourceHierarchyDefinition,
157
+ sourceName: this.#sourceName,
119
158
  nodeIdentifierPaths: props.paths,
120
159
  });
121
160
  this.invalidateHierarchyCache("Hierarchy filter set");
@@ -131,44 +170,51 @@ class IModelHierarchyProviderImpl {
131
170
  category: LOGGING_NAMESPACE_PERFORMANCE,
132
171
  message: /* c8 ignore next */ () => `[${requestContext.requestId}] Requesting hierarchy level definitions for ${createNodeIdentifierForLogging(props.parentNode)}`,
133
172
  });
134
- let parentNode = props.parentNode;
135
- if (parentNode && HierarchyNode.isGeneric(parentNode) && parentNode.key.source && parentNode.key.source !== this._imodelAccess.imodelKey) {
136
- return EMPTY;
137
- }
138
- if (parentNode && HierarchyNode.isInstancesNode(parentNode)) {
139
- parentNode = {
140
- ...parentNode,
141
- key: {
142
- ...parentNode.key,
143
- instanceKeys: parentNode.key.instanceKeys.filter((ik) => !ik.imodelKey || ik.imodelKey === this._imodelAccess.imodelKey),
144
- },
145
- };
146
- assert(HierarchyNodeKey.isInstances(parentNode.key));
147
- if (parentNode.key.instanceKeys.length === 0) {
173
+ return from(this._imodels).pipe(mergeMap(({ imodelAccess }, imodelAccessIndex) => {
174
+ let parentNode = props.parentNode;
175
+ if (parentNode && HierarchyNode.isGeneric(parentNode) && parentNode.key.source && parentNode.key.source !== this.#sourceName) {
148
176
  return EMPTY;
149
177
  }
150
- }
151
- return this._activeHierarchyDefinition.defineHierarchyLevel({ ...defineHierarchyLevelProps, parentNode }).pipe(mergeAll(), finalize(() => doLog({
178
+ if (parentNode && HierarchyNode.isInstancesNode(parentNode)) {
179
+ parentNode = {
180
+ ...parentNode,
181
+ key: {
182
+ ...parentNode.key,
183
+ instanceKeys: parentNode.key.instanceKeys.filter((ik) => !ik.imodelKey || ik.imodelKey === imodelAccess.imodelKey),
184
+ },
185
+ };
186
+ assert(HierarchyNodeKey.isInstances(parentNode.key));
187
+ if (parentNode.key.instanceKeys.length === 0) {
188
+ return EMPTY;
189
+ }
190
+ }
191
+ return this._activeHierarchyDefinition.defineHierarchyLevel({ ...defineHierarchyLevelProps, parentNode }).pipe(mergeAll(), map(
192
+ /* Using `as` to work around TS not understanding that `{ x: A, y: B | C }` is the same as `{ x: A } & ({ y: B } | { y: C })` */
193
+ (hierarchyNodesDefinition) => ({ imodelAccess, imodelAccessIndex, hierarchyNodesDefinition })));
194
+ }), finalize(() => doLog({
152
195
  category: LOGGING_NAMESPACE_PERFORMANCE,
153
196
  message: /* c8 ignore next */ () => `[${requestContext.requestId}] Received all hierarchy level definitions for ${createNodeIdentifierForLogging(props.parentNode)}`,
154
197
  })));
155
198
  }
156
199
  createSourceNodesObservable(props) {
157
200
  // pipe definitions to nodes and put "share replay" on it
158
- return this.createHierarchyLevelDefinitionsObservable(props).pipe(mergeMap((def) => {
201
+ return this.createHierarchyLevelDefinitionsObservable(props).pipe(mergeMap(({ imodelAccess, imodelAccessIndex, hierarchyNodesDefinition: def }) => defer(() => {
159
202
  if (HierarchyNodesDefinition.isGenericNode(def)) {
160
- return of({ ...def.node, key: { type: "generic", id: def.node.key, source: this._imodelAccess.imodelKey } });
203
+ return of({
204
+ ...def.node,
205
+ key: { type: "generic", id: def.node.key, source: this.#sourceName },
206
+ });
161
207
  }
162
- return this._queryScheduler.scheduleSubscription(of(def.query).pipe(map((query) => filterQueryByInstanceKeys(query, props.filteredInstanceKeys)), mergeMap((query) => readNodes({
163
- queryExecutor: this._imodelAccess,
208
+ return this.getQueryScheduler(imodelAccess.imodelKey).scheduleSubscription(of(def.query).pipe(map((query) => filterQueryByInstanceKeys(query, props.filteredInstanceKeys)), mergeMap((query) => readNodes({
209
+ queryExecutor: imodelAccess,
164
210
  query,
165
211
  limit: props.hierarchyLevelSizeLimit,
166
212
  parser: this._activeHierarchyDefinition.parseNode ? (row) => this._activeHierarchyDefinition.parseNode(row, props.parentNode) : undefined,
167
213
  })), map((node) => ({
168
214
  ...node,
169
- key: { ...node.key, instanceKeys: node.key.instanceKeys.map((key) => ({ ...key, imodelKey: this._imodelAccess.imodelKey })) },
215
+ key: { ...node.key, instanceKeys: node.key.instanceKeys.map((key) => ({ ...key, imodelKey: imodelAccess.imodelKey })) },
170
216
  }))));
171
- }), finalize(() => doLog({
217
+ }).pipe(map((node) => ({ imodelAccess, imodelAccessIndex, node })))), mergeNodes, map(({ node }) => node), finalize(() => doLog({
172
218
  category: LOGGING_NAMESPACE_PERFORMANCE,
173
219
  message: /* c8 ignore next */ () => `[${props.requestContext.requestId}] Read all child nodes ${createNodeIdentifierForLogging(props.parentNode)}`,
174
220
  })), shareReplayWithErrors());
@@ -182,6 +228,7 @@ class IModelHierarchyProviderImpl {
182
228
  // format `ConcatenatedValue` labels into string labels
183
229
  mergeMap((node) => applyLabelsFormatting(node, this._valuesFormatter)),
184
230
  // we have `ProcessedHierarchyNode` from here
231
+ // let consumers step-in
185
232
  preProcessNodes(this._activeHierarchyDefinition),
186
233
  // process hiding
187
234
  createHideIfNoChildrenOperator((n) => this.getChildNodesObservables({ parentNode: n, requestContext: props.requestContext }).hasNodes), createHideNodesInHierarchyOperator(
@@ -192,7 +239,7 @@ class IModelHierarchyProviderImpl {
192
239
  })));
193
240
  }
194
241
  createProcessedNodesObservable(preprocessedNodesObservable, props) {
195
- return preprocessedNodesObservable.pipe(createGroupingOperator(this._imodelAccess, props.parentNode, this._valuesFormatter, this._localizedStrings, undefined, (gn) => this.onGroupingNodeCreated(gn, props)), finalize(() => doLog({
242
+ return preprocessedNodesObservable.pipe(createGroupingOperator(this.getPrimaryIModelAccess(), props.parentNode, this._valuesFormatter, this._localizedStrings, undefined, (gn) => this.onGroupingNodeCreated(gn, props)), finalize(() => doLog({
196
243
  category: LOGGING_NAMESPACE_PERFORMANCE,
197
244
  message: /* c8 ignore next */ () => `[${props.requestContext.requestId}] Finished grouping child nodes for ${createNodeIdentifierForLogging(props.parentNode)}`,
198
245
  })));
@@ -202,9 +249,7 @@ class IModelHierarchyProviderImpl {
202
249
  if ("processingParams" in n) {
203
250
  delete n.processingParams;
204
251
  }
205
- return Object.assign(n, {
206
- children: hasChildren(n),
207
- });
252
+ return { ...n, children: hasChildren(n) };
208
253
  }), takeUntil(this._dispose), finalize(() => doLog({
209
254
  category: LOGGING_NAMESPACE_PERFORMANCE,
210
255
  message: /* c8 ignore next */ () => `[${props.requestContext.requestId}] Finished finalizing child nodes for ${createNodeIdentifierForLogging(props.parentNode)}`,
@@ -319,11 +364,11 @@ class IModelHierarchyProviderImpl {
319
364
  return from(parentNode.groupedInstanceKeys);
320
365
  }
321
366
  assert(!parentNode || HierarchyNode.isGeneric(parentNode) || HierarchyNode.isInstancesNode(parentNode));
322
- const hierarchyLevelDefinitions = this.createHierarchyLevelDefinitionsObservable({ parentNode, instanceFilter, requestContext });
323
367
  // split the definitions based on whether they're for generic nodes or for instance nodes
324
- const [genericDefs, instanceDefs] = partition(hierarchyLevelDefinitions, HierarchyNodesDefinition.isGenericNode);
368
+ const hierarchyLevelDefinitions = this.createHierarchyLevelDefinitionsObservable({ parentNode, instanceFilter, requestContext });
369
+ const [instanceDefs, genericDefs] = partition(hierarchyLevelDefinitions, (x) => HierarchyNodesDefinition.isInstanceNodesQuery(x.hierarchyNodesDefinition));
325
370
  // query instance keys and a flag whether they should be hidden
326
- const instanceKeys = instanceDefs.pipe(mergeMap((def) => this._queryScheduler.scheduleSubscription(of(def.query).pipe(mergeMap(async (query) => {
371
+ const instanceKeys = instanceDefs.pipe(mergeMap(({ imodelAccess, hierarchyNodesDefinition: def }) => this.getQueryScheduler(imodelAccess.imodelKey).scheduleSubscription(of(def.query).pipe(mergeMap(async (query) => {
327
372
  const ecsql = `
328
373
  SELECT
329
374
  ${NodeSelectClauseColumnNames.FullClassName},
@@ -333,7 +378,7 @@ class IModelHierarchyProviderImpl {
333
378
  ${query.ecsql}
334
379
  )
335
380
  `;
336
- const reader = this._imodelAccess.createQueryReader({ ...query, ecsql }, {
381
+ const reader = imodelAccess.createQueryReader({ ...query, ecsql }, {
337
382
  rowFormat: "Indexes",
338
383
  limit: hierarchyLevelSizeLimit,
339
384
  restartToken: `${this.#componentName}/${this.#componentId}/node-instance-keys/${Guid.createValue()}`,
@@ -342,7 +387,7 @@ class IModelHierarchyProviderImpl {
342
387
  key: {
343
388
  className: normalizeFullClassName(row[0]),
344
389
  id: row[1],
345
- imodelKey: this._imodelAccess.imodelKey,
390
+ imodelKey: imodelAccess.imodelKey,
346
391
  },
347
392
  hide: !!row[2],
348
393
  })));
@@ -352,17 +397,17 @@ class IModelHierarchyProviderImpl {
352
397
  // hidden items' handling:
353
398
  // - if a generic node is hidden, we'll want to retrieve instance keys of its children, otherwise we don't care about it,
354
399
  // - if an instance key is hidden, we want to create a merged node similar to what we do in `createHideNodesInHierarchyOperator`
355
- const hiddenParentNodes = merge(genericDefs.pipe(filter((def) => !!def.node.processingParams?.hideInHierarchy), map((def) => ({
400
+ const hiddenParentNodes = merge(genericDefs.pipe(filter(({ hierarchyNodesDefinition }) => !!hierarchyNodesDefinition.node.processingParams?.hideInHierarchy), map(({ hierarchyNodesDefinition }) => ({
356
401
  type: "generic",
357
- id: def.node.key,
358
- source: this._imodelAccess.imodelKey,
402
+ id: hierarchyNodesDefinition.node.key,
403
+ source: this.#sourceName,
359
404
  }))), hiddenNodeInstanceKeys.pipe(
360
405
  // first merge all keys by class
361
- reduceToMergeMapList(({ key }) => key.className, ({ key }) => key.id),
406
+ reduceToMergeMapList(({ key }) => key.className, ({ key }) => key),
362
407
  // then, for each class, create an instance key
363
- mergeMap((mergedMap) => [...mergedMap.entries()].map(([className, ids]) => ({
408
+ mergeMap((mergedMap) => [...mergedMap.entries()].map(([className, keys]) => ({
364
409
  type: "instances",
365
- instanceKeys: ids.map((id) => ({ className, id, imodelKey: this._imodelAccess.imodelKey })),
410
+ instanceKeys: keys.map(({ id, imodelKey }) => ({ className, id, imodelKey })),
366
411
  }))))).pipe(map((key) => ({
367
412
  key,
368
413
  parentKeys: [],
@@ -451,4 +496,49 @@ function filterQueryByInstanceKeys(query, filteredInstanceKeys) {
451
496
  };
452
497
  /* c8 ignore end */
453
498
  }
499
+ function mergeNodes(source) {
500
+ return from(source).pipe(reduce((acc, input) => {
501
+ for (let i = 0; i < acc.length; ++i) {
502
+ if (input.imodelAccess === acc[i].imodelAccess) {
503
+ // don't attempt to merge nodes if they come from the same provider
504
+ continue;
505
+ }
506
+ // the first argument for `tryMergeNodes` acts as the primary data source, so we have to make sure we pass
507
+ // the node with higher `imodelAccessIndex` first
508
+ const candidates = input.imodelAccessIndex > acc[i].imodelAccessIndex
509
+ ? { imodelAccess: input.imodelAccess, imodelAccessIndex: input.imodelAccessIndex, primary: input.node, secondary: acc[i].node }
510
+ : { imodelAccess: acc[i].imodelAccess, imodelAccessIndex: acc[i].imodelAccessIndex, primary: acc[i].node, secondary: input.node };
511
+ const merged = tryMergeNodes(candidates.primary, candidates.secondary);
512
+ if (merged) {
513
+ acc[i] = { ...acc[i], node: merged, imodelAccess: candidates.imodelAccess, imodelAccessIndex: candidates.imodelAccessIndex };
514
+ return acc;
515
+ }
516
+ }
517
+ acc.push(input);
518
+ return acc;
519
+ }, new Array()), mergeAll());
520
+ }
521
+ function tryMergeInstanceNodes(primary, secondary) {
522
+ if (HierarchyNode.isInstancesNode(primary) &&
523
+ HierarchyNode.isInstancesNode(secondary) &&
524
+ primary.key.instanceKeys.some((lhsKey) => secondary.key.instanceKeys.some((rhsKey) => InstanceKey.equals(lhsKey, rhsKey)))) {
525
+ return {
526
+ ...primary,
527
+ key: {
528
+ type: "instances",
529
+ instanceKeys: primary.key.instanceKeys.concat(secondary.key.instanceKeys),
530
+ },
531
+ };
532
+ }
533
+ return undefined;
534
+ }
535
+ function tryMergeGenericNodes(primary, secondary) {
536
+ if (HierarchyNode.isGeneric(primary) && HierarchyNode.isGeneric(secondary) && primary.key.type === secondary.key.type) {
537
+ return primary;
538
+ }
539
+ return undefined;
540
+ }
541
+ function tryMergeNodes(primary, secondary) {
542
+ return tryMergeInstanceNodes(primary, secondary) ?? tryMergeGenericNodes(primary, secondary) ?? undefined;
543
+ }
454
544
  //# sourceMappingURL=IModelHierarchyProvider.js.map