@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.
- package/orm/data-types/tsvector.d.ts +3 -1
- package/orm/data-types/tsvector.js +2 -0
- package/orm/decorators.d.ts +166 -16
- package/orm/decorators.js +46 -0
- package/orm/index.d.ts +1 -1
- package/orm/index.js +1 -1
- package/orm/{query.d.ts → query/base.d.ts} +14 -166
- package/orm/{query.js → query/base.js} +3 -2
- package/orm/query/index.d.ts +2 -0
- package/orm/query/index.js +2 -0
- package/orm/query/parade.d.ts +172 -0
- package/orm/query/parade.js +4 -0
- package/orm/repository.types.d.ts +27 -6
- package/orm/schemas/index.d.ts +1 -0
- package/orm/schemas/index.js +1 -0
- package/orm/schemas/tsvector.d.ts +12 -0
- package/orm/schemas/tsvector.js +20 -0
- package/orm/server/drizzle/schema-converter.js +100 -19
- package/orm/server/query-converter.d.ts +2 -2
- package/orm/server/query-converter.js +138 -91
- package/orm/server/repository.d.ts +6 -6
- package/orm/server/repository.js +34 -23
- package/orm/sqls.d.ts +19 -16
- package/orm/sqls.js +28 -38
- package/package.json +6 -6
- package/test/drizzle/0000_natural_cannonball.sql +9 -0
- package/test/drizzle/meta/0000_snapshot.json +29 -10
- package/test/drizzle/meta/_journal.json +2 -2
- package/test/test.model.js +8 -2
- package/test1.js +9 -9
- package/utils/enum.js +1 -1
- package/utils/object/object.d.ts +5 -3
- package/utils/object/object.js +17 -7
- package/utils/string/casing.d.ts +3 -0
- package/utils/string/casing.js +15 -0
- package/utils/string/index.d.ts +1 -1
- package/utils/string/index.js +1 -1
- package/test/drizzle/0000_sturdy_patch.sql +0 -9
- package/utils/string/snake-case.d.ts +0 -1
- 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 {};
|
|
@@ -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
|
-
*
|
|
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
|
-
*
|
|
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
|
-
*
|
|
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).
|
package/orm/schemas/index.d.ts
CHANGED
package/orm/schemas/index.js
CHANGED
|
@@ -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
|
|
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 =
|
|
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.
|
|
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,
|
|
276
|
-
return getIdentifier(tableName,
|
|
355
|
+
function getPrimaryKeyName(tableName, columnsOrBaseName, options) {
|
|
356
|
+
return getIdentifier(tableName, columnsOrBaseName, 'pk', options);
|
|
277
357
|
}
|
|
278
|
-
function getIndexName(tableName,
|
|
279
|
-
return getIdentifier(tableName,
|
|
358
|
+
function getIndexName(tableName, columnsOrBaseName, options) {
|
|
359
|
+
return getIdentifier(tableName, columnsOrBaseName, 'idx', options);
|
|
280
360
|
}
|
|
281
|
-
function getUniqueName(tableName,
|
|
282
|
-
return getIdentifier(tableName,
|
|
361
|
+
function getUniqueName(tableName, columnsOrBaseName, options) {
|
|
362
|
+
return getIdentifier(tableName, columnsOrBaseName, 'unique', options);
|
|
283
363
|
}
|
|
284
|
-
function getForeignKeyName(tableName,
|
|
285
|
-
return getIdentifier(tableName,
|
|
364
|
+
function getForeignKeyName(tableName, columnsOrBaseName, options) {
|
|
365
|
+
return getIdentifier(tableName, columnsOrBaseName, 'fkey', options);
|
|
286
366
|
}
|
|
287
|
-
function getIdentifier(tableName,
|
|
288
|
-
const
|
|
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,
|
|
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.
|