@itwin/presentation-shared
Version:
The package contains types and utilities used across different iTwin.js Presentation packages.
140 lines • 7.43 kB
JavaScript
;
/*---------------------------------------------------------------------------------------------
* Copyright (c) Bentley Systems, Incorporated. All rights reserved.
* See LICENSE.md in the project root for license terms and full copyright notice.
*--------------------------------------------------------------------------------------------*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.createRelationshipPathJoinClause = createRelationshipPathJoinClause;
const Metadata_js_1 = require("../Metadata.js");
const ECSqlValueSelectorSnippets_js_1 = require("./ECSqlValueSelectorSnippets.js");
/**
* Creates an ECSQL JOIN snippet for given relationships' path.
*
* Possible results:
* - When the relationship is represented by a navigation property on either source or target:
* ```SQL
* INNER JOIN [target_schema_name].[target_class_name] [target_alias] ON [target_alias].[navigation_property_name].[Id] = [source_alias].[ECInstanceId]
* ```
* - When outer joining through a non-navigation-property relationship:
* ```SQL
* LEFT JOIN (
* SELECT [relationship_alias].*
* FROM [relationship_schema_name].[relationship_class_name] [relationship_alias]
* INNER JOIN [target_schema_name].[target_class_name] [target_alias] ON [target_alias].[ECInstanceId] = [relationship_alias].[TargetECInstanceId]
* ) [relationship_alias]
* LEFT JOIN [target_schema_name].[target_class_name] [target_alias] ON [target_alias].[ECInstanceId] = [relationship_alias].[TargetECInstanceId]
* ```
* - When inner joining through a non-navigation-property relationship:
* ```SQL
* INNER JOIN [relationship_schema_name].[relationship_class_name] [relationship_alias] ON [relationship_alias].[SourceECInstanceId] = [source_alias].[ECInstanceId]
* INNER JOIN [target_schema_name].[target_class_name] [target_alias] ON [target_alias].[ECInstanceId] = [relationship_alias].[TargetECInstanceId]
* ```
* @public
*/
async function createRelationshipPathJoinClause(props) {
if (props.path.length === 0) {
return "";
}
let prev = {
alias: props.path[0].sourceAlias,
joinPropertyName: "ECInstanceId",
className: props.path[0].sourceClassName,
};
let clause = "";
for (const stepDef of props.path) {
const step = await getRelationshipPathStepClasses(props.schemaProvider, stepDef);
const navigationProperty = await getNavigationProperty(step);
if (navigationProperty) {
const isNavigationPropertyForward = navigationProperty.direction === "Forward";
const relationshipJoinPropertyNames = isNavigationPropertyForward === !step.relationshipReverse
? {
this: (0, ECSqlValueSelectorSnippets_js_1.createRawPropertyValueSelector)(step.targetAlias, "ECInstanceId"),
next: (0, ECSqlValueSelectorSnippets_js_1.createRawPropertyValueSelector)(prev.alias, navigationProperty.name, "Id"),
}
: {
this: (0, ECSqlValueSelectorSnippets_js_1.createRawPropertyValueSelector)(step.targetAlias, navigationProperty.name, "Id"),
next: (0, ECSqlValueSelectorSnippets_js_1.createRawPropertyValueSelector)(prev.alias, prev.joinPropertyName),
};
clause += `
${getJoinClause(step.joinType)} ${getClassSelectClause(step.target, step.targetAlias)}
ON ${relationshipJoinPropertyNames.this} = ${relationshipJoinPropertyNames.next}
`;
prev = {
alias: step.targetAlias,
className: step.target.fullName,
joinPropertyName: "ECInstanceId",
};
}
else {
const relationshipJoinPropertyNames = !step.relationshipReverse
? { this: "SourceECInstanceId", next: "TargetECInstanceId" }
: { this: "TargetECInstanceId", next: "SourceECInstanceId" };
if (step.joinType === "outer") {
clause += `
${getJoinClause("outer")} (
SELECT [${step.relationshipAlias}].*
FROM ${getClassSelectClause(step.relationship, step.relationshipAlias)}
${getJoinClause("inner")} ${getClassSelectClause(step.target, step.targetAlias)}
ON ${(0, ECSqlValueSelectorSnippets_js_1.createRawPropertyValueSelector)(step.targetAlias, "ECInstanceId")}
= ${(0, ECSqlValueSelectorSnippets_js_1.createRawPropertyValueSelector)(step.relationshipAlias, relationshipJoinPropertyNames.next)}
) [${step.relationshipAlias}]
ON ${(0, ECSqlValueSelectorSnippets_js_1.createRawPropertyValueSelector)(step.relationshipAlias, relationshipJoinPropertyNames.this)}
= ${(0, ECSqlValueSelectorSnippets_js_1.createRawPropertyValueSelector)(prev.alias, prev.joinPropertyName)}
`;
}
else {
clause += `
${getJoinClause("inner")} ${getClassSelectClause(step.relationship, step.relationshipAlias)}
ON ${(0, ECSqlValueSelectorSnippets_js_1.createRawPropertyValueSelector)(step.relationshipAlias, relationshipJoinPropertyNames.this)}
= ${(0, ECSqlValueSelectorSnippets_js_1.createRawPropertyValueSelector)(prev.alias, prev.joinPropertyName)}
`;
}
clause += `
${getJoinClause(step.joinType)} ${getClassSelectClause(step.target, step.targetAlias)}
ON ${(0, ECSqlValueSelectorSnippets_js_1.createRawPropertyValueSelector)(step.targetAlias, "ECInstanceId")}
= ${(0, ECSqlValueSelectorSnippets_js_1.createRawPropertyValueSelector)(step.relationshipAlias, relationshipJoinPropertyNames.next)}
`;
prev = {
alias: step.targetAlias,
className: step.target.fullName,
joinPropertyName: "ECInstanceId",
};
}
}
return clause;
}
async function getRelationshipPathStepClasses(schemaProvider, step) {
const { sourceClassName, relationshipName, targetClassName, ...rest } = step;
return {
...rest,
source: await (0, Metadata_js_1.getClass)(schemaProvider, sourceClassName),
relationship: (await (0, Metadata_js_1.getClass)(schemaProvider, relationshipName)),
target: await (0, Metadata_js_1.getClass)(schemaProvider, targetClassName),
};
}
async function getNavigationProperty(step) {
const source = !step.relationshipReverse ? step.source : step.target;
const target = !step.relationshipReverse ? step.target : step.source;
for (const prop of await source.getProperties()) {
if (prop.isNavigation() && prop.direction === "Forward" && (await prop.relationshipClass).fullName === step.relationship.fullName) {
return prop;
}
}
for (const prop of await target.getProperties()) {
if (prop.isNavigation() && prop.direction === "Backward" && (await prop.relationshipClass).fullName === step.relationship.fullName) {
return prop;
}
}
return undefined;
}
function getJoinClause(type) {
if (type === "outer") {
return "OUTER JOIN";
}
return "INNER JOIN";
}
function getClassSelectClause(ecClass, alias) {
const classSelector = `[${ecClass.schema.name}].[${ecClass.name}]`;
return `${classSelector} [${alias}]`;
}
//# sourceMappingURL=ECSqlJoinSnippets.js.map