@tstdl/base 0.93.28 → 0.93.29

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.
Files changed (40) hide show
  1. package/orm/data-types/tsvector.d.ts +3 -1
  2. package/orm/data-types/tsvector.js +2 -0
  3. package/orm/decorators.d.ts +166 -16
  4. package/orm/decorators.js +46 -0
  5. package/orm/index.d.ts +1 -1
  6. package/orm/index.js +1 -1
  7. package/orm/{query.d.ts → query/base.d.ts} +14 -166
  8. package/orm/{query.js → query/base.js} +3 -2
  9. package/orm/query/index.d.ts +2 -0
  10. package/orm/query/index.js +2 -0
  11. package/orm/query/parade.d.ts +172 -0
  12. package/orm/query/parade.js +4 -0
  13. package/orm/repository.types.d.ts +27 -6
  14. package/orm/schemas/index.d.ts +1 -0
  15. package/orm/schemas/index.js +1 -0
  16. package/orm/schemas/tsvector.d.ts +12 -0
  17. package/orm/schemas/tsvector.js +20 -0
  18. package/orm/server/drizzle/schema-converter.js +100 -19
  19. package/orm/server/query-converter.d.ts +2 -2
  20. package/orm/server/query-converter.js +138 -91
  21. package/orm/server/repository.d.ts +6 -6
  22. package/orm/server/repository.js +34 -23
  23. package/orm/sqls.d.ts +19 -16
  24. package/orm/sqls.js +28 -38
  25. package/package.json +6 -6
  26. package/test/drizzle/0000_natural_cannonball.sql +9 -0
  27. package/test/drizzle/meta/0000_snapshot.json +29 -10
  28. package/test/drizzle/meta/_journal.json +2 -2
  29. package/test/test.model.js +8 -2
  30. package/test1.js +9 -9
  31. package/utils/enum.js +1 -1
  32. package/utils/object/object.d.ts +5 -3
  33. package/utils/object/object.js +17 -7
  34. package/utils/string/casing.d.ts +3 -0
  35. package/utils/string/casing.js +15 -0
  36. package/utils/string/index.d.ts +1 -1
  37. package/utils/string/index.js +1 -1
  38. package/test/drizzle/0000_sturdy_patch.sql +0 -9
  39. package/utils/string/snake-case.d.ts +0 -1
  40. package/utils/string/snake-case.js +0 -4
@@ -0,0 +1,172 @@
1
+ import type { SQL } from 'drizzle-orm';
2
+ import type { LiteralUnion, UnionToIntersection } from 'type-fest';
3
+ import type { Record } from '../../types/index.js';
4
+ import type { BaseEntity } from '../entity.js';
5
+ import type { TargetColumnPath } from '../repository.types.js';
6
+ /** Represents a comparison query using various operators like $eq, $ne, $gt, $in, etc. */
7
+ export type ParadeComparisonQuery = Partial<ComparisonParadeQuery>;
8
+ /** Union of keys representing comparison query operators. */
9
+ export type ParadeComparisonQueryTypes = keyof ParadeComparisonQuery;
10
+ /** Array containing all valid comparison query operator keys. */
11
+ export declare const paradeComparisonQueryTypes: ParadeComparisonQueryTypes[];
12
+ /** Represents specialized query types beyond simple comparisons. */
13
+ export type ParadeSpecialQuery<T extends BaseEntity = BaseEntity> = ParadeSearchQuery<T> | {
14
+ $parade: ParadeSpecialQueryObject<T>;
15
+ };
16
+ /** Union of keys representing special query operators. */
17
+ export type ParadeSpecialQueryTypes = keyof UnionToIntersection<ParadeSpecialQuery>;
18
+ /** Array containing all valid special query operator keys. */
19
+ export declare const paradeSpecialQueryTypes: ParadeSpecialQueryTypes[];
20
+ /** Represents a ParadeDB query on a specific field. */
21
+ export type ComparisonParadeQuery = {
22
+ $parade: ParadeComparisonQueryObject;
23
+ };
24
+ /** Represents a term in a proximity query. */
25
+ export type ParadeProximityTerm = string | {
26
+ regex: string;
27
+ maxExpansions?: number;
28
+ } | {
29
+ array: readonly (string | {
30
+ regex: string;
31
+ maxExpansions?: number;
32
+ })[];
33
+ };
34
+ /**
35
+ * Represents a bound for a ParadeDB range query.
36
+ */
37
+ type ParadeRangeBound<V> = {
38
+ included: V;
39
+ } | {
40
+ excluded: V;
41
+ };
42
+ /** A recursive type representing the rich ParadeDB / Tantivy query DSL for FIELD-LEVEL queries. */
43
+ export type ParadeComparisonQueryObject = {
44
+ term?: {
45
+ value: any;
46
+ boost?: number;
47
+ const?: number;
48
+ };
49
+ fuzzyTerm?: {
50
+ value: string;
51
+ distance?: number;
52
+ transpositionCostOne?: boolean;
53
+ prefix?: boolean;
54
+ boost?: number;
55
+ const?: number;
56
+ };
57
+ regex?: {
58
+ pattern: string;
59
+ boost?: number;
60
+ const?: number;
61
+ };
62
+ phrase?: {
63
+ phrases: readonly string[];
64
+ slop?: number;
65
+ boost?: number;
66
+ const?: number;
67
+ };
68
+ match?: {
69
+ value: string;
70
+ distance?: number;
71
+ prefix?: boolean;
72
+ conjunctionMode?: boolean;
73
+ tokenizer?: ParadeTokenizerObject;
74
+ transpositionCostOne?: boolean;
75
+ boost?: number;
76
+ const?: number;
77
+ };
78
+ exists?: null;
79
+ range?: {
80
+ lowerBound?: ParadeRangeBound<any> | null;
81
+ upperBound?: ParadeRangeBound<any> | null;
82
+ isDatetime?: boolean;
83
+ };
84
+ termSet?: {
85
+ terms: readonly string[];
86
+ conjunctionMode?: boolean;
87
+ };
88
+ phrasePrefix?: {
89
+ phrases: readonly string[];
90
+ maxExpansions?: number;
91
+ };
92
+ regexPhrase?: {
93
+ regexes: readonly string[];
94
+ slop?: number;
95
+ maxExpansions?: number;
96
+ };
97
+ rangeTerm?: {
98
+ value: any;
99
+ operator?: 'Intersects' | 'Contains' | 'Within';
100
+ };
101
+ proximity?: {
102
+ ordered?: boolean;
103
+ distance: number;
104
+ terms: readonly [ParadeProximityTerm, ParadeProximityTerm, ...ParadeProximityTerm[]];
105
+ };
106
+ };
107
+ /**
108
+ * A type representing the rich ParadeDB / Tantivy query DSL for TOP-LEVEL queries.
109
+ */
110
+ export type ParadeSpecialQueryObject<T extends BaseEntity = BaseEntity> = {
111
+ all?: null;
112
+ empty?: null;
113
+ moreLikeThis?: {
114
+ keyValue?: any;
115
+ document?: Record<TargetColumnPath<T>, any>;
116
+ fields?: readonly TargetColumnPath<T>[];
117
+ minDocFrequency?: number;
118
+ maxDocFrequency?: number;
119
+ minTermFrequency?: number;
120
+ maxQueryTerms?: number;
121
+ minWordLength?: number;
122
+ maxWordLength?: number;
123
+ stopWords?: readonly string[];
124
+ };
125
+ };
126
+ /**
127
+ * Represents a ParadeDB search using its native JSON-based query language,
128
+ * or a simplified multi-field match query.
129
+ */
130
+ export type ParadeSearchQuery<T extends BaseEntity = BaseEntity> = {
131
+ $parade: {
132
+ /** The fields to search across. */
133
+ fields: readonly [TargetColumnPath<T>, ...TargetColumnPath<T>[]];
134
+ /** The search query string. */
135
+ query: string | SQL<string>;
136
+ /**
137
+ * The conjunction mode for token matching within the query string.
138
+ * `true` for AND (all terms must match), `false` for OR (any term can match).
139
+ * Defaults to `false` (OR).
140
+ */
141
+ conjunction?: boolean;
142
+ /**
143
+ * If greater than zero, enables fuzzy matching.
144
+ * Specifies the maximum Levenshtein distance (edit distance). Max value is 2.
145
+ * @default 0
146
+ */
147
+ distance?: number;
148
+ /**
149
+ * When `true` and fuzzy matching is enabled, considers transpositions as a single edit.
150
+ * @default true
151
+ */
152
+ transpositionCostOne?: boolean;
153
+ /**
154
+ * When `true` and fuzzy matching is enabled, allows prefix matching.
155
+ * @default false
156
+ */
157
+ prefix?: boolean;
158
+ /**
159
+ * The alias of a field configuration to use for the search.
160
+ * If specified, all fields must have this alias defined.
161
+ */
162
+ alias?: string;
163
+ };
164
+ };
165
+ /**
166
+ * Represents a ParadeDB tokenizer configuration object for a `match` query.
167
+ */
168
+ export type ParadeTokenizerObject = {
169
+ type: LiteralUnion<'default' | 'whitespace' | 'raw' | 'keyword' | 'regex' | 'ngram' | 'source_code' | 'chinese_compatible' | 'chinese_lindera' | 'korean_lindera' | 'japanese_lindera' | 'jieba' | 'icu', string>;
170
+ [key: string]: any;
171
+ };
172
+ export {};
@@ -0,0 +1,4 @@
1
+ /** Array containing all valid comparison query operator keys. */
2
+ export const paradeComparisonQueryTypes = ['$parade'];
3
+ /** Array containing all valid special query operator keys. */
4
+ export const paradeSpecialQueryTypes = ['$parade'];
@@ -8,7 +8,7 @@ import type { UntaggedDeep } from '../types/tagged.js';
8
8
  import type { AnyColumn, SQL, SQLWrapper } from 'drizzle-orm';
9
9
  import type { PartialDeep } from 'type-fest';
10
10
  import type { BaseEntity, Entity, EntityMetadata } from './entity.js';
11
- import type { FullTextSearchQuery, Query } from './query.js';
11
+ import type { FullTextSearchQuery, Query } from './query/index.js';
12
12
  import type { TsHeadlineOptions } from './sqls.js';
13
13
  type WithSql<T> = {
14
14
  [P in keyof T]: T[P] extends Record ? WithSql<T[P]> : (T[P] | SQL);
@@ -79,13 +79,23 @@ export type HighlightOptions<T extends BaseEntity> = {
79
79
  * The source to generate the highlight from. Can be one or more property paths or a raw SQL expression.
80
80
  */
81
81
  source: TargetColumnPath<T> | SQL<string>;
82
+ /**
83
+ * If true, includes the byte offsets of highlight matches in the result.
84
+ * @default false
85
+ */
86
+ includePositions?: boolean;
82
87
  } & (TsHeadlineOptions | ParadeDbHighlightOptions);
83
88
  /**
84
- * Options for highlighting with ParadeDB (`pdb.snippet`).
89
+ * Options for highlighting with ParadeDB (`pdb.snippet` or `pdb.snippets`).
85
90
  */
86
91
  export type ParadeDbHighlightOptions = {
87
92
  /**
88
- * Limits the number of characters in the snippet. Defaults to 150.
93
+ * If true, retrieves multiple snippets using `pdb.snippets`.
94
+ * If false (default), retrieves a single best snippet using `pdb.snippet`.
95
+ */
96
+ multiple?: boolean;
97
+ /**
98
+ * Limits the number of characters in each snippet. Defaults to 150.
89
99
  */
90
100
  maxNumChars?: number;
91
101
  /**
@@ -97,13 +107,19 @@ export type ParadeDbHighlightOptions = {
97
107
  */
98
108
  endTag?: string;
99
109
  /**
100
- * Limits the number of highlighted terms returned in the snippet.
110
+ * The maximum number of snippets to return per document. Only used when `multiple` is true.
111
+ * @default 5
101
112
  */
102
113
  limit?: number;
103
114
  /**
104
- * Ignores the first N highlighted terms.
115
+ * The number of snippets to skip before returning results. Only used when `multiple` is true.
116
+ * @default 0
105
117
  */
106
118
  offset?: number;
119
+ /**
120
+ * The order in which to sort the snippets. Can be 'score' (default) or 'position'. Only used when `multiple` is true.
121
+ */
122
+ sortBy?: 'score' | 'position';
107
123
  };
108
124
  /**
109
125
  * Options for the `search` method.
@@ -151,7 +167,12 @@ export type SearchOptions<T extends BaseEntity> = SimplifyObject<TypedOmit<LoadM
151
167
  export type SearchResult<T extends BaseEntity> = {
152
168
  entity: T;
153
169
  score?: number;
154
- highlight?: string;
170
+ highlight?: string | string[];
171
+ /**
172
+ * Byte offsets of highlight matches. Format is an array of start/end pairs.
173
+ * e.g., `[{ start: 14, end: 19 }]`
174
+ */
175
+ highlightPositions?: [start: number, end: number][];
155
176
  };
156
177
  /**
157
178
  * Options for update operations (currently inherits from LoadOptions, primarily for ordering).
@@ -1,4 +1,5 @@
1
1
  export * from './json.js';
2
2
  export * from './numeric-date.js';
3
3
  export * from './timestamp.js';
4
+ export * from './tsvector.js';
4
5
  export * from './uuid.js';
@@ -1,4 +1,5 @@
1
1
  export * from './json.js';
2
2
  export * from './numeric-date.js';
3
3
  export * from './timestamp.js';
4
+ export * from './tsvector.js';
4
5
  export * from './uuid.js';
@@ -0,0 +1,12 @@
1
+ import type { JsonPath } from '../../json-path/json-path.js';
2
+ import { Schema, type SchemaDecoratorOptions, type SchemaPropertyDecorator, type SchemaTestOptions, type SchemaTestResult } from '../../schema/index.js';
3
+ export declare class TsVectorSchema extends Schema<string> {
4
+ readonly name = "TsVector";
5
+ _test(_value: any, _path: JsonPath, _options: SchemaTestOptions): SchemaTestResult<string>;
6
+ }
7
+ export declare function tsvector(): TsVectorSchema;
8
+ /**
9
+ * Defines a `tsvector` column.
10
+ * For automatically generated tsvector columns, use `@GeneratedTsVector()` instead.
11
+ */
12
+ export declare function TsVectorProperty(options?: SchemaDecoratorOptions): SchemaPropertyDecorator;
@@ -0,0 +1,20 @@
1
+ import { Property, Schema, SchemaError } from '../../schema/index.js';
2
+ export class TsVectorSchema extends Schema {
3
+ name = 'TsVector';
4
+ _test(_value, _path, _options) {
5
+ return {
6
+ valid: false,
7
+ error: SchemaError.expectedButGot('nothing', 'value', _path, { customMessage: `TSVectorSchema does not support validation as tsvector columns are not directly mapped to application data.` })
8
+ };
9
+ }
10
+ }
11
+ export function tsvector() {
12
+ return new TsVectorSchema();
13
+ }
14
+ /**
15
+ * Defines a `tsvector` column.
16
+ * For automatically generated tsvector columns, use `@GeneratedTsVector()` instead.
17
+ */
18
+ export function TsVectorProperty(options) {
19
+ return Property(tsvector(), options);
20
+ }
@@ -1,4 +1,4 @@
1
- import { SQL } from 'drizzle-orm';
1
+ import { sql, SQL } from 'drizzle-orm';
2
2
  import { toCamelCase, toSnakeCase } from 'drizzle-orm/casing';
3
3
  import { boolean, check, doublePrecision, foreignKey, index, integer, jsonb, pgSchema, primaryKey, text, unique, uniqueIndex, uuid } from 'drizzle-orm/pg-core';
4
4
  import { match, P } from 'ts-pattern';
@@ -13,16 +13,17 @@ import { decodeText, encodeUtf8 } from '../../../utils/encoding.js';
13
13
  import { enumValues } from '../../../utils/enum.js';
14
14
  import { memoize, memoizeSingle } from '../../../utils/function/memoize.js';
15
15
  import { compileDereferencer } from '../../../utils/object/dereference.js';
16
- import { fromEntries, objectEntries } from '../../../utils/object/object.js';
17
- import { assertDefined, assertDefinedPass, isArray, isDefined, isNotNullOrUndefined, isNull, isString, isUndefined } from '../../../utils/type-guards.js';
16
+ import { fromEntries, mapObjectKeysToSnakeCase, objectEntries } from '../../../utils/object/object.js';
17
+ import { assertDefined, assertDefinedPass, isArray, isDefined, isNotNull, isNotNullOrUndefined, isNull, isString, isUndefined } from '../../../utils/type-guards.js';
18
18
  import { resolveValueOrProvider } from '../../../utils/value-or-provider.js';
19
- import { bytea, numericDate, timestamp } from '../../data-types/index.js';
19
+ import { bytea, numericDate, timestamp, tsvector } from '../../data-types/index.js';
20
20
  import { JsonSchema } from '../../schemas/json.js';
21
21
  import { NumericDateSchema } from '../../schemas/numeric-date.js';
22
22
  import { TimestampSchema } from '../../schemas/timestamp.js';
23
+ import { TsVectorSchema } from '../../schemas/tsvector.js';
23
24
  import { UuidSchema } from '../../schemas/uuid.js';
24
25
  import { decryptBytes, encryptBytes } from '../encryption.js';
25
- import { convertQuery, resolveTargetColumn } from '../query-converter.js';
26
+ import { convertQuery, resolveTargetColumn, resolveTargetColumns } from '../query-converter.js';
26
27
  const getDbSchema = memoizeSingle(pgSchema);
27
28
  export const getDrizzleTableFromType = memoize(_getDrizzleTableFromType);
28
29
  const columnDefinitionsSymbol = Symbol('columnDefinitions');
@@ -59,10 +60,13 @@ export function _getDrizzleTableFromType(type, fallbackSchemaName) {
59
60
  if (columnValue instanceof SQL) {
60
61
  return columnValue;
61
62
  }
62
- assertDefined(columnValue, 'Missing column name for index.');
63
+ assertDefined(columnValue, 'Missing column for index.');
63
64
  const [columnNameOrConfig, columnOrder] = isArray(columnValue) ? columnValue : [columnValue];
64
65
  const order = columnOrder ?? data.order ?? 'asc';
65
- let column = isString(columnNameOrConfig) ? getColumn(table, columnNameOrConfig) : columnNameOrConfig;
66
+ let column = getColumn(table, columnNameOrConfig);
67
+ if (isDefined(data.options?.opclass)) {
68
+ return sql `${column} ${sql.raw(data.options.opclass)}`;
69
+ }
66
70
  column = column[order]();
67
71
  if (data.options?.nulls == 'first') {
68
72
  column = column.nullsFirst();
@@ -89,16 +93,90 @@ export function _getDrizzleTableFromType(type, fallbackSchemaName) {
89
93
  function buildPrimaryKey(table) {
90
94
  const columns = primaryKeyColumnDefinitions.map((columnDefinition) => table[columnDefinition.name]);
91
95
  return primaryKey({
92
- name: mergedTableReflectionData.compundPrimaryKeyName ?? getPrimaryKeyName(tableName, columns, { naming: mergedTableReflectionData.compundPrimaryKeyNaming }),
96
+ name: mergedTableReflectionData.compoundPrimaryKeyName ?? getPrimaryKeyName(tableName, columns, { naming: mergedTableReflectionData.compoundPrimaryKeyNaming }),
93
97
  columns,
94
98
  });
95
99
  }
100
+ function buildParadeCast(columnSql, paradeOptions) {
101
+ const { tokenizer, alias } = paradeOptions;
102
+ const { type, parameters: rawParameters = [], filters = {} } = isString(tokenizer) ? { type: tokenizer, parameters: [], filters: undefined } : (tokenizer ?? {});
103
+ const parameters = objectEntries({ alias, ...filters })
104
+ .filter(([_, value]) => isDefined(value))
105
+ .map(([key, value]) => {
106
+ return `'${toSnakeCase(key)}=${String(value)}'`;
107
+ });
108
+ if (isUndefined(type)) {
109
+ return sql `${columnSql}`;
110
+ }
111
+ if (parameters.length == 0) {
112
+ return sql `((${columnSql})::pdb.${sql.raw(type)})`;
113
+ }
114
+ const allParameters = [...rawParameters.map((param) => String(param)), ...parameters];
115
+ const parametersSql = sql.join(allParameters.map((parameter) => sql `${sql.raw(parameter)}`), sql `, `);
116
+ return sql `((${columnSql})::pdb.${sql.raw(type)}(${parametersSql}))`;
117
+ }
118
+ function buildParadeIndex(table) {
119
+ const paradeIndexData = mergedTableReflectionData.paradeIndex;
120
+ const propertyLevelColumns = columnDefinitions.filter((definition) => isDefined(definition.reflectionData?.paradeField));
121
+ if (isUndefined(paradeIndexData) && (propertyLevelColumns.length == 0)) {
122
+ return null;
123
+ }
124
+ const { naming, where, concurrently, columns: classLevelColumns, compoundColumns, expressions, ...indexOptions } = paradeIndexData ?? {};
125
+ const classLevelColumnSqls = (classLevelColumns ?? [])
126
+ .map((configOrProvider) => resolveValueOrProvider(configOrProvider, table))
127
+ .map((columnConfig) => {
128
+ const [columnOrSql, fieldOptions] = isArray(columnConfig) ? columnConfig : [columnConfig, undefined];
129
+ const resolvedColumnSql = resolveTargetColumn(columnOrSql, table, columnDefinitionsMap);
130
+ if (isUndefined(fieldOptions)) {
131
+ return resolvedColumnSql.getSQL();
132
+ }
133
+ return buildParadeCast(resolvedColumnSql, fieldOptions);
134
+ });
135
+ const compoundColumnSqls = (compoundColumns ?? []).map(([columnsOrProvider, fieldOptions]) => {
136
+ const columns = resolveValueOrProvider(columnsOrProvider, table);
137
+ const resolvedColumns = resolveTargetColumns(columns, table, columnDefinitionsMap);
138
+ const compoundExpression = sql.join(resolvedColumns, sql ` || ' ' || `);
139
+ return buildParadeCast(compoundExpression, fieldOptions);
140
+ });
141
+ const expressionSqls = (expressions ?? []).map(([expressionSqlOrProvider, paradeOptions]) => {
142
+ const expressionSql = resolveValueOrProvider(expressionSqlOrProvider, table);
143
+ return buildParadeCast(expressionSql, paradeOptions);
144
+ });
145
+ const propertyLevelColumnSqls = propertyLevelColumns
146
+ .map((columnDef) => {
147
+ const columnSql = resolveTargetColumn(columnDef, table, columnDefinitionsMap).getSQL();
148
+ const paradeOptions = columnDef.reflectionData.paradeField;
149
+ return buildParadeCast(columnSql, paradeOptions);
150
+ });
151
+ const indexName = getIndexName(tableName, 'parade', { naming });
152
+ const indexColumns = [
153
+ table.id, // this orm always uses 'id' as key field as every entity has it
154
+ ...classLevelColumnSqls,
155
+ ...propertyLevelColumnSqls,
156
+ ...compoundColumnSqls,
157
+ ...expressionSqls,
158
+ ];
159
+ let builder = index(indexName).using('bm25', ...indexColumns);
160
+ if (isDefined(where)) {
161
+ const query = convertQuery(where(table), table, columnDefinitionsMap);
162
+ builder = builder.where(query.inlineParams());
163
+ }
164
+ if (concurrently == true) {
165
+ builder = builder.concurrently();
166
+ }
167
+ builder = builder.with({
168
+ key_field: `'id'`,
169
+ ...mapObjectKeysToSnakeCase(indexOptions),
170
+ });
171
+ return builder;
172
+ }
96
173
  const primaryKeyColumnDefinitions = columnDefinitions.filter((columnDefinition) => columnDefinition.reflectionData?.primaryKey == true);
97
174
  const skipPrimaryKey = primaryKeyColumnDefinitions.length > 1;
98
175
  const columnEntries = columnDefinitions.map((entry) => [entry.name, entry.buildType({ skipPrimaryKey })]);
99
176
  const drizzleSchema = dbSchema.table(tableName, fromEntries(columnEntries), (drizzleTable) => {
100
177
  const table = drizzleTable;
101
178
  const indexes = tableReflectionDatas.flatMap((tableReflectionData) => tableReflectionData.index).filter(isDefined).map((data) => buildIndex(table, data));
179
+ const bm25Index = buildParadeIndex(table);
102
180
  const checks = tableReflectionDatas.flatMap((tableReflectionData) => tableReflectionData.checks).filter(isDefined).map((data) => check(data.name, data.builder(table)));
103
181
  return [
104
182
  ...((primaryKeyColumnDefinitions.length > 1)
@@ -130,6 +208,7 @@ export function _getDrizzleTableFromType(type, fallbackSchemaName) {
130
208
  return constraint;
131
209
  }),
132
210
  ...indexes,
211
+ ...(isNotNull(bm25Index) ? [bm25Index] : []),
133
212
  ...checks,
134
213
  ];
135
214
  });
@@ -242,6 +321,7 @@ function getPostgresBaseColumn(columnName, dbSchema, schema, reflectionData, con
242
321
  .with(P.instanceOf(EnumerationSchema), (s) => getPgEnum(dbSchema, s.enumeration, context)(columnName))
243
322
  .with(P.instanceOf(JsonSchema), () => jsonb(columnName))
244
323
  .with(P.instanceOf(Uint8ArraySchema), () => bytea(columnName))
324
+ .with(P.instanceOf(TsVectorSchema), () => tsvector(columnName))
245
325
  .otherwise(() => {
246
326
  throw new NotSupportedError(`Schema "${schema.constructor.name}" not supported on type "${context.type.name}" for property "${context.property}"`);
247
327
  });
@@ -272,23 +352,24 @@ export function getPgEnum(schema, enumeration, context) {
272
352
  function getDefaultTableName(type) {
273
353
  return toSnakeCase(isString(type.entityName) ? type.entityName : type.name.replace(/\d+$/u, ''));
274
354
  }
275
- function getPrimaryKeyName(tableName, columns, options) {
276
- return getIdentifier(tableName, columns, 'pk', options);
355
+ function getPrimaryKeyName(tableName, columnsOrBaseName, options) {
356
+ return getIdentifier(tableName, columnsOrBaseName, 'pk', options);
277
357
  }
278
- function getIndexName(tableName, columns, options) {
279
- return getIdentifier(tableName, columns, 'idx', options);
358
+ function getIndexName(tableName, columnsOrBaseName, options) {
359
+ return getIdentifier(tableName, columnsOrBaseName, 'idx', options);
280
360
  }
281
- function getUniqueName(tableName, columns, options) {
282
- return getIdentifier(tableName, columns, 'unique', options);
361
+ function getUniqueName(tableName, columnsOrBaseName, options) {
362
+ return getIdentifier(tableName, columnsOrBaseName, 'unique', options);
283
363
  }
284
- function getForeignKeyName(tableName, columns, options) {
285
- return getIdentifier(tableName, columns, 'fkey', options);
364
+ function getForeignKeyName(tableName, columnsOrBaseName, options) {
365
+ return getIdentifier(tableName, columnsOrBaseName, 'fkey', options);
286
366
  }
287
- function getIdentifier(tableName, columns, suffix, options) {
288
- const identifier = `${getTablePrefix(tableName, options?.naming)}_${getColumnNames(columns).join('_')}_${suffix}`;
367
+ function getIdentifier(tableName, columnsOrBaseName, suffix, options) {
368
+ const middle = isString(columnsOrBaseName) ? columnsOrBaseName : getColumnNames(columnsOrBaseName).join('_');
369
+ const identifier = `${getTablePrefix(tableName, options?.naming)}_${middle}_${suffix}`;
289
370
  if (identifier.length > 63) {
290
371
  if (options?.naming != 'abbreviated-table') {
291
- return getIdentifier(tableName, columns, suffix, { naming: 'abbreviated-table' });
372
+ return getIdentifier(tableName, columnsOrBaseName, suffix, { naming: 'abbreviated-table' });
292
373
  }
293
374
  throw new Error(`Identifier "${identifier}" for table "${tableName}" is too long. Maximum length is 63 characters.`);
294
375
  }
@@ -1,6 +1,6 @@
1
1
  import { SQL, type SQLWrapper } from 'drizzle-orm';
2
2
  import type { BaseEntity } from '../entity.js';
3
- import type { Query, TsVectorParser, TsVectorWeight } from '../query.js';
3
+ import type { Query, TsVectorParser, TsVectorWeight } from '../query/index.js';
4
4
  import type { TargetColumn } from '../repository.types.js';
5
5
  import type { ColumnDefinition, ExtraConfigColumnsFromType, PgTableFromType } from './types.js';
6
6
  /**
@@ -16,7 +16,7 @@ export declare function resolveTargetColumn<T extends BaseEntity>(target: Target
16
16
  * @param table The Drizzle table object.
17
17
  * @param columnDefinitionsMap A map from property names to column definitions.
18
18
  */
19
- export declare function resolveTargetColumns<T extends BaseEntity>(targets: (TargetColumn<T> | ColumnDefinition)[], table: PgTableFromType, columnDefinitionsMap: Map<string, ColumnDefinition>): SQLWrapper[];
19
+ export declare function resolveTargetColumns<T extends BaseEntity>(targets: readonly (TargetColumn<T> | ColumnDefinition)[], table: PgTableFromType | ExtraConfigColumnsFromType, columnDefinitionsMap: Map<string, ColumnDefinition>): SQLWrapper[];
20
20
  /**
21
21
  * Converts a query object into a Drizzle SQL condition.
22
22
  * Recursively handles nested logical operators and maps property names to database columns.