@tstdl/base 0.93.9 → 0.93.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,13 +1,9 @@
1
- /**
2
- * @module
3
- * Converts a generic query object structure into a Drizzle ORM SQL condition.
4
- * Supports logical operators ($and, $or, $nor) and various comparison operators
5
- * ($eq, $neq, $in, $nin, $lt, $lte, $gt, $gte, $regex).
6
- */
7
1
  import { and, eq, gt, gte, inArray, isNotNull, isNull, isSQLWrapper, lt, lte, ne, not, notInArray, or, SQL, sql } from 'drizzle-orm';
2
+ import { match } from 'ts-pattern';
8
3
  import { NotSupportedError } from '../../errors/not-supported.error.js';
9
4
  import { hasOwnProperty, objectEntries } from '../../utils/object/object.js';
10
5
  import { assertDefinedPass, isDefined, isPrimitive, isRegExp, isString, isUndefined } from '../../utils/type-guards.js';
6
+ import { isSimilar, phraseToTsQuery, plainToTsQuery, setweight, toTsQuery, toTsVector, websearchToTsQuery } from '../sqls.js';
11
7
  const sqlTrue = sql `true`;
12
8
  /**
13
9
  * Converts a query object into a Drizzle SQL condition.
@@ -60,10 +56,27 @@ export function convertQuery(query, table, columnDefinitionsMap) {
60
56
  }
61
57
  break;
62
58
  }
59
+ case '$fts': {
60
+ const ftsQuery = value;
61
+ const method = ftsQuery.method ?? 'vector';
62
+ const sqlValue = match(method)
63
+ .with('vector', () => {
64
+ const tsquery = getTsQuery(ftsQuery.query, ftsQuery.language ?? 'simple', ftsQuery.parser ?? 'raw');
65
+ const tsvector = getTsVector(ftsQuery.fields, ftsQuery.language ?? 'simple', table, columnDefinitionsMap, ftsQuery.weights);
66
+ return sql `${tsvector} @@ ${tsquery}`;
67
+ })
68
+ .with('similarity', () => {
69
+ const searchExpression = getSimilaritySearchExpression(ftsQuery.fields, table, columnDefinitionsMap);
70
+ return isSimilar(searchExpression, ftsQuery.query);
71
+ })
72
+ .exhaustive();
73
+ conditions.push(sqlValue);
74
+ break;
75
+ }
63
76
  default: {
64
77
  const columnDef = assertDefinedPass(columnDefinitionsMap.get(property), `Could not map property ${property} to column.`);
65
78
  const column = table[columnDef.name];
66
- const condition = getCondition(property, value, column);
79
+ const condition = getCondition(property, value, column, table, columnDefinitionsMap);
67
80
  conditions.push(condition);
68
81
  break;
69
82
  }
@@ -79,10 +92,8 @@ export function convertQuery(query, table, columnDefinitionsMap) {
79
92
  * @param value The value or comparison object for the property.
80
93
  * @param column The Drizzle column object.
81
94
  * @returns A Drizzle SQL condition.
82
- * @throws {NotSupportedError} If an unsupported operator like $exists, $text, $geoShape, or $geoDistance is used.
83
- * @throws {Error} If the value structure is not a recognized comparison operator.
84
95
  */
85
- function getCondition(property, value, column) {
96
+ function getCondition(property, value, column, table, columnDefinitionsMap) {
86
97
  const isPrimitiveValue = isPrimitive(value);
87
98
  if (isPrimitiveValue || hasOwnProperty(value, '$eq')) {
88
99
  const queryValue = isPrimitiveValue ? value : value.$eq;
@@ -92,7 +103,7 @@ function getCondition(property, value, column) {
92
103
  return eq(column, queryValue);
93
104
  }
94
105
  if (hasOwnProperty(value, '$and')) {
95
- const innerQueries = value.$and.map((query) => getCondition(property, query, column)); // eslint-disable-line @typescript-eslint/no-unsafe-argument
106
+ const innerQueries = value.$and.map((query) => getCondition(property, query, column, table, columnDefinitionsMap)); // eslint-disable-line @typescript-eslint/no-unsafe-argument
96
107
  const andQuery = and(...innerQueries);
97
108
  if (isUndefined(andQuery)) {
98
109
  throw new Error(`No valid conditions in $and for property "${property}".`);
@@ -100,7 +111,7 @@ function getCondition(property, value, column) {
100
111
  return andQuery;
101
112
  }
102
113
  if (hasOwnProperty(value, '$or')) {
103
- const innerQueries = value.$or.map((query) => getCondition(property, query, column)); // eslint-disable-line @typescript-eslint/no-unsafe-argument
114
+ const innerQueries = value.$or.map((query) => getCondition(property, query, column, table, columnDefinitionsMap)); // eslint-disable-line @typescript-eslint/no-unsafe-argument
104
115
  const orQuery = or(...innerQueries);
105
116
  if (isUndefined(orQuery)) {
106
117
  throw new Error(`No valid conditions in $or for property "${property}".`);
@@ -108,7 +119,7 @@ function getCondition(property, value, column) {
108
119
  return orQuery;
109
120
  }
110
121
  if (hasOwnProperty(value, '$not')) {
111
- const innerQuery = getCondition(property, value.$not, column); // eslint-disable-line @typescript-eslint/no-unsafe-argument
122
+ const innerQuery = getCondition(property, value.$not, column, table, columnDefinitionsMap); // eslint-disable-line @typescript-eslint/no-unsafe-argument
112
123
  return not(innerQuery);
113
124
  }
114
125
  if (hasOwnProperty(value, '$neq')) {
@@ -156,8 +167,17 @@ function getCondition(property, value, column) {
156
167
  const operator = (regexp.flags?.includes('i') ?? false) ? sql.raw('~*') : sql.raw('~');
157
168
  return sql `${column} ${operator} ${regexp.value}`;
158
169
  }
159
- if (hasOwnProperty(value, '$text')) {
160
- throw new NotSupportedError('$text is not supported.');
170
+ if (hasOwnProperty(value, '$fts')) {
171
+ const queryValue = value.$fts;
172
+ const { query, method = 'vector', language = 'simple', parser = 'plain' } = isString(queryValue)
173
+ ? { query: queryValue }
174
+ : queryValue;
175
+ if (method == 'similarity') {
176
+ return isSimilar(column, query);
177
+ }
178
+ const tsquery = getTsQuery(query, language, parser);
179
+ const tsvector = toTsVector(language, column);
180
+ return sql `${tsvector} @@ ${tsquery}`;
161
181
  }
162
182
  if (hasOwnProperty(value, '$geoShape')) {
163
183
  throw new NotSupportedError('$geoShape is not supported.');
@@ -167,3 +187,37 @@ function getCondition(property, value, column) {
167
187
  }
168
188
  throw new Error(`Unsupported query type "${property}".`);
169
189
  }
190
+ export function getTsQuery(text, language, parser) {
191
+ switch (parser) {
192
+ case 'raw':
193
+ return toTsQuery(language, text);
194
+ case 'plain':
195
+ return plainToTsQuery(language, text);
196
+ case 'phrase':
197
+ return phraseToTsQuery(language, text);
198
+ case 'websearch':
199
+ return websearchToTsQuery(language, text);
200
+ default:
201
+ throw new NotSupportedError(`Unsupported text search parser: ${parser}`);
202
+ }
203
+ }
204
+ export function getTsVector(fields, language, table, columnDefinitionsMap, weights) {
205
+ const tsvector = sql.join(fields.map((field) => {
206
+ const columnDef = assertDefinedPass(columnDefinitionsMap.get(field), `Could not map property ${field} to column.`);
207
+ const column = table[columnDef.name];
208
+ const vector = toTsVector(language, column);
209
+ const weight = weights?.[field];
210
+ if (isDefined(weight)) {
211
+ return setweight(vector, weight);
212
+ }
213
+ return vector;
214
+ }), sql ` || `);
215
+ return tsvector;
216
+ }
217
+ export function getSimilaritySearchExpression(fields, table, columnDefinitionsMap) {
218
+ const columns = fields.map((field) => {
219
+ const columnDef = assertDefinedPass(columnDefinitionsMap.get(field), `Could not map property ${field} to column.`);
220
+ return table[columnDef.name];
221
+ });
222
+ return sql.join(columns, sql ` || ' ' || `);
223
+ }
@@ -1,10 +1,10 @@
1
1
  import { SQL } from 'drizzle-orm';
2
2
  import type { PgColumn, PgInsertValue, PgUpdateSetSource } from 'drizzle-orm/pg-core';
3
- import { afterResolve, type Resolvable, resolveArgumentType } from '../../injector/interfaces.js';
3
+ import { afterResolve, resolveArgumentType, type Resolvable } from '../../injector/interfaces.js';
4
4
  import type { DeepPartial, OneOrMany, Paths, Type, UntaggedDeep } from '../../types/index.js';
5
5
  import { Entity, type EntityMetadataAttributes, type EntityType, type EntityWithoutMetadata } from '../entity.js';
6
- import type { Query } from '../query.js';
7
- import type { EntityMetadataUpdate, EntityUpdate, LoadManyOptions, LoadOptions, NewEntity, Order, TargetColumnPaths } from '../repository.types.js';
6
+ import type { FullTextSearchQuery, Query } from '../query.js';
7
+ import type { EntityMetadataUpdate, EntityUpdate, LoadManyOptions, LoadOptions, NewEntity, Order, SearchOptions, SearchResult, TargetColumnPaths } from '../repository.types.js';
8
8
  import type { Database } from './database.js';
9
9
  import type { PgTransaction } from './transaction.js';
10
10
  import { Transactional } from './transactional.js';
@@ -40,6 +40,14 @@ export declare class EntityRepository<T extends Entity | EntityWithoutMetadata =
40
40
  [afterResolve](): void;
41
41
  private expirationLoop;
42
42
  protected getTransactionalContextData(): EntityRepositoryContext;
43
+ /**
44
+ * Performs a full-text search and returns entities ranked by relevance.
45
+ * This method is a convenience wrapper around `loadManyByQuery` with the `$fts` operator.
46
+ * @param query The search query using the `$fts` operator.
47
+ * @param options Search options including ranking, and highlighting configuration.
48
+ * @returns A promise that resolves to an array of search results, including the entity, score, and optional highlight.
49
+ */
50
+ search(query: FullTextSearchQuery<T>['$fts'], options?: SearchOptions<T>): Promise<SearchResult<T>[]>;
43
51
  /**
44
52
  * Loads a single entity by its ID.
45
53
  * Throws `NotFoundError` if the entity is not found.
@@ -416,7 +424,7 @@ export declare class EntityRepository<T extends Entity | EntityWithoutMetadata =
416
424
  identity: undefined;
417
425
  generated: undefined;
418
426
  }, {}, {}>;
419
- }, "partial", globalThis.Record<string, "not-null">, false, "where" | "limit", {
427
+ }, "partial", globalThis.Record<string, "not-null">, false, "limit" | "where", {
420
428
  id: string;
421
429
  }[], {
422
430
  id: PgColumn<{
@@ -436,7 +444,7 @@ export declare class EntityRepository<T extends Entity | EntityWithoutMetadata =
436
444
  identity: undefined;
437
445
  generated: undefined;
438
446
  }, {}, {}>;
439
- }>, "where" | "limit">;
447
+ }>, "limit" | "where">;
440
448
  protected getAttributesUpdate(attributes: SQL | EntityMetadataAttributes | undefined): SQL<unknown> | undefined;
441
449
  protected _mapManyToEntity(columns: InferSelect[], transformContext: TransformContext): Promise<T[]>;
442
450
  protected _mapToEntity(columns: InferSelect, transformContext: TransformContext): Promise<T>;
@@ -5,9 +5,10 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
5
5
  return c > 3 && r && Object.defineProperty(target, key, r), r;
6
6
  };
7
7
  import { and, asc, count, desc, eq, inArray, isNull, isSQLWrapper, lte, or, SQL, sql } from 'drizzle-orm';
8
- import { match } from 'ts-pattern';
8
+ import { match, P } from 'ts-pattern';
9
9
  import { CancellationSignal } from '../../cancellation/token.js';
10
10
  import { NotFoundError } from '../../errors/not-found.error.js';
11
+ import { NotSupportedError } from '../../errors/not-supported.error.js';
11
12
  import { Singleton } from '../../injector/decorators.js';
12
13
  import { inject, injectArgument } from '../../injector/inject.js';
13
14
  import { afterResolve, resolveArgumentType } from '../../injector/interfaces.js';
@@ -19,15 +20,17 @@ import { importSymmetricKey } from '../../utils/cryptography.js';
19
20
  import { fromDeepObjectEntries, fromEntries, objectEntries } from '../../utils/object/object.js';
20
21
  import { cancelableTimeout } from '../../utils/timing.js';
21
22
  import { tryIgnoreAsync } from '../../utils/try-ignore.js';
22
- import { assertDefined, assertDefinedPass, isArray, isDefined, isString, isUndefined } from '../../utils/type-guards.js';
23
+ import { assertDefined, assertDefinedPass, isArray, isBoolean, isDefined, isInstanceOf, isString, isUndefined } from '../../utils/type-guards.js';
23
24
  import { typeExtends } from '../../utils/type/index.js';
24
25
  import { millisecondsPerSecond } from '../../utils/units.js';
25
26
  import { Entity } from '../entity.js';
26
- import { TRANSACTION_TIMESTAMP } from '../sqls.js';
27
+ import { isSimilar, similarity, TRANSACTION_TIMESTAMP, tsHeadline, tsRankCd } from '../sqls.js';
27
28
  import { getColumnDefinitions, getColumnDefinitionsMap, getDrizzleTableFromType } from './drizzle/schema-converter.js';
28
- import { convertQuery } from './query-converter.js';
29
+ import { convertQuery, getSimilaritySearchExpression, getTsQuery, getTsVector } from './query-converter.js';
29
30
  import { ENCRYPTION_SECRET } from './tokens.js';
30
31
  import { getTransactionalContextData, injectTransactional, injectTransactionalAsync, isInTransactionalContext, Transactional } from './transactional.js';
32
+ const searchScoreColumn = '__tsl_score';
33
+ const searchHighlightColumn = '__tsl_highlight';
31
34
  export const repositoryType = Symbol('repositoryType');
32
35
  /**
33
36
  * Configuration class for EntityRepository.
@@ -92,6 +95,105 @@ let EntityRepository = class EntityRepository extends Transactional {
92
95
  };
93
96
  return context;
94
97
  }
98
+ /**
99
+ * Performs a full-text search and returns entities ranked by relevance.
100
+ * This method is a convenience wrapper around `loadManyByQuery` with the `$fts` operator.
101
+ * @param query The search query using the `$fts` operator.
102
+ * @param options Search options including ranking, and highlighting configuration.
103
+ * @returns A promise that resolves to an array of search results, including the entity, score, and optional highlight.
104
+ */
105
+ async search(query, options) {
106
+ const { method = 'vector' } = query;
107
+ let whereClause;
108
+ let rankExpression;
109
+ let highlightExpression;
110
+ const rankEnabled = options?.rank ?? true;
111
+ match(method)
112
+ .with('similarity', () => {
113
+ if (isDefined(query.weights) || isDefined(query.parser) || isDefined(options?.highlight)) {
114
+ throw new NotSupportedError('`weights`, `parser`, and `highlight` are not applicable to similarity search.');
115
+ }
116
+ const searchExpression = getSimilaritySearchExpression(query.fields, this.table, this.#columnDefinitionsMap);
117
+ whereClause = isSimilar(searchExpression, query.query);
118
+ if (rankEnabled) {
119
+ rankExpression = isInstanceOf(options?.rank, SQL)
120
+ ? options.rank
121
+ : similarity(searchExpression, query.query);
122
+ }
123
+ })
124
+ .with('vector', () => {
125
+ const { language = 'simple' } = query;
126
+ const languageSql = isString(language) ? language : sql `${language}`;
127
+ const tsquery = getTsQuery(query.query, languageSql, query.parser ?? 'raw');
128
+ const tsvector = getTsVector(query.fields, languageSql, this.table, this.#columnDefinitionsMap, query.weights);
129
+ whereClause = sql `${tsvector} @@ ${tsquery}`;
130
+ if (rankEnabled) {
131
+ if (isInstanceOf(options?.rank, SQL)) {
132
+ rankExpression = options.rank;
133
+ }
134
+ else {
135
+ const rankOptions = isBoolean(options?.rank) ? undefined : options?.rank;
136
+ rankExpression = tsRankCd(tsvector, tsquery, rankOptions);
137
+ }
138
+ }
139
+ if (isDefined(options?.highlight)) {
140
+ const { source, ...headlineOptions } = (isString(options.highlight) || isInstanceOf(options.highlight, SQL))
141
+ ? { source: options.highlight }
142
+ : options.highlight;
143
+ const document = match(source)
144
+ .with(P.instanceOf(SQL), (s) => s)
145
+ .otherwise((paths) => {
146
+ const columns = this.getColumns(paths);
147
+ return sql.join(columns, sql ` || ' ' || `);
148
+ });
149
+ highlightExpression = tsHeadline(languageSql, document, tsquery, headlineOptions);
150
+ }
151
+ })
152
+ .exhaustive();
153
+ if (isDefined(options?.filter)) {
154
+ const filter = this.convertQuery(options.filter);
155
+ whereClause = and(whereClause, filter);
156
+ }
157
+ const selection = fromEntries(this.#columnDefinitions.map((column) => [column.name, this.getColumn(column)]));
158
+ if (isDefined(rankExpression)) {
159
+ selection[searchScoreColumn] = rankExpression.as(searchScoreColumn);
160
+ }
161
+ if (isDefined(highlightExpression)) {
162
+ selection[searchHighlightColumn] = highlightExpression.as(searchHighlightColumn);
163
+ }
164
+ let dbQuery = match(options?.distinct ?? false)
165
+ .with(false, () => this.session.select(selection))
166
+ .with(true, () => this.session.selectDistinct(selection))
167
+ .otherwise((targets) => {
168
+ const ons = targets.map((target) => isString(target) ? this.getColumn(target) : target);
169
+ return this.session.selectDistinctOn(ons, selection);
170
+ })
171
+ .from(this.#table)
172
+ .where(whereClause)
173
+ .$dynamic();
174
+ if (isDefined(options?.offset)) {
175
+ dbQuery = dbQuery.offset(options.offset);
176
+ }
177
+ if (isDefined(options?.limit)) {
178
+ dbQuery = dbQuery.limit(options.limit);
179
+ }
180
+ if (rankEnabled) {
181
+ const orderByExpressions = [];
182
+ if (isArray(options?.distinct)) {
183
+ const ons = options.distinct.map((target) => isString(target) ? this.getColumn(target) : target.getSQL());
184
+ orderByExpressions.push(...ons);
185
+ }
186
+ orderByExpressions.push(desc(sql.identifier(searchScoreColumn)));
187
+ dbQuery = dbQuery.orderBy(...orderByExpressions);
188
+ }
189
+ const transformContext = await this.getTransformContext();
190
+ const rows = await dbQuery;
191
+ return await toArrayAsync(mapAsync(rows, async ({ [searchScoreColumn]: score, [searchHighlightColumn]: highlight, ...row }) => ({
192
+ entity: await this._mapToEntity(row, transformContext),
193
+ score: score,
194
+ highlight: highlight,
195
+ })));
196
+ }
95
197
  /**
96
198
  * Loads a single entity by its ID.
97
199
  * Throws `NotFoundError` if the entity is not found.
package/orm/sqls.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { type AnyColumn, type Column, type SQL } from 'drizzle-orm';
1
+ import { type AnyColumn, type Column, type SQL, type SQLChunk } from 'drizzle-orm';
2
2
  import type { GetSelectTableSelection, SelectResultField, TableLike } from 'drizzle-orm/query-builders/select.types';
3
3
  import type { Uuid } from './types.js';
4
4
  /** Drizzle SQL helper for getting the current transaction's timestamp. Returns a Date object. */
@@ -7,18 +7,60 @@ export declare const TRANSACTION_TIMESTAMP: SQL<Date>;
7
7
  export declare const RANDOM_UUID_V4: SQL<Uuid>;
8
8
  /** Represents valid units for PostgreSQL interval values. */
9
9
  export type IntervalUnit = 'millennium' | 'millenniums' | 'millennia' | 'century' | 'centuries' | 'decade' | 'decades' | 'year' | 'years' | 'day' | 'days' | 'hour' | 'hours' | 'minute' | 'minutes' | 'second' | 'seconds' | 'millisecond' | 'milliseconds' | 'microsecond' | 'microseconds';
10
+ export type TsHeadlineOptions = {
11
+ /**
12
+ * The longest headline to output.
13
+ * @default 35
14
+ */
15
+ maxWords?: number;
16
+ /**
17
+ * The shortest headline to output.
18
+ * @default 15
19
+ */
20
+ minWords?: number;
21
+ /**
22
+ * Words of this length or less will be dropped at the start and end of a headline, unless they are query terms.
23
+ * @default 3
24
+ */
25
+ shortWord?: number;
26
+ /**
27
+ * If true the whole document will be used as the headline, ignoring the preceding three parameters.
28
+ * @default false
29
+ */
30
+ highlightAll?: boolean;
31
+ /**
32
+ * Maximum number of text fragments to display. Default is 0, which selects a non-fragment-based headline generation method. A value greater than zero selects fragment-based headline generation.
33
+ * @default 0
34
+ */
35
+ maxFragments?: number;
36
+ /**
37
+ * The strings with which to delimit query words appearing in the document, to distinguish them from other excerpted words.
38
+ * @default '<b>'
39
+ */
40
+ startSel?: string;
41
+ /**
42
+ * The strings with which to delimit query words appearing in the document, to distinguish them from other excerpted words.
43
+ * @default '</b>'
44
+ */
45
+ stopSel?: string;
46
+ /**
47
+ * When more than one fragment is displayed, the fragments will be separated by this string.
48
+ * @default ' ... '
49
+ */
50
+ fragmentDelimiter?: string;
51
+ };
10
52
  export declare function autoAlias<T>(column: AnyColumn<{
11
53
  data: T;
12
54
  }>): SQL.Aliased<T>;
13
55
  /**
14
- * Creates a Drizzle SQL interval expression.
56
+ * Creates a PostgreSQL interval expression.
15
57
  * @param value - The numeric value of the interval.
16
58
  * @param unit - The unit of the interval (e.g., 'day', 'hour').
17
59
  * @returns A Drizzle SQL object representing the interval.
18
60
  */
19
61
  export declare function interval(value: number, unit: IntervalUnit): SQL;
20
62
  /**
21
- * Creates a Drizzle SQL `array_agg` aggregate function call.
63
+ * Creates a PostgreSQL `array_agg` aggregate function call.
22
64
  * Aggregates values from a column into a PostgreSQL array.
23
65
  * @template T - The Drizzle column type.
24
66
  * @param column - The column to aggregate.
@@ -26,7 +68,7 @@ export declare function interval(value: number, unit: IntervalUnit): SQL;
26
68
  */
27
69
  export declare function arrayAgg<T extends Column>(column: T): SQL<SelectResultField<T>[]>;
28
70
  /**
29
- * Creates a Drizzle SQL `json_agg` aggregate function call.
71
+ * Creates a PostgreSQL `json_agg` aggregate function call.
30
72
  * Aggregates rows or column values into a JSON array.
31
73
  * @template T - The Drizzle table or column type.
32
74
  * @param tableOrColumn - The table or column to aggregate into JSON.
@@ -37,7 +79,7 @@ export declare namespace jsonAgg {
37
79
  var withNull: <T extends TableLike>(tableOrColumn: T) => SQL<(SelectResultField<GetSelectTableSelection<T>> | null)[]>;
38
80
  }
39
81
  /**
40
- * Creates a Drizzle SQL `coalesce` function call.
82
+ * Creates a PostgreSQL `coalesce` function call.
41
83
  * Returns the first non-null value from the provided list of columns or SQL expressions.
42
84
  * @template T - An array type of Drizzle Columns or SQL expressions.
43
85
  * @param columns - The columns or SQL expressions to check.
@@ -45,7 +87,7 @@ export declare namespace jsonAgg {
45
87
  */
46
88
  export declare function coalesce<T extends (Column | SQL)[]>(...columns: T): SQL<SelectResultField<T>[number]>;
47
89
  /**
48
- * Creates a Drizzle SQL `to_jsonb` function call.
90
+ * Creates a PostgreSQL `to_jsonb` function call.
49
91
  * Converts the input column or SQL expression to a JSONB value.
50
92
  * @template T - The Drizzle column or SQL expression type.
51
93
  * @param column - The column or SQL expression to convert.
@@ -53,14 +95,14 @@ export declare function coalesce<T extends (Column | SQL)[]>(...columns: T): SQL
53
95
  */
54
96
  export declare function toJsonb<T extends (Column | SQL)>(column: T): SQL<SelectResultField<T>>;
55
97
  /**
56
- * Creates a Drizzle SQL `num_nulls` function call.
98
+ * Creates a PostgreSQL `num_nulls` function call.
57
99
  * Counts the number of null arguments.
58
100
  * @param columns - The columns to check for nulls.
59
101
  * @returns A Drizzle SQL object representing the count of nulls.
60
102
  */
61
103
  export declare function numNulls(...columns: Column[]): SQL<number>;
62
104
  /**
63
- * Creates a Drizzle SQL `num_nonnulls` function call.
105
+ * Creates a PostgreSQL `num_nonnulls` function call.
64
106
  * Counts the number of non-null arguments.
65
107
  * @param columns - The columns to check for non-nulls.
66
108
  * @returns A Drizzle SQL object representing the count of non-nulls.
@@ -74,3 +116,96 @@ export declare function greatest<T extends (Column | SQL | number)[]>(...values:
74
116
  [P in keyof T]: T[P] extends number ? Exclude<T[P], number> | SQL<number> : T[P];
75
117
  }[number]>>;
76
118
  export declare function greatest<T>(...values: T[]): SQL<SelectResultField<T>>;
119
+ export declare function toTsVector(language: string | SQL, column: Column | SQLChunk): SQL<string>;
120
+ /**
121
+ * Creates a PostgreSQL `to_tsquery` function call.
122
+ * This function parses text into a tsquery, respecting operators like & (AND), | (OR), and ! (NOT).
123
+ * @param language The text search configuration (e.g., 'english').
124
+ * @param text The search query text with operators.
125
+ * @returns A Drizzle SQL object representing the tsquery.
126
+ */
127
+ export declare function toTsQuery(language: string | SQL, text: string | SQLChunk): SQL;
128
+ /**
129
+ * Creates a PostgreSQL `plainto_tsquery` function call.
130
+ * This function parses text into tokens and normalizes them, creating a tsquery
131
+ * where tokens are joined by the AND operator. It's useful for raw user input.
132
+ * @param language The text search configuration (e.g., 'english').
133
+ * @param text The search query text.
134
+ * @returns A Drizzle SQL object representing the tsquery.
135
+ */
136
+ export declare function plainToTsQuery(language: string | SQL, text: string | SQLChunk): SQL;
137
+ /**
138
+ * Creates a PostgreSQL `phraseto_tsquery` function call.
139
+ * This function is similar to `plainto_tsquery` but creates a tsquery that searches for a phrase.
140
+ * @param language The text search configuration (e.g., 'english').
141
+ * @param text The phrase to search for.
142
+ * @returns A Drizzle SQL object representing the tsquery.
143
+ */
144
+ export declare function phraseToTsQuery(language: string | SQL, text: string | SQLChunk): SQL;
145
+ /**
146
+ * Creates a PostgreSQL `websearch_to_tsquery` function call.
147
+ * This function is similar to `plainto_tsquery` but also handles "quoted phrases"
148
+ * and prefixes terms with `-` or `!` for negation. It's suitable for web search style inputs.
149
+ * @param language The text search configuration (e.g., 'english').
150
+ * @param text The search query text.
151
+ * @returns A Drizzle SQL object representing the tsquery.
152
+ */
153
+ export declare function websearchToTsQuery(language: string | SQL, text: string | SQLChunk): SQL;
154
+ /**
155
+ * Creates a PostgreSQL `setweight` function call.
156
+ * Assigns a weight ('A', 'B', 'C', 'D') to a tsvector.
157
+ * @param tsvector The tsvector to weight.
158
+ * @param weight The weight to assign.
159
+ * @returns A Drizzle SQL object representing the weighted tsvector.
160
+ */
161
+ export declare function setweight(tsvector: SQL, weight: 'A' | 'B' | 'C' | 'D'): SQL;
162
+ /**
163
+ * Creates a PostgreSQL `ts_rank_cd` function call for relevance ranking.
164
+ * @param tsvector The document's tsvector.
165
+ * @param tsquery The search query's tsquery.
166
+ * @param options Optional configuration for ranking, including weights and normalization.
167
+ * @returns A Drizzle SQL object representing the relevance score.
168
+ */
169
+ export declare function tsRankCd(tsvector: SQL, tsquery: SQL, options?: {
170
+ weights?: number[];
171
+ normalization?: number;
172
+ }): SQL<number>;
173
+ /**
174
+ * Creates a PostgreSQL `ts_headline` function call for highlighting search results.
175
+ *
176
+ * @param language The text search configuration.
177
+ * @param document The original text column.
178
+ * @param tsquery The search query's tsquery.
179
+ * @param options Optional configuration object for ts_headline (e.g., { MaxWords: 10 }).
180
+ * @returns A Drizzle SQL object representing the highlighted text snippet.
181
+ *
182
+ * @warning If using HTML tags for `startSel` and `stopSel` (the default), be aware of
183
+ * Cross-Site Scripting (XSS) vulnerabilities. Ensure the `document` content is
184
+ * properly sanitized before rendering it in a browser if it comes from an untrusted source.
185
+ */
186
+ export declare function tsHeadline(language: string | SQL, document: string | SQL | AnyColumn, tsquery: SQL, options?: TsHeadlineOptions): SQL<string>;
187
+ /**
188
+ * Creates a PostgreSQL `similarity` function call (from pg_trgm extension).
189
+ * Calculates the similarity between two strings based on trigram matching.
190
+ * @param left The first text column or expression.
191
+ * @param right The second text value or expression to compare against.
192
+ * @returns A Drizzle SQL object representing the similarity score (0 to 1).
193
+ */
194
+ export declare function similarity(left: string | SQL | AnyColumn, right: string | SQL | AnyColumn): SQL<number>;
195
+ /**
196
+ * Creates a PostgreSQL `%` operator call (from pg_trgm extension) for similarity check.
197
+ * Returns true if the similarity between the two arguments is greater than the current similarity threshold.
198
+ * @param left The text column or expression.
199
+ * @param right The text value or expression to compare against.
200
+ * @returns A Drizzle SQL object representing a boolean similarity check.
201
+ */
202
+ export declare function isSimilar(left: string | SQL | AnyColumn, right: string | SQL | AnyColumn): SQL<boolean>;
203
+ /**
204
+ * Creates a PostgreSQL `<->` operator call (from pg_trgm extension) for similarity distance.
205
+ * Returns the "distance" between the arguments, that is one minus the similarity() value.
206
+ * This is useful for ordering by similarity with an index.
207
+ * @param left The text column or expression.
208
+ * @param right The text value or expression to compare against.
209
+ * @returns A Drizzle SQL object representing the similarity distance.
210
+ */
211
+ export declare function similarityDistance(left: string | SQL | AnyColumn, right: string | SQL | AnyColumn): SQL<number>;