@tanstack/db 0.0.13 → 0.0.15

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 (198) hide show
  1. package/dist/cjs/collection.cjs +117 -104
  2. package/dist/cjs/collection.cjs.map +1 -1
  3. package/dist/cjs/collection.d.cts +19 -22
  4. package/dist/cjs/index.cjs +35 -13
  5. package/dist/cjs/index.cjs.map +1 -1
  6. package/dist/cjs/index.d.cts +0 -1
  7. package/dist/cjs/query/builder/functions.cjs +107 -0
  8. package/dist/cjs/query/builder/functions.cjs.map +1 -0
  9. package/dist/cjs/query/builder/functions.d.cts +38 -0
  10. package/dist/cjs/query/builder/index.cjs +499 -0
  11. package/dist/cjs/query/builder/index.cjs.map +1 -0
  12. package/dist/cjs/query/builder/index.d.cts +324 -0
  13. package/dist/cjs/query/builder/ref-proxy.cjs +96 -0
  14. package/dist/cjs/query/builder/ref-proxy.cjs.map +1 -0
  15. package/dist/cjs/query/builder/ref-proxy.d.cts +28 -0
  16. package/dist/cjs/query/builder/types.d.cts +80 -0
  17. package/dist/cjs/query/compiler/evaluators.cjs +261 -0
  18. package/dist/cjs/query/compiler/evaluators.cjs.map +1 -0
  19. package/dist/cjs/query/compiler/evaluators.d.cts +11 -0
  20. package/dist/cjs/query/compiler/group-by.cjs +271 -0
  21. package/dist/cjs/query/compiler/group-by.cjs.map +1 -0
  22. package/dist/cjs/query/compiler/group-by.d.cts +7 -0
  23. package/dist/cjs/query/compiler/index.cjs +181 -0
  24. package/dist/cjs/query/compiler/index.cjs.map +1 -0
  25. package/dist/cjs/query/compiler/index.d.cts +15 -0
  26. package/dist/cjs/query/compiler/joins.cjs +116 -0
  27. package/dist/cjs/query/compiler/joins.cjs.map +1 -0
  28. package/dist/cjs/query/compiler/joins.d.cts +11 -0
  29. package/dist/cjs/query/compiler/order-by.cjs +89 -0
  30. package/dist/cjs/query/compiler/order-by.cjs.map +1 -0
  31. package/dist/cjs/query/compiler/order-by.d.cts +9 -0
  32. package/dist/cjs/query/compiler/select.cjs +57 -0
  33. package/dist/cjs/query/compiler/select.cjs.map +1 -0
  34. package/dist/cjs/query/compiler/select.d.cts +15 -0
  35. package/dist/cjs/query/index.d.cts +6 -5
  36. package/dist/cjs/query/ir.cjs +57 -0
  37. package/dist/cjs/query/ir.cjs.map +1 -0
  38. package/dist/cjs/query/ir.d.cts +81 -0
  39. package/dist/cjs/query/live-query-collection.cjs +224 -0
  40. package/dist/cjs/query/live-query-collection.cjs.map +1 -0
  41. package/dist/cjs/query/live-query-collection.d.cts +124 -0
  42. package/dist/cjs/transactions.cjs +20 -13
  43. package/dist/cjs/transactions.cjs.map +1 -1
  44. package/dist/cjs/transactions.d.cts +13 -4
  45. package/dist/cjs/types.d.cts +14 -1
  46. package/dist/esm/collection.d.ts +19 -22
  47. package/dist/esm/collection.js +118 -105
  48. package/dist/esm/collection.js.map +1 -1
  49. package/dist/esm/index.d.ts +0 -1
  50. package/dist/esm/index.js +34 -12
  51. package/dist/esm/index.js.map +1 -1
  52. package/dist/esm/query/builder/functions.d.ts +38 -0
  53. package/dist/esm/query/builder/functions.js +107 -0
  54. package/dist/esm/query/builder/functions.js.map +1 -0
  55. package/dist/esm/query/builder/index.d.ts +324 -0
  56. package/dist/esm/query/builder/index.js +499 -0
  57. package/dist/esm/query/builder/index.js.map +1 -0
  58. package/dist/esm/query/builder/ref-proxy.d.ts +28 -0
  59. package/dist/esm/query/builder/ref-proxy.js +96 -0
  60. package/dist/esm/query/builder/ref-proxy.js.map +1 -0
  61. package/dist/esm/query/builder/types.d.ts +80 -0
  62. package/dist/esm/query/compiler/evaluators.d.ts +11 -0
  63. package/dist/esm/query/compiler/evaluators.js +261 -0
  64. package/dist/esm/query/compiler/evaluators.js.map +1 -0
  65. package/dist/esm/query/compiler/group-by.d.ts +7 -0
  66. package/dist/esm/query/compiler/group-by.js +271 -0
  67. package/dist/esm/query/compiler/group-by.js.map +1 -0
  68. package/dist/esm/query/compiler/index.d.ts +15 -0
  69. package/dist/esm/query/compiler/index.js +181 -0
  70. package/dist/esm/query/compiler/index.js.map +1 -0
  71. package/dist/esm/query/compiler/joins.d.ts +11 -0
  72. package/dist/esm/query/compiler/joins.js +116 -0
  73. package/dist/esm/query/compiler/joins.js.map +1 -0
  74. package/dist/esm/query/compiler/order-by.d.ts +9 -0
  75. package/dist/esm/query/compiler/order-by.js +89 -0
  76. package/dist/esm/query/compiler/order-by.js.map +1 -0
  77. package/dist/esm/query/compiler/select.d.ts +15 -0
  78. package/dist/esm/query/compiler/select.js +57 -0
  79. package/dist/esm/query/compiler/select.js.map +1 -0
  80. package/dist/esm/query/index.d.ts +6 -5
  81. package/dist/esm/query/ir.d.ts +81 -0
  82. package/dist/esm/query/ir.js +57 -0
  83. package/dist/esm/query/ir.js.map +1 -0
  84. package/dist/esm/query/live-query-collection.d.ts +124 -0
  85. package/dist/esm/query/live-query-collection.js +224 -0
  86. package/dist/esm/query/live-query-collection.js.map +1 -0
  87. package/dist/esm/transactions.d.ts +13 -4
  88. package/dist/esm/transactions.js +20 -13
  89. package/dist/esm/transactions.js.map +1 -1
  90. package/dist/esm/types.d.ts +14 -1
  91. package/package.json +3 -4
  92. package/src/collection.ts +152 -129
  93. package/src/index.ts +0 -1
  94. package/src/query/builder/functions.ts +267 -0
  95. package/src/query/builder/index.ts +648 -0
  96. package/src/query/builder/ref-proxy.ts +156 -0
  97. package/src/query/builder/types.ts +278 -0
  98. package/src/query/compiler/evaluators.ts +315 -0
  99. package/src/query/compiler/group-by.ts +428 -0
  100. package/src/query/compiler/index.ts +276 -0
  101. package/src/query/compiler/joins.ts +228 -0
  102. package/src/query/compiler/order-by.ts +139 -0
  103. package/src/query/compiler/select.ts +173 -0
  104. package/src/query/index.ts +64 -5
  105. package/src/query/ir.ts +128 -0
  106. package/src/query/live-query-collection.ts +509 -0
  107. package/src/transactions.ts +34 -19
  108. package/src/types.ts +16 -1
  109. package/dist/cjs/query/compiled-query.cjs +0 -160
  110. package/dist/cjs/query/compiled-query.cjs.map +0 -1
  111. package/dist/cjs/query/compiled-query.d.cts +0 -20
  112. package/dist/cjs/query/evaluators.cjs +0 -161
  113. package/dist/cjs/query/evaluators.cjs.map +0 -1
  114. package/dist/cjs/query/evaluators.d.cts +0 -14
  115. package/dist/cjs/query/extractors.cjs +0 -122
  116. package/dist/cjs/query/extractors.cjs.map +0 -1
  117. package/dist/cjs/query/extractors.d.cts +0 -22
  118. package/dist/cjs/query/functions.cjs +0 -152
  119. package/dist/cjs/query/functions.cjs.map +0 -1
  120. package/dist/cjs/query/functions.d.cts +0 -21
  121. package/dist/cjs/query/group-by.cjs +0 -88
  122. package/dist/cjs/query/group-by.cjs.map +0 -1
  123. package/dist/cjs/query/group-by.d.cts +0 -40
  124. package/dist/cjs/query/joins.cjs +0 -141
  125. package/dist/cjs/query/joins.cjs.map +0 -1
  126. package/dist/cjs/query/joins.d.cts +0 -14
  127. package/dist/cjs/query/order-by.cjs +0 -185
  128. package/dist/cjs/query/order-by.cjs.map +0 -1
  129. package/dist/cjs/query/order-by.d.cts +0 -3
  130. package/dist/cjs/query/pipeline-compiler.cjs +0 -89
  131. package/dist/cjs/query/pipeline-compiler.cjs.map +0 -1
  132. package/dist/cjs/query/pipeline-compiler.d.cts +0 -10
  133. package/dist/cjs/query/query-builder.cjs +0 -307
  134. package/dist/cjs/query/query-builder.cjs.map +0 -1
  135. package/dist/cjs/query/query-builder.d.cts +0 -225
  136. package/dist/cjs/query/schema.d.cts +0 -100
  137. package/dist/cjs/query/select.cjs +0 -130
  138. package/dist/cjs/query/select.cjs.map +0 -1
  139. package/dist/cjs/query/select.d.cts +0 -3
  140. package/dist/cjs/query/types.d.cts +0 -189
  141. package/dist/cjs/query/utils.cjs +0 -154
  142. package/dist/cjs/query/utils.cjs.map +0 -1
  143. package/dist/cjs/query/utils.d.cts +0 -37
  144. package/dist/cjs/utils.cjs +0 -17
  145. package/dist/cjs/utils.cjs.map +0 -1
  146. package/dist/cjs/utils.d.cts +0 -3
  147. package/dist/esm/query/compiled-query.d.ts +0 -20
  148. package/dist/esm/query/compiled-query.js +0 -160
  149. package/dist/esm/query/compiled-query.js.map +0 -1
  150. package/dist/esm/query/evaluators.d.ts +0 -14
  151. package/dist/esm/query/evaluators.js +0 -161
  152. package/dist/esm/query/evaluators.js.map +0 -1
  153. package/dist/esm/query/extractors.d.ts +0 -22
  154. package/dist/esm/query/extractors.js +0 -122
  155. package/dist/esm/query/extractors.js.map +0 -1
  156. package/dist/esm/query/functions.d.ts +0 -21
  157. package/dist/esm/query/functions.js +0 -152
  158. package/dist/esm/query/functions.js.map +0 -1
  159. package/dist/esm/query/group-by.d.ts +0 -40
  160. package/dist/esm/query/group-by.js +0 -88
  161. package/dist/esm/query/group-by.js.map +0 -1
  162. package/dist/esm/query/joins.d.ts +0 -14
  163. package/dist/esm/query/joins.js +0 -141
  164. package/dist/esm/query/joins.js.map +0 -1
  165. package/dist/esm/query/order-by.d.ts +0 -3
  166. package/dist/esm/query/order-by.js +0 -185
  167. package/dist/esm/query/order-by.js.map +0 -1
  168. package/dist/esm/query/pipeline-compiler.d.ts +0 -10
  169. package/dist/esm/query/pipeline-compiler.js +0 -89
  170. package/dist/esm/query/pipeline-compiler.js.map +0 -1
  171. package/dist/esm/query/query-builder.d.ts +0 -225
  172. package/dist/esm/query/query-builder.js +0 -307
  173. package/dist/esm/query/query-builder.js.map +0 -1
  174. package/dist/esm/query/schema.d.ts +0 -100
  175. package/dist/esm/query/select.d.ts +0 -3
  176. package/dist/esm/query/select.js +0 -130
  177. package/dist/esm/query/select.js.map +0 -1
  178. package/dist/esm/query/types.d.ts +0 -189
  179. package/dist/esm/query/utils.d.ts +0 -37
  180. package/dist/esm/query/utils.js +0 -154
  181. package/dist/esm/query/utils.js.map +0 -1
  182. package/dist/esm/utils.d.ts +0 -3
  183. package/dist/esm/utils.js +0 -17
  184. package/dist/esm/utils.js.map +0 -1
  185. package/src/query/compiled-query.ts +0 -234
  186. package/src/query/evaluators.ts +0 -250
  187. package/src/query/extractors.ts +0 -214
  188. package/src/query/functions.ts +0 -297
  189. package/src/query/group-by.ts +0 -139
  190. package/src/query/joins.ts +0 -260
  191. package/src/query/order-by.ts +0 -264
  192. package/src/query/pipeline-compiler.ts +0 -149
  193. package/src/query/query-builder.ts +0 -902
  194. package/src/query/schema.ts +0 -268
  195. package/src/query/select.ts +0 -208
  196. package/src/query/types.ts +0 -418
  197. package/src/query/utils.ts +0 -245
  198. package/src/utils.ts +0 -15
@@ -1,902 +0,0 @@
1
- import type { Collection } from "../collection"
2
- import type {
3
- Comparator,
4
- ComparatorValue,
5
- Condition,
6
- From,
7
- JoinClause,
8
- Limit,
9
- LiteralValue,
10
- Offset,
11
- OrderBy,
12
- Query,
13
- Select,
14
- WhereCallback,
15
- WithQuery,
16
- } from "./schema.js"
17
- import type {
18
- Context,
19
- Flatten,
20
- InferResultTypeFromSelectTuple,
21
- Input,
22
- InputReference,
23
- PropertyReference,
24
- PropertyReferenceString,
25
- RemoveIndexSignature,
26
- Schema,
27
- } from "./types.js"
28
-
29
- type CollectionRef = { [K: string]: Collection<any> }
30
-
31
- export class BaseQueryBuilder<TContext extends Context<Schema>> {
32
- private readonly query: Partial<Query<TContext>> = {}
33
-
34
- /**
35
- * Create a new QueryBuilder instance.
36
- */
37
- constructor(query: Partial<Query<TContext>> = {}) {
38
- this.query = query
39
- }
40
-
41
- from<TCollectionRef extends CollectionRef>(
42
- collectionRef: TCollectionRef
43
- ): QueryBuilder<{
44
- baseSchema: Flatten<
45
- TContext[`baseSchema`] & {
46
- [K in keyof TCollectionRef & string]: RemoveIndexSignature<
47
- (TCollectionRef[keyof TCollectionRef] extends Collection<infer T>
48
- ? T
49
- : never) &
50
- Input
51
- >
52
- }
53
- >
54
- schema: Flatten<{
55
- [K in keyof TCollectionRef & string]: RemoveIndexSignature<
56
- (TCollectionRef[keyof TCollectionRef] extends Collection<infer T>
57
- ? T
58
- : never) &
59
- Input
60
- >
61
- }>
62
- default: keyof TCollectionRef & string
63
- }>
64
-
65
- from<
66
- T extends InputReference<{
67
- baseSchema: TContext[`baseSchema`]
68
- schema: TContext[`baseSchema`]
69
- }>,
70
- >(
71
- collection: T
72
- ): QueryBuilder<{
73
- baseSchema: TContext[`baseSchema`]
74
- schema: {
75
- [K in T]: RemoveIndexSignature<TContext[`baseSchema`][T]>
76
- }
77
- default: T
78
- }>
79
-
80
- from<
81
- T extends InputReference<{
82
- baseSchema: TContext[`baseSchema`]
83
- schema: TContext[`baseSchema`]
84
- }>,
85
- TAs extends string,
86
- >(
87
- collection: T,
88
- as: TAs
89
- ): QueryBuilder<{
90
- baseSchema: TContext[`baseSchema`]
91
- schema: {
92
- [K in TAs]: RemoveIndexSignature<TContext[`baseSchema`][T]>
93
- }
94
- default: TAs
95
- }>
96
-
97
- /**
98
- * Specify the collection to query from.
99
- * This is the first method that must be called in the chain.
100
- *
101
- * @param collection The collection name to query from
102
- * @param as Optional alias for the collection
103
- * @returns A new QueryBuilder with the from clause set
104
- */
105
- from<
106
- T extends
107
- | InputReference<{
108
- baseSchema: TContext[`baseSchema`]
109
- schema: TContext[`baseSchema`]
110
- }>
111
- | CollectionRef,
112
- TAs extends string | undefined,
113
- >(collection: T, as?: TAs) {
114
- // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
115
- if (typeof collection === `object` && collection !== null) {
116
- return this.fromCollectionRef(collection)
117
- } else if (typeof collection === `string`) {
118
- return this.fromInputReference(
119
- collection as InputReference<{
120
- baseSchema: TContext[`baseSchema`]
121
- schema: TContext[`baseSchema`]
122
- }>,
123
- as
124
- )
125
- } else {
126
- throw new Error(`Invalid collection type`)
127
- }
128
- }
129
-
130
- private fromCollectionRef<TCollectionRef extends CollectionRef>(
131
- collectionRef: TCollectionRef
132
- ) {
133
- const keys = Object.keys(collectionRef)
134
- if (keys.length !== 1) {
135
- throw new Error(`Expected exactly one key`)
136
- }
137
-
138
- const key = keys[0]!
139
- const collection = collectionRef[key]!
140
-
141
- const newBuilder = new BaseQueryBuilder()
142
- Object.assign(newBuilder.query, this.query)
143
- newBuilder.query.from = key as From<TContext>
144
- newBuilder.query.collections ??= {}
145
- newBuilder.query.collections[key] = collection
146
-
147
- return newBuilder as unknown as QueryBuilder<{
148
- baseSchema: TContext[`baseSchema`] & {
149
- [K in keyof TCollectionRef &
150
- string]: (TCollectionRef[keyof TCollectionRef] extends Collection<
151
- infer T
152
- >
153
- ? T
154
- : never) &
155
- Input
156
- }
157
- schema: {
158
- [K in keyof TCollectionRef &
159
- string]: (TCollectionRef[keyof TCollectionRef] extends Collection<
160
- infer T
161
- >
162
- ? T
163
- : never) &
164
- Input
165
- }
166
- default: keyof TCollectionRef & string
167
- }>
168
- }
169
-
170
- private fromInputReference<
171
- T extends InputReference<{
172
- baseSchema: TContext[`baseSchema`]
173
- schema: TContext[`baseSchema`]
174
- }>,
175
- TAs extends string | undefined,
176
- >(collection: T, as?: TAs) {
177
- const newBuilder = new BaseQueryBuilder()
178
- Object.assign(newBuilder.query, this.query)
179
- newBuilder.query.from = collection as From<TContext>
180
- if (as) {
181
- newBuilder.query.as = as
182
- }
183
-
184
- // Calculate the result type without deep nesting
185
- type ResultSchema = TAs extends undefined
186
- ? { [K in T]: TContext[`baseSchema`][T] }
187
- : { [K in string & TAs]: TContext[`baseSchema`][T] }
188
-
189
- type ResultDefault = TAs extends undefined ? T : string & TAs
190
-
191
- // Use simpler type assertion to avoid excessive depth
192
- return newBuilder as unknown as QueryBuilder<{
193
- baseSchema: TContext[`baseSchema`]
194
- schema: ResultSchema
195
- default: ResultDefault
196
- }>
197
- }
198
-
199
- /**
200
- * Specify what columns to select.
201
- * Overwrites any previous select clause.
202
- * Also supports callback functions that receive the row context and return selected data.
203
- *
204
- * @param selects The columns to select (can include callbacks)
205
- * @returns A new QueryBuilder with the select clause set
206
- */
207
- select<TSelects extends Array<Select<TContext>>>(
208
- this: QueryBuilder<TContext>,
209
- ...selects: TSelects
210
- ) {
211
- // Validate function calls in the selects
212
- // Need to use a type assertion to bypass deep recursive type checking
213
- const validatedSelects = selects.map((select) => {
214
- // If the select is an object with aliases, validate each value
215
- if (
216
- typeof select === `object` &&
217
- // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
218
- select !== null &&
219
- !Array.isArray(select)
220
- ) {
221
- const result: Record<string, any> = {}
222
-
223
- for (const [key, value] of Object.entries(select)) {
224
- // If it's a function call (object with a single key that is an allowed function name)
225
- if (
226
- typeof value === `object` &&
227
- value !== null &&
228
- !Array.isArray(value)
229
- ) {
230
- const keys = Object.keys(value)
231
- if (keys.length === 1) {
232
- const funcName = keys[0]!
233
- // List of allowed function names from AllowedFunctionName
234
- const allowedFunctions = [
235
- `SUM`,
236
- `COUNT`,
237
- `AVG`,
238
- `MIN`,
239
- `MAX`,
240
- `DATE`,
241
- `JSON_EXTRACT`,
242
- `JSON_EXTRACT_PATH`,
243
- `UPPER`,
244
- `LOWER`,
245
- `COALESCE`,
246
- `CONCAT`,
247
- `LENGTH`,
248
- `ORDER_INDEX`,
249
- ]
250
-
251
- if (!allowedFunctions.includes(funcName)) {
252
- console.warn(
253
- `Unsupported function: ${funcName}. Expected one of: ${allowedFunctions.join(`, `)}`
254
- )
255
- }
256
- }
257
- }
258
-
259
- result[key] = value
260
- }
261
-
262
- return result
263
- }
264
-
265
- return select
266
- })
267
-
268
- // Ensure we have an orderByIndex in the select if we have an orderBy
269
- // This is required if select is called after orderBy
270
- if (this._query.orderBy) {
271
- validatedSelects.push({ _orderByIndex: { ORDER_INDEX: `fractional` } })
272
- }
273
-
274
- const newBuilder = new BaseQueryBuilder<TContext>(
275
- (this as BaseQueryBuilder<TContext>).query
276
- )
277
- newBuilder.query.select = validatedSelects as Array<Select<TContext>>
278
-
279
- return newBuilder as QueryBuilder<
280
- Flatten<
281
- Omit<TContext, `result`> & {
282
- result: InferResultTypeFromSelectTuple<TContext, TSelects>
283
- }
284
- >
285
- >
286
- }
287
-
288
- /**
289
- * Add a where clause comparing two values.
290
- */
291
- where<T extends Comparator>(
292
- left: PropertyReferenceString<TContext> | LiteralValue,
293
- operator: T,
294
- right: ComparatorValue<T, TContext>
295
- ): QueryBuilder<TContext>
296
-
297
- /**
298
- * Add a where clause with a complete condition object.
299
- */
300
- where(condition: Condition<TContext>): QueryBuilder<TContext>
301
-
302
- /**
303
- * Add a where clause with a callback function.
304
- */
305
- where(callback: WhereCallback<TContext>): QueryBuilder<TContext>
306
-
307
- /**
308
- * Add a where clause to filter the results.
309
- * Can be called multiple times to add AND conditions.
310
- * Also supports callback functions that receive the row context.
311
- *
312
- * @param leftOrConditionOrCallback The left operand, complete condition, or callback function
313
- * @param operator Optional comparison operator
314
- * @param right Optional right operand
315
- * @returns A new QueryBuilder with the where clause added
316
- */
317
- where(
318
- leftOrConditionOrCallback: any,
319
- operator?: any,
320
- right?: any
321
- ): QueryBuilder<TContext> {
322
- // Create a new builder with a copy of the current query
323
- // Use simplistic approach to avoid deep type errors
324
- const newBuilder = new BaseQueryBuilder<TContext>()
325
- Object.assign(newBuilder.query, this.query)
326
-
327
- let condition: any
328
-
329
- // Determine if this is a callback, complete condition, or individual parts
330
- if (typeof leftOrConditionOrCallback === `function`) {
331
- // It's a callback function
332
- condition = leftOrConditionOrCallback
333
- } else if (operator !== undefined && right !== undefined) {
334
- // Create a condition from parts
335
- condition = [leftOrConditionOrCallback, operator, right]
336
- } else {
337
- // Use the provided condition directly
338
- condition = leftOrConditionOrCallback
339
- }
340
-
341
- // Where is always an array, so initialize or append
342
- if (!newBuilder.query.where) {
343
- newBuilder.query.where = [condition]
344
- } else {
345
- newBuilder.query.where = [...newBuilder.query.where, condition]
346
- }
347
-
348
- return newBuilder as unknown as QueryBuilder<TContext>
349
- }
350
-
351
- /**
352
- * Add a having clause comparing two values.
353
- * For filtering results after they have been grouped.
354
- */
355
- having(
356
- left: PropertyReferenceString<TContext> | LiteralValue,
357
- operator: Comparator,
358
- right: PropertyReferenceString<TContext> | LiteralValue
359
- ): QueryBuilder<TContext>
360
-
361
- /**
362
- * Add a having clause with a complete condition object.
363
- * For filtering results after they have been grouped.
364
- */
365
- having(condition: Condition<TContext>): QueryBuilder<TContext>
366
-
367
- /**
368
- * Add a having clause with a callback function.
369
- * For filtering results after they have been grouped.
370
- */
371
- having(callback: WhereCallback<TContext>): QueryBuilder<TContext>
372
-
373
- /**
374
- * Add a having clause to filter the grouped results.
375
- * Can be called multiple times to add AND conditions.
376
- * Also supports callback functions that receive the row context.
377
- *
378
- * @param leftOrConditionOrCallback The left operand, complete condition, or callback function
379
- * @param operator Optional comparison operator
380
- * @param right Optional right operand
381
- * @returns A new QueryBuilder with the having clause added
382
- */
383
- having(
384
- leftOrConditionOrCallback: any,
385
- operator?: any,
386
- right?: any
387
- ): QueryBuilder<TContext> {
388
- // Create a new builder with a copy of the current query
389
- const newBuilder = new BaseQueryBuilder<TContext>()
390
- Object.assign(newBuilder.query, this.query)
391
-
392
- let condition: any
393
-
394
- // Determine if this is a callback, complete condition, or individual parts
395
- if (typeof leftOrConditionOrCallback === `function`) {
396
- // It's a callback function
397
- condition = leftOrConditionOrCallback
398
- } else if (operator !== undefined && right !== undefined) {
399
- // Create a condition from parts
400
- condition = [leftOrConditionOrCallback, operator, right]
401
- } else {
402
- // Use the provided condition directly
403
- condition = leftOrConditionOrCallback
404
- }
405
-
406
- // Having is always an array, so initialize or append
407
- if (!newBuilder.query.having) {
408
- newBuilder.query.having = [condition]
409
- } else {
410
- newBuilder.query.having = [...newBuilder.query.having, condition]
411
- }
412
-
413
- return newBuilder as QueryBuilder<TContext>
414
- }
415
-
416
- /**
417
- * Add a join clause to the query using a CollectionRef.
418
- */
419
- join<TCollectionRef extends CollectionRef>(joinClause: {
420
- type: `inner` | `left` | `right` | `full` | `cross`
421
- from: TCollectionRef
422
- on: Condition<
423
- Flatten<{
424
- baseSchema: TContext[`baseSchema`]
425
- schema: TContext[`schema`] & {
426
- [K in keyof TCollectionRef & string]: RemoveIndexSignature<
427
- (TCollectionRef[keyof TCollectionRef] extends Collection<infer T>
428
- ? T
429
- : never) &
430
- Input
431
- >
432
- }
433
- }>
434
- >
435
- where?: Condition<
436
- Flatten<{
437
- baseSchema: TContext[`baseSchema`]
438
- schema: {
439
- [K in keyof TCollectionRef & string]: RemoveIndexSignature<
440
- (TCollectionRef[keyof TCollectionRef] extends Collection<infer T>
441
- ? T
442
- : never) &
443
- Input
444
- >
445
- }
446
- }>
447
- >
448
- }): QueryBuilder<
449
- Flatten<
450
- Omit<TContext, `schema`> & {
451
- schema: TContext[`schema`] & {
452
- [K in keyof TCollectionRef & string]: RemoveIndexSignature<
453
- (TCollectionRef[keyof TCollectionRef] extends Collection<infer T>
454
- ? T
455
- : never) &
456
- Input
457
- >
458
- }
459
- hasJoin: true
460
- }
461
- >
462
- >
463
-
464
- /**
465
- * Add a join clause to the query without specifying an alias.
466
- * The collection name will be used as the default alias.
467
- */
468
- join<
469
- T extends InputReference<{
470
- baseSchema: TContext[`baseSchema`]
471
- schema: TContext[`baseSchema`]
472
- }>,
473
- >(joinClause: {
474
- type: `inner` | `left` | `right` | `full` | `cross`
475
- from: T
476
- on: Condition<
477
- Flatten<{
478
- baseSchema: TContext[`baseSchema`]
479
- schema: TContext[`schema`] & {
480
- [K in T]: RemoveIndexSignature<TContext[`baseSchema`][T]>
481
- }
482
- }>
483
- >
484
- where?: Condition<
485
- Flatten<{
486
- baseSchema: TContext[`baseSchema`]
487
- schema: { [K in T]: RemoveIndexSignature<TContext[`baseSchema`][T]> }
488
- }>
489
- >
490
- }): QueryBuilder<
491
- Flatten<
492
- Omit<TContext, `schema`> & {
493
- schema: TContext[`schema`] & {
494
- [K in T]: RemoveIndexSignature<TContext[`baseSchema`][T]>
495
- }
496
- hasJoin: true
497
- }
498
- >
499
- >
500
-
501
- /**
502
- * Add a join clause to the query with a specified alias.
503
- */
504
- join<
505
- TFrom extends InputReference<{
506
- baseSchema: TContext[`baseSchema`]
507
- schema: TContext[`baseSchema`]
508
- }>,
509
- TAs extends string,
510
- >(joinClause: {
511
- type: `inner` | `left` | `right` | `full` | `cross`
512
- from: TFrom
513
- as: TAs
514
- on: Condition<
515
- Flatten<{
516
- baseSchema: TContext[`baseSchema`]
517
- schema: TContext[`schema`] & {
518
- [K in TAs]: RemoveIndexSignature<TContext[`baseSchema`][TFrom]>
519
- }
520
- }>
521
- >
522
- where?: Condition<
523
- Flatten<{
524
- baseSchema: TContext[`baseSchema`]
525
- schema: {
526
- [K in TAs]: RemoveIndexSignature<TContext[`baseSchema`][TFrom]>
527
- }
528
- }>
529
- >
530
- }): QueryBuilder<
531
- Flatten<
532
- Omit<TContext, `schema`> & {
533
- schema: TContext[`schema`] & {
534
- [K in TAs]: RemoveIndexSignature<TContext[`baseSchema`][TFrom]>
535
- }
536
- hasJoin: true
537
- }
538
- >
539
- >
540
-
541
- join<
542
- TFrom extends
543
- | InputReference<{
544
- baseSchema: TContext[`baseSchema`]
545
- schema: TContext[`baseSchema`]
546
- }>
547
- | CollectionRef,
548
- TAs extends string | undefined = undefined,
549
- >(joinClause: {
550
- type: `inner` | `left` | `right` | `full` | `cross`
551
- from: TFrom
552
- as?: TAs
553
- on: Condition<
554
- Flatten<{
555
- baseSchema: TContext[`baseSchema`]
556
- schema: TContext[`schema`] &
557
- (TFrom extends CollectionRef
558
- ? {
559
- [K in keyof TFrom & string]: RemoveIndexSignature<
560
- (TFrom[keyof TFrom] extends Collection<infer T> ? T : never) &
561
- Input
562
- >
563
- }
564
- : TFrom extends InputReference<infer TRef>
565
- ? {
566
- [K in keyof TRef & string]: RemoveIndexSignature<
567
- TRef[keyof TRef]
568
- >
569
- }
570
- : never)
571
- }>
572
- >
573
- where?: Condition<
574
- Flatten<{
575
- baseSchema: TContext[`baseSchema`]
576
- schema: TContext[`schema`] &
577
- (TFrom extends CollectionRef
578
- ? {
579
- [K in keyof TFrom & string]: RemoveIndexSignature<
580
- (TFrom[keyof TFrom] extends Collection<infer T> ? T : never) &
581
- Input
582
- >
583
- }
584
- : TFrom extends InputReference<infer TRef>
585
- ? {
586
- [K in keyof TRef & string]: RemoveIndexSignature<
587
- TRef[keyof TRef]
588
- >
589
- }
590
- : never)
591
- }>
592
- >
593
- }): QueryBuilder<any> {
594
- // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
595
- if (typeof joinClause.from === `object` && joinClause.from !== null) {
596
- return this.joinCollectionRef(
597
- joinClause as {
598
- type: `inner` | `left` | `right` | `full` | `cross`
599
- from: CollectionRef
600
- on: Condition<any>
601
- where?: Condition<any>
602
- }
603
- )
604
- } else {
605
- return this.joinInputReference(
606
- joinClause as {
607
- type: `inner` | `left` | `right` | `full` | `cross`
608
- from: InputReference<{
609
- baseSchema: TContext[`baseSchema`]
610
- schema: TContext[`baseSchema`]
611
- }>
612
- as?: TAs
613
- on: Condition<any>
614
- where?: Condition<any>
615
- }
616
- )
617
- }
618
- }
619
-
620
- private joinCollectionRef<TCollectionRef extends CollectionRef>(joinClause: {
621
- type: `inner` | `left` | `right` | `full` | `cross`
622
- from: TCollectionRef
623
- on: Condition<any>
624
- where?: Condition<any>
625
- }): QueryBuilder<any> {
626
- // Create a new builder with a copy of the current query
627
- const newBuilder = new BaseQueryBuilder<TContext>()
628
- Object.assign(newBuilder.query, this.query)
629
-
630
- // Get the collection key
631
- const keys = Object.keys(joinClause.from)
632
- if (keys.length !== 1) {
633
- throw new Error(`Expected exactly one key in CollectionRef`)
634
- }
635
- const key = keys[0]!
636
- const collection = joinClause.from[key]
637
- if (!collection) {
638
- throw new Error(`Collection not found for key: ${key}`)
639
- }
640
-
641
- // Create a copy of the join clause for the query
642
- const joinClauseCopy = {
643
- type: joinClause.type,
644
- from: key,
645
- on: joinClause.on,
646
- where: joinClause.where,
647
- } as JoinClause<TContext>
648
-
649
- // Add the join clause to the query
650
- if (!newBuilder.query.join) {
651
- newBuilder.query.join = [joinClauseCopy]
652
- } else {
653
- newBuilder.query.join = [...newBuilder.query.join, joinClauseCopy]
654
- }
655
-
656
- // Add the collection to the collections map
657
- newBuilder.query.collections ??= {}
658
- newBuilder.query.collections[key] = collection
659
-
660
- // Return the new builder with updated schema type
661
- return newBuilder as QueryBuilder<
662
- Flatten<
663
- Omit<TContext, `schema`> & {
664
- schema: TContext[`schema`] & {
665
- [K in keyof TCollectionRef & string]: RemoveIndexSignature<
666
- (TCollectionRef[keyof TCollectionRef] extends Collection<infer T>
667
- ? T
668
- : never) &
669
- Input
670
- >
671
- }
672
- }
673
- >
674
- >
675
- }
676
-
677
- private joinInputReference<
678
- TFrom extends InputReference<{
679
- baseSchema: TContext[`baseSchema`]
680
- schema: TContext[`baseSchema`]
681
- }>,
682
- TAs extends string | undefined = undefined,
683
- >(joinClause: {
684
- type: `inner` | `left` | `right` | `full` | `cross`
685
- from: TFrom
686
- as?: TAs
687
- on: Condition<any>
688
- where?: Condition<any>
689
- }): QueryBuilder<any> {
690
- // Create a new builder with a copy of the current query
691
- const newBuilder = new BaseQueryBuilder<TContext>()
692
- Object.assign(newBuilder.query, this.query)
693
-
694
- // Create a copy of the join clause for the query
695
- const joinClauseCopy = { ...joinClause } as JoinClause<TContext>
696
-
697
- // Add the join clause to the query
698
- if (!newBuilder.query.join) {
699
- newBuilder.query.join = [joinClauseCopy]
700
- } else {
701
- newBuilder.query.join = [...newBuilder.query.join, joinClauseCopy]
702
- }
703
-
704
- // Determine the alias or use the collection name as default
705
- const _effectiveAlias = joinClause.as ?? joinClause.from
706
-
707
- // Return the new builder with updated schema type
708
- return newBuilder as QueryBuilder<
709
- Flatten<
710
- Omit<TContext, `schema`> & {
711
- schema: TContext[`schema`] & {
712
- [K in typeof _effectiveAlias]: TContext[`baseSchema`][TFrom]
713
- }
714
- }
715
- >
716
- >
717
- }
718
-
719
- /**
720
- * Add an orderBy clause to sort the results.
721
- * Overwrites any previous orderBy clause.
722
- *
723
- * @param orderBy The order specification
724
- * @returns A new QueryBuilder with the orderBy clause set
725
- */
726
- orderBy(orderBy: OrderBy<TContext>): QueryBuilder<TContext> {
727
- // Create a new builder with a copy of the current query
728
- const newBuilder = new BaseQueryBuilder<TContext>()
729
- Object.assign(newBuilder.query, this.query)
730
-
731
- // Set the orderBy clause
732
- newBuilder.query.orderBy = orderBy
733
-
734
- // Ensure we have an orderByIndex in the select if we have an orderBy
735
- // This is required if select is called before orderBy
736
- newBuilder.query.select = [
737
- ...(newBuilder.query.select ?? []),
738
- { _orderByIndex: { ORDER_INDEX: `fractional` } },
739
- ]
740
-
741
- return newBuilder as QueryBuilder<TContext>
742
- }
743
-
744
- /**
745
- * Set a limit on the number of results returned.
746
- *
747
- * @param limit Maximum number of results to return
748
- * @returns A new QueryBuilder with the limit set
749
- */
750
- limit(limit: Limit<TContext>): QueryBuilder<TContext> {
751
- // Create a new builder with a copy of the current query
752
- const newBuilder = new BaseQueryBuilder<TContext>()
753
- Object.assign(newBuilder.query, this.query)
754
-
755
- // Set the limit
756
- newBuilder.query.limit = limit
757
-
758
- return newBuilder as QueryBuilder<TContext>
759
- }
760
-
761
- /**
762
- * Set an offset to skip a number of results.
763
- *
764
- * @param offset Number of results to skip
765
- * @returns A new QueryBuilder with the offset set
766
- */
767
- offset(offset: Offset<TContext>): QueryBuilder<TContext> {
768
- // Create a new builder with a copy of the current query
769
- const newBuilder = new BaseQueryBuilder<TContext>()
770
- Object.assign(newBuilder.query, this.query)
771
-
772
- // Set the offset
773
- newBuilder.query.offset = offset
774
-
775
- return newBuilder as QueryBuilder<TContext>
776
- }
777
-
778
- /**
779
- * Add a groupBy clause to group the results by one or more columns.
780
- *
781
- * @param groupBy The column(s) to group by
782
- * @returns A new QueryBuilder with the groupBy clause set
783
- */
784
- groupBy(
785
- groupBy: PropertyReference<TContext> | Array<PropertyReference<TContext>>
786
- ): QueryBuilder<TContext> {
787
- // Create a new builder with a copy of the current query
788
- const newBuilder = new BaseQueryBuilder<TContext>()
789
- Object.assign(newBuilder.query, this.query)
790
-
791
- // Set the groupBy clause
792
- newBuilder.query.groupBy = groupBy
793
-
794
- return newBuilder as QueryBuilder<TContext>
795
- }
796
-
797
- /**
798
- * Define a Common Table Expression (CTE) that can be referenced in the main query.
799
- * This allows referencing the CTE by name in subsequent from/join clauses.
800
- *
801
- * @param name The name of the CTE
802
- * @param queryBuilderCallback A function that builds the CTE query
803
- * @returns A new QueryBuilder with the CTE added
804
- */
805
- with<TName extends string, TResult = Record<string, unknown>>(
806
- name: TName,
807
- queryBuilderCallback: (
808
- builder: InitialQueryBuilder<{
809
- baseSchema: TContext[`baseSchema`]
810
- schema: {}
811
- }>
812
- ) => QueryBuilder<any>
813
- ): InitialQueryBuilder<{
814
- baseSchema: TContext[`baseSchema`] & { [K in TName]: TResult }
815
- schema: TContext[`schema`]
816
- }> {
817
- // Create a new builder with a copy of the current query
818
- const newBuilder = new BaseQueryBuilder<TContext>()
819
- Object.assign(newBuilder.query, this.query)
820
-
821
- // Create a new builder for the CTE
822
- const cteBuilder = new BaseQueryBuilder<{
823
- baseSchema: TContext[`baseSchema`]
824
- schema: {}
825
- }>()
826
-
827
- // Get the CTE query from the callback
828
- const cteQueryBuilder = queryBuilderCallback(
829
- cteBuilder as InitialQueryBuilder<{
830
- baseSchema: TContext[`baseSchema`]
831
- schema: {}
832
- }>
833
- )
834
-
835
- // Get the query from the builder
836
- const cteQuery = cteQueryBuilder._query
837
-
838
- // Add an 'as' property to the CTE
839
- const withQuery: WithQuery<any> = {
840
- ...cteQuery,
841
- as: name,
842
- }
843
-
844
- // Add the CTE to the with array
845
- if (!newBuilder.query.with) {
846
- newBuilder.query.with = [withQuery]
847
- } else {
848
- newBuilder.query.with = [...newBuilder.query.with, withQuery]
849
- }
850
-
851
- // Use a type cast that simplifies the type structure to avoid recursion
852
- return newBuilder as unknown as InitialQueryBuilder<{
853
- baseSchema: TContext[`baseSchema`] & { [K in TName]: TResult }
854
- schema: TContext[`schema`]
855
- }>
856
- }
857
-
858
- get _query(): Query<TContext> {
859
- return this.query as Query<TContext>
860
- }
861
- }
862
-
863
- export type InitialQueryBuilder<TContext extends Context<Schema>> = Pick<
864
- BaseQueryBuilder<TContext>,
865
- `from` | `with`
866
- >
867
-
868
- export type QueryBuilder<TContext extends Context<Schema>> = Omit<
869
- BaseQueryBuilder<TContext>,
870
- `from`
871
- >
872
-
873
- /**
874
- * Create a new query builder with the given schema
875
- */
876
- export function queryBuilder<TBaseSchema extends Schema = {}>() {
877
- return new BaseQueryBuilder<{
878
- baseSchema: TBaseSchema
879
- schema: {}
880
- }>() as InitialQueryBuilder<{
881
- baseSchema: TBaseSchema
882
- schema: {}
883
- }>
884
- }
885
-
886
- export type ResultsFromContext<TContext extends Context<Schema>> = Flatten<
887
- TContext[`result`] extends object
888
- ? TContext[`result`] // If there is a select we will have a result type
889
- : TContext[`hasJoin`] extends true
890
- ? TContext[`schema`] // If there is a join, the query returns the namespaced schema
891
- : TContext[`default`] extends keyof TContext[`schema`]
892
- ? TContext[`schema`][TContext[`default`]] // If there is no join we return the flat default schema
893
- : never // Should never happen
894
- >
895
-
896
- export type ResultFromQueryBuilder<TQueryBuilder> = Flatten<
897
- TQueryBuilder extends QueryBuilder<infer C>
898
- ? C extends { result: infer R }
899
- ? R
900
- : never
901
- : never
902
- >