import { SqlBuildContext } from './conditions'; import type { OrderDirection } from '../entity/db-context'; import { QueryExecutor } from '../entity/db-context'; import type { DatabaseClient } from '../database/database-client.interface'; import { Subquery } from './subquery'; /** * Union type: UNION removes duplicates, UNION ALL keeps all rows */ export type UnionType = 'UNION' | 'UNION ALL'; /** * Minimal contract a union component must satisfy. SelectQueryBuilder provides * the optional `_consumeUnionMetadata` and `_applyUnionPostProcessing` hooks * so the union can reconstruct nested-object projections and apply collection * mappers; non-SelectQueryBuilder components (rare; e.g. ad-hoc objects used * in tests) skip those steps. */ export interface UnionLegBuilder { buildUnionSql: (context: SqlBuildContext) => string; /** @internal */ _consumeUnionMetadata?(): { nestedPaths: Set; selectionResult: any; } | undefined; /** @internal */ _applyUnionPostProcessing?(rows: any[], meta: { nestedPaths: Set; selectionResult: any; }): any[]; } /** * Builder for UNION and UNION ALL queries. * Combines multiple SELECT queries into a single result set. * * @example * ```typescript * // Basic UNION (removes duplicates) * const result = await db.users * .select(u => ({ id: u.id, name: u.name })) * .union( * db.customers.select(c => ({ id: c.id, name: c.name })) * ) * .toList(); * * // UNION ALL (keeps duplicates) * const allRows = await db.activeUsers * .select(u => ({ id: u.id })) * .unionAll( * db.inactiveUsers.select(u => ({ id: u.id })) * ) * .toList(); * * // Multiple unions with ordering * const sorted = await db.users * .select(u => ({ id: u.id, name: u.name })) * .union(db.customers.select(c => ({ id: c.id, name: c.name }))) * .unionAll(db.vendors.select(v => ({ id: v.id, name: v.name }))) * .orderBy(r => r.name) * .limit(100) * .toList(); * ``` */ export declare class UnionQueryBuilder { private components; private orderByFields; private limitValue?; private offsetValue?; private client; private executor?; /** * Internal constructor - use SelectQueryBuilder.union() or unionAll() to create instances */ constructor(firstQuery: UnionLegBuilder, client: DatabaseClient, executor?: QueryExecutor); /** * Override the timeout for this union query (ms). Only this query is wrapped * (`SET LOCAL statement_timeout`). Overrides the connection-level default; pass * `0` to disable. On timeout a `QueryTimeoutError` is thrown. */ withTimeout(timeoutMs: number): this; /** * Mark this union query as expected to finish within `expectedMs` (ms). If it * runs longer, the context's `onQueryTakingTooLong` callback fires (the query * is NOT cancelled). Overrides the context's `longRunningQueryThreshold`. */ expectedExecutionTime(expectedMs: number): this; /** * Add a query with UNION (removes duplicate rows) * * @param query The query to union with the current result * @returns A new UnionQueryBuilder with the added query * * @example * ```typescript * const users = await db.activeUsers * .select(u => ({ id: u.id, email: u.email })) * .union(db.pendingUsers.select(u => ({ id: u.id, email: u.email }))) * .toList(); * ``` */ union(query: UnionLegBuilder): UnionQueryBuilder; /** * Add a query with UNION ALL (keeps all rows including duplicates) * * @param query The query to union with the current result * @returns A new UnionQueryBuilder with the added query * * @example * ```typescript * // UNION ALL is faster than UNION as it doesn't need to remove duplicates * const allLogs = await db.errorLogs * .select(l => ({ timestamp: l.createdAt, message: l.message })) * .unionAll(db.infoLogs.select(l => ({ timestamp: l.createdAt, message: l.message }))) * .orderBy(r => r.timestamp) * .toList(); * ``` */ unionAll(query: UnionLegBuilder): UnionQueryBuilder; /** * Order the combined result set * * @param selector Function that selects the field(s) to order by * @returns This builder for chaining * * @example * ```typescript * // Single field * .orderBy(r => r.name) * * // Multiple fields * .orderBy(r => [r.lastName, r.firstName]) * * // With direction * .orderBy(r => [[r.createdAt, 'DESC'], [r.name, 'ASC']]) * ``` */ orderBy(selector: (row: TSelection) => T): this; orderBy(selector: (row: TSelection) => T[]): this; orderBy(selector: (row: TSelection) => Array<[T, OrderDirection]>): this; /** * Limit the number of results * * @param count Maximum number of rows to return * @returns This builder for chaining */ limit(count: number): this; /** * Skip a number of results * * @param count Number of rows to skip * @returns This builder for chaining */ offset(count: number): this; /** * Execute the union query and return results * * @returns Promise resolving to array of results */ toList(): Promise; /** * Get the first result or null if no results * * @returns Promise resolving to first result or null */ firstOrDefault(): Promise; /** * Count the total number of results * * @returns Promise resolving to the count */ count(): Promise; /** * Build the SQL for this union query * @internal */ buildSql(): { sql: string; params: any[]; }; /** * Build the SQL for this union query inside an outer context (e.g. when * this union is used as a subquery via {@link asSubquery}). Reuses the * outer context's `paramCounter` and `params` array so parameter indices * chain correctly across the whole composite statement. * * @internal */ buildSql(outerContext: SqlBuildContext): string; /** * Convert this union query to a subquery usable in WHERE, SELECT, JOIN, or * FROM clauses of an outer query. Mirrors {@link SelectQueryBuilder.asSubquery} * but composes a UNION ALL / UNION across multiple SELECT legs into a single * subquery expression. * * @template TMode - 'scalar' for a single value, 'array' for column list (use * with `inSubquery(...)`), 'table' for full rows * @returns Subquery that can be passed to `inSubquery`, `exists`, etc. * * @example * ```typescript * // Filter outer query by the union of two id selections * const friendIds = db.userRelations * .where(r => eq(r.parentId, userId)) * .select(r => r.slaveId) * .unionAll( * db.userRelations * .where(r => eq(r.slaveId, userId)) * .select(r => r.parentId) * ) * .asSubquery('array'); * * const rows = await db.usersEshop * .where(u => inSubquery(u.id, friendIds)) * .select(u => ({ id: u.id, name: u.name })) * .toList(); * ``` */ asSubquery(mode?: TMode): Subquery; /** * Get the SQL string for debugging * * @returns The SQL that would be executed */ toSql(): string; /** * Create a mock row for orderBy selector */ private createMockRow; /** * Clone this builder */ private clone; } /** * Check if a value is a UnionQueryBuilder */ export declare function isUnionQueryBuilder(value: any): value is UnionQueryBuilder; //# sourceMappingURL=union-builder.d.ts.map