@tstdl/base 0.93.28 → 0.93.30
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 +2 -0
- package/orm/schemas/index.js +2 -0
- package/orm/schemas/numeric.d.ts +17 -0
- package/orm/schemas/numeric.js +23 -0
- package/orm/schemas/tsvector.d.ts +12 -0
- package/orm/schemas/tsvector.js +20 -0
- package/orm/server/drizzle/schema-converter.js +102 -24
- package/orm/server/query-converter.d.ts +2 -2
- package/orm/server/query-converter.js +137 -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/orm/types.d.ts +3 -8
- 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 -6
- package/test1.js +13 -10
- 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>>;
|
|
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,17 @@
|
|
|
1
|
+
import { NumberSchema, type NumberSchemaOptions, type SchemaDecoratorOptions, type SchemaPropertyDecorator } from '../../schema/index.js';
|
|
2
|
+
import type { TypedOmit } from '../../types/types.js';
|
|
3
|
+
export type NumericSchemaOptions = NumberSchemaOptions & {
|
|
4
|
+
precision?: number;
|
|
5
|
+
scale?: number;
|
|
6
|
+
mode?: 'string' | 'number' | 'bigint';
|
|
7
|
+
};
|
|
8
|
+
export declare class NumericSchema extends NumberSchema {
|
|
9
|
+
readonly name = "Numeric";
|
|
10
|
+
readonly precision: number | null;
|
|
11
|
+
readonly scale: number | null;
|
|
12
|
+
readonly mode: 'string' | 'number' | 'bigint' | null;
|
|
13
|
+
constructor(options?: NumericSchemaOptions);
|
|
14
|
+
}
|
|
15
|
+
export declare function numeric(precision: number, scale: number, options?: TypedOmit<NumericSchemaOptions, 'precision' | 'scale'>): NumericSchema;
|
|
16
|
+
export declare function numeric(options?: NumericSchemaOptions): NumericSchema;
|
|
17
|
+
export declare function NumericProperty(options?: NumericSchemaOptions & SchemaDecoratorOptions): SchemaPropertyDecorator;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { NumberSchema, Property } from '../../schema/index.js';
|
|
2
|
+
import { isObject } from '../../utils/type-guards.js';
|
|
3
|
+
export class NumericSchema extends NumberSchema {
|
|
4
|
+
name = 'Numeric';
|
|
5
|
+
precision;
|
|
6
|
+
scale;
|
|
7
|
+
mode;
|
|
8
|
+
constructor(options) {
|
|
9
|
+
super(options);
|
|
10
|
+
this.precision = options?.precision ?? null;
|
|
11
|
+
this.scale = options?.scale ?? null;
|
|
12
|
+
this.mode = options?.mode ?? null;
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
export function numeric(precisionOrOptions, scale, options) {
|
|
16
|
+
if (isObject(precisionOrOptions)) {
|
|
17
|
+
return new NumericSchema(precisionOrOptions);
|
|
18
|
+
}
|
|
19
|
+
return new NumericSchema({ precision: precisionOrOptions, scale, ...options });
|
|
20
|
+
}
|
|
21
|
+
export function NumericProperty(options) {
|
|
22
|
+
return Property(numeric(options), options);
|
|
23
|
+
}
|
|
@@ -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,6 +1,6 @@
|
|
|
1
|
-
import { SQL } from 'drizzle-orm';
|
|
1
|
+
import { sql, SQL } from 'drizzle-orm';
|
|
2
2
|
import { toCamelCase, toSnakeCase } from 'drizzle-orm/casing';
|
|
3
|
-
import { boolean, check, doublePrecision, foreignKey, index, integer, jsonb, pgSchema, primaryKey, text, unique, uniqueIndex, uuid } from 'drizzle-orm/pg-core';
|
|
3
|
+
import { boolean, check, doublePrecision, foreignKey, index, integer, jsonb, numeric, pgSchema, primaryKey, text, unique, uniqueIndex, uuid } from 'drizzle-orm/pg-core';
|
|
4
4
|
import { match, P } from 'ts-pattern';
|
|
5
5
|
import { MultiKeyMap } from '../../../data-structures/multi-key-map.js';
|
|
6
6
|
import { tryGetEnumName } from '../../../enumeration/enumeration.js';
|
|
@@ -13,16 +13,13 @@ 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';
|
|
20
|
-
import { JsonSchema } from '../../schemas/
|
|
21
|
-
import { NumericDateSchema } from '../../schemas/numeric-date.js';
|
|
22
|
-
import { TimestampSchema } from '../../schemas/timestamp.js';
|
|
23
|
-
import { UuidSchema } from '../../schemas/uuid.js';
|
|
19
|
+
import { bytea, numericDate, timestamp, tsvector } from '../../data-types/index.js';
|
|
20
|
+
import { JsonSchema, NumericDateSchema, NumericSchema, TimestampSchema, TsVectorSchema, UuidSchema } from '../../schemas/index.js';
|
|
24
21
|
import { decryptBytes, encryptBytes } from '../encryption.js';
|
|
25
|
-
import { convertQuery, resolveTargetColumn } from '../query-converter.js';
|
|
22
|
+
import { convertQuery, resolveTargetColumn, resolveTargetColumns } from '../query-converter.js';
|
|
26
23
|
const getDbSchema = memoizeSingle(pgSchema);
|
|
27
24
|
export const getDrizzleTableFromType = memoize(_getDrizzleTableFromType);
|
|
28
25
|
const columnDefinitionsSymbol = Symbol('columnDefinitions');
|
|
@@ -59,10 +56,13 @@ export function _getDrizzleTableFromType(type, fallbackSchemaName) {
|
|
|
59
56
|
if (columnValue instanceof SQL) {
|
|
60
57
|
return columnValue;
|
|
61
58
|
}
|
|
62
|
-
assertDefined(columnValue, 'Missing column
|
|
59
|
+
assertDefined(columnValue, 'Missing column for index.');
|
|
63
60
|
const [columnNameOrConfig, columnOrder] = isArray(columnValue) ? columnValue : [columnValue];
|
|
64
61
|
const order = columnOrder ?? data.order ?? 'asc';
|
|
65
|
-
let column =
|
|
62
|
+
let column = getColumn(table, columnNameOrConfig);
|
|
63
|
+
if (isDefined(data.options?.opclass)) {
|
|
64
|
+
return sql `${column} ${sql.raw(data.options.opclass)}`;
|
|
65
|
+
}
|
|
66
66
|
column = column[order]();
|
|
67
67
|
if (data.options?.nulls == 'first') {
|
|
68
68
|
column = column.nullsFirst();
|
|
@@ -89,16 +89,90 @@ export function _getDrizzleTableFromType(type, fallbackSchemaName) {
|
|
|
89
89
|
function buildPrimaryKey(table) {
|
|
90
90
|
const columns = primaryKeyColumnDefinitions.map((columnDefinition) => table[columnDefinition.name]);
|
|
91
91
|
return primaryKey({
|
|
92
|
-
name: mergedTableReflectionData.
|
|
92
|
+
name: mergedTableReflectionData.compoundPrimaryKeyName ?? getPrimaryKeyName(tableName, columns, { naming: mergedTableReflectionData.compoundPrimaryKeyNaming }),
|
|
93
93
|
columns,
|
|
94
94
|
});
|
|
95
95
|
}
|
|
96
|
+
function buildParadeCast(columnSql, paradeOptions) {
|
|
97
|
+
const { tokenizer, alias } = paradeOptions;
|
|
98
|
+
const { type, parameters: rawParameters = [], filters = {} } = isString(tokenizer) ? { type: tokenizer, parameters: [], filters: undefined } : (tokenizer ?? {});
|
|
99
|
+
const parameters = objectEntries({ alias, ...filters })
|
|
100
|
+
.filter(([_, value]) => isDefined(value))
|
|
101
|
+
.map(([key, value]) => {
|
|
102
|
+
return `'${toSnakeCase(key)}=${String(value)}'`;
|
|
103
|
+
});
|
|
104
|
+
if (isUndefined(type)) {
|
|
105
|
+
return sql `${columnSql}`;
|
|
106
|
+
}
|
|
107
|
+
if (parameters.length == 0) {
|
|
108
|
+
return sql `((${columnSql})::pdb.${sql.raw(type)})`;
|
|
109
|
+
}
|
|
110
|
+
const allParameters = [...rawParameters.map((param) => String(param)), ...parameters];
|
|
111
|
+
const parametersSql = sql.join(allParameters.map((parameter) => sql `${sql.raw(parameter)}`), sql `, `);
|
|
112
|
+
return sql `((${columnSql})::pdb.${sql.raw(type)}(${parametersSql}))`;
|
|
113
|
+
}
|
|
114
|
+
function buildParadeIndex(table) {
|
|
115
|
+
const paradeIndexData = mergedTableReflectionData.paradeIndex;
|
|
116
|
+
const propertyLevelColumns = columnDefinitions.filter((definition) => isDefined(definition.reflectionData?.paradeField));
|
|
117
|
+
if (isUndefined(paradeIndexData) && (propertyLevelColumns.length == 0)) {
|
|
118
|
+
return null;
|
|
119
|
+
}
|
|
120
|
+
const { naming, where, concurrently, columns: classLevelColumns, compoundColumns, expressions, ...indexOptions } = paradeIndexData ?? {};
|
|
121
|
+
const classLevelColumnSqls = (classLevelColumns ?? [])
|
|
122
|
+
.map((configOrProvider) => resolveValueOrProvider(configOrProvider, table))
|
|
123
|
+
.map((columnConfig) => {
|
|
124
|
+
const [columnOrSql, fieldOptions] = isArray(columnConfig) ? columnConfig : [columnConfig, undefined];
|
|
125
|
+
const resolvedColumnSql = resolveTargetColumn(columnOrSql, table, columnDefinitionsMap);
|
|
126
|
+
if (isUndefined(fieldOptions)) {
|
|
127
|
+
return resolvedColumnSql.getSQL();
|
|
128
|
+
}
|
|
129
|
+
return buildParadeCast(resolvedColumnSql, fieldOptions);
|
|
130
|
+
});
|
|
131
|
+
const compoundColumnSqls = (compoundColumns ?? []).map(([columnsOrProvider, fieldOptions]) => {
|
|
132
|
+
const columns = resolveValueOrProvider(columnsOrProvider, table);
|
|
133
|
+
const resolvedColumns = resolveTargetColumns(columns, table, columnDefinitionsMap);
|
|
134
|
+
const compoundExpression = sql.join(resolvedColumns, sql ` || ' ' || `);
|
|
135
|
+
return buildParadeCast(compoundExpression, fieldOptions);
|
|
136
|
+
});
|
|
137
|
+
const expressionSqls = (expressions ?? []).map(([expressionSqlOrProvider, paradeOptions]) => {
|
|
138
|
+
const expressionSql = resolveValueOrProvider(expressionSqlOrProvider, table);
|
|
139
|
+
return buildParadeCast(expressionSql, paradeOptions);
|
|
140
|
+
});
|
|
141
|
+
const propertyLevelColumnSqls = propertyLevelColumns
|
|
142
|
+
.map((columnDef) => {
|
|
143
|
+
const columnSql = resolveTargetColumn(columnDef, table, columnDefinitionsMap).getSQL();
|
|
144
|
+
const paradeOptions = columnDef.reflectionData.paradeField;
|
|
145
|
+
return buildParadeCast(columnSql, paradeOptions);
|
|
146
|
+
});
|
|
147
|
+
const indexName = getIndexName(tableName, 'parade', { naming });
|
|
148
|
+
const indexColumns = [
|
|
149
|
+
table.id, // this orm always uses 'id' as key field as every entity has it
|
|
150
|
+
...classLevelColumnSqls,
|
|
151
|
+
...propertyLevelColumnSqls,
|
|
152
|
+
...compoundColumnSqls,
|
|
153
|
+
...expressionSqls,
|
|
154
|
+
];
|
|
155
|
+
let builder = index(indexName).using('bm25', ...indexColumns);
|
|
156
|
+
if (isDefined(where)) {
|
|
157
|
+
const query = convertQuery(where(table), table, columnDefinitionsMap);
|
|
158
|
+
builder = builder.where(query.inlineParams());
|
|
159
|
+
}
|
|
160
|
+
if (concurrently == true) {
|
|
161
|
+
builder = builder.concurrently();
|
|
162
|
+
}
|
|
163
|
+
builder = builder.with({
|
|
164
|
+
key_field: `'id'`,
|
|
165
|
+
...mapObjectKeysToSnakeCase(indexOptions),
|
|
166
|
+
});
|
|
167
|
+
return builder;
|
|
168
|
+
}
|
|
96
169
|
const primaryKeyColumnDefinitions = columnDefinitions.filter((columnDefinition) => columnDefinition.reflectionData?.primaryKey == true);
|
|
97
170
|
const skipPrimaryKey = primaryKeyColumnDefinitions.length > 1;
|
|
98
171
|
const columnEntries = columnDefinitions.map((entry) => [entry.name, entry.buildType({ skipPrimaryKey })]);
|
|
99
172
|
const drizzleSchema = dbSchema.table(tableName, fromEntries(columnEntries), (drizzleTable) => {
|
|
100
173
|
const table = drizzleTable;
|
|
101
174
|
const indexes = tableReflectionDatas.flatMap((tableReflectionData) => tableReflectionData.index).filter(isDefined).map((data) => buildIndex(table, data));
|
|
175
|
+
const bm25Index = buildParadeIndex(table);
|
|
102
176
|
const checks = tableReflectionDatas.flatMap((tableReflectionData) => tableReflectionData.checks).filter(isDefined).map((data) => check(data.name, data.builder(table)));
|
|
103
177
|
return [
|
|
104
178
|
...((primaryKeyColumnDefinitions.length > 1)
|
|
@@ -130,6 +204,7 @@ export function _getDrizzleTableFromType(type, fallbackSchemaName) {
|
|
|
130
204
|
return constraint;
|
|
131
205
|
}),
|
|
132
206
|
...indexes,
|
|
207
|
+
...(isNotNull(bm25Index) ? [bm25Index] : []),
|
|
133
208
|
...checks,
|
|
134
209
|
];
|
|
135
210
|
});
|
|
@@ -236,12 +311,14 @@ function getPostgresBaseColumn(columnName, dbSchema, schema, reflectionData, con
|
|
|
236
311
|
})
|
|
237
312
|
.with(P.instanceOf(TimestampSchema), () => timestamp(columnName))
|
|
238
313
|
.with(P.instanceOf(NumericDateSchema), () => numericDate(columnName))
|
|
314
|
+
.with(P.instanceOf(NumericSchema), (n) => numeric(columnName, { precision: n.precision ?? undefined, scale: n.scale ?? undefined, mode: n.mode ?? 'number' }))
|
|
239
315
|
.with(P.instanceOf(NumberSchema), (s) => (s.integer ? integer(columnName) : doublePrecision(columnName)))
|
|
240
316
|
.with(P.instanceOf(StringSchema), () => text(columnName))
|
|
241
317
|
.with(P.instanceOf(BooleanSchema), () => boolean(columnName))
|
|
242
318
|
.with(P.instanceOf(EnumerationSchema), (s) => getPgEnum(dbSchema, s.enumeration, context)(columnName))
|
|
243
319
|
.with(P.instanceOf(JsonSchema), () => jsonb(columnName))
|
|
244
320
|
.with(P.instanceOf(Uint8ArraySchema), () => bytea(columnName))
|
|
321
|
+
.with(P.instanceOf(TsVectorSchema), () => tsvector(columnName))
|
|
245
322
|
.otherwise(() => {
|
|
246
323
|
throw new NotSupportedError(`Schema "${schema.constructor.name}" not supported on type "${context.type.name}" for property "${context.property}"`);
|
|
247
324
|
});
|
|
@@ -272,23 +349,24 @@ export function getPgEnum(schema, enumeration, context) {
|
|
|
272
349
|
function getDefaultTableName(type) {
|
|
273
350
|
return toSnakeCase(isString(type.entityName) ? type.entityName : type.name.replace(/\d+$/u, ''));
|
|
274
351
|
}
|
|
275
|
-
function getPrimaryKeyName(tableName,
|
|
276
|
-
return getIdentifier(tableName,
|
|
352
|
+
function getPrimaryKeyName(tableName, columnsOrBaseName, options) {
|
|
353
|
+
return getIdentifier(tableName, columnsOrBaseName, 'pk', options);
|
|
277
354
|
}
|
|
278
|
-
function getIndexName(tableName,
|
|
279
|
-
return getIdentifier(tableName,
|
|
355
|
+
function getIndexName(tableName, columnsOrBaseName, options) {
|
|
356
|
+
return getIdentifier(tableName, columnsOrBaseName, 'idx', options);
|
|
280
357
|
}
|
|
281
|
-
function getUniqueName(tableName,
|
|
282
|
-
return getIdentifier(tableName,
|
|
358
|
+
function getUniqueName(tableName, columnsOrBaseName, options) {
|
|
359
|
+
return getIdentifier(tableName, columnsOrBaseName, 'unique', options);
|
|
283
360
|
}
|
|
284
|
-
function getForeignKeyName(tableName,
|
|
285
|
-
return getIdentifier(tableName,
|
|
361
|
+
function getForeignKeyName(tableName, columnsOrBaseName, options) {
|
|
362
|
+
return getIdentifier(tableName, columnsOrBaseName, 'fkey', options);
|
|
286
363
|
}
|
|
287
|
-
function getIdentifier(tableName,
|
|
288
|
-
const
|
|
364
|
+
function getIdentifier(tableName, columnsOrBaseName, suffix, options) {
|
|
365
|
+
const middle = isString(columnsOrBaseName) ? columnsOrBaseName : getColumnNames(columnsOrBaseName).join('_');
|
|
366
|
+
const identifier = `${getTablePrefix(tableName, options?.naming)}_${middle}_${suffix}`;
|
|
289
367
|
if (identifier.length > 63) {
|
|
290
368
|
if (options?.naming != 'abbreviated-table') {
|
|
291
|
-
return getIdentifier(tableName,
|
|
369
|
+
return getIdentifier(tableName, columnsOrBaseName, suffix, { naming: 'abbreviated-table' });
|
|
292
370
|
}
|
|
293
371
|
throw new Error(`Identifier "${identifier}" for table "${tableName}" is too long. Maximum length is 63 characters.`);
|
|
294
372
|
}
|
|
@@ -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.
|