@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.
- package/CHANGELOG.md +56 -9
- package/lib/cjs/hierarchies/HierarchyFiltering.d.ts +77 -36
- package/lib/cjs/hierarchies/HierarchyFiltering.d.ts.map +1 -1
- package/lib/cjs/hierarchies/HierarchyFiltering.js +126 -98
- package/lib/cjs/hierarchies/HierarchyFiltering.js.map +1 -1
- package/lib/cjs/hierarchies/HierarchyMerge.d.ts +19 -0
- package/lib/cjs/hierarchies/HierarchyMerge.d.ts.map +1 -0
- package/lib/cjs/hierarchies/HierarchyMerge.js +43 -0
- package/lib/cjs/hierarchies/HierarchyMerge.js.map +1 -0
- package/lib/cjs/hierarchies/HierarchyNode.d.ts +2 -2
- package/lib/cjs/hierarchies/HierarchyNode.d.ts.map +1 -1
- package/lib/cjs/hierarchies/HierarchyNode.js.map +1 -1
- package/lib/cjs/hierarchies/HierarchyNodeKey.d.ts.map +1 -1
- package/lib/cjs/hierarchies/HierarchyNodeKey.js +8 -8
- package/lib/cjs/hierarchies/HierarchyNodeKey.js.map +1 -1
- package/lib/cjs/hierarchies/HierarchyProvider.d.ts +0 -18
- package/lib/cjs/hierarchies/HierarchyProvider.d.ts.map +1 -1
- package/lib/cjs/hierarchies/HierarchyProvider.js +0 -37
- package/lib/cjs/hierarchies/HierarchyProvider.js.map +1 -1
- package/lib/cjs/hierarchies/imodel/FilteringHierarchyDefinition.d.ts +2 -0
- package/lib/cjs/hierarchies/imodel/FilteringHierarchyDefinition.d.ts.map +1 -1
- package/lib/cjs/hierarchies/imodel/FilteringHierarchyDefinition.js +45 -30
- package/lib/cjs/hierarchies/imodel/FilteringHierarchyDefinition.js.map +1 -1
- package/lib/cjs/hierarchies/imodel/IModelHierarchyProvider.d.ts +42 -7
- package/lib/cjs/hierarchies/imodel/IModelHierarchyProvider.d.ts.map +1 -1
- package/lib/cjs/hierarchies/imodel/IModelHierarchyProvider.js +144 -45
- package/lib/cjs/hierarchies/imodel/IModelHierarchyProvider.js.map +1 -1
- package/lib/cjs/hierarchies/imodel/TreeNodesReader.d.ts.map +1 -1
- package/lib/cjs/hierarchies/imodel/TreeNodesReader.js +10 -1
- package/lib/cjs/hierarchies/imodel/TreeNodesReader.js.map +1 -1
- package/lib/cjs/hierarchies/imodel/operators/HideIfNoChildren.d.ts.map +1 -1
- package/lib/cjs/hierarchies/imodel/operators/HideIfNoChildren.js +1 -0
- package/lib/cjs/hierarchies/imodel/operators/HideIfNoChildren.js.map +1 -1
- package/lib/cjs/hierarchies/internal/Common.d.ts +3 -0
- package/lib/cjs/hierarchies/internal/Common.d.ts.map +1 -1
- package/lib/cjs/hierarchies/internal/Common.js +12 -1
- package/lib/cjs/hierarchies/internal/Common.js.map +1 -1
- package/lib/cjs/hierarchies/internal/EachValueFrom.d.ts.map +1 -1
- package/lib/cjs/hierarchies/internal/EachValueFrom.js +1 -0
- package/lib/cjs/hierarchies/internal/EachValueFrom.js.map +1 -1
- package/lib/cjs/hierarchies/internal/RxjsHierarchyDefinition.d.ts.map +1 -1
- package/lib/cjs/hierarchies/internal/RxjsHierarchyDefinition.js +2 -1
- package/lib/cjs/hierarchies/internal/RxjsHierarchyDefinition.js.map +1 -1
- package/lib/cjs/presentation-hierarchies.d.ts +3 -2
- package/lib/cjs/presentation-hierarchies.d.ts.map +1 -1
- package/lib/cjs/presentation-hierarchies.js +4 -3
- package/lib/cjs/presentation-hierarchies.js.map +1 -1
- package/lib/esm/hierarchies/HierarchyFiltering.d.ts +77 -36
- package/lib/esm/hierarchies/HierarchyFiltering.d.ts.map +1 -1
- package/lib/esm/hierarchies/HierarchyFiltering.js +125 -98
- package/lib/esm/hierarchies/HierarchyFiltering.js.map +1 -1
- package/lib/esm/hierarchies/HierarchyMerge.d.ts +19 -0
- package/lib/esm/hierarchies/HierarchyMerge.d.ts.map +1 -0
- package/lib/esm/hierarchies/HierarchyMerge.js +40 -0
- package/lib/esm/hierarchies/HierarchyMerge.js.map +1 -0
- package/lib/esm/hierarchies/HierarchyNode.d.ts +2 -2
- package/lib/esm/hierarchies/HierarchyNode.d.ts.map +1 -1
- package/lib/esm/hierarchies/HierarchyNode.js.map +1 -1
- package/lib/esm/hierarchies/HierarchyNodeKey.d.ts.map +1 -1
- package/lib/esm/hierarchies/HierarchyNodeKey.js +9 -9
- package/lib/esm/hierarchies/HierarchyNodeKey.js.map +1 -1
- package/lib/esm/hierarchies/HierarchyProvider.d.ts +0 -18
- package/lib/esm/hierarchies/HierarchyProvider.d.ts.map +1 -1
- package/lib/esm/hierarchies/HierarchyProvider.js +1 -36
- package/lib/esm/hierarchies/HierarchyProvider.js.map +1 -1
- package/lib/esm/hierarchies/imodel/FilteringHierarchyDefinition.d.ts +2 -0
- package/lib/esm/hierarchies/imodel/FilteringHierarchyDefinition.d.ts.map +1 -1
- package/lib/esm/hierarchies/imodel/FilteringHierarchyDefinition.js +47 -32
- package/lib/esm/hierarchies/imodel/FilteringHierarchyDefinition.js.map +1 -1
- package/lib/esm/hierarchies/imodel/IModelHierarchyProvider.d.ts +42 -7
- package/lib/esm/hierarchies/imodel/IModelHierarchyProvider.d.ts.map +1 -1
- package/lib/esm/hierarchies/imodel/IModelHierarchyProvider.js +146 -48
- package/lib/esm/hierarchies/imodel/IModelHierarchyProvider.js.map +1 -1
- package/lib/esm/hierarchies/imodel/TreeNodesReader.d.ts.map +1 -1
- package/lib/esm/hierarchies/imodel/TreeNodesReader.js +10 -1
- package/lib/esm/hierarchies/imodel/TreeNodesReader.js.map +1 -1
- package/lib/esm/hierarchies/imodel/operators/HideIfNoChildren.d.ts.map +1 -1
- package/lib/esm/hierarchies/imodel/operators/HideIfNoChildren.js +1 -0
- package/lib/esm/hierarchies/imodel/operators/HideIfNoChildren.js.map +1 -1
- package/lib/esm/hierarchies/internal/Common.d.ts +3 -0
- package/lib/esm/hierarchies/internal/Common.d.ts.map +1 -1
- package/lib/esm/hierarchies/internal/Common.js +11 -1
- package/lib/esm/hierarchies/internal/Common.js.map +1 -1
- package/lib/esm/hierarchies/internal/EachValueFrom.d.ts.map +1 -1
- package/lib/esm/hierarchies/internal/EachValueFrom.js +1 -0
- package/lib/esm/hierarchies/internal/EachValueFrom.js.map +1 -1
- package/lib/esm/hierarchies/internal/RxjsHierarchyDefinition.d.ts.map +1 -1
- package/lib/esm/hierarchies/internal/RxjsHierarchyDefinition.js +3 -2
- package/lib/esm/hierarchies/internal/RxjsHierarchyDefinition.js.map +1 -1
- package/lib/esm/presentation-hierarchies.d.ts +3 -2
- package/lib/esm/presentation-hierarchies.d.ts.map +1 -1
- package/lib/esm/presentation-hierarchies.js +2 -2
- package/lib/esm/presentation-hierarchies.js.map +1 -1
- package/package.json +17 -17
|
@@ -37,7 +37,10 @@ interface IModelHierarchyProviderProps {
|
|
|
37
37
|
* @see `ECClassHierarchyInspector`
|
|
38
38
|
*/
|
|
39
39
|
imodelAccess: IModelAccess;
|
|
40
|
-
/**
|
|
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
|
-
|
|
76
|
-
|
|
77
|
-
|
|
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;
|
|
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
|
-
|
|
57
|
+
_imodels;
|
|
46
58
|
_hierarchyChanged;
|
|
47
59
|
_valuesFormatter;
|
|
48
60
|
_sourceHierarchyDefinition;
|
|
49
61
|
_activeHierarchyDefinition;
|
|
50
62
|
_localizedStrings;
|
|
51
|
-
|
|
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
|
-
|
|
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.
|
|
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
|
-
|
|
72
|
-
|
|
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.
|
|
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
|
-
|
|
131
|
-
|
|
132
|
-
|
|
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
|
-
|
|
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({
|
|
203
|
+
return of({
|
|
204
|
+
...def.node,
|
|
205
|
+
key: { type: "generic", id: def.node.key, source: this.#sourceName },
|
|
206
|
+
});
|
|
157
207
|
}
|
|
158
|
-
return this.
|
|
159
|
-
queryExecutor:
|
|
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:
|
|
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.
|
|
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
|
|
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
|
|
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.
|
|
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 =
|
|
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:
|
|
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((
|
|
400
|
+
const hiddenParentNodes = merge(genericDefs.pipe(filter(({ hierarchyNodesDefinition }) => !!hierarchyNodesDefinition.node.processingParams?.hideInHierarchy), map(({ hierarchyNodesDefinition }) => ({
|
|
348
401
|
type: "generic",
|
|
349
|
-
id:
|
|
350
|
-
source: this
|
|
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
|
|
406
|
+
reduceToMergeMapList(({ key }) => key.className, ({ key }) => key),
|
|
354
407
|
// then, for each class, create an instance key
|
|
355
|
-
mergeMap((mergedMap) => [...mergedMap.entries()].map(([className,
|
|
408
|
+
mergeMap((mergedMap) => [...mergedMap.entries()].map(([className, keys]) => ({
|
|
356
409
|
type: "instances",
|
|
357
|
-
instanceKeys:
|
|
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
|