PuddySqlQuery is a high-level query builder class for managing SQL tables using flexible filters, tag-based conditions, JOINs, pagination, and result validation.
It supports complex nested logic (AND/OR), dynamic tag filtering, composable JOIN structures, and automatic SQL clause generation — all while keeping the API intuitive and extensible.
Perfect for advanced search interfaces, dashboards, data exploration tools, or any project that needs powerful yet readable SQL control.
Defines the schema structure for programmatic SQL table creation or modification.
Each array entry represents a single column with this 4-item tuple format:
[columnName, columnType, columnOptions, columnMeta]
- columnName (
string): Name of the column, e.g.,"id","username". - columnType (
string): SQL data type, e.g.,"TEXT","INTEGER". - columnOptions (
string): SQL options likeNOT NULL,PRIMARY KEY,DEFAULT. - columnMeta (
any): Arbitrary metadata (UI hints, descriptions, tags).
Result of a paginated SQL query locating a specific item:
- page (
number): Current page number (starting at 1). - pages (
number): Total pages available. - total (
number): Total items in dataset. - position (
number): Exact item index (starting at 0). - item (
FreeObj| optional): The actual found item, if returned.
Definition for tag group filtering in SQL clauses:
- group.column (
string| optional): SQL column for tag data (default:this.getColumnName()). - group.tableName (
string| optional): Table name used (default:this.defaultTableName). - group.allowWildcards (
boolean| optional): Allow wildcards in matching (default:false). - group.include (
Array<string|string[]>): Tag values or OR-groups to include in filtering.
Result object for paginated queries:
- items (
any[]): Array of items returned for current page. - totalPages (
number): Total available pages. - totalItems (
number): Total matching items without pagination.
Flexible input for SELECT clause:
Can be a string, array of strings, or object with:
aliases(mapping display names to real columns)values(array of columns to select)boost(weighted ranking configuration with alias and boost rules)
Can also benullfor defaults.
Parameter cache for building WHERE clauses:
index(number): Starting index for SQL placeholders ($1,$2, ...). Defaults to1.values(any[]): Values collected for parameter binding.
Generic object with arbitrary keys and unknown values:
- Keys can be
string,number, orsymbol. - Values are of unknown type. Useful for flexible data containers.
Conditions used in SQL WHERE clause:
group('AND'|'OR'|'and'|'or'| optional): Logical operator for condition groups.conditions(array ofWhereConditionsorQueryGroup| optional): Nested logical conditions.funcName,operator,value,valType,lPos,newOp,column(various optional fields for SQL expressions and chaining).
Flexible group of WHERE conditions, supporting either:
- Single condition object:
{ column: 'name', operator: '=', value: 'pudding' }- Named group of conditions:
{
searchByName: { column: 'name', operator: 'ILIKE', value: '%fluttershy%' },
searchByType: { column: 'type', operator: '=', value: 'pegasus' }
}Enables dynamic grouping for advanced filters and scoped searches.
Rule for weighted query boosting:
columns(optional array): Columns to apply boost on.operator: Condition operator, e.g.,'=','LIKE'.value: Value or array of values to match.weight: Numeric factor to boost matched results.
Object defining SQL JOIN clauses:
table(string): Table name to join.compare(string): ON clause condition.type(string| optional): JOIN type, defaults to'left'.
Settings for SQL table/entity configuration:
name(optional string): Table or view name.select(SelectQuery): SELECT clause config. Defaults to"*".join(optional string): JOIN table name.joinCompare(optional string): JOIN condition, e.g.'t.key = j.key'.order(optional string): ORDER BY clause.id(string): Primary key column. Defaults to'key'.subId(optional string): Secondary key column (composite key or scope).
Configuration for SQL entity querying and joining:
select(string): Default columns to select.name(string): Main table/view name.id(string): Primary key column name.joinCompare(optional string): JOIN matching condition.order(optional string): Default ordering clause.subId(optional string): Secondary ID column.join(optional string): JOIN clause (e.g.,"LEFT JOIN profiles ON ...").
Function type that takes and returns a WhereConditions object.
Used for SQL WHERE clause transformations or appends.
Map of named condition identifiers to their transformation functions (WhereConditionsFunc).
The main query engine for operating on a specific SQL table with support for dynamic conditions, tags, joins, and advanced query building.
#conditions: Registered SQL conditions map.#customValFunc: Custom value transformation functions.#db: The internalPuddySqlEnginedatabase instance.#settings: Current table/query settings.#table: Table schema and column metadata.#tagColumns: Tag filtering helpers per column.
Safely retrieves the internal database instance.
Throws if the internal DB is not a valid PuddySqlEngine instance.
const db = instance.getDb();Throws:
Error if DB is invalid or uninitialized.
Initializes the query instance with a set of predefined SQL condition operators and function-based conditions.
- Adds common operators like
'LIKE','NOT','=','!=','>=','<=','>','<'. - Registers advanced SQL functions using
addConditionV2for phonetic, string, math, and date/time functions:- Phonetic:
SOUNDEX - Case conversion:
LOWER,UPPER - Trim whitespace:
TRIM,LTRIM,RTRIM - String length:
LENGTH - Math:
ABS,ROUND,CEIL,FLOOR - Null handling:
COALESCE - String formatting:
HEX,QUOTE - Unicode:
UNICODE,CHAR - Type inspection:
TYPEOF - Date/time extraction:
DATE,TIME,DATETIME,JULIANDAY
- Phonetic:
Checks if a condition is registered under the given key.
-
Parameters:
key(string): Condition identifier.
-
Returns:
boolean:trueif condition exists, elsefalse.
Retrieves the condition function registered with the given key.
-
Parameters:
key(string): Condition identifier.
-
Returns:
WhereConditionsFunc: The condition function.
-
Throws:
Errorif condition key is not found.
Returns a shallow copy of all registered condition functions.
- Returns:
SqlConditions— Object mapping keys to condition functions.
Registers a new condition type for query generation.
-
Parameters:
key(string): Unique identifier for the condition.conditionHandler(string|WhereConditions|WhereConditionsFunc):
Defines the condition logic.- String: SQL operator (e.g.,
'=','LIKE') - Object: Must include
.operator(e.g.,{ operator: '>=' }) - Function: Custom condition builder function.
- String: SQL operator (e.g.,
valueHandler(function(string): string|null): Optional function transforming parameter values (e.g., forSOUNDEX).
-
Validation:
- Throws if
keyis not a non-empty string. - Throws if
keyalready exists in conditions or value handlers. - Throws if
conditionHandleris not valid type. - Throws if
valueHandleris provided but not a function.
- Throws if
-
Behavior:
- Stores
conditionHandlerinternally (function wrapped if string or object). - Stores
valueHandlerif provided.
- Stores
Registers a SQL function-based condition with optional parameter transformation.
-
Purpose:
Wraps a SQL column in a function (e.g.,LOWER(column)) and optionally applies the same function to the parameter (LOWER($1)). -
Parameters:
funcName(string): SQL function name (must be non-empty).editParamByDefault(boolean, defaultfalse): Iftrue, applies function to the query parameter by default.operator(string, default'='): SQL comparison operator.
-
Runtime Behavior:
- Uses
group.newOpif provided to override operator. - Uses
group.funcNameif provided to override function name in SQL and value transformation. - Final SQL resembles:
FUNC(column) OP FUNC($n)orFUNC(column) OP $ndepending oneditParamByDefault.
- Uses
-
Throws:
TypeErrorif parameters are invalid types or empty strings.
-
Example Usages:
- Registering ROUND with operator
!=:addConditionV2('ROUND', false, '!=');
- Registering LOWER with parameter function:
addConditionV2('LOWER', true); // Parses: LOWER(username) = LOWER($1)
- Registering UPPER without parameter function:
addConditionV2('UPPER'); // Parses: UPPER(username) = $1
- Overriding operator at runtime:
addConditionV2('CEIL', true); parse({ column: 'price', value: 3, newOp: '>', operator: 'CEIL', funcName: null }); // Result: CEIL(price) > 3
- Registering ROUND with operator
Generates a SQL SELECT clause from flexible input formats, supporting aliases, complex expressions, and weighted boosts via CASE statements.
null/undefined: returns'*'string: returns the parsed column or SQL expression (supports aliases withAS)string[]: returns a comma-separated list of parsed columnsobject: supports structured input with:aliases: key-value pairs for columns and aliasesvalues: array of column names or expressionsboost: weighted relevance using CASE, with rules for columns, operators, values, and weights
columns(optional): single string or array of strings (target columns)value: string or array (value(s) to compare) or raw SQL condition ifcolumnsomittedoperator: SQL operator (default'LIKE', supports'IN','=', etc.)weight: numeric weight applied on match (default1)
All values are escaped using
pg.escapeLiteral()for PostgreSQL safety.
TypeErrorfor invalid input typesErrorifboost.aliasis missing or invalidErrorifboost.valueexists but is not an array
selectGenerator();
// returns '*'
selectGenerator('COUNT(*) AS total');
// returns 'COUNT(*) AS total'
selectGenerator(['id', 'username']);
// returns 'id, username'
selectGenerator({
aliases: { id: 'image_id', uploader: 'user_name' },
values: ['created_at', 'score']
});
// returns 'id AS image_id, uploader AS user_name, created_at, score'
selectGenerator({
aliases: { id: 'image_id', uploader: 'user_name' },
values: ['created_at'],
boost: {
alias: 'relevance',
value: [
{ columns: ['tags', 'description'], value: 'fluttershy', weight: 2 },
{ columns: 'tags', value: 'pinkie pie', operator: 'LIKE', weight: 1.5 },
{ columns: 'tags', value: 'oc', weight: -1 },
{ value: "score > 100 AND views < 1000", weight: 5 }
]
}
});
// returns CASE statement boosting relevance + other columnsParses an individual column or SQL expression, optionally applying an alias.
column(string): Column name or SQL expressionalias(string, optional): Alias name
Returns: SQL snippet like "column" or "column AS alias"
Throws:
TypeErrorif inputs are not strings
These methods help generate SQLite-compatible JSON queries:
Internal helper that asserts the input is a string and returns it.
Generates SQLite JSON extraction snippet:
json_extract(where, '$.name')where(string): JSON column or expressionname(string): Key or JSON path
Generates SQLite json_each call to expand JSON arrays or objects into rows:
json_each(source)source(string): JSON column or expression
Combines getJsonExtract and getJsonEach to expand a JSON array from a specific key:
json_each(json_extract(where, '$.name'))where(string): JSON columnname(string): Key containing array
Extracts a JSON value and casts it to a specified SQLite type:
CAST(json_extract(where, '$.name') AS TYPE)where(string): JSON columnname(string): Key to extracttype(string): SQLite type to cast to (e.g.,'INTEGER','TEXT','REAL')
Updates a SQL table schema by adding, removing, modifying, or renaming columns.
changes(SqlTableConfig): Array of change commands, each an array:['ADD', columnName, columnType, columnOptions?]['REMOVE', columnName]['MODIFY', columnName, newColumnType, newOptions?]['RENAME', oldColumnName, newColumnName]
- Executes corresponding
ALTER TABLESQL statements. - Uses
this.getDb()for DB access. - Throws on invalid input.
- Logs errors if DB operations fail but continues.
TypeErrorifchangesis not an array of arrays or invalid action.Errorif required parameters for each action are missing or invalid.
Drops the current table if it exists.
Promise<boolean>: resolvestrueon success,falseon failure (except connection errors, which reject).
- Runs
DROP TABLE {tableName}. - Uses connection error detection to reject or resolve accordingly.
Creates a SQL table based on column definitions.
columns(SqlTableConfig): Array of column definitions, each an array:[name, type, options?]- Custom type
"TAGS"is converted internally to"JSON"and registered for tag management.
- Builds a
CREATE TABLE IF NOT EXISTSstatement. - Stores the column schema in
this.#tablewith uppercase types and options. - Creates
PuddySqlTagsinstances for"TAGS"columns stored inthis.#tagColumns. - Throws on invalid definitions or missing table name.
Checks if a column has an associated tag editor (PuddySqlTags instance).
name(string): Column name.
boolean—trueif tag editor exists,falseotherwise.
Retrieves the PuddySqlTags instance for a specific tag column.
name(string): Column name.
PuddySqlTagsinstance.
Errorif the column has no associated tag editor or invalid input.
Returns all tag editors as a shallow copy object mapping column names to their PuddySqlTags.
Record<string, PuddySqlTags>— All tag editors by column.
A private map of functions that convert raw SQL values into native JavaScript types.
| Key | Description |
|---|---|
boolean |
Returns true for: true, "true", 1, "1". |
bigInt |
Converts values to BigInt. Returns null on failure. |
int |
Parses to integer (parseInt). Returns null if NaN. |
float |
Parses to float (parseFloat). Returns null if NaN. |
json |
Parses JSON strings. Returns original if already object/array. |
tags |
Parses strings to arrays of strings. Invalid elements become null. |
text |
Returns the string if it's valid. Otherwise null. |
date |
Converts to valid Date object. Returns null if invalid. |
Maps SQL types (e.g., INTEGER, TEXT, TAGS) to their corresponding #jsonEscape functions.
Supported aliases include:
BOOLEAN,BOOLBIGINT,DECIMAL,NUMERICINTEGER,INT,SMALLINT,TINYINTREAL,FLOAT,DOUBLETEXT,CHAR,VARCHAR,CLOBJSON,TAGSDATE,DATETIME,TIMESTAMP,TIME
Converts result rows from raw SQL types into properly typed JavaScript values based on this.#table.
result(object) — A result row from the database.
FreeObj— The same object, converted.
Converts values for writing back to the database using type definitions from this.#table.
valueObj(FreeObj) — Object of raw input values.
FreeObj— The same object, with values sanitized or transformed.
Merges new settings with existing internal config and sets the database instance.
settings(Partial<TableSettings>) — Any new values to apply.db(PuddySqlEngine) — Reference to the main database engine.
- Validates and fills default values (
select,join,id, etc.) - Uses
selectGeneratorfor advanced select clause handling.
TypeErrorifsettingsis not an object.Errorifdbis not an instance ofPuddySqlEngine.
Maps driver types to the field used to count affected rows:
"sqlite3"→"changes""postgre"→"rowCount"
Used internally to abstract SQL dialect differences.
Returns the number of affected rows from a database result, abstracting engine-specific fields.
| Engine | Field |
|---|---|
sqlite3 |
changes |
postgre |
rowCount |
| Fallback | rowsAffected |
result(object|null) — Result fromrun()or query execution.
number— Affected rows, or0.
Checks if a row exists using primary key (and optional subId for composite keys).
id(string|number) — Primary ID.subId(string|number) — Optional secondary ID.
Promise<boolean>—trueif exists.
Used when saving data back to SQL.
| Type | Behavior |
|---|---|
JSON |
JSON.stringify(value) |
TAGS |
Ensures all entries are strings before stringifying |
Applies the appropriate fix function for a specific column type before writing.
v(any) — Value to transform.name(string) — Column name.
any— Escaped value or original.
Updates one or more rows using a filter object instead of direct IDs.
valueObj(FreeObj) — Fields to update.filter(QueryGroup) — WHERE clause object.
Promise<number>— Rows updated.
Updates a row using its primary key (subId supported if available).
id(string|number) — Primary ID.valueObj(FreeObj) — Fields to update.
Promise<number>— Rows updated.
Inserts or updates one or more rows. Automatically escapes values and handles generated IDs.
id(string|number|string[]|number[]) — Primary key(s).valueObj(FreeObj|FreeObj[]) — One or many objects with fields.onlyIfNew(boolean) — Iftrue, skips update on conflict.
- Uses
ON CONFLICT DO UPDATE(orDO NOTHING) - Detects auto-generated or primary key columns and returns them.
- Ensures all entries in
valueObj[]are uniform in keys.
Promise<FreeObj|FreeObj[]|null>— Inserted or updated row(s), ornull.
Fetches a single record using its primary key (and optional subId if defined).
id(string|number) — Primary key.subId?(string|number) — Optional composite key.
Promise<FreeObj|null>— Record if found, elsenull.
Deletes records matching a complex filter condition.
filter(QueryGroup) — Object representing WHERE conditions.
Promise<number>— Number of rows deleted.
Deletes a record by primary key (and optional subId).
id(string|number)subId?(string|number)
Promise<number>— Number of rows deleted.
Returns up to count items. Optionally filter by ID and control selected columns.
count(number)filterId?(string|number|null)selectValue?(SelectQuery) — Default:"*"
Promise<FreeObj[]>
Fetches all records in the table or filtered by a specific ID.
filterId?(string|number|null)selectValue?(SelectQuery) — Default:"*"
Promise<FreeObj[]>
Runs a paginated query and returns total items and total pages.
query(string)params(any[])perPage(number)page(number)queryName?(string) — Optional label for debugging.
Promise<{ items: FreeObj[], totalPages: number, totalItems: number }>— Paginated result set.
Parses a flexible query condition group into a SQL WHERE clause.
- Logical groups (
AND,OR) - Custom condition handlers (
this.#conditions) - Nested or flat filters
- Placeholders via
pCache
pCache(Pcache) — Placeholder manager:{ index: number, values: any[] }group(QueryGroup) — Group or single condition object.
string— SQL WHERE clause string (withoutWHERE)
const p = { index: 1, values: [] };
const clause = this.parseWhere(p, {
group: 'OR',
conditions: [
{ column: 'status', value: 'active' },
{ column: 'type', value: 'admin', operator: '=' },
],
});
// clause: "(status = $1) OR (type = $2)"
// p.values: ['active', 'admin']Returns a default LEFT JOIN clause if configured in #settings.
string— SQL JOIN clause or empty string.
Internal object with predefined JOIN types:
inner:'INNER JOIN'left:'LEFT JOIN'right:'RIGHT JOIN'full:'FULL JOIN'cross:'CROSS JOIN'join:'JOIN'(shorthand for inner)
Parses JOIN configuration into one or more SQL JOIN clauses.
join(JoinObj | JoinObj[] | string | null)
string— JOIN clauses string.
Finds the first matching result and returns its position, page, and optionally its data.
Query only: findQuery(searchData)
searchData(object)q: QueryGroup or object conditiontagCriteria: Single or multiple tag filterstagCriteriaOps: Logical ops between tag filtersperPage: Required, results per pageselect: Columns to select ('*'by default, ornullto skip data)order: CustomORDER BYjoin: JOIN configs (string, object or array)
Promise<FindResult|null>
Performs a complete filtered query with optional pagination, joins, and tags.
Query only: searchQuery(searchData)
-
searchData(object)q: QueryGroup or flat condition objecttagsQ: Tags filter (array or object)tagsOpsQ: Logical ops between tags (['AND', 'OR'])select: Select clause ('*', array, object ornull)perPage: Items per pagepage: Page number (default: 1)order: Custom ORDER BY clausejoin: JOINs configurationlimit: Max number of rows (ignored ifperPageis used)
Promise<FreeObj[] | PaginationResult>
await table.search({
q: {
group: 'AND',
conditions: [
{ column: 'status', value: 'active' },
{
group: 'OR',
conditions: [
{ column: 'type', value: 'admin' },
{ column: 'type', value: 'mod' }
]
}
]
},
tagsQ: [{ column: 'tags', value: 'featured' }],
select: '*',
perPage: 15,
page: 1,
order: 'created_at DESC',
join: [
{ type: 'left', table: 'profiles', compare: 't.profile_id = j1.id' },
{ type: 'left', table: 'roles', compare: 'j1.role_id = j2.id' }
]
});