UNPKG

typeorm

Version:

Data-Mapper ORM for TypeScript, ES7, ES6, ES5. Supports MySQL, PostgreSQL, MariaDB, SQLite, MS SQL Server, Oracle, MongoDB databases.

377 lines (375 loc) • 21.9 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.NestedSetSubjectExecutor = void 0; var tslib_1 = require("tslib"); var OrmUtils_1 = require("../../util/OrmUtils"); var NestedSetMultipleRootError_1 = require("../../error/NestedSetMultipleRootError"); var NestedSetIds = /** @class */ (function () { function NestedSetIds() { } return NestedSetIds; }()); /** * Executes subject operations for nested set tree entities. */ var NestedSetSubjectExecutor = /** @class */ (function () { // ------------------------------------------------------------------------- // Constructor // ------------------------------------------------------------------------- function NestedSetSubjectExecutor(queryRunner) { this.queryRunner = queryRunner; } // ------------------------------------------------------------------------- // Public Methods // ------------------------------------------------------------------------- /** * Executes operations when subject is being inserted. */ NestedSetSubjectExecutor.prototype.insert = function (subject) { return tslib_1.__awaiter(this, void 0, void 0, function () { var escape, tableName, leftColumnName, rightColumnName, parent, parentId, parentNsRight, isUniqueRoot; var _this = this; return tslib_1.__generator(this, function (_a) { switch (_a.label) { case 0: escape = function (alias) { return _this.queryRunner.connection.driver.escape(alias); }; tableName = this.getTableName(subject.metadata.tablePath); leftColumnName = escape(subject.metadata.nestedSetLeftColumn.databaseName); rightColumnName = escape(subject.metadata.nestedSetRightColumn.databaseName); parent = subject.metadata.treeParentRelation.getEntityValue(subject.entity); if (!parent && subject.parentSubject && subject.parentSubject.entity) // if entity was attached via children parent = subject.parentSubject.insertedValueSet ? subject.parentSubject.insertedValueSet : subject.parentSubject.entity; parentId = subject.metadata.getEntityIdMap(parent); parentNsRight = undefined; if (!parentId) return [3 /*break*/, 2]; return [4 /*yield*/, this.queryRunner.manager .createQueryBuilder() .select(subject.metadata.targetName + "." + subject.metadata.nestedSetRightColumn.propertyPath, "right") .from(subject.metadata.target, subject.metadata.targetName) .whereInIds(parentId) .getRawOne() .then(function (result) { var value = result ? result["right"] : undefined; // CockroachDB returns numeric types as string return typeof value === "string" ? parseInt(value) : value; })]; case 1: parentNsRight = _a.sent(); _a.label = 2; case 2: if (!(parentNsRight !== undefined)) return [3 /*break*/, 4]; return [4 /*yield*/, this.queryRunner.query("UPDATE " + tableName + " SET " + (leftColumnName + " = CASE WHEN " + leftColumnName + " > " + parentNsRight + " THEN " + leftColumnName + " + 2 ELSE " + leftColumnName + " END,") + (rightColumnName + " = " + rightColumnName + " + 2 ") + ("WHERE " + rightColumnName + " >= " + parentNsRight))]; case 3: _a.sent(); OrmUtils_1.OrmUtils.mergeDeep(subject.insertedValueSet, subject.metadata.nestedSetLeftColumn.createValueMap(parentNsRight), subject.metadata.nestedSetRightColumn.createValueMap(parentNsRight + 1)); return [3 /*break*/, 6]; case 4: return [4 /*yield*/, this.isUniqueRootEntity(subject, parent)]; case 5: isUniqueRoot = _a.sent(); // Validate if a root entity already exits and throw an exception if (!isUniqueRoot) throw new NestedSetMultipleRootError_1.NestedSetMultipleRootError(); OrmUtils_1.OrmUtils.mergeDeep(subject.insertedValueSet, subject.metadata.nestedSetLeftColumn.createValueMap(1), subject.metadata.nestedSetRightColumn.createValueMap(2)); _a.label = 6; case 6: return [2 /*return*/]; } }); }); }; /** * Executes operations when subject is being updated. */ NestedSetSubjectExecutor.prototype.update = function (subject) { return tslib_1.__awaiter(this, void 0, void 0, function () { var parent, entity, oldParent, oldParentId, parentId, escape_1, tableName, leftColumnName, rightColumnName, entityId, entityNs, parentNs, isMovingUp, treeSize, entitySize, updateLeftSide, updateRightSide, isUniqueRoot; var _this = this; return tslib_1.__generator(this, function (_a) { switch (_a.label) { case 0: parent = subject.metadata.treeParentRelation.getEntityValue(subject.entity); if (!parent && subject.parentSubject && subject.parentSubject.entity) // if entity was attached via children parent = subject.parentSubject.entity; entity = subject.databaseEntity; if (!entity && parent) // if entity was attached via children entity = subject.metadata.treeChildrenRelation.getEntityValue(parent).find(function (child) { return Object.entries(subject.identifier).every(function (_a) { var _b = tslib_1.__read(_a, 2), key = _b[0], value = _b[1]; return child[key] === value; }); }); // Exit if the parent or the entity where never set if (entity === undefined || parent === undefined) { return [2 /*return*/]; } oldParent = subject.metadata.treeParentRelation.getEntityValue(entity); oldParentId = subject.metadata.getEntityIdMap(oldParent); parentId = subject.metadata.getEntityIdMap(parent); // Exit if the new and old parents are the same if (OrmUtils_1.OrmUtils.compareIds(oldParentId, parentId)) { return [2 /*return*/]; } if (!parent) return [3 /*break*/, 9]; escape_1 = function (alias) { return _this.queryRunner.connection.driver.escape(alias); }; tableName = this.getTableName(subject.metadata.tablePath); leftColumnName = escape_1(subject.metadata.nestedSetLeftColumn.databaseName); rightColumnName = escape_1(subject.metadata.nestedSetRightColumn.databaseName); entityId = subject.metadata.getEntityIdMap(entity); entityNs = undefined; if (!entityId) return [3 /*break*/, 2]; return [4 /*yield*/, this.getNestedSetIds(subject.metadata, entityId)]; case 1: entityNs = (_a.sent())[0]; _a.label = 2; case 2: parentNs = undefined; if (!parentId) return [3 /*break*/, 4]; return [4 /*yield*/, this.getNestedSetIds(subject.metadata, parentId)]; case 3: parentNs = (_a.sent())[0]; _a.label = 4; case 4: if (!(entityNs !== undefined && parentNs !== undefined)) return [3 /*break*/, 8]; isMovingUp = parentNs.left > entityNs.left; treeSize = entityNs.right - entityNs.left + 1; entitySize = void 0; if (isMovingUp) { entitySize = parentNs.left - entityNs.right; } else { entitySize = parentNs.right - entityNs.left; } updateLeftSide = "WHEN " + leftColumnName + " >= " + entityNs.left + " AND " + (leftColumnName + " < " + entityNs.right + " ") + ("THEN " + leftColumnName + " + " + entitySize + " "); updateRightSide = "WHEN " + rightColumnName + " > " + entityNs.left + " AND " + (rightColumnName + " <= " + entityNs.right + " ") + ("THEN " + rightColumnName + " + " + entitySize + " "); if (!isMovingUp) return [3 /*break*/, 6]; return [4 /*yield*/, this.queryRunner.query("UPDATE " + tableName + " " + ("SET " + leftColumnName + " = CASE ") + ("WHEN " + leftColumnName + " > " + entityNs.right + " AND ") + (leftColumnName + " <= " + parentNs.left + " ") + ("THEN " + leftColumnName + " - " + treeSize + " ") + updateLeftSide + ("ELSE " + leftColumnName + " ") + "END, " + (rightColumnName + " = CASE ") + ("WHEN " + rightColumnName + " > " + entityNs.right + " AND ") + (rightColumnName + " < " + parentNs.left + " ") + ("THEN " + rightColumnName + " - " + treeSize + " ") + updateRightSide + ("ELSE " + rightColumnName + " ") + "END")]; case 5: _a.sent(); return [3 /*break*/, 8]; case 6: return [4 /*yield*/, this.queryRunner.query("UPDATE " + tableName + " " + ("SET " + leftColumnName + " = CASE ") + ("WHEN " + leftColumnName + " < " + entityNs.left + " AND ") + (leftColumnName + " > " + parentNs.right + " ") + ("THEN " + leftColumnName + " + " + treeSize + " ") + updateLeftSide + ("ELSE " + leftColumnName + " ") + "END, " + (rightColumnName + " = CASE ") + ("WHEN " + rightColumnName + " < " + entityNs.left + " AND ") + (rightColumnName + " >= " + parentNs.right + " ") + ("THEN " + rightColumnName + " + " + treeSize + " ") + updateRightSide + ("ELSE " + rightColumnName + " ") + "END")]; case 7: _a.sent(); _a.label = 8; case 8: return [3 /*break*/, 11]; case 9: return [4 /*yield*/, this.isUniqueRootEntity(subject, parent)]; case 10: isUniqueRoot = _a.sent(); // Validate if a root entity already exits and throw an exception if (!isUniqueRoot) throw new NestedSetMultipleRootError_1.NestedSetMultipleRootError(); _a.label = 11; case 11: return [2 /*return*/]; } }); }); }; /** * Executes operations when subject is being removed. */ NestedSetSubjectExecutor.prototype.remove = function (subjects) { return tslib_1.__awaiter(this, void 0, void 0, function () { var metadata, escape, tableName, leftColumnName, rightColumnName, entitiesIds, subjects_1, subjects_1_1, subject, entityId, entitiesNs, entitiesNs_1, entitiesNs_1_1, entity, treeSize, e_1_1; var e_2, _a, e_1, _b; var _this = this; return tslib_1.__generator(this, function (_c) { switch (_c.label) { case 0: if (!Array.isArray(subjects)) subjects = [subjects]; metadata = subjects[0].metadata; escape = function (alias) { return _this.queryRunner.connection.driver.escape(alias); }; tableName = this.getTableName(metadata.tablePath); leftColumnName = escape(metadata.nestedSetLeftColumn.databaseName); rightColumnName = escape(metadata.nestedSetRightColumn.databaseName); entitiesIds = []; try { for (subjects_1 = tslib_1.__values(subjects), subjects_1_1 = subjects_1.next(); !subjects_1_1.done; subjects_1_1 = subjects_1.next()) { subject = subjects_1_1.value; entityId = metadata.getEntityIdMap(subject.entity); if (entityId) { entitiesIds.push(entityId); } } } catch (e_2_1) { e_2 = { error: e_2_1 }; } finally { try { if (subjects_1_1 && !subjects_1_1.done && (_a = subjects_1.return)) _a.call(subjects_1); } finally { if (e_2) throw e_2.error; } } return [4 /*yield*/, this.getNestedSetIds(metadata, entitiesIds)]; case 1: entitiesNs = _c.sent(); _c.label = 2; case 2: _c.trys.push([2, 7, 8, 9]); entitiesNs_1 = tslib_1.__values(entitiesNs), entitiesNs_1_1 = entitiesNs_1.next(); _c.label = 3; case 3: if (!!entitiesNs_1_1.done) return [3 /*break*/, 6]; entity = entitiesNs_1_1.value; treeSize = entity.right - entity.left + 1; return [4 /*yield*/, this.queryRunner.query("UPDATE " + tableName + " " + ("SET " + leftColumnName + " = CASE ") + ("WHEN " + leftColumnName + " > " + entity.left + " THEN " + leftColumnName + " - " + treeSize + " ") + ("ELSE " + leftColumnName + " ") + "END, " + (rightColumnName + " = CASE ") + ("WHEN " + rightColumnName + " > " + entity.right + " THEN " + rightColumnName + " - " + treeSize + " ") + ("ELSE " + rightColumnName + " ") + "END")]; case 4: _c.sent(); _c.label = 5; case 5: entitiesNs_1_1 = entitiesNs_1.next(); return [3 /*break*/, 3]; case 6: return [3 /*break*/, 9]; case 7: e_1_1 = _c.sent(); e_1 = { error: e_1_1 }; return [3 /*break*/, 9]; case 8: try { if (entitiesNs_1_1 && !entitiesNs_1_1.done && (_b = entitiesNs_1.return)) _b.call(entitiesNs_1); } finally { if (e_1) throw e_1.error; } return [7 /*endfinally*/]; case 9: return [2 /*return*/]; } }); }); }; /** * Get the nested set ids for a given entity */ NestedSetSubjectExecutor.prototype.getNestedSetIds = function (metadata, ids) { var select = { left: metadata.targetName + "." + metadata.nestedSetLeftColumn.propertyPath, right: metadata.targetName + "." + metadata.nestedSetRightColumn.propertyPath }; var queryBuilder = this.queryRunner.manager.createQueryBuilder(); Object.entries(select).forEach(function (_a) { var _b = tslib_1.__read(_a, 2), key = _b[0], value = _b[1]; queryBuilder.addSelect(value, key); }); return queryBuilder .from(metadata.target, metadata.targetName) .whereInIds(ids) .orderBy(select.right, "DESC") .getRawMany() .then(function (results) { var e_3, _a, e_4, _b; var data = []; try { for (var results_1 = tslib_1.__values(results), results_1_1 = results_1.next(); !results_1_1.done; results_1_1 = results_1.next()) { var result = results_1_1.value; var entry = {}; try { for (var _c = (e_4 = void 0, tslib_1.__values(Object.keys(select))), _d = _c.next(); !_d.done; _d = _c.next()) { var key = _d.value; var value = result ? result[key] : undefined; // CockroachDB returns numeric types as string entry[key] = typeof value === "string" ? parseInt(value) : value; } } catch (e_4_1) { e_4 = { error: e_4_1 }; } finally { try { if (_d && !_d.done && (_b = _c.return)) _b.call(_c); } finally { if (e_4) throw e_4.error; } } data.push(entry); } } catch (e_3_1) { e_3 = { error: e_3_1 }; } finally { try { if (results_1_1 && !results_1_1.done && (_a = results_1.return)) _a.call(results_1); } finally { if (e_3) throw e_3.error; } } return data; }); }; NestedSetSubjectExecutor.prototype.isUniqueRootEntity = function (subject, parent) { return tslib_1.__awaiter(this, void 0, void 0, function () { var escape, tableName, parameters, whereCondition, countAlias, result; var _this = this; return tslib_1.__generator(this, function (_a) { switch (_a.label) { case 0: escape = function (alias) { return _this.queryRunner.connection.driver.escape(alias); }; tableName = this.getTableName(subject.metadata.tablePath); parameters = []; whereCondition = subject.metadata.treeParentRelation.joinColumns.map(function (column) { var columnName = escape(column.databaseName); var parameter = column.getEntityValue(parent); if (parameter == null) { return columnName + " IS NULL"; } parameters.push(parameter); var parameterName = _this.queryRunner.connection.driver.createParameter("entity_" + column.databaseName, parameters.length - 1); return columnName + " = " + parameterName; }).join(" AND "); countAlias = "count"; return [4 /*yield*/, this.queryRunner.query("SELECT COUNT(1) AS " + escape(countAlias) + " FROM " + tableName + " WHERE " + whereCondition, parameters, true)]; case 1: result = _a.sent(); return [2 /*return*/, parseInt(result.records[0][countAlias]) === 0]; } }); }); }; /** * Gets escaped table name with schema name if SqlServer or Postgres driver used with custom * schema name, otherwise returns escaped table name. */ NestedSetSubjectExecutor.prototype.getTableName = function (tablePath) { var _this = this; return tablePath.split(".") .map(function (i) { // this condition need because in SQL Server driver when custom database name was specified and schema name was not, we got `dbName..tableName` string, and doesn't need to escape middle empty string return i === "" ? i : _this.queryRunner.connection.driver.escape(i); }).join("."); }; return NestedSetSubjectExecutor; }()); exports.NestedSetSubjectExecutor = NestedSetSubjectExecutor; //# sourceMappingURL=NestedSetSubjectExecutor.js.map