UNPKG

14.9 kBJavaScriptView Raw
1"use strict";
2Object.defineProperty(exports, "__esModule", { value: true });
3var tslib_1 = require("tslib");
4var Repository_1 = require("./Repository");
5var AbstractSqliteDriver_1 = require("../driver/sqlite-abstract/AbstractSqliteDriver");
6/**
7 * Repository with additional functions to work with trees.
8 *
9 * @see Repository
10 */
11var TreeRepository = /** @class */ (function (_super) {
12 tslib_1.__extends(TreeRepository, _super);
13 function TreeRepository() {
14 return _super !== null && _super.apply(this, arguments) || this;
15 }
16 // todo: implement moving
17 // todo: implement removing
18 // -------------------------------------------------------------------------
19 // Public Methods
20 // -------------------------------------------------------------------------
21 /**
22 * Gets complete trees for all roots in the table.
23 */
24 TreeRepository.prototype.findTrees = function () {
25 return tslib_1.__awaiter(this, void 0, void 0, function () {
26 var roots;
27 var _this = this;
28 return tslib_1.__generator(this, function (_a) {
29 switch (_a.label) {
30 case 0: return [4 /*yield*/, this.findRoots()];
31 case 1:
32 roots = _a.sent();
33 return [4 /*yield*/, Promise.all(roots.map(function (root) { return _this.findDescendantsTree(root); }))];
34 case 2:
35 _a.sent();
36 return [2 /*return*/, roots];
37 }
38 });
39 });
40 };
41 /**
42 * Roots are entities that have no ancestors. Finds them all.
43 */
44 TreeRepository.prototype.findRoots = function () {
45 var _this = this;
46 var escapeAlias = function (alias) { return _this.manager.connection.driver.escape(alias); };
47 var escapeColumn = function (column) { return _this.manager.connection.driver.escape(column); };
48 var parentPropertyName = this.manager.connection.namingStrategy.joinColumnName(this.metadata.treeParentRelation.propertyName, "id");
49 return this.createQueryBuilder("treeEntity")
50 .where(escapeAlias("treeEntity") + "." + escapeColumn(parentPropertyName) + " IS NULL")
51 .getMany();
52 };
53 /**
54 * Gets all children (descendants) of the given entity. Returns them all in a flat array.
55 */
56 TreeRepository.prototype.findDescendants = function (entity) {
57 return this
58 .createDescendantsQueryBuilder("treeEntity", "treeClosure", entity)
59 .getMany();
60 };
61 /**
62 * Gets all children (descendants) of the given entity. Returns them in a tree - nested into each other.
63 */
64 TreeRepository.prototype.findDescendantsTree = function (entity) {
65 var _this = this;
66 // todo: throw exception if there is no column of this relation?
67 return this
68 .createDescendantsQueryBuilder("treeEntity", "treeClosure", entity)
69 .getRawAndEntities()
70 .then(function (entitiesAndScalars) {
71 var relationMaps = _this.createRelationMaps("treeEntity", entitiesAndScalars.raw);
72 _this.buildChildrenEntityTree(entity, entitiesAndScalars.entities, relationMaps);
73 return entity;
74 });
75 };
76 /**
77 * Gets number of descendants of the entity.
78 */
79 TreeRepository.prototype.countDescendants = function (entity) {
80 return this
81 .createDescendantsQueryBuilder("treeEntity", "treeClosure", entity)
82 .getCount();
83 };
84 /**
85 * Creates a query builder used to get descendants of the entities in a tree.
86 */
87 TreeRepository.prototype.createDescendantsQueryBuilder = function (alias, closureTableAlias, entity) {
88 var _this = this;
89 // create shortcuts for better readability
90 var escape = function (alias) { return _this.manager.connection.driver.escape(alias); };
91 if (this.metadata.treeType === "closure-table") {
92 var joinCondition = this.metadata.closureJunctionTable.descendantColumns.map(function (column) {
93 return escape(closureTableAlias) + "." + escape(column.propertyPath) + " = " + escape(alias) + "." + escape(column.referencedColumn.propertyPath);
94 }).join(" AND ");
95 var parameters_1 = {};
96 var whereCondition = this.metadata.closureJunctionTable.ancestorColumns.map(function (column) {
97 parameters_1[column.referencedColumn.propertyName] = column.referencedColumn.getEntityValue(entity);
98 return escape(closureTableAlias) + "." + escape(column.propertyPath) + " = :" + column.referencedColumn.propertyName;
99 }).join(" AND ");
100 return this
101 .createQueryBuilder(alias)
102 .innerJoin(this.metadata.closureJunctionTable.tableName, closureTableAlias, joinCondition)
103 .where(whereCondition)
104 .setParameters(parameters_1);
105 }
106 else if (this.metadata.treeType === "nested-set") {
107 var whereCondition = alias + "." + this.metadata.nestedSetLeftColumn.propertyPath + " BETWEEN " +
108 "joined." + this.metadata.nestedSetLeftColumn.propertyPath + " AND joined." + this.metadata.nestedSetRightColumn.propertyPath;
109 var parameters_2 = {};
110 var joinCondition = this.metadata.treeParentRelation.joinColumns.map(function (joinColumn) {
111 var parameterName = joinColumn.referencedColumn.propertyPath.replace(".", "_");
112 parameters_2[parameterName] = joinColumn.referencedColumn.getEntityValue(entity);
113 return "joined." + joinColumn.referencedColumn.propertyPath + " = :" + parameterName;
114 }).join(" AND ");
115 return this
116 .createQueryBuilder(alias)
117 .innerJoin(this.metadata.targetName, "joined", whereCondition)
118 .where(joinCondition, parameters_2);
119 }
120 else if (this.metadata.treeType === "materialized-path") {
121 return this
122 .createQueryBuilder(alias)
123 .where(function (qb) {
124 var subQuery = qb.subQuery()
125 .select(_this.metadata.targetName + "." + _this.metadata.materializedPathColumn.propertyPath, "path")
126 .from(_this.metadata.target, _this.metadata.targetName)
127 .whereInIds(_this.metadata.getEntityIdMap(entity));
128 qb.setNativeParameters(subQuery.expressionMap.nativeParameters);
129 if (_this.manager.connection.driver instanceof AbstractSqliteDriver_1.AbstractSqliteDriver) {
130 return alias + "." + _this.metadata.materializedPathColumn.propertyPath + " LIKE " + subQuery.getQuery() + " || '%'";
131 }
132 else {
133 return alias + "." + _this.metadata.materializedPathColumn.propertyPath + " LIKE CONCAT(" + subQuery.getQuery() + ", '%')";
134 }
135 });
136 }
137 throw new Error("Supported only in tree entities");
138 };
139 /**
140 * Gets all parents (ancestors) of the given entity. Returns them all in a flat array.
141 */
142 TreeRepository.prototype.findAncestors = function (entity) {
143 return this
144 .createAncestorsQueryBuilder("treeEntity", "treeClosure", entity)
145 .getMany();
146 };
147 /**
148 * Gets all parents (ancestors) of the given entity. Returns them in a tree - nested into each other.
149 */
150 TreeRepository.prototype.findAncestorsTree = function (entity) {
151 var _this = this;
152 // todo: throw exception if there is no column of this relation?
153 return this
154 .createAncestorsQueryBuilder("treeEntity", "treeClosure", entity)
155 .getRawAndEntities()
156 .then(function (entitiesAndScalars) {
157 var relationMaps = _this.createRelationMaps("treeEntity", entitiesAndScalars.raw);
158 _this.buildParentEntityTree(entity, entitiesAndScalars.entities, relationMaps);
159 return entity;
160 });
161 };
162 /**
163 * Gets number of ancestors of the entity.
164 */
165 TreeRepository.prototype.countAncestors = function (entity) {
166 return this
167 .createAncestorsQueryBuilder("treeEntity", "treeClosure", entity)
168 .getCount();
169 };
170 /**
171 * Creates a query builder used to get ancestors of the entities in the tree.
172 */
173 TreeRepository.prototype.createAncestorsQueryBuilder = function (alias, closureTableAlias, entity) {
174 // create shortcuts for better readability
175 // const escape = (alias: string) => this.manager.connection.driver.escape(alias);
176 var _this = this;
177 if (this.metadata.treeType === "closure-table") {
178 var joinCondition = this.metadata.closureJunctionTable.ancestorColumns.map(function (column) {
179 return closureTableAlias + "." + column.propertyPath + " = " + alias + "." + column.referencedColumn.propertyPath;
180 }).join(" AND ");
181 var parameters_3 = {};
182 var whereCondition = this.metadata.closureJunctionTable.descendantColumns.map(function (column) {
183 parameters_3[column.referencedColumn.propertyName] = column.referencedColumn.getEntityValue(entity);
184 return closureTableAlias + "." + column.propertyPath + " = :" + column.referencedColumn.propertyName;
185 }).join(" AND ");
186 return this
187 .createQueryBuilder(alias)
188 .innerJoin(this.metadata.closureJunctionTable.tableName, closureTableAlias, joinCondition)
189 .where(whereCondition)
190 .setParameters(parameters_3);
191 }
192 else if (this.metadata.treeType === "nested-set") {
193 var joinCondition = "joined." + this.metadata.nestedSetLeftColumn.propertyPath + " BETWEEN " +
194 alias + "." + this.metadata.nestedSetLeftColumn.propertyPath + " AND " + alias + "." + this.metadata.nestedSetRightColumn.propertyPath;
195 var parameters_4 = {};
196 var whereCondition = this.metadata.treeParentRelation.joinColumns.map(function (joinColumn) {
197 var parameterName = joinColumn.referencedColumn.propertyPath.replace(".", "_");
198 parameters_4[parameterName] = joinColumn.referencedColumn.getEntityValue(entity);
199 return "joined." + joinColumn.referencedColumn.propertyPath + " = :" + parameterName;
200 }).join(" AND ");
201 return this
202 .createQueryBuilder(alias)
203 .innerJoin(this.metadata.targetName, "joined", joinCondition)
204 .where(whereCondition, parameters_4);
205 }
206 else if (this.metadata.treeType === "materialized-path") {
207 // example: SELECT * FROM category category WHERE (SELECT mpath FROM `category` WHERE id = 2) LIKE CONCAT(category.mpath, '%');
208 return this
209 .createQueryBuilder(alias)
210 .where(function (qb) {
211 var subQuery = qb.subQuery()
212 .select(_this.metadata.targetName + "." + _this.metadata.materializedPathColumn.propertyPath, "path")
213 .from(_this.metadata.target, _this.metadata.targetName)
214 .whereInIds(_this.metadata.getEntityIdMap(entity));
215 qb.setNativeParameters(subQuery.expressionMap.nativeParameters);
216 if (_this.manager.connection.driver instanceof AbstractSqliteDriver_1.AbstractSqliteDriver) {
217 return subQuery.getQuery() + " LIKE " + alias + "." + _this.metadata.materializedPathColumn.propertyPath + " || '%'";
218 }
219 else {
220 return subQuery.getQuery() + " LIKE CONCAT(" + alias + "." + _this.metadata.materializedPathColumn.propertyPath + ", '%')";
221 }
222 });
223 }
224 throw new Error("Supported only in tree entities");
225 };
226 /**
227 * Moves entity to the children of then given entity.
228 *
229 move(entity: Entity, to: Entity): Promise<void> {
230 return Promise.resolve();
231 } */
232 // -------------------------------------------------------------------------
233 // Protected Methods
234 // -------------------------------------------------------------------------
235 TreeRepository.prototype.createRelationMaps = function (alias, rawResults) {
236 var _this = this;
237 return rawResults.map(function (rawResult) {
238 var joinColumn = _this.metadata.treeParentRelation.joinColumns[0];
239 // fixes issue #2518, default to databaseName property when givenDatabaseName is not set
240 var joinColumnName = joinColumn.givenDatabaseName || joinColumn.databaseName;
241 var id = rawResult[alias + "_" + _this.metadata.primaryColumns[0].databaseName];
242 var parentId = rawResult[alias + "_" + joinColumnName];
243 return {
244 id: _this.manager.connection.driver.prepareHydratedValue(id, _this.metadata.primaryColumns[0]),
245 parentId: _this.manager.connection.driver.prepareHydratedValue(parentId, joinColumn),
246 };
247 });
248 };
249 TreeRepository.prototype.buildChildrenEntityTree = function (entity, entities, relationMaps) {
250 var _this = this;
251 var childProperty = this.metadata.treeChildrenRelation.propertyName;
252 var parentEntityId = this.metadata.primaryColumns[0].getEntityValue(entity);
253 var childRelationMaps = relationMaps.filter(function (relationMap) { return relationMap.parentId === parentEntityId; });
254 var childIds = childRelationMaps.map(function (relationMap) { return relationMap.id; });
255 entity[childProperty] = entities.filter(function (entity) { return childIds.indexOf(entity.id) !== -1; });
256 entity[childProperty].forEach(function (childEntity) {
257 _this.buildChildrenEntityTree(childEntity, entities, relationMaps);
258 });
259 };
260 TreeRepository.prototype.buildParentEntityTree = function (entity, entities, relationMaps) {
261 var _this = this;
262 var parentProperty = this.metadata.treeParentRelation.propertyName;
263 var entityId = this.metadata.primaryColumns[0].getEntityValue(entity);
264 var parentRelationMap = relationMaps.find(function (relationMap) { return relationMap.id === entityId; });
265 var parentEntity = entities.find(function (entity) {
266 if (!parentRelationMap)
267 return false;
268 return entity[_this.metadata.primaryColumns[0].propertyName] === parentRelationMap.parentId;
269 });
270 if (parentEntity) {
271 entity[parentProperty] = parentEntity;
272 this.buildParentEntityTree(entity[parentProperty], entities, relationMaps);
273 }
274 };
275 return TreeRepository;
276}(Repository_1.Repository));
277exports.TreeRepository = TreeRepository;
278
279//# sourceMappingURL=TreeRepository.js.map