@tanstack/db 0.0.14 → 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 +18 -21
  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 +10 -1
  45. package/dist/cjs/types.d.cts +13 -0
  46. package/dist/esm/collection.d.ts +18 -21
  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 +10 -1
  88. package/dist/esm/transactions.js +20 -13
  89. package/dist/esm/transactions.js.map +1 -1
  90. package/dist/esm/types.d.ts +13 -0
  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 +27 -16
  108. package/src/types.ts +15 -0
  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,88 +0,0 @@
1
- import { groupBy, groupByOperators } from "@electric-sql/d2mini";
2
- import { extractValueFromNamespacedRow, evaluateOperandOnNamespacedRow } from "./extractors.js";
3
- import { isAggregateFunctionCall } from "./utils.js";
4
- const { sum, count, avg, min, max, median, mode } = groupByOperators;
5
- function processGroupBy(pipeline, query, mainTableAlias) {
6
- const groupByColumns = Array.isArray(query.groupBy) ? query.groupBy : [query.groupBy];
7
- const keyExtractor = ([_oldKey, namespacedRow]) => {
8
- const key = {};
9
- for (const column of groupByColumns) {
10
- if (typeof column === `string` && column.startsWith(`@`)) {
11
- const columnRef = column.substring(1);
12
- const columnName = columnRef.includes(`.`) ? columnRef.split(`.`)[1] : columnRef;
13
- key[columnName] = extractValueFromNamespacedRow(
14
- namespacedRow,
15
- columnRef,
16
- mainTableAlias
17
- );
18
- }
19
- }
20
- return key;
21
- };
22
- const aggregates = {};
23
- if (!query.select) {
24
- throw new Error(`SELECT clause is required for GROUP BY`);
25
- }
26
- for (const item of query.select) {
27
- if (typeof item === `object`) {
28
- for (const [alias, expr] of Object.entries(item)) {
29
- if (typeof expr === `object` && isAggregateFunctionCall(expr)) {
30
- const functionName = Object.keys(expr)[0];
31
- const columnRef = expr[functionName];
32
- aggregates[alias] = getAggregateFunction(
33
- functionName,
34
- columnRef,
35
- mainTableAlias
36
- );
37
- }
38
- }
39
- }
40
- }
41
- if (Object.keys(aggregates).length > 0) {
42
- pipeline = pipeline.pipe(groupBy(keyExtractor, aggregates));
43
- }
44
- return pipeline;
45
- }
46
- function getAggregateFunction(functionName, columnRef, mainTableAlias) {
47
- const valueExtractor = ([_oldKey, namespacedRow]) => {
48
- let value;
49
- if (typeof columnRef === `string` && columnRef.startsWith(`@`)) {
50
- value = extractValueFromNamespacedRow(
51
- namespacedRow,
52
- columnRef.substring(1),
53
- mainTableAlias
54
- );
55
- } else {
56
- value = evaluateOperandOnNamespacedRow(
57
- namespacedRow,
58
- columnRef,
59
- mainTableAlias
60
- );
61
- }
62
- return typeof value === `number` ? value : 0;
63
- };
64
- switch (functionName.toUpperCase()) {
65
- case `SUM`:
66
- return sum(valueExtractor);
67
- case `COUNT`:
68
- return count();
69
- // count() doesn't need a value extractor
70
- case `AVG`:
71
- return avg(valueExtractor);
72
- case `MIN`:
73
- return min(valueExtractor);
74
- case `MAX`:
75
- return max(valueExtractor);
76
- case `MEDIAN`:
77
- return median(valueExtractor);
78
- case `MODE`:
79
- return mode(valueExtractor);
80
- default:
81
- throw new Error(`Unsupported aggregate function: ${functionName}`);
82
- }
83
- }
84
- export {
85
- getAggregateFunction,
86
- processGroupBy
87
- };
88
- //# sourceMappingURL=group-by.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"group-by.js","sources":["../../../src/query/group-by.ts"],"sourcesContent":["import { groupBy, groupByOperators } from \"@electric-sql/d2mini\"\nimport {\n evaluateOperandOnNamespacedRow,\n extractValueFromNamespacedRow,\n} from \"./extractors\"\nimport { isAggregateFunctionCall } from \"./utils\"\nimport type { ConditionOperand, FunctionCall, Query } from \"./schema\"\nimport type { NamespacedAndKeyedStream } from \"../types.js\"\n\nconst { sum, count, avg, min, max, median, mode } = groupByOperators\n\n/**\n * Process the groupBy clause in a D2QL query\n */\nexport function processGroupBy(\n pipeline: NamespacedAndKeyedStream,\n query: Query,\n mainTableAlias: string\n) {\n // Normalize groupBy to an array of column references\n const groupByColumns = Array.isArray(query.groupBy)\n ? query.groupBy\n : [query.groupBy]\n\n // Create a key extractor function for the groupBy operator\n const keyExtractor = ([_oldKey, namespacedRow]: [\n string,\n Record<string, unknown>,\n ]) => {\n const key: Record<string, unknown> = {}\n\n // Extract each groupBy column value\n for (const column of groupByColumns) {\n if (typeof column === `string` && (column as string).startsWith(`@`)) {\n const columnRef = (column as string).substring(1)\n const columnName = columnRef.includes(`.`)\n ? columnRef.split(`.`)[1]\n : columnRef\n\n key[columnName!] = extractValueFromNamespacedRow(\n namespacedRow,\n columnRef,\n mainTableAlias\n )\n }\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 (!query.select) {\n throw new Error(`SELECT clause is required for GROUP BY`)\n }\n\n // Scan the SELECT clause for aggregate functions\n for (const item of query.select) {\n if (typeof item === `object`) {\n for (const [alias, expr] of Object.entries(item)) {\n if (typeof expr === `object` && isAggregateFunctionCall(expr)) {\n // Get the function name (the only key in the object)\n const functionName = Object.keys(expr)[0]\n // Get the column reference or expression to aggregate\n const columnRef = (expr as FunctionCall)[\n functionName as keyof FunctionCall\n ]\n\n // Add the aggregate function to our aggregates object\n aggregates[alias] = getAggregateFunction(\n functionName!,\n columnRef,\n mainTableAlias\n )\n }\n }\n }\n }\n\n // Apply the groupBy operator if we have any aggregates\n if (Object.keys(aggregates).length > 0) {\n pipeline = pipeline.pipe(groupBy(keyExtractor, aggregates))\n }\n\n return pipeline\n}\n\n/**\n * Helper function to get an aggregate function based on the function name\n */\nexport function getAggregateFunction(\n functionName: string,\n columnRef: string | ConditionOperand,\n mainTableAlias: string\n) {\n // Create a value extractor function for the column to aggregate\n const valueExtractor = ([_oldKey, namespacedRow]: [\n string,\n Record<string, unknown>,\n ]) => {\n let value: unknown\n if (typeof columnRef === `string` && columnRef.startsWith(`@`)) {\n value = extractValueFromNamespacedRow(\n namespacedRow,\n columnRef.substring(1),\n mainTableAlias\n )\n } else {\n value = evaluateOperandOnNamespacedRow(\n namespacedRow,\n columnRef as ConditionOperand,\n mainTableAlias\n )\n }\n // Ensure we return a number for aggregate functions\n return typeof value === `number` ? value : 0\n }\n\n // Return the appropriate aggregate function\n switch (functionName.toUpperCase()) {\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 case `MEDIAN`:\n return median(valueExtractor)\n case `MODE`:\n return mode(valueExtractor)\n default:\n throw new Error(`Unsupported aggregate function: ${functionName}`)\n }\n}\n"],"names":[],"mappings":";;;AASA,MAAM,EAAE,KAAK,OAAO,KAAK,KAAK,KAAK,QAAQ,SAAS;AAKpC,SAAA,eACd,UACA,OACA,gBACA;AAEM,QAAA,iBAAiB,MAAM,QAAQ,MAAM,OAAO,IAC9C,MAAM,UACN,CAAC,MAAM,OAAO;AAGlB,QAAM,eAAe,CAAC,CAAC,SAAS,aAAa,MAGvC;AACJ,UAAM,MAA+B,CAAC;AAGtC,eAAW,UAAU,gBAAgB;AACnC,UAAI,OAAO,WAAW,YAAa,OAAkB,WAAW,GAAG,GAAG;AAC9D,cAAA,YAAa,OAAkB,UAAU,CAAC;AAC1C,cAAA,aAAa,UAAU,SAAS,GAAG,IACrC,UAAU,MAAM,GAAG,EAAE,CAAC,IACtB;AAEJ,YAAI,UAAW,IAAI;AAAA,UACjB;AAAA,UACA;AAAA,UACA;AAAA,QACF;AAAA,MAAA;AAAA,IACF;AAGK,WAAA;AAAA,EACT;AAGA,QAAM,aAAkC,CAAC;AAErC,MAAA,CAAC,MAAM,QAAQ;AACX,UAAA,IAAI,MAAM,wCAAwC;AAAA,EAAA;AAI/C,aAAA,QAAQ,MAAM,QAAQ;AAC3B,QAAA,OAAO,SAAS,UAAU;AAC5B,iBAAW,CAAC,OAAO,IAAI,KAAK,OAAO,QAAQ,IAAI,GAAG;AAChD,YAAI,OAAO,SAAS,YAAY,wBAAwB,IAAI,GAAG;AAE7D,gBAAM,eAAe,OAAO,KAAK,IAAI,EAAE,CAAC;AAElC,gBAAA,YAAa,KACjB,YACF;AAGA,qBAAW,KAAK,IAAI;AAAA,YAClB;AAAA,YACA;AAAA,YACA;AAAA,UACF;AAAA,QAAA;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAIF,MAAI,OAAO,KAAK,UAAU,EAAE,SAAS,GAAG;AACtC,eAAW,SAAS,KAAK,QAAQ,cAAc,UAAU,CAAC;AAAA,EAAA;AAGrD,SAAA;AACT;AAKgB,SAAA,qBACd,cACA,WACA,gBACA;AAEA,QAAM,iBAAiB,CAAC,CAAC,SAAS,aAAa,MAGzC;AACA,QAAA;AACJ,QAAI,OAAO,cAAc,YAAY,UAAU,WAAW,GAAG,GAAG;AACtD,cAAA;AAAA,QACN;AAAA,QACA,UAAU,UAAU,CAAC;AAAA,QACrB;AAAA,MACF;AAAA,IAAA,OACK;AACG,cAAA;AAAA,QACN;AAAA,QACA;AAAA,QACA;AAAA,MACF;AAAA,IAAA;AAGK,WAAA,OAAO,UAAU,WAAW,QAAQ;AAAA,EAC7C;AAGQ,UAAA,aAAa,YAAe,GAAA;AAAA,IAClC,KAAK;AACH,aAAO,IAAI,cAAc;AAAA,IAC3B,KAAK;AACH,aAAO,MAAM;AAAA;AAAA,IACf,KAAK;AACH,aAAO,IAAI,cAAc;AAAA,IAC3B,KAAK;AACH,aAAO,IAAI,cAAc;AAAA,IAC3B,KAAK;AACH,aAAO,IAAI,cAAc;AAAA,IAC3B,KAAK;AACH,aAAO,OAAO,cAAc;AAAA,IAC9B,KAAK;AACH,aAAO,KAAK,cAAc;AAAA,IAC5B;AACE,YAAM,IAAI,MAAM,mCAAmC,YAAY,EAAE;AAAA,EAAA;AAEvE;"}
@@ -1,14 +0,0 @@
1
- import { Query } from './index.js';
2
- import { IStreamBuilder } from '@electric-sql/d2mini';
3
- import { KeyedStream, NamespacedAndKeyedStream, NamespacedRow } from '../types.js';
4
- /**
5
- * Creates a processing pipeline for join clauses
6
- */
7
- export declare function processJoinClause(pipeline: NamespacedAndKeyedStream, query: Query, tables: Record<string, KeyedStream>, mainTableAlias: string, allInputs: Record<string, KeyedStream>): NamespacedAndKeyedStream;
8
- /**
9
- * Creates a processing pipeline for join results
10
- */
11
- export declare function processJoinResults(mainTableAlias: string, joinedTableAlias: string, joinClause: {
12
- on: any;
13
- type: string;
14
- }): (pipeline: IStreamBuilder<[key: string, [[string, NamespacedRow] | undefined, [string, NamespacedRow] | undefined]]>) => NamespacedAndKeyedStream;
@@ -1,141 +0,0 @@
1
- import { map, join, consolidate, filter } from "@electric-sql/d2mini";
2
- import { evaluateConditionOnNamespacedRow } from "./evaluators.js";
3
- import { extractJoinKey } from "./extractors.js";
4
- function processJoinClause(pipeline, query, tables, mainTableAlias, allInputs) {
5
- if (!query.join) return pipeline;
6
- const input = allInputs[query.from];
7
- for (const joinClause of query.join) {
8
- const joinedTableAlias = joinClause.as || joinClause.from;
9
- const joinType = joinClause.type === `cross` ? `inner` : joinClause.type;
10
- const [mainKeyRef, , joinedKeyRefs] = joinClause.on;
11
- const mainPipeline = pipeline.pipe(
12
- map(([currentKey, namespacedRow]) => {
13
- const mainRow = namespacedRow[mainTableAlias];
14
- const key = extractJoinKey(mainRow, mainKeyRef, mainTableAlias);
15
- return [key, [currentKey, namespacedRow]];
16
- })
17
- );
18
- let joinedTableInput;
19
- if (allInputs[joinClause.from]) {
20
- joinedTableInput = allInputs[joinClause.from];
21
- } else {
22
- joinedTableInput = input.graph.newInput();
23
- }
24
- tables[joinedTableAlias] = joinedTableInput;
25
- const joinedPipeline = joinedTableInput.pipe(
26
- map(([currentKey, row]) => {
27
- const namespacedRow = { [joinedTableAlias]: row };
28
- const key = extractJoinKey(row, joinedKeyRefs, joinedTableAlias);
29
- return [key, [currentKey, namespacedRow]];
30
- })
31
- );
32
- switch (joinType) {
33
- case `inner`:
34
- pipeline = mainPipeline.pipe(
35
- join(joinedPipeline, `inner`),
36
- consolidate(),
37
- processJoinResults(mainTableAlias, joinedTableAlias, joinClause)
38
- );
39
- break;
40
- case `left`:
41
- pipeline = mainPipeline.pipe(
42
- join(joinedPipeline, `left`),
43
- consolidate(),
44
- processJoinResults(mainTableAlias, joinedTableAlias, joinClause)
45
- );
46
- break;
47
- case `right`:
48
- pipeline = mainPipeline.pipe(
49
- join(joinedPipeline, `right`),
50
- consolidate(),
51
- processJoinResults(mainTableAlias, joinedTableAlias, joinClause)
52
- );
53
- break;
54
- case `full`:
55
- pipeline = mainPipeline.pipe(
56
- join(joinedPipeline, `full`),
57
- consolidate(),
58
- processJoinResults(mainTableAlias, joinedTableAlias, joinClause)
59
- );
60
- break;
61
- default:
62
- pipeline = mainPipeline.pipe(
63
- join(joinedPipeline, `inner`),
64
- consolidate(),
65
- processJoinResults(mainTableAlias, joinedTableAlias, joinClause)
66
- );
67
- }
68
- }
69
- return pipeline;
70
- }
71
- function processJoinResults(mainTableAlias, joinedTableAlias, joinClause) {
72
- return function(pipeline) {
73
- return pipeline.pipe(
74
- // Process the join result and handle nulls in the same step
75
- map((result) => {
76
- const [_key, [main, joined]] = result;
77
- const mainKey = main == null ? void 0 : main[0];
78
- const mainNamespacedRow = main == null ? void 0 : main[1];
79
- const joinedKey = joined == null ? void 0 : joined[0];
80
- const joinedNamespacedRow = joined == null ? void 0 : joined[1];
81
- if (joinClause.type === `inner` || joinClause.type === `cross`) {
82
- if (!mainNamespacedRow || !joinedNamespacedRow) {
83
- return void 0;
84
- }
85
- }
86
- if (joinClause.type === `left` && !mainNamespacedRow) {
87
- return void 0;
88
- }
89
- if (joinClause.type === `right` && !joinedNamespacedRow) {
90
- return void 0;
91
- }
92
- const mergedNamespacedRow = {};
93
- if (mainNamespacedRow) {
94
- Object.entries(mainNamespacedRow).forEach(
95
- ([tableAlias, tableData]) => {
96
- mergedNamespacedRow[tableAlias] = tableData;
97
- }
98
- );
99
- }
100
- if (joinedNamespacedRow) {
101
- Object.entries(joinedNamespacedRow).forEach(
102
- ([tableAlias, tableData]) => {
103
- mergedNamespacedRow[tableAlias] = tableData;
104
- }
105
- );
106
- } else if (joinClause.type === `left` || joinClause.type === `full`) ;
107
- if (!mainNamespacedRow && (joinClause.type === `right` || joinClause.type === `full`)) ;
108
- const newKey = `[${mainKey},${joinedKey}]`;
109
- return [newKey, mergedNamespacedRow];
110
- }),
111
- // Filter out undefined results
112
- filter((value) => value !== void 0),
113
- // Process the ON condition
114
- filter(([_key, namespacedRow]) => {
115
- if (!joinClause.on || joinClause.type === `cross`) {
116
- return true;
117
- }
118
- if (joinClause.type === `left` && namespacedRow[joinedTableAlias] === void 0) {
119
- return true;
120
- }
121
- if (joinClause.type === `right` && namespacedRow[mainTableAlias] === void 0) {
122
- return true;
123
- }
124
- if (joinClause.type === `full` && (namespacedRow[mainTableAlias] === void 0 || namespacedRow[joinedTableAlias] === void 0)) {
125
- return true;
126
- }
127
- return evaluateConditionOnNamespacedRow(
128
- namespacedRow,
129
- joinClause.on,
130
- mainTableAlias,
131
- joinedTableAlias
132
- );
133
- })
134
- );
135
- };
136
- }
137
- export {
138
- processJoinClause,
139
- processJoinResults
140
- };
141
- //# sourceMappingURL=joins.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"joins.js","sources":["../../../src/query/joins.ts"],"sourcesContent":["import {\n consolidate,\n filter,\n join as joinOperator,\n map,\n} from \"@electric-sql/d2mini\"\nimport { evaluateConditionOnNamespacedRow } from \"./evaluators.js\"\nimport { extractJoinKey } from \"./extractors.js\"\nimport type { Query } from \"./index.js\"\nimport type { IStreamBuilder, JoinType } from \"@electric-sql/d2mini\"\nimport type {\n KeyedStream,\n NamespacedAndKeyedStream,\n NamespacedRow,\n} from \"../types.js\"\n\n/**\n * Creates a processing pipeline for join clauses\n */\nexport function processJoinClause(\n pipeline: NamespacedAndKeyedStream,\n query: Query,\n tables: Record<string, KeyedStream>,\n mainTableAlias: string,\n allInputs: Record<string, KeyedStream>\n) {\n if (!query.join) return pipeline\n const input = allInputs[query.from]\n\n for (const joinClause of query.join) {\n // Create a stream for the joined table\n const joinedTableAlias = joinClause.as || joinClause.from\n\n // Get the right join type for the operator\n const joinType: JoinType =\n joinClause.type === `cross` ? `inner` : joinClause.type\n\n // The `in` is formatted as ['@mainKeyRef', '=', '@joinedKeyRef']\n // Destructure the main key reference and the joined key references\n const [mainKeyRef, , joinedKeyRefs] = joinClause.on\n\n // We need to prepare the main pipeline and the joined pipeline\n // to have the correct key format for joining\n const mainPipeline = pipeline.pipe(\n map(([currentKey, namespacedRow]) => {\n // Extract the key from the ON condition left side for the main table\n const mainRow = namespacedRow[mainTableAlias]!\n\n // Extract the join key from the main row\n const key = extractJoinKey(mainRow, mainKeyRef, mainTableAlias)\n\n // Return [key, namespacedRow] as a KeyValue type\n return [key, [currentKey, namespacedRow]] as [\n unknown,\n [string, typeof namespacedRow],\n ]\n })\n )\n\n // Get the joined table input from the inputs map\n let joinedTableInput: KeyedStream\n\n if (allInputs[joinClause.from]) {\n // Use the provided input if available\n joinedTableInput = allInputs[joinClause.from]!\n } else {\n // Create a new input if not provided\n joinedTableInput =\n input!.graph.newInput<[string, Record<string, unknown>]>()\n }\n\n tables[joinedTableAlias] = joinedTableInput\n\n // Create a pipeline for the joined table\n const joinedPipeline = joinedTableInput.pipe(\n map(([currentKey, row]) => {\n // Wrap the row in an object with the table alias as the key\n const namespacedRow: NamespacedRow = { [joinedTableAlias]: row }\n\n // Extract the key from the ON condition right side for the joined table\n const key = extractJoinKey(row, joinedKeyRefs, joinedTableAlias)\n\n // Return [key, namespacedRow] as a KeyValue type\n return [key, [currentKey, namespacedRow]] as [\n string,\n [string, typeof namespacedRow],\n ]\n })\n )\n\n // Apply join with appropriate typings based on join type\n switch (joinType) {\n case `inner`:\n pipeline = mainPipeline.pipe(\n joinOperator(joinedPipeline, `inner`),\n consolidate(),\n processJoinResults(mainTableAlias, joinedTableAlias, joinClause)\n )\n break\n case `left`:\n pipeline = mainPipeline.pipe(\n joinOperator(joinedPipeline, `left`),\n consolidate(),\n processJoinResults(mainTableAlias, joinedTableAlias, joinClause)\n )\n break\n case `right`:\n pipeline = mainPipeline.pipe(\n joinOperator(joinedPipeline, `right`),\n consolidate(),\n processJoinResults(mainTableAlias, joinedTableAlias, joinClause)\n )\n break\n case `full`:\n pipeline = mainPipeline.pipe(\n joinOperator(joinedPipeline, `full`),\n consolidate(),\n processJoinResults(mainTableAlias, joinedTableAlias, joinClause)\n )\n break\n default:\n pipeline = mainPipeline.pipe(\n joinOperator(joinedPipeline, `inner`),\n consolidate(),\n processJoinResults(mainTableAlias, joinedTableAlias, joinClause)\n )\n }\n }\n return pipeline\n}\n\n/**\n * Creates a processing pipeline for join results\n */\nexport function processJoinResults(\n mainTableAlias: string,\n joinedTableAlias: string,\n joinClause: { on: any; type: string }\n) {\n return function (\n pipeline: IStreamBuilder<\n [\n key: string,\n [\n [string, NamespacedRow] | undefined,\n [string, NamespacedRow] | undefined,\n ],\n ]\n >\n ): NamespacedAndKeyedStream {\n return pipeline.pipe(\n // Process the join result and handle nulls in the same step\n map((result) => {\n const [_key, [main, joined]] = result\n const mainKey = main?.[0]\n const mainNamespacedRow = main?.[1]\n const joinedKey = joined?.[0]\n const joinedNamespacedRow = joined?.[1]\n\n // For inner joins, both sides should be non-null\n if (joinClause.type === `inner` || joinClause.type === `cross`) {\n if (!mainNamespacedRow || !joinedNamespacedRow) {\n return undefined // Will be filtered out\n }\n }\n\n // For left joins, the main row must be non-null\n if (joinClause.type === `left` && !mainNamespacedRow) {\n return undefined // Will be filtered out\n }\n\n // For right joins, the joined row must be non-null\n if (joinClause.type === `right` && !joinedNamespacedRow) {\n return undefined // Will be filtered out\n }\n\n // Merge the nested rows\n const mergedNamespacedRow: NamespacedRow = {}\n\n // Add main row data if it exists\n if (mainNamespacedRow) {\n Object.entries(mainNamespacedRow).forEach(\n ([tableAlias, tableData]) => {\n mergedNamespacedRow[tableAlias] = tableData\n }\n )\n }\n\n // If we have a joined row, add it to the merged result\n if (joinedNamespacedRow) {\n Object.entries(joinedNamespacedRow).forEach(\n ([tableAlias, tableData]) => {\n mergedNamespacedRow[tableAlias] = tableData\n }\n )\n } else if (joinClause.type === `left` || joinClause.type === `full`) {\n // For left or full joins, add the joined table with undefined data if missing\n // mergedNamespacedRow[joinedTableAlias] = undefined\n }\n\n // For right or full joins, add the main table with undefined data if missing\n if (\n !mainNamespacedRow &&\n (joinClause.type === `right` || joinClause.type === `full`)\n ) {\n // mergedNamespacedRow[mainTableAlias] = undefined\n }\n\n // New key\n const newKey = `[${mainKey},${joinedKey}]`\n\n return [newKey, mergedNamespacedRow] as [\n string,\n typeof mergedNamespacedRow,\n ]\n }),\n // Filter out undefined results\n filter((value) => value !== undefined),\n // Process the ON condition\n filter(([_key, namespacedRow]: [string, NamespacedRow]) => {\n // If there's no ON condition, or it's a cross join, always return true\n if (!joinClause.on || joinClause.type === `cross`) {\n return true\n }\n\n // For LEFT JOIN, if the right side is null, we should include the row\n if (\n joinClause.type === `left` &&\n namespacedRow[joinedTableAlias] === undefined\n ) {\n return true\n }\n\n // For RIGHT JOIN, if the left side is null, we should include the row\n if (\n joinClause.type === `right` &&\n namespacedRow[mainTableAlias] === undefined\n ) {\n return true\n }\n\n // For FULL JOIN, if either side is null, we should include the row\n if (\n joinClause.type === `full` &&\n (namespacedRow[mainTableAlias] === undefined ||\n namespacedRow[joinedTableAlias] === undefined)\n ) {\n return true\n }\n\n return evaluateConditionOnNamespacedRow(\n namespacedRow,\n joinClause.on,\n mainTableAlias,\n joinedTableAlias\n )\n })\n )\n }\n}\n"],"names":["joinOperator"],"mappings":"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"}
@@ -1,3 +0,0 @@
1
- import { Query } from './schema.js';
2
- import { NamespacedAndKeyedStream } from '../types.js';
3
- export declare function processOrderBy(resultPipeline: NamespacedAndKeyedStream, query: Query, mainTableAlias: string): NamespacedAndKeyedStream;
@@ -1,185 +0,0 @@
1
- import { orderByWithIndex, map, orderByWithFractionalIndex, orderBy } from "@electric-sql/d2mini";
2
- import { evaluateOperandOnNamespacedRow } from "./extractors.js";
3
- import { isOrderIndexFunctionCall } from "./utils.js";
4
- function processOrderBy(resultPipeline, query, mainTableAlias) {
5
- let hasOrderIndexColumn = false;
6
- let orderIndexType = `numeric`;
7
- let orderIndexAlias = ``;
8
- for (const item of query.select) {
9
- if (typeof item === `object`) {
10
- for (const [alias, expr] of Object.entries(item)) {
11
- if (typeof expr === `object` && isOrderIndexFunctionCall(expr)) {
12
- hasOrderIndexColumn = true;
13
- orderIndexAlias = alias;
14
- orderIndexType = getOrderIndexType(expr);
15
- break;
16
- }
17
- }
18
- }
19
- if (hasOrderIndexColumn) break;
20
- }
21
- const orderByItems = [];
22
- if (typeof query.orderBy === `string`) {
23
- orderByItems.push({
24
- operand: query.orderBy,
25
- direction: `asc`
26
- });
27
- } else if (Array.isArray(query.orderBy)) {
28
- for (const item of query.orderBy) {
29
- if (typeof item === `string`) {
30
- orderByItems.push({
31
- operand: item,
32
- direction: `asc`
33
- });
34
- } else if (typeof item === `object`) {
35
- for (const [column, direction] of Object.entries(item)) {
36
- orderByItems.push({
37
- operand: column,
38
- direction
39
- });
40
- }
41
- }
42
- }
43
- } else if (typeof query.orderBy === `object`) {
44
- for (const [column, direction] of Object.entries(query.orderBy)) {
45
- orderByItems.push({
46
- operand: column,
47
- direction
48
- });
49
- }
50
- }
51
- const valueExtractor = (namespacedRow) => {
52
- if (orderByItems.length > 1) {
53
- return orderByItems.map(
54
- (item) => evaluateOperandOnNamespacedRow(
55
- namespacedRow,
56
- item.operand,
57
- mainTableAlias
58
- )
59
- );
60
- } else if (orderByItems.length === 1) {
61
- const item = orderByItems[0];
62
- const val = evaluateOperandOnNamespacedRow(
63
- namespacedRow,
64
- item.operand,
65
- mainTableAlias
66
- );
67
- return val;
68
- }
69
- return null;
70
- };
71
- const ascComparator = (a, b) => {
72
- if (typeof a === `string` && typeof b === `string`) {
73
- return a.localeCompare(b);
74
- }
75
- if (Array.isArray(a) && Array.isArray(b)) {
76
- for (let i = 0; i < Math.min(a.length, b.length); i++) {
77
- const result = ascComparator(a[i], b[i]);
78
- if (result !== 0) {
79
- return result;
80
- }
81
- }
82
- return a.length - b.length;
83
- }
84
- const bothObjects = typeof a === `object` && typeof b === `object`;
85
- const bothDates = a instanceof Date && b instanceof Date;
86
- const notNull = a !== null && b !== null;
87
- if (bothObjects && !bothDates && notNull) {
88
- return a.toString().localeCompare(b.toString());
89
- }
90
- if (a < b) return -1;
91
- if (a > b) return 1;
92
- return 0;
93
- };
94
- const descComparator = (a, b) => {
95
- return ascComparator(b, a);
96
- };
97
- const makeComparator = (orderByProps) => {
98
- return (a, b) => {
99
- if (orderByProps.length > 1) {
100
- const arrayA = a;
101
- const arrayB = b;
102
- for (let i = 0; i < orderByProps.length; i++) {
103
- const direction = orderByProps[i].direction;
104
- const compareFn = direction === `desc` ? descComparator : ascComparator;
105
- const result = compareFn(arrayA[i], arrayB[i]);
106
- if (result !== 0) {
107
- return result;
108
- }
109
- }
110
- return arrayA.length - arrayB.length;
111
- }
112
- if (orderByProps.length === 1) {
113
- const direction = orderByProps[0].direction;
114
- return direction === `desc` ? descComparator(a, b) : ascComparator(a, b);
115
- }
116
- return ascComparator(a, b);
117
- };
118
- };
119
- const comparator = makeComparator(orderByItems);
120
- if (hasOrderIndexColumn) {
121
- if (orderIndexType === `numeric`) {
122
- resultPipeline = resultPipeline.pipe(
123
- orderByWithIndex(valueExtractor, {
124
- limit: query.limit,
125
- offset: query.offset,
126
- comparator
127
- }),
128
- map(([key, [value, index]]) => {
129
- const result = {
130
- ...value,
131
- [mainTableAlias]: {
132
- ...value[mainTableAlias],
133
- [orderIndexAlias]: index
134
- }
135
- };
136
- return [key, result];
137
- })
138
- );
139
- } else {
140
- resultPipeline = resultPipeline.pipe(
141
- orderByWithFractionalIndex(valueExtractor, {
142
- limit: query.limit,
143
- offset: query.offset,
144
- comparator
145
- }),
146
- map(([key, [value, index]]) => {
147
- const result = {
148
- ...value,
149
- [mainTableAlias]: {
150
- ...value[mainTableAlias],
151
- [orderIndexAlias]: index
152
- }
153
- };
154
- return [key, result];
155
- })
156
- );
157
- }
158
- } else {
159
- resultPipeline = resultPipeline.pipe(
160
- orderBy(valueExtractor, {
161
- limit: query.limit,
162
- offset: query.offset,
163
- comparator
164
- })
165
- );
166
- }
167
- return resultPipeline;
168
- }
169
- function getOrderIndexType(obj) {
170
- if (!isOrderIndexFunctionCall(obj)) {
171
- throw new Error(`Not an ORDER_INDEX function call`);
172
- }
173
- const arg = obj[`ORDER_INDEX`];
174
- if (arg === `numeric` || arg === true || arg === `default`) {
175
- return `numeric`;
176
- } else if (arg === `fractional`) {
177
- return `fractional`;
178
- } else {
179
- throw new Error(`Invalid ORDER_INDEX type: ` + arg);
180
- }
181
- }
182
- export {
183
- processOrderBy
184
- };
185
- //# sourceMappingURL=order-by.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"order-by.js","sources":["../../../src/query/order-by.ts"],"sourcesContent":["import {\n map,\n orderBy,\n orderByWithFractionalIndex,\n orderByWithIndex,\n} from \"@electric-sql/d2mini\"\nimport { evaluateOperandOnNamespacedRow } from \"./extractors\"\nimport { isOrderIndexFunctionCall } from \"./utils\"\nimport type { ConditionOperand, Query } from \"./schema\"\nimport type {\n KeyedNamespacedRow,\n NamespacedAndKeyedStream,\n NamespacedRow,\n} from \"../types\"\n\ntype OrderByItem = {\n operand: ConditionOperand\n direction: `asc` | `desc`\n}\n\ntype OrderByItems = Array<OrderByItem>\n\nexport function processOrderBy(\n resultPipeline: NamespacedAndKeyedStream,\n query: Query,\n mainTableAlias: string\n) {\n // Check if any column in the SELECT clause is an ORDER_INDEX function call\n let hasOrderIndexColumn = false\n let orderIndexType: `numeric` | `fractional` = `numeric`\n let orderIndexAlias = ``\n\n // Scan the SELECT clause for ORDER_INDEX functions\n // TODO: Select is going to be optional in future - we will automatically add an\n // attribute for the index column\n for (const item of query.select!) {\n if (typeof item === `object`) {\n for (const [alias, expr] of Object.entries(item)) {\n if (typeof expr === `object` && isOrderIndexFunctionCall(expr)) {\n hasOrderIndexColumn = true\n orderIndexAlias = alias\n orderIndexType = getOrderIndexType(expr)\n break\n }\n }\n }\n if (hasOrderIndexColumn) break\n }\n\n // Normalize orderBy to an array of objects\n const orderByItems: OrderByItems = []\n\n if (typeof query.orderBy === `string`) {\n // Handle string format: '@column'\n orderByItems.push({\n operand: query.orderBy,\n direction: `asc`,\n })\n } else if (Array.isArray(query.orderBy)) {\n // Handle array format: ['@column1', { '@column2': 'desc' }]\n for (const item of query.orderBy) {\n if (typeof item === `string`) {\n orderByItems.push({\n operand: item,\n direction: `asc`,\n })\n } else if (typeof item === `object`) {\n for (const [column, direction] of Object.entries(item)) {\n orderByItems.push({\n operand: column,\n direction: direction as `asc` | `desc`,\n })\n }\n }\n }\n } else if (typeof query.orderBy === `object`) {\n // Handle object format: { '@column': 'desc' }\n for (const [column, direction] of Object.entries(query.orderBy)) {\n orderByItems.push({\n operand: column,\n direction: direction as `asc` | `desc`,\n })\n }\n }\n\n // Create a value extractor function for the orderBy operator\n // const valueExtractor = ([key, namespacedRow]: [\n const valueExtractor = (namespacedRow: NamespacedRow) => {\n // For multiple orderBy columns, create a composite key\n if (orderByItems.length > 1) {\n return orderByItems.map((item) =>\n evaluateOperandOnNamespacedRow(\n namespacedRow,\n item.operand,\n mainTableAlias\n )\n )\n } else if (orderByItems.length === 1) {\n // For a single orderBy column, use the value directly\n const item = orderByItems[0]\n const val = evaluateOperandOnNamespacedRow(\n namespacedRow,\n item!.operand,\n mainTableAlias\n )\n return val\n }\n\n // Default case - no ordering\n return null\n }\n\n const ascComparator = (a: any, b: any): number => {\n // if a and b are both strings, compare them based on locale\n if (typeof a === `string` && typeof b === `string`) {\n return a.localeCompare(b)\n }\n\n // if a and b are both arrays, compare them element by element\n if (Array.isArray(a) && Array.isArray(b)) {\n for (let i = 0; i < Math.min(a.length, b.length); i++) {\n // Compare the values\n const result = ascComparator(a[i], b[i])\n\n if (result !== 0) {\n return result\n }\n }\n // All elements are equal up to the minimum length\n return a.length - b.length\n }\n\n // If at least one of the values is an object then we don't really know how to meaningfully compare them\n // therefore we turn them into strings and compare those\n // There are 2 exceptions:\n // 1) if both objects are dates then we can compare them\n // 2) if either object is nullish then we can't call toString on it\n const bothObjects = typeof a === `object` && typeof b === `object`\n const bothDates = a instanceof Date && b instanceof Date\n const notNull = a !== null && b !== null\n if (bothObjects && !bothDates && notNull) {\n // Every object should support `toString`\n return a.toString().localeCompare(b.toString())\n }\n\n if (a < b) return -1\n if (a > b) return 1\n return 0\n }\n\n const descComparator = (a: unknown, b: unknown): number => {\n return ascComparator(b, a)\n }\n\n // Create a multi-property comparator that respects the order and direction of each property\n const makeComparator = (orderByProps: OrderByItems) => {\n return (a: unknown, b: unknown) => {\n // If we're comparing arrays (multiple properties), compare each property in order\n if (orderByProps.length > 1) {\n // `a` and `b` must be arrays since `orderByItems.length > 1`\n // hence the extracted values must be arrays\n const arrayA = a as Array<unknown>\n const arrayB = b as Array<unknown>\n for (let i = 0; i < orderByProps.length; i++) {\n const direction = orderByProps[i]!.direction\n const compareFn =\n direction === `desc` ? descComparator : ascComparator\n const result = compareFn(arrayA[i], arrayB[i])\n if (result !== 0) {\n return result\n }\n }\n // should normally always be 0 because\n // both values are extracted based on orderByItems\n return arrayA.length - arrayB.length\n }\n\n // Single property comparison\n if (orderByProps.length === 1) {\n const direction = orderByProps[0]!.direction\n return direction === `desc` ? descComparator(a, b) : ascComparator(a, b)\n }\n\n return ascComparator(a, b)\n }\n }\n const comparator = makeComparator(orderByItems)\n\n // Apply the appropriate orderBy operator based on whether an ORDER_INDEX column is requested\n if (hasOrderIndexColumn) {\n if (orderIndexType === `numeric`) {\n // Use orderByWithIndex for numeric indices\n resultPipeline = resultPipeline.pipe(\n orderByWithIndex(valueExtractor, {\n limit: query.limit,\n offset: query.offset,\n comparator,\n }),\n map(([key, [value, index]]) => {\n // Add the index to the result\n // We add this to the main table alias for now\n // TODO: re are going to need to refactor the whole order by pipeline\n const result = {\n ...(value as Record<string, unknown>),\n [mainTableAlias]: {\n ...value[mainTableAlias],\n [orderIndexAlias]: index,\n },\n }\n return [key, result] as KeyedNamespacedRow\n })\n )\n } else {\n // Use orderByWithFractionalIndex for fractional indices\n resultPipeline = resultPipeline.pipe(\n orderByWithFractionalIndex(valueExtractor, {\n limit: query.limit,\n offset: query.offset,\n comparator,\n }),\n map(([key, [value, index]]) => {\n // Add the index to the result\n // We add this to the main table alias for now\n // TODO: re are going to need to refactor the whole order by pipeline\n const result = {\n ...(value as Record<string, unknown>),\n [mainTableAlias]: {\n ...value[mainTableAlias],\n [orderIndexAlias]: index,\n },\n }\n return [key, result] as KeyedNamespacedRow\n })\n )\n }\n } else {\n // Use regular orderBy if no index column is requested\n resultPipeline = resultPipeline.pipe(\n orderBy(valueExtractor, {\n limit: query.limit,\n offset: query.offset,\n comparator,\n })\n )\n }\n\n return resultPipeline\n}\n\n// Helper function to extract the ORDER_INDEX type from a function call\nfunction getOrderIndexType(obj: any): `numeric` | `fractional` {\n if (!isOrderIndexFunctionCall(obj)) {\n throw new Error(`Not an ORDER_INDEX function call`)\n }\n\n const arg = obj[`ORDER_INDEX`]\n if (arg === `numeric` || arg === true || arg === `default`) {\n return `numeric`\n } else if (arg === `fractional`) {\n return `fractional`\n } else {\n throw new Error(`Invalid ORDER_INDEX type: ` + arg)\n }\n}\n"],"names":[],"mappings":"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"}
@@ -1,10 +0,0 @@
1
- import { Query } from './schema.js';
2
- import { IStreamBuilder } from '@electric-sql/d2mini';
3
- import { KeyedStream } from '../types.js';
4
- /**
5
- * Compiles a query into a D2 pipeline
6
- * @param query The query to compile
7
- * @param inputs Mapping of table names to input streams
8
- * @returns A stream builder representing the compiled query
9
- */
10
- export declare function compileQueryPipeline<T extends IStreamBuilder<unknown>>(query: Query, inputs: Record<string, KeyedStream>): T;
@@ -1,89 +0,0 @@
1
- import { map, filter } from "@electric-sql/d2mini";
2
- import { evaluateWhereOnNamespacedRow } from "./evaluators.js";
3
- import { processJoinClause } from "./joins.js";
4
- import { processGroupBy } from "./group-by.js";
5
- import { processOrderBy } from "./order-by.js";
6
- import { processSelect } from "./select.js";
7
- function compileQueryPipeline(query, inputs) {
8
- const allInputs = { ...inputs };
9
- if (query.with && query.with.length > 0) {
10
- for (const withQuery of query.with) {
11
- if (!withQuery.as) {
12
- throw new Error(`WITH query must have an "as" property`);
13
- }
14
- if (allInputs[withQuery.as]) {
15
- throw new Error(`CTE with name "${withQuery.as}" already exists`);
16
- }
17
- const withQueryWithoutWith = { ...withQuery, with: void 0 };
18
- const compiledWithQuery = compileQueryPipeline(
19
- withQueryWithoutWith,
20
- allInputs
21
- );
22
- allInputs[withQuery.as] = compiledWithQuery;
23
- }
24
- }
25
- const tables = {};
26
- const mainTableAlias = query.as || query.from;
27
- const input = allInputs[query.from];
28
- if (!input) {
29
- throw new Error(`Input for table "${query.from}" not found in inputs map`);
30
- }
31
- tables[mainTableAlias] = input;
32
- let pipeline = input.pipe(
33
- map(([key, row]) => {
34
- const ret = [key, { [mainTableAlias]: row }];
35
- return ret;
36
- })
37
- );
38
- if (query.join) {
39
- pipeline = processJoinClause(
40
- pipeline,
41
- query,
42
- tables,
43
- mainTableAlias,
44
- allInputs
45
- );
46
- }
47
- if (query.where) {
48
- pipeline = pipeline.pipe(
49
- filter(([_key, row]) => {
50
- const result = evaluateWhereOnNamespacedRow(
51
- row,
52
- query.where,
53
- mainTableAlias
54
- );
55
- return result;
56
- })
57
- );
58
- }
59
- if (query.groupBy) {
60
- pipeline = processGroupBy(pipeline, query, mainTableAlias);
61
- }
62
- if (query.having) {
63
- pipeline = pipeline.pipe(
64
- filter(([_key, row]) => {
65
- const result = evaluateWhereOnNamespacedRow(
66
- row,
67
- query.having,
68
- mainTableAlias
69
- );
70
- return result;
71
- })
72
- );
73
- }
74
- if (query.orderBy) {
75
- pipeline = processOrderBy(pipeline, query, mainTableAlias);
76
- } else if (query.limit !== void 0 || query.offset !== void 0) {
77
- throw new Error(
78
- `LIMIT and OFFSET require an ORDER BY clause to ensure deterministic results`
79
- );
80
- }
81
- const resultPipeline = query.select ? processSelect(pipeline, query, mainTableAlias, allInputs) : !query.join && !query.groupBy ? pipeline.pipe(
82
- map(([key, row]) => [key, row[mainTableAlias]])
83
- ) : pipeline;
84
- return resultPipeline;
85
- }
86
- export {
87
- compileQueryPipeline
88
- };
89
- //# sourceMappingURL=pipeline-compiler.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"pipeline-compiler.js","sources":["../../../src/query/pipeline-compiler.ts"],"sourcesContent":["import { filter, map } from \"@electric-sql/d2mini\"\nimport { evaluateWhereOnNamespacedRow } from \"./evaluators.js\"\nimport { processJoinClause } from \"./joins.js\"\nimport { processGroupBy } from \"./group-by.js\"\nimport { processOrderBy } from \"./order-by.js\"\nimport { processSelect } from \"./select.js\"\nimport type { Query } from \"./schema.js\"\nimport type { IStreamBuilder } from \"@electric-sql/d2mini\"\nimport type {\n InputRow,\n KeyedStream,\n NamespacedAndKeyedStream,\n} from \"../types.js\"\n\n/**\n * Compiles a query into a D2 pipeline\n * @param query The query to compile\n * @param inputs Mapping of table names to input streams\n * @returns A stream builder representing the compiled query\n */\nexport function compileQueryPipeline<T extends IStreamBuilder<unknown>>(\n query: Query,\n inputs: Record<string, KeyedStream>\n): T {\n // Create a copy of the inputs map to avoid modifying the original\n const allInputs = { ...inputs }\n\n // Process WITH queries if they exist\n if (query.with && query.with.length > 0) {\n // Process each WITH query in order\n for (const withQuery of query.with) {\n // Ensure the WITH query has an alias\n if (!withQuery.as) {\n throw new Error(`WITH query must have an \"as\" property`)\n }\n\n // Check if this CTE name already exists in the inputs\n if (allInputs[withQuery.as]) {\n throw new Error(`CTE with name \"${withQuery.as}\" already exists`)\n }\n\n // Create a new query without the 'with' property to avoid circular references\n const withQueryWithoutWith = { ...withQuery, with: undefined }\n\n // Compile the WITH query using the current set of inputs\n // (which includes previously compiled WITH queries)\n const compiledWithQuery = compileQueryPipeline(\n withQueryWithoutWith,\n allInputs\n )\n\n // Add the compiled query to the inputs map using its alias\n allInputs[withQuery.as] = compiledWithQuery as KeyedStream\n }\n }\n\n // Create a map of table aliases to inputs\n const tables: Record<string, KeyedStream> = {}\n\n // The main table is the one in the FROM clause\n const mainTableAlias = query.as || query.from\n\n // Get the main input from the inputs map (now including CTEs)\n const input = allInputs[query.from]\n if (!input) {\n throw new Error(`Input for table \"${query.from}\" not found in inputs map`)\n }\n\n tables[mainTableAlias] = input\n\n // Prepare the initial pipeline with the main table wrapped in its alias\n let pipeline: NamespacedAndKeyedStream = input.pipe(\n map(([key, row]) => {\n // Initialize the record with a nested structure\n const ret = [key, { [mainTableAlias]: row }] as [\n string,\n Record<string, typeof row>,\n ]\n return ret\n })\n )\n\n // Process JOIN clauses if they exist\n if (query.join) {\n pipeline = processJoinClause(\n pipeline,\n query,\n tables,\n mainTableAlias,\n allInputs\n )\n }\n\n // Process the WHERE clause if it exists\n if (query.where) {\n pipeline = pipeline.pipe(\n filter(([_key, row]) => {\n const result = evaluateWhereOnNamespacedRow(\n row,\n query.where!,\n mainTableAlias\n )\n return result\n })\n )\n }\n\n // Process the GROUP BY clause if it exists\n if (query.groupBy) {\n pipeline = processGroupBy(pipeline, query, mainTableAlias)\n }\n\n // Process the HAVING clause if it exists\n // This works similarly to WHERE but is applied after any aggregations\n if (query.having) {\n pipeline = pipeline.pipe(\n filter(([_key, row]) => {\n // For HAVING, we're working with the flattened row that contains both\n // the group by keys and the aggregate results directly\n const result = evaluateWhereOnNamespacedRow(\n row,\n query.having!,\n mainTableAlias\n )\n return result\n })\n )\n }\n\n // Process orderBy parameter if it exists\n if (query.orderBy) {\n pipeline = processOrderBy(pipeline, query, mainTableAlias)\n } else if (query.limit !== undefined || query.offset !== undefined) {\n // If there's a limit or offset without orderBy, throw an error\n throw new Error(\n `LIMIT and OFFSET require an ORDER BY clause to ensure deterministic results`\n )\n }\n\n // Process the SELECT clause - this is where we flatten the structure\n const resultPipeline: KeyedStream | NamespacedAndKeyedStream = query.select\n ? processSelect(pipeline, query, mainTableAlias, allInputs)\n : !query.join && !query.groupBy\n ? pipeline.pipe(\n map(([key, row]) => [key, row[mainTableAlias]] as InputRow)\n )\n : pipeline\n return resultPipeline as T\n}\n"],"names":[],"mappings":";;;;;;AAoBgB,SAAA,qBACd,OACA,QACG;AAEG,QAAA,YAAY,EAAE,GAAG,OAAO;AAG9B,MAAI,MAAM,QAAQ,MAAM,KAAK,SAAS,GAAG;AAE5B,eAAA,aAAa,MAAM,MAAM;AAE9B,UAAA,CAAC,UAAU,IAAI;AACX,cAAA,IAAI,MAAM,uCAAuC;AAAA,MAAA;AAIrD,UAAA,UAAU,UAAU,EAAE,GAAG;AAC3B,cAAM,IAAI,MAAM,kBAAkB,UAAU,EAAE,kBAAkB;AAAA,MAAA;AAIlE,YAAM,uBAAuB,EAAE,GAAG,WAAW,MAAM,OAAU;AAI7D,YAAM,oBAAoB;AAAA,QACxB;AAAA,QACA;AAAA,MACF;AAGU,gBAAA,UAAU,EAAE,IAAI;AAAA,IAAA;AAAA,EAC5B;AAIF,QAAM,SAAsC,CAAC;AAGvC,QAAA,iBAAiB,MAAM,MAAM,MAAM;AAGnC,QAAA,QAAQ,UAAU,MAAM,IAAI;AAClC,MAAI,CAAC,OAAO;AACV,UAAM,IAAI,MAAM,oBAAoB,MAAM,IAAI,2BAA2B;AAAA,EAAA;AAG3E,SAAO,cAAc,IAAI;AAGzB,MAAI,WAAqC,MAAM;AAAA,IAC7C,IAAI,CAAC,CAAC,KAAK,GAAG,MAAM;AAEZ,YAAA,MAAM,CAAC,KAAK,EAAE,CAAC,cAAc,GAAG,KAAK;AAIpC,aAAA;AAAA,IACR,CAAA;AAAA,EACH;AAGA,MAAI,MAAM,MAAM;AACH,eAAA;AAAA,MACT;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAAA,EAAA;AAIF,MAAI,MAAM,OAAO;AACf,eAAW,SAAS;AAAA,MAClB,OAAO,CAAC,CAAC,MAAM,GAAG,MAAM;AACtB,cAAM,SAAS;AAAA,UACb;AAAA,UACA,MAAM;AAAA,UACN;AAAA,QACF;AACO,eAAA;AAAA,MACR,CAAA;AAAA,IACH;AAAA,EAAA;AAIF,MAAI,MAAM,SAAS;AACN,eAAA,eAAe,UAAU,OAAO,cAAc;AAAA,EAAA;AAK3D,MAAI,MAAM,QAAQ;AAChB,eAAW,SAAS;AAAA,MAClB,OAAO,CAAC,CAAC,MAAM,GAAG,MAAM;AAGtB,cAAM,SAAS;AAAA,UACb;AAAA,UACA,MAAM;AAAA,UACN;AAAA,QACF;AACO,eAAA;AAAA,MACR,CAAA;AAAA,IACH;AAAA,EAAA;AAIF,MAAI,MAAM,SAAS;AACN,eAAA,eAAe,UAAU,OAAO,cAAc;AAAA,EAAA,WAChD,MAAM,UAAU,UAAa,MAAM,WAAW,QAAW;AAElE,UAAM,IAAI;AAAA,MACR;AAAA,IACF;AAAA,EAAA;AAIF,QAAM,iBAAyD,MAAM,SACjE,cAAc,UAAU,OAAO,gBAAgB,SAAS,IACxD,CAAC,MAAM,QAAQ,CAAC,MAAM,UACpB,SAAS;AAAA,IACP,IAAI,CAAC,CAAC,KAAK,GAAG,MAAM,CAAC,KAAK,IAAI,cAAc,CAAC,CAAa;AAAA,EAAA,IAE5D;AACC,SAAA;AACT;"}