Files
evento/node_modules/@mikro-orm/sql/query/QueryBuilder.d.ts
2026-03-18 14:55:56 -03:00

1647 lines
58 KiB
TypeScript

import {
type AnyEntity,
type AutoPath,
type Collection,
type ConnectionType,
type Dictionary,
type EntityData,
type EntityDTOFlat,
type EntityDTOProp,
type EntityKey,
type EntityManager,
EntityMetadata,
type EntityName,
type EntityProperty,
type ExpandProperty,
type FilterObject,
type FilterOptions,
type FilterValue,
type FlushMode,
type GroupOperator,
type Loaded,
LockMode,
type LoggingOptions,
type MetadataStorage,
type PrimaryProperty,
type ObjectQuery,
PopulateHint,
type PopulateOptions,
type PopulatePath,
QueryFlag,
type QueryOrderKeysFlat,
type QueryOrderMap,
type QueryResult,
RawQueryFragment,
type Raw,
type RequiredEntityData,
type Scalar,
type SerializeDTO,
type Subquery,
type Transaction,
} from '@mikro-orm/core';
import { JoinType, QueryType } from './enums.js';
import type { AbstractSqlDriver } from '../AbstractSqlDriver.js';
import { type Alias, type OnConflictClause, QueryBuilderHelper } from './QueryBuilderHelper.js';
import type { SqlEntityManager } from '../SqlEntityManager.js';
import type { ICriteriaNodeProcessOptions, InternalField, JoinOptions } from '../typings.js';
import type { AbstractSqlPlatform } from '../AbstractSqlPlatform.js';
import { type CteOptions, NativeQueryBuilder } from './NativeQueryBuilder.js';
export interface ExecuteOptions {
mapResults?: boolean;
mergeResults?: boolean;
}
export interface QBStreamOptions {
/**
* Results are mapped to entities, if you set `mapResults: false` you will get POJOs instead.
*
* @default true
*/
mapResults?: boolean;
/**
* When populating to-many relations, the ORM streams fully merged entities instead of yielding every row.
* You can opt out of this behavior by specifying `mergeResults: false`. This will yield every row from
* the SQL result, but still mapped to entities, meaning that to-many collections will contain at most
* one item, and you will get duplicate root entities when they have multiple items in the populated
* collection.
*
* @default true
*/
mergeResults?: boolean;
/**
* When enabled, the driver will return the raw database results without renaming the fields to match the entity property names.
*
* @default false
*/
rawResults?: boolean;
}
type IsNever<T, True = true, False = false> = [T] extends [never] ? True : False;
type GetAlias<T extends string> = T extends `${infer A}.${string}` ? A : never;
type GetPropName<T extends string> = T extends `${string}.${infer P}` ? P : T;
type AppendToHint<Parent extends string, Child extends string> = `${Parent}.${Child}`;
/**
* Context tuple format: [Path, Alias, Type, Select]
* - Path: The relation path from root entity (e.g., 'books', 'books.author')
* - Alias: The SQL alias used in the query (e.g., 'b', 'a1')
* - Type: The entity type of the joined relation
* - Select: Whether this join was created via joinAndSelect (affects Fields tracking)
*
* Example: After `qb.leftJoin('a.books', 'b')`, Context becomes:
* { b: ['books', 'b', Book, false] }
*/
type AddToContext<Type extends object, Context, Field extends string, Alias extends string, Select extends boolean> = {
[K in Alias]: [GetPath<Context, Field>, K, ExpandProperty<Type[GetPropName<Field> & keyof Type]>, Select];
};
type GetPath<Context, Field extends string> =
GetAlias<Field> extends infer Alias
? IsNever<Alias> extends true
? GetPropName<Field>
: Alias extends keyof Context
? Context[Alias] extends [infer Path, ...any[]]
? AppendToHint<Path & string, GetPropName<Field>>
: GetPropName<Field>
: GetPropName<Field>
: GetPropName<Field>;
type GetType<Type extends object, Context, Field extends string> =
GetAlias<Field> extends infer Alias
? IsNever<Alias> extends true
? Type
: [Context] extends [never]
? Type
: Alias extends keyof Context
? Context[Alias] extends [string, string, infer PropType, any]
? PropType & object
: Type
: Type
: Type;
type AddToHint<RootAlias, Context, Field extends string, Select extends boolean = false> = Select extends true
? GetAlias<Field> extends infer Alias
? IsNever<Alias> extends true
? GetPropName<Field>
: Alias extends RootAlias
? GetPropName<Field>
: Alias extends keyof Context
? Context[Alias] extends [infer Path, ...any[]]
? AppendToHint<Path & string, GetPropName<Field>>
: GetPropName<Field>
: GetPropName<Field>
: GetPropName<Field>
: never;
export type ModifyHint<RootAlias, Context, Hint extends string, Field extends string, Select extends boolean = false> =
| Hint
| AddToHint<RootAlias, Context, Field, Select>;
export type ModifyContext<
Entity extends object,
Context,
Field extends string,
Alias extends string,
Select extends boolean = false,
> =
IsNever<Context> extends true
? AddToContext<GetType<Entity, object, Field>, object, Field, Alias, Select>
: Context & AddToContext<GetType<Entity, Context, Field>, Context, Field, Alias, Select>;
type StripRootAlias<
F extends string,
RootAlias extends string,
Context = never,
> = F extends `${RootAlias}.${infer Field}`
? Field
: F extends `${infer Alias}.${string}`
? Alias extends AliasNames<Context>
? never
: F
: F;
type StripFieldAlias<F extends string> = F extends `${infer Path} as ${string}` ? Path : F;
type ExtractRootFields<Fields, RootAlias extends string, Context = never> = [Fields] extends ['*']
? '*'
: Fields extends `${RootAlias}.*`
? '*'
: Fields extends string
? StripRootAlias<StripFieldAlias<Fields>, RootAlias, Context>
: never;
type PrefixWithPath<Path extends string, Field extends string> = `${Path}.${Field}`;
type StripJoinAlias<F extends string, Alias extends string> = F extends `${Alias}.${infer Field}` ? Field : F;
export type JoinSelectField<JoinedEntity, Alias extends string> =
| (keyof JoinedEntity & string)
| `${Alias}.${keyof JoinedEntity & string}`;
type AddJoinFields<
RootAlias,
Context,
Field extends string,
Alias extends string,
JoinFields extends readonly string[],
> = JoinFields extends readonly (infer F)[]
? F extends string
? PrefixWithPath<AddToHint<RootAlias, Context, Field, true> & string, StripJoinAlias<F, Alias>>
: never
: never;
export type ModifyFields<
CurrentFields extends string,
RootAlias,
Context,
Field extends string,
Alias extends string,
JoinFields extends readonly string[] | undefined,
> = JoinFields extends readonly string[]
? CurrentFields | AddJoinFields<RootAlias, Context, Field, Alias, JoinFields>
: CurrentFields;
type EntityRelations<T> = EntityKey<T, true>;
type JoinedEntityType<Entity extends object, Context, Field extends string> = ExpandProperty<
GetType<Entity, Context, Field>[GetPropName<Field> & keyof GetType<Entity, Context, Field>]
>;
type AliasNames<Context> = Context[keyof Context] extends infer Join
? Join extends any
? Join extends [string, infer Alias, any, any]
? Alias & string
: never
: never
: never;
type ContextRelationKeys<Context> = Context[keyof Context] extends infer Join
? Join extends any
? Join extends [string, infer Alias, infer Type, any]
? `${Alias & string}.${EntityRelations<Type & object>}`
: never
: never
: never;
export type QBField<Entity, RootAlias extends string, Context> =
| EntityRelations<Entity>
| `${RootAlias}.${EntityRelations<Entity>}`
| ([Context] extends [never] ? never : ContextRelationKeys<Context>);
type ContextFieldKeys<Context> = Context[keyof Context] extends infer Join
? Join extends any
? Join extends [string, infer Alias, infer Type, any]
? `${Alias & string}.${keyof Type & string}`
: never
: never
: never;
type WithAlias<T extends string> = T | `${T} as ${string}`;
export type Field<Entity, RootAlias extends string = never, Context = never> =
| WithAlias<EntityKey<Entity>>
| (IsNever<RootAlias> extends true ? never : WithAlias<`${RootAlias}.${EntityKey<Entity>}`> | `${RootAlias}.*`)
| ([Context] extends [never] ? never : WithAlias<ContextFieldKeys<Context>> | `${AliasNames<Context>}.*`)
| '*'
| QueryBuilder<any>
| NativeQueryBuilder
| RawQueryFragment<any>
| (RawQueryFragment & symbol);
type RootAliasOrderKeys<RootAlias extends string, Entity> = {
[K in `${RootAlias}.${EntityKey<Entity>}`]?: QueryOrderKeysFlat;
};
type ContextOrderKeys<Context> = {
[K in ContextFieldKeys<Context>]?: QueryOrderKeysFlat;
};
type RawOrderKeys<RawAliases extends string> = {
[K in RawAliases]?: QueryOrderKeysFlat;
};
export type ContextOrderByMap<
Entity,
RootAlias extends string = never,
Context = never,
RawAliases extends string = never,
> =
| QueryOrderMap<Entity>
| ((IsNever<RootAlias> extends true ? {} : RootAliasOrderKeys<RootAlias, Entity>) &
([Context] extends [never] ? {} : ContextOrderKeys<Context>) &
(IsNever<RawAliases> extends true ? {} : string extends RawAliases ? {} : RawOrderKeys<RawAliases>));
type AliasedPath<Alias extends string, Type, P extends string> = P extends `${Alias}.*`
? P
: P extends `${Alias}.${infer Rest}`
? `${Alias}.${AutoPath<Type & object, Rest, `${PopulatePath.ALL}`>}`
: never;
type ContextAliasedPath<Context, P extends string> = Context[keyof Context] extends infer Join
? Join extends any
? Join extends [string, infer Alias, infer Type, any]
? AliasedPath<Alias & string, Type, P>
: never
: never
: never;
type NestedAutoPath<Entity, RootAlias extends string, Context, P extends string> = P extends `${string}:ref`
? never
: P extends `${infer Path} as ${string}`
?
| AliasedPath<RootAlias, Entity, Path>
| ContextAliasedPath<Context, Path>
| AutoPath<Entity, Path, `${PopulatePath.ALL}`> extends never
? never
: P
: AliasedPath<RootAlias, Entity, P> | ContextAliasedPath<Context, P> | AutoPath<Entity, P, `${PopulatePath.ALL}`>;
type AliasedObjectQuery<Entity extends object, Alias extends string> = {
[K in EntityKey<Entity> as `${Alias}.${K}`]?: ObjectQuery<Entity>[K];
};
type JoinCondition<JoinedEntity extends object, Alias extends string> = (
| ObjectQuery<JoinedEntity>
| AliasedObjectQuery<JoinedEntity, Alias>
) & {
$not?: JoinCondition<JoinedEntity, Alias>;
$or?: JoinCondition<JoinedEntity, Alias>[];
$and?: JoinCondition<JoinedEntity, Alias>[];
};
type RawJoinCondition = {
[key: string]: FilterValue<Scalar> | RawQueryFragment;
};
type ExtractRawAliasFromField<F> =
F extends RawQueryFragment<infer A>
? A extends string
? A
: never
: F extends `${string} as ${infer A}`
? A
: never;
type ExtractRawAliasesFromTuple<T extends readonly unknown[]> = T extends readonly [infer Head, ...infer Tail]
? ExtractRawAliasFromField<Head> | ExtractRawAliasesFromTuple<Tail>
: never;
type ExtractRawAliases<Fields> = Fields extends readonly unknown[]
? ExtractRawAliasesFromTuple<Fields>
: ExtractRawAliasFromField<Fields>;
type FlatOperatorMap = {
$eq?: Scalar | readonly Scalar[] | Subquery | null;
$ne?: Scalar | readonly Scalar[] | Subquery | null;
$in?: readonly Scalar[] | Raw | Subquery;
$nin?: readonly Scalar[] | Raw | Subquery;
$gt?: Scalar | Subquery;
$gte?: Scalar | Subquery;
$lt?: Scalar | Subquery;
$lte?: Scalar | Subquery;
$like?: string;
$re?: string;
$ilike?: string;
$fulltext?: string;
$overlap?: readonly string[] | string | object;
$contains?: readonly string[] | string | object;
$contained?: readonly string[] | string | object;
$exists?: boolean;
$hasKey?: string;
$hasKeys?: readonly string[];
$hasSomeKeys?: readonly string[];
};
type AliasedFilterValue = Scalar | FlatOperatorMap | readonly Scalar[] | null | QueryBuilder<any> | NativeQueryBuilder;
type TypedAliasedFilterValue<T> = FilterValue<ExpandProperty<T>> | QueryBuilder<any> | NativeQueryBuilder;
type RootAliasFilterKeys<RootAlias extends string, Entity> = {
[K in EntityKey<Entity> as `${RootAlias}.${K}`]?: TypedAliasedFilterValue<Entity[K]>;
};
type ContextFilterKeys<Context> = {
[K in ContextFieldKeys<Context>]?: AliasedFilterValue;
};
type RawFilterKeys<RawAliases extends string> = {
[K in RawAliases]?: AliasedFilterValue;
};
type NestedFilterCondition<Entity, RootAlias extends string, Context, RawAliases extends string> = ObjectQuery<Entity> &
(IsNever<RootAlias> extends true ? {} : string extends RootAlias ? {} : RootAliasFilterKeys<RootAlias, Entity>) &
([Context] extends [never] ? {} : ContextFilterKeys<Context>) &
(IsNever<RawAliases> extends true ? {} : string extends RawAliases ? {} : RawFilterKeys<RawAliases>);
type GroupOperators<RootAlias extends string, Context, Entity, RawAliases extends string> = {
$and?: NestedFilterCondition<Entity, RootAlias, Context, RawAliases>[];
$or?: NestedFilterCondition<Entity, RootAlias, Context, RawAliases>[];
$not?: NestedFilterCondition<Entity, RootAlias, Context, RawAliases>;
};
export type AliasedFilterCondition<
RootAlias extends string,
Context,
Entity,
RawAliases extends string = never,
> = (IsNever<RootAlias> extends true ? {} : string extends RootAlias ? {} : RootAliasFilterKeys<RootAlias, Entity>) &
([Context] extends [never] ? {} : ContextFilterKeys<Context>) &
(IsNever<RawAliases> extends true ? {} : string extends RawAliases ? {} : RawFilterKeys<RawAliases>) &
GroupOperators<RootAlias, Context, Entity, RawAliases>;
export type QBFilterQuery<
Entity,
RootAlias extends string = never,
Context = never,
RawAliases extends string = never,
> = FilterObject<Entity> & AliasedFilterCondition<RootAlias, Context, Entity, RawAliases>;
/** @internal */
export interface QBState<Entity extends object> {
type?: QueryType;
fields?: InternalField<Entity>[];
populate: PopulateOptions<Entity>[];
populateWhere?: ObjectQuery<Entity> | PopulateHint | `${PopulateHint}`;
populateFilter?: ObjectQuery<Entity> | PopulateHint | `${PopulateHint}`;
resolvedPopulateWhere?: ObjectQuery<Entity> | PopulateHint | `${PopulateHint}`;
populateMap: Dictionary<string>;
aliasCounter: number;
flags: Set<QueryFlag>;
finalized: boolean;
populateHintFinalized: boolean;
joins: Dictionary<JoinOptions>;
explicitAlias: boolean;
schema?: string;
cond: Dictionary;
data?: Dictionary;
orderBy: QueryOrderMap<Entity>[];
groupBy: InternalField<Entity>[];
having: Dictionary;
returning?: InternalField<Entity>[];
onConflict?: OnConflictClause<Entity>[];
limit?: number;
offset?: number;
distinctOn?: string[];
joinedProps: Map<string, PopulateOptions<any>>;
cache?: boolean | number | [string, number];
indexHint?: string;
collation?: string;
comments: string[];
hintComments: string[];
flushMode?: FlushMode;
lockMode?: LockMode;
lockTables?: string[];
subQueries: Dictionary<string>;
mainAlias?: Alias<Entity>;
aliases: Dictionary<Alias<any>>;
tptAlias: Dictionary<string>;
unionQuery?: {
sql: string;
params: readonly unknown[];
};
ctes: (CteOptions & {
name: string;
query: NativeQueryBuilder | RawQueryFragment;
recursive?: boolean;
})[];
tptJoinsApplied: boolean;
autoJoinedPaths: string[];
}
/**
* SQL query builder with fluent interface.
*
* ```ts
* const qb = orm.em.createQueryBuilder(Publisher);
* qb.select('*')
* .where({
* name: 'test 123',
* type: PublisherType.GLOBAL,
* })
* .orderBy({
* name: QueryOrder.DESC,
* type: QueryOrder.ASC,
* })
* .limit(2, 1);
*
* const publisher = await qb.getSingleResult();
* ```
*/
export declare class QueryBuilder<
Entity extends object = AnyEntity,
RootAlias extends string = never,
Hint extends string = never,
Context extends object = never,
RawAliases extends string = never,
Fields extends string = '*',
CTEs extends Record<string, object> = {},
> implements Subquery {
#private;
protected readonly metadata: MetadataStorage;
protected readonly driver: AbstractSqlDriver;
protected readonly context?: Transaction | undefined;
protected connectionType?: ConnectionType | undefined;
protected em?: SqlEntityManager | undefined;
protected loggerContext?: (LoggingOptions & Dictionary) | undefined;
readonly __subquery: true;
/** @internal */
static createDefaultState<T extends object>(): QBState<T>;
get mainAlias(): Alias<Entity>;
get alias(): string;
get helper(): QueryBuilderHelper;
get type(): QueryType;
/** @internal */
get state(): QBState<Entity>;
protected readonly platform: AbstractSqlPlatform;
/**
* @internal
*/
constructor(
entityName: EntityName<Entity> | QueryBuilder<Entity, any, any, any>,
metadata: MetadataStorage,
driver: AbstractSqlDriver,
context?: Transaction | undefined,
alias?: string,
connectionType?: ConnectionType | undefined,
em?: SqlEntityManager | undefined,
loggerContext?: (LoggingOptions & Dictionary) | undefined,
);
/**
* Creates a SELECT query, specifying the fields to retrieve.
*
* @example
* ```ts
* // Select specific fields
* const qb = em.createQueryBuilder(User, 'u');
* qb.select(['u.id', 'u.name', 'u.email']);
*
* // Select with raw expressions
* qb.select([raw('count(*) as total')]);
*
* // Select with aliases (works for regular and formula properties)
* qb.select(['id', 'fullName as displayName']);
* qb.select(['id', sql.ref('fullName').as('displayName')]);
*
* // Select with distinct
* qb.select('*', true);
* ```
*/
select<const F extends readonly Field<Entity, RootAlias, Context>[]>(
fields: F,
distinct?: boolean,
): SelectQueryBuilder<
Entity,
RootAlias,
Hint,
Context,
RawAliases | ExtractRawAliases<F>,
ExtractRootFields<F[number] & string, RootAlias, Context>,
CTEs
>;
/**
* Creates a SELECT query, specifying the fields to retrieve.
*
* @example
* ```ts
* // Select specific fields
* const qb = em.createQueryBuilder(User, 'u');
* qb.select(['u.id', 'u.name', 'u.email']);
*
* // Select with raw expressions
* qb.select([raw('count(*) as total')]);
*
* // Select with distinct
* qb.select('*', true);
* ```
*/
select<const P extends string>(
fields: readonly NestedAutoPath<Entity, RootAlias, Context, P>[],
distinct?: boolean,
): SelectQueryBuilder<Entity, RootAlias, Hint, Context, RawAliases, ExtractRootFields<P, RootAlias, Context>, CTEs>;
/**
* Creates a SELECT query, specifying the fields to retrieve.
*
* @example
* ```ts
* // Select specific fields with nested paths (e.g., for embedded properties)
* const qb = em.createQueryBuilder(User, 'u');
* qb.select('address.city');
* ```
*/
select<const P extends string>(
fields: NestedAutoPath<Entity, RootAlias, Context, P>,
distinct?: boolean,
): SelectQueryBuilder<Entity, RootAlias, Hint, Context, RawAliases, ExtractRootFields<P, RootAlias, Context>, CTEs>;
/**
* Creates a SELECT query, specifying the fields to retrieve.
*
* @example
* ```ts
* // Select specific fields
* const qb = em.createQueryBuilder(User, 'u');
* qb.select(['u.id', 'u.name', 'u.email']);
*
* // Select with raw expressions
* qb.select([raw('count(*) as total')]);
*
* // Select with distinct
* qb.select('*', true);
* ```
*/
select<const F extends Field<Entity, RootAlias, Context>>(
fields: F,
distinct?: boolean,
): SelectQueryBuilder<
Entity,
RootAlias,
Hint,
Context,
RawAliases | ExtractRawAliases<readonly [F]>,
ExtractRootFields<F & string, RootAlias, Context>,
CTEs
>;
/**
* Adds fields to an existing SELECT query.
*/
addSelect<const F extends Field<Entity, RootAlias, Context> | readonly Field<Entity, RootAlias, Context>[]>(
fields: F,
): SelectQueryBuilder<
Entity,
RootAlias,
Hint,
Context,
RawAliases | ExtractRawAliases<F extends readonly unknown[] ? F : [F]>,
Fields | ExtractRootFields<F extends readonly (infer U)[] ? U & string : F & string, RootAlias, Context>,
CTEs
>;
distinct(): SelectQueryBuilder<Entity, RootAlias, Hint, Context, RawAliases, Fields, CTEs>;
/** postgres only */
distinctOn<const F extends readonly Field<Entity, RootAlias, Context>[]>(
fields: F,
): SelectQueryBuilder<Entity, RootAlias, Hint, Context, RawAliases, Fields, CTEs>;
/** postgres only */
distinctOn<F extends Field<Entity, RootAlias, Context>>(
fields: F,
): SelectQueryBuilder<Entity, RootAlias, Hint, Context, RawAliases, Fields, CTEs>;
/**
* Creates an INSERT query with the given data.
*
* @example
* ```ts
* await em.createQueryBuilder(User)
* .insert({ name: 'John', email: 'john@example.com' })
* .execute();
*
* // Bulk insert
* await em.createQueryBuilder(User)
* .insert([{ name: 'John' }, { name: 'Jane' }])
* .execute();
* ```
*/
insert(
data: RequiredEntityData<Entity> | RequiredEntityData<Entity>[],
): InsertQueryBuilder<Entity, RootAlias, Context>;
/**
* Creates an UPDATE query with the given data.
* Use `where()` to specify which rows to update.
*
* @example
* ```ts
* await em.createQueryBuilder(User)
* .update({ name: 'John Doe' })
* .where({ id: 1 })
* .execute();
* ```
*/
update(data: EntityData<Entity>): UpdateQueryBuilder<Entity, RootAlias, Context>;
/**
* Creates a DELETE query.
* Use `where()` to specify which rows to delete.
*
* @example
* ```ts
* await em.createQueryBuilder(User)
* .delete()
* .where({ id: 1 })
* .execute();
*
* // Or pass the condition directly
* await em.createQueryBuilder(User)
* .delete({ isActive: false })
* .execute();
* ```
*/
delete(cond?: QBFilterQuery<Entity, RootAlias, Context, RawAliases>): DeleteQueryBuilder<Entity, RootAlias, Context>;
/**
* Creates a TRUNCATE query to remove all rows from the table.
*/
truncate(): TruncateQueryBuilder<Entity>;
/**
* Creates a COUNT query to count matching rows.
*
* @example
* ```ts
* const count = await em.createQueryBuilder(User)
* .count()
* .where({ isActive: true })
* .execute('get');
* ```
*/
count<F extends Field<Entity, RootAlias, Context>>(field?: F | F[], distinct?: boolean): CountQueryBuilder<Entity>;
/**
* Adds a JOIN clause to the query for an entity relation.
*
* @example
* ```ts
* const qb = em.createQueryBuilder(Book, 'b');
* qb.select('*')
* .join('b.author', 'a')
* .where({ 'a.name': 'John' });
* ```
*/
join<Field extends QBField<Entity, RootAlias, Context>, Alias extends string>(
field: Field,
alias: Alias,
cond?: JoinCondition<JoinedEntityType<Entity, Context, Field & string>, Alias>,
type?: JoinType,
path?: string,
schema?: string,
): SelectQueryBuilder<
Entity,
RootAlias,
ModifyHint<RootAlias, Context, Hint, Field> & {},
ModifyContext<Entity, Context, Field, Alias>,
RawAliases,
'*',
CTEs
>;
/**
* Adds a JOIN clause to the query for a subquery.
*/
join<Alias extends string>(
field: RawQueryFragment | QueryBuilder<any>,
alias: Alias,
cond?: RawJoinCondition,
type?: JoinType,
path?: string,
schema?: string,
): SelectQueryBuilder<Entity, RootAlias, Hint, ModifyContext<Entity, Context, string, Alias>, RawAliases, '*', CTEs>;
/**
* Adds an INNER JOIN clause to the query for an entity relation.
*/
innerJoin<Field extends QBField<Entity, RootAlias, Context>, Alias extends string>(
field: Field,
alias: Alias,
cond?: JoinCondition<JoinedEntityType<Entity, Context, Field & string>, Alias>,
schema?: string,
): SelectQueryBuilder<
Entity,
RootAlias,
ModifyHint<RootAlias, Context, Hint, Field> & {},
ModifyContext<Entity, Context, Field, Alias>,
RawAliases,
'*',
CTEs
>;
/**
* Adds an INNER JOIN clause to the query for a subquery.
*/
innerJoin<Alias extends string>(
field: RawQueryFragment | QueryBuilder<any>,
alias: Alias,
cond?: RawJoinCondition,
schema?: string,
): SelectQueryBuilder<Entity, RootAlias, Hint, ModifyContext<Entity, Context, string, Alias>, RawAliases, '*', CTEs>;
innerJoinLateral<Alias extends string>(
field: RawQueryFragment | QueryBuilder<any>,
alias: Alias,
cond?: RawJoinCondition,
schema?: string,
): SelectQueryBuilder<Entity, RootAlias, Hint, ModifyContext<Entity, Context, string, Alias>, RawAliases, '*', CTEs>;
/**
* Adds a LEFT JOIN clause to the query for an entity relation.
*/
leftJoin<Field extends QBField<Entity, RootAlias, Context>, Alias extends string>(
field: Field,
alias: Alias,
cond?: JoinCondition<JoinedEntityType<Entity, Context, Field & string>, Alias>,
schema?: string,
): SelectQueryBuilder<
Entity,
RootAlias,
ModifyHint<RootAlias, Context, Hint, Field> & {},
ModifyContext<Entity, Context, Field, Alias>,
RawAliases,
'*',
CTEs
>;
/**
* Adds a LEFT JOIN clause to the query for a subquery.
*/
leftJoin<Alias extends string>(
field: RawQueryFragment | QueryBuilder<any>,
alias: Alias,
cond?: RawJoinCondition,
schema?: string,
): SelectQueryBuilder<Entity, RootAlias, Hint, ModifyContext<Entity, Context, string, Alias>, RawAliases, '*', CTEs>;
leftJoinLateral<Alias extends string>(
field: RawQueryFragment | QueryBuilder<any>,
alias: Alias,
cond?: RawJoinCondition,
schema?: string,
): SelectQueryBuilder<Entity, RootAlias, Hint, ModifyContext<Entity, Context, string, Alias>, RawAliases, '*', CTEs>;
/**
* Adds a JOIN clause and automatically selects the joined entity's fields.
* This is useful for eager loading related entities.
*
* @example
* ```ts
* const qb = em.createQueryBuilder(Book, 'b');
* qb.select('*')
* .joinAndSelect('b.author', 'a')
* .where({ 'a.name': 'John' });
* ```
*/
joinAndSelect<
Field extends QBField<Entity, RootAlias, Context>,
Alias extends string,
const JoinFields extends
| readonly [
JoinSelectField<JoinedEntityType<Entity, Context, Field & string>, Alias>,
...JoinSelectField<JoinedEntityType<Entity, Context, Field & string>, Alias>[],
]
| undefined = undefined,
>(
field: Field | [Field, RawQueryFragment | QueryBuilder<any>],
alias: Alias,
cond?: JoinCondition<JoinedEntityType<Entity, Context, Field & string>, Alias>,
type?: JoinType,
path?: string,
fields?: JoinFields,
schema?: string,
): SelectQueryBuilder<
Entity,
RootAlias,
ModifyHint<RootAlias, Context, Hint, Field, true> & {},
ModifyContext<Entity, Context, Field, Alias, true>,
RawAliases,
ModifyFields<Fields, RootAlias, Context, Field, Alias, JoinFields>,
CTEs
>;
leftJoinAndSelect<
Field extends QBField<Entity, RootAlias, Context>,
Alias extends string,
const JoinFields extends
| readonly [
JoinSelectField<JoinedEntityType<Entity, Context, Field & string>, Alias>,
...JoinSelectField<JoinedEntityType<Entity, Context, Field & string>, Alias>[],
]
| undefined = undefined,
>(
field: Field | [Field, RawQueryFragment | QueryBuilder<any>],
alias: Alias,
cond?: JoinCondition<JoinedEntityType<Entity, Context, Field & string>, Alias>,
fields?: JoinFields,
schema?: string,
): SelectQueryBuilder<
Entity,
RootAlias,
ModifyHint<RootAlias, Context, Hint, Field, true> & {},
ModifyContext<Entity, Context, Field, Alias, true>,
RawAliases,
ModifyFields<Fields, RootAlias, Context, Field, Alias, JoinFields>,
CTEs
>;
leftJoinLateralAndSelect<
Field extends QBField<Entity, RootAlias, Context>,
Alias extends string,
const JoinFields extends
| readonly [
JoinSelectField<JoinedEntityType<Entity, Context, Field & string>, Alias>,
...JoinSelectField<JoinedEntityType<Entity, Context, Field & string>, Alias>[],
]
| undefined = undefined,
>(
field: [Field, RawQueryFragment | QueryBuilder<any>],
alias: Alias,
cond?: JoinCondition<JoinedEntityType<Entity, Context, Field & string>, Alias>,
fields?: JoinFields,
schema?: string,
): SelectQueryBuilder<
Entity,
RootAlias,
ModifyHint<RootAlias, Context, Hint, Field, true> & {},
ModifyContext<Entity, Context, Field, Alias, true>,
RawAliases,
ModifyFields<Fields, RootAlias, Context, Field, Alias, JoinFields>,
CTEs
>;
innerJoinAndSelect<
Field extends QBField<Entity, RootAlias, Context>,
Alias extends string,
const JoinFields extends
| readonly [
JoinSelectField<JoinedEntityType<Entity, Context, Field & string>, Alias>,
...JoinSelectField<JoinedEntityType<Entity, Context, Field & string>, Alias>[],
]
| undefined = undefined,
>(
field: Field | [Field, RawQueryFragment | QueryBuilder<any>],
alias: Alias,
cond?: JoinCondition<JoinedEntityType<Entity, Context, Field & string>, Alias>,
fields?: JoinFields,
schema?: string,
): SelectQueryBuilder<
Entity,
RootAlias,
ModifyHint<RootAlias, Context, Hint, Field, true> & {},
ModifyContext<Entity, Context, Field, Alias, true>,
RawAliases,
ModifyFields<Fields, RootAlias, Context, Field, Alias, JoinFields>,
CTEs
>;
innerJoinLateralAndSelect<
Field extends QBField<Entity, RootAlias, Context>,
Alias extends string,
const JoinFields extends
| readonly [
JoinSelectField<JoinedEntityType<Entity, Context, Field & string>, Alias>,
...JoinSelectField<JoinedEntityType<Entity, Context, Field & string>, Alias>[],
]
| undefined = undefined,
>(
field: [Field, RawQueryFragment | QueryBuilder<any>],
alias: Alias,
cond?: JoinCondition<JoinedEntityType<Entity, Context, Field & string>, Alias>,
fields?: JoinFields,
schema?: string,
): SelectQueryBuilder<
Entity,
RootAlias,
ModifyHint<RootAlias, Context, Hint, Field, true> & {},
ModifyContext<Entity, Context, Field, Alias, true>,
RawAliases,
ModifyFields<Fields, RootAlias, Context, Field, Alias, JoinFields>,
CTEs
>;
protected getFieldsForJoinedLoad(
prop: EntityProperty<Entity>,
alias: string,
explicitFields?: readonly string[],
): InternalField<Entity>[];
/**
* Apply filters to the QB where condition.
*/
applyFilters(filterOptions?: FilterOptions): Promise<void>;
/**
* @internal
*/
scheduleFilterCheck(path: string): void;
/**
* @internal
*/
applyJoinedFilters(em: EntityManager, filterOptions: FilterOptions | undefined): Promise<void>;
withSubQuery(subQuery: RawQueryFragment | NativeQueryBuilder, alias: string): this;
/**
* Adds a WHERE clause to the query using an object condition.
*
* Supports filtering by:
* - Direct entity properties: `{ name: 'John' }`
* - Nested relations/embedded: `{ author: { name: 'John' } }`
* - Aliased properties after joins: `{ 'a.name': 'John' }` or `{ 'b.title': 'test' }`
* - Filter operators: `{ age: { $gte: 18 } }`
* - Logical operators: `{ $or: [{ name: 'John' }, { name: 'Jane' }] }`
*
* @example
* ```ts
* // Filter by entity properties
* qb.where({ name: 'John', age: { $gte: 18 } });
*
* // Filter by nested relation
* qb.where({ author: { name: 'John' } });
*
* // Filter by aliased properties after join
* qb.leftJoin('a.books', 'b').where({ 'b.title': 'test' });
*
* // Combine with logical operators
* qb.where({ $or: [{ status: 'active' }, { role: 'admin' }] });
* ```
*/
where(cond: QBFilterQuery<Entity, RootAlias, Context, RawAliases>, operator?: keyof typeof GroupOperator): this;
/**
* Adds a WHERE clause to the query using a raw SQL string or fragment.
*
* @example
* ```ts
* // Raw SQL with parameters
* qb.where('name = ? AND age >= ?', ['John', 18]);
*
* // Using raw() helper
* qb.where(raw('lower(name) = ?', ['john']));
* ```
*/
where(cond: string | RawQueryFragment, params?: any[], operator?: keyof typeof GroupOperator): this;
/**
* Adds an AND WHERE clause to the query using an object condition.
*
* @example
* ```ts
* qb.where({ status: 'active' }).andWhere({ role: 'admin' });
* qb.where({ name: 'John' }).andWhere({ 'b.title': 'test' });
* ```
*/
andWhere(cond: QBFilterQuery<Entity, RootAlias, Context, RawAliases>): this;
/**
* Adds an AND WHERE clause to the query using a raw SQL string or fragment.
*
* @example
* ```ts
* qb.where({ status: 'active' }).andWhere('age >= ?', [18]);
* ```
*/
andWhere(cond: string | RawQueryFragment, params?: any[]): this;
/**
* Adds an OR WHERE clause to the query using an object condition.
*
* @example
* ```ts
* qb.where({ status: 'active' }).orWhere({ role: 'admin' });
* qb.where({ name: 'John' }).orWhere({ name: 'Jane' });
* ```
*/
orWhere(cond: QBFilterQuery<Entity, RootAlias, Context, RawAliases>): this;
/**
* Adds an OR WHERE clause to the query using a raw SQL string or fragment.
*
* @example
* ```ts
* qb.where({ status: 'active' }).orWhere('role = ?', ['admin']);
* ```
*/
orWhere(cond: string | RawQueryFragment, params?: any[]): this;
/**
* Adds an ORDER BY clause to the query, replacing any existing order.
*
* @example
* ```ts
* qb.orderBy({ name: 'asc', createdAt: 'desc' });
* qb.orderBy([{ name: 'asc' }, { createdAt: 'desc' }]);
* qb.orderBy({ profile: { bio: 'asc' } }); // nested via object
* qb.orderBy({ 'profile.bio': 'asc' }); // nested via dot notation
* ```
*/
orderBy(
orderBy:
| ContextOrderByMap<Entity, RootAlias, Context, RawAliases>
| ContextOrderByMap<Entity, RootAlias, Context, RawAliases>[],
): SelectQueryBuilder<Entity, RootAlias, Hint, Context, RawAliases, Fields, CTEs>;
/**
* Adds an ORDER BY clause to the query, replacing any existing order.
*
* @example
* ```ts
* qb.orderBy({ name: 'asc', createdAt: 'desc' });
* qb.orderBy([{ name: 'asc' }, { createdAt: 'desc' }]);
* qb.orderBy({ profile: { bio: 'asc' } }); // nested via object
* qb.orderBy({ 'profile.bio': 'asc' }); // nested via dot notation
* ```
*/
orderBy<const T extends Record<string, QueryOrderKeysFlat>>(
orderBy: T & {
[K in keyof T]: K extends NestedAutoPath<Entity, RootAlias, Context, K & string>
? T[K]
: K extends RawAliases
? T[K]
: never;
},
): SelectQueryBuilder<Entity, RootAlias, Hint, Context, RawAliases, Fields, CTEs>;
/**
* Adds additional ORDER BY clause without replacing existing order.
*/
andOrderBy(
orderBy:
| ContextOrderByMap<Entity, RootAlias, Context, RawAliases>
| ContextOrderByMap<Entity, RootAlias, Context, RawAliases>[],
): SelectQueryBuilder<Entity, RootAlias, Hint, Context, RawAliases, Fields, CTEs>;
/**
* Adds additional ORDER BY clause without replacing existing order.
*/
andOrderBy<const T extends Record<string, QueryOrderKeysFlat>>(
orderBy: T & {
[K in keyof T]: K extends NestedAutoPath<Entity, RootAlias, Context, K & string>
? T[K]
: K extends RawAliases
? T[K]
: never;
},
): SelectQueryBuilder<Entity, RootAlias, Hint, Context, RawAliases, Fields, CTEs>;
private processOrderBy;
/** Collect custom aliases from select fields (stored as 'resolved as alias' strings by select()). */
private getSelectAliases;
/**
* Adds a GROUP BY clause to the query.
*
* @example
* ```ts
* qb.select([raw('count(*) as count'), 'status'])
* .groupBy('status');
* ```
*/
groupBy<const F extends readonly Field<Entity, RootAlias, Context>[]>(
fields: F,
): SelectQueryBuilder<Entity, RootAlias, Hint, Context, RawAliases, Fields, CTEs>;
/**
* Adds a GROUP BY clause to the query.
*
* @example
* ```ts
* qb.select([raw('count(*) as count'), 'status'])
* .groupBy('status');
* ```
*/
groupBy<F extends Field<Entity, RootAlias, Context>>(
fields: F,
): SelectQueryBuilder<Entity, RootAlias, Hint, Context, RawAliases, Fields, CTEs>;
/**
* Adds a GROUP BY clause to the query.
*
* @example
* ```ts
* qb.select([raw('count(*) as count'), 'status'])
* .groupBy('status');
* ```
*/
groupBy<const P extends string>(
fields: NestedAutoPath<Entity, RootAlias, Context, P> | readonly NestedAutoPath<Entity, RootAlias, Context, P>[],
): SelectQueryBuilder<Entity, RootAlias, Hint, Context, RawAliases, Fields, CTEs>;
/**
* Adds a HAVING clause to the query, typically used with GROUP BY.
*
* @example
* ```ts
* qb.select([raw('count(*) as count'), 'status'])
* .groupBy('status')
* .having({ count: { $gt: 5 } });
* ```
*/
having(
cond?: QBFilterQuery<Entity, RootAlias, Context, RawAliases> | string,
params?: any[],
operator?: keyof typeof GroupOperator,
): SelectQueryBuilder<Entity, RootAlias, Hint, Context, RawAliases, Fields, CTEs>;
andHaving(
cond?: QBFilterQuery<Entity, RootAlias, Context, RawAliases> | string,
params?: any[],
): SelectQueryBuilder<Entity, RootAlias, Hint, Context, RawAliases, Fields, CTEs>;
orHaving(
cond?: QBFilterQuery<Entity, RootAlias, Context, RawAliases> | string,
params?: any[],
): SelectQueryBuilder<Entity, RootAlias, Hint, Context, RawAliases, Fields, CTEs>;
onConflict<F extends Field<Entity, RootAlias, Context>>(
fields?: F | F[] | RawQueryFragment,
): InsertQueryBuilder<Entity, RootAlias, Context>;
ignore(): this;
merge<const P extends string>(data: readonly NestedAutoPath<Entity, RootAlias, Context, P>[]): this;
merge<F extends Field<Entity, RootAlias, Context>>(data?: EntityData<Entity> | F[]): this;
returning<F extends Field<Entity, RootAlias, Context>>(fields?: F | F[]): this;
/**
* @internal
*/
populate(
populate: PopulateOptions<Entity>[],
populateWhere?: ObjectQuery<Entity> | PopulateHint | `${PopulateHint}`,
populateFilter?: ObjectQuery<Entity> | PopulateHint | `${PopulateHint}`,
): this;
/**
* Sets a LIMIT clause to restrict the number of results.
*
* @example
* ```ts
* qb.select('*').limit(10); // First 10 results
* qb.select('*').limit(10, 20); // 10 results starting from offset 20
* ```
*/
limit(
limit?: number,
offset?: number,
): SelectQueryBuilder<Entity, RootAlias, Hint, Context, RawAliases, Fields, CTEs>;
/**
* Sets an OFFSET clause to skip a number of results.
*
* @example
* ```ts
* qb.select('*').limit(10).offset(20); // Results 21-30
* ```
*/
offset(offset?: number): SelectQueryBuilder<Entity, RootAlias, Hint, Context, RawAliases, Fields, CTEs>;
withSchema(schema?: string): this;
setLockMode(mode?: LockMode, tables?: string[]): this;
setFlushMode(flushMode?: FlushMode): this;
setFlag(flag: QueryFlag): this;
unsetFlag(flag: QueryFlag): this;
hasFlag(flag: QueryFlag): boolean;
cache(config?: boolean | number | [string, number]): this;
/**
* Adds index hint to the FROM clause.
*/
indexHint(sql: string | undefined): this;
/**
* Adds COLLATE clause to ORDER BY expressions.
*/
collation(collation: string | undefined): this;
/**
* Prepend comment to the sql query using the syntax `/* ... *&#8205;/`. Some characters are forbidden such as `/*, *&#8205;/` and `?`.
*/
comment(comment: string | string[] | undefined): this;
/**
* Add hints to the query using comment-like syntax `/*+ ... *&#8205;/`. MySQL and Oracle use this syntax for optimizer hints.
* Also various DB proxies and routers use this syntax to pass hints to alter their behavior. In other dialects the hints
* are ignored as simple comments.
*/
hintComment(comment: string | string[] | undefined): this;
/**
* Specifies FROM which entity's table select/update/delete will be executed, removing all previously set FROM-s.
* Allows setting a main string alias of the selection data.
*/
from<Entity extends object>(
target: QueryBuilder<Entity>,
aliasName?: string,
): SelectQueryBuilder<Entity, RootAlias, Hint, Context, RawAliases, Fields, CTEs>;
/**
* Specifies FROM which entity's table select/update/delete will be executed, removing all previously set FROM-s.
* Allows setting a main string alias of the selection data.
*/
from<Entity extends object>(
target: EntityName<Entity>,
): SelectQueryBuilder<Entity, RootAlias, Hint, Context, RawAliases, Fields, CTEs>;
/**
* Specifies a CTE name as the FROM source, with full type safety.
* The entity type is inferred from the CTE definition passed to `.with()`.
*/
from<Name extends string & keyof CTEs, Alias extends string = Name>(
target: Name,
aliasName?: Alias,
): SelectQueryBuilder<CTEs[Name], Alias, never, never, never, '*', CTEs>;
getNativeQuery(processVirtualEntity?: boolean): NativeQueryBuilder;
protected processReturningStatement(
qb: NativeQueryBuilder,
meta?: EntityMetadata,
data?: Dictionary,
returning?: Field<any>[],
): void;
/**
* Returns the query with parameters as wildcards.
*/
getQuery(): string;
/**
* Returns raw fragment representation of this QueryBuilder.
*/
toRaw(): RawQueryFragment;
toQuery(): {
sql: string;
params: readonly unknown[];
};
/**
* Returns the list of all parameters for this query.
*/
getParams(): readonly unknown[];
/**
* Returns raw interpolated query string with all the parameters inlined.
*/
getFormattedQuery(): string;
/**
* @internal
*/
getAliasForJoinPath(path?: string | JoinOptions, options?: ICriteriaNodeProcessOptions): string | undefined;
/**
* @internal
*/
getJoinForPath(path: string, options?: ICriteriaNodeProcessOptions): JoinOptions | undefined;
/**
* @internal
*/
getNextAlias(entityName?: string | EntityName): string;
/**
* Registers a join for a specific polymorphic target type.
* Used by the driver to create per-target LEFT JOINs for JOINED loading.
* @internal
*/
addPolymorphicJoin(
prop: EntityProperty,
targetMeta: EntityMetadata,
ownerAlias: string,
alias: string,
type: JoinType,
path: string,
schema?: string,
): void;
/**
* @internal
*/
getAliasMap(): Dictionary<EntityName>;
/**
* Executes this QB and returns the raw results, mapped to the property names (unless disabled via last parameter).
* Use `method` to specify what kind of result you want to get (array/single/meta).
*/
execute<U = any>(method?: 'all' | 'get' | 'run', options?: ExecuteOptions | boolean): Promise<U>;
private getConnection;
/**
* Executes the query and returns an async iterable (async generator) that yields results one by one.
* By default, the results are merged and mapped to entity instances, without adding them to the identity map.
* You can disable merging and mapping by passing the options `{ mergeResults: false, mapResults: false }`.
* This is useful for processing large datasets without loading everything into memory at once.
*
* ```ts
* const qb = em.createQueryBuilder(Book, 'b');
* qb.select('*').where({ title: '1984' }).leftJoinAndSelect('b.author', 'a');
*
* for await (const book of qb.stream()) {
* // book is an instance of Book entity
* console.log(book.title, book.author.name);
* }
* ```
*/
stream(options?: QBStreamOptions): AsyncIterableIterator<Loaded<Entity, Hint, Fields>>;
/**
* Alias for `qb.getResultList()`
*/
getResult(): Promise<Loaded<Entity, Hint, Fields>[]>;
/**
* Executes the query, returning array of results mapped to entity instances.
*/
getResultList(limit?: number): Promise<Loaded<Entity, Hint, Fields>[]>;
private propagatePopulateHint;
private mapResult;
private mapResults;
/**
* Executes the query, returning the first result or null
*/
getSingleResult(): Promise<Loaded<Entity, Hint, Fields> | null>;
/**
* Executes count query (without offset and limit), returning total count of results
*/
getCount<F extends Field<Entity, RootAlias, Context>>(field?: F | F[], distinct?: boolean): Promise<number>;
/**
* Executes the query, returning both array of results and total count query (without offset and limit).
*/
getResultAndCount(): Promise<[Loaded<Entity, Hint, Fields>[], number]>;
/**
* Returns native query builder instance with sub-query aliased with given alias.
*/
as(alias: string): NativeQueryBuilder;
/**
* Returns native query builder instance with sub-query aliased with given alias.
* You can provide the target entity name as the first parameter and use the second parameter to point to an existing property to infer its field name.
*/
as<T>(targetEntity: EntityName<T>, alias: EntityKey<T>): NativeQueryBuilder;
/**
* Combines the current query with one or more other queries using `UNION ALL`.
* All queries must select the same columns. Returns a `QueryBuilder` that
* can be used with `$in`, passed to `qb.from()`, or converted via `.getQuery()`,
* `.getParams()`, `.toQuery()`, `.toRaw()`, etc.
*
* ```ts
* const qb1 = em.createQueryBuilder(Employee).select('id').where(condition1);
* const qb2 = em.createQueryBuilder(Employee).select('id').where(condition2);
* const qb3 = em.createQueryBuilder(Employee).select('id').where(condition3);
* const subquery = qb1.unionAll(qb2, qb3);
*
* const results = await em.find(Employee, { id: { $in: subquery } });
* ```
*/
unionAll(...others: (QueryBuilder<any> | NativeQueryBuilder)[]): QueryBuilder<Entity>;
/**
* Combines the current query with one or more other queries using `UNION` (with deduplication).
* All queries must select the same columns. Returns a `QueryBuilder` that
* can be used with `$in`, passed to `qb.from()`, or converted via `.getQuery()`,
* `.getParams()`, `.toQuery()`, `.toRaw()`, etc.
*
* ```ts
* const qb1 = em.createQueryBuilder(Employee).select('id').where(condition1);
* const qb2 = em.createQueryBuilder(Employee).select('id').where(condition2);
* const subquery = qb1.union(qb2);
*
* const results = await em.find(Employee, { id: { $in: subquery } });
* ```
*/
union(...others: (QueryBuilder<any> | NativeQueryBuilder)[]): QueryBuilder<Entity>;
private buildUnionQuery;
/**
* Adds a Common Table Expression (CTE) to the query.
* When a `QueryBuilder` is passed, its entity type is tracked for type-safe `from()`.
*
* @example
* ```ts
* const recentBooks = em.createQueryBuilder(Book, 'b').select('*').where({ ... });
* const qb = em.createQueryBuilder(Author, 'a')
* .with('recent_books', recentBooks)
* .select('*')
* .from('recent_books', 'rb'); // entity type inferred as Book
* ```
*/
with<Name extends string, Q extends QueryBuilder<any>>(
name: Name,
query: Q,
options?: CteOptions,
): QueryBuilder<
Entity,
RootAlias,
Hint,
Context,
RawAliases,
Fields,
CTEs & Record<Name, Q extends QueryBuilder<infer T> ? T : object>
>;
/**
* Adds a Common Table Expression (CTE) to the query using a `NativeQueryBuilder` or raw SQL fragment.
* The CTE name is tracked but without entity type inference — use `from()` to query from it.
*/
with<Name extends string>(
name: Name,
query: NativeQueryBuilder | RawQueryFragment,
options?: CteOptions,
): QueryBuilder<Entity, RootAlias, Hint, Context, RawAliases, Fields, CTEs & Record<Name, object>>;
/**
* Adds a recursive Common Table Expression (CTE) to the query.
* When a `QueryBuilder` is passed, its entity type is tracked for type-safe `from()`.
*
* @example
* ```ts
* const base = em.createQueryBuilder(Category).select('*').where({ parent: null });
* const rec = em.createQueryBuilder(Category, 'c').select('c.*')
* .leftJoin('c.parent', 'ct', { id: sql.ref('c.parentId') });
* const qb = em.createQueryBuilder(Category)
* .withRecursive('category_tree', base.unionAll(rec))
* .select('*')
* .from('category_tree', 'ct'); // entity type inferred as Category
* ```
*/
withRecursive<Name extends string, Q extends QueryBuilder<any>>(
name: Name,
query: Q,
options?: CteOptions,
): QueryBuilder<
Entity,
RootAlias,
Hint,
Context,
RawAliases,
Fields,
CTEs & Record<Name, Q extends QueryBuilder<infer T> ? T : object>
>;
/**
* Adds a recursive Common Table Expression (CTE) to the query using a `NativeQueryBuilder` or raw SQL fragment.
* The CTE name is tracked but without entity type inference — use `from()` to query from it.
*/
withRecursive<Name extends string>(
name: Name,
query: NativeQueryBuilder | RawQueryFragment,
options?: CteOptions,
): QueryBuilder<Entity, RootAlias, Hint, Context, RawAliases, Fields, CTEs & Record<Name, object>>;
private addCte;
clone(
reset?: boolean | (keyof QBState<Entity>)[],
preserve?: (keyof QBState<Entity>)[],
): QueryBuilder<Entity, RootAlias, Hint, Context, RawAliases, Fields, CTEs>;
/**
* Sets logger context for this query builder.
*/
setLoggerContext(context: LoggingOptions & Dictionary): void;
/**
* Gets logger context for this query builder.
*/
getLoggerContext<T extends Dictionary & LoggingOptions = Dictionary>(): T;
private fromVirtual;
/**
* Adds a join from a property object. Used internally for TPT joins where the property
* is synthetic (not in entity.properties) but defined on metadata (e.g., tptParentProp).
* The caller must create the alias first via createAlias().
* @internal
*/
addPropertyJoin(
prop: EntityProperty,
ownerAlias: string,
alias: string,
type: JoinType,
path: string,
schema?: string,
): string;
private joinReference;
protected prepareFields<T>(
fields: InternalField<T>[],
type?: 'where' | 'groupBy' | 'sub-query',
schema?: string,
): (string | RawQueryFragment)[];
/**
* Resolves nested paths like `a.books.title` to their actual field references.
* Auto-joins relations as needed and returns `{alias}.{field}`.
* For embeddeds: navigates into flattened embeddeds to return the correct field name.
*/
protected resolveNestedPath(field: string): string | string[];
protected init(type: QueryType, data?: any, cond?: any): this;
private getQueryBase;
private applyDiscriminatorCondition;
/**
* Ensures TPT joins are applied. Can be called early before finalize() to populate
* the _tptAlias map for use in join resolution. Safe to call multiple times.
* @internal
*/
ensureTPTJoins(): void;
/**
* For TPT (Table-Per-Type) inheritance: INNER JOINs parent tables.
* When querying a child entity, we need to join all parent tables.
* Field selection is handled separately in addTPTParentFields().
*/
private applyTPTJoins;
/**
* For TPT inheritance: adds field selections from parent tables.
*/
private addTPTParentFields;
/**
* For TPT polymorphic queries: LEFT JOINs all child tables when querying a TPT base class.
* Adds discriminator and child fields to determine and load the concrete type.
*/
private applyTPTPolymorphicJoins;
private finalize;
/** @internal */
processPopulateHint(): void;
private processPopulateWhere;
private mergeOnConditions;
/**
* When adding an inner join on a left joined relation, we need to nest them,
* otherwise the inner join could discard rows of the root table.
*/
private processNestedJoins;
private hasToManyJoins;
protected wrapPaginateSubQuery(meta: EntityMetadata): void;
/**
* Computes the set of populate paths from the _populate hints.
*/
protected getPopulatePaths(): Set<string>;
protected normalizeJoinPath(join: JoinOptions, meta: EntityMetadata): string;
/**
* Transfers WHERE conditions to ORDER BY joins that are not used for population.
* This ensures the outer query's ORDER BY uses the same filtered rows as the subquery.
* GH #6160
*/
protected transferConditionsForOrderByJoins(
meta: EntityMetadata,
cond: Dictionary | undefined,
populatePaths: Set<string>,
): void;
/**
* Removes joins that are not used for population or ordering to improve performance.
*/
protected pruneJoinsForPagination(meta: EntityMetadata, populatePaths: Set<string>): void;
/**
* Transfers WHERE conditions that reference a join alias to the join's ON clause.
* This is needed when a join is kept for ORDER BY after pagination wrapping,
* so the outer query orders by the same filtered rows as the subquery.
* @internal
*/
protected transferConditionsToJoin(cond: Dictionary, join: JoinOptions, path?: string): void;
private wrapModifySubQuery;
private getSchema;
/** @internal */
createAlias<U = unknown>(
entityName: EntityName<U>,
aliasName: string,
subQuery?: NativeQueryBuilder | RawQueryFragment,
): Alias<U>;
private createMainAlias;
private fromSubQuery;
private fromEntityName;
private fromRawTable;
private createQueryBuilderHelper;
private ensureFromClause;
private ensureNotFinalized;
}
export interface RunQueryBuilder<
Entity extends object,
RootAlias extends string = never,
Context extends object = never,
RawAliases extends string = never,
> extends Omit<
QueryBuilder<Entity, RootAlias, never, Context, RawAliases, '*'>,
'getResult' | 'getSingleResult' | 'getResultList' | 'where'
> {
where(
cond: QBFilterQuery<Entity, RootAlias, Context, RawAliases> | string,
params?: keyof typeof GroupOperator | any[],
operator?: keyof typeof GroupOperator,
): this;
execute<Result = QueryResult<Entity>>(method?: 'all' | 'get' | 'run', mapResults?: boolean): Promise<Result>;
}
/**
* @internal Optimized DTO type for execute().
* Bypasses the double mapped type of EntityDTO<Loaded<T, H, F>> by using DirectDTO
* which only iterates selected keys instead of all entity keys.
*
* - Wildcard, no joins: EntityDTO<T>
* - Selected fields, no joins: DirectDTO<T, F> (~132x faster than Pick<EntityDTO<T>, F>)
* - Wildcard + single-level join: Omit<EntityDTO<T>> + override populated relations
* - Selected fields + single-level join: DirectDTO for root + DirectDTO for joined (~60x faster)
* - Wildcard + nested joins: uses SerializeDTO<T, H> (~40x faster than EntityDTO<Loaded<T, H>>)
* - Fields + nested joins: falls back to EntityDTO<Loaded<T, H, F>>
*/
type DirectDTO<T, F extends keyof T> = {
[K in F]: EntityDTOProp<T, NonNullable<T[K]>> | Extract<T[K], null | undefined>;
};
type PopulatedDTO<T, K extends keyof T> =
NonNullable<T[K]> extends Collection<infer U> ? EntityDTOFlat<U & object>[] : EntityDTOFlat<ExpandProperty<T[K]>>;
type SubFields<F extends string, Rel extends string> = F extends `${Rel}.${infer Sub}` ? Sub : never;
type RootFields<F extends string, H extends string> = F extends `${string}.${string}`
? F extends `${H}.${string}`
? never
: F
: F;
type JoinDTO<T, K extends keyof T, F extends string> =
NonNullable<T[K]> extends Collection<infer U>
? SubFields<F, K & string> extends never
? EntityDTOProp<T, Collection<U>>
: DirectDTO<U, (SubFields<F, K & string> | PrimaryProperty<U>) & keyof U>[]
: SubFields<F, K & string> extends never
? EntityDTOProp<T, T[K]>
:
| DirectDTO<
NonNullable<T[K]>,
(SubFields<F, K & string> | PrimaryProperty<NonNullable<T[K]>>) & keyof NonNullable<T[K]>
>
| Extract<T[K], null | undefined>;
type ExecuteDTO<T, H extends string, F extends string> = [H] extends [never]
? [F] extends ['*']
? EntityDTOFlat<T>
: DirectDTO<T, F & keyof T>
: [F] extends ['*']
? true extends (H extends `${string}.${string}` ? true : false)
? SerializeDTO<T, H>
: Omit<EntityDTOFlat<T>, H & keyof EntityDTOFlat<T>> & {
[K in H & keyof T as K & keyof EntityDTOFlat<T>]: PopulatedDTO<T, K> | Extract<T[K], null | undefined>;
}
: true extends (H extends `${string}.${string}` ? true : false)
? EntityDTOFlat<Loaded<T, H, F>>
: DirectDTO<T, (RootFields<F, H> | PrimaryProperty<T>) & keyof T> & {
[K in H & keyof T]: JoinDTO<T, K, F>;
};
/** Shorthand for `QueryBuilder` with all generic parameters set to `any`. */
export type AnyQueryBuilder<T extends object = AnyEntity> = QueryBuilder<T, any, any, any, any, any, any>;
export interface SelectQueryBuilder<
Entity extends object = AnyEntity,
RootAlias extends string = never,
Hint extends string = never,
Context extends object = never,
RawAliases extends string = never,
Fields extends string = '*',
CTEs extends Record<string, object> = {},
> extends QueryBuilder<Entity, RootAlias, Hint, Context, RawAliases, Fields, CTEs> {
execute<Result = ExecuteDTO<Entity, Hint, Fields>[]>(
method?: 'all' | 'get' | 'run',
mapResults?: boolean,
): Promise<Result>;
execute<Result = ExecuteDTO<Entity, Hint, Fields>[]>(method: 'all', mapResults?: boolean): Promise<Result>;
execute<Result = ExecuteDTO<Entity, Hint, Fields>>(method: 'get', mapResults?: boolean): Promise<Result>;
execute<Result = QueryResult<Entity>>(method: 'run', mapResults?: boolean): Promise<Result>;
}
export interface CountQueryBuilder<Entity extends object> extends QueryBuilder<Entity, any, any> {
execute<
Result = {
count: number;
}[],
>(
method?: 'all' | 'get' | 'run',
mapResults?: boolean,
): Promise<Result>;
execute<
Result = {
count: number;
}[],
>(
method: 'all',
mapResults?: boolean,
): Promise<Result>;
execute<
Result = {
count: number;
},
>(
method: 'get',
mapResults?: boolean,
): Promise<Result>;
execute<
Result = QueryResult<{
count: number;
}>,
>(
method: 'run',
mapResults?: boolean,
): Promise<Result>;
}
export interface InsertQueryBuilder<
T extends object,
RootAlias extends string = never,
Context extends object = never,
> extends RunQueryBuilder<T, RootAlias, Context> {}
export interface UpdateQueryBuilder<
T extends object,
RootAlias extends string = never,
Context extends object = never,
> extends RunQueryBuilder<T, RootAlias, Context> {}
export interface DeleteQueryBuilder<
T extends object,
RootAlias extends string = never,
Context extends object = never,
> extends RunQueryBuilder<T, RootAlias, Context> {}
export interface TruncateQueryBuilder<T extends object> extends RunQueryBuilder<T> {}
export {};