UNPKG

11.8 kBJavaScriptView Raw
1"use strict";
2Object.defineProperty(exports, "__esModule", { value: true });
3var tslib_1 = require("tslib");
4/**
5 * Wraps entities and creates getters/setters for their relations
6 * to be able to lazily load relations when accessing these relations.
7 */
8var RelationLoader = /** @class */ (function () {
9 // -------------------------------------------------------------------------
10 // Constructor
11 // -------------------------------------------------------------------------
12 function RelationLoader(connection) {
13 this.connection = connection;
14 }
15 // -------------------------------------------------------------------------
16 // Public Methods
17 // -------------------------------------------------------------------------
18 /**
19 * Loads relation data for the given entity and its relation.
20 */
21 RelationLoader.prototype.load = function (relation, entityOrEntities, queryRunner) {
22 if (queryRunner && queryRunner.isReleased)
23 queryRunner = undefined; // get new one if already closed
24 if (relation.isManyToOne || relation.isOneToOneOwner) {
25 return this.loadManyToOneOrOneToOneOwner(relation, entityOrEntities, queryRunner);
26 }
27 else if (relation.isOneToMany || relation.isOneToOneNotOwner) {
28 return this.loadOneToManyOrOneToOneNotOwner(relation, entityOrEntities, queryRunner);
29 }
30 else if (relation.isManyToManyOwner) {
31 return this.loadManyToManyOwner(relation, entityOrEntities, queryRunner);
32 }
33 else { // many-to-many non owner
34 return this.loadManyToManyNotOwner(relation, entityOrEntities, queryRunner);
35 }
36 };
37 /**
38 * Loads data for many-to-one and one-to-one owner relations.
39 *
40 * (ow) post.category<=>category.post
41 * loaded: category from post
42 * example: SELECT category.id AS category_id, category.name AS category_name FROM category category
43 * INNER JOIN post Post ON Post.category=category.id WHERE Post.id=1
44 */
45 RelationLoader.prototype.loadManyToOneOrOneToOneOwner = function (relation, entityOrEntities, queryRunner) {
46 var entities = entityOrEntities instanceof Array ? entityOrEntities : [entityOrEntities];
47 var columns = relation.entityMetadata.primaryColumns;
48 var joinColumns = relation.isOwning ? relation.joinColumns : relation.inverseRelation.joinColumns;
49 var conditions = joinColumns.map(function (joinColumn) {
50 return relation.entityMetadata.name + "." + joinColumn.propertyName + " = " + relation.propertyName + "." + joinColumn.referencedColumn.propertyName;
51 }).join(" AND ");
52 var joinAliasName = relation.entityMetadata.name;
53 var qb = this.connection
54 .createQueryBuilder(queryRunner)
55 .select(relation.propertyName) // category
56 .from(relation.type, relation.propertyName) // Category, category
57 .innerJoin(relation.entityMetadata.target, joinAliasName, conditions);
58 if (columns.length === 1) {
59 qb.where(joinAliasName + "." + columns[0].propertyPath + " IN (:..." + (joinAliasName + "_" + columns[0].propertyName) + ")");
60 qb.setParameter(joinAliasName + "_" + columns[0].propertyName, entities.map(function (entity) { return columns[0].getEntityValue(entity); }));
61 }
62 else {
63 var condition = entities.map(function (entity, entityIndex) {
64 return columns.map(function (column, columnIndex) {
65 var paramName = joinAliasName + "_entity_" + entityIndex + "_" + columnIndex;
66 qb.setParameter(paramName, column.getEntityValue(entity));
67 return joinAliasName + "." + column.propertyPath + " = :" + paramName;
68 }).join(" AND ");
69 }).map(function (condition) { return "(" + condition + ")"; }).join(" OR ");
70 qb.where(condition);
71 }
72 return qb.getMany();
73 // return qb.getOne(); todo: fix all usages
74 };
75 /**
76 * Loads data for one-to-many and one-to-one not owner relations.
77 *
78 * SELECT post
79 * FROM post post
80 * WHERE post.[joinColumn.name] = entity[joinColumn.referencedColumn]
81 */
82 RelationLoader.prototype.loadOneToManyOrOneToOneNotOwner = function (relation, entityOrEntities, queryRunner) {
83 var entities = entityOrEntities instanceof Array ? entityOrEntities : [entityOrEntities];
84 var aliasName = relation.propertyName;
85 var columns = relation.inverseRelation.joinColumns;
86 var qb = this.connection
87 .createQueryBuilder(queryRunner)
88 .select(aliasName)
89 .from(relation.inverseRelation.entityMetadata.target, aliasName);
90 if (columns.length === 1) {
91 qb.where(aliasName + "." + columns[0].propertyPath + " IN (:..." + (aliasName + "_" + columns[0].propertyName) + ")");
92 qb.setParameter(aliasName + "_" + columns[0].propertyName, entities.map(function (entity) { return columns[0].referencedColumn.getEntityValue(entity); }));
93 }
94 else {
95 var condition = entities.map(function (entity, entityIndex) {
96 return columns.map(function (column, columnIndex) {
97 var paramName = aliasName + "_entity_" + entityIndex + "_" + columnIndex;
98 qb.setParameter(paramName, column.referencedColumn.getEntityValue(entity));
99 return aliasName + "." + column.propertyPath + " = :" + paramName;
100 }).join(" AND ");
101 }).map(function (condition) { return "(" + condition + ")"; }).join(" OR ");
102 qb.where(condition);
103 }
104 return qb.getMany();
105 // return relation.isOneToMany ? qb.getMany() : qb.getOne(); todo: fix all usages
106 };
107 /**
108 * Loads data for many-to-many owner relations.
109 *
110 * SELECT category
111 * FROM category category
112 * INNER JOIN post_categories post_categories
113 * ON post_categories.postId = :postId
114 * AND post_categories.categoryId = category.id
115 */
116 RelationLoader.prototype.loadManyToManyOwner = function (relation, entityOrEntities, queryRunner) {
117 var entities = entityOrEntities instanceof Array ? entityOrEntities : [entityOrEntities];
118 var mainAlias = relation.propertyName;
119 var joinAlias = relation.junctionEntityMetadata.tableName;
120 var joinColumnConditions = relation.joinColumns.map(function (joinColumn) {
121 return joinAlias + "." + joinColumn.propertyName + " IN (:..." + joinColumn.propertyName + ")";
122 });
123 var inverseJoinColumnConditions = relation.inverseJoinColumns.map(function (inverseJoinColumn) {
124 return joinAlias + "." + inverseJoinColumn.propertyName + "=" + mainAlias + "." + inverseJoinColumn.referencedColumn.propertyName;
125 });
126 var parameters = relation.joinColumns.reduce(function (parameters, joinColumn) {
127 parameters[joinColumn.propertyName] = entities.map(function (entity) { return joinColumn.referencedColumn.getEntityValue(entity); });
128 return parameters;
129 }, {});
130 return this.connection
131 .createQueryBuilder(queryRunner)
132 .select(mainAlias)
133 .from(relation.type, mainAlias)
134 .innerJoin(joinAlias, joinAlias, tslib_1.__spread(joinColumnConditions, inverseJoinColumnConditions).join(" AND "))
135 .setParameters(parameters)
136 .getMany();
137 };
138 /**
139 * Loads data for many-to-many not owner relations.
140 *
141 * SELECT post
142 * FROM post post
143 * INNER JOIN post_categories post_categories
144 * ON post_categories.postId = post.id
145 * AND post_categories.categoryId = post_categories.categoryId
146 */
147 RelationLoader.prototype.loadManyToManyNotOwner = function (relation, entityOrEntities, queryRunner) {
148 var entities = entityOrEntities instanceof Array ? entityOrEntities : [entityOrEntities];
149 var mainAlias = relation.propertyName;
150 var joinAlias = relation.junctionEntityMetadata.tableName;
151 var joinColumnConditions = relation.inverseRelation.joinColumns.map(function (joinColumn) {
152 return joinAlias + "." + joinColumn.propertyName + " = " + mainAlias + "." + joinColumn.referencedColumn.propertyName;
153 });
154 var inverseJoinColumnConditions = relation.inverseRelation.inverseJoinColumns.map(function (inverseJoinColumn) {
155 return joinAlias + "." + inverseJoinColumn.propertyName + " IN (:..." + inverseJoinColumn.propertyName + ")";
156 });
157 var parameters = relation.inverseRelation.inverseJoinColumns.reduce(function (parameters, joinColumn) {
158 parameters[joinColumn.propertyName] = entities.map(function (entity) { return joinColumn.referencedColumn.getEntityValue(entity); });
159 return parameters;
160 }, {});
161 return this.connection
162 .createQueryBuilder(queryRunner)
163 .select(mainAlias)
164 .from(relation.type, mainAlias)
165 .innerJoin(joinAlias, joinAlias, tslib_1.__spread(joinColumnConditions, inverseJoinColumnConditions).join(" AND "))
166 .setParameters(parameters)
167 .getMany();
168 };
169 /**
170 * Wraps given entity and creates getters/setters for its given relation
171 * to be able to lazily load data when accessing this relation.
172 */
173 RelationLoader.prototype.enableLazyLoad = function (relation, entity, queryRunner) {
174 var relationLoader = this;
175 var dataIndex = "__" + relation.propertyName + "__"; // in what property of the entity loaded data will be stored
176 var promiseIndex = "__promise_" + relation.propertyName + "__"; // in what property of the entity loading promise will be stored
177 var resolveIndex = "__has_" + relation.propertyName + "__"; // indicates if relation data already was loaded or not, we need this flag if loaded data is empty
178 Object.defineProperty(entity, relation.propertyName, {
179 get: function () {
180 var _this = this;
181 if (this[resolveIndex] === true || this[dataIndex]) // if related data already was loaded then simply return it
182 return Promise.resolve(this[dataIndex]);
183 if (this[promiseIndex]) // if related data is loading then return a promise relationLoader loads it
184 return this[promiseIndex];
185 // nothing is loaded yet, load relation data and save it in the model once they are loaded
186 this[promiseIndex] = relationLoader.load(relation, this, queryRunner).then(function (result) {
187 if (relation.isOneToOne || relation.isManyToOne)
188 result = result[0];
189 _this[dataIndex] = result;
190 _this[resolveIndex] = true;
191 delete _this[promiseIndex];
192 return _this[dataIndex];
193 });
194 return this[promiseIndex];
195 },
196 set: function (value) {
197 var _this = this;
198 if (value instanceof Promise) { // if set data is a promise then wait for its resolve and save in the object
199 value.then(function (result) {
200 _this[dataIndex] = result;
201 _this[resolveIndex] = true;
202 });
203 }
204 else { // if its direct data set (non promise, probably not safe-typed)
205 this[dataIndex] = value;
206 this[resolveIndex] = true;
207 }
208 },
209 configurable: true
210 });
211 };
212 return RelationLoader;
213}());
214exports.RelationLoader = RelationLoader;
215
216//# sourceMappingURL=RelationLoader.js.map