@simplysm/orm-common 13.0.98 → 13.0.100
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/README.md +109 -184
- package/docs/core.md +125 -116
- package/docs/expression.md +168 -230
- package/docs/query-builder.md +46 -149
- package/docs/queryable.md +144 -524
- package/docs/schema-builders.md +245 -197
- package/docs/types.md +190 -190
- package/docs/utilities.md +13 -108
- package/package.json +2 -2
package/docs/queryable.md
CHANGED
|
@@ -1,504 +1,155 @@
|
|
|
1
1
|
# Queryable / Executable
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
## `Queryable`
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
## Queryable
|
|
8
|
-
|
|
9
|
-
Immutable chaining query builder for SELECT, INSERT, UPDATE, DELETE, and UPSERT operations. Each method returns a new `Queryable` instance.
|
|
5
|
+
Type-safe query builder class. Constructs SELECT, INSERT, UPDATE, DELETE queries on tables/views in a chaining manner.
|
|
10
6
|
|
|
11
7
|
```typescript
|
|
12
|
-
class Queryable<
|
|
8
|
+
class Queryable<
|
|
9
|
+
TData extends DataRecord,
|
|
10
|
+
TFrom extends TableBuilder<any, any> | never,
|
|
11
|
+
> {
|
|
13
12
|
constructor(readonly meta: QueryableMeta<TData>);
|
|
14
|
-
// ... chaining and execution methods
|
|
15
|
-
}
|
|
16
|
-
```
|
|
17
|
-
|
|
18
|
-
### Column Selection
|
|
19
|
-
|
|
20
|
-
#### select
|
|
21
|
-
|
|
22
|
-
Specify columns to SELECT. Returns a new column structure.
|
|
23
|
-
|
|
24
|
-
```typescript
|
|
25
|
-
select<R extends Record<string, any>>(
|
|
26
|
-
fn: (columns: QueryableRecord<TData>) => R,
|
|
27
|
-
): Queryable<UnwrapQueryableRecord<R>, never>;
|
|
28
|
-
```
|
|
29
|
-
|
|
30
|
-
```typescript
|
|
31
|
-
db.user().select((u) => ({
|
|
32
|
-
userName: u.name,
|
|
33
|
-
userEmail: u.email,
|
|
34
|
-
}))
|
|
35
|
-
```
|
|
36
|
-
|
|
37
|
-
#### distinct
|
|
38
|
-
|
|
39
|
-
Apply DISTINCT to remove duplicate rows.
|
|
40
|
-
|
|
41
|
-
```typescript
|
|
42
|
-
distinct(): Queryable<TData, never>;
|
|
43
|
-
```
|
|
44
|
-
|
|
45
|
-
#### lock
|
|
46
|
-
|
|
47
|
-
Apply row lock (FOR UPDATE) for exclusive access within a transaction.
|
|
48
|
-
|
|
49
|
-
```typescript
|
|
50
|
-
lock(): Queryable<TData, TFrom>;
|
|
51
|
-
```
|
|
52
|
-
|
|
53
|
-
### Pagination
|
|
54
|
-
|
|
55
|
-
#### top
|
|
56
|
-
|
|
57
|
-
Select only the top N rows (can be used without ORDER BY).
|
|
58
|
-
|
|
59
|
-
```typescript
|
|
60
|
-
top(count: number): Queryable<TData, TFrom>;
|
|
61
|
-
```
|
|
62
|
-
|
|
63
|
-
#### limit
|
|
64
|
-
|
|
65
|
-
Set LIMIT/OFFSET for pagination. Requires a preceding `orderBy()`.
|
|
66
|
-
|
|
67
|
-
```typescript
|
|
68
|
-
limit(skip: number, take: number): Queryable<TData, TFrom>;
|
|
69
|
-
```
|
|
70
|
-
|
|
71
|
-
### Sorting
|
|
72
|
-
|
|
73
|
-
#### orderBy
|
|
74
|
-
|
|
75
|
-
Add an ORDER BY clause. Multiple calls apply in order.
|
|
76
|
-
|
|
77
|
-
```typescript
|
|
78
|
-
orderBy(
|
|
79
|
-
fn: (columns: QueryableRecord<TData>) => ExprUnit<ColumnPrimitive>,
|
|
80
|
-
orderBy?: "ASC" | "DESC",
|
|
81
|
-
): Queryable<TData, TFrom>;
|
|
82
|
-
```
|
|
83
|
-
|
|
84
|
-
```typescript
|
|
85
|
-
db.user()
|
|
86
|
-
.orderBy((u) => u.name)
|
|
87
|
-
.orderBy((u) => u.age, "DESC")
|
|
88
|
-
```
|
|
89
|
-
|
|
90
|
-
### Filtering
|
|
91
|
-
|
|
92
|
-
#### where
|
|
93
|
-
|
|
94
|
-
Add a WHERE condition. Multiple calls are combined with AND.
|
|
95
|
-
|
|
96
|
-
```typescript
|
|
97
|
-
where(
|
|
98
|
-
predicate: (columns: QueryableRecord<TData>) => WhereExprUnit[],
|
|
99
|
-
): Queryable<TData, TFrom>;
|
|
100
|
-
```
|
|
101
|
-
|
|
102
|
-
```typescript
|
|
103
|
-
db.user()
|
|
104
|
-
.where((u) => [expr.eq(u.isActive, true)])
|
|
105
|
-
.where((u) => [expr.gte(u.age, 18)])
|
|
106
|
-
```
|
|
107
|
-
|
|
108
|
-
#### search
|
|
109
|
-
|
|
110
|
-
Perform text search across multiple columns. Supports OR, +must, and -exclude syntax.
|
|
111
|
-
|
|
112
|
-
```typescript
|
|
113
|
-
search(
|
|
114
|
-
fn: (columns: QueryableRecord<TData>) => ExprUnit<string | undefined>[],
|
|
115
|
-
searchText: string,
|
|
116
|
-
): Queryable<TData, TFrom>;
|
|
117
|
-
```
|
|
118
|
-
|
|
119
|
-
```typescript
|
|
120
|
-
db.user()
|
|
121
|
-
.search((u) => [u.name, u.email], "John Doe -withdrawn")
|
|
122
|
-
```
|
|
123
|
-
|
|
124
|
-
### Grouping
|
|
125
|
-
|
|
126
|
-
#### groupBy
|
|
127
|
-
|
|
128
|
-
Add a GROUP BY clause.
|
|
129
|
-
|
|
130
|
-
```typescript
|
|
131
|
-
groupBy(
|
|
132
|
-
fn: (columns: QueryableRecord<TData>) => ExprUnit<ColumnPrimitive>[],
|
|
133
|
-
): Queryable<TData, never>;
|
|
134
|
-
```
|
|
135
|
-
|
|
136
|
-
#### having
|
|
137
|
-
|
|
138
|
-
Add a HAVING clause (filtering after GROUP BY).
|
|
139
|
-
|
|
140
|
-
```typescript
|
|
141
|
-
having(
|
|
142
|
-
predicate: (columns: QueryableRecord<TData>) => WhereExprUnit[],
|
|
143
|
-
): Queryable<TData, never>;
|
|
144
|
-
```
|
|
145
|
-
|
|
146
|
-
```typescript
|
|
147
|
-
db.order()
|
|
148
|
-
.select((o) => ({
|
|
149
|
-
userId: o.userId,
|
|
150
|
-
totalAmount: expr.sum(o.amount),
|
|
151
|
-
}))
|
|
152
|
-
.groupBy((o) => [o.userId])
|
|
153
|
-
.having((o) => [expr.gte(o.totalAmount, 10000)])
|
|
154
|
-
```
|
|
155
|
-
|
|
156
|
-
### Joins
|
|
157
|
-
|
|
158
|
-
#### join
|
|
159
|
-
|
|
160
|
-
LEFT OUTER JOIN for 1:N relations (result added as array).
|
|
161
|
-
|
|
162
|
-
```typescript
|
|
163
|
-
join<A extends string, R extends DataRecord>(
|
|
164
|
-
as: A,
|
|
165
|
-
fn: (qr: JoinQueryable, cols: QueryableRecord<TData>) => Queryable<R, any>,
|
|
166
|
-
): Queryable<TData & { [K in A]?: R[] }, TFrom>;
|
|
167
|
-
```
|
|
168
|
-
|
|
169
|
-
```typescript
|
|
170
|
-
db.user()
|
|
171
|
-
.join("posts", (qr, u) =>
|
|
172
|
-
qr.from(Post).where((p) => [expr.eq(p.userId, u.id)])
|
|
173
|
-
)
|
|
174
|
-
// Result: { id, name, posts: [{ id, title }, ...] }
|
|
175
|
-
```
|
|
176
|
-
|
|
177
|
-
#### joinSingle
|
|
178
|
-
|
|
179
|
-
LEFT OUTER JOIN for N:1 or 1:1 relations (result added as single object).
|
|
180
|
-
|
|
181
|
-
```typescript
|
|
182
|
-
joinSingle<A extends string, R extends DataRecord>(
|
|
183
|
-
as: A,
|
|
184
|
-
fn: (qr: JoinQueryable, cols: QueryableRecord<TData>) => Queryable<R, any>,
|
|
185
|
-
): Queryable<TData & { [K in A]?: R }, TFrom>;
|
|
186
|
-
```
|
|
187
|
-
|
|
188
|
-
```typescript
|
|
189
|
-
db.post()
|
|
190
|
-
.joinSingle("user", (qr, p) =>
|
|
191
|
-
qr.from(User).where((u) => [expr.eq(u.id, p.userId)])
|
|
192
|
-
)
|
|
193
|
-
// Result: { id, title, user: { id, name } | undefined }
|
|
194
|
-
```
|
|
195
|
-
|
|
196
|
-
#### include
|
|
197
|
-
|
|
198
|
-
Automatically JOIN related tables based on FK/FKT relations defined in `TableBuilder`.
|
|
199
|
-
|
|
200
|
-
```typescript
|
|
201
|
-
include(fn: (item: PathProxy<TData>) => PathProxy<any>): Queryable<TData, TFrom>;
|
|
202
|
-
```
|
|
203
|
-
|
|
204
|
-
```typescript
|
|
205
|
-
// Single relation
|
|
206
|
-
db.post().include((p) => p.author)
|
|
207
|
-
|
|
208
|
-
// Nested relation
|
|
209
|
-
db.post().include((p) => p.author.company)
|
|
210
|
-
|
|
211
|
-
// Multiple relations
|
|
212
|
-
db.user()
|
|
213
|
-
.include((u) => u.company)
|
|
214
|
-
.include((u) => u.posts)
|
|
215
|
-
```
|
|
216
|
-
|
|
217
|
-
### Subqueries
|
|
218
|
-
|
|
219
|
-
#### wrap
|
|
220
|
-
|
|
221
|
-
Wrap the current Queryable as a subquery. Required when using `count()` after `distinct()` or `groupBy()`.
|
|
222
|
-
|
|
223
|
-
```typescript
|
|
224
|
-
wrap(): Queryable<TData, never>;
|
|
225
|
-
```
|
|
226
|
-
|
|
227
|
-
#### union (static)
|
|
228
|
-
|
|
229
|
-
Combine multiple Queryables with UNION (removes duplicates). Requires at least 2 queryables.
|
|
230
|
-
|
|
231
|
-
```typescript
|
|
232
|
-
static union<TData extends DataRecord>(
|
|
233
|
-
...queries: Queryable<TData, any>[]
|
|
234
|
-
): Queryable<TData, never>;
|
|
235
|
-
```
|
|
236
|
-
|
|
237
|
-
```typescript
|
|
238
|
-
const combined = Queryable.union(
|
|
239
|
-
db.user().where((u) => [expr.eq(u.type, "admin")]),
|
|
240
|
-
db.user().where((u) => [expr.eq(u.type, "manager")]),
|
|
241
|
-
);
|
|
242
|
-
```
|
|
243
|
-
|
|
244
|
-
### Recursive CTE
|
|
245
|
-
|
|
246
|
-
#### recursive
|
|
247
13
|
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
): Queryable<TData,
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
): Promise<
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
```typescript
|
|
316
|
-
async insert(records: TFrom["$inferInsert"][]): Promise<void>;
|
|
317
|
-
async insert<K extends keyof TFrom["$inferColumns"] & string>(
|
|
318
|
-
records: TFrom["$inferInsert"][],
|
|
319
|
-
outputColumns: K[],
|
|
320
|
-
): Promise<Pick<TFrom["$inferColumns"], K>[]>;
|
|
321
|
-
```
|
|
322
|
-
|
|
323
|
-
```typescript
|
|
324
|
-
// Simple insert
|
|
325
|
-
await db.user().insert([{ name: "Alice", createdAt: DateTime.now() }]);
|
|
326
|
-
|
|
327
|
-
// Insert with output
|
|
328
|
-
const [inserted] = await db.user().insert(
|
|
329
|
-
[{ name: "Alice" }],
|
|
330
|
-
["id"],
|
|
331
|
-
);
|
|
332
|
-
```
|
|
333
|
-
|
|
334
|
-
#### insertIfNotExists
|
|
335
|
-
|
|
336
|
-
INSERT only if no data matches the current WHERE condition.
|
|
337
|
-
|
|
338
|
-
```typescript
|
|
339
|
-
async insertIfNotExists(record: TFrom["$inferInsert"]): Promise<void>;
|
|
340
|
-
async insertIfNotExists<K extends keyof TFrom["$inferColumns"] & string>(
|
|
341
|
-
record: TFrom["$inferInsert"],
|
|
342
|
-
outputColumns: K[],
|
|
343
|
-
): Promise<Pick<TFrom["$inferColumns"], K>>;
|
|
344
|
-
```
|
|
345
|
-
|
|
346
|
-
#### insertInto
|
|
347
|
-
|
|
348
|
-
INSERT INTO ... SELECT -- insert the current SELECT results into another table.
|
|
349
|
-
|
|
350
|
-
```typescript
|
|
351
|
-
async insertInto<TTable extends TableBuilder<DataToColumnBuilderRecord<TData>, any>>(
|
|
352
|
-
targetTable: TTable,
|
|
353
|
-
): Promise<void>;
|
|
354
|
-
async insertInto<TTable, TOut extends keyof TTable["$inferColumns"] & string>(
|
|
355
|
-
targetTable: TTable,
|
|
356
|
-
outputColumns: TOut[],
|
|
357
|
-
): Promise<Pick<TData, TOut>[]>;
|
|
358
|
-
```
|
|
359
|
-
|
|
360
|
-
#### update
|
|
361
|
-
|
|
362
|
-
Execute an UPDATE query.
|
|
363
|
-
|
|
364
|
-
```typescript
|
|
365
|
-
async update(
|
|
366
|
-
recordFwd: (cols: QueryableRecord<TData>) => QueryableWriteRecord<TFrom["$inferUpdate"]>,
|
|
367
|
-
): Promise<void>;
|
|
368
|
-
async update<K extends keyof TFrom["$columns"] & string>(
|
|
369
|
-
recordFwd: (cols: QueryableRecord<TData>) => QueryableWriteRecord<TFrom["$inferUpdate"]>,
|
|
370
|
-
outputColumns: K[],
|
|
371
|
-
): Promise<Pick<TFrom["$columns"], K>[]>;
|
|
372
|
-
```
|
|
373
|
-
|
|
374
|
-
```typescript
|
|
375
|
-
await db.user()
|
|
376
|
-
.where((u) => [expr.eq(u.id, 1)])
|
|
377
|
-
.update((u) => ({
|
|
378
|
-
name: expr.val("string", "New Name"),
|
|
379
|
-
}));
|
|
380
|
-
```
|
|
381
|
-
|
|
382
|
-
#### delete
|
|
383
|
-
|
|
384
|
-
Execute a DELETE query.
|
|
385
|
-
|
|
386
|
-
```typescript
|
|
387
|
-
async delete(): Promise<void>;
|
|
388
|
-
async delete<K extends keyof TFrom["$columns"] & string>(
|
|
389
|
-
outputColumns: K[],
|
|
390
|
-
): Promise<Pick<TFrom["$columns"], K>[]>;
|
|
391
|
-
```
|
|
392
|
-
|
|
393
|
-
#### upsert
|
|
394
|
-
|
|
395
|
-
Execute an UPSERT (UPDATE or INSERT) query.
|
|
396
|
-
|
|
397
|
-
```typescript
|
|
398
|
-
async upsert(
|
|
399
|
-
updateFn: (cols: QueryableRecord<TData>) => QueryableWriteRecord<TFrom["$inferUpdate"]>,
|
|
400
|
-
): Promise<void>;
|
|
401
|
-
async upsert<U extends QueryableWriteRecord<TFrom["$inferUpdate"]>>(
|
|
402
|
-
updateFn: (cols: QueryableRecord<TData>) => U,
|
|
403
|
-
insertFn: (updateRecord: U) => QueryableWriteRecord<TFrom["$inferInsert"]>,
|
|
404
|
-
): Promise<void>;
|
|
405
|
-
```
|
|
406
|
-
|
|
407
|
-
```typescript
|
|
408
|
-
// Same data for update and insert
|
|
409
|
-
await db.user()
|
|
410
|
-
.where((u) => [expr.eq(u.email, "test@test.com")])
|
|
411
|
-
.upsert(() => ({
|
|
412
|
-
name: expr.val("string", "Test"),
|
|
413
|
-
email: expr.val("string", "test@test.com"),
|
|
414
|
-
}));
|
|
415
|
-
|
|
416
|
-
// Different data for update vs insert
|
|
417
|
-
await db.user()
|
|
418
|
-
.where((u) => [expr.eq(u.email, "test@test.com")])
|
|
419
|
-
.upsert(
|
|
420
|
-
() => ({ loginCount: expr.val("number", 1) }),
|
|
421
|
-
(update) => ({ ...update, email: expr.val("string", "test@test.com") }),
|
|
422
|
-
);
|
|
423
|
-
```
|
|
424
|
-
|
|
425
|
-
### DDL Helper
|
|
426
|
-
|
|
427
|
-
#### switchFk
|
|
428
|
-
|
|
429
|
-
Enable or disable FK constraints on the table (usable within a transaction).
|
|
430
|
-
|
|
431
|
-
```typescript
|
|
432
|
-
async switchFk(enabled: boolean): Promise<void>;
|
|
14
|
+
// SELECT / DISTINCT / LOCK
|
|
15
|
+
select<R extends Record<string, any>>(
|
|
16
|
+
fn: (columns: QueryableRecord<TData>) => R,
|
|
17
|
+
): Queryable<UnwrapQueryableRecord<R>, never>;
|
|
18
|
+
distinct(): Queryable<TData, never>;
|
|
19
|
+
lock(): Queryable<TData, TFrom>;
|
|
20
|
+
|
|
21
|
+
// TOP / LIMIT
|
|
22
|
+
top(count: number): Queryable<TData, TFrom>;
|
|
23
|
+
limit(skip: number, take: number): Queryable<TData, TFrom>;
|
|
24
|
+
|
|
25
|
+
// ORDER BY
|
|
26
|
+
orderBy(
|
|
27
|
+
fn: (columns: QueryableRecord<TData>) => ExprUnit<ColumnPrimitive>,
|
|
28
|
+
orderBy?: "ASC" | "DESC",
|
|
29
|
+
): Queryable<TData, TFrom>;
|
|
30
|
+
|
|
31
|
+
// WHERE
|
|
32
|
+
where(predicate: (columns: QueryableRecord<TData>) => WhereExprUnit[]): Queryable<TData, TFrom>;
|
|
33
|
+
search(
|
|
34
|
+
fn: (columns: QueryableRecord<TData>) => ExprUnit<string | undefined>[],
|
|
35
|
+
searchText: string,
|
|
36
|
+
): Queryable<TData, TFrom>;
|
|
37
|
+
|
|
38
|
+
// GROUP BY / HAVING
|
|
39
|
+
groupBy<R extends Record<string, any>>(
|
|
40
|
+
fn: (columns: QueryableRecord<TData>) => R,
|
|
41
|
+
): Queryable<UnwrapQueryableRecord<R>, never>;
|
|
42
|
+
having(predicate: (columns: QueryableRecord<TData>) => WhereExprUnit[]): Queryable<TData, TFrom>;
|
|
43
|
+
|
|
44
|
+
// JOIN
|
|
45
|
+
join<TKey extends string, TJoinData extends DataRecord>(
|
|
46
|
+
key: TKey,
|
|
47
|
+
joinFn: (j: JoinQueryable) => Queryable<TJoinData, any>,
|
|
48
|
+
on?: (own: QueryableRecord<TData>, join: QueryableRecord<TJoinData>) => WhereExprUnit[],
|
|
49
|
+
): Queryable<TData & { [K in TKey]?: TJoinData[] }, TFrom>;
|
|
50
|
+
joinSingle<TKey extends string, TJoinData extends DataRecord>(
|
|
51
|
+
key: TKey,
|
|
52
|
+
joinFn: (j: JoinQueryable) => Queryable<TJoinData, any>,
|
|
53
|
+
on?: (own: QueryableRecord<TData>, join: QueryableRecord<TJoinData>) => WhereExprUnit[],
|
|
54
|
+
): Queryable<TData & { [K in TKey]?: TJoinData }, TFrom>;
|
|
55
|
+
include<TKey extends string & keyof TData>(key: TKey): Queryable<TData, TFrom>;
|
|
56
|
+
|
|
57
|
+
// RECURSIVE
|
|
58
|
+
recursive<TBaseData extends DataRecord>(
|
|
59
|
+
baseQueryFn: (q: Queryable<TData, TFrom>) => Queryable<TBaseData, any>,
|
|
60
|
+
recursiveBodyFn: (cte: RecursiveQueryable<TBaseData>) => Queryable<any, any>,
|
|
61
|
+
): Queryable<TBaseData, never>;
|
|
62
|
+
|
|
63
|
+
// EXECUTE (SELECT)
|
|
64
|
+
execute(): Promise<TData[]>;
|
|
65
|
+
single(): Promise<TData | undefined>;
|
|
66
|
+
count(): Promise<number>;
|
|
67
|
+
exists(): Promise<boolean>;
|
|
68
|
+
|
|
69
|
+
// QueryDef generation
|
|
70
|
+
getSelectQueryDef(): SelectQueryDef;
|
|
71
|
+
getResultMeta(): ResultMeta;
|
|
72
|
+
|
|
73
|
+
// CUD (requires TFrom to be TableBuilder)
|
|
74
|
+
insert(records: TFrom["$inferInsert"][], options?: { overrideIdentity?: boolean }): Promise<TFrom["$inferColumns"][]>;
|
|
75
|
+
insertIfNotExists(record: TFrom["$inferInsert"][]): Promise<TFrom["$inferColumns"][]>;
|
|
76
|
+
insertInto(subQuery: Queryable<any, any>, options?: { overrideIdentity?: boolean }): Promise<TFrom["$inferColumns"][]>;
|
|
77
|
+
update(fn: (columns: QueryableRecord<TData>) => Partial<...>): Promise<TFrom["$inferColumns"][]>;
|
|
78
|
+
upsert(insertRecord: TFrom["$inferInsert"], updateFn: (columns: ...) => Partial<...>): Promise<TFrom["$inferColumns"][]>;
|
|
79
|
+
delete(): Promise<TFrom["$inferColumns"][]>;
|
|
80
|
+
}
|
|
433
81
|
```
|
|
434
82
|
|
|
435
|
-
###
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
83
|
+
### Key Methods
|
|
84
|
+
|
|
85
|
+
| Method | Description |
|
|
86
|
+
|--------|-------------|
|
|
87
|
+
| `select()` | Specify columns to SELECT |
|
|
88
|
+
| `distinct()` | Remove duplicate rows |
|
|
89
|
+
| `lock()` | Apply row lock (FOR UPDATE) |
|
|
90
|
+
| `top()` | Select only top N rows |
|
|
91
|
+
| `limit()` | Set LIMIT/OFFSET for pagination (requires `orderBy()`) |
|
|
92
|
+
| `orderBy()` | Add sorting condition (multiple calls apply in order) |
|
|
93
|
+
| `where()` | Add WHERE condition (multiple calls combined with AND) |
|
|
94
|
+
| `search()` | Perform text search with LIKE patterns |
|
|
95
|
+
| `groupBy()` | Group results |
|
|
96
|
+
| `having()` | Add HAVING condition |
|
|
97
|
+
| `join()` | LEFT JOIN with 1:N result (array) |
|
|
98
|
+
| `joinSingle()` | LEFT JOIN with 1:1 result (single object) |
|
|
99
|
+
| `include()` | Include pre-defined relation from schema |
|
|
100
|
+
| `recursive()` | Build recursive CTE query |
|
|
101
|
+
| `execute()` | Execute SELECT and return results |
|
|
102
|
+
| `single()` | Execute SELECT and return first result |
|
|
103
|
+
| `count()` | Return record count |
|
|
104
|
+
| `exists()` | Check if records exist |
|
|
105
|
+
| `insert()` | INSERT records |
|
|
106
|
+
| `insertIfNotExists()` | INSERT only if not exists |
|
|
107
|
+
| `insertInto()` | INSERT from subquery |
|
|
108
|
+
| `update()` | UPDATE matching records |
|
|
109
|
+
| `upsert()` | INSERT or UPDATE |
|
|
110
|
+
| `delete()` | DELETE matching records |
|
|
111
|
+
|
|
112
|
+
## `queryable`
|
|
113
|
+
|
|
114
|
+
Factory function to create a Queryable accessor for DbContext.
|
|
115
|
+
|
|
116
|
+
```typescript
|
|
117
|
+
function queryable<T extends TableBuilder<any, any> | ViewBuilder<any, any, any>>(
|
|
456
118
|
db: DbContextBase,
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
): () => Queryable<
|
|
119
|
+
builder: T,
|
|
120
|
+
alias?: string,
|
|
121
|
+
): () => Queryable<T["$inferSelect"], T extends TableBuilder<any, any> ? T : never>;
|
|
460
122
|
```
|
|
461
123
|
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
## getMatchedPrimaryKeys
|
|
124
|
+
## `Executable`
|
|
465
125
|
|
|
466
|
-
|
|
126
|
+
Stored procedure execution wrapper class.
|
|
467
127
|
|
|
468
128
|
```typescript
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
Throws if FK/PK column count does not match.
|
|
476
|
-
|
|
477
|
-
---
|
|
478
|
-
|
|
479
|
-
## Executable
|
|
129
|
+
class Executable<TParams extends ColumnBuilderRecord, TReturns extends ColumnBuilderRecord> {
|
|
130
|
+
constructor(
|
|
131
|
+
private readonly _db: DbContextBase,
|
|
132
|
+
private readonly _builder: ProcedureBuilder<TParams, TReturns>,
|
|
133
|
+
);
|
|
480
134
|
|
|
481
|
-
|
|
135
|
+
getExecProcQueryDef(params?: InferColumnExprs<TParams>): {
|
|
136
|
+
type: "execProc";
|
|
137
|
+
procedure: { database?: string; schema?: string; name: string };
|
|
138
|
+
params?: Record<string, Expr>;
|
|
139
|
+
};
|
|
482
140
|
|
|
483
|
-
|
|
484
|
-
class Executable<
|
|
485
|
-
TParams extends ColumnBuilderRecord,
|
|
486
|
-
TReturns extends ColumnBuilderRecord,
|
|
487
|
-
> {
|
|
488
|
-
getExecProcQueryDef(params?: InferColumnExprs<TParams>): ExecProcQueryDef;
|
|
489
|
-
async execute(params: InferColumnExprs<TParams>): Promise<InferColumnExprs<TReturns>[][]>;
|
|
141
|
+
execute(params: InferColumnExprs<TParams>): Promise<InferColumnExprs<TReturns>[][]>;
|
|
490
142
|
}
|
|
491
143
|
```
|
|
492
144
|
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
145
|
+
| Method | Description |
|
|
146
|
+
|--------|-------------|
|
|
147
|
+
| `getExecProcQueryDef()` | Generate procedure execution QueryDef |
|
|
148
|
+
| `execute()` | Execute the stored procedure |
|
|
496
149
|
|
|
497
|
-
|
|
150
|
+
## `executable`
|
|
498
151
|
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
Create an Executable factory function for a procedure.
|
|
152
|
+
Factory function to create an Executable accessor for DbContext.
|
|
502
153
|
|
|
503
154
|
```typescript
|
|
504
155
|
function executable<
|
|
@@ -510,29 +161,14 @@ function executable<
|
|
|
510
161
|
): () => Executable<TParams, TReturns>;
|
|
511
162
|
```
|
|
512
163
|
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
## parseSearchQuery
|
|
164
|
+
## `parseSearchQuery`
|
|
516
165
|
|
|
517
|
-
Parse a search query string
|
|
166
|
+
Parse a search query string and convert to SQL LIKE patterns.
|
|
518
167
|
|
|
519
168
|
```typescript
|
|
520
169
|
function parseSearchQuery(searchText: string): ParsedSearchQuery;
|
|
521
170
|
```
|
|
522
171
|
|
|
523
|
-
### ParsedSearchQuery
|
|
524
|
-
|
|
525
|
-
```typescript
|
|
526
|
-
interface ParsedSearchQuery {
|
|
527
|
-
/** General search terms (OR condition) - LIKE pattern */
|
|
528
|
-
or: string[];
|
|
529
|
-
/** Required search terms (AND condition, + prefix or quotes) - LIKE pattern */
|
|
530
|
-
must: string[];
|
|
531
|
-
/** Excluded search terms (NOT condition, - prefix) - LIKE pattern */
|
|
532
|
-
not: string[];
|
|
533
|
-
}
|
|
534
|
-
```
|
|
535
|
-
|
|
536
172
|
### Search Syntax
|
|
537
173
|
|
|
538
174
|
| Syntax | Meaning | Example |
|
|
@@ -543,36 +179,20 @@ interface ParsedSearchQuery {
|
|
|
543
179
|
| `"exact phrase"` | Exact match (required) | `"delicious fruit"` |
|
|
544
180
|
| `*` | Wildcard | `app*` -> `app%` |
|
|
545
181
|
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
| Input | Meaning |
|
|
549
|
-
|-------|---------|
|
|
550
|
-
| `\\` | Literal `\` |
|
|
551
|
-
| `\*` | Literal `*` |
|
|
552
|
-
| `\%` | Literal `%` |
|
|
553
|
-
| `\"` | Literal `"` |
|
|
554
|
-
| `\+` | Literal `+` |
|
|
555
|
-
| `\-` | Literal `-` |
|
|
182
|
+
## `ParsedSearchQuery`
|
|
556
183
|
|
|
557
|
-
|
|
184
|
+
Search query parsing result.
|
|
558
185
|
|
|
559
186
|
```typescript
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
// }
|
|
187
|
+
interface ParsedSearchQuery {
|
|
188
|
+
or: string[];
|
|
189
|
+
must: string[];
|
|
190
|
+
not: string[];
|
|
191
|
+
}
|
|
566
192
|
```
|
|
567
193
|
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
|
573
|
-
|------|-------------|
|
|
574
|
-
| `QueryableRecord<TData>` | Maps `TData` fields to `ExprUnit` wrappers (for column references in callbacks) |
|
|
575
|
-
| `QueryableWriteRecord<TData>` | Maps `TData` fields to `ExprInput` (for UPDATE/INSERT value callbacks) |
|
|
576
|
-
| `NullableQueryableRecord<TData>` | Like `QueryableRecord` but all primitives are `| undefined` (LEFT JOIN null propagation) |
|
|
577
|
-
| `UnwrapQueryableRecord<R>` | Reverse-transform from `QueryableRecord` back to `DataRecord` |
|
|
578
|
-
| `PathProxy<TObject>` | Type-safe path proxy for `include()` -- only non-primitive fields are accessible |
|
|
194
|
+
| Field | Type | Description |
|
|
195
|
+
|-------|------|-------------|
|
|
196
|
+
| `or` | `string[]` | General search terms (OR condition) - LIKE pattern |
|
|
197
|
+
| `must` | `string[]` | Required search terms (AND condition, + prefix or quotes) - LIKE pattern |
|
|
198
|
+
| `not` | `string[]` | Excluded search terms (NOT condition, - prefix) - LIKE pattern |
|