@tanstack/db 0.0.27 → 0.0.29

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 (164) hide show
  1. package/dist/cjs/change-events.cjs +141 -0
  2. package/dist/cjs/change-events.cjs.map +1 -0
  3. package/dist/cjs/change-events.d.cts +49 -0
  4. package/dist/cjs/collection.cjs +234 -86
  5. package/dist/cjs/collection.cjs.map +1 -1
  6. package/dist/cjs/collection.d.cts +95 -20
  7. package/dist/cjs/errors.cjs +509 -1
  8. package/dist/cjs/errors.cjs.map +1 -1
  9. package/dist/cjs/errors.d.cts +225 -1
  10. package/dist/cjs/index.cjs +82 -3
  11. package/dist/cjs/index.cjs.map +1 -1
  12. package/dist/cjs/index.d.cts +5 -1
  13. package/dist/cjs/indexes/auto-index.cjs +64 -0
  14. package/dist/cjs/indexes/auto-index.cjs.map +1 -0
  15. package/dist/cjs/indexes/auto-index.d.cts +9 -0
  16. package/dist/cjs/indexes/base-index.cjs +46 -0
  17. package/dist/cjs/indexes/base-index.cjs.map +1 -0
  18. package/dist/cjs/indexes/base-index.d.cts +54 -0
  19. package/dist/cjs/indexes/index-options.d.cts +13 -0
  20. package/dist/cjs/indexes/lazy-index.cjs +193 -0
  21. package/dist/cjs/indexes/lazy-index.cjs.map +1 -0
  22. package/dist/cjs/indexes/lazy-index.d.cts +96 -0
  23. package/dist/cjs/indexes/ordered-index.cjs +227 -0
  24. package/dist/cjs/indexes/ordered-index.cjs.map +1 -0
  25. package/dist/cjs/indexes/ordered-index.d.cts +72 -0
  26. package/dist/cjs/local-storage.cjs +9 -15
  27. package/dist/cjs/local-storage.cjs.map +1 -1
  28. package/dist/cjs/query/builder/functions.cjs +11 -0
  29. package/dist/cjs/query/builder/functions.cjs.map +1 -1
  30. package/dist/cjs/query/builder/functions.d.cts +4 -0
  31. package/dist/cjs/query/builder/index.cjs +6 -7
  32. package/dist/cjs/query/builder/index.cjs.map +1 -1
  33. package/dist/cjs/query/builder/ref-proxy.cjs +37 -0
  34. package/dist/cjs/query/builder/ref-proxy.cjs.map +1 -1
  35. package/dist/cjs/query/builder/ref-proxy.d.cts +12 -0
  36. package/dist/cjs/query/compiler/evaluators.cjs +83 -58
  37. package/dist/cjs/query/compiler/evaluators.cjs.map +1 -1
  38. package/dist/cjs/query/compiler/evaluators.d.cts +8 -0
  39. package/dist/cjs/query/compiler/expressions.cjs +61 -0
  40. package/dist/cjs/query/compiler/expressions.cjs.map +1 -0
  41. package/dist/cjs/query/compiler/expressions.d.cts +25 -0
  42. package/dist/cjs/query/compiler/group-by.cjs +5 -10
  43. package/dist/cjs/query/compiler/group-by.cjs.map +1 -1
  44. package/dist/cjs/query/compiler/index.cjs +23 -17
  45. package/dist/cjs/query/compiler/index.cjs.map +1 -1
  46. package/dist/cjs/query/compiler/index.d.cts +12 -3
  47. package/dist/cjs/query/compiler/joins.cjs +61 -12
  48. package/dist/cjs/query/compiler/joins.cjs.map +1 -1
  49. package/dist/cjs/query/compiler/order-by.cjs +4 -34
  50. package/dist/cjs/query/compiler/order-by.cjs.map +1 -1
  51. package/dist/cjs/query/compiler/types.d.cts +2 -2
  52. package/dist/cjs/query/live-query-collection.cjs +54 -12
  53. package/dist/cjs/query/live-query-collection.cjs.map +1 -1
  54. package/dist/cjs/query/optimizer.cjs +45 -7
  55. package/dist/cjs/query/optimizer.cjs.map +1 -1
  56. package/dist/cjs/query/optimizer.d.cts +13 -3
  57. package/dist/cjs/transactions.cjs +5 -4
  58. package/dist/cjs/transactions.cjs.map +1 -1
  59. package/dist/cjs/types.d.cts +31 -0
  60. package/dist/cjs/utils/array-utils.cjs +18 -0
  61. package/dist/cjs/utils/array-utils.cjs.map +1 -0
  62. package/dist/cjs/utils/array-utils.d.cts +8 -0
  63. package/dist/cjs/utils/comparison.cjs +52 -0
  64. package/dist/cjs/utils/comparison.cjs.map +1 -0
  65. package/dist/cjs/utils/comparison.d.cts +11 -0
  66. package/dist/cjs/utils/index-optimization.cjs +270 -0
  67. package/dist/cjs/utils/index-optimization.cjs.map +1 -0
  68. package/dist/cjs/utils/index-optimization.d.cts +29 -0
  69. package/dist/esm/change-events.d.ts +49 -0
  70. package/dist/esm/change-events.js +141 -0
  71. package/dist/esm/change-events.js.map +1 -0
  72. package/dist/esm/collection.d.ts +95 -20
  73. package/dist/esm/collection.js +232 -84
  74. package/dist/esm/collection.js.map +1 -1
  75. package/dist/esm/errors.d.ts +225 -1
  76. package/dist/esm/errors.js +510 -2
  77. package/dist/esm/errors.js.map +1 -1
  78. package/dist/esm/index.d.ts +5 -1
  79. package/dist/esm/index.js +81 -2
  80. package/dist/esm/index.js.map +1 -1
  81. package/dist/esm/indexes/auto-index.d.ts +9 -0
  82. package/dist/esm/indexes/auto-index.js +64 -0
  83. package/dist/esm/indexes/auto-index.js.map +1 -0
  84. package/dist/esm/indexes/base-index.d.ts +54 -0
  85. package/dist/esm/indexes/base-index.js +46 -0
  86. package/dist/esm/indexes/base-index.js.map +1 -0
  87. package/dist/esm/indexes/index-options.d.ts +13 -0
  88. package/dist/esm/indexes/lazy-index.d.ts +96 -0
  89. package/dist/esm/indexes/lazy-index.js +193 -0
  90. package/dist/esm/indexes/lazy-index.js.map +1 -0
  91. package/dist/esm/indexes/ordered-index.d.ts +72 -0
  92. package/dist/esm/indexes/ordered-index.js +227 -0
  93. package/dist/esm/indexes/ordered-index.js.map +1 -0
  94. package/dist/esm/local-storage.js +9 -15
  95. package/dist/esm/local-storage.js.map +1 -1
  96. package/dist/esm/query/builder/functions.d.ts +4 -0
  97. package/dist/esm/query/builder/functions.js +11 -0
  98. package/dist/esm/query/builder/functions.js.map +1 -1
  99. package/dist/esm/query/builder/index.js +6 -7
  100. package/dist/esm/query/builder/index.js.map +1 -1
  101. package/dist/esm/query/builder/ref-proxy.d.ts +12 -0
  102. package/dist/esm/query/builder/ref-proxy.js +37 -0
  103. package/dist/esm/query/builder/ref-proxy.js.map +1 -1
  104. package/dist/esm/query/compiler/evaluators.d.ts +8 -0
  105. package/dist/esm/query/compiler/evaluators.js +84 -59
  106. package/dist/esm/query/compiler/evaluators.js.map +1 -1
  107. package/dist/esm/query/compiler/expressions.d.ts +25 -0
  108. package/dist/esm/query/compiler/expressions.js +61 -0
  109. package/dist/esm/query/compiler/expressions.js.map +1 -0
  110. package/dist/esm/query/compiler/group-by.js +5 -10
  111. package/dist/esm/query/compiler/group-by.js.map +1 -1
  112. package/dist/esm/query/compiler/index.d.ts +12 -3
  113. package/dist/esm/query/compiler/index.js +23 -17
  114. package/dist/esm/query/compiler/index.js.map +1 -1
  115. package/dist/esm/query/compiler/joins.js +61 -12
  116. package/dist/esm/query/compiler/joins.js.map +1 -1
  117. package/dist/esm/query/compiler/order-by.js +1 -31
  118. package/dist/esm/query/compiler/order-by.js.map +1 -1
  119. package/dist/esm/query/compiler/types.d.ts +2 -2
  120. package/dist/esm/query/live-query-collection.js +54 -12
  121. package/dist/esm/query/live-query-collection.js.map +1 -1
  122. package/dist/esm/query/optimizer.d.ts +13 -3
  123. package/dist/esm/query/optimizer.js +40 -2
  124. package/dist/esm/query/optimizer.js.map +1 -1
  125. package/dist/esm/transactions.js +5 -4
  126. package/dist/esm/transactions.js.map +1 -1
  127. package/dist/esm/types.d.ts +31 -0
  128. package/dist/esm/utils/array-utils.d.ts +8 -0
  129. package/dist/esm/utils/array-utils.js +18 -0
  130. package/dist/esm/utils/array-utils.js.map +1 -0
  131. package/dist/esm/utils/comparison.d.ts +11 -0
  132. package/dist/esm/utils/comparison.js +52 -0
  133. package/dist/esm/utils/comparison.js.map +1 -0
  134. package/dist/esm/utils/index-optimization.d.ts +29 -0
  135. package/dist/esm/utils/index-optimization.js +270 -0
  136. package/dist/esm/utils/index-optimization.js.map +1 -0
  137. package/package.json +1 -1
  138. package/src/change-events.ts +257 -0
  139. package/src/collection.ts +318 -105
  140. package/src/errors.ts +545 -1
  141. package/src/index.ts +7 -1
  142. package/src/indexes/auto-index.ts +108 -0
  143. package/src/indexes/base-index.ts +119 -0
  144. package/src/indexes/index-options.ts +42 -0
  145. package/src/indexes/lazy-index.ts +251 -0
  146. package/src/indexes/ordered-index.ts +305 -0
  147. package/src/local-storage.ts +16 -17
  148. package/src/query/builder/functions.ts +14 -0
  149. package/src/query/builder/index.ts +12 -7
  150. package/src/query/builder/ref-proxy.ts +65 -0
  151. package/src/query/compiler/evaluators.ts +114 -62
  152. package/src/query/compiler/expressions.ts +92 -0
  153. package/src/query/compiler/group-by.ts +10 -10
  154. package/src/query/compiler/index.ts +52 -22
  155. package/src/query/compiler/joins.ts +114 -15
  156. package/src/query/compiler/order-by.ts +1 -45
  157. package/src/query/compiler/types.ts +2 -2
  158. package/src/query/live-query-collection.ts +95 -15
  159. package/src/query/optimizer.ts +94 -5
  160. package/src/transactions.ts +10 -4
  161. package/src/types.ts +38 -0
  162. package/src/utils/array-utils.ts +28 -0
  163. package/src/utils/comparison.ts +79 -0
  164. package/src/utils/index-optimization.ts +546 -0
@@ -1,25 +1,34 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
+ const errors = require("../../errors.cjs");
3
4
  function compileExpression(expr) {
5
+ const compiledFn = compileExpressionInternal(expr, false);
6
+ return compiledFn;
7
+ }
8
+ function compileSingleRowExpression(expr) {
9
+ const compiledFn = compileExpressionInternal(expr, true);
10
+ return compiledFn;
11
+ }
12
+ function compileExpressionInternal(expr, isSingleRow) {
4
13
  switch (expr.type) {
5
14
  case `val`: {
6
15
  const value = expr.value;
7
16
  return () => value;
8
17
  }
9
18
  case `ref`: {
10
- return compileRef(expr);
19
+ return isSingleRow ? compileSingleRowRef(expr) : compileRef(expr);
11
20
  }
12
21
  case `func`: {
13
- return compileFunction(expr);
22
+ return compileFunction(expr, isSingleRow);
14
23
  }
15
24
  default:
16
- throw new Error(`Unknown expression type: ${expr.type}`);
25
+ throw new errors.UnknownExpressionTypeError(expr.type);
17
26
  }
18
27
  }
19
28
  function compileRef(ref) {
20
29
  const [tableAlias, ...propertyPath] = ref.path;
21
30
  if (!tableAlias) {
22
- throw new Error(`Reference path cannot be empty`);
31
+ throw new errors.EmptyReferencePathError();
23
32
  }
24
33
  if (propertyPath.length === 0) {
25
34
  return (namespacedRow) => namespacedRow[tableAlias];
@@ -46,69 +55,84 @@ function compileRef(ref) {
46
55
  };
47
56
  }
48
57
  }
49
- function compileFunction(func) {
50
- const compiledArgs = func.args.map(compileExpression);
58
+ function compileSingleRowRef(ref) {
59
+ const propertyPath = ref.path;
60
+ return (item) => {
61
+ let value = item;
62
+ for (const prop of propertyPath) {
63
+ if (value == null) {
64
+ return value;
65
+ }
66
+ value = value[prop];
67
+ }
68
+ return value;
69
+ };
70
+ }
71
+ function compileFunction(func, isSingleRow) {
72
+ const compiledArgs = func.args.map(
73
+ (arg) => compileExpressionInternal(arg, isSingleRow)
74
+ );
51
75
  switch (func.name) {
52
76
  // Comparison operators
53
77
  case `eq`: {
54
78
  const argA = compiledArgs[0];
55
79
  const argB = compiledArgs[1];
56
- return (namespacedRow) => {
57
- const a = argA(namespacedRow);
58
- const b = argB(namespacedRow);
80
+ return (data) => {
81
+ const a = argA(data);
82
+ const b = argB(data);
59
83
  return a === b;
60
84
  };
61
85
  }
62
86
  case `gt`: {
63
87
  const argA = compiledArgs[0];
64
88
  const argB = compiledArgs[1];
65
- return (namespacedRow) => {
66
- const a = argA(namespacedRow);
67
- const b = argB(namespacedRow);
89
+ return (data) => {
90
+ const a = argA(data);
91
+ const b = argB(data);
68
92
  return a > b;
69
93
  };
70
94
  }
71
95
  case `gte`: {
72
96
  const argA = compiledArgs[0];
73
97
  const argB = compiledArgs[1];
74
- return (namespacedRow) => {
75
- const a = argA(namespacedRow);
76
- const b = argB(namespacedRow);
98
+ return (data) => {
99
+ const a = argA(data);
100
+ const b = argB(data);
77
101
  return a >= b;
78
102
  };
79
103
  }
80
104
  case `lt`: {
81
105
  const argA = compiledArgs[0];
82
106
  const argB = compiledArgs[1];
83
- return (namespacedRow) => {
84
- const a = argA(namespacedRow);
85
- const b = argB(namespacedRow);
107
+ return (data) => {
108
+ const a = argA(data);
109
+ const b = argB(data);
86
110
  return a < b;
87
111
  };
88
112
  }
89
113
  case `lte`: {
90
114
  const argA = compiledArgs[0];
91
115
  const argB = compiledArgs[1];
92
- return (namespacedRow) => {
93
- const a = argA(namespacedRow);
94
- const b = argB(namespacedRow);
116
+ return (data) => {
117
+ const a = argA(data);
118
+ const b = argB(data);
95
119
  return a <= b;
96
120
  };
97
121
  }
98
122
  // Boolean operators
99
123
  case `and`:
100
- return (namespacedRow) => {
124
+ return (data) => {
101
125
  for (const compiledArg of compiledArgs) {
102
- if (!compiledArg(namespacedRow)) {
126
+ if (!compiledArg(data)) {
103
127
  return false;
104
128
  }
105
129
  }
106
130
  return true;
107
131
  };
108
132
  case `or`:
109
- return (namespacedRow) => {
133
+ return (data) => {
110
134
  for (const compiledArg of compiledArgs) {
111
- if (compiledArg(namespacedRow)) {
135
+ if (compiledArg(data)) {
112
136
  return true;
113
137
  }
114
138
  }
@@ -116,15 +140,15 @@ function compileFunction(func) {
116
140
  };
117
141
  case `not`: {
118
142
  const arg = compiledArgs[0];
119
- return (namespacedRow) => !arg(namespacedRow);
143
+ return (data) => !arg(data);
120
144
  }
121
145
  // Array operators
122
146
  case `in`: {
123
147
  const valueEvaluator = compiledArgs[0];
124
148
  const arrayEvaluator = compiledArgs[1];
125
- return (namespacedRow) => {
126
- const value = valueEvaluator(namespacedRow);
127
- const array = arrayEvaluator(namespacedRow);
149
+ return (data) => {
150
+ const value = valueEvaluator(data);
151
+ const array = arrayEvaluator(data);
128
152
  if (!Array.isArray(array)) {
129
153
  return false;
130
154
  }
@@ -135,40 +159,40 @@ function compileFunction(func) {
135
159
  case `like`: {
136
160
  const valueEvaluator = compiledArgs[0];
137
161
  const patternEvaluator = compiledArgs[1];
138
- return (namespacedRow) => {
139
- const value = valueEvaluator(namespacedRow);
140
- const pattern = patternEvaluator(namespacedRow);
162
+ return (data) => {
163
+ const value = valueEvaluator(data);
164
+ const pattern = patternEvaluator(data);
141
165
  return evaluateLike(value, pattern, false);
142
166
  };
143
167
  }
144
168
  case `ilike`: {
145
169
  const valueEvaluator = compiledArgs[0];
146
170
  const patternEvaluator = compiledArgs[1];
147
- return (namespacedRow) => {
148
- const value = valueEvaluator(namespacedRow);
149
- const pattern = patternEvaluator(namespacedRow);
171
+ return (data) => {
172
+ const value = valueEvaluator(data);
173
+ const pattern = patternEvaluator(data);
150
174
  return evaluateLike(value, pattern, true);
151
175
  };
152
176
  }
153
177
  // String functions
154
178
  case `upper`: {
155
179
  const arg = compiledArgs[0];
156
- return (namespacedRow) => {
157
- const value = arg(namespacedRow);
180
+ return (data) => {
181
+ const value = arg(data);
158
182
  return typeof value === `string` ? value.toUpperCase() : value;
159
183
  };
160
184
  }
161
185
  case `lower`: {
162
186
  const arg = compiledArgs[0];
163
- return (namespacedRow) => {
164
- const value = arg(namespacedRow);
187
+ return (data) => {
188
+ const value = arg(data);
165
189
  return typeof value === `string` ? value.toLowerCase() : value;
166
190
  };
167
191
  }
168
192
  case `length`: {
169
193
  const arg = compiledArgs[0];
170
- return (namespacedRow) => {
171
- const value = arg(namespacedRow);
194
+ return (data) => {
195
+ const value = arg(data);
172
196
  if (typeof value === `string`) {
173
197
  return value.length;
174
198
  }
@@ -179,9 +203,9 @@ function compileFunction(func) {
179
203
  };
180
204
  }
181
205
  case `concat`:
182
- return (namespacedRow) => {
206
+ return (data) => {
183
207
  return compiledArgs.map((evaluator) => {
184
- const arg = evaluator(namespacedRow);
208
+ const arg = evaluator(data);
185
209
  try {
186
210
  return String(arg ?? ``);
187
211
  } catch {
@@ -194,9 +218,9 @@ function compileFunction(func) {
194
218
  }).join(``);
195
219
  };
196
220
  case `coalesce`:
197
- return (namespacedRow) => {
221
+ return (data) => {
198
222
  for (const evaluator of compiledArgs) {
199
- const value = evaluator(namespacedRow);
223
+ const value = evaluator(data);
200
224
  if (value !== null && value !== void 0) {
201
225
  return value;
202
226
  }
@@ -207,42 +231,42 @@ function compileFunction(func) {
207
231
  case `add`: {
208
232
  const argA = compiledArgs[0];
209
233
  const argB = compiledArgs[1];
210
- return (namespacedRow) => {
211
- const a = argA(namespacedRow);
212
- const b = argB(namespacedRow);
234
+ return (data) => {
235
+ const a = argA(data);
236
+ const b = argB(data);
213
237
  return (a ?? 0) + (b ?? 0);
214
238
  };
215
239
  }
216
240
  case `subtract`: {
217
241
  const argA = compiledArgs[0];
218
242
  const argB = compiledArgs[1];
219
- return (namespacedRow) => {
220
- const a = argA(namespacedRow);
221
- const b = argB(namespacedRow);
243
+ return (data) => {
244
+ const a = argA(data);
245
+ const b = argB(data);
222
246
  return (a ?? 0) - (b ?? 0);
223
247
  };
224
248
  }
225
249
  case `multiply`: {
226
250
  const argA = compiledArgs[0];
227
251
  const argB = compiledArgs[1];
228
- return (namespacedRow) => {
229
- const a = argA(namespacedRow);
230
- const b = argB(namespacedRow);
252
+ return (data) => {
253
+ const a = argA(data);
254
+ const b = argB(data);
231
255
  return (a ?? 0) * (b ?? 0);
232
256
  };
233
257
  }
234
258
  case `divide`: {
235
259
  const argA = compiledArgs[0];
236
260
  const argB = compiledArgs[1];
237
- return (namespacedRow) => {
238
- const a = argA(namespacedRow);
239
- const b = argB(namespacedRow);
261
+ return (data) => {
262
+ const a = argA(data);
263
+ const b = argB(data);
240
264
  const divisor = b ?? 0;
241
265
  return divisor !== 0 ? (a ?? 0) / divisor : null;
242
266
  };
243
267
  }
244
268
  default:
245
- throw new Error(`Unknown function: ${func.name}`);
269
+ throw new errors.UnknownFunctionError(func.name);
246
270
  }
247
271
  }
248
272
  function evaluateLike(value, pattern, caseInsensitive) {
@@ -258,4 +282,5 @@ function evaluateLike(value, pattern, caseInsensitive) {
258
282
  return regex.test(searchValue);
259
283
  }
260
284
  exports.compileExpression = compileExpression;
285
+ exports.compileSingleRowExpression = compileSingleRowExpression;
261
286
  //# sourceMappingURL=evaluators.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"evaluators.cjs","sources":["../../../../src/query/compiler/evaluators.ts"],"sourcesContent":["import type { BasicExpression, Func, PropRef } from \"../ir.js\"\nimport type { NamespacedRow } from \"../../types.js\"\n\n/**\n * Compiled expression evaluator function type\n */\nexport type CompiledExpression = (namespacedRow: NamespacedRow) => any\n\n/**\n * Compiles an expression into an optimized evaluator function.\n * This eliminates branching during evaluation by pre-compiling the expression structure.\n */\nexport function compileExpression(expr: BasicExpression): CompiledExpression {\n switch (expr.type) {\n case `val`: {\n // For constant values, return a function that just returns the value\n const value = expr.value\n return () => value\n }\n\n case `ref`: {\n // For references, pre-compile the property path navigation\n return compileRef(expr)\n }\n\n case `func`: {\n // For functions, pre-compile the function and its arguments\n return compileFunction(expr)\n }\n\n default:\n throw new Error(`Unknown expression type: ${(expr as any).type}`)\n }\n}\n\n/**\n * Compiles a reference expression into an optimized evaluator\n */\nfunction compileRef(ref: PropRef): CompiledExpression {\n const [tableAlias, ...propertyPath] = ref.path\n\n if (!tableAlias) {\n throw new Error(`Reference path cannot be empty`)\n }\n\n // Pre-compile the property path navigation\n if (propertyPath.length === 0) {\n // Simple table reference\n return (namespacedRow) => namespacedRow[tableAlias]\n } else if (propertyPath.length === 1) {\n // Single property access - most common case\n const prop = propertyPath[0]!\n return (namespacedRow) => {\n const tableData = namespacedRow[tableAlias]\n return tableData?.[prop]\n }\n } else {\n // Multiple property navigation\n return (namespacedRow) => {\n const tableData = namespacedRow[tableAlias]\n if (tableData === undefined) {\n return undefined\n }\n\n let value: any = tableData\n for (const prop of propertyPath) {\n if (value == null) {\n return value\n }\n value = value[prop]\n }\n return value\n }\n }\n}\n\n/**\n * Compiles a function expression into an optimized evaluator\n */\nfunction compileFunction(func: Func): CompiledExpression {\n // Pre-compile all arguments\n const compiledArgs = func.args.map(compileExpression)\n\n switch (func.name) {\n // Comparison operators\n case `eq`: {\n const argA = compiledArgs[0]!\n const argB = compiledArgs[1]!\n return (namespacedRow) => {\n const a = argA(namespacedRow)\n const b = argB(namespacedRow)\n return a === b\n }\n }\n case `gt`: {\n const argA = compiledArgs[0]!\n const argB = compiledArgs[1]!\n return (namespacedRow) => {\n const a = argA(namespacedRow)\n const b = argB(namespacedRow)\n return a > b\n }\n }\n case `gte`: {\n const argA = compiledArgs[0]!\n const argB = compiledArgs[1]!\n return (namespacedRow) => {\n const a = argA(namespacedRow)\n const b = argB(namespacedRow)\n return a >= b\n }\n }\n case `lt`: {\n const argA = compiledArgs[0]!\n const argB = compiledArgs[1]!\n return (namespacedRow) => {\n const a = argA(namespacedRow)\n const b = argB(namespacedRow)\n return a < b\n }\n }\n case `lte`: {\n const argA = compiledArgs[0]!\n const argB = compiledArgs[1]!\n return (namespacedRow) => {\n const a = argA(namespacedRow)\n const b = argB(namespacedRow)\n return a <= b\n }\n }\n\n // Boolean operators\n case `and`:\n return (namespacedRow) => {\n for (const compiledArg of compiledArgs) {\n if (!compiledArg(namespacedRow)) {\n return false\n }\n }\n return true\n }\n case `or`:\n return (namespacedRow) => {\n for (const compiledArg of compiledArgs) {\n if (compiledArg(namespacedRow)) {\n return true\n }\n }\n return false\n }\n case `not`: {\n const arg = compiledArgs[0]!\n return (namespacedRow) => !arg(namespacedRow)\n }\n\n // Array operators\n case `in`: {\n const valueEvaluator = compiledArgs[0]!\n const arrayEvaluator = compiledArgs[1]!\n return (namespacedRow) => {\n const value = valueEvaluator(namespacedRow)\n const array = arrayEvaluator(namespacedRow)\n if (!Array.isArray(array)) {\n return false\n }\n return array.includes(value)\n }\n }\n\n // String operators\n case `like`: {\n const valueEvaluator = compiledArgs[0]!\n const patternEvaluator = compiledArgs[1]!\n return (namespacedRow) => {\n const value = valueEvaluator(namespacedRow)\n const pattern = patternEvaluator(namespacedRow)\n return evaluateLike(value, pattern, false)\n }\n }\n case `ilike`: {\n const valueEvaluator = compiledArgs[0]!\n const patternEvaluator = compiledArgs[1]!\n return (namespacedRow) => {\n const value = valueEvaluator(namespacedRow)\n const pattern = patternEvaluator(namespacedRow)\n return evaluateLike(value, pattern, true)\n }\n }\n\n // String functions\n case `upper`: {\n const arg = compiledArgs[0]!\n return (namespacedRow) => {\n const value = arg(namespacedRow)\n return typeof value === `string` ? value.toUpperCase() : value\n }\n }\n case `lower`: {\n const arg = compiledArgs[0]!\n return (namespacedRow) => {\n const value = arg(namespacedRow)\n return typeof value === `string` ? value.toLowerCase() : value\n }\n }\n case `length`: {\n const arg = compiledArgs[0]!\n return (namespacedRow) => {\n const value = arg(namespacedRow)\n if (typeof value === `string`) {\n return value.length\n }\n if (Array.isArray(value)) {\n return value.length\n }\n return 0\n }\n }\n case `concat`:\n return (namespacedRow) => {\n return compiledArgs\n .map((evaluator) => {\n const arg = evaluator(namespacedRow)\n try {\n return String(arg ?? ``)\n } catch {\n try {\n return JSON.stringify(arg) || ``\n } catch {\n return `[object]`\n }\n }\n })\n .join(``)\n }\n case `coalesce`:\n return (namespacedRow) => {\n for (const evaluator of compiledArgs) {\n const value = evaluator(namespacedRow)\n if (value !== null && value !== undefined) {\n return value\n }\n }\n return null\n }\n\n // Math functions\n case `add`: {\n const argA = compiledArgs[0]!\n const argB = compiledArgs[1]!\n return (namespacedRow) => {\n const a = argA(namespacedRow)\n const b = argB(namespacedRow)\n return (a ?? 0) + (b ?? 0)\n }\n }\n case `subtract`: {\n const argA = compiledArgs[0]!\n const argB = compiledArgs[1]!\n return (namespacedRow) => {\n const a = argA(namespacedRow)\n const b = argB(namespacedRow)\n return (a ?? 0) - (b ?? 0)\n }\n }\n case `multiply`: {\n const argA = compiledArgs[0]!\n const argB = compiledArgs[1]!\n return (namespacedRow) => {\n const a = argA(namespacedRow)\n const b = argB(namespacedRow)\n return (a ?? 0) * (b ?? 0)\n }\n }\n case `divide`: {\n const argA = compiledArgs[0]!\n const argB = compiledArgs[1]!\n return (namespacedRow) => {\n const a = argA(namespacedRow)\n const b = argB(namespacedRow)\n const divisor = b ?? 0\n return divisor !== 0 ? (a ?? 0) / divisor : null\n }\n }\n\n default:\n throw new Error(`Unknown function: ${func.name}`)\n }\n}\n\n/**\n * Evaluates LIKE/ILIKE patterns\n */\nfunction evaluateLike(\n value: any,\n pattern: any,\n caseInsensitive: boolean\n): boolean {\n if (typeof value !== `string` || typeof pattern !== `string`) {\n return false\n }\n\n const searchValue = caseInsensitive ? value.toLowerCase() : value\n const searchPattern = caseInsensitive ? pattern.toLowerCase() : pattern\n\n // Convert SQL LIKE pattern to regex\n // First escape all regex special chars except % and _\n let regexPattern = searchPattern.replace(/[.*+?^${}()|[\\]\\\\]/g, `\\\\$&`)\n\n // Then convert SQL wildcards to regex\n regexPattern = regexPattern.replace(/%/g, `.*`) // % matches any sequence\n regexPattern = regexPattern.replace(/_/g, `.`) // _ matches any single char\n\n const regex = new RegExp(`^${regexPattern}$`)\n return regex.test(searchValue)\n}\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"evaluators.cjs","sources":["../../../../src/query/compiler/evaluators.ts"],"sourcesContent":["import {\n EmptyReferencePathError,\n UnknownExpressionTypeError,\n UnknownFunctionError,\n} from \"../../errors.js\"\nimport type { BasicExpression, Func, PropRef } from \"../ir.js\"\nimport type { NamespacedRow } from \"../../types.js\"\n\n/**\n * Compiled expression evaluator function type\n */\nexport type CompiledExpression = (namespacedRow: NamespacedRow) => any\n\n/**\n * Compiled single-row expression evaluator function type\n */\nexport type CompiledSingleRowExpression = (item: Record<string, unknown>) => any\n\n/**\n * Compiles an expression into an optimized evaluator function.\n * This eliminates branching during evaluation by pre-compiling the expression structure.\n */\nexport function compileExpression(expr: BasicExpression): CompiledExpression {\n const compiledFn = compileExpressionInternal(expr, false)\n return compiledFn as CompiledExpression\n}\n\n/**\n * Compiles a single-row expression into an optimized evaluator function.\n */\nexport function compileSingleRowExpression(\n expr: BasicExpression\n): CompiledSingleRowExpression {\n const compiledFn = compileExpressionInternal(expr, true)\n return compiledFn as CompiledSingleRowExpression\n}\n\n/**\n * Internal unified expression compiler that handles both namespaced and single-row evaluation\n */\nfunction compileExpressionInternal(\n expr: BasicExpression,\n isSingleRow: boolean\n): (data: any) => any {\n switch (expr.type) {\n case `val`: {\n // For constant values, return a function that just returns the value\n const value = expr.value\n return () => value\n }\n\n case `ref`: {\n // For references, compile based on evaluation mode\n return isSingleRow ? compileSingleRowRef(expr) : compileRef(expr)\n }\n\n case `func`: {\n // For functions, use the unified compiler\n return compileFunction(expr, isSingleRow)\n }\n\n default:\n throw new UnknownExpressionTypeError((expr as any).type)\n }\n}\n\n/**\n * Compiles a reference expression into an optimized evaluator\n */\nfunction compileRef(ref: PropRef): CompiledExpression {\n const [tableAlias, ...propertyPath] = ref.path\n\n if (!tableAlias) {\n throw new EmptyReferencePathError()\n }\n\n // Pre-compile the property path navigation\n if (propertyPath.length === 0) {\n // Simple table reference\n return (namespacedRow) => namespacedRow[tableAlias]\n } else if (propertyPath.length === 1) {\n // Single property access - most common case\n const prop = propertyPath[0]!\n return (namespacedRow) => {\n const tableData = namespacedRow[tableAlias]\n return tableData?.[prop]\n }\n } else {\n // Multiple property navigation\n return (namespacedRow) => {\n const tableData = namespacedRow[tableAlias]\n if (tableData === undefined) {\n return undefined\n }\n\n let value: any = tableData\n for (const prop of propertyPath) {\n if (value == null) {\n return value\n }\n value = value[prop]\n }\n return value\n }\n }\n}\n\n/**\n * Compiles a reference expression for single-row evaluation\n */\nfunction compileSingleRowRef(ref: PropRef): CompiledSingleRowExpression {\n const propertyPath = ref.path\n\n // This function works for all path lengths including empty path\n return (item) => {\n let value: any = item\n for (const prop of propertyPath) {\n if (value == null) {\n return value\n }\n value = value[prop]\n }\n return value\n }\n}\n\n/**\n * Compiles a function expression for both namespaced and single-row evaluation\n */\nfunction compileFunction(func: Func, isSingleRow: boolean): (data: any) => any {\n // Pre-compile all arguments using the appropriate compiler\n const compiledArgs = func.args.map((arg) =>\n compileExpressionInternal(arg, isSingleRow)\n )\n\n switch (func.name) {\n // Comparison operators\n case `eq`: {\n const argA = compiledArgs[0]!\n const argB = compiledArgs[1]!\n return (data) => {\n const a = argA(data)\n const b = argB(data)\n return a === b\n }\n }\n case `gt`: {\n const argA = compiledArgs[0]!\n const argB = compiledArgs[1]!\n return (data) => {\n const a = argA(data)\n const b = argB(data)\n return a > b\n }\n }\n case `gte`: {\n const argA = compiledArgs[0]!\n const argB = compiledArgs[1]!\n return (data) => {\n const a = argA(data)\n const b = argB(data)\n return a >= b\n }\n }\n case `lt`: {\n const argA = compiledArgs[0]!\n const argB = compiledArgs[1]!\n return (data) => {\n const a = argA(data)\n const b = argB(data)\n return a < b\n }\n }\n case `lte`: {\n const argA = compiledArgs[0]!\n const argB = compiledArgs[1]!\n return (data) => {\n const a = argA(data)\n const b = argB(data)\n return a <= b\n }\n }\n\n // Boolean operators\n case `and`:\n return (data) => {\n for (const compiledArg of compiledArgs) {\n if (!compiledArg(data)) {\n return false\n }\n }\n return true\n }\n case `or`:\n return (data) => {\n for (const compiledArg of compiledArgs) {\n if (compiledArg(data)) {\n return true\n }\n }\n return false\n }\n case `not`: {\n const arg = compiledArgs[0]!\n return (data) => !arg(data)\n }\n\n // Array operators\n case `in`: {\n const valueEvaluator = compiledArgs[0]!\n const arrayEvaluator = compiledArgs[1]!\n return (data) => {\n const value = valueEvaluator(data)\n const array = arrayEvaluator(data)\n if (!Array.isArray(array)) {\n return false\n }\n return array.includes(value)\n }\n }\n\n // String operators\n case `like`: {\n const valueEvaluator = compiledArgs[0]!\n const patternEvaluator = compiledArgs[1]!\n return (data) => {\n const value = valueEvaluator(data)\n const pattern = patternEvaluator(data)\n return evaluateLike(value, pattern, false)\n }\n }\n case `ilike`: {\n const valueEvaluator = compiledArgs[0]!\n const patternEvaluator = compiledArgs[1]!\n return (data) => {\n const value = valueEvaluator(data)\n const pattern = patternEvaluator(data)\n return evaluateLike(value, pattern, true)\n }\n }\n\n // String functions\n case `upper`: {\n const arg = compiledArgs[0]!\n return (data) => {\n const value = arg(data)\n return typeof value === `string` ? value.toUpperCase() : value\n }\n }\n case `lower`: {\n const arg = compiledArgs[0]!\n return (data) => {\n const value = arg(data)\n return typeof value === `string` ? value.toLowerCase() : value\n }\n }\n case `length`: {\n const arg = compiledArgs[0]!\n return (data) => {\n const value = arg(data)\n if (typeof value === `string`) {\n return value.length\n }\n if (Array.isArray(value)) {\n return value.length\n }\n return 0\n }\n }\n case `concat`:\n return (data) => {\n return compiledArgs\n .map((evaluator) => {\n const arg = evaluator(data)\n try {\n return String(arg ?? ``)\n } catch {\n try {\n return JSON.stringify(arg) || ``\n } catch {\n return `[object]`\n }\n }\n })\n .join(``)\n }\n case `coalesce`:\n return (data) => {\n for (const evaluator of compiledArgs) {\n const value = evaluator(data)\n if (value !== null && value !== undefined) {\n return value\n }\n }\n return null\n }\n\n // Math functions\n case `add`: {\n const argA = compiledArgs[0]!\n const argB = compiledArgs[1]!\n return (data) => {\n const a = argA(data)\n const b = argB(data)\n return (a ?? 0) + (b ?? 0)\n }\n }\n case `subtract`: {\n const argA = compiledArgs[0]!\n const argB = compiledArgs[1]!\n return (data) => {\n const a = argA(data)\n const b = argB(data)\n return (a ?? 0) - (b ?? 0)\n }\n }\n case `multiply`: {\n const argA = compiledArgs[0]!\n const argB = compiledArgs[1]!\n return (data) => {\n const a = argA(data)\n const b = argB(data)\n return (a ?? 0) * (b ?? 0)\n }\n }\n case `divide`: {\n const argA = compiledArgs[0]!\n const argB = compiledArgs[1]!\n return (data) => {\n const a = argA(data)\n const b = argB(data)\n const divisor = b ?? 0\n return divisor !== 0 ? (a ?? 0) / divisor : null\n }\n }\n\n default:\n throw new UnknownFunctionError(func.name)\n }\n}\n\n/**\n * Evaluates LIKE/ILIKE patterns\n */\nfunction evaluateLike(\n value: any,\n pattern: any,\n caseInsensitive: boolean\n): boolean {\n if (typeof value !== `string` || typeof pattern !== `string`) {\n return false\n }\n\n const searchValue = caseInsensitive ? value.toLowerCase() : value\n const searchPattern = caseInsensitive ? pattern.toLowerCase() : pattern\n\n // Convert SQL LIKE pattern to regex\n // First escape all regex special chars except % and _\n let regexPattern = searchPattern.replace(/[.*+?^${}()|[\\]\\\\]/g, `\\\\$&`)\n\n // Then convert SQL wildcards to regex\n regexPattern = regexPattern.replace(/%/g, `.*`) // % matches any sequence\n regexPattern = regexPattern.replace(/_/g, `.`) // _ matches any single char\n\n const regex = new RegExp(`^${regexPattern}$`)\n return regex.test(searchValue)\n}\n"],"names":["UnknownExpressionTypeError","EmptyReferencePathError","UnknownFunctionError"],"mappings":"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"}
@@ -4,8 +4,16 @@ import { NamespacedRow } from '../../types.js';
4
4
  * Compiled expression evaluator function type
5
5
  */
6
6
  export type CompiledExpression = (namespacedRow: NamespacedRow) => any;
7
+ /**
8
+ * Compiled single-row expression evaluator function type
9
+ */
10
+ export type CompiledSingleRowExpression = (item: Record<string, unknown>) => any;
7
11
  /**
8
12
  * Compiles an expression into an optimized evaluator function.
9
13
  * This eliminates branching during evaluation by pre-compiling the expression structure.
10
14
  */
11
15
  export declare function compileExpression(expr: BasicExpression): CompiledExpression;
16
+ /**
17
+ * Compiles a single-row expression into an optimized evaluator function.
18
+ */
19
+ export declare function compileSingleRowExpression(expr: BasicExpression): CompiledSingleRowExpression;
@@ -0,0 +1,61 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
+ const ir = require("../ir.cjs");
4
+ const SUPPORTED_COLLECTION_FUNCS = /* @__PURE__ */ new Set([
5
+ `eq`,
6
+ `gt`,
7
+ `lt`,
8
+ `gte`,
9
+ `lte`,
10
+ `and`,
11
+ `or`,
12
+ `in`
13
+ ]);
14
+ function isConvertibleToCollectionFilter(whereClause) {
15
+ const tpe = whereClause.type;
16
+ if (tpe === `func`) {
17
+ if (!SUPPORTED_COLLECTION_FUNCS.has(whereClause.name)) {
18
+ return false;
19
+ }
20
+ return whereClause.args.every(
21
+ (arg) => isConvertibleToCollectionFilter(arg)
22
+ );
23
+ }
24
+ return [`val`, `ref`].includes(tpe);
25
+ }
26
+ function convertToBasicExpression(whereClause, collectionAlias) {
27
+ const tpe = whereClause.type;
28
+ if (tpe === `val`) {
29
+ return new ir.Value(whereClause.value);
30
+ } else if (tpe === `ref`) {
31
+ const path = whereClause.path;
32
+ if (Array.isArray(path)) {
33
+ if (path[0] === collectionAlias && path.length > 1) {
34
+ return new ir.PropRef(path.slice(1));
35
+ } else if (path.length === 1 && path[0] !== void 0) {
36
+ return new ir.PropRef([path[0]]);
37
+ }
38
+ }
39
+ return new ir.PropRef(Array.isArray(path) ? path : [String(path)]);
40
+ } else {
41
+ if (!SUPPORTED_COLLECTION_FUNCS.has(whereClause.name)) {
42
+ return null;
43
+ }
44
+ const args = [];
45
+ for (const arg of whereClause.args) {
46
+ const convertedArg = convertToBasicExpression(
47
+ arg,
48
+ collectionAlias
49
+ );
50
+ if (convertedArg == null) {
51
+ return null;
52
+ }
53
+ args.push(convertedArg);
54
+ }
55
+ return new ir.Func(whereClause.name, args);
56
+ }
57
+ }
58
+ exports.SUPPORTED_COLLECTION_FUNCS = SUPPORTED_COLLECTION_FUNCS;
59
+ exports.convertToBasicExpression = convertToBasicExpression;
60
+ exports.isConvertibleToCollectionFilter = isConvertibleToCollectionFilter;
61
+ //# sourceMappingURL=expressions.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"expressions.cjs","sources":["../../../../src/query/compiler/expressions.ts"],"sourcesContent":["import { Func, PropRef, Value } from \"../ir.js\"\nimport type { BasicExpression } from \"../ir.js\"\n\n/**\n * Functions supported by the collection index system.\n * These are the only functions that can be used in WHERE clauses\n * that are pushed down to collection subscriptions for index optimization.\n */\nexport const SUPPORTED_COLLECTION_FUNCS = new Set([\n `eq`,\n `gt`,\n `lt`,\n `gte`,\n `lte`,\n `and`,\n `or`,\n `in`,\n])\n\n/**\n * Determines if a WHERE clause can be converted to collection-compatible BasicExpression format.\n * This checks if the expression only uses functions supported by the collection index system.\n *\n * @param whereClause - The WHERE clause to check\n * @returns True if the clause can be converted for collection index optimization\n */\nexport function isConvertibleToCollectionFilter(\n whereClause: BasicExpression<boolean>\n): boolean {\n const tpe = whereClause.type\n if (tpe === `func`) {\n // Check if this function is supported\n if (!SUPPORTED_COLLECTION_FUNCS.has(whereClause.name)) {\n return false\n }\n // Recursively check all arguments\n return whereClause.args.every((arg) =>\n isConvertibleToCollectionFilter(arg as BasicExpression<boolean>)\n )\n }\n return [`val`, `ref`].includes(tpe)\n}\n\n/**\n * Converts a WHERE clause to BasicExpression format compatible with collection indexes.\n * This function creates proper BasicExpression class instances that the collection\n * index system can understand.\n *\n * @param whereClause - The WHERE clause to convert\n * @param collectionAlias - The alias of the collection being filtered\n * @returns The converted BasicExpression or null if conversion fails\n */\nexport function convertToBasicExpression(\n whereClause: BasicExpression<boolean>,\n collectionAlias: string\n): BasicExpression<boolean> | null {\n const tpe = whereClause.type\n if (tpe === `val`) {\n return new Value(whereClause.value)\n } else if (tpe === `ref`) {\n const path = whereClause.path\n if (Array.isArray(path)) {\n if (path[0] === collectionAlias && path.length > 1) {\n // Remove the table alias from the path for single-collection queries\n return new PropRef(path.slice(1))\n } else if (path.length === 1 && path[0] !== undefined) {\n // Single field reference\n return new PropRef([path[0]])\n }\n }\n // Fallback for non-array paths\n return new PropRef(Array.isArray(path) ? path : [String(path)])\n } else {\n // Check if this function is supported\n if (!SUPPORTED_COLLECTION_FUNCS.has(whereClause.name)) {\n return null\n }\n // Recursively convert all arguments\n const args: Array<BasicExpression> = []\n for (const arg of whereClause.args) {\n const convertedArg = convertToBasicExpression(\n arg as BasicExpression<boolean>,\n collectionAlias\n )\n if (convertedArg == null) {\n return null\n }\n args.push(convertedArg)\n }\n return new Func(whereClause.name, args)\n }\n}\n"],"names":["Value","PropRef","Func"],"mappings":";;;AAQO,MAAM,iDAAiC,IAAI;AAAA,EAChD;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,CAAC;AASM,SAAS,gCACd,aACS;AACT,QAAM,MAAM,YAAY;AACxB,MAAI,QAAQ,QAAQ;AAElB,QAAI,CAAC,2BAA2B,IAAI,YAAY,IAAI,GAAG;AACrD,aAAO;AAAA,IACT;AAEA,WAAO,YAAY,KAAK;AAAA,MAAM,CAAC,QAC7B,gCAAgC,GAA+B;AAAA,IAAA;AAAA,EAEnE;AACA,SAAO,CAAC,OAAO,KAAK,EAAE,SAAS,GAAG;AACpC;AAWO,SAAS,yBACd,aACA,iBACiC;AACjC,QAAM,MAAM,YAAY;AACxB,MAAI,QAAQ,OAAO;AACjB,WAAO,IAAIA,GAAAA,MAAM,YAAY,KAAK;AAAA,EACpC,WAAW,QAAQ,OAAO;AACxB,UAAM,OAAO,YAAY;AACzB,QAAI,MAAM,QAAQ,IAAI,GAAG;AACvB,UAAI,KAAK,CAAC,MAAM,mBAAmB,KAAK,SAAS,GAAG;AAElD,eAAO,IAAIC,GAAAA,QAAQ,KAAK,MAAM,CAAC,CAAC;AAAA,MAClC,WAAW,KAAK,WAAW,KAAK,KAAK,CAAC,MAAM,QAAW;AAErD,eAAO,IAAIA,GAAAA,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC;AAAA,MAC9B;AAAA,IACF;AAEA,WAAO,IAAIA,GAAAA,QAAQ,MAAM,QAAQ,IAAI,IAAI,OAAO,CAAC,OAAO,IAAI,CAAC,CAAC;AAAA,EAChE,OAAO;AAEL,QAAI,CAAC,2BAA2B,IAAI,YAAY,IAAI,GAAG;AACrD,aAAO;AAAA,IACT;AAEA,UAAM,OAA+B,CAAA;AACrC,eAAW,OAAO,YAAY,MAAM;AAClC,YAAM,eAAe;AAAA,QACnB;AAAA,QACA;AAAA,MAAA;AAEF,UAAI,gBAAgB,MAAM;AACxB,eAAO;AAAA,MACT;AACA,WAAK,KAAK,YAAY;AAAA,IACxB;AACA,WAAO,IAAIC,GAAAA,KAAK,YAAY,MAAM,IAAI;AAAA,EACxC;AACF;;;;"}
@@ -0,0 +1,25 @@
1
+ import { BasicExpression } from '../ir.js';
2
+ /**
3
+ * Functions supported by the collection index system.
4
+ * These are the only functions that can be used in WHERE clauses
5
+ * that are pushed down to collection subscriptions for index optimization.
6
+ */
7
+ export declare const SUPPORTED_COLLECTION_FUNCS: Set<string>;
8
+ /**
9
+ * Determines if a WHERE clause can be converted to collection-compatible BasicExpression format.
10
+ * This checks if the expression only uses functions supported by the collection index system.
11
+ *
12
+ * @param whereClause - The WHERE clause to check
13
+ * @returns True if the clause can be converted for collection index optimization
14
+ */
15
+ export declare function isConvertibleToCollectionFilter(whereClause: BasicExpression<boolean>): boolean;
16
+ /**
17
+ * Converts a WHERE clause to BasicExpression format compatible with collection indexes.
18
+ * This function creates proper BasicExpression class instances that the collection
19
+ * index system can understand.
20
+ *
21
+ * @param whereClause - The WHERE clause to convert
22
+ * @param collectionAlias - The alias of the collection being filtered
23
+ * @returns The converted BasicExpression or null if conversion fails
24
+ */
25
+ export declare function convertToBasicExpression(whereClause: BasicExpression<boolean>, collectionAlias: string): BasicExpression<boolean> | null;
@@ -2,6 +2,7 @@
2
2
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
3
  const d2mini = require("@electric-sql/d2mini");
4
4
  const ir = require("../ir.cjs");
5
+ const errors = require("../../errors.cjs");
5
6
  const evaluators = require("./evaluators.cjs");
6
7
  const { sum, count, avg, min, max } = d2mini.groupByOperators;
7
8
  function validateAndCreateMapping(groupByClause, selectClause) {
@@ -18,9 +19,7 @@ function validateAndCreateMapping(groupByClause, selectClause) {
18
19
  (groupExpr) => expressionsEqual(expr, groupExpr)
19
20
  );
20
21
  if (groupIndex === -1) {
21
- throw new Error(
22
- `Non-aggregate expression '${alias}' in SELECT must also appear in GROUP BY clause`
23
- );
22
+ throw new errors.NonAggregateExpressionNotInGroupByError(alias);
24
23
  }
25
24
  selectToGroupByIndex.set(alias, groupIndex);
26
25
  }
@@ -223,7 +222,7 @@ function getAggregateFunction(aggExpr) {
223
222
  case `max`:
224
223
  return max(valueExtractor);
225
224
  default:
226
- throw new Error(`Unsupported aggregate function: ${aggExpr.name}`);
225
+ throw new errors.UnsupportedAggregateFunctionError(aggExpr.name);
227
226
  }
228
227
  }
229
228
  function transformHavingClause(havingExpr, selectClause) {
@@ -235,9 +234,7 @@ function transformHavingClause(havingExpr, selectClause) {
235
234
  return new ir.PropRef([`result`, alias]);
236
235
  }
237
236
  }
238
- throw new Error(
239
- `Aggregate function in HAVING clause must also be in SELECT clause: ${aggExpr.name}`
240
- );
237
+ throw new errors.AggregateFunctionNotInSelectError(aggExpr.name);
241
238
  }
242
239
  case `func`: {
243
240
  const funcExpr = havingExpr;
@@ -259,9 +256,7 @@ function transformHavingClause(havingExpr, selectClause) {
259
256
  case `val`:
260
257
  return havingExpr;
261
258
  default:
262
- throw new Error(
263
- `Unknown expression type in HAVING clause: ${havingExpr.type}`
264
- );
259
+ throw new errors.UnknownHavingExpressionTypeError(havingExpr.type);
265
260
  }
266
261
  }
267
262
  function aggregatesEqual(agg1, agg2) {
@@ -1 +1 @@
1
- {"version":3,"file":"group-by.cjs","sources":["../../../../src/query/compiler/group-by.ts"],"sourcesContent":["import { filter, groupBy, groupByOperators, map } from \"@electric-sql/d2mini\"\nimport { Func, PropRef } from \"../ir.js\"\nimport { compileExpression } from \"./evaluators.js\"\nimport type {\n Aggregate,\n BasicExpression,\n GroupBy,\n Having,\n Select,\n} from \"../ir.js\"\nimport type { NamespacedAndKeyedStream, NamespacedRow } from \"../../types.js\"\n\nconst { sum, count, avg, min, max } = groupByOperators\n\n/**\n * Interface for caching the mapping between GROUP BY expressions and SELECT expressions\n */\ninterface GroupBySelectMapping {\n selectToGroupByIndex: Map<string, number> // Maps SELECT alias to GROUP BY expression index\n groupByExpressions: Array<any> // The GROUP BY expressions for reference\n}\n\n/**\n * Validates that all non-aggregate expressions in SELECT are present in GROUP BY\n * and creates a cached mapping for efficient lookup during processing\n */\nfunction validateAndCreateMapping(\n groupByClause: GroupBy,\n selectClause?: Select\n): GroupBySelectMapping {\n const selectToGroupByIndex = new Map<string, number>()\n const groupByExpressions = [...groupByClause]\n\n if (!selectClause) {\n return { selectToGroupByIndex, groupByExpressions }\n }\n\n // Validate each SELECT expression\n for (const [alias, expr] of Object.entries(selectClause)) {\n if (expr.type === `agg`) {\n // Aggregate expressions are allowed and don't need to be in GROUP BY\n continue\n }\n\n // Non-aggregate expression must be in GROUP BY\n const groupIndex = groupByExpressions.findIndex((groupExpr) =>\n expressionsEqual(expr, groupExpr)\n )\n\n if (groupIndex === -1) {\n throw new Error(\n `Non-aggregate expression '${alias}' in SELECT must also appear in GROUP BY clause`\n )\n }\n\n // Cache the mapping\n selectToGroupByIndex.set(alias, groupIndex)\n }\n\n return { selectToGroupByIndex, groupByExpressions }\n}\n\n/**\n * Processes the GROUP BY clause with optional HAVING and SELECT\n * Works with the new __select_results structure from early SELECT processing\n */\nexport function processGroupBy(\n pipeline: NamespacedAndKeyedStream,\n groupByClause: GroupBy,\n havingClauses?: Array<Having>,\n selectClause?: Select,\n fnHavingClauses?: Array<(row: any) => any>\n): NamespacedAndKeyedStream {\n // Handle empty GROUP BY (single-group aggregation)\n if (groupByClause.length === 0) {\n // For single-group aggregation, create a single group with all data\n const aggregates: Record<string, any> = {}\n\n if (selectClause) {\n // Scan the SELECT clause for aggregate functions\n for (const [alias, expr] of Object.entries(selectClause)) {\n if (expr.type === `agg`) {\n const aggExpr = expr\n aggregates[alias] = getAggregateFunction(aggExpr)\n }\n }\n }\n\n // Use a constant key for single group\n const keyExtractor = () => ({ __singleGroup: true })\n\n // Apply the groupBy operator with single group\n pipeline = pipeline.pipe(\n groupBy(keyExtractor, aggregates)\n ) as NamespacedAndKeyedStream\n\n // Update __select_results to include aggregate values\n pipeline = pipeline.pipe(\n map(([, aggregatedRow]) => {\n // Start with the existing __select_results from early SELECT processing\n const selectResults = (aggregatedRow as any).__select_results || {}\n const finalResults: Record<string, any> = { ...selectResults }\n\n if (selectClause) {\n // Update with aggregate results\n for (const [alias, expr] of Object.entries(selectClause)) {\n if (expr.type === `agg`) {\n finalResults[alias] = aggregatedRow[alias]\n }\n // Non-aggregates keep their original values from early SELECT processing\n }\n }\n\n // Use a single key for the result and update __select_results\n return [\n `single_group`,\n {\n ...aggregatedRow,\n __select_results: finalResults,\n },\n ] as [unknown, Record<string, any>]\n })\n )\n\n // Apply HAVING clauses if present\n if (havingClauses && havingClauses.length > 0) {\n for (const havingClause of havingClauses) {\n const transformedHavingClause = transformHavingClause(\n havingClause,\n selectClause || {}\n )\n const compiledHaving = compileExpression(transformedHavingClause)\n\n pipeline = pipeline.pipe(\n filter(([, row]) => {\n // Create a namespaced row structure for HAVING evaluation\n const namespacedRow = { result: (row as any).__select_results }\n return compiledHaving(namespacedRow)\n })\n )\n }\n }\n\n // Apply functional HAVING clauses if present\n if (fnHavingClauses && fnHavingClauses.length > 0) {\n for (const fnHaving of fnHavingClauses) {\n pipeline = pipeline.pipe(\n filter(([, row]) => {\n // Create a namespaced row structure for functional HAVING evaluation\n const namespacedRow = { result: (row as any).__select_results }\n return fnHaving(namespacedRow)\n })\n )\n }\n }\n\n return pipeline\n }\n\n // Multi-group aggregation logic...\n // Validate and create mapping for non-aggregate expressions in SELECT\n const mapping = validateAndCreateMapping(groupByClause, selectClause)\n\n // Pre-compile groupBy expressions\n const compiledGroupByExpressions = groupByClause.map(compileExpression)\n\n // Create a key extractor function using simple __key_X format\n const keyExtractor = ([, row]: [\n string,\n NamespacedRow & { __select_results?: any },\n ]) => {\n // Use the original namespaced row for GROUP BY expressions, not __select_results\n const namespacedRow = { ...row }\n delete (namespacedRow as any).__select_results\n\n const key: Record<string, unknown> = {}\n\n // Use simple __key_X format for each groupBy expression\n for (let i = 0; i < groupByClause.length; i++) {\n const compiledExpr = compiledGroupByExpressions[i]!\n const value = compiledExpr(namespacedRow)\n key[`__key_${i}`] = value\n }\n\n return key\n }\n\n // Create aggregate functions for any aggregated columns in the SELECT clause\n const aggregates: Record<string, any> = {}\n\n if (selectClause) {\n // Scan the SELECT clause for aggregate functions\n for (const [alias, expr] of Object.entries(selectClause)) {\n if (expr.type === `agg`) {\n const aggExpr = expr\n aggregates[alias] = getAggregateFunction(aggExpr)\n }\n }\n }\n\n // Apply the groupBy operator\n pipeline = pipeline.pipe(groupBy(keyExtractor, aggregates))\n\n // Update __select_results to handle GROUP BY results\n pipeline = pipeline.pipe(\n map(([, aggregatedRow]) => {\n // Start with the existing __select_results from early SELECT processing\n const selectResults = (aggregatedRow as any).__select_results || {}\n const finalResults: Record<string, any> = {}\n\n if (selectClause) {\n // Process each SELECT expression\n for (const [alias, expr] of Object.entries(selectClause)) {\n if (expr.type !== `agg`) {\n // Use cached mapping to get the corresponding __key_X for non-aggregates\n const groupIndex = mapping.selectToGroupByIndex.get(alias)\n if (groupIndex !== undefined) {\n finalResults[alias] = aggregatedRow[`__key_${groupIndex}`]\n } else {\n // Fallback to original SELECT results\n finalResults[alias] = selectResults[alias]\n }\n } else {\n // Use aggregate results\n finalResults[alias] = aggregatedRow[alias]\n }\n }\n } else {\n // No SELECT clause - just use the group keys\n for (let i = 0; i < groupByClause.length; i++) {\n finalResults[`__key_${i}`] = aggregatedRow[`__key_${i}`]\n }\n }\n\n // Generate a simple key for the live collection using group values\n let finalKey: unknown\n if (groupByClause.length === 1) {\n finalKey = aggregatedRow[`__key_0`]\n } else {\n const keyParts: Array<unknown> = []\n for (let i = 0; i < groupByClause.length; i++) {\n keyParts.push(aggregatedRow[`__key_${i}`])\n }\n finalKey = JSON.stringify(keyParts)\n }\n\n return [\n finalKey,\n {\n ...aggregatedRow,\n __select_results: finalResults,\n },\n ] as [unknown, Record<string, any>]\n })\n )\n\n // Apply HAVING clauses if present\n if (havingClauses && havingClauses.length > 0) {\n for (const havingClause of havingClauses) {\n const transformedHavingClause = transformHavingClause(\n havingClause,\n selectClause || {}\n )\n const compiledHaving = compileExpression(transformedHavingClause)\n\n pipeline = pipeline.pipe(\n filter(([, row]) => {\n // Create a namespaced row structure for HAVING evaluation\n const namespacedRow = { result: (row as any).__select_results }\n return compiledHaving(namespacedRow)\n })\n )\n }\n }\n\n // Apply functional HAVING clauses if present\n if (fnHavingClauses && fnHavingClauses.length > 0) {\n for (const fnHaving of fnHavingClauses) {\n pipeline = pipeline.pipe(\n filter(([, row]) => {\n // Create a namespaced row structure for functional HAVING evaluation\n const namespacedRow = { result: (row as any).__select_results }\n return fnHaving(namespacedRow)\n })\n )\n }\n }\n\n return pipeline\n}\n\n/**\n * Helper function to check if two expressions are equal\n */\nfunction expressionsEqual(expr1: any, expr2: any): boolean {\n if (!expr1 || !expr2) return false\n if (expr1.type !== expr2.type) return false\n\n switch (expr1.type) {\n case `ref`:\n // Compare paths as arrays\n if (!expr1.path || !expr2.path) return false\n if (expr1.path.length !== expr2.path.length) return false\n return expr1.path.every(\n (segment: string, i: number) => segment === expr2.path[i]\n )\n case `val`:\n return expr1.value === expr2.value\n case `func`:\n return (\n expr1.name === expr2.name &&\n expr1.args?.length === expr2.args?.length &&\n (expr1.args || []).every((arg: any, i: number) =>\n expressionsEqual(arg, expr2.args[i])\n )\n )\n case `agg`:\n return (\n expr1.name === expr2.name &&\n expr1.args?.length === expr2.args?.length &&\n (expr1.args || []).every((arg: any, i: number) =>\n expressionsEqual(arg, expr2.args[i])\n )\n )\n default:\n return false\n }\n}\n\n/**\n * Helper function to get an aggregate function based on the Agg expression\n */\nfunction getAggregateFunction(aggExpr: Aggregate) {\n // Pre-compile the value extractor expression\n const compiledExpr = compileExpression(aggExpr.args[0]!)\n\n // Create a value extractor function for the expression to aggregate\n const valueExtractor = ([, namespacedRow]: [string, NamespacedRow]) => {\n const value = compiledExpr(namespacedRow)\n // Ensure we return a number for numeric aggregate functions\n return typeof value === `number` ? value : value != null ? Number(value) : 0\n }\n\n // Return the appropriate aggregate function\n switch (aggExpr.name.toLowerCase()) {\n case `sum`:\n return sum(valueExtractor)\n case `count`:\n return count() // count() doesn't need a value extractor\n case `avg`:\n return avg(valueExtractor)\n case `min`:\n return min(valueExtractor)\n case `max`:\n return max(valueExtractor)\n default:\n throw new Error(`Unsupported aggregate function: ${aggExpr.name}`)\n }\n}\n\n/**\n * Transforms a HAVING clause to replace Agg expressions with references to computed values\n */\nfunction transformHavingClause(\n havingExpr: BasicExpression | Aggregate,\n selectClause: Select\n): BasicExpression {\n switch (havingExpr.type) {\n case `agg`: {\n const aggExpr = havingExpr\n // Find matching aggregate in SELECT clause\n for (const [alias, selectExpr] of Object.entries(selectClause)) {\n if (selectExpr.type === `agg` && aggregatesEqual(aggExpr, selectExpr)) {\n // Replace with a reference to the computed aggregate\n return new PropRef([`result`, alias])\n }\n }\n // If no matching aggregate found in SELECT, throw error\n throw new Error(\n `Aggregate function in HAVING clause must also be in SELECT clause: ${aggExpr.name}`\n )\n }\n\n case `func`: {\n const funcExpr = havingExpr\n // Transform function arguments recursively\n const transformedArgs = funcExpr.args.map(\n (arg: BasicExpression | Aggregate) =>\n transformHavingClause(arg, selectClause)\n )\n return new Func(funcExpr.name, transformedArgs)\n }\n\n case `ref`: {\n const refExpr = havingExpr\n // Check if this is a direct reference to a SELECT alias\n if (refExpr.path.length === 1) {\n const alias = refExpr.path[0]!\n if (selectClause[alias]) {\n // This is a reference to a SELECT alias, convert to result.alias\n return new PropRef([`result`, alias])\n }\n }\n // Return as-is for other refs\n return havingExpr as BasicExpression\n }\n\n case `val`:\n // Return as-is\n return havingExpr as BasicExpression\n\n default:\n throw new Error(\n `Unknown expression type in HAVING clause: ${(havingExpr as any).type}`\n )\n }\n}\n\n/**\n * Checks if two aggregate expressions are equal\n */\nfunction aggregatesEqual(agg1: Aggregate, agg2: Aggregate): boolean {\n return (\n agg1.name === agg2.name &&\n agg1.args.length === agg2.args.length &&\n agg1.args.every((arg, i) => expressionsEqual(arg, agg2.args[i]))\n )\n}\n"],"names":["groupByOperators","aggregates","keyExtractor","groupBy","map","compileExpression","filter","PropRef","Func"],"mappings":"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"}
1
+ {"version":3,"file":"group-by.cjs","sources":["../../../../src/query/compiler/group-by.ts"],"sourcesContent":["import { filter, groupBy, groupByOperators, map } from \"@electric-sql/d2mini\"\nimport { Func, PropRef } from \"../ir.js\"\nimport {\n AggregateFunctionNotInSelectError,\n NonAggregateExpressionNotInGroupByError,\n UnknownHavingExpressionTypeError,\n UnsupportedAggregateFunctionError,\n} from \"../../errors.js\"\nimport { compileExpression } from \"./evaluators.js\"\nimport type {\n Aggregate,\n BasicExpression,\n GroupBy,\n Having,\n Select,\n} from \"../ir.js\"\nimport type { NamespacedAndKeyedStream, NamespacedRow } from \"../../types.js\"\n\nconst { sum, count, avg, min, max } = groupByOperators\n\n/**\n * Interface for caching the mapping between GROUP BY expressions and SELECT expressions\n */\ninterface GroupBySelectMapping {\n selectToGroupByIndex: Map<string, number> // Maps SELECT alias to GROUP BY expression index\n groupByExpressions: Array<any> // The GROUP BY expressions for reference\n}\n\n/**\n * Validates that all non-aggregate expressions in SELECT are present in GROUP BY\n * and creates a cached mapping for efficient lookup during processing\n */\nfunction validateAndCreateMapping(\n groupByClause: GroupBy,\n selectClause?: Select\n): GroupBySelectMapping {\n const selectToGroupByIndex = new Map<string, number>()\n const groupByExpressions = [...groupByClause]\n\n if (!selectClause) {\n return { selectToGroupByIndex, groupByExpressions }\n }\n\n // Validate each SELECT expression\n for (const [alias, expr] of Object.entries(selectClause)) {\n if (expr.type === `agg`) {\n // Aggregate expressions are allowed and don't need to be in GROUP BY\n continue\n }\n\n // Non-aggregate expression must be in GROUP BY\n const groupIndex = groupByExpressions.findIndex((groupExpr) =>\n expressionsEqual(expr, groupExpr)\n )\n\n if (groupIndex === -1) {\n throw new NonAggregateExpressionNotInGroupByError(alias)\n }\n\n // Cache the mapping\n selectToGroupByIndex.set(alias, groupIndex)\n }\n\n return { selectToGroupByIndex, groupByExpressions }\n}\n\n/**\n * Processes the GROUP BY clause with optional HAVING and SELECT\n * Works with the new __select_results structure from early SELECT processing\n */\nexport function processGroupBy(\n pipeline: NamespacedAndKeyedStream,\n groupByClause: GroupBy,\n havingClauses?: Array<Having>,\n selectClause?: Select,\n fnHavingClauses?: Array<(row: any) => any>\n): NamespacedAndKeyedStream {\n // Handle empty GROUP BY (single-group aggregation)\n if (groupByClause.length === 0) {\n // For single-group aggregation, create a single group with all data\n const aggregates: Record<string, any> = {}\n\n if (selectClause) {\n // Scan the SELECT clause for aggregate functions\n for (const [alias, expr] of Object.entries(selectClause)) {\n if (expr.type === `agg`) {\n const aggExpr = expr\n aggregates[alias] = getAggregateFunction(aggExpr)\n }\n }\n }\n\n // Use a constant key for single group\n const keyExtractor = () => ({ __singleGroup: true })\n\n // Apply the groupBy operator with single group\n pipeline = pipeline.pipe(\n groupBy(keyExtractor, aggregates)\n ) as NamespacedAndKeyedStream\n\n // Update __select_results to include aggregate values\n pipeline = pipeline.pipe(\n map(([, aggregatedRow]) => {\n // Start with the existing __select_results from early SELECT processing\n const selectResults = (aggregatedRow as any).__select_results || {}\n const finalResults: Record<string, any> = { ...selectResults }\n\n if (selectClause) {\n // Update with aggregate results\n for (const [alias, expr] of Object.entries(selectClause)) {\n if (expr.type === `agg`) {\n finalResults[alias] = aggregatedRow[alias]\n }\n // Non-aggregates keep their original values from early SELECT processing\n }\n }\n\n // Use a single key for the result and update __select_results\n return [\n `single_group`,\n {\n ...aggregatedRow,\n __select_results: finalResults,\n },\n ] as [unknown, Record<string, any>]\n })\n )\n\n // Apply HAVING clauses if present\n if (havingClauses && havingClauses.length > 0) {\n for (const havingClause of havingClauses) {\n const transformedHavingClause = transformHavingClause(\n havingClause,\n selectClause || {}\n )\n const compiledHaving = compileExpression(transformedHavingClause)\n\n pipeline = pipeline.pipe(\n filter(([, row]) => {\n // Create a namespaced row structure for HAVING evaluation\n const namespacedRow = { result: (row as any).__select_results }\n return compiledHaving(namespacedRow)\n })\n )\n }\n }\n\n // Apply functional HAVING clauses if present\n if (fnHavingClauses && fnHavingClauses.length > 0) {\n for (const fnHaving of fnHavingClauses) {\n pipeline = pipeline.pipe(\n filter(([, row]) => {\n // Create a namespaced row structure for functional HAVING evaluation\n const namespacedRow = { result: (row as any).__select_results }\n return fnHaving(namespacedRow)\n })\n )\n }\n }\n\n return pipeline\n }\n\n // Multi-group aggregation logic...\n // Validate and create mapping for non-aggregate expressions in SELECT\n const mapping = validateAndCreateMapping(groupByClause, selectClause)\n\n // Pre-compile groupBy expressions\n const compiledGroupByExpressions = groupByClause.map(compileExpression)\n\n // Create a key extractor function using simple __key_X format\n const keyExtractor = ([, row]: [\n string,\n NamespacedRow & { __select_results?: any },\n ]) => {\n // Use the original namespaced row for GROUP BY expressions, not __select_results\n const namespacedRow = { ...row }\n delete (namespacedRow as any).__select_results\n\n const key: Record<string, unknown> = {}\n\n // Use simple __key_X format for each groupBy expression\n for (let i = 0; i < groupByClause.length; i++) {\n const compiledExpr = compiledGroupByExpressions[i]!\n const value = compiledExpr(namespacedRow)\n key[`__key_${i}`] = value\n }\n\n return key\n }\n\n // Create aggregate functions for any aggregated columns in the SELECT clause\n const aggregates: Record<string, any> = {}\n\n if (selectClause) {\n // Scan the SELECT clause for aggregate functions\n for (const [alias, expr] of Object.entries(selectClause)) {\n if (expr.type === `agg`) {\n const aggExpr = expr\n aggregates[alias] = getAggregateFunction(aggExpr)\n }\n }\n }\n\n // Apply the groupBy operator\n pipeline = pipeline.pipe(groupBy(keyExtractor, aggregates))\n\n // Update __select_results to handle GROUP BY results\n pipeline = pipeline.pipe(\n map(([, aggregatedRow]) => {\n // Start with the existing __select_results from early SELECT processing\n const selectResults = (aggregatedRow as any).__select_results || {}\n const finalResults: Record<string, any> = {}\n\n if (selectClause) {\n // Process each SELECT expression\n for (const [alias, expr] of Object.entries(selectClause)) {\n if (expr.type !== `agg`) {\n // Use cached mapping to get the corresponding __key_X for non-aggregates\n const groupIndex = mapping.selectToGroupByIndex.get(alias)\n if (groupIndex !== undefined) {\n finalResults[alias] = aggregatedRow[`__key_${groupIndex}`]\n } else {\n // Fallback to original SELECT results\n finalResults[alias] = selectResults[alias]\n }\n } else {\n // Use aggregate results\n finalResults[alias] = aggregatedRow[alias]\n }\n }\n } else {\n // No SELECT clause - just use the group keys\n for (let i = 0; i < groupByClause.length; i++) {\n finalResults[`__key_${i}`] = aggregatedRow[`__key_${i}`]\n }\n }\n\n // Generate a simple key for the live collection using group values\n let finalKey: unknown\n if (groupByClause.length === 1) {\n finalKey = aggregatedRow[`__key_0`]\n } else {\n const keyParts: Array<unknown> = []\n for (let i = 0; i < groupByClause.length; i++) {\n keyParts.push(aggregatedRow[`__key_${i}`])\n }\n finalKey = JSON.stringify(keyParts)\n }\n\n return [\n finalKey,\n {\n ...aggregatedRow,\n __select_results: finalResults,\n },\n ] as [unknown, Record<string, any>]\n })\n )\n\n // Apply HAVING clauses if present\n if (havingClauses && havingClauses.length > 0) {\n for (const havingClause of havingClauses) {\n const transformedHavingClause = transformHavingClause(\n havingClause,\n selectClause || {}\n )\n const compiledHaving = compileExpression(transformedHavingClause)\n\n pipeline = pipeline.pipe(\n filter(([, row]) => {\n // Create a namespaced row structure for HAVING evaluation\n const namespacedRow = { result: (row as any).__select_results }\n return compiledHaving(namespacedRow)\n })\n )\n }\n }\n\n // Apply functional HAVING clauses if present\n if (fnHavingClauses && fnHavingClauses.length > 0) {\n for (const fnHaving of fnHavingClauses) {\n pipeline = pipeline.pipe(\n filter(([, row]) => {\n // Create a namespaced row structure for functional HAVING evaluation\n const namespacedRow = { result: (row as any).__select_results }\n return fnHaving(namespacedRow)\n })\n )\n }\n }\n\n return pipeline\n}\n\n/**\n * Helper function to check if two expressions are equal\n */\nfunction expressionsEqual(expr1: any, expr2: any): boolean {\n if (!expr1 || !expr2) return false\n if (expr1.type !== expr2.type) return false\n\n switch (expr1.type) {\n case `ref`:\n // Compare paths as arrays\n if (!expr1.path || !expr2.path) return false\n if (expr1.path.length !== expr2.path.length) return false\n return expr1.path.every(\n (segment: string, i: number) => segment === expr2.path[i]\n )\n case `val`:\n return expr1.value === expr2.value\n case `func`:\n return (\n expr1.name === expr2.name &&\n expr1.args?.length === expr2.args?.length &&\n (expr1.args || []).every((arg: any, i: number) =>\n expressionsEqual(arg, expr2.args[i])\n )\n )\n case `agg`:\n return (\n expr1.name === expr2.name &&\n expr1.args?.length === expr2.args?.length &&\n (expr1.args || []).every((arg: any, i: number) =>\n expressionsEqual(arg, expr2.args[i])\n )\n )\n default:\n return false\n }\n}\n\n/**\n * Helper function to get an aggregate function based on the Agg expression\n */\nfunction getAggregateFunction(aggExpr: Aggregate) {\n // Pre-compile the value extractor expression\n const compiledExpr = compileExpression(aggExpr.args[0]!)\n\n // Create a value extractor function for the expression to aggregate\n const valueExtractor = ([, namespacedRow]: [string, NamespacedRow]) => {\n const value = compiledExpr(namespacedRow)\n // Ensure we return a number for numeric aggregate functions\n return typeof value === `number` ? value : value != null ? Number(value) : 0\n }\n\n // Return the appropriate aggregate function\n switch (aggExpr.name.toLowerCase()) {\n case `sum`:\n return sum(valueExtractor)\n case `count`:\n return count() // count() doesn't need a value extractor\n case `avg`:\n return avg(valueExtractor)\n case `min`:\n return min(valueExtractor)\n case `max`:\n return max(valueExtractor)\n default:\n throw new UnsupportedAggregateFunctionError(aggExpr.name)\n }\n}\n\n/**\n * Transforms a HAVING clause to replace Agg expressions with references to computed values\n */\nfunction transformHavingClause(\n havingExpr: BasicExpression | Aggregate,\n selectClause: Select\n): BasicExpression {\n switch (havingExpr.type) {\n case `agg`: {\n const aggExpr = havingExpr\n // Find matching aggregate in SELECT clause\n for (const [alias, selectExpr] of Object.entries(selectClause)) {\n if (selectExpr.type === `agg` && aggregatesEqual(aggExpr, selectExpr)) {\n // Replace with a reference to the computed aggregate\n return new PropRef([`result`, alias])\n }\n }\n // If no matching aggregate found in SELECT, throw error\n throw new AggregateFunctionNotInSelectError(aggExpr.name)\n }\n\n case `func`: {\n const funcExpr = havingExpr\n // Transform function arguments recursively\n const transformedArgs = funcExpr.args.map(\n (arg: BasicExpression | Aggregate) =>\n transformHavingClause(arg, selectClause)\n )\n return new Func(funcExpr.name, transformedArgs)\n }\n\n case `ref`: {\n const refExpr = havingExpr\n // Check if this is a direct reference to a SELECT alias\n if (refExpr.path.length === 1) {\n const alias = refExpr.path[0]!\n if (selectClause[alias]) {\n // This is a reference to a SELECT alias, convert to result.alias\n return new PropRef([`result`, alias])\n }\n }\n // Return as-is for other refs\n return havingExpr as BasicExpression\n }\n\n case `val`:\n // Return as-is\n return havingExpr as BasicExpression\n\n default:\n throw new UnknownHavingExpressionTypeError((havingExpr as any).type)\n }\n}\n\n/**\n * Checks if two aggregate expressions are equal\n */\nfunction aggregatesEqual(agg1: Aggregate, agg2: Aggregate): boolean {\n return (\n agg1.name === agg2.name &&\n agg1.args.length === agg2.args.length &&\n agg1.args.every((arg, i) => expressionsEqual(arg, agg2.args[i]))\n )\n}\n"],"names":["groupByOperators","NonAggregateExpressionNotInGroupByError","aggregates","keyExtractor","groupBy","map","compileExpression","filter","UnsupportedAggregateFunctionError","PropRef","AggregateFunctionNotInSelectError","Func","UnknownHavingExpressionTypeError"],"mappings":"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"}