/** * QueryBuilder — Fluent SQL query builder for Tina4 Node.js. * * Usage: * // Standalone * const result = QueryBuilder.fromTable("users", db) * .select("id", "name") * .where("active = ?", [1]) * .orderBy("name ASC") * .limit(10) * .get(); * * // From ORM model * const result = User.query() * .where("age > ?", [18]) * .orderBy("name") * .get(); */ import type { DatabaseAdapter } from "./types.js"; import { DatabaseResult } from "./databaseResult.js"; export declare class QueryBuilder { private table; private db; private columns; private selectParams; private wheres; private params; private joinClauses; private groupByCols; private havings; private havingParams; private orderByCols; private orderByParams; private primaryKey; private limitVal; private offsetVal; /** * Private constructor — use static factory methods. */ private constructor(); /** * Create a QueryBuilder for a table. * * @param tableName - Table name. * @param db - Optional database adapter. * @returns A new QueryBuilder instance. */ static fromTable(tableName: string, db?: DatabaseAdapter, primaryKey?: string): QueryBuilder; /** * Set the columns to select. * * @param cols - Column names. * @returns this for chaining. */ select(...cols: string[]): QueryBuilder; /** * Add a WHERE condition (AND). * * @param condition - SQL condition with ? placeholders. * @param params - Parameter values. * @returns this for chaining. */ where(condition: string, params?: unknown[]): QueryBuilder; /** * Add a WHERE condition (OR). * * @param condition - SQL condition with ? placeholders. * @param params - Parameter values. * @returns this for chaining. */ orWhere(condition: string, params?: unknown[]): QueryBuilder; /** * Add an INNER JOIN. * * @param table - Table to join. * @param onClause - Join condition. * @returns this for chaining. */ join(table: string, onClause: string): QueryBuilder; /** * Add a LEFT JOIN. * * @param table - Table to join. * @param onClause - Join condition. * @returns this for chaining. */ leftJoin(table: string, onClause: string): QueryBuilder; /** * Add a GROUP BY column. * * @param column - Column name. * @returns this for chaining. */ groupBy(column: string): QueryBuilder; /** * Add a HAVING clause. * * @param expression - HAVING expression with ? placeholders. * @param params - Parameter values. * @returns this for chaining. */ having(expression: string, params?: unknown[]): QueryBuilder; /** * Add an ORDER BY clause. * * @param expression - Column and direction (e.g. "name ASC"). * @returns this for chaining. */ orderBy(expression: string): QueryBuilder; withinDistance(column: string, pointValue: unknown, radiusMetres: number, srid?: number): QueryBuilder; intersects(column: string, geometry: unknown, srid?: number): QueryBuilder; bbox(column: string, minLon: unknown, minLat: unknown, maxLon: unknown, maxLat: unknown, srid?: number): QueryBuilder; selectDistance(column: string, pointValue: unknown, alias?: string, srid?: number): QueryBuilder; orderByDistance(column: string, pointValue: unknown, direction?: "ASC" | "DESC", srid?: number): QueryBuilder; /** * Set LIMIT and optional OFFSET. * * @param count - Maximum rows to return. * @param offset - Number of rows to skip. * @returns this for chaining. */ limit(count: number, offset?: number): QueryBuilder; /** * Build and return the SQL string without executing. * * @returns The constructed SQL query. */ toSql(): string; /** * Execute the query and return a DatabaseResult. * * BREAKING (3.13.95, parity): this returned a bare array of rows. The other * three frameworks all return the DatabaseResult that `db.fetch()` produces: * Python get() -> DatabaseResult (orm/query_builder/__init__.py) * PHP get(): mixed -> $this->db->fetch(...) * Ruby get -> @db.fetch(...) * Node was the odd one out, so the same builder chain returned a different * TYPE per language and portable code could not read `.records`, `.count`, * `.limit` or `.offset` off it. * * MIGRATION: read `.records` for the rows. * before: const rows = await qb.get(); rows.length * after: const result = await qb.get(); result.records.length * DatabaseResult is iterable, so `for (const row of result)` and * `[...result]` work unchanged, and `response()`/`res.json()` already * auto-serialize it to a JSON array. * * No default LIMIT is applied when `.limit()` was never called (v3.13.39) -- * a silent cap here was a data-loss-on-read footgun. That is unchanged. * * @returns DatabaseResult carrying `.records`, `.count`, `.limit`, `.offset`. */ get(): Promise; /** * Execute the query and return a single row. * * @returns A single row object, or null. */ first>(): Promise; /** * Execute the query and return the row count. * * @returns Number of matching rows. */ count(): Promise; /** * Check whether any matching rows exist. * * @returns True if at least one row matches. */ exists(): Promise; /** * Convert the fluent builder state into a MongoDB-compatible query document. * * @returns An object with filter, projection, sort, limit, skip (only non-empty keys). */ toMongo(): { filter?: Record; projection?: Record; sort?: Record; limit?: number; skip?: number; }; /** * Parse a single SQL condition string into a MongoDB filter object. */ private parseConditionToMongo; /** * Merge multiple single-field mongo condition objects into one. * Uses $and if field keys conflict. */ private mergeMongoConditions; /** * Build the WHERE clause from accumulated conditions. */ private buildWhere; /** * Ensure a database adapter is available. */ private ensureDb; private engine; }