@itwin/presentation-hierarchies 2.0.0-alpha.4 → 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 (94) hide show
  1. package/CHANGELOG.md +56 -9
  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 +144 -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 +10 -1
  30. package/lib/cjs/hierarchies/imodel/TreeNodesReader.js.map +1 -1
  31. package/lib/cjs/hierarchies/imodel/operators/HideIfNoChildren.d.ts.map +1 -1
  32. package/lib/cjs/hierarchies/imodel/operators/HideIfNoChildren.js +1 -0
  33. package/lib/cjs/hierarchies/imodel/operators/HideIfNoChildren.js.map +1 -1
  34. package/lib/cjs/hierarchies/internal/Common.d.ts +3 -0
  35. package/lib/cjs/hierarchies/internal/Common.d.ts.map +1 -1
  36. package/lib/cjs/hierarchies/internal/Common.js +12 -1
  37. package/lib/cjs/hierarchies/internal/Common.js.map +1 -1
  38. package/lib/cjs/hierarchies/internal/EachValueFrom.d.ts.map +1 -1
  39. package/lib/cjs/hierarchies/internal/EachValueFrom.js +1 -0
  40. package/lib/cjs/hierarchies/internal/EachValueFrom.js.map +1 -1
  41. package/lib/cjs/hierarchies/internal/RxjsHierarchyDefinition.d.ts.map +1 -1
  42. package/lib/cjs/hierarchies/internal/RxjsHierarchyDefinition.js +2 -1
  43. package/lib/cjs/hierarchies/internal/RxjsHierarchyDefinition.js.map +1 -1
  44. package/lib/cjs/presentation-hierarchies.d.ts +3 -2
  45. package/lib/cjs/presentation-hierarchies.d.ts.map +1 -1
  46. package/lib/cjs/presentation-hierarchies.js +4 -3
  47. package/lib/cjs/presentation-hierarchies.js.map +1 -1
  48. package/lib/esm/hierarchies/HierarchyFiltering.d.ts +77 -36
  49. package/lib/esm/hierarchies/HierarchyFiltering.d.ts.map +1 -1
  50. package/lib/esm/hierarchies/HierarchyFiltering.js +125 -98
  51. package/lib/esm/hierarchies/HierarchyFiltering.js.map +1 -1
  52. package/lib/esm/hierarchies/HierarchyMerge.d.ts +19 -0
  53. package/lib/esm/hierarchies/HierarchyMerge.d.ts.map +1 -0
  54. package/lib/esm/hierarchies/HierarchyMerge.js +40 -0
  55. package/lib/esm/hierarchies/HierarchyMerge.js.map +1 -0
  56. package/lib/esm/hierarchies/HierarchyNode.d.ts +2 -2
  57. package/lib/esm/hierarchies/HierarchyNode.d.ts.map +1 -1
  58. package/lib/esm/hierarchies/HierarchyNode.js.map +1 -1
  59. package/lib/esm/hierarchies/HierarchyNodeKey.d.ts.map +1 -1
  60. package/lib/esm/hierarchies/HierarchyNodeKey.js +9 -9
  61. package/lib/esm/hierarchies/HierarchyNodeKey.js.map +1 -1
  62. package/lib/esm/hierarchies/HierarchyProvider.d.ts +0 -18
  63. package/lib/esm/hierarchies/HierarchyProvider.d.ts.map +1 -1
  64. package/lib/esm/hierarchies/HierarchyProvider.js +1 -36
  65. package/lib/esm/hierarchies/HierarchyProvider.js.map +1 -1
  66. package/lib/esm/hierarchies/imodel/FilteringHierarchyDefinition.d.ts +2 -0
  67. package/lib/esm/hierarchies/imodel/FilteringHierarchyDefinition.d.ts.map +1 -1
  68. package/lib/esm/hierarchies/imodel/FilteringHierarchyDefinition.js +47 -32
  69. package/lib/esm/hierarchies/imodel/FilteringHierarchyDefinition.js.map +1 -1
  70. package/lib/esm/hierarchies/imodel/IModelHierarchyProvider.d.ts +42 -7
  71. package/lib/esm/hierarchies/imodel/IModelHierarchyProvider.d.ts.map +1 -1
  72. package/lib/esm/hierarchies/imodel/IModelHierarchyProvider.js +146 -48
  73. package/lib/esm/hierarchies/imodel/IModelHierarchyProvider.js.map +1 -1
  74. package/lib/esm/hierarchies/imodel/TreeNodesReader.d.ts.map +1 -1
  75. package/lib/esm/hierarchies/imodel/TreeNodesReader.js +10 -1
  76. package/lib/esm/hierarchies/imodel/TreeNodesReader.js.map +1 -1
  77. package/lib/esm/hierarchies/imodel/operators/HideIfNoChildren.d.ts.map +1 -1
  78. package/lib/esm/hierarchies/imodel/operators/HideIfNoChildren.js +1 -0
  79. package/lib/esm/hierarchies/imodel/operators/HideIfNoChildren.js.map +1 -1
  80. package/lib/esm/hierarchies/internal/Common.d.ts +3 -0
  81. package/lib/esm/hierarchies/internal/Common.d.ts.map +1 -1
  82. package/lib/esm/hierarchies/internal/Common.js +11 -1
  83. package/lib/esm/hierarchies/internal/Common.js.map +1 -1
  84. package/lib/esm/hierarchies/internal/EachValueFrom.d.ts.map +1 -1
  85. package/lib/esm/hierarchies/internal/EachValueFrom.js +1 -0
  86. package/lib/esm/hierarchies/internal/EachValueFrom.js.map +1 -1
  87. package/lib/esm/hierarchies/internal/RxjsHierarchyDefinition.d.ts.map +1 -1
  88. package/lib/esm/hierarchies/internal/RxjsHierarchyDefinition.js +3 -2
  89. package/lib/esm/hierarchies/internal/RxjsHierarchyDefinition.js.map +1 -1
  90. package/lib/esm/presentation-hierarchies.d.ts +3 -2
  91. package/lib/esm/presentation-hierarchies.d.ts.map +1 -1
  92. package/lib/esm/presentation-hierarchies.js +2 -2
  93. package/lib/esm/presentation-hierarchies.js.map +1 -1
  94. package/package.json +17 -17
@@ -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;AAuBxC,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,32 +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();
68
+ #componentId;
69
+ #componentName;
70
+ #sourceName;
55
71
  constructor(props) {
56
- this._imodelAccess = props.imodelAccess;
72
+ if (props.imodels.length === 0) {
73
+ throw new Error(`Creating an iModel hierarchy provider requires at least one iModel.`);
74
+ }
75
+ this.#componentId = Guid.createValue();
76
+ this.#componentName = "IModelHierarchyProviderImpl";
77
+ /* c8 ignore next */
78
+ this.#sourceName = props.sourceName ?? `${this.#componentName}:${this.#componentId}`;
79
+ this._imodels = props.imodels;
57
80
  this._hierarchyChanged = new BeEvent();
58
81
  this._activeHierarchyDefinition = this._sourceHierarchyDefinition = getRxjsHierarchyDefinition(props.hierarchyDefinition);
59
82
  this._valuesFormatter = props?.formatter ?? createDefaultValueFormatter();
60
83
  this._localizedStrings = { other: "Other", unspecified: "Not specified", ...props?.localizedStrings };
61
- this._queryScheduler = new SubscriptionScheduler(props.queryConcurrency ?? DEFAULT_QUERY_CONCURRENCY);
84
+ this._queryConcurrency = props.queryConcurrency ?? DEFAULT_QUERY_CONCURRENCY;
85
+ this._querySchedulers = new Map();
62
86
  this.setHierarchyFilter(props.filtering);
63
87
  const queryCacheSize = props.queryCacheSize ?? DEFAULT_QUERY_CACHE_SIZE;
64
88
  if (queryCacheSize !== 0) {
@@ -68,11 +92,18 @@ class IModelHierarchyProviderImpl {
68
92
  variationsCount: 1,
69
93
  });
70
94
  }
71
- this._unsubscribe = props.imodelChanged?.addListener(() => {
72
- 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}"`);
73
101
  this._dispose.next();
74
102
  this._hierarchyChanged.raiseEvent();
75
103
  });
104
+ this._unsubscribe = () => {
105
+ imodelChangeSubscription.unsubscribe();
106
+ };
76
107
  }
77
108
  [Symbol.dispose]() {
78
109
  this._dispose.next();
@@ -85,6 +116,17 @@ class IModelHierarchyProviderImpl {
85
116
  get hierarchyChanged() {
86
117
  return this._hierarchyChanged;
87
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
+ }
88
130
  invalidateHierarchyCache(reason) {
89
131
  doLog({
90
132
  category: `${LOGGING_NAMESPACE}.Events`,
@@ -110,8 +152,9 @@ class IModelHierarchyProviderImpl {
110
152
  return;
111
153
  }
112
154
  this._activeHierarchyDefinition = new FilteringHierarchyDefinition({
113
- imodelAccess: this._imodelAccess,
155
+ imodelAccess: this.getPrimaryIModelAccess(),
114
156
  source: this._sourceHierarchyDefinition,
157
+ sourceName: this.#sourceName,
115
158
  nodeIdentifierPaths: props.paths,
116
159
  });
117
160
  this.invalidateHierarchyCache("Hierarchy filter set");
@@ -127,44 +170,51 @@ class IModelHierarchyProviderImpl {
127
170
  category: LOGGING_NAMESPACE_PERFORMANCE,
128
171
  message: /* c8 ignore next */ () => `[${requestContext.requestId}] Requesting hierarchy level definitions for ${createNodeIdentifierForLogging(props.parentNode)}`,
129
172
  });
130
- let parentNode = props.parentNode;
131
- if (parentNode && HierarchyNode.isGeneric(parentNode) && parentNode.key.source && parentNode.key.source !== this._imodelAccess.imodelKey) {
132
- return EMPTY;
133
- }
134
- if (parentNode && HierarchyNode.isInstancesNode(parentNode)) {
135
- parentNode = {
136
- ...parentNode,
137
- key: {
138
- ...parentNode.key,
139
- instanceKeys: parentNode.key.instanceKeys.filter((ik) => !ik.imodelKey || ik.imodelKey === this._imodelAccess.imodelKey),
140
- },
141
- };
142
- assert(HierarchyNodeKey.isInstances(parentNode.key));
143
- 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) {
144
176
  return EMPTY;
145
177
  }
146
- }
147
- 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({
148
195
  category: LOGGING_NAMESPACE_PERFORMANCE,
149
196
  message: /* c8 ignore next */ () => `[${requestContext.requestId}] Received all hierarchy level definitions for ${createNodeIdentifierForLogging(props.parentNode)}`,
150
197
  })));
151
198
  }
152
199
  createSourceNodesObservable(props) {
153
200
  // pipe definitions to nodes and put "share replay" on it
154
- return this.createHierarchyLevelDefinitionsObservable(props).pipe(mergeMap((def) => {
201
+ return this.createHierarchyLevelDefinitionsObservable(props).pipe(mergeMap(({ imodelAccess, imodelAccessIndex, hierarchyNodesDefinition: def }) => defer(() => {
155
202
  if (HierarchyNodesDefinition.isGenericNode(def)) {
156
- 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
+ });
157
207
  }
158
- return this._queryScheduler.scheduleSubscription(of(def.query).pipe(map((query) => filterQueryByInstanceKeys(query, props.filteredInstanceKeys)), mergeMap((query) => readNodes({
159
- 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,
160
210
  query,
161
211
  limit: props.hierarchyLevelSizeLimit,
162
212
  parser: this._activeHierarchyDefinition.parseNode ? (row) => this._activeHierarchyDefinition.parseNode(row, props.parentNode) : undefined,
163
213
  })), map((node) => ({
164
214
  ...node,
165
- 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 })) },
166
216
  }))));
167
- }), finalize(() => doLog({
217
+ }).pipe(map((node) => ({ imodelAccess, imodelAccessIndex, node })))), mergeNodes, map(({ node }) => node), finalize(() => doLog({
168
218
  category: LOGGING_NAMESPACE_PERFORMANCE,
169
219
  message: /* c8 ignore next */ () => `[${props.requestContext.requestId}] Read all child nodes ${createNodeIdentifierForLogging(props.parentNode)}`,
170
220
  })), shareReplayWithErrors());
@@ -178,6 +228,7 @@ class IModelHierarchyProviderImpl {
178
228
  // format `ConcatenatedValue` labels into string labels
179
229
  mergeMap((node) => applyLabelsFormatting(node, this._valuesFormatter)),
180
230
  // we have `ProcessedHierarchyNode` from here
231
+ // let consumers step-in
181
232
  preProcessNodes(this._activeHierarchyDefinition),
182
233
  // process hiding
183
234
  createHideIfNoChildrenOperator((n) => this.getChildNodesObservables({ parentNode: n, requestContext: props.requestContext }).hasNodes), createHideNodesInHierarchyOperator(
@@ -188,7 +239,7 @@ class IModelHierarchyProviderImpl {
188
239
  })));
189
240
  }
190
241
  createProcessedNodesObservable(preprocessedNodesObservable, props) {
191
- 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({
192
243
  category: LOGGING_NAMESPACE_PERFORMANCE,
193
244
  message: /* c8 ignore next */ () => `[${props.requestContext.requestId}] Finished grouping child nodes for ${createNodeIdentifierForLogging(props.parentNode)}`,
194
245
  })));
@@ -198,9 +249,7 @@ class IModelHierarchyProviderImpl {
198
249
  if ("processingParams" in n) {
199
250
  delete n.processingParams;
200
251
  }
201
- return Object.assign(n, {
202
- children: hasChildren(n),
203
- });
252
+ return { ...n, children: hasChildren(n) };
204
253
  }), takeUntil(this._dispose), finalize(() => doLog({
205
254
  category: LOGGING_NAMESPACE_PERFORMANCE,
206
255
  message: /* c8 ignore next */ () => `[${props.requestContext.requestId}] Finished finalizing child nodes for ${createNodeIdentifierForLogging(props.parentNode)}`,
@@ -315,11 +364,11 @@ class IModelHierarchyProviderImpl {
315
364
  return from(parentNode.groupedInstanceKeys);
316
365
  }
317
366
  assert(!parentNode || HierarchyNode.isGeneric(parentNode) || HierarchyNode.isInstancesNode(parentNode));
318
- const hierarchyLevelDefinitions = this.createHierarchyLevelDefinitionsObservable({ parentNode, instanceFilter, requestContext });
319
367
  // split the definitions based on whether they're for generic nodes or for instance nodes
320
- 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));
321
370
  // query instance keys and a flag whether they should be hidden
322
- 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) => {
323
372
  const ecsql = `
324
373
  SELECT
325
374
  ${NodeSelectClauseColumnNames.FullClassName},
@@ -329,12 +378,16 @@ class IModelHierarchyProviderImpl {
329
378
  ${query.ecsql}
330
379
  )
331
380
  `;
332
- const reader = this._imodelAccess.createQueryReader({ ...query, ecsql }, { rowFormat: "Indexes", limit: hierarchyLevelSizeLimit });
381
+ const reader = imodelAccess.createQueryReader({ ...query, ecsql }, {
382
+ rowFormat: "Indexes",
383
+ limit: hierarchyLevelSizeLimit,
384
+ restartToken: `${this.#componentName}/${this.#componentId}/node-instance-keys/${Guid.createValue()}`,
385
+ });
333
386
  return from(reader).pipe(map((row) => ({
334
387
  key: {
335
388
  className: normalizeFullClassName(row[0]),
336
389
  id: row[1],
337
- imodelKey: this._imodelAccess.imodelKey,
390
+ imodelKey: imodelAccess.imodelKey,
338
391
  },
339
392
  hide: !!row[2],
340
393
  })));
@@ -344,17 +397,17 @@ class IModelHierarchyProviderImpl {
344
397
  // hidden items' handling:
345
398
  // - if a generic node is hidden, we'll want to retrieve instance keys of its children, otherwise we don't care about it,
346
399
  // - if an instance key is hidden, we want to create a merged node similar to what we do in `createHideNodesInHierarchyOperator`
347
- 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 }) => ({
348
401
  type: "generic",
349
- id: def.node.key,
350
- source: this._imodelAccess.imodelKey,
402
+ id: hierarchyNodesDefinition.node.key,
403
+ source: this.#sourceName,
351
404
  }))), hiddenNodeInstanceKeys.pipe(
352
405
  // first merge all keys by class
353
- reduceToMergeMapList(({ key }) => key.className, ({ key }) => key.id),
406
+ reduceToMergeMapList(({ key }) => key.className, ({ key }) => key),
354
407
  // then, for each class, create an instance key
355
- mergeMap((mergedMap) => [...mergedMap.entries()].map(([className, ids]) => ({
408
+ mergeMap((mergedMap) => [...mergedMap.entries()].map(([className, keys]) => ({
356
409
  type: "instances",
357
- instanceKeys: ids.map((id) => ({ className, id, imodelKey: this._imodelAccess.imodelKey })),
410
+ instanceKeys: keys.map(({ id, imodelKey }) => ({ className, id, imodelKey })),
358
411
  }))))).pipe(map((key) => ({
359
412
  key,
360
413
  parentKeys: [],
@@ -443,4 +496,49 @@ function filterQueryByInstanceKeys(query, filteredInstanceKeys) {
443
496
  };
444
497
  /* c8 ignore end */
445
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
+ }
446
544
  //# sourceMappingURL=IModelHierarchyProvider.js.map