metal-orm 1.0.15 → 1.0.16

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