mirror of
https://github.com/dbgate/dbgate
synced 2024-11-07 20:26:23 +00:00
1723 lines
49 KiB
TypeScript
1723 lines
49 KiB
TypeScript
import type {
|
|
CollectionInfo,
|
|
ColumnInfo,
|
|
DatabaseInfo,
|
|
FilterBehaviour,
|
|
ForeignKeyInfo,
|
|
NamedObjectInfo,
|
|
RangeDefinition,
|
|
TableInfo,
|
|
ViewInfo,
|
|
} from 'dbgate-types';
|
|
import {
|
|
detectSqlFilterBehaviour,
|
|
equalFullName,
|
|
isCollectionInfo,
|
|
isTableInfo,
|
|
isViewInfo,
|
|
mongoFilterBehaviour,
|
|
stringFilterBehaviour,
|
|
} from 'dbgate-tools';
|
|
import {
|
|
ChangePerspectiveConfigFunc,
|
|
createPerspectiveNodeConfig,
|
|
MultipleDatabaseInfo,
|
|
PerspectiveConfig,
|
|
PerspectiveCustomJoinConfig,
|
|
PerspectiveDatabaseConfig,
|
|
PerspectiveDatabaseEngineType,
|
|
PerspectiveFilterColumnInfo,
|
|
PerspectiveNodeConfig,
|
|
PerspectiveReferenceConfig,
|
|
} from './PerspectiveConfig';
|
|
import _isEqual from 'lodash/isEqual';
|
|
import _isArray from 'lodash/isArray';
|
|
import _cloneDeep from 'lodash/cloneDeep';
|
|
import _compact from 'lodash/compact';
|
|
import _uniq from 'lodash/uniq';
|
|
import _flatten from 'lodash/flatten';
|
|
import _uniqBy from 'lodash/uniqBy';
|
|
import _sortBy from 'lodash/sortBy';
|
|
import _cloneDeepWith from 'lodash/cloneDeepWith';
|
|
import _findIndex from 'lodash/findIndex';
|
|
import { PerspectiveDataLoadProps, PerspectiveDataProvider } from './PerspectiveDataProvider';
|
|
import stableStringify from 'json-stable-stringify';
|
|
import { parseFilter } from 'dbgate-filterparser';
|
|
import { CompoudCondition, Condition, Expression, Select } from 'dbgate-sqltree';
|
|
// import { getPerspectiveDefaultColumns } from './getPerspectiveDefaultColumns';
|
|
import uuidv1 from 'uuid/v1';
|
|
import { PerspectiveDataPatternColumn } from './PerspectiveDataPattern';
|
|
import {
|
|
getPerspectiveMostNestedChildColumnName,
|
|
getPerspectiveParentColumnName,
|
|
perspectiveValueMatcher,
|
|
} from './perspectiveTools';
|
|
|
|
export interface PerspectiveDataLoadPropsWithNode {
|
|
props: PerspectiveDataLoadProps;
|
|
node: PerspectiveTreeNode;
|
|
}
|
|
|
|
// export function groupPerspectiveLoadProps(
|
|
// ...list: PerspectiveDataLoadPropsWithNode[]
|
|
// ): PerspectiveDataLoadPropsWithNode[] {
|
|
// const res: PerspectiveDataLoadPropsWithNode[] = [];
|
|
// for (const item of list) {
|
|
// const existing = res.find(
|
|
// x =>
|
|
// x.node == item.node &&
|
|
// x.props.schemaName == item.props.schemaName &&
|
|
// x.props.pureName == item.props.pureName &&
|
|
// _isEqual(x.props.bindingColumns, item.props.bindingColumns)
|
|
// );
|
|
// if (existing) {
|
|
// existing.props.bindingValues.push(...item.props.bindingValues);
|
|
// } else {
|
|
// res.push(_cloneDeep(item));
|
|
// }
|
|
// }
|
|
// return res;
|
|
// }
|
|
|
|
export abstract class PerspectiveTreeNode {
|
|
constructor(
|
|
public dbs: MultipleDatabaseInfo,
|
|
public config: PerspectiveConfig,
|
|
public setConfig: ChangePerspectiveConfigFunc,
|
|
public parentNode: PerspectiveTreeNode,
|
|
public dataProvider: PerspectiveDataProvider,
|
|
public defaultDatabaseConfig: PerspectiveDatabaseConfig,
|
|
public designerId: string
|
|
) {
|
|
this.nodeConfig = config.nodes.find(x => x.designerId == designerId);
|
|
this.parentNodeConfig = parentNode?.nodeConfig;
|
|
}
|
|
readonly nodeConfig: PerspectiveNodeConfig;
|
|
parentNodeConfig: PerspectiveNodeConfig;
|
|
// defaultChecked: boolean;
|
|
abstract get title();
|
|
abstract get codeName();
|
|
abstract get isExpandable();
|
|
childNodesCache: PerspectiveTreeNode[] = null;
|
|
get childNodes(): PerspectiveTreeNode[] {
|
|
if (!this.childNodesCache) {
|
|
this.childNodesCache = this.generateChildNodes();
|
|
}
|
|
return this.childNodesCache;
|
|
}
|
|
abstract generateChildNodes(): PerspectiveTreeNode[];
|
|
abstract get icon(): string;
|
|
get fieldName() {
|
|
return this.codeName;
|
|
}
|
|
get headerTableAttributes() {
|
|
return null;
|
|
}
|
|
get dataField() {
|
|
return this.codeName;
|
|
}
|
|
get tableCode() {
|
|
return null;
|
|
}
|
|
get namedObject(): NamedObjectInfo {
|
|
return null;
|
|
}
|
|
get tableNodeOrParent(): PerspectiveTableNode {
|
|
if (this instanceof PerspectiveTableNode) {
|
|
return this;
|
|
}
|
|
if (this.parentNode == null) {
|
|
return null;
|
|
}
|
|
return this.parentNode.tableNodeOrParent;
|
|
}
|
|
get engineType(): PerspectiveDatabaseEngineType {
|
|
return null;
|
|
}
|
|
get databaseConfig(): PerspectiveDatabaseConfig {
|
|
const res = { ...this.defaultDatabaseConfig };
|
|
if (this.nodeConfig?.conid) res.conid = this.nodeConfig?.conid;
|
|
if (this.nodeConfig?.database) res.database = this.nodeConfig?.database;
|
|
return res;
|
|
}
|
|
abstract getNodeLoadProps(parentRows: any[]): PerspectiveDataLoadProps;
|
|
get isRoot() {
|
|
return this.parentNode == null;
|
|
}
|
|
get rootNode(): PerspectiveTreeNode {
|
|
if (this.isRoot) return this;
|
|
return this.parentNode?.rootNode;
|
|
}
|
|
get isSortable() {
|
|
return false;
|
|
}
|
|
get generatesHiearchicGridColumn() {
|
|
return this.isExpandable && this.isCheckedNode;
|
|
}
|
|
get generatesDataGridColumn() {
|
|
return this.isCheckedColumn;
|
|
}
|
|
get validParentDesignerId() {
|
|
if (this.designerId) return this.designerId;
|
|
return this.parentNode?.validParentDesignerId;
|
|
}
|
|
get preloadedLevelData() {
|
|
return false;
|
|
}
|
|
get findByDesignerIdWithoutDesignerId() {
|
|
return false;
|
|
}
|
|
matchChildRow(parentRow: any, childRow: any): boolean {
|
|
return true;
|
|
}
|
|
|
|
hasTableCode(code: string) {
|
|
return code == this.tableCode || this.parentNode?.hasTableCode(code);
|
|
}
|
|
|
|
// get uniqueName() {
|
|
// if (this.parentNode) return `${this.parentNode.uniqueName}::${this.codeName}`;
|
|
// return this.codeName;
|
|
// }
|
|
get level() {
|
|
if (this.parentNode) return this.parentNode.level + 1;
|
|
return 0;
|
|
}
|
|
get isExpanded() {
|
|
return this.parentNodeConfig?.expandedColumns?.includes(this.codeName);
|
|
}
|
|
get isCheckedColumn() {
|
|
if (this.parentNodeConfig?.checkedColumns?.includes(this.codeName)) return true;
|
|
return false;
|
|
}
|
|
get isChecked() {
|
|
return this.isCheckedColumn;
|
|
}
|
|
get isCheckedNode() {
|
|
return !!this.designerId && !!this.config.nodes?.find(x => x.designerId == this.designerId)?.isNodeChecked;
|
|
}
|
|
get isSecondaryChecked() {
|
|
return false;
|
|
}
|
|
get secondaryCheckable() {
|
|
return false;
|
|
}
|
|
get columnTitle() {
|
|
return this.title;
|
|
}
|
|
get filterBehaviour(): FilterBehaviour {
|
|
return stringFilterBehaviour;
|
|
}
|
|
get columnName() {
|
|
return null;
|
|
}
|
|
get customJoinConfig(): PerspectiveCustomJoinConfig {
|
|
return null;
|
|
}
|
|
get db(): DatabaseInfo {
|
|
return this.dbs?.[this.databaseConfig.conid]?.[this.databaseConfig.database];
|
|
}
|
|
get isCircular() {
|
|
return false;
|
|
}
|
|
|
|
hasDesignerIdInIncestors(designerId: string): boolean {
|
|
if (designerId == this.designerId) return true;
|
|
return this.parentNode?.hasDesignerIdInIncestors(designerId) || false;
|
|
}
|
|
|
|
getChildMatchColumns() {
|
|
return [];
|
|
}
|
|
|
|
getParentMatchColumns() {
|
|
return [];
|
|
}
|
|
|
|
parseFilterCondition(source = null) {
|
|
return null;
|
|
}
|
|
|
|
get hasUncheckedNodeInPath() {
|
|
if (!this.parentNode) return false;
|
|
if (this.designerId && !this.isCheckedNode) return true;
|
|
return this.parentNode.hasUncheckedNodeInPath;
|
|
}
|
|
|
|
get childDataColumn() {
|
|
if (this.isCheckedColumn) {
|
|
return this.codeName;
|
|
}
|
|
return null;
|
|
}
|
|
|
|
toggleExpanded(value?: boolean) {
|
|
this.includeInNodeSet('expandedColumns', value == null ? !this.isExpanded : value);
|
|
}
|
|
|
|
toggleChecked(value?: boolean) {
|
|
this.includeInNodeSet('checkedColumns', value == null ? !this.isChecked : value);
|
|
}
|
|
|
|
toggleCheckedNode(value?: boolean) {
|
|
this.setConfig(cfg => {
|
|
const oldCheckedValue = cfg.nodes.find(x => x.designerId == this.designerId)?.isNodeChecked;
|
|
const [cfgChanged, nodeCfg] = this.ensureNodeConfig(cfg);
|
|
const res = {
|
|
...cfgChanged,
|
|
nodes: cfgChanged.nodes.map(node =>
|
|
node.designerId == (this.designerId || nodeCfg.designerId)
|
|
? {
|
|
...node,
|
|
isNodeChecked: value == null ? !oldCheckedValue : value,
|
|
}
|
|
: node
|
|
),
|
|
};
|
|
return res;
|
|
});
|
|
}
|
|
|
|
toggleSecondaryChecked(value?: boolean) {}
|
|
|
|
createReferenceConfigColumns(): PerspectiveReferenceConfig['columns'] {
|
|
return null;
|
|
}
|
|
|
|
ensureNodeConfig(cfg: PerspectiveConfig): [PerspectiveConfig, PerspectiveNodeConfig] {
|
|
let node = cfg.nodes.find(x => x.designerId == this.designerId);
|
|
if (!node) {
|
|
const nodeConfig: PerspectiveNodeConfig = {
|
|
...createPerspectiveNodeConfig(this.namedObject),
|
|
isAutoGenerated: true,
|
|
conid: this.parentNodeConfig?.conid,
|
|
database: this.parentNodeConfig?.database,
|
|
};
|
|
const refConfig: PerspectiveReferenceConfig = {
|
|
designerId: uuidv1(),
|
|
sourceId: this.parentNode.designerId,
|
|
targetId: nodeConfig.designerId,
|
|
isAutoGenerated: true,
|
|
columns: this.createReferenceConfigColumns(),
|
|
};
|
|
return [
|
|
{
|
|
...cfg,
|
|
nodes: [...cfg.nodes, nodeConfig],
|
|
references: [...cfg.references, refConfig],
|
|
},
|
|
nodeConfig,
|
|
];
|
|
}
|
|
return [cfg, node];
|
|
}
|
|
|
|
includeInNodeSet(field: 'expandedColumns' | 'uncheckedColumns' | 'checkedColumns', isIncluded: boolean) {
|
|
this.setConfig(cfg => {
|
|
const changedFields = n => ({
|
|
...n,
|
|
[field]: isIncluded ? [...(n[field] || []), this.codeName] : (n[field] || []).filter(x => x != this.codeName),
|
|
});
|
|
|
|
const [cfgChanged, nodeCfg] = this.parentNode?.tableNodeOrParent?.ensureNodeConfig(cfg);
|
|
|
|
const res = {
|
|
...cfgChanged,
|
|
nodes: cfgChanged.nodes.map(n =>
|
|
n.designerId == (this.parentNode?.tableNodeOrParent?.designerId || nodeCfg?.designerId) ? changedFields(n) : n
|
|
),
|
|
};
|
|
return res;
|
|
});
|
|
}
|
|
|
|
getFilter() {
|
|
return this.parentNodeConfig?.filters?.[this.codeName];
|
|
}
|
|
|
|
getDataLoadColumns() {
|
|
return _compact(
|
|
_uniq([
|
|
...this.childNodes.map(x => x.childDataColumn),
|
|
..._flatten(this.childNodes.filter(x => x.isExpandable && x.isChecked).map(x => x.getChildMatchColumns())),
|
|
...this.getParentMatchColumns(),
|
|
...this.childNodes
|
|
.filter(x => x instanceof PerspectivePatternColumnNode)
|
|
.filter(x => this.nodeConfig?.checkedColumns?.find(y => y.startsWith(x.codeName + '::')))
|
|
.map(x => x.columnName),
|
|
])
|
|
);
|
|
}
|
|
|
|
getMutliColumnCondition(source): Condition {
|
|
if (!this.nodeConfig?.multiColumnFilter) return null;
|
|
|
|
const base = this.getBaseTableFromThis() as TableInfo | ViewInfo | CollectionInfo;
|
|
if (!base) return null;
|
|
|
|
const isDocDb = isCollectionInfo(base);
|
|
if (isDocDb) {
|
|
return this.getMutliColumnNoSqlCondition();
|
|
} else {
|
|
return this.getMutliColumnSqlCondition(source);
|
|
}
|
|
}
|
|
|
|
getMutliColumnSqlCondition(source): Condition {
|
|
if (!this.nodeConfig?.multiColumnFilter) return null;
|
|
|
|
const base = this.getBaseTableFromThis() as TableInfo | ViewInfo;
|
|
if (!base) return null;
|
|
try {
|
|
const condition = parseFilter(this.nodeConfig?.multiColumnFilter, stringFilterBehaviour);
|
|
if (condition) {
|
|
const orCondition: CompoudCondition = {
|
|
conditionType: 'or',
|
|
conditions: [],
|
|
};
|
|
for (const column of base.columns || []) {
|
|
orCondition.conditions.push(
|
|
_cloneDeepWith(condition, (expr: Expression) => {
|
|
if (expr.exprType == 'placeholder') {
|
|
return {
|
|
exprType: 'column',
|
|
alias: source,
|
|
columnName: column.columnName,
|
|
};
|
|
}
|
|
})
|
|
);
|
|
}
|
|
if (orCondition.conditions.length > 0) {
|
|
return orCondition;
|
|
}
|
|
}
|
|
} catch (err) {
|
|
console.warn(err.message);
|
|
}
|
|
return null;
|
|
}
|
|
|
|
getMutliColumnNoSqlCondition(): Condition {
|
|
if (!this.nodeConfig?.multiColumnFilter) return null;
|
|
const pattern = this.dataProvider?.dataPatterns?.[this.designerId];
|
|
if (!pattern) return null;
|
|
|
|
const condition = parseFilter(this.nodeConfig?.multiColumnFilter, mongoFilterBehaviour);
|
|
if (!condition) return null;
|
|
|
|
const orCondition: CompoudCondition = {
|
|
conditionType: 'or',
|
|
conditions: [],
|
|
};
|
|
for (const column of pattern.columns || []) {
|
|
orCondition.conditions.push(
|
|
_cloneDeepWith(condition, (expr: Expression) => {
|
|
if (expr.exprType == 'placeholder') {
|
|
return {
|
|
exprType: 'column',
|
|
columnName: column.name,
|
|
};
|
|
}
|
|
})
|
|
);
|
|
}
|
|
if (orCondition.conditions.length > 0) {
|
|
return orCondition;
|
|
}
|
|
}
|
|
|
|
getChildrenSqlCondition(source = null): Condition {
|
|
const conditions = _compact([
|
|
...this.childNodes.map(x => x.parseFilterCondition(source)),
|
|
...this.buildParentFilterConditions(),
|
|
this.getMutliColumnCondition(source),
|
|
]);
|
|
if (conditions.length == 0) {
|
|
return null;
|
|
}
|
|
if (conditions.length == 1) {
|
|
return conditions[0];
|
|
}
|
|
return {
|
|
conditionType: 'and',
|
|
conditions,
|
|
};
|
|
}
|
|
|
|
getOrderBy(table: TableInfo | ViewInfo | CollectionInfo): PerspectiveDataLoadProps['orderBy'] {
|
|
const res = _compact(
|
|
this.childNodes.map(node => {
|
|
const sort = this.nodeConfig?.sort?.find(x => x.columnName == node.columnName);
|
|
if (sort) {
|
|
return {
|
|
columnName: node.columnName,
|
|
order: sort.order,
|
|
};
|
|
}
|
|
})
|
|
);
|
|
if (res.length > 0) return res;
|
|
const pkColumns = (table as TableInfo)?.primaryKey?.columns.map(x => ({
|
|
columnName: x.columnName,
|
|
order: 'ASC' as 'ASC',
|
|
}));
|
|
if (pkColumns) return pkColumns;
|
|
const uqColumns = (table as CollectionInfo)?.uniqueKey;
|
|
if (uqColumns?.length >= 1) return uqColumns.map(x => ({ columnName: x.columnName, order: 'ASC' }));
|
|
const columns = (table as TableInfo | ViewInfo)?.columns;
|
|
if (columns) return [{ columnName: columns[0].columnName, order: 'ASC' }];
|
|
return [{ columnName: '_id', order: 'ASC' }];
|
|
}
|
|
|
|
getBaseTables() {
|
|
const res = [];
|
|
const table = this.getBaseTableFromThis();
|
|
if (table) res.push({ table, node: this });
|
|
for (const child of this.childNodes) {
|
|
if (!child.isChecked) continue;
|
|
res.push(...child.getBaseTables());
|
|
}
|
|
return res;
|
|
}
|
|
getBaseTableFromThis(): TableInfo | ViewInfo | CollectionInfo {
|
|
return null;
|
|
}
|
|
|
|
get filterInfo(): PerspectiveFilterColumnInfo {
|
|
return null;
|
|
}
|
|
|
|
findChildNodeByUniquePath(uniquePath: string[]) {
|
|
if (uniquePath.length == 0) {
|
|
return this;
|
|
}
|
|
const child = this.childNodes.find(x => x.codeName == uniquePath[0]);
|
|
return child?.findChildNodeByUniquePath(uniquePath.slice(1));
|
|
}
|
|
|
|
// findNodeByUniqueName(uniqueName: string): PerspectiveTreeNode {
|
|
// if (!uniqueName) return null;
|
|
// const uniquePath = uniqueName.split('::');
|
|
// if (uniquePath[0] != this.codeName) return null;
|
|
// return this.findChildNodeByUniquePath(uniquePath.slice(1));
|
|
// }
|
|
|
|
findNodeByDesignerId(designerId: string): PerspectiveTreeNode {
|
|
if (!this.designerId && !this.findByDesignerIdWithoutDesignerId) {
|
|
return null;
|
|
}
|
|
if (!designerId) {
|
|
return null;
|
|
}
|
|
if (designerId == this.designerId) {
|
|
return this;
|
|
}
|
|
|
|
for (const child of this.childNodes) {
|
|
const res = child.findNodeByDesignerId(designerId);
|
|
if (res) {
|
|
return res;
|
|
}
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
get supportsParentFilter() {
|
|
return (
|
|
(this.parentNode?.isRoot || this.parentNode?.supportsParentFilter) &&
|
|
this.parentNode?.databaseConfig?.conid == this.databaseConfig?.conid &&
|
|
this.parentNode?.databaseConfig?.database == this.databaseConfig?.database &&
|
|
this.engineType == 'sqldb' &&
|
|
this.parentNode?.engineType == 'sqldb'
|
|
);
|
|
}
|
|
|
|
get isParentFilter() {
|
|
return !!this.nodeConfig?.isParentFilter;
|
|
}
|
|
|
|
buildParentFilterConditions(): Condition[] {
|
|
const leafNodes = _compact(
|
|
(this.config?.nodes || [])
|
|
.filter(x => x.isParentFilter)
|
|
.map(x => this.rootNode.findNodeByDesignerId(x.designerId))
|
|
);
|
|
const conditions: Condition[] = _compact(
|
|
leafNodes.map(leafNode => {
|
|
if (leafNode == this) return null;
|
|
const select: Select = {
|
|
commandType: 'select',
|
|
from: {
|
|
name: leafNode.namedObject,
|
|
alias: 'pert_0',
|
|
relations: [],
|
|
},
|
|
selectAll: true,
|
|
};
|
|
let lastNode = leafNode;
|
|
let node = leafNode;
|
|
let index = 1;
|
|
let lastAlias = 'pert_0';
|
|
while (node?.parentNode && node?.parentNode?.designerId != this?.designerId) {
|
|
node = node.parentNode;
|
|
let alias = `pert_${index}`;
|
|
select.from.relations.push({
|
|
joinType: 'INNER JOIN',
|
|
alias,
|
|
name: node.namedObject,
|
|
conditions: lastNode.getParentJoinCondition(lastAlias, alias),
|
|
});
|
|
lastAlias = alias;
|
|
lastNode = node;
|
|
index += 1;
|
|
}
|
|
if (node?.parentNode?.designerId != this?.designerId) return null;
|
|
select.where = {
|
|
conditionType: 'and',
|
|
conditions: _compact([
|
|
...lastNode.getParentJoinCondition(lastAlias, this.namedObject.pureName),
|
|
leafNode.getChildrenSqlCondition({ alias: 'pert_0' }),
|
|
]),
|
|
};
|
|
|
|
return {
|
|
conditionType: 'exists',
|
|
subQuery: select,
|
|
};
|
|
})
|
|
);
|
|
return conditions;
|
|
}
|
|
|
|
getParentJoinCondition(alias: string, parentAlias: string): Condition[] {
|
|
return [];
|
|
}
|
|
|
|
get sortOrder() {
|
|
return this.parentNodeConfig?.sort?.find(x => x.columnName == this.columnName)?.order;
|
|
}
|
|
|
|
get sortOrderIndex() {
|
|
return this.parentNodeConfig?.sort?.length > 1
|
|
? _findIndex(this.parentNodeConfig?.sort, x => x.columnName == this.columnName)
|
|
: -1;
|
|
}
|
|
}
|
|
|
|
export class PerspectiveTableColumnNode extends PerspectiveTreeNode {
|
|
foreignKey: ForeignKeyInfo;
|
|
refTable: TableInfo;
|
|
isView: boolean;
|
|
isTable: boolean;
|
|
constructor(
|
|
public column: ColumnInfo,
|
|
public table: TableInfo | ViewInfo,
|
|
dbs: MultipleDatabaseInfo,
|
|
config: PerspectiveConfig,
|
|
setConfig: ChangePerspectiveConfigFunc,
|
|
dataProvider: PerspectiveDataProvider,
|
|
defaultDatabaseConfig: PerspectiveDatabaseConfig,
|
|
parentNode: PerspectiveTreeNode,
|
|
designerId: string
|
|
) {
|
|
super(dbs, config, setConfig, parentNode, dataProvider, defaultDatabaseConfig, designerId);
|
|
|
|
this.isTable = !!this.db?.tables?.find(x => x.schemaName == table.schemaName && x.pureName == table.pureName);
|
|
this.isView = !!this.db?.views?.find(x => x.schemaName == table.schemaName && x.pureName == table.pureName);
|
|
|
|
this.foreignKey = (table as TableInfo)?.foreignKeys?.find(
|
|
fk => fk.columns.length == 1 && fk.columns[0].columnName == column.columnName
|
|
);
|
|
|
|
this.refTable = this.db.tables.find(
|
|
x => x.pureName == this.foreignKey?.refTableName && x.schemaName == this.foreignKey?.refSchemaName
|
|
);
|
|
}
|
|
|
|
get engineType() {
|
|
return this.parentNode.engineType;
|
|
}
|
|
|
|
matchChildRow(parentRow: any, childRow: any): boolean {
|
|
if (!this.foreignKey) return false;
|
|
return parentRow[this.foreignKey.columns[0].columnName] == childRow[this.foreignKey.columns[0].refColumnName];
|
|
}
|
|
|
|
getChildMatchColumns() {
|
|
if (!this.foreignKey) return [];
|
|
return [this.foreignKey.columns[0].columnName];
|
|
}
|
|
|
|
getParentMatchColumns() {
|
|
if (!this.foreignKey) return [];
|
|
return [this.foreignKey.columns[0].refColumnName];
|
|
}
|
|
|
|
getParentJoinCondition(alias: string, parentAlias: string): Condition[] {
|
|
if (!this.foreignKey) return [];
|
|
return this.foreignKey.columns.map(column => {
|
|
const res: Condition = {
|
|
conditionType: 'binary',
|
|
operator: '=',
|
|
left: {
|
|
exprType: 'column',
|
|
columnName: column.columnName,
|
|
source: { alias: parentAlias },
|
|
},
|
|
right: {
|
|
exprType: 'column',
|
|
columnName: column.refColumnName,
|
|
source: { alias },
|
|
},
|
|
};
|
|
return res;
|
|
});
|
|
}
|
|
|
|
createReferenceConfigColumns(): PerspectiveReferenceConfig['columns'] {
|
|
return this.foreignKey?.columns?.map(col => ({
|
|
source: col.columnName,
|
|
target: col.refColumnName,
|
|
}));
|
|
}
|
|
|
|
getNodeLoadProps(parentRows: any[]): PerspectiveDataLoadProps {
|
|
if (!this.foreignKey) return null;
|
|
return {
|
|
schemaName: this.foreignKey.refSchemaName,
|
|
pureName: this.foreignKey.refTableName,
|
|
bindingColumns: [this.foreignKey.columns[0].refColumnName],
|
|
bindingValues: _uniqBy(
|
|
parentRows.map(row => [row[this.foreignKey.columns[0].columnName]]),
|
|
stableStringify
|
|
),
|
|
dataColumns: this.getDataLoadColumns(),
|
|
databaseConfig: this.databaseConfig,
|
|
orderBy: this.getOrderBy(this.refTable),
|
|
sqlCondition: this.getChildrenSqlCondition(),
|
|
engineType: 'sqldb',
|
|
};
|
|
}
|
|
|
|
get icon() {
|
|
if (this.isCircular) return 'img circular';
|
|
if (this.column.autoIncrement) return 'img autoincrement';
|
|
if (this.foreignKey) return 'img foreign-key';
|
|
return 'img column';
|
|
}
|
|
|
|
get codeName() {
|
|
return this.column.columnName;
|
|
}
|
|
|
|
get columnName() {
|
|
return this.column.columnName;
|
|
}
|
|
|
|
get fieldName() {
|
|
return this.codeName + 'Ref';
|
|
// return this.codeName ;
|
|
}
|
|
|
|
get title() {
|
|
return this.column.columnName;
|
|
}
|
|
|
|
get isExpandable() {
|
|
return !!this.foreignKey;
|
|
}
|
|
|
|
get isSortable() {
|
|
return true;
|
|
}
|
|
|
|
get filterBehaviour(): FilterBehaviour {
|
|
return detectSqlFilterBehaviour(this.column.dataType);
|
|
}
|
|
|
|
get isCircular() {
|
|
return !!this.parentNode?.parentNode?.hasTableCode(this.tableCode);
|
|
}
|
|
|
|
get isSecondaryChecked() {
|
|
return super.isCheckedColumn;
|
|
}
|
|
get isChecked() {
|
|
if (this.foreignKey) return this.isCheckedNode;
|
|
return super.isCheckedColumn;
|
|
}
|
|
get secondaryCheckable() {
|
|
return !!this.foreignKey;
|
|
}
|
|
toggleChecked(value?: boolean) {
|
|
if (this.foreignKey) {
|
|
this.toggleCheckedNode(value);
|
|
} else {
|
|
super.toggleChecked(value);
|
|
}
|
|
}
|
|
toggleSecondaryChecked(value?: boolean) {
|
|
super.toggleChecked(value == null ? !this.isSecondaryChecked : value);
|
|
}
|
|
|
|
generateChildNodes(): PerspectiveTreeNode[] {
|
|
if (!this.foreignKey) return [];
|
|
const tbl = this?.db?.tables?.find(
|
|
x => x.pureName == this.foreignKey?.refTableName && x.schemaName == this.foreignKey?.refSchemaName
|
|
);
|
|
|
|
return getTableChildPerspectiveNodes(
|
|
tbl,
|
|
this.dbs,
|
|
this.config,
|
|
this.setConfig,
|
|
this.dataProvider,
|
|
this.defaultDatabaseConfig,
|
|
this
|
|
);
|
|
}
|
|
|
|
getBaseTableFromThis() {
|
|
return this.refTable;
|
|
}
|
|
|
|
get filterInfo(): PerspectiveFilterColumnInfo {
|
|
return {
|
|
columnName: this.columnName,
|
|
filterBehaviour: this.filterBehaviour,
|
|
pureName: this.column.pureName,
|
|
schemaName: this.column.schemaName,
|
|
foreignKey: this.foreignKey,
|
|
};
|
|
}
|
|
|
|
parseFilterCondition(source = null): Condition {
|
|
const filter = this.getFilter();
|
|
if (!filter) return null;
|
|
const condition = parseFilter(filter, this.filterBehaviour);
|
|
if (!condition) return null;
|
|
return _cloneDeepWith(condition, (expr: Expression) => {
|
|
if (expr.exprType == 'placeholder') {
|
|
return {
|
|
exprType: 'column',
|
|
columnName: this.column.columnName,
|
|
source,
|
|
};
|
|
}
|
|
});
|
|
}
|
|
|
|
get headerTableAttributes() {
|
|
if (this.foreignKey) {
|
|
return {
|
|
schemaName: this.foreignKey.refSchemaName,
|
|
pureName: this.foreignKey.refTableName,
|
|
conid: this.databaseConfig.conid,
|
|
database: this.databaseConfig.database,
|
|
objectTypeField: this.table.objectTypeField,
|
|
};
|
|
}
|
|
return null;
|
|
}
|
|
|
|
get tableCode() {
|
|
if (this.foreignKey) {
|
|
return `${this.foreignKey.refSchemaName}|${this.foreignKey.refTableName}`;
|
|
}
|
|
return `${this.table.schemaName}|${this.table.pureName}`;
|
|
}
|
|
|
|
get namedObject(): NamedObjectInfo {
|
|
if (this.foreignKey) {
|
|
return {
|
|
schemaName: this.foreignKey.refSchemaName,
|
|
pureName: this.foreignKey.refTableName,
|
|
};
|
|
}
|
|
return null;
|
|
}
|
|
}
|
|
|
|
export class PerspectivePatternColumnNode extends PerspectiveTreeNode {
|
|
foreignKey: ForeignKeyInfo;
|
|
refTable: TableInfo;
|
|
|
|
constructor(
|
|
public table: TableInfo | ViewInfo | CollectionInfo,
|
|
public column: PerspectiveDataPatternColumn,
|
|
public tableColumn: ColumnInfo,
|
|
dbs: MultipleDatabaseInfo,
|
|
config: PerspectiveConfig,
|
|
setConfig: ChangePerspectiveConfigFunc,
|
|
dataProvider: PerspectiveDataProvider,
|
|
defaultDatabaseConfig: PerspectiveDatabaseConfig,
|
|
parentNode: PerspectiveTreeNode,
|
|
designerId: string
|
|
) {
|
|
super(dbs, config, setConfig, parentNode, dataProvider, defaultDatabaseConfig, designerId);
|
|
this.parentNodeConfig = this.tableNodeOrParent?.nodeConfig;
|
|
// console.log('PATTERN COLUMN', column);
|
|
}
|
|
|
|
get isChildColumn() {
|
|
return this.parentNode instanceof PerspectivePatternColumnNode;
|
|
}
|
|
|
|
get findByDesignerIdWithoutDesignerId() {
|
|
return this.isExpandable;
|
|
}
|
|
|
|
// matchChildRow(parentRow: any, childRow: any): boolean {
|
|
// console.log('MATCH PATTENR ROW', parentRow, childRow);
|
|
// return false;
|
|
// // if (!this.foreignKey) return false;
|
|
// // return parentRow[this.foreignKey.columns[0].columnName] == childRow[this.foreignKey.columns[0].refColumnName];
|
|
// }
|
|
|
|
// getChildMatchColumns() {
|
|
// if (!this.foreignKey) return [];
|
|
// return [this.foreignKey.columns[0].columnName];
|
|
// }
|
|
|
|
// getParentMatchColumns() {
|
|
// if (!this.foreignKey) return [];
|
|
// return [this.foreignKey.columns[0].refColumnName];
|
|
// }
|
|
|
|
// getParentJoinCondition(alias: string, parentAlias: string): Condition[] {
|
|
// if (!this.foreignKey) return [];
|
|
// return this.foreignKey.columns.map(column => {
|
|
// const res: Condition = {
|
|
// conditionType: 'binary',
|
|
// operator: '=',
|
|
// left: {
|
|
// exprType: 'column',
|
|
// columnName: column.columnName,
|
|
// source: { alias: parentAlias },
|
|
// },
|
|
// right: {
|
|
// exprType: 'column',
|
|
// columnName: column.refColumnName,
|
|
// source: { alias },
|
|
// },
|
|
// };
|
|
// return res;
|
|
// });
|
|
// }
|
|
|
|
// createReferenceConfigColumns(): PerspectiveReferenceConfig['columns'] {
|
|
// return this.foreignKey?.columns?.map(col => ({
|
|
// source: col.columnName,
|
|
// target: col.refColumnName,
|
|
// }));
|
|
// }
|
|
|
|
getNodeLoadProps(parentRows: any[]): PerspectiveDataLoadProps {
|
|
// console.log('GETTING PATTERN', parentRows);
|
|
return null;
|
|
}
|
|
|
|
get generatesHiearchicGridColumn(): boolean {
|
|
// return true;
|
|
// console.log('generatesHiearchicGridColumn', this.parentTableNode?.nodeConfig?.checkedColumns, this.codeName + '::');
|
|
if (this.tableNodeOrParent?.nodeConfig?.checkedColumns?.find(x => x.startsWith(this.codeName + '::'))) {
|
|
return true;
|
|
}
|
|
// return false;
|
|
|
|
return this.hasCheckedJoinChild();
|
|
}
|
|
|
|
// get generatesHiearchicGridColumn() {
|
|
// // return this.config &&;
|
|
// }
|
|
|
|
get icon() {
|
|
if (this.column.types.includes('json')) {
|
|
return 'img json';
|
|
}
|
|
return 'img column';
|
|
}
|
|
|
|
get codeName() {
|
|
if (this.parentNode instanceof PerspectivePatternColumnNode) {
|
|
return `${this.parentNode.codeName}::${this.column.name}`;
|
|
}
|
|
return this.column.name;
|
|
}
|
|
|
|
get columnName() {
|
|
return this.column.name;
|
|
}
|
|
|
|
get fieldName() {
|
|
return this.column.name;
|
|
}
|
|
|
|
get title() {
|
|
return this.column.name;
|
|
}
|
|
|
|
get isExpandable() {
|
|
return this.column.columns.length > 0;
|
|
}
|
|
|
|
get isSortable() {
|
|
return !this.isChildColumn;
|
|
}
|
|
|
|
get filterBehaviour(): FilterBehaviour {
|
|
if (this.tableColumn) return detectSqlFilterBehaviour(this.tableColumn.dataType);
|
|
return mongoFilterBehaviour;
|
|
}
|
|
|
|
get preloadedLevelData() {
|
|
return true;
|
|
}
|
|
|
|
generatePatternChildNodes(): PerspectivePatternColumnNode[] {
|
|
return this.column.columns.map(
|
|
column =>
|
|
new PerspectivePatternColumnNode(
|
|
this.table,
|
|
column,
|
|
this.tableColumn,
|
|
this.dbs,
|
|
this.config,
|
|
this.setConfig,
|
|
this.dataProvider,
|
|
this.defaultDatabaseConfig,
|
|
this,
|
|
null
|
|
)
|
|
);
|
|
}
|
|
|
|
hasCheckedJoinChild() {
|
|
for (const node of this.childNodes) {
|
|
if (node instanceof PerspectivePatternColumnNode) {
|
|
if (node.hasCheckedJoinChild()) return true;
|
|
}
|
|
if (node.isCheckedNode) return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
generateChildNodes(): PerspectiveTreeNode[] {
|
|
const patternChildren = this.generatePatternChildNodes();
|
|
|
|
const customs = [];
|
|
// console.log('GETTING CHILDREN', this.config.nodes, this.config.references);
|
|
for (const node of this.config.nodes) {
|
|
for (const ref of this.config.references) {
|
|
const validDesignerId = this.validParentDesignerId;
|
|
if (
|
|
(ref.sourceId == validDesignerId && ref.targetId == node.designerId) ||
|
|
(ref.targetId == validDesignerId && ref.sourceId == node.designerId)
|
|
) {
|
|
// console.log('TESTING REF', ref, this.codeName);
|
|
if (ref.columns.length != 1) continue;
|
|
// console.log('CP1');
|
|
if (
|
|
ref.sourceId == validDesignerId &&
|
|
this.codeName == getPerspectiveParentColumnName(ref.columns[0].source)
|
|
) {
|
|
if (ref.columns[0].target.includes('::')) continue;
|
|
} else if (
|
|
ref.targetId == validDesignerId &&
|
|
this.codeName == getPerspectiveParentColumnName(ref.columns[0].target)
|
|
) {
|
|
if (ref.columns[0].source.includes('::')) continue;
|
|
} else {
|
|
continue;
|
|
}
|
|
// console.log('CP2');
|
|
|
|
const newConfig: PerspectiveDatabaseConfig = { ...this.defaultDatabaseConfig };
|
|
if (node.conid) newConfig.conid = node.conid;
|
|
if (node.database) newConfig.database = node.database;
|
|
const db = this.dbs?.[newConfig.conid]?.[newConfig.database];
|
|
const table = db?.tables?.find(x => x.pureName == node.pureName && x.schemaName == node.schemaName);
|
|
const view = db?.views?.find(x => x.pureName == node.pureName && x.schemaName == node.schemaName);
|
|
const collection = db?.collections?.find(x => x.pureName == node.pureName && x.schemaName == node.schemaName);
|
|
|
|
const join: PerspectiveCustomJoinConfig = {
|
|
refNodeDesignerId: node.designerId,
|
|
referenceDesignerId: ref.designerId,
|
|
baseDesignerId: validDesignerId,
|
|
joinName: node.alias,
|
|
refTableName: node.pureName,
|
|
refSchemaName: node.schemaName,
|
|
conid: node.conid,
|
|
database: node.database,
|
|
columns:
|
|
ref.sourceId == validDesignerId
|
|
? ref.columns.map(col => ({ baseColumnName: col.source, refColumnName: col.target }))
|
|
: ref.columns.map(col => ({ baseColumnName: col.target, refColumnName: col.source })),
|
|
};
|
|
|
|
if (table || view || collection) {
|
|
customs.push(
|
|
new PerspectiveCustomJoinTreeNode(
|
|
join,
|
|
table || view || collection,
|
|
this.dbs,
|
|
this.config,
|
|
this.setConfig,
|
|
this.dataProvider,
|
|
this.defaultDatabaseConfig,
|
|
this,
|
|
node.designerId
|
|
)
|
|
);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return [...patternChildren, ...customs];
|
|
// return [];
|
|
// if (!this.foreignKey) return [];
|
|
// const tbl = this?.db?.tables?.find(
|
|
// x => x.pureName == this.foreignKey?.refTableName && x.schemaName == this.foreignKey?.refSchemaName
|
|
// );
|
|
|
|
// return getTableChildPerspectiveNodes(
|
|
// tbl,
|
|
// this.dbs,
|
|
// this.config,
|
|
// this.setConfig,
|
|
// this.dataProvider,
|
|
// this.databaseConfig,
|
|
// this
|
|
// );
|
|
}
|
|
|
|
get filterInfo(): PerspectiveFilterColumnInfo {
|
|
if (this.isChildColumn) {
|
|
return null;
|
|
}
|
|
|
|
return {
|
|
columnName: this.columnName,
|
|
filterBehaviour: this.filterBehaviour,
|
|
pureName: this.table.pureName,
|
|
schemaName: this.table.schemaName,
|
|
foreignKey: this.foreignKey,
|
|
};
|
|
}
|
|
|
|
parseFilterCondition(source = null): {} {
|
|
const filter = this.getFilter();
|
|
if (!filter) return null;
|
|
const condition = parseFilter(filter, mongoFilterBehaviour);
|
|
if (!condition) return null;
|
|
return _cloneDeepWith(condition, expr => {
|
|
if (expr.__placeholder__) {
|
|
return {
|
|
[this.columnName]: expr.__placeholder__,
|
|
};
|
|
}
|
|
});
|
|
}
|
|
|
|
// get headerTableAttributes() {
|
|
// if (this.foreignKey) {
|
|
// return {
|
|
// schemaName: this.foreignKey.refSchemaName,
|
|
// pureName: this.foreignKey.refTableName,
|
|
// conid: this.databaseConfig.conid,
|
|
// database: this.databaseConfig.database,
|
|
// };
|
|
// }
|
|
// return null;
|
|
// }
|
|
|
|
// get tableCode() {
|
|
// return `${this.collection.schemaName}|${this.table.pureName}`;
|
|
// }
|
|
|
|
// get namedObject(): NamedObjectInfo {
|
|
// if (this.foreignKey) {
|
|
// return {
|
|
// schemaName: this.foreignKey.refSchemaName,
|
|
// pureName: this.foreignKey.refTableName,
|
|
// };
|
|
// }
|
|
// return null;
|
|
// }
|
|
}
|
|
|
|
export class PerspectiveTableNode extends PerspectiveTreeNode {
|
|
constructor(
|
|
public table: TableInfo | ViewInfo | CollectionInfo,
|
|
dbs: MultipleDatabaseInfo,
|
|
config: PerspectiveConfig,
|
|
setConfig: ChangePerspectiveConfigFunc,
|
|
public dataProvider: PerspectiveDataProvider,
|
|
defaultDatabaseConfig: PerspectiveDatabaseConfig,
|
|
parentNode: PerspectiveTreeNode,
|
|
designerId: string
|
|
) {
|
|
super(dbs, config, setConfig, parentNode, dataProvider, defaultDatabaseConfig, designerId);
|
|
}
|
|
|
|
get engineType(): PerspectiveDatabaseEngineType {
|
|
return isCollectionInfo(this.table) ? 'docdb' : 'sqldb';
|
|
}
|
|
|
|
getNodeLoadProps(parentRows: any[]): PerspectiveDataLoadProps {
|
|
const isDocDb = isCollectionInfo(this.table);
|
|
return {
|
|
schemaName: this.table.schemaName,
|
|
pureName: this.table.pureName,
|
|
dataColumns: this.getDataLoadColumns(),
|
|
allColumns: isDocDb,
|
|
databaseConfig: this.databaseConfig,
|
|
orderBy: this.getOrderBy(this.table),
|
|
sqlCondition: this.getChildrenSqlCondition(),
|
|
engineType: isDocDb ? 'docdb' : 'sqldb',
|
|
};
|
|
}
|
|
|
|
get codeName() {
|
|
return this.table.schemaName ? `${this.table.schemaName}:${this.table.pureName}` : this.table.pureName;
|
|
}
|
|
|
|
get title() {
|
|
return this.nodeConfig?.alias || this.table.pureName;
|
|
}
|
|
|
|
get isExpandable() {
|
|
return true;
|
|
}
|
|
|
|
generateChildNodes(): PerspectiveTreeNode[] {
|
|
return getTableChildPerspectiveNodes(
|
|
this.table,
|
|
this.dbs,
|
|
this.config,
|
|
this.setConfig,
|
|
this.dataProvider,
|
|
this.defaultDatabaseConfig,
|
|
this
|
|
);
|
|
}
|
|
|
|
get icon() {
|
|
return 'img table';
|
|
}
|
|
|
|
getBaseTableFromThis() {
|
|
return this.table;
|
|
}
|
|
|
|
get headerTableAttributes() {
|
|
return {
|
|
schemaName: this.table.schemaName,
|
|
pureName: this.table.pureName,
|
|
conid: this.databaseConfig.conid,
|
|
database: this.databaseConfig.database,
|
|
objectTypeField: this.table.objectTypeField,
|
|
};
|
|
}
|
|
|
|
get tableCode() {
|
|
return `${this.table.schemaName}|${this.table.pureName}`;
|
|
}
|
|
|
|
get namedObject(): NamedObjectInfo {
|
|
return {
|
|
schemaName: this.table.schemaName,
|
|
pureName: this.table.pureName,
|
|
};
|
|
}
|
|
}
|
|
export class PerspectiveTableReferenceNode extends PerspectiveTableNode {
|
|
constructor(
|
|
public foreignKey: ForeignKeyInfo,
|
|
table: TableInfo,
|
|
dbs: MultipleDatabaseInfo,
|
|
config: PerspectiveConfig,
|
|
setConfig: ChangePerspectiveConfigFunc,
|
|
public dataProvider: PerspectiveDataProvider,
|
|
defaultDatabaseConfig: PerspectiveDatabaseConfig,
|
|
public isMultiple: boolean,
|
|
parentNode: PerspectiveTreeNode,
|
|
designerId: string
|
|
) {
|
|
super(table, dbs, config, setConfig, dataProvider, defaultDatabaseConfig, parentNode, designerId);
|
|
}
|
|
|
|
matchChildRow(parentRow: any, childRow: any): boolean {
|
|
if (!this.foreignKey) return false;
|
|
return parentRow[this.foreignKey.columns[0].refColumnName] == childRow[this.foreignKey.columns[0].columnName];
|
|
}
|
|
|
|
getChildMatchColumns() {
|
|
if (!this.foreignKey) return [];
|
|
return [this.foreignKey.columns[0].refColumnName];
|
|
}
|
|
|
|
getParentMatchColumns() {
|
|
if (!this.foreignKey) return [];
|
|
return [this.foreignKey.columns[0].columnName];
|
|
}
|
|
|
|
getNodeLoadProps(parentRows: any[]): PerspectiveDataLoadProps {
|
|
if (!this.foreignKey) return null;
|
|
return {
|
|
schemaName: this.table.schemaName,
|
|
pureName: this.table.pureName,
|
|
bindingColumns: [this.foreignKey.columns[0].columnName],
|
|
bindingValues: _uniqBy(
|
|
parentRows.map(row => [row[this.foreignKey.columns[0].refColumnName]]),
|
|
stableStringify
|
|
),
|
|
dataColumns: this.getDataLoadColumns(),
|
|
databaseConfig: this.databaseConfig,
|
|
orderBy: this.getOrderBy(this.table),
|
|
sqlCondition: this.getChildrenSqlCondition(),
|
|
engineType: 'sqldb',
|
|
};
|
|
}
|
|
|
|
createReferenceConfigColumns(): PerspectiveReferenceConfig['columns'] {
|
|
return this.foreignKey?.columns?.map(col => ({
|
|
source: col.refColumnName,
|
|
target: col.columnName,
|
|
}));
|
|
}
|
|
|
|
get columnTitle() {
|
|
return this.table.pureName;
|
|
}
|
|
|
|
get title() {
|
|
if (this.isMultiple) {
|
|
return `${super.title} (${this.foreignKey.columns.map(x => x.columnName).join(', ')})`;
|
|
}
|
|
return super.title;
|
|
}
|
|
|
|
get codeName() {
|
|
if (this.isMultiple) {
|
|
return `${super.codeName}-${this.foreignKey.columns.map(x => x.columnName).join('_')}`;
|
|
}
|
|
return super.codeName;
|
|
}
|
|
|
|
get isChecked() {
|
|
return this.isCheckedNode;
|
|
}
|
|
|
|
toggleChecked(value?: boolean) {
|
|
this.toggleCheckedNode(value);
|
|
}
|
|
|
|
getParentJoinCondition(alias: string, parentAlias: string): Condition[] {
|
|
if (!this.foreignKey) return [];
|
|
return this.foreignKey.columns.map(column => {
|
|
const res: Condition = {
|
|
conditionType: 'binary',
|
|
operator: '=',
|
|
left: {
|
|
exprType: 'column',
|
|
columnName: column.refColumnName,
|
|
source: { alias: parentAlias },
|
|
},
|
|
right: {
|
|
exprType: 'column',
|
|
columnName: column.columnName,
|
|
source: { alias },
|
|
},
|
|
};
|
|
return res;
|
|
});
|
|
}
|
|
}
|
|
|
|
export class PerspectiveCustomJoinTreeNode extends PerspectiveTableNode {
|
|
constructor(
|
|
public customJoin: PerspectiveCustomJoinConfig,
|
|
table: TableInfo | ViewInfo | CollectionInfo,
|
|
dbs: MultipleDatabaseInfo,
|
|
config: PerspectiveConfig,
|
|
setConfig: ChangePerspectiveConfigFunc,
|
|
public dataProvider: PerspectiveDataProvider,
|
|
defaultDatabaseConfig: PerspectiveDatabaseConfig,
|
|
parentNode: PerspectiveTreeNode,
|
|
designerId: string
|
|
) {
|
|
super(table, dbs, config, setConfig, dataProvider, defaultDatabaseConfig, parentNode, designerId);
|
|
}
|
|
|
|
matchChildRow(parentRow: any, childRow: any): boolean {
|
|
// console.log('MATCH ROW', parentRow, childRow);
|
|
for (const column of this.customJoin.columns) {
|
|
if (
|
|
!perspectiveValueMatcher(
|
|
parentRow[getPerspectiveMostNestedChildColumnName(column.baseColumnName)],
|
|
childRow[column.refColumnName]
|
|
)
|
|
) {
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
getChildMatchColumns() {
|
|
return this.customJoin.columns.map(x => x.baseColumnName);
|
|
}
|
|
|
|
getParentMatchColumns() {
|
|
return this.customJoin.columns.map(x => x.refColumnName);
|
|
}
|
|
|
|
getNodeLoadProps(parentRows: any[]): PerspectiveDataLoadProps {
|
|
// console.log('CUSTOM JOIN', this.customJoin);
|
|
// console.log('PARENT ROWS', parentRows);
|
|
|
|
// console.log('this.getDataLoadColumns()', this.getDataLoadColumns());
|
|
const isDocDb = isCollectionInfo(this.table);
|
|
|
|
// const bindingValues = [];
|
|
|
|
// for (const row of parentRows) {
|
|
// const rowBindingValueArrays = [];
|
|
// for (const col of this.customJoin.columns) {
|
|
// const path = col.baseColumnName.split('::');
|
|
// const values = [];
|
|
|
|
// function processSubpath(parent, subpath) {
|
|
// if (subpath.length == 0) {
|
|
// values.push(parent);
|
|
// return;
|
|
// }
|
|
// if (parent == null) {
|
|
// return;
|
|
// }
|
|
|
|
// const obj = parent[subpath[0]];
|
|
// if (_isArray(obj)) {
|
|
// for (const elem of obj) {
|
|
// processSubpath(elem, subpath.slice(1));
|
|
// }
|
|
// } else {
|
|
// processSubpath(obj, subpath.slice(1));
|
|
// }
|
|
// }
|
|
|
|
// processSubpath(row, path);
|
|
|
|
// rowBindingValueArrays.push(values);
|
|
// }
|
|
|
|
// const valueCount = Math.max(...rowBindingValueArrays.map(x => x.length));
|
|
|
|
// for (let i = 0; i < valueCount; i += 1) {
|
|
// const value = Array(this.customJoin.columns.length);
|
|
// for (let col = 0; col < this.customJoin.columns.length; col++) {
|
|
// value[col] = rowBindingValueArrays[col][i % rowBindingValueArrays[col].length];
|
|
// }
|
|
// bindingValues.push(value);
|
|
// }
|
|
// }
|
|
const bindingValues = parentRows.map(row =>
|
|
this.customJoin.columns.map(x => row[getPerspectiveMostNestedChildColumnName(x.baseColumnName)])
|
|
);
|
|
|
|
// console.log('bindingValues', bindingValues);
|
|
// console.log(
|
|
// 'bindingValues UNIQ',
|
|
// _uniqBy(bindingValues, x => JSON.stringify(x))
|
|
// );
|
|
|
|
return {
|
|
schemaName: this.table.schemaName,
|
|
pureName: this.table.pureName,
|
|
bindingColumns: this.getParentMatchColumns(),
|
|
bindingValues: _uniqBy(bindingValues, x => JSON.stringify(x)),
|
|
dataColumns: this.getDataLoadColumns(),
|
|
allColumns: isDocDb,
|
|
databaseConfig: this.databaseConfig,
|
|
orderBy: this.getOrderBy(this.table),
|
|
sqlCondition: this.getChildrenSqlCondition(),
|
|
// mongoCondition: isMongo ? this.getChildrenMongoCondition() : null,
|
|
engineType: isDocDb ? 'docdb' : 'sqldb',
|
|
};
|
|
}
|
|
|
|
// get title() {
|
|
// return this.customJoin.joinName || this.customJoin.refTableName;
|
|
// }
|
|
|
|
get icon() {
|
|
return 'icon custom-join';
|
|
}
|
|
|
|
get codeName() {
|
|
return this.customJoin.refNodeDesignerId;
|
|
}
|
|
|
|
get customJoinConfig(): PerspectiveCustomJoinConfig {
|
|
return this.customJoin;
|
|
}
|
|
|
|
get isChecked() {
|
|
return this.isCheckedNode;
|
|
}
|
|
|
|
toggleChecked(value?: boolean) {
|
|
this.toggleCheckedNode(value);
|
|
}
|
|
|
|
getParentJoinCondition(alias: string, parentAlias: string): Condition[] {
|
|
return this.customJoin.columns.map(column => {
|
|
const res: Condition = {
|
|
conditionType: 'binary',
|
|
operator: '=',
|
|
left: {
|
|
exprType: 'column',
|
|
columnName: column.baseColumnName,
|
|
source: { alias: parentAlias },
|
|
},
|
|
right: {
|
|
exprType: 'column',
|
|
columnName: column.refColumnName,
|
|
source: { alias },
|
|
},
|
|
};
|
|
return res;
|
|
});
|
|
}
|
|
}
|
|
|
|
function findDesignerIdForNode<T extends PerspectiveTreeNode>(
|
|
config: PerspectiveConfig,
|
|
parentNode: PerspectiveTreeNode,
|
|
nodeCreateFunc: (designerId: string) => T
|
|
): T {
|
|
const node = nodeCreateFunc(null);
|
|
const refColumns = node.createReferenceConfigColumns();
|
|
if (!refColumns?.length) {
|
|
return node;
|
|
}
|
|
const ref1 = config.references.find(
|
|
x =>
|
|
x.sourceId == parentNode.designerId &&
|
|
_isEqual(
|
|
refColumns.map(x => x.source),
|
|
x.columns.map(x => x.source)
|
|
) &&
|
|
_isEqual(
|
|
refColumns.map(x => x.target),
|
|
x.columns.map(x => x.target)
|
|
) &&
|
|
equalFullName(
|
|
config.nodes.find(n => n.designerId == x.targetId),
|
|
node.namedObject
|
|
)
|
|
);
|
|
if (ref1 && !parentNode.hasDesignerIdInIncestors(ref1.targetId)) {
|
|
// console.log('FOUND1', node.title, ref1.targetId, refColumns);
|
|
return nodeCreateFunc(ref1.targetId);
|
|
}
|
|
|
|
const ref2 = config.references.find(
|
|
x =>
|
|
x.targetId == parentNode.designerId &&
|
|
_isEqual(
|
|
refColumns.map(x => x.target),
|
|
x.columns.map(x => x.source)
|
|
) &&
|
|
_isEqual(
|
|
refColumns.map(x => x.source),
|
|
x.columns.map(x => x.target)
|
|
) &&
|
|
equalFullName(
|
|
config.nodes.find(n => n.designerId == x.sourceId),
|
|
node.namedObject
|
|
)
|
|
);
|
|
if (ref2 && !parentNode.hasDesignerIdInIncestors(ref2.sourceId)) {
|
|
// console.log('FOUND2', node.title, ref2.sourceId, refColumns);
|
|
return nodeCreateFunc(ref2.sourceId);
|
|
}
|
|
|
|
return node;
|
|
}
|
|
|
|
export function getTableChildPerspectiveNodes(
|
|
table: TableInfo | ViewInfo | CollectionInfo,
|
|
dbs: MultipleDatabaseInfo,
|
|
config: PerspectiveConfig,
|
|
setConfig: ChangePerspectiveConfigFunc,
|
|
dataProvider: PerspectiveDataProvider,
|
|
defaultDatabaseConfig: PerspectiveDatabaseConfig,
|
|
parentNode: PerspectiveTreeNode
|
|
) {
|
|
if (!table) return [];
|
|
const db = parentNode.db;
|
|
|
|
const pattern = dataProvider?.dataPatterns?.[parentNode.designerId];
|
|
|
|
const tableOrView = isTableInfo(table) || isViewInfo(table) ? table : null;
|
|
|
|
const columnNodes =
|
|
tableOrView?.columns?.map(col =>
|
|
findDesignerIdForNode(config, parentNode, designerId =>
|
|
pattern?.columns?.find(x => x.name == col.columnName)?.types.includes('json')
|
|
? new PerspectivePatternColumnNode(
|
|
table,
|
|
pattern?.columns?.find(x => x.name == col.columnName),
|
|
col,
|
|
dbs,
|
|
config,
|
|
setConfig,
|
|
dataProvider,
|
|
defaultDatabaseConfig,
|
|
parentNode,
|
|
designerId
|
|
)
|
|
: new PerspectiveTableColumnNode(
|
|
col,
|
|
tableOrView,
|
|
dbs,
|
|
config,
|
|
setConfig,
|
|
dataProvider,
|
|
defaultDatabaseConfig,
|
|
parentNode,
|
|
designerId
|
|
)
|
|
)
|
|
) ||
|
|
pattern?.columns?.map(col =>
|
|
findDesignerIdForNode(
|
|
config,
|
|
parentNode,
|
|
designerId =>
|
|
new PerspectivePatternColumnNode(
|
|
table,
|
|
col,
|
|
null,
|
|
dbs,
|
|
config,
|
|
setConfig,
|
|
dataProvider,
|
|
defaultDatabaseConfig,
|
|
parentNode,
|
|
designerId
|
|
)
|
|
)
|
|
) ||
|
|
[];
|
|
// if (!columnNodes.find(x => x.isChecked)) {
|
|
// const circularColumns = columnNodes.filter(x => x.isCircular).map(x => x.columnName);
|
|
// const defaultColumns = getPerspectiveDefaultColumns(table, db, circularColumns);
|
|
// for (const node of columnNodes) {
|
|
// if (defaultColumns.includes(node.columnName)) {
|
|
// no
|
|
// }
|
|
// }
|
|
// }
|
|
|
|
const res = [];
|
|
res.push(...columnNodes);
|
|
const dependencies = [];
|
|
if (db && (table as TableInfo)?.dependencies) {
|
|
for (const fk of (table as TableInfo)?.dependencies) {
|
|
const tbl = db.tables.find(x => x.pureName == fk.pureName && x.schemaName == fk.schemaName);
|
|
if (tbl) {
|
|
const isMultiple =
|
|
(table as TableInfo)?.dependencies.filter(x => x.pureName == fk.pureName && x.schemaName == fk.schemaName)
|
|
.length >= 2;
|
|
dependencies.push(
|
|
findDesignerIdForNode(
|
|
config,
|
|
parentNode,
|
|
designerId =>
|
|
new PerspectiveTableReferenceNode(
|
|
fk,
|
|
tbl,
|
|
dbs,
|
|
config,
|
|
setConfig,
|
|
dataProvider,
|
|
defaultDatabaseConfig,
|
|
isMultiple,
|
|
parentNode,
|
|
designerId
|
|
)
|
|
)
|
|
);
|
|
}
|
|
}
|
|
}
|
|
res.push(..._sortBy(dependencies, 'title'));
|
|
|
|
const customs = [];
|
|
for (const node of config.nodes) {
|
|
if (node.designerId == parentNode.parentNode?.designerId || res.find(x => x.designerId == node.designerId)) {
|
|
// already used as FK
|
|
continue;
|
|
}
|
|
for (const ref of config.references) {
|
|
if (
|
|
(ref.sourceId == parentNode.designerId && ref.targetId == node.designerId) ||
|
|
(ref.targetId == parentNode.designerId && ref.sourceId == node.designerId)
|
|
) {
|
|
if (ref.columns.find(x => x.source.includes('::') || x.target.includes('::'))) {
|
|
continue;
|
|
}
|
|
const newConfig: PerspectiveDatabaseConfig = { ...defaultDatabaseConfig };
|
|
if (node.conid) newConfig.conid = node.conid;
|
|
if (node.database) newConfig.database = node.database;
|
|
const db = dbs?.[newConfig.conid]?.[newConfig.database];
|
|
const table = db?.tables?.find(x => x.pureName == node.pureName && x.schemaName == node.schemaName);
|
|
const view = db?.views?.find(x => x.pureName == node.pureName && x.schemaName == node.schemaName);
|
|
const collection = db?.collections?.find(x => x.pureName == node.pureName && x.schemaName == node.schemaName);
|
|
|
|
const join: PerspectiveCustomJoinConfig = {
|
|
refNodeDesignerId: node.designerId,
|
|
referenceDesignerId: ref.designerId,
|
|
baseDesignerId: parentNode.designerId,
|
|
joinName: node.alias,
|
|
refTableName: node.pureName,
|
|
refSchemaName: node.schemaName,
|
|
conid: node.conid,
|
|
database: node.database,
|
|
columns:
|
|
ref.sourceId == parentNode.designerId
|
|
? ref.columns.map(col => ({ baseColumnName: col.source, refColumnName: col.target }))
|
|
: ref.columns.map(col => ({ baseColumnName: col.target, refColumnName: col.source })),
|
|
};
|
|
|
|
if (table || view || collection) {
|
|
customs.push(
|
|
new PerspectiveCustomJoinTreeNode(
|
|
join,
|
|
table || view || collection,
|
|
dbs,
|
|
config,
|
|
setConfig,
|
|
dataProvider,
|
|
defaultDatabaseConfig,
|
|
parentNode,
|
|
node.designerId
|
|
)
|
|
);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
res.push(..._sortBy(customs, 'title'));
|
|
|
|
return res;
|
|
}
|