UNPKG

typeorm

Version:

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

537 lines (535 loc) • 23.3 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); var OrmUtils_1 = require("../util/OrmUtils"); var PostgresDriver_1 = require("../driver/postgres/PostgresDriver"); var SqlServerDriver_1 = require("../driver/sqlserver/SqlServerDriver"); var CannotCreateEntityIdMapError_1 = require("../error/CannotCreateEntityIdMapError"); /** * Contains all entity metadata. */ var EntityMetadata = /** @class */ (function () { // --------------------------------------------------------------------- // Constructor // --------------------------------------------------------------------- function EntityMetadata(options) { /** * Children entity metadatas. Used in inheritance patterns. */ this.childEntityMetadatas = []; /** * All "inheritance tree" from a target entity. * For example for target Post < ContentModel < Unit it will be an array of [Post, ContentModel, Unit]. * It also contains child entities for single table inheritance. */ this.inheritanceTree = []; /** * Table type. Tables can be abstract, closure, junction, embedded, etc. */ this.tableType = "regular"; /** * Indicates if schema will be synchronized for this entity or not. */ this.synchronize = true; /** * Checks if there any non-nullable column exist in this entity. */ this.hasNonNullableRelations = false; /** * Indicates if this entity metadata of a junction table, or not. * Junction table is a table created by many-to-many relationship. * * Its also possible to understand if entity is junction via tableType. */ this.isJunction = false; /** * Checks if this table is a junction table of the closure table. * This type is for tables that contain junction metadata of the closure tables. */ this.isClosureJunction = false; /** * Checks if entity's table has multiple primary columns. */ this.hasMultiplePrimaryKeys = false; /** * Indicates if this entity metadata has uuid generated columns. */ this.hasUUIDGeneratedColumns = false; /** * Entity's column metadatas defined by user. */ this.ownColumns = []; /** * Columns of the entity, including columns that are coming from the embeddeds of this entity. */ this.columns = []; /** * Ancestor columns used only in closure junction tables. */ this.ancestorColumns = []; /** * Descendant columns used only in closure junction tables. */ this.descendantColumns = []; /** * All columns except for virtual columns. */ this.nonVirtualColumns = []; /** * In the case if this entity metadata is junction table's entity metadata, * this will contain all referenced columns of owner entity. */ this.ownerColumns = []; /** * In the case if this entity metadata is junction table's entity metadata, * this will contain all referenced columns of inverse entity. */ this.inverseColumns = []; /** * Gets the column with generated flag. */ this.generatedColumns = []; /** * Gets the primary columns. */ this.primaryColumns = []; /** * Entity's relation metadatas. */ this.ownRelations = []; /** * Relations of the entity, including relations that are coming from the embeddeds of this entity. */ this.relations = []; /** * List of eager relations this metadata has. */ this.eagerRelations = []; /** * List of eager relations this metadata has. */ this.lazyRelations = []; /** * Gets only one-to-one relations of the entity. */ this.oneToOneRelations = []; /** * Gets only owner one-to-one relations of the entity. */ this.ownerOneToOneRelations = []; /** * Gets only one-to-many relations of the entity. */ this.oneToManyRelations = []; /** * Gets only many-to-one relations of the entity. */ this.manyToOneRelations = []; /** * Gets only many-to-many relations of the entity. */ this.manyToManyRelations = []; /** * Gets only owner many-to-many relations of the entity. */ this.ownerManyToManyRelations = []; /** * Gets only owner one-to-one and many-to-one relations. */ this.relationsWithJoinColumns = []; /** * Entity's relation id metadatas. */ this.relationIds = []; /** * Entity's relation id metadatas. */ this.relationCounts = []; /** * Entity's foreign key metadatas. */ this.foreignKeys = []; /** * Entity's embedded metadatas. */ this.embeddeds = []; /** * All embeddeds - embeddeds from this entity metadata and from all child embeddeds, etc. */ this.allEmbeddeds = []; /** * Entity's own indices. */ this.ownIndices = []; /** * Entity's index metadatas. */ this.indices = []; /** * Entity's unique metadatas. */ this.uniques = []; /** * Entity's check metadatas. */ this.checks = []; /** * Entity's own listener metadatas. */ this.ownListeners = []; /** * Entity listener metadatas. */ this.listeners = []; /** * Listener metadatas with "AFTER LOAD" type. */ this.afterLoadListeners = []; /** * Listener metadatas with "AFTER INSERT" type. */ this.beforeInsertListeners = []; /** * Listener metadatas with "AFTER INSERT" type. */ this.afterInsertListeners = []; /** * Listener metadatas with "AFTER UPDATE" type. */ this.beforeUpdateListeners = []; /** * Listener metadatas with "AFTER UPDATE" type. */ this.afterUpdateListeners = []; /** * Listener metadatas with "AFTER REMOVE" type. */ this.beforeRemoveListeners = []; /** * Listener metadatas with "AFTER REMOVE" type. */ this.afterRemoveListeners = []; this.connection = options.connection; this.inheritanceTree = options.inheritanceTree || []; this.inheritancePattern = options.inheritancePattern; this.treeType = options.tableTree ? options.tableTree.type : undefined; this.parentClosureEntityMetadata = options.parentClosureEntityMetadata; this.tableMetadataArgs = options.args; this.target = this.tableMetadataArgs.target; this.tableType = this.tableMetadataArgs.type; } // ------------------------------------------------------------------------- // Public Methods // ------------------------------------------------------------------------- /** * Creates a new entity. */ EntityMetadata.prototype.create = function (queryRunner) { var _this = this; // if target is set to a function (e.g. class) that can be created then create it var ret; if (this.target instanceof Function) { ret = new this.target(); this.lazyRelations.forEach(function (relation) { return _this.connection.relationLoader.enableLazyLoad(relation, ret, queryRunner); }); return ret; } // otherwise simply return a new empty object var newObject = {}; this.lazyRelations.forEach(function (relation) { return _this.connection.relationLoader.enableLazyLoad(relation, newObject, queryRunner); }); return newObject; }; /** * Checks if given entity has an id. */ EntityMetadata.prototype.hasId = function (entity) { if (!entity) return false; return this.primaryColumns.every(function (primaryColumn) { var value = primaryColumn.getEntityValue(entity); return value !== null && value !== undefined && value !== ""; }); }; /** * Checks if given entity / object contains ALL primary keys entity must have. * Returns true if it contains all of them, false if at least one of them is not defined. */ EntityMetadata.prototype.hasAllPrimaryKeys = function (entity) { return this.primaryColumns.every(function (primaryColumn) { var value = primaryColumn.getEntityValue(entity); return value !== null && value !== undefined; }); }; /** * Ensures that given object is an entity id map. * If given id is an object then it means its already id map. * If given id isn't an object then it means its a value of the id column * and it creates a new id map with this value and name of the primary column. */ EntityMetadata.prototype.ensureEntityIdMap = function (id) { if (id instanceof Object) return id; if (this.hasMultiplePrimaryKeys) throw new CannotCreateEntityIdMapError_1.CannotCreateEntityIdMapError(this, id); return this.primaryColumns[0].createValueMap(id); }; /** * Gets primary keys of the entity and returns them in a literal object. * For example, for Post{ id: 1, title: "hello" } where id is primary it will return { id: 1 } * For multiple primary keys it returns multiple keys in object. * For primary keys inside embeds it returns complex object literal with keys in them. */ EntityMetadata.prototype.getEntityIdMap = function (entity) { if (!entity) return undefined; return EntityMetadata.getValueMap(entity, this.primaryColumns, { skipNulls: true }); }; /** * Creates a "mixed id map". * If entity has multiple primary keys (ids) then it will return just regular id map, like what getEntityIdMap returns. * But if entity has a single primary key then it will return just value of the id column of the entity, just value. * This is called mixed id map. */ EntityMetadata.prototype.getEntityIdMixedMap = function (entity) { if (!entity) return entity; var idMap = this.getEntityIdMap(entity); if (this.hasMultiplePrimaryKeys) { return idMap; } else if (idMap) { return this.primaryColumns[0].getEntityValue(idMap); // todo: what about parent primary column? } return idMap; }; /** * Compares two different entities by their ids. * Returns true if they match, false otherwise. */ EntityMetadata.prototype.compareEntities = function (firstEntity, secondEntity) { var firstEntityIdMap = this.getEntityIdMap(firstEntity); if (!firstEntityIdMap) return false; var secondEntityIdMap = this.getEntityIdMap(secondEntity); if (!secondEntityIdMap) return false; return EntityMetadata.compareIds(firstEntityIdMap, secondEntityIdMap); }; /** * Finds column with a given property name. */ EntityMetadata.prototype.findColumnWithPropertyName = function (propertyName) { return this.columns.find(function (column) { return column.propertyName === propertyName; }); }; /** * Finds column with a given database name. */ EntityMetadata.prototype.findColumnWithDatabaseName = function (databaseName) { return this.columns.find(function (column) { return column.databaseName === databaseName; }); }; /** * Finds column with a given property path. */ EntityMetadata.prototype.findColumnWithPropertyPath = function (propertyPath) { var column = this.columns.find(function (column) { return column.propertyPath === propertyPath; }); if (column) return column; // in the case if column with property path was not found, try to find a relation with such property path // if we find relation and it has a single join column then its the column user was seeking var relation = this.relations.find(function (relation) { return relation.propertyPath === propertyPath; }); if (relation && relation.joinColumns.length === 1) return relation.joinColumns[0]; return undefined; }; /** * Finds columns with a given property path. * Property path can match a relation, and relations can contain multiple columns. */ EntityMetadata.prototype.findColumnsWithPropertyPath = function (propertyPath) { var column = this.columns.find(function (column) { return column.propertyPath === propertyPath; }); if (column) return [column]; // in the case if column with property path was not found, try to find a relation with such property path // if we find relation and it has a single join column then its the column user was seeking var relation = this.relations.find(function (relation) { return relation.propertyPath === propertyPath; }); if (relation && relation.joinColumns) return relation.joinColumns; return []; }; /** * Finds relation with the given property path. */ EntityMetadata.prototype.findRelationWithPropertyPath = function (propertyPath) { return this.relations.find(function (relation) { return relation.propertyPath === propertyPath; }); }; /** * Checks if there is an embedded with a given property path. */ EntityMetadata.prototype.hasEmbeddedWithPropertyPath = function (propertyPath) { return this.allEmbeddeds.some(function (embedded) { return embedded.propertyPath === propertyPath; }); }; /** * Finds embedded with a given property path. */ EntityMetadata.prototype.findEmbeddedWithPropertyPath = function (propertyPath) { return this.allEmbeddeds.find(function (embedded) { return embedded.propertyPath === propertyPath; }); }; /** * Iterates through entity and finds and extracts all values from relations in the entity. * If relation value is an array its being flattened. */ EntityMetadata.prototype.extractRelationValuesFromEntity = function (entity, relations) { var relationsAndValues = []; relations.forEach(function (relation) { var value = relation.getEntityValue(entity); if (value instanceof Array) { value.forEach(function (subValue) { return relationsAndValues.push([relation, subValue, relation.inverseEntityMetadata]); }); } else if (value) { relationsAndValues.push([relation, value, relation.inverseEntityMetadata]); } }); return relationsAndValues; }; // ------------------------------------------------------------------------- // Public Static Methods // ------------------------------------------------------------------------- /** * Creates a property paths for a given entity. */ EntityMetadata.createPropertyPath = function (metadata, entity, prefix) { var _this = this; if (prefix === void 0) { prefix = ""; } var paths = []; Object.keys(entity).forEach(function (key) { // check for function is needed in the cases when createPropertyPath used on values containg a function as a value // example: .update().set({ name: () => `SUBSTR('', 1, 2)` }) var parentPath = prefix ? prefix + "." + key : key; if (metadata.hasEmbeddedWithPropertyPath(parentPath)) { var subPaths = _this.createPropertyPath(metadata, entity[key], parentPath); paths.push.apply(paths, subPaths); } else { var path = prefix ? prefix + "." + key : key; paths.push(path); } }); return paths; }; /** * Finds difference between two entity id maps. * Returns items that exist in the first array and absent in the second array. */ EntityMetadata.difference = function (firstIdMaps, secondIdMaps) { return firstIdMaps.filter(function (firstIdMap) { return !secondIdMaps.find(function (secondIdMap) { return OrmUtils_1.OrmUtils.deepCompare(firstIdMap, secondIdMap); }); }); }; /** * Compares ids of the two entities. * Returns true if they match, false otherwise. */ EntityMetadata.compareIds = function (firstId, secondId) { if (firstId === undefined || firstId === null || secondId === undefined || secondId === null) return false; return OrmUtils_1.OrmUtils.deepCompare(firstId, secondId); }; /** * Creates value map from the given values and columns. * Examples of usages are primary columns map and join columns map. */ EntityMetadata.getValueMap = function (entity, columns, options) { return columns.reduce(function (map, column) { var value = column.getEntityValueMap(entity, options); // make sure that none of the values of the columns are not missing if (map === undefined || value === null || value === undefined) return undefined; return column.isObjectId ? Object.assign(map, value) : OrmUtils_1.OrmUtils.mergeDeep(map, value); }, {}); }; // --------------------------------------------------------------------- // Public Builder Methods // --------------------------------------------------------------------- EntityMetadata.prototype.build = function () { var namingStrategy = this.connection.namingStrategy; var entityPrefix = this.connection.options.entityPrefix; this.engine = this.tableMetadataArgs.engine; this.database = this.tableMetadataArgs.database; this.schema = this.tableMetadataArgs.schema || this.connection.options.schema; this.givenTableName = this.tableMetadataArgs.type === "entity-child" && this.parentEntityMetadata ? this.parentEntityMetadata.givenTableName : this.tableMetadataArgs.name; this.synchronize = this.tableMetadataArgs.synchronize === false ? false : true; this.targetName = this.tableMetadataArgs.target instanceof Function ? this.tableMetadataArgs.target.name : this.tableMetadataArgs.target; if (this.tableMetadataArgs.type === "closure-junction") { this.tableNameWithoutPrefix = namingStrategy.closureJunctionTableName(this.givenTableName); } else if (this.tableMetadataArgs.type === "entity-child" && this.parentEntityMetadata) { this.tableNameWithoutPrefix = namingStrategy.tableName(this.parentEntityMetadata.targetName, this.parentEntityMetadata.givenTableName); } else { this.tableNameWithoutPrefix = namingStrategy.tableName(this.targetName, this.givenTableName); } this.tableName = entityPrefix ? namingStrategy.prefixTableName(entityPrefix, this.tableNameWithoutPrefix) : this.tableNameWithoutPrefix; this.target = this.target ? this.target : this.tableName; this.name = this.targetName ? this.targetName : this.tableName; this.tablePath = this.buildTablePath(); this.schemaPath = this.buildSchemaPath(); this.orderBy = (this.tableMetadataArgs.orderBy instanceof Function) ? this.tableMetadataArgs.orderBy(this.propertiesMap) : this.tableMetadataArgs.orderBy; // todo: is propertiesMap available here? Looks like its not this.isJunction = this.tableMetadataArgs.type === "closure-junction" || this.tableMetadataArgs.type === "junction"; this.isClosureJunction = this.tableMetadataArgs.type === "closure-junction"; }; /** * Registers a new column in the entity and recomputes all depend properties. */ EntityMetadata.prototype.registerColumn = function (column) { if (this.ownColumns.indexOf(column) !== -1) return; this.ownColumns.push(column); this.columns = this.embeddeds.reduce(function (columns, embedded) { return columns.concat(embedded.columnsFromTree); }, this.ownColumns); this.primaryColumns = this.columns.filter(function (column) { return column.isPrimary; }); this.hasMultiplePrimaryKeys = this.primaryColumns.length > 1; this.hasUUIDGeneratedColumns = this.columns.filter(function (column) { return column.isGenerated || column.generationStrategy === "uuid"; }).length > 0; this.propertiesMap = this.createPropertiesMap(); if (this.childEntityMetadatas) this.childEntityMetadatas.forEach(function (entityMetadata) { return entityMetadata.registerColumn(column); }); }; /** * Creates a special object - all columns and relations of the object (plus columns and relations from embeds) * in a special format - { propertyName: propertyName }. * * example: Post{ id: number, name: string, counterEmbed: { count: number }, category: Category }. * This method will create following object: * { id: "id", counterEmbed: { count: "counterEmbed.count" }, category: "category" } */ EntityMetadata.prototype.createPropertiesMap = function () { var map = {}; this.columns.forEach(function (column) { return OrmUtils_1.OrmUtils.mergeDeep(map, column.createValueMap(column.propertyPath)); }); this.relations.forEach(function (relation) { return OrmUtils_1.OrmUtils.mergeDeep(map, relation.createValueMap(relation.propertyPath)); }); return map; }; /** * Builds table path using database name, schema name and table name. */ EntityMetadata.prototype.buildTablePath = function () { var tablePath = this.tableName; if (this.schema) tablePath = this.schema + "." + tablePath; if (this.database && !(this.connection.driver instanceof PostgresDriver_1.PostgresDriver)) { if (!this.schema && this.connection.driver instanceof SqlServerDriver_1.SqlServerDriver) { tablePath = this.database + ".." + tablePath; } else { tablePath = this.database + "." + tablePath; } } return tablePath; }; /** * Builds table path using schema name and database name. */ EntityMetadata.prototype.buildSchemaPath = function () { if (!this.schema) return undefined; return this.database && !(this.connection.driver instanceof PostgresDriver_1.PostgresDriver) ? this.database + "." + this.schema : this.schema; }; return EntityMetadata; }()); exports.EntityMetadata = EntityMetadata; //# sourceMappingURL=EntityMetadata.js.map