import { type AggregatePath, type ColumnFilter, type ComputedSource, type EntityFieldInfo, type EntityRelationInfo, type FieldExtent, type FieldExtentField, type FilterAdapter, type FilterEntity, type GroupByCountField, type SortItem, type VectorSearchOptions } from '@dudousxd/nestjs-filter';
import { Table } from 'drizzle-orm';
import { DrizzleQuery } from './drizzle-query.js';
import { type DrizzleDialect } from './operator-resolver.js';
import { DrizzleSchemaMetadata } from './schema-metadata.js';
import type { DrizzleDatabase } from './types.js';
export interface DrizzleAdapterOptions {
    /**
     * The schema module — the same object passed to `drizzle(client, { schema })`
     * (tables AND `relations()` declarations). Defaults to the schema the `db`
     * instance was created with. Relation features (includes, dot-notation
     * filters, `whereHas`, `@Relations`, to-many aggregates, `describe()`
     * relations) need it; column filtering, sort, search and pagination do not.
     */
    schema?: Record<string, unknown>;
    /** Overrides dialect detection (normally read off the `db` instance). */
    dialect?: DrizzleDialect;
}
/** Reads the dialect off a drizzle database instance. */
export declare function detectDialect(db: DrizzleDatabase): DrizzleDialect;
/**
 * {@link FilterAdapter} for Drizzle ORM.
 *
 * The "entity" is a Drizzle table object (`pgTable`/`mysqlTable`/`sqliteTable`),
 * and the query builder handed to filters is a {@link DrizzleQuery} — an
 * accumulator of conditions/ordering/projection that becomes one
 * `db.select().from(table)` at execution time. See the package README for the
 * design and the differences from the MikroORM / TypeORM adapters.
 */
export declare class DrizzleAdapter implements FilterAdapter {
    readonly db: DrizzleDatabase;
    private readonly logger;
    readonly dialect: DrizzleDialect;
    readonly metadata: DrizzleSchemaMetadata;
    constructor(db: DrizzleDatabase, options?: DrizzleAdapterOptions);
    private newContext;
    /**
     * A fresh {@link DrizzleQuery} over `table` — the same object `@ApplyFilter`
     * injects into controllers. Useful in services:
     *
     * ```ts
     * const q = adapter.query(users);
     * await runner.apply(UserFilter, input, q);
     * const rows = await q.execute();
     * ```
     */
    query<TTable extends Table>(table: TTable): DrizzleQuery<TTable>;
    createQueryBuilder<E>(entity: FilterEntity<E>): unknown;
    applyRelationConstraint(qb: unknown, relationName: string, callback: (relationQb: unknown) => Promise<void>): Promise<void>;
    getEntityFields(entity: FilterEntity): EntityFieldInfo[] | null;
    getEntityRelations(entity: FilterEntity): EntityRelationInfo[] | null;
    getRelatedFields(entity: FilterEntity, relationName: string): EntityFieldInfo[] | null;
    resolveFieldPath(entity: FilterEntity, path: string): 'field' | 'relation' | 'json' | null;
    applyIncludes(qb: unknown, includes: string[]): void;
    populate(rows: unknown[], relations: string[], entity: FilterEntity): Promise<void>;
    /**
     * Resolves a (possibly dotted) field path of `q` to a condition built over
     * the column it lands on. A root column is used directly; a relation path
     * becomes nested `EXISTS` subqueries with `build` applied to the last hop's
     * column; a path ending on a relation compares its key. Unknown/unsafe paths
     * resolve to `undefined` — never to a condition-less `EXISTS`, which would
     * quietly widen the filter to "has any related row".
     */
    private pathCondition;
    /**
     * A condition on a bare relation (`where: [{ field: 'manager', … }]`): a
     * to-one relation that owns its foreign key compares that key directly;
     * any other relation compares the related row's key inside an `EXISTS`.
     */
    private relationKeyCondition;
    applyColumnFilters(qb: unknown, filters: ColumnFilter[]): void;
    applyAutoField(qb: unknown, field: string, value: unknown): void;
    applyAutoRelationField(qb: unknown, relationName: string, field: string, value: unknown): void;
    applySearch(qb: unknown, term: string, columns: string[]): void;
    applyVectorSearch(qb: unknown, term: string, vectorColumn: string, opts?: VectorSearchOptions): void;
    /** A projectable value for a field: a root column, or a to-one relation column's scalar subquery. */
    private fieldValue;
    applyDistinct(qb: unknown, fields: string[]): void;
    applySelect(qb: unknown, fields: string[], entity: FilterEntity): void;
    /**
     * What to ORDER BY for a field. Under a DISTINCT projection that already
     * selected a non-column expression for this field, order by its output
     * alias: a second copy of a correlated subquery is a different expression,
     * which Postgres rejects under DISTINCT.
     */
    private sortTarget;
    applySort(qb: unknown, sorts: SortItem[]): void;
    applyOffsetPagination(qb: unknown, page: number, size: number): void;
    getPrimaryKey(entity: FilterEntity): string | null;
    applyKeysetPagination(qb: unknown, keyset: SortItem[], values: unknown[]): void;
    applyKeysetOrderAndLimit(qb: unknown, keyset: SortItem[], limit: number): void;
    getResultAndCount<T = unknown>(qb: unknown): Promise<{
        rows: T[];
        total: number;
    }>;
    getDistinctResultAndCount(qb: unknown): Promise<{
        rows: Record<string, unknown>[];
        total: number;
    }>;
    getResult(qb: unknown): Promise<unknown[]>;
    /**
     * Resolves a developer-declared computed source to SQL. A string is emitted
     * verbatim (auto-parenthesized when it is a bare `SELECT …` / `EXISTS (…)`);
     * a function receives `{ alias: <root table name>, em: db }` and may return a
     * SQL string, a drizzle `sql` fragment, or a drizzle select builder (used as
     * a scalar subquery). Because the adapter never joins, an unqualified column
     * name in a string source always means the root table's column.
     */
    private computedExpression;
    applyComputedField(qb: unknown, source: ComputedSource, value: unknown): void;
    applyComputedSort(qb: unknown, source: ComputedSource, direction: 'asc' | 'desc'): void;
    applyComputedSelect(qb: unknown, alias: string, source: ComputedSource): void;
    applyComputedDistinct(qb: unknown, alias: string, source: ComputedSource): void;
    private addDistinctMember;
    /**
     * Compiles `posts.$count` / `posts.$sum.views` / … into a correlated scalar
     * subquery over the to-many relation — never a JOIN + GROUP BY, which would
     * multiply the root rows. The relation and child column come from schema
     * metadata; nothing client-supplied is emitted as SQL text.
     *
     * @returns the expression, and — for `min`/`max` — the child column, whose
     *   encoder binds comparison values (dates compare as the column stores them).
     */
    private aggregateSubquery;
    private originalColumn;
    applyAggregateSort(qb: unknown, aggregate: AggregatePath, direction: 'asc' | 'desc'): void;
    applyAggregateField(qb: unknown, aggregate: AggregatePath, filter: ColumnFilter): void;
    applyAggregateDistinct(qb: unknown, aggregate: AggregatePath): void;
    /**
     * The expression for a measurable/groupable field, plus the column whose
     * decoder should map its values back (so a date comes back as a `Date`, a
     * SQLite boolean as `true`/`false`).
     */
    private measurable;
    private asText;
    /**
     * `SELECT <expr> AS value, COUNT(*) AS count … GROUP BY 1`, or the bucketed
     * `FLOOR(<expr> / ?) * ?` variant. The bucket width is a bound parameter;
     * grouping by ordinal position keeps Postgres from rejecting the query
     * because the SELECT and GROUP BY copies of the expression carry different
     * placeholders (`$1` vs `$3`).
     */
    groupByCount(qb: unknown, field: GroupByCountField, _entity: FilterEntity, opts?: {
        bucket?: number;
        limit?: number;
        offset?: number;
        search?: string;
    }): Promise<Array<{
        value: unknown;
        count: number;
    }>>;
    /**
     * `MIN`/`MAX` of every requested field in ONE select over the filtered rows —
     * ordering and the page window are not part of the question and are simply
     * not emitted. Values are decoded through the field's column, so dates stay
     * dates.
     */
    fieldExtent(qb: unknown, fields: FieldExtentField[]): Promise<Record<string, FieldExtent>>;
}
//# sourceMappingURL=drizzle.adapter.d.ts.map