metal-orm 1.0.15 → 1.0.17

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 (63) hide show
  1. package/README.md +64 -61
  2. package/dist/decorators/index.cjs +490 -175
  3. package/dist/decorators/index.cjs.map +1 -1
  4. package/dist/decorators/index.d.cts +1 -5
  5. package/dist/decorators/index.d.ts +1 -5
  6. package/dist/decorators/index.js +490 -175
  7. package/dist/decorators/index.js.map +1 -1
  8. package/dist/index.cjs +1044 -483
  9. package/dist/index.cjs.map +1 -1
  10. package/dist/index.d.cts +67 -15
  11. package/dist/index.d.ts +67 -15
  12. package/dist/index.js +1033 -482
  13. package/dist/index.js.map +1 -1
  14. package/dist/{select-Bkv8g8u_.d.cts → select-BPCn6MOH.d.cts} +486 -32
  15. package/dist/{select-Bkv8g8u_.d.ts → select-BPCn6MOH.d.ts} +486 -32
  16. package/package.json +2 -1
  17. package/src/codegen/naming-strategy.ts +64 -0
  18. package/src/codegen/typescript.ts +48 -53
  19. package/src/core/ast/aggregate-functions.ts +50 -4
  20. package/src/core/ast/expression-builders.ts +22 -15
  21. package/src/core/ast/expression-nodes.ts +6 -0
  22. package/src/core/ddl/introspect/functions/postgres.ts +2 -6
  23. package/src/core/ddl/schema-generator.ts +3 -2
  24. package/src/core/ddl/schema-introspect.ts +1 -1
  25. package/src/core/dialect/abstract.ts +40 -8
  26. package/src/core/dialect/mssql/functions.ts +24 -15
  27. package/src/core/dialect/postgres/functions.ts +33 -24
  28. package/src/core/dialect/sqlite/functions.ts +19 -12
  29. package/src/core/functions/datetime.ts +2 -1
  30. package/src/core/functions/numeric.ts +2 -1
  31. package/src/core/functions/standard-strategy.ts +52 -12
  32. package/src/core/functions/text.ts +2 -1
  33. package/src/core/functions/types.ts +8 -8
  34. package/src/decorators/column.ts +13 -4
  35. package/src/index.ts +13 -5
  36. package/src/orm/domain-event-bus.ts +43 -25
  37. package/src/orm/entity-context.ts +30 -0
  38. package/src/orm/entity-meta.ts +42 -2
  39. package/src/orm/entity-metadata.ts +1 -6
  40. package/src/orm/entity.ts +88 -88
  41. package/src/orm/execute.ts +42 -25
  42. package/src/orm/execution-context.ts +18 -0
  43. package/src/orm/hydration-context.ts +16 -0
  44. package/src/orm/identity-map.ts +4 -0
  45. package/src/orm/interceptor-pipeline.ts +29 -0
  46. package/src/orm/lazy-batch.ts +6 -6
  47. package/src/orm/orm-session.ts +245 -0
  48. package/src/orm/orm.ts +58 -0
  49. package/src/orm/query-logger.ts +15 -0
  50. package/src/orm/relation-change-processor.ts +5 -1
  51. package/src/orm/relations/belongs-to.ts +45 -44
  52. package/src/orm/relations/has-many.ts +44 -43
  53. package/src/orm/relations/has-one.ts +140 -139
  54. package/src/orm/relations/many-to-many.ts +46 -45
  55. package/src/orm/runtime-types.ts +60 -2
  56. package/src/orm/transaction-runner.ts +7 -0
  57. package/src/orm/unit-of-work.ts +7 -1
  58. package/src/query-builder/insert-query-state.ts +13 -3
  59. package/src/query-builder/select-helpers.ts +50 -0
  60. package/src/query-builder/select.ts +616 -18
  61. package/src/query-builder/update-query-state.ts +31 -9
  62. package/src/schema/types.ts +16 -6
  63. package/src/orm/orm-context.ts +0 -159
@@ -1,494 +1,1092 @@
1
1
  import { TableDef } from '../schema/table.js';
2
+
2
3
  import { ColumnDef } from '../schema/column.js';
4
+
3
5
  import { SelectQueryNode, SetOperationKind } from '../core/ast/query.js';
6
+
4
7
  import { HydrationPlan } from '../core/hydration/types.js';
8
+
5
9
  import {
10
+
6
11
  ColumnNode,
12
+
7
13
  ExpressionNode,
14
+
8
15
  FunctionNode,
16
+
9
17
  LiteralNode,
18
+
10
19
  BinaryExpressionNode,
20
+
11
21
  CaseExpressionNode,
22
+
12
23
  WindowFunctionNode,
24
+
13
25
  exists,
26
+
14
27
  notExists
28
+
15
29
  } from '../core/ast/expression.js';
30
+
16
31
  import { CompiledQuery, Dialect } from '../core/dialect/abstract.js';
32
+
17
33
  import { DialectKey, resolveDialectInput } from '../core/dialect/dialect-factory.js';
18
34
 
35
+
36
+
19
37
  type SelectDialectInput = Dialect | DialectKey;
38
+
20
39
  import { SelectQueryState } from './select-query-state.js';
40
+
21
41
  import { HydrationManager } from './hydration-manager.js';
42
+
22
43
  import {
44
+
23
45
  resolveSelectQueryBuilderDependencies,
46
+
24
47
  SelectQueryBuilderContext,
48
+
25
49
  SelectQueryBuilderDependencies,
50
+
26
51
  SelectQueryBuilderEnvironment
52
+
27
53
  } from './select-query-builder-deps.js';
54
+
28
55
  import { QueryAstService } from './query-ast-service.js';
29
- import { ColumnSelector } from './column-selector.js';
30
- import { RelationManager } from './relation-manager.js';
31
- import { RelationIncludeOptions } from './relation-types.js';
32
- import { JOIN_KINDS, JoinKind, ORDER_DIRECTIONS, OrderDirection } from '../core/sql/sql.js';
33
- import { Entity, RelationMap } from '../schema/types.js';
34
- import { OrmContext } from '../orm/orm-context.js';
35
- import { executeHydrated } from '../orm/execute.js';
36
- import { createJoinNode } from '../core/ast/join-node.js';
37
56
 
38
- /**
57
+ import { ColumnSelector } from './column-selector.js';
58
+
59
+ import { RelationManager } from './relation-manager.js';
60
+
61
+ import { RelationIncludeOptions } from './relation-types.js';
62
+
63
+ import type { RelationDef } from '../schema/relation.js';
64
+
65
+ import { JOIN_KINDS, JoinKind, ORDER_DIRECTIONS, OrderDirection } from '../core/sql/sql.js';
66
+
67
+ import { Entity, RelationMap, RelationTargetTable } from '../schema/types.js';
68
+
69
+ import { OrmSession } from '../orm/orm-session.ts';
70
+
71
+ import { ExecutionContext } from '../orm/execution-context.js';
72
+
73
+ import { HydrationContext } from '../orm/hydration-context.js';
74
+
75
+ import { executeHydrated, executeHydratedWithContexts } from '../orm/execute.js';
76
+
77
+ import { createJoinNode } from '../core/ast/join-node.js';
78
+
79
+
80
+ type ColumnSelectionValue = ColumnDef | FunctionNode | CaseExpressionNode | WindowFunctionNode;
81
+
82
+ type DeepSelectConfig<TTable extends TableDef> = {
83
+ root?: (keyof TTable['columns'] & string)[];
84
+ } & {
85
+ [K in keyof TTable['relations'] & string]?: (
86
+ keyof RelationTargetTable<TTable['relations'][K]>['columns'] & string
87
+ )[];
88
+ };
89
+
90
+
91
+ /**
92
+
39
93
  * Main query builder class for constructing SQL SELECT queries
94
+
40
95
  * @typeParam T - Result type for projections (unused)
96
+
41
97
  * @typeParam TTable - Table definition being queried
98
+
42
99
  */
100
+
43
101
  export class SelectQueryBuilder<T = any, TTable extends TableDef = TableDef> {
102
+
44
103
  private readonly env: SelectQueryBuilderEnvironment;
104
+
45
105
  private readonly context: SelectQueryBuilderContext;
106
+
46
107
  private readonly columnSelector: ColumnSelector;
108
+
47
109
  private readonly relationManager: RelationManager;
110
+
48
111
  private readonly lazyRelations: Set<string>;
49
112
 
113
+
114
+
50
115
  /**
116
+
51
117
  * Creates a new SelectQueryBuilder instance
118
+
52
119
  * @param table - Table definition to query
120
+
53
121
  * @param state - Optional initial query state
122
+
54
123
  * @param hydration - Optional hydration manager
124
+
55
125
  * @param dependencies - Optional query builder dependencies
126
+
56
127
  */
128
+
57
129
  constructor(
130
+
58
131
  table: TTable,
132
+
59
133
  state?: SelectQueryState,
134
+
60
135
  hydration?: HydrationManager,
136
+
61
137
  dependencies?: Partial<SelectQueryBuilderDependencies>,
138
+
62
139
  lazyRelations?: Set<string>
140
+
63
141
  ) {
142
+
64
143
  const deps = resolveSelectQueryBuilderDependencies(dependencies);
144
+
65
145
  this.env = { table, deps };
146
+
66
147
  const initialState = state ?? deps.createState(table);
148
+
67
149
  const initialHydration = hydration ?? deps.createHydration(table);
150
+
68
151
  this.context = {
152
+
69
153
  state: initialState,
154
+
70
155
  hydration: initialHydration
156
+
71
157
  };
158
+
72
159
  this.lazyRelations = new Set(lazyRelations ?? []);
160
+
73
161
  this.columnSelector = new ColumnSelector(this.env);
162
+
74
163
  this.relationManager = new RelationManager(this.env);
164
+
75
165
  }
76
166
 
167
+
168
+
77
169
  private clone(
170
+
78
171
  context: SelectQueryBuilderContext = this.context,
172
+
79
173
  lazyRelations = new Set(this.lazyRelations)
174
+
80
175
  ): SelectQueryBuilder<T, TTable> {
176
+
81
177
  return new SelectQueryBuilder(this.env.table as TTable, context.state, context.hydration, this.env.deps, lazyRelations);
178
+
82
179
  }
83
180
 
181
+
182
+
84
183
  private resolveQueryNode(query: SelectQueryBuilder<any, TableDef<any>> | SelectQueryNode): SelectQueryNode {
184
+
85
185
  return typeof (query as any).getAST === 'function'
186
+
86
187
  ? (query as SelectQueryBuilder<any, TableDef<any>>).getAST()
188
+
87
189
  : (query as SelectQueryNode);
190
+
88
191
  }
89
192
 
193
+
194
+
90
195
  private createChildBuilder<R, TChild extends TableDef>(table: TChild): SelectQueryBuilder<R, TChild> {
196
+
91
197
  return new SelectQueryBuilder(table, undefined, undefined, this.env.deps);
198
+
92
199
  }
93
200
 
201
+
202
+
94
203
  private applyAst(
204
+
95
205
  context: SelectQueryBuilderContext,
206
+
96
207
  mutator: (service: QueryAstService) => SelectQueryState
208
+
97
209
  ): SelectQueryBuilderContext {
210
+
98
211
  const astService = this.env.deps.createQueryAstService(this.env.table, context.state);
212
+
99
213
  const nextState = mutator(astService);
214
+
100
215
  return { state: nextState, hydration: context.hydration };
216
+
101
217
  }
102
218
 
219
+
220
+
103
221
  private applyJoin(
222
+
104
223
  context: SelectQueryBuilderContext,
224
+
105
225
  table: TableDef,
226
+
106
227
  condition: BinaryExpressionNode,
228
+
107
229
  kind: JoinKind
230
+
108
231
  ): SelectQueryBuilderContext {
232
+
109
233
  const joinNode = createJoinNode(kind, table.name, condition);
234
+
110
235
  return this.applyAst(context, service => service.withJoin(joinNode));
236
+
111
237
  }
112
238
 
239
+
240
+
113
241
  private applySetOperation(
242
+
114
243
  operator: SetOperationKind,
244
+
115
245
  query: SelectQueryBuilder<any, TableDef<any>> | SelectQueryNode
246
+
116
247
  ): SelectQueryBuilderContext {
248
+
117
249
  const subAst = this.resolveQueryNode(query);
250
+
118
251
  return this.applyAst(this.context, service => service.withSetOperation(operator, subAst));
252
+
119
253
  }
120
254
 
255
+
256
+
121
257
  /**
258
+
122
259
  * Selects specific columns for the query
260
+
123
261
  * @param columns - Record of column definitions, function nodes, case expressions, or window functions
262
+
124
263
  * @returns New query builder instance with selected columns
264
+
125
265
  */
126
- select(columns: Record<string, ColumnDef | FunctionNode | CaseExpressionNode | WindowFunctionNode>): SelectQueryBuilder<T, TTable> {
127
- return this.clone(this.columnSelector.select(this.context, columns));
128
- }
266
+
267
+ select(columns: Record<string, ColumnSelectionValue>): SelectQueryBuilder<T, TTable> {
268
+
269
+ return this.clone(this.columnSelector.select(this.context, columns));
270
+
271
+ }
272
+
273
+
274
+ /**
275
+ * Selects columns from the root table by name (typed).
276
+ * @param cols - Column names on the root table
277
+ */
278
+ selectColumns<K extends keyof TTable['columns'] & string>(...cols: K[]): SelectQueryBuilder<T, TTable> {
279
+ const selection: Record<string, ColumnDef> = {};
280
+
281
+ for (const key of cols) {
282
+ const col = this.env.table.columns[key];
283
+ if (!col) {
284
+ throw new Error(`Column '${key}' not found on table '${this.env.table.name}'`);
285
+ }
286
+ selection[key] = col;
287
+ }
288
+
289
+ return this.select(selection);
290
+ }
291
+
292
+
129
293
 
130
294
  /**
295
+
131
296
  * Selects raw column expressions
297
+
132
298
  * @param cols - Column expressions as strings
299
+
133
300
  * @returns New query builder instance with raw column selections
301
+
134
302
  */
303
+
135
304
  selectRaw(...cols: string[]): SelectQueryBuilder<T, TTable> {
305
+
136
306
  return this.clone(this.columnSelector.selectRaw(this.context, cols));
307
+
137
308
  }
138
309
 
310
+
311
+
139
312
  /**
313
+
140
314
  * Adds a Common Table Expression (CTE) to the query
315
+
141
316
  * @param name - Name of the CTE
317
+
142
318
  * @param query - Query builder or query node for the CTE
319
+
143
320
  * @param columns - Optional column names for the CTE
321
+
144
322
  * @returns New query builder instance with the CTE
323
+
145
324
  */
325
+
146
326
  with(name: string, query: SelectQueryBuilder<any, TableDef<any>> | SelectQueryNode, columns?: string[]): SelectQueryBuilder<T, TTable> {
327
+
147
328
  const subAst = this.resolveQueryNode(query);
329
+
148
330
  const nextContext = this.applyAst(this.context, service => service.withCte(name, subAst, columns, false));
331
+
149
332
  return this.clone(nextContext);
333
+
150
334
  }
151
335
 
336
+
337
+
152
338
  /**
339
+
153
340
  * Adds a recursive Common Table Expression (CTE) to the query
341
+
154
342
  * @param name - Name of the CTE
343
+
155
344
  * @param query - Query builder or query node for the CTE
345
+
156
346
  * @param columns - Optional column names for the CTE
347
+
157
348
  * @returns New query builder instance with the recursive CTE
349
+
158
350
  */
351
+
159
352
  withRecursive(name: string, query: SelectQueryBuilder<any, TableDef<any>> | SelectQueryNode, columns?: string[]): SelectQueryBuilder<T, TTable> {
353
+
160
354
  const subAst = this.resolveQueryNode(query);
355
+
161
356
  const nextContext = this.applyAst(this.context, service => service.withCte(name, subAst, columns, true));
357
+
162
358
  return this.clone(nextContext);
359
+
163
360
  }
164
361
 
362
+
363
+
165
364
  /**
365
+
166
366
  * Selects a subquery as a column
367
+
167
368
  * @param alias - Alias for the subquery column
369
+
168
370
  * @param sub - Query builder or query node for the subquery
371
+
169
372
  * @returns New query builder instance with the subquery selection
373
+
170
374
  */
375
+
171
376
  selectSubquery(alias: string, sub: SelectQueryBuilder<any, TableDef<any>> | SelectQueryNode): SelectQueryBuilder<T, TTable> {
377
+
172
378
  const query = this.resolveQueryNode(sub);
379
+
173
380
  return this.clone(this.columnSelector.selectSubquery(this.context, alias, query));
381
+
174
382
  }
175
383
 
384
+
385
+
176
386
  /**
387
+
177
388
  * Adds an INNER JOIN to the query
389
+
178
390
  * @param table - Table to join
391
+
179
392
  * @param condition - Join condition expression
393
+
180
394
  * @returns New query builder instance with the INNER JOIN
395
+
181
396
  */
397
+
182
398
  innerJoin(table: TableDef, condition: BinaryExpressionNode): SelectQueryBuilder<T, TTable> {
399
+
183
400
  const nextContext = this.applyJoin(this.context, table, condition, JOIN_KINDS.INNER);
401
+
184
402
  return this.clone(nextContext);
403
+
185
404
  }
186
405
 
406
+
407
+
187
408
  /**
409
+
188
410
  * Adds a LEFT JOIN to the query
411
+
189
412
  * @param table - Table to join
413
+
190
414
  * @param condition - Join condition expression
415
+
191
416
  * @returns New query builder instance with the LEFT JOIN
417
+
192
418
  */
419
+
193
420
  leftJoin(table: TableDef, condition: BinaryExpressionNode): SelectQueryBuilder<T, TTable> {
421
+
194
422
  const nextContext = this.applyJoin(this.context, table, condition, JOIN_KINDS.LEFT);
423
+
195
424
  return this.clone(nextContext);
425
+
196
426
  }
197
427
 
428
+
429
+
198
430
  /**
431
+
199
432
  * Adds a RIGHT JOIN to the query
433
+
200
434
  * @param table - Table to join
435
+
201
436
  * @param condition - Join condition expression
437
+
202
438
  * @returns New query builder instance with the RIGHT JOIN
439
+
203
440
  */
441
+
204
442
  rightJoin(table: TableDef, condition: BinaryExpressionNode): SelectQueryBuilder<T, TTable> {
443
+
205
444
  const nextContext = this.applyJoin(this.context, table, condition, JOIN_KINDS.RIGHT);
445
+
206
446
  return this.clone(nextContext);
447
+
207
448
  }
208
449
 
450
+
451
+
209
452
  /**
453
+
210
454
  * Matches records based on a relationship
455
+
211
456
  * @param relationName - Name of the relationship to match
457
+
212
458
  * @param predicate - Optional predicate expression
459
+
213
460
  * @returns New query builder instance with the relationship match
461
+
214
462
  */
463
+
215
464
  match(relationName: string, predicate?: ExpressionNode): SelectQueryBuilder<T, TTable> {
465
+
216
466
  const nextContext = this.relationManager.match(this.context, relationName, predicate);
467
+
217
468
  return this.clone(nextContext);
469
+
218
470
  }
219
471
 
472
+
473
+
220
474
  /**
475
+
221
476
  * Joins a related table
477
+
222
478
  * @param relationName - Name of the relationship to join
479
+
223
480
  * @param joinKind - Type of join (defaults to INNER)
481
+
224
482
  * @param extraCondition - Optional additional join condition
483
+
225
484
  * @returns New query builder instance with the relationship join
485
+
226
486
  */
487
+
227
488
  joinRelation(
489
+
228
490
  relationName: string,
491
+
229
492
  joinKind: JoinKind = JOIN_KINDS.INNER,
493
+
230
494
  extraCondition?: ExpressionNode
495
+
231
496
  ): SelectQueryBuilder<T, TTable> {
497
+
232
498
  const nextContext = this.relationManager.joinRelation(this.context, relationName, joinKind, extraCondition);
499
+
233
500
  return this.clone(nextContext);
501
+
234
502
  }
235
503
 
504
+
505
+
236
506
  /**
507
+
237
508
  * Includes related data in the query results
509
+
238
510
  * @param relationName - Name of the relationship to include
511
+
239
512
  * @param options - Optional include options
513
+
240
514
  * @returns New query builder instance with the relationship inclusion
515
+
241
516
  */
517
+
242
518
  include(relationName: string, options?: RelationIncludeOptions): SelectQueryBuilder<T, TTable> {
519
+
243
520
  const nextContext = this.relationManager.include(this.context, relationName, options);
521
+
244
522
  return this.clone(nextContext);
245
- }
246
523
 
247
- includeLazy<K extends keyof RelationMap<TTable>>(relationName: K): SelectQueryBuilder<T, TTable> {
248
- const nextLazy = new Set(this.lazyRelations);
249
- nextLazy.add(relationName as string);
250
- return this.clone(this.context, nextLazy);
251
524
  }
252
525
 
526
+
527
+
528
+ includeLazy<K extends keyof RelationMap<TTable>>(relationName: K): SelectQueryBuilder<T, TTable> {
529
+
530
+ const nextLazy = new Set(this.lazyRelations);
531
+
532
+ nextLazy.add(relationName as string);
533
+
534
+ return this.clone(this.context, nextLazy);
535
+
536
+ }
537
+
538
+ /**
539
+ * Selects columns for a related table in a single hop.
540
+ */
541
+ selectRelationColumns<
542
+ K extends keyof TTable['relations'] & string,
543
+ TRel extends RelationDef = TTable['relations'][K],
544
+ TTarget extends TableDef = RelationTargetTable<TRel>,
545
+ C extends keyof TTarget['columns'] & string = keyof TTarget['columns'] & string
546
+ >(relationName: K, ...cols: C[]): SelectQueryBuilder<T, TTable> {
547
+ const relation = this.env.table.relations[relationName] as RelationDef | undefined;
548
+ if (!relation) {
549
+ throw new Error(`Relation '${relationName}' not found on table '${this.env.table.name}'`);
550
+ }
551
+ const target = relation.target;
552
+
553
+ for (const col of cols) {
554
+ if (!target.columns[col]) {
555
+ throw new Error(
556
+ `Column '${col}' not found on related table '${target.name}' for relation '${relationName}'`
557
+ );
558
+ }
559
+ }
560
+
561
+ return this.include(relationName as string, { columns: cols as string[] });
562
+ }
563
+
564
+
565
+ /**
566
+ * Convenience alias for selecting specific columns from a relation.
567
+ */
568
+ includePick<
569
+ K extends keyof TTable['relations'] & string,
570
+ TRel extends RelationDef = TTable['relations'][K],
571
+ TTarget extends TableDef = RelationTargetTable<TRel>,
572
+ C extends keyof TTarget['columns'] & string = keyof TTarget['columns'] & string
573
+ >(relationName: K, cols: C[]): SelectQueryBuilder<T, TTable> {
574
+ return this.selectRelationColumns(relationName, ...cols);
575
+ }
576
+
577
+
578
+ /**
579
+ * Selects columns for the root table and relations from a single config object.
580
+ */
581
+ selectColumnsDeep(config: DeepSelectConfig<TTable>): SelectQueryBuilder<T, TTable> {
582
+ let qb: SelectQueryBuilder<T, TTable> = this;
583
+
584
+ if (config.root?.length) {
585
+ qb = qb.selectColumns(...config.root);
586
+ }
587
+
588
+ for (const key of Object.keys(config) as (keyof typeof config)[]) {
589
+ if (key === 'root') continue;
590
+ const relName = key as keyof TTable['relations'] & string;
591
+ const cols = config[relName as keyof DeepSelectConfig<TTable>] as string[] | undefined;
592
+ if (!cols || !cols.length) continue;
593
+ qb = qb.selectRelationColumns(relName, ...(cols as string[]));
594
+ }
595
+
596
+ return qb;
597
+ }
598
+
599
+
600
+
253
601
  getLazyRelations(): (keyof RelationMap<TTable>)[] {
602
+
254
603
  return Array.from(this.lazyRelations) as (keyof RelationMap<TTable>)[];
604
+
255
605
  }
256
606
 
607
+
608
+
257
609
  getTable(): TTable {
610
+
258
611
  return this.env.table as TTable;
612
+
259
613
  }
260
614
 
261
- async execute(ctx: OrmContext): Promise<Entity<TTable>[]> {
615
+
616
+
617
+ async execute(ctx: OrmSession): Promise<Entity<TTable>[]> {
618
+
262
619
  return executeHydrated(ctx, this);
620
+
621
+ }
622
+
623
+
624
+
625
+ async executeWithContexts(execCtx: ExecutionContext, hydCtx: HydrationContext): Promise<Entity<TTable>[]> {
626
+
627
+ return executeHydratedWithContexts(execCtx, hydCtx, this);
628
+
263
629
  }
264
630
 
631
+
632
+
265
633
  /**
634
+
266
635
  * Adds a WHERE condition to the query
636
+
267
637
  * @param expr - Expression for the WHERE clause
638
+
268
639
  * @returns New query builder instance with the WHERE condition
640
+
269
641
  */
642
+
270
643
  where(expr: ExpressionNode): SelectQueryBuilder<T, TTable> {
644
+
271
645
  const nextContext = this.applyAst(this.context, service => service.withWhere(expr));
646
+
272
647
  return this.clone(nextContext);
648
+
273
649
  }
274
650
 
651
+
652
+
275
653
  /**
654
+
276
655
  * Adds a GROUP BY clause to the query
656
+
277
657
  * @param col - Column definition or column node to group by
658
+
278
659
  * @returns New query builder instance with the GROUP BY clause
660
+
279
661
  */
662
+
280
663
  groupBy(col: ColumnDef | ColumnNode): SelectQueryBuilder<T, TTable> {
664
+
281
665
  const nextContext = this.applyAst(this.context, service => service.withGroupBy(col));
666
+
282
667
  return this.clone(nextContext);
668
+
283
669
  }
284
670
 
671
+
672
+
285
673
  /**
674
+
286
675
  * Adds a HAVING condition to the query
676
+
287
677
  * @param expr - Expression for the HAVING clause
678
+
288
679
  * @returns New query builder instance with the HAVING condition
680
+
289
681
  */
682
+
290
683
  having(expr: ExpressionNode): SelectQueryBuilder<T, TTable> {
684
+
291
685
  const nextContext = this.applyAst(this.context, service => service.withHaving(expr));
686
+
292
687
  return this.clone(nextContext);
688
+
293
689
  }
294
690
 
691
+
692
+
295
693
  /**
694
+
296
695
  * Adds an ORDER BY clause to the query
696
+
297
697
  * @param col - Column definition or column node to order by
698
+
298
699
  * @param direction - Order direction (defaults to ASC)
700
+
299
701
  * @returns New query builder instance with the ORDER BY clause
702
+
300
703
  */
704
+
301
705
  orderBy(col: ColumnDef | ColumnNode, direction: OrderDirection = ORDER_DIRECTIONS.ASC): SelectQueryBuilder<T, TTable> {
706
+
302
707
  const nextContext = this.applyAst(this.context, service => service.withOrderBy(col, direction));
708
+
303
709
  return this.clone(nextContext);
710
+
304
711
  }
305
712
 
713
+
714
+
306
715
  /**
716
+
307
717
  * Adds a DISTINCT clause to the query
718
+
308
719
  * @param cols - Columns to make distinct
720
+
309
721
  * @returns New query builder instance with the DISTINCT clause
722
+
310
723
  */
724
+
311
725
  distinct(...cols: (ColumnDef | ColumnNode)[]): SelectQueryBuilder<T, TTable> {
726
+
312
727
  return this.clone(this.columnSelector.distinct(this.context, cols));
728
+
313
729
  }
314
730
 
731
+
732
+
315
733
  /**
734
+
316
735
  * Adds a LIMIT clause to the query
736
+
317
737
  * @param n - Maximum number of rows to return
738
+
318
739
  * @returns New query builder instance with the LIMIT clause
740
+
319
741
  */
742
+
320
743
  limit(n: number): SelectQueryBuilder<T, TTable> {
744
+
321
745
  const nextContext = this.applyAst(this.context, service => service.withLimit(n));
746
+
322
747
  return this.clone(nextContext);
748
+
323
749
  }
324
750
 
751
+
752
+
325
753
  /**
754
+
326
755
  * Adds an OFFSET clause to the query
756
+
327
757
  * @param n - Number of rows to skip
758
+
328
759
  * @returns New query builder instance with the OFFSET clause
760
+
329
761
  */
762
+
330
763
  offset(n: number): SelectQueryBuilder<T, TTable> {
764
+
331
765
  const nextContext = this.applyAst(this.context, service => service.withOffset(n));
766
+
332
767
  return this.clone(nextContext);
768
+
333
769
  }
334
770
 
771
+
772
+
335
773
  /**
774
+
336
775
  * Combines this query with another using UNION
776
+
337
777
  * @param query - Query to union with
778
+
338
779
  * @returns New query builder instance with the set operation
780
+
339
781
  */
782
+
340
783
  union(query: SelectQueryBuilder<any, TableDef<any>> | SelectQueryNode): SelectQueryBuilder<T, TTable> {
784
+
341
785
  return this.clone(this.applySetOperation('UNION', query));
786
+
342
787
  }
343
788
 
789
+
790
+
344
791
  /**
792
+
345
793
  * Combines this query with another using UNION ALL
794
+
346
795
  * @param query - Query to union with
796
+
347
797
  * @returns New query builder instance with the set operation
798
+
348
799
  */
800
+
349
801
  unionAll(query: SelectQueryBuilder<any, TableDef<any>> | SelectQueryNode): SelectQueryBuilder<T, TTable> {
802
+
350
803
  return this.clone(this.applySetOperation('UNION ALL', query));
804
+
351
805
  }
352
806
 
807
+
808
+
353
809
  /**
810
+
354
811
  * Combines this query with another using INTERSECT
812
+
355
813
  * @param query - Query to intersect with
814
+
356
815
  * @returns New query builder instance with the set operation
816
+
357
817
  */
818
+
358
819
  intersect(query: SelectQueryBuilder<any, TableDef<any>> | SelectQueryNode): SelectQueryBuilder<T, TTable> {
820
+
359
821
  return this.clone(this.applySetOperation('INTERSECT', query));
822
+
360
823
  }
361
824
 
825
+
826
+
362
827
  /**
828
+
363
829
  * Combines this query with another using EXCEPT
830
+
364
831
  * @param query - Query to subtract
832
+
365
833
  * @returns New query builder instance with the set operation
834
+
366
835
  */
836
+
367
837
  except(query: SelectQueryBuilder<any, TableDef<any>> | SelectQueryNode): SelectQueryBuilder<T, TTable> {
838
+
368
839
  return this.clone(this.applySetOperation('EXCEPT', query));
840
+
369
841
  }
370
842
 
843
+
844
+
371
845
  /**
846
+
372
847
  * Adds a WHERE EXISTS condition to the query
848
+
373
849
  * @param subquery - Subquery to check for existence
850
+
374
851
  * @returns New query builder instance with the WHERE EXISTS condition
852
+
375
853
  */
854
+
376
855
  whereExists(subquery: SelectQueryBuilder<any, TableDef<any>> | SelectQueryNode): SelectQueryBuilder<T, TTable> {
856
+
377
857
  const subAst = this.resolveQueryNode(subquery);
858
+
378
859
  return this.where(exists(subAst));
860
+
379
861
  }
380
862
 
863
+
864
+
381
865
  /**
866
+
382
867
  * Adds a WHERE NOT EXISTS condition to the query
868
+
383
869
  * @param subquery - Subquery to check for non-existence
870
+
384
871
  * @returns New query builder instance with the WHERE NOT EXISTS condition
872
+
385
873
  */
874
+
386
875
  whereNotExists(subquery: SelectQueryBuilder<any, TableDef<any>> | SelectQueryNode): SelectQueryBuilder<T, TTable> {
876
+
387
877
  const subAst = this.resolveQueryNode(subquery);
878
+
388
879
  return this.where(notExists(subAst));
880
+
389
881
  }
390
882
 
883
+
884
+
391
885
  /**
886
+
392
887
  * Adds a WHERE EXISTS condition based on a relationship
888
+
393
889
  * @param relationName - Name of the relationship to check
890
+
394
891
  * @param callback - Optional callback to modify the relationship query
892
+
395
893
  * @returns New query builder instance with the relationship existence check
894
+
396
895
  */
896
+
397
897
  whereHas(
898
+
398
899
  relationName: string,
900
+
399
901
  callback?: <TChildTable extends TableDef>(
902
+
400
903
  qb: SelectQueryBuilder<any, TChildTable>
904
+
401
905
  ) => SelectQueryBuilder<any, TChildTable>
906
+
402
907
  ): SelectQueryBuilder<T, TTable> {
908
+
403
909
  const relation = this.env.table.relations[relationName];
910
+
404
911
  if (!relation) {
912
+
405
913
  throw new Error(`Relation '${relationName}' not found on table '${this.env.table.name}'`);
914
+
406
915
  }
407
916
 
917
+
918
+
408
919
  let subQb = this.createChildBuilder<any, typeof relation.target>(relation.target);
920
+
409
921
  if (callback) {
922
+
410
923
  subQb = callback(subQb);
924
+
411
925
  }
412
926
 
927
+
928
+
413
929
  const subAst = subQb.getAST();
930
+
414
931
  const finalSubAst = this.relationManager.applyRelationCorrelation(this.context, relationName, subAst);
932
+
415
933
  return this.where(exists(finalSubAst));
934
+
416
935
  }
417
936
 
937
+
938
+
418
939
  /**
940
+
419
941
  * Adds a WHERE NOT EXISTS condition based on a relationship
942
+
420
943
  * @param relationName - Name of the relationship to check
944
+
421
945
  * @param callback - Optional callback to modify the relationship query
946
+
422
947
  * @returns New query builder instance with the relationship non-existence check
948
+
423
949
  */
950
+
424
951
  whereHasNot(
952
+
425
953
  relationName: string,
954
+
426
955
  callback?: <TChildTable extends TableDef>(
956
+
427
957
  qb: SelectQueryBuilder<any, TChildTable>
958
+
428
959
  ) => SelectQueryBuilder<any, TChildTable>
960
+
429
961
  ): SelectQueryBuilder<T, TTable> {
962
+
430
963
  const relation = this.env.table.relations[relationName];
964
+
431
965
  if (!relation) {
966
+
432
967
  throw new Error(`Relation '${relationName}' not found on table '${this.env.table.name}'`);
968
+
433
969
  }
434
970
 
971
+
972
+
435
973
  let subQb = this.createChildBuilder<any, typeof relation.target>(relation.target);
974
+
436
975
  if (callback) {
976
+
437
977
  subQb = callback(subQb);
978
+
438
979
  }
439
980
 
981
+
982
+
440
983
  const subAst = subQb.getAST();
984
+
441
985
  const finalSubAst = this.relationManager.applyRelationCorrelation(this.context, relationName, subAst);
986
+
442
987
  return this.where(notExists(finalSubAst));
988
+
443
989
  }
444
990
 
991
+
992
+
445
993
  /**
994
+
446
995
  * Compiles the query to SQL for a specific dialect
996
+
447
997
  * @param dialect - Database dialect to compile for
998
+
448
999
  * @returns Compiled query with SQL and parameters
1000
+
449
1001
  */
1002
+
450
1003
  compile(dialect: SelectDialectInput): CompiledQuery {
1004
+
451
1005
  const resolved = resolveDialectInput(dialect);
1006
+
452
1007
  return resolved.compileSelect(this.context.state.ast);
1008
+
453
1009
  }
454
1010
 
1011
+
1012
+
455
1013
  /**
1014
+
456
1015
  * Converts the query to SQL string for a specific dialect
1016
+
457
1017
  * @param dialect - Database dialect to generate SQL for
1018
+
458
1019
  * @returns SQL string representation of the query
1020
+
459
1021
  */
1022
+
460
1023
  toSql(dialect: SelectDialectInput): string {
1024
+
461
1025
  return this.compile(dialect).sql;
1026
+
462
1027
  }
463
1028
 
1029
+
1030
+
464
1031
  /**
1032
+
465
1033
  * Gets the hydration plan for the query
1034
+
466
1035
  * @returns Hydration plan or undefined if none exists
1036
+
467
1037
  */
1038
+
468
1039
  getHydrationPlan(): HydrationPlan | undefined {
1040
+
469
1041
  return this.context.hydration.getPlan();
1042
+
470
1043
  }
471
1044
 
1045
+
1046
+
472
1047
  /**
1048
+
473
1049
  * Gets the Abstract Syntax Tree (AST) representation of the query
1050
+
474
1051
  * @returns Query AST with hydration applied
1052
+
475
1053
  */
1054
+
476
1055
  getAST(): SelectQueryNode {
1056
+
477
1057
  return this.context.hydration.applyToAst(this.context.state.ast);
1058
+
478
1059
  }
1060
+
479
1061
  }
480
1062
 
1063
+
1064
+
481
1065
  /**
1066
+
482
1067
  * Creates a column node for use in expressions
1068
+
483
1069
  * @param table - Table name
1070
+
484
1071
  * @param name - Column name
1072
+
485
1073
  * @returns ColumnNode with the specified table and name
1074
+
486
1075
  */
1076
+
487
1077
  export const createColumn = (table: string, name: string): ColumnNode => ({ type: 'Column', table, name });
488
1078
 
1079
+
1080
+
489
1081
  /**
1082
+
490
1083
  * Creates a literal value node for use in expressions
1084
+
491
1085
  * @param val - Literal value (string or number)
1086
+
492
1087
  * @returns LiteralNode with the specified value
1088
+
493
1089
  */
1090
+
494
1091
  export const createLiteral = (val: string | number): LiteralNode => ({ type: 'Literal', value: val });
1092
+