UNPKG

25.3 kBJavaScriptView Raw
1"use strict";
2Object.defineProperty(exports, "__esModule", { value: true });
3var tslib_1 = require("tslib");
4var OrmUtils_1 = require("../util/OrmUtils");
5var MongoDriver_1 = require("../driver/mongodb/MongoDriver");
6var PromiseUtils_1 = require("../util/PromiseUtils");
7var FindOperator_1 = require("../find-options/FindOperator");
8var ApplyValueTransformers_1 = require("../util/ApplyValueTransformers");
9/**
10 * This metadata contains all information about entity's column.
11 */
12var ColumnMetadata = /** @class */ (function () {
13 // ---------------------------------------------------------------------
14 // Constructor
15 // ---------------------------------------------------------------------
16 function ColumnMetadata(options) {
17 /**
18 * Type's length in the database.
19 */
20 this.length = "";
21 /**
22 * Indicates if this column is a primary key.
23 */
24 this.isPrimary = false;
25 /**
26 * Indicates if this column is generated (auto increment or generated other way).
27 */
28 this.isGenerated = false;
29 /**
30 * Indicates if column can contain nulls or not.
31 */
32 this.isNullable = false;
33 /**
34 * Indicates if column is selected by query builder or not.
35 */
36 this.isSelect = true;
37 /**
38 * Indicates if column is inserted by default or not.
39 */
40 this.isInsert = true;
41 /**
42 * Indicates if column allows updates or not.
43 */
44 this.isUpdate = true;
45 /**
46 * Column comment.
47 * This feature is not supported by all databases.
48 */
49 this.comment = "";
50 /**
51 * Puts ZEROFILL attribute on to numeric column. Works only for MySQL.
52 * If you specify ZEROFILL for a numeric column, MySQL automatically adds the UNSIGNED attribute to the column
53 */
54 this.zerofill = false;
55 /**
56 * Puts UNSIGNED attribute on to numeric column. Works only for MySQL.
57 */
58 this.unsigned = false;
59 /**
60 * Indicates if this column is an array.
61 */
62 this.isArray = false;
63 /**
64 * Indicates if column is virtual. Virtual columns are not mapped to the entity.
65 */
66 this.isVirtual = false;
67 /**
68 * Indicates if column is discriminator. Discriminator columns are not mapped to the entity.
69 */
70 this.isDiscriminator = false;
71 /**
72 * Indicates if column is tree-level column. Tree-level columns are used in closure entities.
73 */
74 this.isTreeLevel = false;
75 /**
76 * Indicates if this column contains an entity creation date.
77 */
78 this.isCreateDate = false;
79 /**
80 * Indicates if this column contains an entity update date.
81 */
82 this.isUpdateDate = false;
83 /**
84 * Indicates if this column contains an entity version.
85 */
86 this.isVersion = false;
87 /**
88 * Indicates if this column contains an object id.
89 */
90 this.isObjectId = false;
91 /**
92 * Indicates if this column is nested set's left column.
93 * Used only in tree entities with nested-set type.
94 */
95 this.isNestedSetLeft = false;
96 /**
97 * Indicates if this column is nested set's right column.
98 * Used only in tree entities with nested-set type.
99 */
100 this.isNestedSetRight = false;
101 /**
102 * Indicates if this column is materialized path's path column.
103 * Used only in tree entities with materialized path type.
104 */
105 this.isMaterializedPath = false;
106 this.entityMetadata = options.entityMetadata;
107 this.embeddedMetadata = options.embeddedMetadata;
108 this.referencedColumn = options.referencedColumn;
109 if (options.args.target)
110 this.target = options.args.target;
111 if (options.args.propertyName)
112 this.propertyName = options.args.propertyName;
113 if (options.args.options.name)
114 this.givenDatabaseName = options.args.options.name;
115 if (options.args.options.type)
116 this.type = options.args.options.type;
117 if (options.args.options.length)
118 this.length = options.args.options.length ? options.args.options.length.toString() : "";
119 if (options.args.options.width)
120 this.width = options.args.options.width;
121 if (options.args.options.charset)
122 this.charset = options.args.options.charset;
123 if (options.args.options.collation)
124 this.collation = options.args.options.collation;
125 if (options.args.options.primary)
126 this.isPrimary = options.args.options.primary;
127 if (options.args.options.default === null) // to make sure default: null is the same as nullable: true
128 this.isNullable = true;
129 if (options.args.options.nullable !== undefined)
130 this.isNullable = options.args.options.nullable;
131 if (options.args.options.select !== undefined)
132 this.isSelect = options.args.options.select;
133 if (options.args.options.insert !== undefined)
134 this.isInsert = options.args.options.insert;
135 if (options.args.options.update !== undefined)
136 this.isUpdate = options.args.options.update;
137 if (options.args.options.readonly !== undefined)
138 this.isUpdate = !options.args.options.readonly;
139 if (options.args.options.comment)
140 this.comment = options.args.options.comment;
141 if (options.args.options.default !== undefined)
142 this.default = options.args.options.default;
143 if (options.args.options.onUpdate)
144 this.onUpdate = options.args.options.onUpdate;
145 if (options.args.options.scale !== null && options.args.options.scale !== undefined)
146 this.scale = options.args.options.scale;
147 if (options.args.options.zerofill) {
148 this.zerofill = options.args.options.zerofill;
149 this.unsigned = true; // if you specify ZEROFILL for a numeric column, MySQL automatically adds the UNSIGNED attribute to the column
150 }
151 if (options.args.options.unsigned)
152 this.unsigned = options.args.options.unsigned;
153 if (options.args.options.precision !== undefined)
154 this.precision = options.args.options.precision;
155 if (options.args.options.enum) {
156 if (options.args.options.enum instanceof Object && !Array.isArray(options.args.options.enum)) {
157 this.enum = Object.keys(options.args.options.enum)
158 .filter(function (key) { return isNaN(+key); }) // remove numeric keys - typescript numeric enum types generate them
159 .map(function (key) { return options.args.options.enum[key]; });
160 }
161 else {
162 this.enum = options.args.options.enum;
163 }
164 }
165 if (options.args.options.asExpression) {
166 this.asExpression = options.args.options.asExpression;
167 this.generatedType = options.args.options.generatedType ? options.args.options.generatedType : "VIRTUAL";
168 }
169 if (options.args.options.hstoreType)
170 this.hstoreType = options.args.options.hstoreType;
171 if (options.args.options.array)
172 this.isArray = options.args.options.array;
173 if (options.args.mode) {
174 this.isVirtual = options.args.mode === "virtual";
175 this.isTreeLevel = options.args.mode === "treeLevel";
176 this.isCreateDate = options.args.mode === "createDate";
177 this.isUpdateDate = options.args.mode === "updateDate";
178 this.isVersion = options.args.mode === "version";
179 this.isObjectId = options.args.mode === "objectId";
180 }
181 if (options.args.options.transformer)
182 this.transformer = options.args.options.transformer;
183 if (options.args.options.spatialFeatureType)
184 this.spatialFeatureType = options.args.options.spatialFeatureType;
185 if (options.args.options.srid)
186 this.srid = options.args.options.srid;
187 if (this.isTreeLevel)
188 this.type = options.connection.driver.mappedDataTypes.treeLevel;
189 if (this.isCreateDate) {
190 if (!this.type)
191 this.type = options.connection.driver.mappedDataTypes.createDate;
192 if (!this.default)
193 this.default = function () { return options.connection.driver.mappedDataTypes.createDateDefault; };
194 if (this.precision === undefined && options.connection.driver.mappedDataTypes.createDatePrecision)
195 this.precision = options.connection.driver.mappedDataTypes.createDatePrecision;
196 }
197 if (this.isUpdateDate) {
198 if (!this.type)
199 this.type = options.connection.driver.mappedDataTypes.updateDate;
200 if (!this.default)
201 this.default = function () { return options.connection.driver.mappedDataTypes.updateDateDefault; };
202 if (this.precision === undefined && options.connection.driver.mappedDataTypes.updateDatePrecision)
203 this.precision = options.connection.driver.mappedDataTypes.updateDatePrecision;
204 }
205 if (this.isVersion)
206 this.type = options.connection.driver.mappedDataTypes.version;
207 if (options.closureType)
208 this.closureType = options.closureType;
209 if (options.nestedSetLeft)
210 this.isNestedSetLeft = options.nestedSetLeft;
211 if (options.nestedSetRight)
212 this.isNestedSetRight = options.nestedSetRight;
213 if (options.materializedPath)
214 this.isMaterializedPath = options.materializedPath;
215 }
216 // ---------------------------------------------------------------------
217 // Public Methods
218 // ---------------------------------------------------------------------
219 /**
220 * Creates entity id map from the given entity ids array.
221 */
222 ColumnMetadata.prototype.createValueMap = function (value, useDatabaseName) {
223 var _this = this;
224 if (useDatabaseName === void 0) { useDatabaseName = false; }
225 var _a;
226 // extract column value from embeds of entity if column is in embedded
227 if (this.embeddedMetadata) {
228 // example: post[data][information][counters].id where "data", "information" and "counters" are embeddeds
229 // we need to get value of "id" column from the post real entity object and return it in a
230 // { data: { information: { counters: { id: ... } } } } format
231 // first step - we extract all parent properties of the entity relative to this column, e.g. [data, information, counters]
232 var propertyNames = tslib_1.__spread(this.embeddedMetadata.parentPropertyNames);
233 // now need to access post[data][information][counters] to get column value from the counters
234 // and on each step we need to create complex literal object, e.g. first { data },
235 // then { data: { information } }, then { data: { information: { counters } } },
236 // then { data: { information: { counters: [this.propertyName]: entity[data][information][counters][this.propertyName] } } }
237 // this recursive function helps doing that
238 var extractEmbeddedColumnValue_1 = function (propertyNames, map) {
239 var propertyName = propertyNames.shift();
240 if (propertyName) {
241 map[propertyName] = {};
242 extractEmbeddedColumnValue_1(propertyNames, map[propertyName]);
243 return map;
244 }
245 // this is bugfix for #720 when increment number is bigint we need to make sure its a string
246 if ((_this.generationStrategy === "increment" || _this.generationStrategy === "rowid") && _this.type === "bigint")
247 value = String(value);
248 map[useDatabaseName ? _this.databaseName : _this.propertyName] = value;
249 return map;
250 };
251 return extractEmbeddedColumnValue_1(propertyNames, {});
252 }
253 else { // no embeds - no problems. Simply return column property name and its value of the entity
254 // this is bugfix for #720 when increment number is bigint we need to make sure its a string
255 if ((this.generationStrategy === "increment" || this.generationStrategy === "rowid") && this.type === "bigint")
256 value = String(value);
257 return _a = {}, _a[useDatabaseName ? this.databaseName : this.propertyName] = value, _a;
258 }
259 };
260 /**
261 * Extracts column value and returns its column name with this value in a literal object.
262 * If column is in embedded (or recursive embedded) it returns complex literal object.
263 *
264 * Examples what this method can return depend if this column is in embeds.
265 * { id: 1 } or { title: "hello" }, { counters: { code: 1 } }, { data: { information: { counters: { code: 1 } } } }
266 */
267 ColumnMetadata.prototype.getEntityValueMap = function (entity, options) {
268 var _this = this;
269 var _a, _b;
270 var returnNulls = false; // options && options.skipNulls === false ? false : true; // todo: remove if current will not bring problems, uncomment if it will.
271 // extract column value from embeds of entity if column is in embedded
272 if (this.embeddedMetadata) {
273 // example: post[data][information][counters].id where "data", "information" and "counters" are embeddeds
274 // we need to get value of "id" column from the post real entity object and return it in a
275 // { data: { information: { counters: { id: ... } } } } format
276 // first step - we extract all parent properties of the entity relative to this column, e.g. [data, information, counters]
277 var propertyNames = tslib_1.__spread(this.embeddedMetadata.parentPropertyNames);
278 // now need to access post[data][information][counters] to get column value from the counters
279 // and on each step we need to create complex literal object, e.g. first { data },
280 // then { data: { information } }, then { data: { information: { counters } } },
281 // then { data: { information: { counters: [this.propertyName]: entity[data][information][counters][this.propertyName] } } }
282 // this recursive function helps doing that
283 var extractEmbeddedColumnValue_2 = function (propertyNames, value, map) {
284 var propertyName = propertyNames.shift();
285 if (value === undefined)
286 return map;
287 if (propertyName) {
288 var submap = {};
289 extractEmbeddedColumnValue_2(propertyNames, value[propertyName], submap);
290 if (Object.keys(submap).length > 0) {
291 map[propertyName] = submap;
292 }
293 return map;
294 }
295 if (value[_this.propertyName] !== undefined && (returnNulls === false || value[_this.propertyName] !== null))
296 map[_this.propertyName] = value[_this.propertyName];
297 return map;
298 };
299 var map = {};
300 extractEmbeddedColumnValue_2(propertyNames, entity, map);
301 return Object.keys(map).length > 0 ? map : undefined;
302 }
303 else { // no embeds - no problems. Simply return column property name and its value of the entity
304 if (this.relationMetadata && entity[this.propertyName] && entity[this.propertyName] instanceof Object) {
305 var map = this.relationMetadata.joinColumns.reduce(function (map, joinColumn) {
306 var value = joinColumn.referencedColumn.getEntityValueMap(entity[_this.propertyName]);
307 if (value === undefined)
308 return map;
309 return OrmUtils_1.OrmUtils.mergeDeep(map, value);
310 }, {});
311 if (Object.keys(map).length > 0)
312 return _a = {}, _a[this.propertyName] = map, _a;
313 return undefined;
314 }
315 else {
316 if (entity[this.propertyName] !== undefined && (returnNulls === false || entity[this.propertyName] !== null))
317 return _b = {}, _b[this.propertyName] = entity[this.propertyName], _b;
318 return undefined;
319 }
320 }
321 };
322 /**
323 * Extracts column value from the given entity.
324 * If column is in embedded (or recursive embedded) it extracts its value from there.
325 */
326 ColumnMetadata.prototype.getEntityValue = function (entity, transform) {
327 if (transform === void 0) { transform = false; }
328 if (entity === undefined || entity === null)
329 return undefined;
330 // extract column value from embeddeds of entity if column is in embedded
331 var value = undefined;
332 if (this.embeddedMetadata) {
333 // example: post[data][information][counters].id where "data", "information" and "counters" are embeddeds
334 // we need to get value of "id" column from the post real entity object
335 // first step - we extract all parent properties of the entity relative to this column, e.g. [data, information, counters]
336 var propertyNames = tslib_1.__spread(this.embeddedMetadata.parentPropertyNames);
337 // next we need to access post[data][information][counters][this.propertyName] to get column value from the counters
338 // this recursive function takes array of generated property names and gets the post[data][information][counters] embed
339 var extractEmbeddedColumnValue_3 = function (propertyNames, value) {
340 var propertyName = propertyNames.shift();
341 return propertyName && value ? extractEmbeddedColumnValue_3(propertyNames, value[propertyName]) : value;
342 };
343 // once we get nested embed object we get its column, e.g. post[data][information][counters][this.propertyName]
344 var embeddedObject = extractEmbeddedColumnValue_3(propertyNames, entity);
345 if (embeddedObject) {
346 if (this.relationMetadata && this.referencedColumn) {
347 var relatedEntity = this.relationMetadata.getEntityValue(embeddedObject);
348 if (relatedEntity && relatedEntity instanceof Object && !(relatedEntity instanceof FindOperator_1.FindOperator)) {
349 value = this.referencedColumn.getEntityValue(PromiseUtils_1.PromiseUtils.extractValue(relatedEntity));
350 }
351 else if (embeddedObject[this.propertyName] && embeddedObject[this.propertyName] instanceof Object && !(embeddedObject[this.propertyName] instanceof FindOperator_1.FindOperator)) {
352 value = this.referencedColumn.getEntityValue(PromiseUtils_1.PromiseUtils.extractValue(embeddedObject[this.propertyName]));
353 }
354 else {
355 value = PromiseUtils_1.PromiseUtils.extractValue(embeddedObject[this.propertyName]);
356 }
357 }
358 else if (this.referencedColumn) {
359 value = this.referencedColumn.getEntityValue(PromiseUtils_1.PromiseUtils.extractValue(embeddedObject[this.propertyName]));
360 }
361 else {
362 value = PromiseUtils_1.PromiseUtils.extractValue(embeddedObject[this.propertyName]);
363 }
364 }
365 }
366 else { // no embeds - no problems. Simply return column name by property name of the entity
367 if (this.relationMetadata && this.referencedColumn) {
368 var relatedEntity = this.relationMetadata.getEntityValue(entity);
369 if (relatedEntity && relatedEntity instanceof Object && !(relatedEntity instanceof FindOperator_1.FindOperator) && !(relatedEntity instanceof Function)) {
370 value = this.referencedColumn.getEntityValue(PromiseUtils_1.PromiseUtils.extractValue(relatedEntity));
371 }
372 else if (entity[this.propertyName] && entity[this.propertyName] instanceof Object && !(entity[this.propertyName] instanceof FindOperator_1.FindOperator) && !(entity[this.propertyName] instanceof Function)) {
373 value = this.referencedColumn.getEntityValue(PromiseUtils_1.PromiseUtils.extractValue(entity[this.propertyName]));
374 }
375 else {
376 value = entity[this.propertyName];
377 }
378 }
379 else if (this.referencedColumn) {
380 value = this.referencedColumn.getEntityValue(PromiseUtils_1.PromiseUtils.extractValue(entity[this.propertyName]));
381 }
382 else {
383 value = entity[this.propertyName];
384 }
385 }
386 if (transform && this.transformer)
387 value = ApplyValueTransformers_1.ApplyValueTransformers.transformTo(this.transformer, value);
388 return value;
389 };
390 /**
391 * Sets given entity's column value.
392 * Using of this method helps to set entity relation's value of the lazy and non-lazy relations.
393 */
394 ColumnMetadata.prototype.setEntityValue = function (entity, value) {
395 var _this = this;
396 if (this.embeddedMetadata) {
397 // first step - we extract all parent properties of the entity relative to this column, e.g. [data, information, counters]
398 var extractEmbeddedColumnValue_4 = function (embeddedMetadatas, map) {
399 // if (!object[embeddedMetadata.propertyName])
400 // object[embeddedMetadata.propertyName] = embeddedMetadata.create();
401 var embeddedMetadata = embeddedMetadatas.shift();
402 if (embeddedMetadata) {
403 if (!map[embeddedMetadata.propertyName])
404 map[embeddedMetadata.propertyName] = embeddedMetadata.create();
405 extractEmbeddedColumnValue_4(embeddedMetadatas, map[embeddedMetadata.propertyName]);
406 return map;
407 }
408 map[_this.propertyName] = value;
409 return map;
410 };
411 return extractEmbeddedColumnValue_4(tslib_1.__spread(this.embeddedMetadata.embeddedMetadataTree), entity);
412 }
413 else {
414 entity[this.propertyName] = value;
415 }
416 };
417 // ---------------------------------------------------------------------
418 // Builder Methods
419 // ---------------------------------------------------------------------
420 ColumnMetadata.prototype.build = function (connection) {
421 this.propertyPath = this.buildPropertyPath();
422 this.propertyAliasName = this.propertyPath.replace(".", "_");
423 this.databaseName = this.buildDatabaseName(connection);
424 this.databasePath = this.buildDatabasePath();
425 this.databaseNameWithoutPrefixes = connection.namingStrategy.columnName(this.propertyName, this.givenDatabaseName, []);
426 return this;
427 };
428 ColumnMetadata.prototype.buildPropertyPath = function () {
429 var path = "";
430 if (this.embeddedMetadata && this.embeddedMetadata.parentPropertyNames.length)
431 path = this.embeddedMetadata.parentPropertyNames.join(".") + ".";
432 path += this.propertyName;
433 // we add reference column to property path only if this column is virtual
434 // because if its not virtual it means user defined a real column for this relation
435 // also we don't do it if column is inside a junction table
436 if (!this.entityMetadata.isJunction && this.isVirtual && this.referencedColumn && this.referencedColumn.propertyName !== this.propertyName)
437 path += "." + this.referencedColumn.propertyName;
438 return path;
439 };
440 ColumnMetadata.prototype.buildDatabasePath = function () {
441 var path = "";
442 if (this.embeddedMetadata && this.embeddedMetadata.parentPropertyNames.length)
443 path = this.embeddedMetadata.parentPropertyNames.join(".") + ".";
444 path += this.databaseName;
445 // we add reference column to property path only if this column is virtual
446 // because if its not virtual it means user defined a real column for this relation
447 // also we don't do it if column is inside a junction table
448 if (!this.entityMetadata.isJunction && this.isVirtual && this.referencedColumn && this.referencedColumn.databaseName !== this.databaseName)
449 path += "." + this.referencedColumn.databaseName;
450 return path;
451 };
452 ColumnMetadata.prototype.buildDatabaseName = function (connection) {
453 var propertyNames = this.embeddedMetadata ? this.embeddedMetadata.parentPrefixes : [];
454 if (connection.driver instanceof MongoDriver_1.MongoDriver) // we don't need to include embedded name for the mongodb column names
455 propertyNames = [];
456 return connection.namingStrategy.columnName(this.propertyName, this.givenDatabaseName, propertyNames);
457 };
458 return ColumnMetadata;
459}());
460exports.ColumnMetadata = ColumnMetadata;
461
462//# sourceMappingURL=ColumnMetadata.js.map