UNPKG

typeorm

Version:

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

325 lines (323 loc) • 12.8 kB
import { Alias } from "./Alias"; import { JoinAttribute } from "./JoinAttribute"; import { RelationIdAttribute } from "./relation-id/RelationIdAttribute"; import { RelationCountAttribute } from "./relation-count/RelationCountAttribute"; import { TypeORMError } from "../error"; /** * Contains all properties of the QueryBuilder that needs to be build a final query. */ export class QueryExpressionMap { // ------------------------------------------------------------------------- // Constructor // ------------------------------------------------------------------------- constructor(connection) { this.connection = connection; // ------------------------------------------------------------------------- // Public Properties // ------------------------------------------------------------------------- /** * Strategy to load relations. */ this.relationLoadStrategy = "join"; /** * Indicates if QueryBuilder used to select entities and not a raw results. */ this.queryEntity = false; /** * All aliases (including main alias) used in the query. */ this.aliases = []; /** * Represents query type. QueryBuilder is able to build SELECT, UPDATE and DELETE queries. */ this.queryType = "select"; /** * Data needs to be SELECT-ed. */ this.selects = []; /** * Max execution time in millisecond. */ this.maxExecutionTime = 0; /** * Whether SELECT is DISTINCT. */ this.selectDistinct = false; /** * SELECT DISTINCT ON query (postgres). */ this.selectDistinctOn = []; /** * Extra returning columns to be added to the returning statement if driver supports it. */ this.extraReturningColumns = []; /** * Optional on conflict statement used in insertion query in postgres. */ this.onConflict = ""; /** * Optional on ignore statement used in insertion query in databases. */ this.onIgnore = false; /** * JOIN queries. */ this.joinAttributes = []; /** * RelationId queries. */ this.relationIdAttributes = []; /** * Relation count queries. */ this.relationCountAttributes = []; /** * WHERE queries. */ this.wheres = []; /** * HAVING queries. */ this.havings = []; /** * ORDER BY queries. */ this.orderBys = {}; /** * GROUP BY queries. */ this.groupBys = []; /** * Indicates if soft-deleted rows should be included in entity result. * By default the soft-deleted rows are not included. */ this.withDeleted = false; /** * Parameters used to be escaped in final query. */ this.parameters = {}; /** * Indicates if alias, table names and column names will be escaped by driver, or not. * * todo: rename to isQuotingDisabled, also think if it should be named "escaping" */ this.disableEscaping = true; /** * Indicates if virtual columns should be included in entity result. * * todo: what to do with it? is it properly used? what about persistence? */ this.enableRelationIdValues = false; /** * Extra where condition appended to the end of original where conditions with AND keyword. * Original condition will be wrapped into brackets. */ this.extraAppendedAndWhereCondition = ""; /** * Indicates if query builder creates a subquery. */ this.subQuery = false; /** * Indicates if property names are prefixed with alias names during property replacement. * By default this is enabled, however we need this because aliases are not supported in UPDATE and DELETE queries, * but user can use them in WHERE expressions. */ this.aliasNamePrefixingEnabled = true; /** * Options that define QueryBuilder behaviour. */ this.options = []; /** * List of columns where data should be inserted. * Used in INSERT query. */ this.insertColumns = []; /** * Used if user wants to update or delete a specific entities. */ this.whereEntities = []; /** * Indicates if entity must be updated after insertion / updation. * This may produce extra query or use RETURNING / OUTPUT statement (depend on database). */ this.updateEntity = true; /** * Indicates if listeners and subscribers must be called before and after query execution. */ this.callListeners = true; /** * Indicates if query must be wrapped into transaction. */ this.useTransaction = false; /** * Extra parameters. * * @deprecated Use standard parameters instead */ this.nativeParameters = {}; /** * Items from an entity that have been locally generated & are recorded here for later use. * Examples include the UUID generation when the database does not natively support it. * These are included in the entity index order. */ this.locallyGenerated = {}; this.commonTableExpressions = []; if (connection.options.relationLoadStrategy) { this.relationLoadStrategy = connection.options.relationLoadStrategy; } this.timeTravel = connection.options ?.timeTravelQueries || false; } // ------------------------------------------------------------------------- // Accessors // ------------------------------------------------------------------------- /** * Get all ORDER BY queries - if order by is specified by user then it uses them, * otherwise it uses default entity order by if it was set. */ get allOrderBys() { if (!Object.keys(this.orderBys).length && this.mainAlias.hasMetadata && this.options.indexOf("disable-global-order") === -1) { const entityOrderBy = this.mainAlias.metadata.orderBy || {}; return Object.keys(entityOrderBy).reduce((orderBy, key) => { orderBy[this.mainAlias.name + "." + key] = entityOrderBy[key]; return orderBy; }, {}); } return this.orderBys; } // ------------------------------------------------------------------------- // Public Methods // ------------------------------------------------------------------------- /** * Creates a main alias and adds it to the current expression map. */ setMainAlias(alias) { // if main alias is already set then remove it from the array // if (this.mainAlias) // this.aliases.splice(this.aliases.indexOf(this.mainAlias)); // set new main alias this.mainAlias = alias; return alias; } /** * Creates a new alias and adds it to the current expression map. */ createAlias(options) { let aliasName = options.name; if (!aliasName && options.tablePath) aliasName = options.tablePath; if (!aliasName && typeof options.target === "function") aliasName = options.target.name; if (!aliasName && typeof options.target === "string") aliasName = options.target; const alias = new Alias(); alias.type = options.type; if (aliasName) alias.name = aliasName; if (options.metadata) alias.metadata = options.metadata; if (options.target && !alias.hasMetadata) alias.metadata = this.connection.getMetadata(options.target); if (options.tablePath) alias.tablePath = options.tablePath; if (options.subQuery) alias.subQuery = options.subQuery; this.aliases.push(alias); return alias; } /** * Finds alias with the given name. * If alias was not found it throw an exception. */ findAliasByName(aliasName) { const alias = this.aliases.find((alias) => alias.name === aliasName); if (!alias) throw new TypeORMError(`"${aliasName}" alias was not found. Maybe you forgot to join it?`); return alias; } findColumnByAliasExpression(aliasExpression) { const [aliasName, propertyPath] = aliasExpression.split("."); const alias = this.findAliasByName(aliasName); return alias.metadata.findColumnWithPropertyName(propertyPath); } /** * Gets relation metadata of the relation this query builder works with. * * todo: add proper exceptions */ get relationMetadata() { if (!this.mainAlias) throw new TypeORMError(`Entity to work with is not specified!`); // todo: better message const relationMetadata = this.mainAlias.metadata.findRelationWithPropertyPath(this.relationPropertyPath); if (!relationMetadata) throw new TypeORMError(`Relation ${this.relationPropertyPath} was not found in entity ${this.mainAlias.name}`); // todo: better message return relationMetadata; } /** * Copies all properties of the current QueryExpressionMap into a new one. * Useful when QueryBuilder needs to create a copy of itself. */ clone() { const map = new QueryExpressionMap(this.connection); map.queryType = this.queryType; map.selects = this.selects.map((select) => select); map.maxExecutionTime = this.maxExecutionTime; map.selectDistinct = this.selectDistinct; map.selectDistinctOn = this.selectDistinctOn; this.aliases.forEach((alias) => map.aliases.push(new Alias(alias))); map.relationLoadStrategy = this.relationLoadStrategy; map.mainAlias = this.mainAlias; map.valuesSet = this.valuesSet; map.returning = this.returning; map.onConflict = this.onConflict; map.onIgnore = this.onIgnore; map.onUpdate = this.onUpdate; map.joinAttributes = this.joinAttributes.map((join) => new JoinAttribute(this.connection, this, join)); map.relationIdAttributes = this.relationIdAttributes.map((relationId) => new RelationIdAttribute(this, relationId)); map.relationCountAttributes = this.relationCountAttributes.map((relationCount) => new RelationCountAttribute(this, relationCount)); map.wheres = this.wheres.map((where) => ({ ...where })); map.havings = this.havings.map((having) => ({ ...having })); map.orderBys = Object.assign({}, this.orderBys); map.groupBys = this.groupBys.map((groupBy) => groupBy); map.limit = this.limit; map.offset = this.offset; map.skip = this.skip; map.take = this.take; map.lockMode = this.lockMode; map.onLocked = this.onLocked; map.lockVersion = this.lockVersion; map.lockTables = this.lockTables; map.withDeleted = this.withDeleted; map.parameters = Object.assign({}, this.parameters); map.disableEscaping = this.disableEscaping; map.enableRelationIdValues = this.enableRelationIdValues; map.extraAppendedAndWhereCondition = this.extraAppendedAndWhereCondition; map.subQuery = this.subQuery; map.aliasNamePrefixingEnabled = this.aliasNamePrefixingEnabled; map.cache = this.cache; map.cacheId = this.cacheId; map.cacheDuration = this.cacheDuration; map.relationPropertyPath = this.relationPropertyPath; map.of = this.of; map.insertColumns = this.insertColumns; map.whereEntities = this.whereEntities; map.updateEntity = this.updateEntity; map.callListeners = this.callListeners; map.useTransaction = this.useTransaction; map.timeTravel = this.timeTravel; map.nativeParameters = Object.assign({}, this.nativeParameters); map.comment = this.comment; map.commonTableExpressions = this.commonTableExpressions.map((cteOptions) => ({ alias: cteOptions.alias, queryBuilder: typeof cteOptions.queryBuilder === "string" ? cteOptions.queryBuilder : cteOptions.queryBuilder.clone(), options: cteOptions.options, })); return map; } } //# sourceMappingURL=QueryExpressionMap.js.map