puddysql
Version:
🍮 Powerful SQL toolkit for Node.js, built with flexibility and structure in mind. Easily manage SQLite3/PostgreSQL, advanced queries, smart tag systems, and full JSON-friendly filters.
384 lines (383 loc) • 16.6 kB
text/typescript
export default PuddySqlTags;
export type SpecialQuery = {
title: string;
parser?: (arg0: string) => string;
};
export type Pcache = import("./PuddySqlQuery.mjs").Pcache;
export type TagCriteria = import("./PuddySqlQuery.mjs").TagCriteria;
/**
* Represents a key-value pair extracted from a special chunk format.
*/
export type SpecialFromChunks = {
/**
* - The key or identifier extracted from the chunk.
*/
key: string;
/**
* - The associated value linked to the key.
*/
value: string;
};
/**
* A chunk can be a single string or an array of strings grouped as OR conditions.
*/
export type Chunks = Array<string | string[]>;
/**
* Result of parsing a string expression into a column and list of included values.
*/
export type ParseStringResult = {
/**
* - The SQL column to which the values apply.
*/
column: string;
/**
* - List of values or grouped OR conditions to be included in the query.
*/
include: Chunks;
};
/**
* Represents a mapping entry for a tag input definition.
*
* Each tag input is defined by the name of the list where it belongs (`list`)
* and the key (`valueKey`) used to extract the relevant value from tag objects.
* This structure is used to determine how tags are parsed and grouped during search.
*/
export type TagInput = {
/**
* - The list name where the tag will be added.
*/
list: string;
/**
* - The key for the value associated with the tag input.
*/
valueKey: string;
};
/** @typedef {{ title: string; parser?: function(string): string }} SpecialQuery */
/** @typedef {import('./PuddySqlQuery.mjs').Pcache} Pcache */
/** @typedef {import('./PuddySqlQuery.mjs').TagCriteria} TagCriteria */
/**
* Represents a key-value pair extracted from a special chunk format.
*
* @typedef {Object} SpecialFromChunks
* @property {string} key - The key or identifier extracted from the chunk.
* @property {string} value - The associated value linked to the key.
*/
/**
* Represents a collection of string chunks used in parsing or filtering.
*
* @typedef {Array<string | string[]>} Chunks
*
* A chunk can be a single string or an array of strings grouped as OR conditions.
*/
/**
* Result of parsing a string expression into a column and list of included values.
*
* @typedef {Object} ParseStringResult
* @property {string} column - The SQL column to which the values apply.
* @property {Chunks} include - List of values or grouped OR conditions to be included in the query.
*/
/**
* Represents a mapping entry for a tag input definition.
*
* Each tag input is defined by the name of the list where it belongs (`list`)
* and the key (`valueKey`) used to extract the relevant value from tag objects.
* This structure is used to determine how tags are parsed and grouped during search.
*
* @typedef {Object} TagInput
* @property {string} list - The list name where the tag will be added.
* @property {string} valueKey - The key for the value associated with the tag input.
*/
/**
* @class PuddySqlTags
* @description A powerful utility class for building advanced SQL WHERE clauses with support for tag-based filtering,
* custom boolean logic, wildcard parsing, and special query handlers.
*
* PuddySqlTags provides a structured way to interpret and transform flexible user search input into robust SQL conditions,
* including support for parentheses grouping, AND/OR logic, special colon-based filters, and customizable weight systems
* using symbolic operators. Designed with modularity and extensibility in mind, it also prevents unwanted repetitions and
* allows precise control over column names, aliases, and JSON handling through `json_each`.
*
* The class includes:
* - Methods to parse complex string-based filters (`parseString`, `safeParseString`)
* - Smart logic to detect and manage tag groups, boolean relationships, and custom operators
* - Support for boost values, exclusions, and other modifiers via symbols (e.g., `-`, `!`)
* - An internal engine to dynamically build `EXISTS`-based SQL conditions compatible with JSON arrays
* - Integration-ready output for SQLite3, Postgre or similar relational databases
*
* ----------
* 💖 Special Thanks 💖
* Deep gratitude to the Derpibooru project for the inspiration, structure, and creativity
* that influenced this tool. A tiny heartfelt thank you to **Nighty**. :3
*/
declare class PuddySqlTags {
/**
* Creates an instance of the PuddySqlTags class.
* @param {string} defaultColumn - The default column name to use in queries (default is 'tags').
*/
constructor(defaultColumn?: string);
/**
* Adds a new tag input mapping to the #tagInputs property.
*
* This method allows dynamic addition of new tag input mappings by providing a `key`,
* `list`, and `valueKey`. It validates the types of `list` and `valueKey`, and
* prevents adding a tag with the list name "include" and "column" as it is restricted.
*
* @param {string} key - The key (symbol) to associate with the tag input.
* @param {string} list - The list name where the tag will be added.
* @param {string} valueKey - The key for the value associated with the tag input.
* @throws {Error} Throws an error if `list` or `valueKey` are not strings,
* or if the `list` name is "include" or "column".
*/
addTagInput(key: string, list: string, valueKey: string): void;
/**
* Checks if a tag input mapping exists for the given key.
*
* @param {string} key - The tag key to check.
* @returns {boolean} `true` if the key exists in `#tagInputs`, otherwise `false`.
* @throws {TypeError} If `key` is not a string.
*/
hasTagInput(key: string): boolean;
/**
* Removes a tag input mapping from the `#tagInputs` object.
*
* If the key exists, it will be deleted.
* Otherwise, an error is thrown.
*
* @param {string} key - The key of the tag input to remove.
* @throws {Error} If the specified key does not exist in `#tagInputs`.
* @throws {TypeError} If `key` is not a string.
*/
removeTagInput(key: string): void;
/**
* Gets the title of the first item for a given tag input key.
*
* @param {string} key - The key of the tag input to retrieve.
* @returns {TagInput} The title of the first item.
* @throws {TypeError} If `key` is not a string.
* @throws {Error} If the key does not exist or has no valid title.
*/
getTagInput(key: string): TagInput;
/**
* Gets an array of all tag input titles.
*
* @returns {TagInput[]} An array containing the tag inputs.
*/
getAllTagInput(): TagInput[];
/**
* Sets whether repeated tags are allowed.
* Internally sets `this.#noRepeat` to the inverse of the boolean value provided.
* If value is not a boolean, resets `noRepeat` to null.
*
* @param {boolean} value - True to allow repeated tags, false to prevent them.
*/
setCanRepeat(value: boolean): void;
/**
* Sets the wildcard symbol used in the search expression.
* Only updates if the value is a string.
*
* @param {'wildcardA'|'wildcardB'} where - Which wildcard to set.
* @param {string|null} value - The wildcard symbol (e.g. '*', '%').
*/
setWildcard(where: "wildcardA" | "wildcardB", value: string | null): void;
/**
* Adds a new custom special query to the internal list.
* Special queries can affect how tags are interpreted or matched.
*
* @param {Object} config - The special query object to be added.
* @param {string} config.title - The unique title identifier of the special query.
*/
addSpecialQuery(config: {
title: string;
}): void;
/**
* Checks if a special query with the given title exists.
*
* @param {string} title - The title of the special query to check.
* @returns {boolean} `true` if a special query with the title exists, otherwise `false`.
* @throws {TypeError} If `title` is not a string.
*/
hasSpecialQuery(title: string): boolean;
/**
* Removes a special query identified by its title.
*
* If a query with the specified title exists, it is removed.
* Otherwise, an error is thrown.
*
* @param {string} title - The title of the special query to remove.
* @throws {TypeError} If `title` is not a string.
* @throws {Error} If no special query with the given title exists.
*/
removeSpecialQuery(title: string): void;
/**
* Retrieves the title of a special query by its title key.
*
* This method checks if a special query with the given title exists
* and returns its `parser` value. If not found, it throws an error.
*
* @param {string} title - The title of the special query to retrieve.
* @returns {(function(string): string) | null} The function of the found special query.
* @throws {TypeError} If `title` is not a string.
* @throws {Error} If no special query with the given title exists.
*/
getSpecialQuery(title: string): ((arg0: string) => string) | null;
/**
* Returns a list of all special query titles.
*
* This is a shallow extraction of the `title` field from every item
* in the internal `#specialQueries` array.
*
* @returns {string[]} An array of all special query titles.
*/
getAllSpecialQuery(): string[];
/**
* Sets the name of the default SQL column used when building tag-based conditions.
*
* @param {string} value - Column name to be used as default (e.g. 'tags').
*/
setColumnName(value: string): void;
/**
* Gets the current default SQL column name used for tag conditions.
*
* @returns {string} The name of the default column.
*/
getColumnName(): string;
/**
* Sets a limit on the number of items parsed from the search string.
* Used to avoid overloading the engine with too many conditions.
*
* @param {number} value - Maximum number of items to parse (use -1 for no limit).
*/
setParseLimit(value: number): void;
/**
* Gets the current limit on how many tags are parsed from a search string.
*
* @returns {number} The current parse limit.
*/
getParseLimit(): number;
/**
* Enables or disables the use of `json_each()` in SQL statements.
* This affects how JSON-based columns are traversed.
*
* @param {boolean} value - Whether to use `json_each()` in tag conditions.
*/
setUseJsonEach(value: boolean): void;
/**
* Sets the external table name name used in `EXISTS` subqueries, typically referencing `value`.
*
* @param {string} value - The alias to use in SQL subqueries (e.g. 'value').
*/
setTableName(value: string): void;
/**
* Sets the raw SQL string used for the `json_each()` expression.
* This is used for custom SQL generation.
*
* @param {string} value - The SQL snippet (e.g. "json_each(tags)").
*/
setJsonEach(value: string): void;
/**
* Sets whether the engine is running in PostgreSQL mode.
*
* @param {boolean} value - Must be a boolean true/false.
* @throws {TypeError} If the value is not a boolean.
*/
setIsPgMode(value: boolean): void;
/**
* Gets whether the engine is currently in PostgreSQL mode.
*
* @returns {boolean}
*/
getIsPgMode(): boolean;
/**
* Builds an SQL WHERE clause from a structured tag group definition.
*
* This method supports both direct equality and wildcard matching using custom
* wildcard symbols (`wildcardA`, `wildcardB`). Tags can be negated with a leading `!`.
* It generates nested `EXISTS` or `NOT EXISTS` subqueries depending on the `useJsonEach` flag.
*
* The method returns a string representing the SQL WHERE clause, and updates `pCache.values`
* with the filtered values in proper order for parameterized queries.
*
* @param {Pcache} [pCache={ index: 1, values: [] }] - Placeholder cache object.
* @param {TagCriteria} [group={}] - Tag group definition to build the clause from.
*
* @returns {string} The generated SQL condition string (e.g., `(EXISTS (...)) AND (NOT EXISTS (...))`).
*/
parseWhere(group?: TagCriteria, pCache?: Pcache): string;
/**
* Parses a search input string into structured query components.
*
* This method tokenizes the input string based on grouping (parentheses), logical
* operators (`AND`, `OR`), and quoting (single or double). It supports optional
* repetition control (`noRepeat`) and a configurable tag limit (`parseLimit`).
*
* The output is normalized into an `include` list of tags or OR-groups (arrays),
* as well as dynamic sets of extracted metadata like `boosts`, `specials`, etc.
*
* This parser supports expressions like:
* `applejack^2, "rainbow dash", (solo OR duo), pudding AND source:ponybooru`
*
* @param {string} input - The raw input string provided by the user.
* @param {boolean} [strictMode=false] - Enables strict validation checks.
* @param {Object} [strictConfig={}] - Optional validation rules for strict mode:
* @param {boolean} [strictConfig.emptyInput=true] - Throw if input is empty after trimming.
* @param {boolean} [strictConfig.parseLimit=true] - Enforce the parse limit (`this.parseLimit`).
* @param {boolean} [strictConfig.openParens=true] - Require balanced parentheses.
* @param {boolean} [strictConfig.quoteChar=true] - Require closing quotes if one is opened.
*
* @returns {ParseStringResult} An object containing:
* - `column`: The column name from `this.getColumnName()`.
* - `include`: Array of tags and OR-groups to include in the query.
* - Additional properties (e.g., `boosts`, `specials`) depending on matches in `#tagInputs` or `#specialQueries`.
*
* Example return:
* ```js
* {
* column: 'tags',
* include: ['applejack', ['solo', 'duo'], 'pudding'],
* boosts: [{ term: 'applejack', boost: 2 }],
* specials: [{ key: 'source', value: 'ponybooru' }]
* }
* ```
*/
parseString(input: string, strictMode?: boolean, strictConfig?: {
emptyInput?: boolean | undefined;
parseLimit?: boolean | undefined;
openParens?: boolean | undefined;
quoteChar?: boolean | undefined;
}): ParseStringResult;
/**
* Sanitizes and normalizes a raw input string before parsing.
*
* This method prepares user input for parsing by replacing common symbolic
* boolean operators (`&&`, `||`, `-`, `NOT`) with their textual equivalents
* (`AND`, `OR`, `!`). It also trims whitespace and replaces commas with `AND`
* to enforce consistent logical separation.
*
* This is useful when parsing user input that might come from flexible or
* user-friendly interfaces where symbols are more commonly used than
* structured boolean expressions.
*
* @param {string} input - The raw input string provided by the user.
* @param {boolean} [strictMode=false] - Enables strict validation checks.
* @param {Object} [strictConfig={}] - Optional validation rules for strict mode:
* @param {boolean} [strictConfig.emptyInput=true] - Throw if input is empty after trimming.
* @param {boolean} [strictConfig.parseLimit=true] - Enforce the parse limit (`this.parseLimit`).
* @param {boolean} [strictConfig.openParens=true] - Require balanced parentheses.
* @param {boolean} [strictConfig.quoteChar=true] - Require closing quotes if one is opened.
*
* @returns {ParseStringResult} A structured result object returned by `parseString()`,
* containing keys like `column`, `include`, `specials`, `boosts`, etc., depending on
* the tags and expressions detected.
*
* @example
* safeParseString("applejack, -source, rarity || twilight")
* // → equivalent to: parseString("applejack AND !source AND rarity OR twilight")
*/
safeParseString(input: string, strictMode?: boolean, strictConfig?: {
emptyInput?: boolean | undefined;
parseLimit?: boolean | undefined;
openParens?: boolean | undefined;
quoteChar?: boolean | undefined;
}): ParseStringResult;
#private;
}