@itwin/unified-selection
Version:
Package for managing unified selection in iTwin.js applications.
191 lines • 8.31 kB
JavaScript
/*---------------------------------------------------------------------------------------------
* Copyright (c) Bentley Systems, Incorporated. All rights reserved.
* See LICENSE.md in the project root for license terms and full copyright notice.
*--------------------------------------------------------------------------------------------*/
import { Id64 } from "@itwin/core-bentley";
import { formIdBindings, genericExecuteQuery } from "./Utils.js";
/**
* Computes selection from given element IDs.
* @public
*/
export async function* computeSelection(props) {
const { queryExecutor, elementIds, scope } = props;
if (typeof scope === "string") {
yield* computeSelection({ queryExecutor, elementIds, scope: { id: scope } });
return;
}
const nonTransientIds = [];
for (const id of Id64.iterable(elementIds)) {
if (!Id64.isTransient(id)) {
nonTransientIds.push(id);
}
}
switch (scope.id) {
case "element":
yield* computeElementSelection(queryExecutor, nonTransientIds, scope.ancestorLevel ?? 0);
return;
case "category":
yield* computeCategorySelection(queryExecutor, nonTransientIds);
return;
case "model":
yield* computeModelSelection(queryExecutor, nonTransientIds);
return;
case "functional":
yield* computeFunctionalElementSelection(queryExecutor, nonTransientIds, scope.ancestorLevel ?? 0);
return;
}
}
async function* computeElementSelection(queryExecutor, elementIds, ancestorLevel) {
const bindings = [];
const recurseUntilRoot = ancestorLevel < 0;
const ctes = [
`
AncestorElements(ECInstanceId, ECClassId, Depth, ParentId) AS (
SELECT ECInstanceId, ECClassId, ${formAncestorLevelBinding(ancestorLevel, bindings)}, Parent.Id
FROM BisCore.Element
WHERE ${formIdBindings("ECInstanceId", elementIds, bindings)}
UNION ALL
SELECT pe.ECInstanceId, pe.ECClassId, a.Depth - 1, pe.Parent.Id
FROM AncestorElements a
JOIN BisCore.Element pe ON pe.ECInstanceId = a.ParentId
WHERE ${recurseUntilRoot ? "" : "Depth > 0 AND"} ParentId IS NOT NULL
)
`,
];
const ecsql = `
SELECT DISTINCT ECInstanceId, ec_classname(ECClassId, 's.c') AS ClassName
FROM AncestorElements
WHERE ${recurseUntilRoot ? "" : "Depth = 0 OR"} ParentId IS NULL
`;
yield* executeQuery(queryExecutor, { ctes, ecsql, bindings });
}
async function* computeCategorySelection(queryExecutor, ids) {
const bindings = [];
const ecsql = [
`
SELECT DISTINCT c.ECInstanceId, ec_classname(c.ECClassId, 's.c') AS ClassName
FROM BisCore.Category c
JOIN BisCore.GeometricElement2d ge ON ge.Category.Id = c.ECInstanceId
WHERE ${formIdBindings("ge.ECInstanceId", ids, bindings)}
`,
`
SELECT DISTINCT c.ECInstanceId, ec_classname(c.ECClassId, 's.c') AS ClassName
FROM BisCore.Category c
JOIN BisCore.GeometricElement3d ge ON ge.Category.Id = c.ECInstanceId
WHERE ${formIdBindings("ge.ECInstanceId", ids, bindings)}
`,
].join(" UNION ALL ");
yield* executeQuery(queryExecutor, { ecsql, bindings });
}
async function* computeModelSelection(queryExecutor, ids) {
const bindings = [];
const ecsql = `
SELECT DISTINCT m.ECInstanceId, ec_classname(m.ECClassId, 's.c') AS ClassName
FROM BisCore.Model m
JOIN BisCore.Element e ON e.Model.Id = m.ECInstanceId
WHERE ${formIdBindings("e.ECInstanceId", ids, bindings)}
`;
yield* executeQuery(queryExecutor, { ecsql, bindings });
}
async function* computeFunctionalElementSelection(queryExecutor, ids, ancestorLevel) {
const bindings = [];
const recurseUntilRoot = ancestorLevel < 0;
const ctes = [
`
Elements2dOrNearestFunctionalElements(OriginalECInstanceId, OriginalECClassId, ECInstanceId, ECClassId, ParentId) AS (
SELECT ECInstanceId, ECClassId, ECInstanceId, ECClassId, Parent.Id
FROM BisCore.Element
WHERE ${formIdBindings("ECInstanceId", ids, bindings)} AND ECClassId IS NOT (BisCore.GeometricElement3d)
UNION ALL
SELECT
e2onfe.OriginalECInstanceId,
e2onfe.OriginalECClassId,
COALESCE(dgrfe.TargetECInstanceId, pe.ECInstanceId),
COALESCE(dgrfe.TargetECClassId, pe.ECClassId),
pe.Parent.Id
FROM Elements2dOrNearestFunctionalElements e2onfe
LEFT JOIN BisCore.Element pe ON pe.ECInstanceId = e2onfe.ParentId
LEFT JOIN Functional.DrawingGraphicRepresentsFunctionalElement dgrfe ON dgrfe.SourceECInstanceId = e2onfe.ECInstanceId
WHERE e2onfe.ECClassId IS NOT (Functional.FunctionalElement) AND (e2onfe.ParentId IS NOT NULL OR dgrfe.TargetECInstanceId IS NOT NULL)
)
`,
`
Element2dNearestFunctionalElements(OriginalECInstanceId, ECInstanceId, ECClassId) AS (
SELECT OriginalECInstanceId, ECInstanceId, ECClassId
FROM Elements2dOrNearestFunctionalElements
WHERE ECClassId IS (Functional.FunctionalElement)
)
`,
`
Elements2dWithoutFunctionalElement(ECInstanceId, ECClassId) AS (
SELECT e2wfe.OriginalECInstanceId, OriginalECClassId
FROM Elements2dOrNearestFunctionalElements e2wfe
LEFT JOIN Element2dNearestFunctionalElements e2nfe ON e2nfe.OriginalECInstanceId = e2wfe.OriginalECInstanceId
WHERE e2wfe.ParentId IS NULL AND e2nfe.ECInstanceId IS NULL
)
`,
`
Elements2d(ECInstanceId, ECClassId) AS (
SELECT ECInstanceId, ECClassId FROM Element2dNearestFunctionalElements
UNION
SELECT ECInstanceId, ECClassId FROM Elements2dWithoutFunctionalElement
)
`,
`
Element2dAncestorElements(ECInstanceId, ECClassId, Depth, ParentId) AS (
SELECT e.ECInstanceId, e.ECClassId, ${formAncestorLevelBinding(ancestorLevel, bindings)}, e.Parent.Id
FROM BisCore.Element e
JOIN Elements2d e2d ON e2d.ECInstanceId = e.ECInstanceId
UNION ALL
SELECT pe.ECInstanceId, pe.ECClassId, e2ae.Depth - 1, pe.Parent.Id
FROM Element2dAncestorElements e2ae
JOIN BisCore.Element pe ON pe.ECInstanceId = e2ae.ParentId
WHERE ${recurseUntilRoot ? "" : "Depth > 0 AND"} ParentId IS NOT NULL
)
`,
`
Element3dAncestorElements(ECInstanceId, ECClassId, Depth, ParentId) AS (
SELECT ge.ECInstanceId, ge.ECClassId, ${formAncestorLevelBinding(ancestorLevel, bindings)}, ge.Parent.Id
FROM BisCore.GeometricElement3d ge
WHERE ${formIdBindings("ge.ECInstanceId", ids, bindings)}
UNION ALL
SELECT pe.ECInstanceId, pe.ECClassId, e3ae.Depth - 1, pe.Parent.Id
FROM Element3dAncestorElements e3ae
JOIN BisCore.Element pe ON pe.ECInstanceId = e3ae.ParentId
WHERE ${recurseUntilRoot ? "" : "Depth > 0 AND"} ParentId IS NOT NULL
)
`,
`
Element3dAncestorRelatedFunctionalElement(ECInstanceId, ClassName) AS (
SELECT
COALESCE(peff.TargetECInstanceId, e3ae.ECInstanceId),
ec_classname(COALESCE(peff.TargetECClassId, e3ae.ECClassId), 's.c')
FROM Element3dAncestorElements e3ae
LEFT JOIN Functional.PhysicalElementFulfillsFunction peff ON peff.SourceECInstanceId = e3ae.ECInstanceId
WHERE ${recurseUntilRoot ? "" : "e3ae.Depth = 0 OR"} e3ae.ParentId IS NULL
)
`,
];
const ecsql = [
`
SELECT DISTINCT ECInstanceId, ec_classname(ECClassId, 's.c') AS ClassName
FROM Element2dAncestorElements
WHERE ${recurseUntilRoot ? "" : "Depth = 0 OR"} ParentId IS NULL
`,
`
SELECT DISTINCT ECInstanceId, ClassName FROM Element3dAncestorRelatedFunctionalElement
`,
].join(" UNION ");
yield* executeQuery(queryExecutor, { ctes, ecsql, bindings });
}
function formAncestorLevelBinding(ancestorLevel, bindings) {
bindings.push({ type: "int", value: ancestorLevel });
return "?";
}
async function* executeQuery(queryExecutor, query) {
yield* genericExecuteQuery(queryExecutor, query, (row) => ({
className: row.ClassName,
id: row.ECInstanceId,
}));
}
//# sourceMappingURL=SelectionScope.js.map