import { nameof } from "ts-simple-nameof" ; import { Brackets , ObjectLiteral , SelectQueryBuilder , WhereExpression } from "typeorm" ; import { SqlConstants } from "../constants/SqlConstants" ; import { QueryMode } from "../enums/QueryMode" ; import { QueryWhereType } from "../enums/QueryWhereType" ; import { ComparableValue } from "../types/ComparableValue" ; import { EntityBase } from "../types/EntityBase" ; import { JoinedEntityType } from "../types/JoinedEntityType" ; import { QueryConditionOptions } from "../types/QueryConditionOptions" ; import { QueryConditionOptionsInternal } from "../types/QueryConditionOptionsInternal" ; import { QueryOrderOptions } from "../types/QueryOrderOptions" ; import { IComparableQuery } from "./interfaces/IComparableQuery" ; import { IJoinedComparableQuery } from "./interfaces/IJoinedComparableQuery" ; import { IJoinedQuery } from "./interfaces/IJoinedQuery" ; import { IQuery } from "./interfaces/IQuery" ; import { IQueryBuilderPart } from "./interfaces/IQueryBuilderPart" ; import { IQueryInternal } from "./interfaces/IQueryInternal" ; import { ISelectQuery } from "./interfaces/ISelectQuery" ; import { ISelectQueryInternal } from "./interfaces/ISelectQueryInternal" ; import { QueryBuilderPart } from "./QueryBuilderPart" ; export class Query < T extends EntityBase , R extends T | T [ ] , P = T > implements IQuery < T , R , P > , IJoinedQuery < T , R , P > , IComparableQuery < T , R , P > , IJoinedComparableQuery < T , R , P > , IQueryInternal < T , R , P > , ISelectQueryInternal < T , R , P > { private readonly _duplicateAliasHistory : string [ ] ; private readonly _getAction : ( ) => Promise < R > ; private readonly _includeAliasHistory : string [ ] ; private readonly _initialAlias : string ; private readonly _query : SelectQueryBuilder < T > ; private readonly _queryParts : IQueryBuilderPart < T > [ ] ; private _lastAlias : string ; private _queryMode : QueryMode ; private _queryWhereType : QueryWhereType ; private _selectedProperty : string ; param queryBuilder param getAction public constructor ( queryBuilder , getAction , includeAliasHistory = [ ] ) { this . _duplicateAliasHistory = [ ] ; this . _getAction = getAction ; this . _includeAliasHistory = includeAliasHistory ; this . _initialAlias = queryBuilder . alias ; this . _query = queryBuilder ; this . _queryParts = [ ] ; this . _lastAlias = this . _initialAlias ; this . _queryMode = QueryMode . Get ; this . _queryWhereType = QueryWhereType . Normal ; this . _selectedProperty = "" ; } public get getAction ( ) : ( ) => Promise < R > { return this . _getAction ; } public get query ( ) : SelectQueryBuilder < T > { return this . _query ; } public get queryParts ( ) : IQueryBuilderPart < T > [ ] { return this . _queryParts ; } public get selected ( ) : string { return this . _selectedProperty ; } public and < S extends Object , PP = P , RR = P > ( propertySelector ) { return this . andOr ( propertySelector , SqlConstants . OPERATOR_AND , this . _query . andWhere ) ; } public andAny < S extends Object > ( relationSelector , relationCountPropSelector , conditionPropSelector ? ) { return this . relationCount ( relationSelector , relationCountPropSelector , this . _query . andHaving , ">" , conditionPropSelector ) ; } public andNone < S extends Object > ( relationSelector , relationCountPropSelector , conditionPropSelector ? ) { return this . relationCount ( relationSelector , relationCountPropSelector , this . _query . andHaving , "=" , conditionPropSelector ) ; } public beginsWith ( value , options ? ) { return this . completeWhere ( SqlConstants . OPERATOR_LIKE , value , { beginsWith : true } , options ) ; } public async catch ( rejected ) { return this . toPromise ( ) . catch ( rejected ) ; } public contains ( value , options ? ) { return this . completeWhere ( SqlConstants . OPERATOR_LIKE , value , { beginsWith : true , endsWith : true } , options ) ; } public count ( ) { const targetQueryBuilder = this . _query . clone ( ) ; this . compileQueryParts ( this . _queryParts , targetQueryBuilder ) ; return targetQueryBuilder . getCount ( ) ; } public endsWith ( value , options ? ) { return this . completeWhere ( SqlConstants . OPERATOR_LIKE , value , { endsWith : true } , options ) ; } public equal ( value , options ? ) { return this . completeWhere ( SqlConstants . OPERATOR_EQUAL , value , null , options ) ; } public equalJoined ( selector , options ? ) { return this . completeJoinedWhere ( SqlConstants . OPERATOR_EQUAL , selector , options ) ; } public from < F extends EntityBase > ( foreignEntity ) { return this . joinForeignEntity ( foreignEntity ) ; } public greaterThan ( value ) { return this . completeWhere ( SqlConstants . OPERATOR_GREATER , value ) ; } public greaterThanJoined ( selector ) { return this . completeJoinedWhere ( SqlConstants . OPERATOR_GREATER , selector ) ; } public greaterThanOrEqual ( value ) { return this . completeWhere ( SqlConstants . OPERATOR_GREATER_EQUAL , value ) ; } public greaterThanOrEqualJoined ( selector ) { return this . completeJoinedWhere ( SqlConstants . OPERATOR_GREATER_EQUAL , selector ) ; } public groupBy ( propertySelector ) { const propertyName = nameof < P > ( propertySelector ) ; const groupProperty = `${ this . _lastAlias }.${ propertyName }` ; return this . completeGroupBy ( this . _query . groupBy , groupProperty ) ; } public in ( include , options ? ) { this . escapeStringArray ( include as string [ ] ) ; return this . completeWhere ( SqlConstants . OPERATOR_IN , `(${ include . join ( ", " ) })` , { quoteString : false } , options ) ; } public include < S > ( propertySelector ) { return this . includePropertyUsingAlias < S > ( propertySelector , this . _initialAlias ) ; } public inSelected < TI extends EntityBase , RI extends TI | TI [ ] , PI1 = TI > ( innerQuery ) { return this . includeOrExcludeFromInnerQuery ( < ISelectQueryInternal < TI , RI , PI1 > > innerQuery , SqlConstants . OPERATOR_IN ) ; } public isFalse ( ) { this . equal ( false ) ; return this ; } public isNotNull ( ) { return this . completeWhere ( SqlConstants . OPERATOR_IS , SqlConstants . OPERATOR_NOT_NULL , { quoteString : false } ) ; } public isNull ( ) { return this . completeWhere ( SqlConstants . OPERATOR_IS , SqlConstants . OPERATOR_NULL , { quoteString : false } ) ; } public isolatedAnd < S extends Object > ( and ) { return < IQuery < T , R , P > > < any > this . isolatedConditions < T , P > ( < ( ) => IQuery < T , R , P > > < any > and , this . _query . andWhere ) ; } public isolatedOr < S extends Object > ( and ) { return < IQuery < T , R , P > > < any > this . isolatedConditions < T , P > ( < ( ) => IQuery < T , R , P > > < any > and , this . _query . orWhere ) ; } public isolatedWhere < S extends Object > ( where ) { return this . isolatedConditions < T , S > ( < ( ) => IQuery < T , R , S > > < any > where , this . _query . where ) ; } public isTrue ( ) { this . equal ( true ) ; return this ; } public join < S extends Object > ( propertySelector ) { return this . joinPropertyUsingAlias ( propertySelector , this . _initialAlias ) ; } public joinAlso < S extends Object > ( propertySelector ) { return this . joinPropertyUsingAlias ( propertySelector , this . _initialAlias , this . _query . leftJoin ) ; } public lessThan ( value ) { return this . completeWhere ( SqlConstants . OPERATOR_LESS , value ) ; } public lessThanJoined ( selector ) { return this . completeJoinedWhere ( SqlConstants . OPERATOR_LESS , selector ) ; } public lessThanOrEqual ( value ) { return this . completeWhere ( SqlConstants . OPERATOR_LESS_EQUAL , value ) ; } public lessThanOrEqualJoined ( selector ) { return this . completeJoinedWhere ( SqlConstants . OPERATOR_LESS_EQUAL , selector ) ; } public notEqual ( value , options ? ) { return this . completeWhere ( SqlConstants . OPERATOR_NOT_EQUAL , value , null , options ) ; } public notEqualJoined ( selector , options ? ) { return this . completeJoinedWhere ( SqlConstants . OPERATOR_NOT_EQUAL , selector , options ) ; } public notIn ( exclude , options ? ) { this . escapeStringArray ( exclude as string [ ] ) ; return this . completeWhere ( SqlConstants . OPERATOR_NOT_IN , `(${ exclude . join ( ", " ) })` , { quoteString : false } , options ) ; } public notInSelected < TI extends EntityBase , RI extends TI | TI [ ] , PI1 = TI > ( innerQuery ) { return this . includeOrExcludeFromInnerQuery ( < ISelectQueryInternal < TI , RI , PI1 > > innerQuery , SqlConstants . OPERATOR_NOT_IN ) ; } public or < S extends Object > ( propertySelector ) { return this . andOr ( propertySelector , SqlConstants . OPERATOR_OR , this . _query . orWhere ) ; } public orAny < S extends Object > ( relationSelector , relationCountPropSelector , conditionPropSelector ? ) { return this . relationCount ( relationSelector , relationCountPropSelector , this . _query . orHaving , ">" , conditionPropSelector ) ; } public orderBy ( propertySelector , options ? ) { const propertyName = nameof < P > ( propertySelector ) ; const orderProperty = `${ this . _lastAlias }.${ propertyName }` ; return this . completeOrderBy ( this . _query . orderBy , [ orderProperty , "ASC" ] , options ) ; } public orderByDescending ( propertySelector , options ? ) { const propertyName = nameof < P > ( propertySelector ) ; const orderProperty = `${ this . _lastAlias }.${ propertyName }` ; return this . completeOrderBy ( this . _query . orderBy , [ orderProperty , "DESC" ] , options ) ; } public orNone < S extends Object > ( relationSelector , relationCountPropSelector , conditionPropSelector ? ) { return this . relationCount ( relationSelector , relationCountPropSelector , this . _query . orHaving , "=" , conditionPropSelector ) ; } public reset ( ) { this . _lastAlias = this . _initialAlias ; this . _queryWhereType = QueryWhereType . Normal ; return < IQuery < T , R , T > > < any > this ; } public select ( propertySelector ) { const selectedProperty = nameof ( propertySelector ) ; let alias = null ; if ( this . _queryMode === QueryMode . Get ) { alias = this . _initialAlias ; } else { alias = this . _lastAlias ; } this . _selectedProperty = `${ alias }.${ selectedProperty }` ; return this ; } public skip ( skip ) { if ( skip > 0 ) { this . _queryParts . push ( new QueryBuilderPart ( this . _query . skip , [ skip ] ) ) ; } return this ; } public take ( limit ) { if ( limit > 0 ) { this . _queryParts . push ( new QueryBuilderPart ( this . _query . take , [ limit ] ) ) ; } return this ; } public async then ( resolved ) { return this . toPromise ( ) . then ( resolved ) ; } public thenBy ( propertySelector , options ? ) { const propertyName = nameof < P > ( propertySelector ) ; const orderProperty = `${ this . _lastAlias }.${ propertyName }` ; return this . completeOrderBy ( this . _query . addOrderBy , [ orderProperty , "ASC" ] , options ) ; } public thenByDescending ( propertySelector , options ? ) { const propertyName = nameof < P > ( propertySelector ) ; const orderProperty = `${ this . _lastAlias }.${ propertyName }` ; return this . completeOrderBy ( this . _query . addOrderBy , [ orderProperty , "DESC" ] , options ) ; } public thenGroupBy ( propertySelector ) { const propertyName = nameof < P > ( propertySelector ) ; const groupProperty = `${ this . _lastAlias }.${ propertyName }` ; return this . completeGroupBy ( this . _query . addGroupBy , groupProperty ) ; } public thenInclude < S extends Object > ( propertySelector ) { return this . includePropertyUsingAlias < S > ( propertySelector , this . _lastAlias ) ; } public thenJoin < S extends Object > ( propertySelector ) { return this . joinPropertyUsingAlias ( propertySelector , this . _lastAlias ) ; } public thenJoinAlso < S extends Object > ( propertySelector ) { return this . joinPropertyUsingAlias ( propertySelector , this . _lastAlias , this . _query . leftJoin ) ; } public toPromise ( ) { return this . _getAction . call ( this . buildQuery ( this ) ) ; } public usingBaseType ( ) { this . _lastAlias = this . _initialAlias ; return < IQuery < T , R , T > > < any > this ; } public where < S extends Object , F = T | P > ( propertySelector ) { const whereProperties = nameof < F > ( propertySelector ) ; let whereProperty = null ; let lastAlias = this . _lastAlias ; if ( this . _queryMode === QueryMode . Get ) { this . _queryWhereType = QueryWhereType . Normal ; lastAlias = this . _lastAlias = this . _initialAlias ; whereProperty = this . joinMultipleProperties ( whereProperties ) ; const where = `${ this . _lastAlias }.${ whereProperty }` ; this . _queryParts . push ( new QueryBuilderPart ( this . _query . where , [ where ] ) ) ; } else { whereProperty = this . joinMultipleProperties ( whereProperties ) ; this . _queryWhereType = QueryWhereType . Joined ; this . createJoinCondition ( whereProperty ) ; } this . _lastAlias = lastAlias ; this . _queryMode = QueryMode . Compare ; return < IComparableQuery < T , R , T > > < any > this ; } public whereAny < S extends Object > ( relationSelector , relationCountPropSelector , conditionPropSelector ? ) { return this . relationCount ( relationSelector , relationCountPropSelector , this . _query . having , ">" , conditionPropSelector ) ; } public whereNone < S extends Object > ( relationSelector , relationCountPropSelector , conditionPropSelector ? ) { return this . relationCount ( relationSelector , relationCountPropSelector , this . _query . having , "=" , conditionPropSelector ) ; } private addJoinCondition ( whereProperty , condition , targetQueryPart = null ) { const part = targetQueryPart || this . _queryParts . pop ( ) ; let joinCondition = ( < [ string ] > part . queryParams ) . pop ( ) ; joinCondition += ` ${ condition } ${ this . _lastAlias }.${ whereProperty }` ; ( < [ string ] > part . queryParams ) . push ( joinCondition ) ; if ( ! targetQueryPart ) { this . _queryParts . push ( part ) ; } } private andOr < S extends Object , PP = P , RR = P > ( propertySelector , operation , queryAction ) { const whereProperties = nameof < PP > ( propertySelector ) ; const joinAction = operation === "AND" ? this . _query . innerJoin : this . _query . leftJoin ; const lastAlias = this . _lastAlias ; const whereProperty = this . joinMultipleProperties ( whereProperties , joinAction ) ; if ( this . _queryWhereType === QueryWhereType . Joined && this . _queryParts [ this . _queryParts . length - 1 ] . queryParams . length === 3 ) { this . addJoinCondition ( whereProperty , operation ) ; } else { const where = `${ this . _lastAlias }.${ whereProperty }` ; this . _queryParts . push ( new QueryBuilderPart ( queryAction , [ where ] ) ) ; } this . _lastAlias = lastAlias ; this . _queryMode = QueryMode . Compare ; return < IComparableQuery < T , R , RR > > < any > this ; } private buildQuery ( query ) { this . compileQueryParts ( query . queryParts , query . query ) ; return query . query ; } private compileQueryParts < PT > ( queryParts , builder ) { if ( queryParts . length ) { for ( const queryPart of queryParts ) { queryPart . queryAction . call ( builder , ... queryPart . queryParams ) ; } } } private completeGroupBy ( groupAction , groupProperty ) { this . _queryParts . push ( new QueryBuilderPart ( groupAction , [ groupProperty ] ) ) ; return this ; } private completeOrderBy ( orderAction , orderParams , options ? ) { if ( options ) { if ( typeof ( options . nullsFirst ) === "boolean" ) { orderParams . push ( options . nullsFirst ? "NULLS FIRST" : "NULLS LAST" ) ; } } this . _queryParts . push ( new QueryBuilderPart ( orderAction , orderParams ) ) ; return this ; } private completeJoinedWhere ( operator , selector , options ? ) { const selectedProperty = nameof < P > ( selector ) ; const compareValue = `${ this . _lastAlias }.${ selectedProperty }` ; return this . completeWhere ( operator , compareValue , { joiningString : ! ! options && typeof ( options . matchCase ) === "boolean" , quoteString : false } , options ) ; } private completeWhere ( operator , value , optionsInternal ? , options ? ) { let beginsWith = false ; let endsWith = false ; let joiningString = false ; let quoteString = true ; let matchCase = false ; if ( optionsInternal ) { if ( typeof ( optionsInternal . beginsWith ) === "boolean" ) { beginsWith = optionsInternal . beginsWith ; } if ( typeof ( optionsInternal . endsWith ) === "boolean" ) { endsWith = optionsInternal . endsWith ; } if ( typeof ( optionsInternal . joiningString ) === "boolean" ) { joiningString = optionsInternal . joiningString ; } if ( typeof ( optionsInternal . quoteString ) === "boolean" ) { quoteString = optionsInternal . quoteString ; } } if ( options ) { if ( typeof ( options . matchCase ) === "boolean" ) { matchCase = options . matchCase ; } } if ( beginsWith ) { value += "%" ; } if ( endsWith ) { value = `%${ value }` ; } if ( typeof ( value ) === "string" && quoteString ) { value = value . replace ( /'/g , "''" ) ; value = `'${ value }'` ; } const nonWheres = [ ] ; let wherePart = null ; while ( this . _queryParts . length && ! wherePart ) { const part = this . _queryParts . pop ( ) ; if ( ( this . _queryWhereType === QueryWhereType . Normal && ( part . queryAction == this . _query . where || part . queryAction == this . _query . andWhere || part . queryAction == this . _query . orWhere ) ) || ( this . _queryWhereType === QueryWhereType . Joined && ( part . queryAction === this . _query . innerJoin || part . queryAction === this . _query . leftJoin || part . queryAction === this . _query . leftJoinAndSelect || part . queryAction === this . _query . innerJoinAndSelect ) && part . queryParams . length === 3 ) ) { wherePart = part ; } else { nonWheres . unshift ( part ) ; } } if ( ! wherePart ) { throw new Error ( "typeorm-linq-repository: Invalid use of conditional method." ) ; } this . _queryParts . push ( ... nonWheres ) ; if ( this . _queryWhereType === QueryWhereType . Joined ) { const part = wherePart ; let joinCondition = ( < [ string ] > part . queryParams ) . pop ( ) ; if ( typeof ( value ) === "string" && ( quoteString || joiningString ) && ! matchCase ) { value = value . toLowerCase ( ) ; joinCondition = `LOWER(${ joinCondition })` ; } else if ( value instanceof Date ) { value = `'${ value . toISOString ( ) }'` ; } joinCondition += ` ${ operator } ${ value }` ; ( < [ string ] > part . queryParams ) . push ( joinCondition ) ; this . _queryParts . push ( part ) ; } else { const part = wherePart ; let where = ( < [ string ] > part . queryParams ) . pop ( ) ; if ( typeof ( value ) === "string" && ( quoteString || joiningString ) && ! matchCase ) { value = value . toLowerCase ( ) ; where = `LOWER(${ where })` ; } else if ( value instanceof Date ) { value = `'${ value . toISOString ( ) }'` ; } where += ` ${ operator } ${ value }` ; ( < [ string , ObjectLiteral ] > part . queryParams ) . push ( where ) ; this . _queryParts . push ( part ) ; } this . _queryMode = QueryMode . Get ; return this ; } private createJoinCondition ( joinConditionProperty ) { let targetQueryPart = null ; const otherParts = [ ] ; while ( ! targetQueryPart && this . _queryParts . length ) { const part = this . _queryParts . pop ( ) ; if ( part . queryParams && part . queryParams . length > 1 && part . queryParams [ 1 ] === this . _lastAlias ) { targetQueryPart = part ; } else { otherParts . unshift ( part ) ; } } if ( ! targetQueryPart ) { throw new Error ( "typeorm-linq-repository: Invalid use of conditional join." ) ; } this . _queryParts . push ( ... otherParts ) ; if ( targetQueryPart . queryParams . length === 3 ) { this . addJoinCondition ( joinConditionProperty , "AND" , targetQueryPart ) ; } else { const joinCondition = `${ this . _lastAlias }.${ joinConditionProperty }` ; ( < [ string ] > targetQueryPart . queryParams ) . push ( joinCondition ) ; } this . _queryParts . push ( targetQueryPart ) ; } private escapeStringArray ( array ) { array . forEach ( ( value , i ) => { if ( typeof ( value ) === "string" ) { array [ i ] = `'${ value }'` ; } } ) ; } private includeOrExcludeFromInnerQuery < TI extends EntityBase , RI extends TI | TI [ ] , PI1 = TI > ( innerQuery , operator ) { innerQuery . queryParts . unshift ( new QueryBuilderPart ( innerQuery . query . select , [ innerQuery . selected ] ) ) ; const query = this . buildQuery ( < any > innerQuery ) . getQuery ( ) ; this . completeWhere ( operator , `(${ query })` , { quoteString : false } ) ; return this ; } private includePropertyUsingAlias < S extends Object > ( propertySelector , queryAlias ) { return this . joinOrIncludePropertyUsingAlias ( propertySelector , queryAlias , this . _query . leftJoinAndSelect ) ; } private isolatedConditions < IP , IS > ( conditions , conditionAction ) { const query = < Query < T , R , IS > > < any > conditions ( < IQuery < T , R , P > > < any > new Query ( this . _query , this . _getAction , this . _includeAliasHistory ) ) ; const conditionParts = query . queryParts . filter ( qp => qp . queryAction == query . query . where || qp . queryAction == query . query . andWhere || qp . queryAction == query . query . orWhere ) ; const joinParts = query . queryParts . filter ( qp => conditionParts . indexOf ( qp ) < 0 ) ; this . _queryParts . push ( ... joinParts ) ; this . _queryParts . push ( new QueryBuilderPart ( conditionAction , [ new Brackets ( qb => { this . compileQueryParts ( conditionParts , qb ) ; } ) ] ) ) ; return < IQuery < T , R , IP > > < any > this ; } private joinForeignEntity < F extends Object > ( foreignEntity ) { const entityName = nameof ( foreignEntity ) ; const resultAlias = entityName ; this . _lastAlias = resultAlias ; if ( ! ( this . _includeAliasHistory . find ( a => a === resultAlias ) ) ) { this . _includeAliasHistory . push ( resultAlias ) ; this . _queryParts . push ( new QueryBuilderPart ( this . _query . innerJoin , [ foreignEntity , resultAlias , "true" ] ) ) ; } this . setJoinIfNotCompare ( ) ; return < IQuery < T , R , F > | IComparableQuery < T , R , F > > < any > this ; } private joinMultipleProperties ( whereProperties , joinAction = this . _query . innerJoin , checkAliasHistory = true ) { whereProperties = whereProperties . replace ( / /g , "" ) ; whereProperties = whereProperties . replace ( /\.map\((\(?[a-zA-Z0-9_:]+\)?)=>[a-zA-Z0-9]+/g , "" ) . replace ( /\)/g , "" ) ; const separatedProperties = whereProperties . split ( "." ) ; let whereProperty = "" ; if ( checkAliasHistory ) { whereProperty = separatedProperties . pop ( ) ; } for ( let property of separatedProperties ) { if ( property . indexOf ( "map(" ) === 0 ) { property = property . substring ( 4 ) ; } this . joinPropertyUsingAlias ( property , this . _lastAlias , joinAction , checkAliasHistory ) ; } return whereProperty ; } private joinOrIncludePropertyUsingAlias < S extends Object > ( propertySelector , queryAlias , queryAction , checkAliasHistory = true ) { let propertyName = null ; if ( propertySelector instanceof Function ) { propertyName = nameof < P > ( propertySelector ) ; } else { propertyName = propertySelector ; } let resultAlias = `${ queryAlias }_${ propertyName }` ; if ( queryAction !== this . _query . leftJoinAndSelect ) { this . setJoinIfNotCompare ( ) ; } if ( ! checkAliasHistory ) { const existingAliasCount = this . _duplicateAliasHistory . filter ( a => a === resultAlias ) . length ; this . _duplicateAliasHistory . push ( resultAlias ) ; resultAlias += existingAliasCount . toString ( ) ; } this . _lastAlias = resultAlias ; const aliasAlreadyIncluded = this . _includeAliasHistory . some ( a => a === resultAlias ) ; if ( ! checkAliasHistory || ! aliasAlreadyIncluded ) { this . _includeAliasHistory . push ( resultAlias ) ; const queryProperty = `${ queryAlias }.${ propertyName }` ; this . _queryParts . push ( new QueryBuilderPart ( queryAction , [ queryProperty , resultAlias ] ) ) ; } else if ( checkAliasHistory && aliasAlreadyIncluded && queryAction === this . _query . innerJoin ) { const includeQueryPartIndex = this . _queryParts . findIndex ( qp => qp . queryAction === this . _query . leftJoinAndSelect && qp . queryParams [ 1 ] === resultAlias ) ; if ( includeQueryPartIndex >= 0 ) { const includeQueryPart = this . _queryParts [ includeQueryPartIndex ] ; this . _queryParts [ includeQueryPartIndex ] = new QueryBuilderPart ( this . _query . innerJoinAndSelect , includeQueryPart . queryParams ) ; } } return < IQuery < T , R , S > > < any > this ; } private joinPropertyUsingAlias < S extends Object > ( propertySelector , queryAlias , queryAction = this . _query . innerJoin , checkAliasHistory = true ) { return this . joinOrIncludePropertyUsingAlias ( propertySelector , queryAlias , queryAction , checkAliasHistory ) ; } private relationCount < S extends Object > ( relationSelector , relationCountPropSelector , havingQueryAction , havingCountComparer , conditionPropSelector ? ) { this . reset ( ) ; const relations = nameof < T > ( relationSelector ) ; this . joinMultipleProperties ( relations , this . _query . leftJoin , false ) ; const countProp = nameof < S > ( relationCountPropSelector ) ; this . _queryParts . push ( new QueryBuilderPart ( havingQueryAction , [ `COUNT(${ this . _lastAlias }.${ countProp }) ${ havingCountComparer } 0` ] ) ) ; if ( conditionPropSelector ) { const conditionProp = nameof < S > ( conditionPropSelector ) ; this . _queryWhereType = QueryWhereType . Joined ; this . createJoinCondition ( conditionProp ) ; } return < IQuery < T , R , T > | IComparableQuery < T , R , S > > < any > this ; } private setJoinIfNotCompare ( ) { if ( this . _queryMode !== QueryMode . Compare ) { this . _queryMode = QueryMode . Join ; } } }