UNPKG

@sqb/connect

Version:

Multi-dialect database connection framework written with TypeScript

171 lines (170 loc) 8.9 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.joinAssociationGetFirst = joinAssociationGetFirst; exports.joinAssociationGetLast = joinAssociationGetLast; exports.joinAssociation = joinAssociation; exports.prepareFilter = prepareFilter; const builder_1 = require("@sqb/builder"); const embedded_field_metadata_js_1 = require("../model/embedded-field-metadata.js"); const entity_metadata_js_1 = require("../model/entity-metadata.js"); const orm_helper_js_1 = require("../util/orm.helper.js"); async function joinAssociationGetFirst(joinInfos, association, parentAlias, innerJoin) { const joins = await joinAssociation(joinInfos, association, parentAlias, innerJoin); return joins[0]; } async function joinAssociationGetLast(joinInfos, association, parentAlias, innerJoin) { const joins = await joinAssociation(joinInfos, association, parentAlias, innerJoin); return joins[joins.length - 1]; } async function joinAssociation(joinInfos, association, parentAlias, innerJoin) { let joinInfo; let node = association; const result = []; while (node) { joinInfo = joinInfos.find(j => j.association === node && j.parentAlias === parentAlias); if (!joinInfo) { const targetEntity = await node.resolveTarget(); const sourceEntity = await node.resolveSource(); const keyCol = await node.resolveSourceProperty(); const targetCol = await node.resolveTargetProperty(); const joinAlias = 'J' + (joinInfos.length + 1); const join = innerJoin ? (0, builder_1.InnerJoin)(targetEntity.tableName + ' as ' + joinAlias) : (0, builder_1.LeftOuterJoin)(targetEntity.tableName + ' as ' + joinAlias); join.on((0, builder_1.Eq)((0, builder_1.Field)(joinAlias + '.' + targetCol.fieldName, targetCol.dataType, targetCol.isArray), (0, builder_1.Field)(parentAlias + '.' + keyCol.fieldName, keyCol.dataType, keyCol.isArray))); if (node.conditions) await prepareFilter(targetEntity, node.conditions, join._conditions, joinAlias); joinInfo = { association: node, sourceEntity, targetEntity, parentAlias, joinAlias, join, }; joinInfos.push(joinInfo); } result.push(joinInfo); if (!node.next) break; parentAlias = joinInfo.joinAlias; node = node.next; } return result; } async function prepareFilter(entityDef, filter, trgOp, tableAlias = 'T') { let srcOp; if ((0, builder_1.isLogicalOperator)(filter) && filter._operatorType === trgOp._operatorType) srcOp = filter; else { srcOp = (0, builder_1.And)(); if (Array.isArray(filter)) srcOp.add(...filter); else srcOp.add(filter); } // const associationPathCache: Record<string, any> = {}; for (const item of srcOp._items) { if ((0, builder_1.isLogicalOperator)(item)) { const ctor = Object.getPrototypeOf(item).constructor; const logOp = new ctor(); await prepareFilter(entityDef, item, logOp, tableAlias); trgOp.add(logOp); continue; } if ((0, builder_1.isCompOperator)(item)) { if (typeof item._left === 'string') { const itemPath = item._left.split('.'); const l = itemPath.length; let pt; let _curEntity = entityDef; let _curAlias = tableAlias; let _curPrefix = ''; let _curSuffix = ''; let subSelect; let currentOp = trgOp; let i = 0; // const parentPath = itemPath // .slice(0, l - 2) // .join('.') // .toLowerCase(); // const cached = associationPathCache[parentPath]; // if (cached) { // /** Jump to last item */ // i = l - 1; // _curEntity = cached._curEntity; // _curAlias = cached._curAlias; // currentOp = cached.currentOp; // } for (i; i < l; i++) { pt = itemPath[i]; const col = entity_metadata_js_1.EntityMetadata.getField(_curEntity, pt); if (!col) throw new Error(`Unknown property (${item._left}) defined in filter`); /** if last item on path */ if (i === l - 1) { if (!(0, orm_helper_js_1.isColumnField)(col)) throw new Error(`Invalid column expression (${item._left}) defined in filter`); const ctor = Object.getPrototypeOf(item).constructor; currentOp.add(new ctor((0, builder_1.Field)(_curAlias + '.' + _curPrefix + col.fieldName + _curSuffix, col.dataType, col.isArray), item._right)); } else { /** if not last item on path */ if ((0, orm_helper_js_1.isColumnField)(col)) throw new Error(`Invalid column (${item._left}) defined in filter`); if ((0, orm_helper_js_1.isEmbeddedField)(col)) { _curEntity = await embedded_field_metadata_js_1.EmbeddedFieldMetadata.resolveType(col); _curPrefix = _curPrefix + (col.fieldNamePrefix || ''); _curSuffix = (col.fieldNameSuffix || '') + _curSuffix; continue; } if (!(0, orm_helper_js_1.isAssociationField)(col)) throw new Error(`Invalid column (${item._left}) defined in filter`); let node; _curEntity = await col.association.resolveTarget(); if (!subSelect) { const keyCol = await col.association.resolveSourceProperty(); const targetCol = await col.association.resolveTargetProperty(); subSelect = (0, builder_1.Select)((0, builder_1.Raw)('1')).from(_curEntity.tableName + ' K'); subSelect.where((0, builder_1.Eq)((0, builder_1.Field)('K.' + targetCol.fieldName, targetCol.dataType, targetCol.isArray), (0, builder_1.Field)(tableAlias + '.' + keyCol.fieldName, keyCol.dataType, keyCol.isArray))); currentOp.add((0, builder_1.Exists)(subSelect)); if (currentOp._operatorType !== builder_1.OperatorType.and) { currentOp = builder_1.op.or(); subSelect.where(currentOp); } else currentOp = subSelect._where; if (col.association.conditions) { await prepareFilter(_curEntity, col.association.conditions, trgOp, 'K'); } node = col.association.next; _curAlias = 'K'; } else node = col.association; while (node) { const targetEntity = await node.resolveTarget(); const sourceColumn = await node.resolveSourceProperty(); const targetColumn = await node.resolveTargetProperty(); const joinAlias = 'J' + ((subSelect?._joins?.length || 0) + 1); subSelect.join((0, builder_1.InnerJoin)(targetEntity.tableName + ' ' + joinAlias).on((0, builder_1.Eq)((0, builder_1.Field)(joinAlias + '.' + targetColumn.fieldName, targetColumn.dataType, targetColumn.isArray), (0, builder_1.Field)(_curAlias + '.' + sourceColumn.fieldName, sourceColumn.dataType, sourceColumn.isArray)))); _curEntity = targetEntity; _curAlias = joinAlias; node = node.next; } // /** If next path is the last one */ // if (i === l - 2) { // associationPathCache[parentPath] = { // _curEntity, // _curAlias, // currentOp, // }; // } } } continue; } } trgOp.add(item); } }