typeorm
Version:
Data-Mapper ORM for TypeScript, ES7, ES6, ES5. Supports MySQL, PostgreSQL, MariaDB, SQLite, MS SQL Server, Oracle, WebSQL, MongoDB databases.
432 lines (430 loc) • 18.5 kB
JavaScript
import { LazyRelationsWrapper } from "../lazy-loading/LazyRelationsWrapper";
import { OrmUtils } from "../util/OrmUtils";
import { PostgresDriver } from "../driver/postgres/PostgresDriver";
import { SqlServerDriver } from "../driver/sqlserver/SqlServerDriver";
/**
* Contains all entity metadata.
*/
var EntityMetadata = /** @class */ (function () {
// ---------------------------------------------------------------------
// Constructor
// ---------------------------------------------------------------------
function EntityMetadata(options) {
/**
* Children entity metadatas. Used in inheritance patterns.
*/
this.childEntityMetadatas = [];
/**
* Table type. Tables can be abstract, closure, junction, embedded, etc.
*/
this.tableType = "regular";
/**
* 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;
/**
* Entity's column metadatas defined by user.
*/
this.ownColumns = [];
/**
* Entity's relation metadatas.
*/
this.ownRelations = [];
/**
* Entity's own listener metadatas.
*/
this.ownListeners = [];
/**
* Entity's own indices.
*/
this.ownIndices = [];
/**
* 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 = [];
/**
* Columns of the entity, including columns that are coming from the embeddeds of this entity.
*/
this.columns = [];
/**
* 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 = [];
/**
* Entity's relation id metadatas.
*/
this.relationIds = [];
/**
* Entity's relation id metadatas.
*/
this.relationCounts = [];
/**
* Entity's index metadatas.
*/
this.indices = [];
/**
* Entity's foreign key metadatas.
*/
this.foreignKeys = [];
/**
* Entity's embedded metadatas.
*/
this.embeddeds = [];
/**
* Entity listener metadatas.
*/
this.listeners = [];
/**
* Gets the column with generated flag.
*/
this.generatedColumns = [];
/**
* Gets the primary columns.
*/
this.primaryColumns = [];
/**
* Id columns in the parent table (used in table inheritance).
*/
this.parentIdColumns = [];
/**
* 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 = [];
var namingStrategy = options.connection.namingStrategy;
var entityPrefix = options.connection.options.entityPrefix;
this.lazyRelationsWrapper = new LazyRelationsWrapper(options.connection);
this.parentClosureEntityMetadata = options.parentClosureEntityMetadata;
this.target = options.args.target;
this.tableType = options.args.type;
this.engine = options.args.engine;
this.database = options.args.database;
this.schema = options.args.schema || options.connection.options.schema;
this.givenTableName = options.args.name;
this.skipSync = options.args.skipSync || false;
this.targetName = options.args.target instanceof Function ? options.args.target.name : options.args.target;
this.tableNameWithoutPrefix = this.tableType === "closure-junction" ? namingStrategy.closureJunctionTableName(this.givenTableName) : 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(options.connection.driver);
this.schemaPath = this.buildSchemaPath(options.connection.driver);
this.isClassTableChild = this.tableType === "class-table-child";
this.isSingleTableChild = this.tableType === "single-table-child";
this.isEmbeddable = this.tableType === "embeddable";
this.isJunction = this.tableType === "closure-junction" || this.tableType === "junction";
this.isClosureJunction = this.tableType === "closure-junction";
this.isClosure = this.tableType === "closure";
this.isAbstract = this.tableType === "abstract";
this.isRegular = this.tableType === "regular";
this.orderBy = (options.args.orderBy instanceof Function) ? options.args.orderBy(this.propertiesMap) : options.args.orderBy;
}
// -------------------------------------------------------------------------
// Public Methods
// -------------------------------------------------------------------------
/**
* Creates a new entity.
*/
EntityMetadata.prototype.create = function () {
var _this = this;
// if target is set to a function (e.g. class) that can be created then create it
if (this.target instanceof Function)
return new this.target();
// otherwise simply return a new empty object
var newObject = {};
this.relations
.filter(function (relation) { return relation.isLazy; })
.forEach(function (relation) { return _this.lazyRelationsWrapper.wrap(newObject, relation); });
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 !== "";
});
};
/**
* Compares ids of the two entities.
* Returns true if they match, false otherwise.
*/
EntityMetadata.prototype.compareIds = function (firstId, secondId) {
if (firstId === undefined || firstId === null || secondId === undefined || secondId === null)
return false;
return OrmUtils.deepCompare(firstId, secondId);
};
/**
* Compares two different entity instances by their ids.
* Returns true if they match, false otherwise.
*/
EntityMetadata.prototype.compareEntities = function (firstEntity, secondEntity) {
// if any entity ids are empty then they aren't equal
var isFirstEntityEmpty = this.isEntityMapEmpty(firstEntity);
var isSecondEntityEmpty = this.isEntityMapEmpty(secondEntity);
if (isFirstEntityEmpty || isSecondEntityEmpty)
return false;
var firstEntityIds = this.getEntityIdMap(firstEntity);
var secondEntityIds = this.getEntityIdMap(secondEntity);
return this.compareIds(firstEntityIds, secondEntityIds);
};
/**
* Checks if there is an embedded with a given property path.
*/
EntityMetadata.prototype.hasEmbeddedWithPropertyPath = function (propertyPath) {
return !!this.findEmbeddedWithPropertyPath(propertyPath);
};
/**
* Finds embedded with a given property path.
*/
EntityMetadata.prototype.findEmbeddedWithPropertyPath = function (propertyPath) {
return this.embeddeds.find(function (embedded) {
return embedded.propertyPath === propertyPath;
});
};
/**
* 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 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 column with a given database name.
*/
EntityMetadata.prototype.findColumnWithDatabaseName = function (databaseName) {
return this.columns.find(function (column) { return column.databaseName === databaseName; });
};
/**
* Finds relation with the given name.
*/
EntityMetadata.prototype.findRelationWithDbName = function (dbName) {
return this.relationsWithJoinColumns.find(function (relation) {
return !!relation.joinColumns.find(function (column) { return column.databaseName === dbName; });
});
};
/**
* Finds relation with the given property path.
*/
EntityMetadata.prototype.findRelationWithPropertyPath = function (propertyPath) {
return this.relations.find(function (relation) { return relation.propertyPath === propertyPath; });
};
/**
* Computes property name of the entity using given PropertyTypeInFunction.
*/
EntityMetadata.prototype.computePropertyPath = function (nameOrFn) {
return typeof nameOrFn === "string" ? nameOrFn : nameOrFn(this.propertiesMap);
};
/**
* Creates entity id map from the given entity ids array.
*/
EntityMetadata.prototype.createEntityIdMap = function (ids) {
if (!(ids instanceof Array))
ids = [ids];
return this.primaryColumns.reduce(function (map, column, index) {
return OrmUtils.mergeDeep(map, column.createValueMap(ids[index]));
}, {});
};
/**
* Checks each id in the given entity id map if they all aren't empty.
* If they all aren't empty it returns true.
* If at least one id in the given map is empty it returns false.
*/
EntityMetadata.prototype.isEntityMapEmpty = function (entity) {
return !this.primaryColumns.every(function (column) {
var value = column.getEntityValue(entity);
return value !== null && value !== undefined;
});
};
/**
* 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;
var map = this.primaryColumns.reduce(function (map, column) {
if (column.isObjectId)
return Object.assign(map, column.getEntityValueMap(entity));
return OrmUtils.mergeDeep(map, column.getEntityValueMap(entity));
}, {});
return Object.keys(map).length > 0 ? map : undefined;
};
/**
* Same as getEntityIdMap, but instead of id column property names it returns database column names.
*/
EntityMetadata.prototype.getDatabaseEntityIdMap = function (entity) {
var map = {};
this.primaryColumns.forEach(function (column) {
var entityValue = column.getEntityValue(entity);
if (entityValue === null || entityValue === undefined)
return;
map[column.databaseName] = entityValue;
});
var hasAllIds = Object.keys(map).every(function (key) {
return map[key] !== undefined && map[key] !== null;
});
return hasAllIds ? map : undefined;
};
/**
* 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 idMap[this.primaryColumns[0].propertyName]; // todo: what about parent primary column?
}
return idMap;
};
/**
* Checks if given 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.checkIfObjectContainsAllPrimaryKeys = function (object) {
return this.primaryColumns.every(function (primaryColumn) {
return object.hasOwnProperty(primaryColumn.propertyName);
});
};
/**
* Iterates throw 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 Builder Methods
// ---------------------------------------------------------------------
/**
* Registers a new column in the entity and recomputes all depend properties.
*/
EntityMetadata.prototype.registerColumn = function (column) {
this.ownColumns.push(column);
this.columns = this.embeddeds.reduce(function (columns, embedded) { return columns.concat(embedded.columnsFromTree); }, this.ownColumns);
this.parentIdColumns = this.columns.filter(function (column) { return column.isParentId; });
this.primaryColumns = this.columns.filter(function (column) { return column.isPrimary; });
this.hasMultiplePrimaryKeys = this.primaryColumns.length > 1;
this.propertiesMap = this.createPropertiesMap();
};
/**
* 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.mergeDeep(map, column.createValueMap(column.propertyPath)); });
this.relations.forEach(function (relation) { return OrmUtils.mergeDeep(map, relation.createValueMap(relation.propertyPath)); });
return map;
};
// ---------------------------------------------------------------------
// Protected Methods
// ---------------------------------------------------------------------
/**
* Builds table path using database name and schema name and table name.
*/
EntityMetadata.prototype.buildTablePath = function (driver) {
var tablePath = this.tableName;
if (this.schema)
tablePath = this.schema + "." + tablePath;
if (this.database && !(driver instanceof PostgresDriver)) {
if (!this.schema && driver instanceof 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 (driver) {
if (!this.schema)
return undefined;
return this.database && !(driver instanceof PostgresDriver) ? this.database + "." + this.schema : this.schema;
};
return EntityMetadata;
}());
export { EntityMetadata };
//# sourceMappingURL=EntityMetadata.js.map