@tanstack/db 0.5.33 → 0.6.0

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 (273) hide show
  1. package/dist/cjs/collection/change-events.cjs.map +1 -1
  2. package/dist/cjs/collection/change-events.d.cts +3 -2
  3. package/dist/cjs/collection/changes.cjs +13 -4
  4. package/dist/cjs/collection/changes.cjs.map +1 -1
  5. package/dist/cjs/collection/changes.d.cts +10 -1
  6. package/dist/cjs/collection/cleanup-queue.cjs +89 -0
  7. package/dist/cjs/collection/cleanup-queue.cjs.map +1 -0
  8. package/dist/cjs/collection/cleanup-queue.d.cts +30 -0
  9. package/dist/cjs/collection/events.cjs +14 -0
  10. package/dist/cjs/collection/events.cjs.map +1 -1
  11. package/dist/cjs/collection/events.d.cts +39 -1
  12. package/dist/cjs/collection/index.cjs +66 -28
  13. package/dist/cjs/collection/index.cjs.map +1 -1
  14. package/dist/cjs/collection/index.d.cts +49 -36
  15. package/dist/cjs/collection/indexes.cjs +211 -62
  16. package/dist/cjs/collection/indexes.cjs.map +1 -1
  17. package/dist/cjs/collection/indexes.d.cts +27 -17
  18. package/dist/cjs/collection/lifecycle.cjs +5 -22
  19. package/dist/cjs/collection/lifecycle.cjs.map +1 -1
  20. package/dist/cjs/collection/lifecycle.d.cts +0 -1
  21. package/dist/cjs/collection/mutations.cjs +18 -0
  22. package/dist/cjs/collection/mutations.cjs.map +1 -1
  23. package/dist/cjs/collection/mutations.d.cts +1 -0
  24. package/dist/cjs/collection/state.cjs +381 -53
  25. package/dist/cjs/collection/state.cjs.map +1 -1
  26. package/dist/cjs/collection/state.d.cts +65 -1
  27. package/dist/cjs/collection/subscription.cjs +6 -0
  28. package/dist/cjs/collection/subscription.cjs.map +1 -1
  29. package/dist/cjs/collection/subscription.d.cts +4 -0
  30. package/dist/cjs/collection/sync.cjs +108 -1
  31. package/dist/cjs/collection/sync.cjs.map +1 -1
  32. package/dist/cjs/collection/sync.d.cts +2 -0
  33. package/dist/cjs/collection/transaction-metadata.cjs +5 -0
  34. package/dist/cjs/collection/transaction-metadata.cjs.map +1 -0
  35. package/dist/cjs/collection/transaction-metadata.d.cts +1 -0
  36. package/dist/cjs/errors.cjs +8 -0
  37. package/dist/cjs/errors.cjs.map +1 -1
  38. package/dist/cjs/errors.d.cts +3 -0
  39. package/dist/cjs/index.cjs +22 -4
  40. package/dist/cjs/index.cjs.map +1 -1
  41. package/dist/cjs/index.d.cts +11 -3
  42. package/dist/cjs/indexes/auto-index.cjs +13 -6
  43. package/dist/cjs/indexes/auto-index.cjs.map +1 -1
  44. package/dist/cjs/indexes/base-index.cjs +0 -3
  45. package/dist/cjs/indexes/base-index.cjs.map +1 -1
  46. package/dist/cjs/indexes/base-index.d.cts +2 -6
  47. package/dist/cjs/indexes/basic-index.cjs +361 -0
  48. package/dist/cjs/indexes/basic-index.cjs.map +1 -0
  49. package/dist/cjs/indexes/basic-index.d.cts +102 -0
  50. package/dist/cjs/indexes/btree-index.cjs.map +1 -1
  51. package/dist/cjs/indexes/btree-index.d.cts +1 -1
  52. package/dist/cjs/indexes/index-options.d.cts +8 -9
  53. package/dist/cjs/indexes/index-registry.cjs +89 -0
  54. package/dist/cjs/indexes/index-registry.cjs.map +1 -0
  55. package/dist/cjs/indexes/index-registry.d.cts +61 -0
  56. package/dist/cjs/local-only.cjs +5 -0
  57. package/dist/cjs/local-only.cjs.map +1 -1
  58. package/dist/cjs/query/builder/functions.cjs +27 -11
  59. package/dist/cjs/query/builder/functions.cjs.map +1 -1
  60. package/dist/cjs/query/builder/functions.d.cts +25 -3
  61. package/dist/cjs/query/builder/index.cjs +200 -39
  62. package/dist/cjs/query/builder/index.cjs.map +1 -1
  63. package/dist/cjs/query/builder/index.d.cts +4 -3
  64. package/dist/cjs/query/builder/ref-proxy.cjs.map +1 -1
  65. package/dist/cjs/query/builder/ref-proxy.d.cts +14 -3
  66. package/dist/cjs/query/builder/types.d.cts +84 -19
  67. package/dist/cjs/query/compiler/evaluators.cjs +51 -0
  68. package/dist/cjs/query/compiler/evaluators.cjs.map +1 -1
  69. package/dist/cjs/query/compiler/group-by.cjs +100 -28
  70. package/dist/cjs/query/compiler/group-by.cjs.map +1 -1
  71. package/dist/cjs/query/compiler/group-by.d.cts +4 -2
  72. package/dist/cjs/query/compiler/index.cjs +283 -11
  73. package/dist/cjs/query/compiler/index.cjs.map +1 -1
  74. package/dist/cjs/query/compiler/index.d.cts +30 -2
  75. package/dist/cjs/query/compiler/order-by.cjs +29 -10
  76. package/dist/cjs/query/compiler/order-by.cjs.map +1 -1
  77. package/dist/cjs/query/compiler/order-by.d.cts +1 -1
  78. package/dist/cjs/query/compiler/select.cjs +8 -0
  79. package/dist/cjs/query/compiler/select.cjs.map +1 -1
  80. package/dist/cjs/query/index.d.cts +2 -1
  81. package/dist/cjs/query/ir.cjs +18 -1
  82. package/dist/cjs/query/ir.cjs.map +1 -1
  83. package/dist/cjs/query/ir.d.cts +21 -1
  84. package/dist/cjs/query/live/collection-config-builder.cjs +501 -5
  85. package/dist/cjs/query/live/collection-config-builder.cjs.map +1 -1
  86. package/dist/cjs/query/live/collection-config-builder.d.cts +7 -0
  87. package/dist/cjs/query/live/types.d.cts +3 -3
  88. package/dist/cjs/query/live/utils.cjs +43 -3
  89. package/dist/cjs/query/live/utils.cjs.map +1 -1
  90. package/dist/cjs/query/live/utils.d.cts +1 -0
  91. package/dist/cjs/query/live-query-collection.cjs.map +1 -1
  92. package/dist/cjs/query/live-query-collection.d.cts +9 -6
  93. package/dist/cjs/query/query-once.cjs.map +1 -1
  94. package/dist/cjs/query/query-once.d.cts +7 -5
  95. package/dist/cjs/query/subset-dedupe.cjs +9 -3
  96. package/dist/cjs/query/subset-dedupe.cjs.map +1 -1
  97. package/dist/cjs/types.d.cts +42 -8
  98. package/dist/cjs/utils/array-utils.cjs +27 -0
  99. package/dist/cjs/utils/array-utils.cjs.map +1 -0
  100. package/dist/cjs/utils/array-utils.d.cts +16 -0
  101. package/dist/cjs/utils/comparison.cjs +11 -0
  102. package/dist/cjs/utils/comparison.cjs.map +1 -1
  103. package/dist/cjs/utils/index-optimization.cjs +4 -0
  104. package/dist/cjs/utils/index-optimization.cjs.map +1 -1
  105. package/dist/cjs/utils.cjs +7 -9
  106. package/dist/cjs/utils.cjs.map +1 -1
  107. package/dist/cjs/utils.d.cts +6 -1
  108. package/dist/cjs/virtual-props.cjs +33 -0
  109. package/dist/cjs/virtual-props.cjs.map +1 -0
  110. package/dist/cjs/virtual-props.d.cts +196 -0
  111. package/dist/esm/collection/change-events.d.ts +3 -2
  112. package/dist/esm/collection/change-events.js.map +1 -1
  113. package/dist/esm/collection/changes.d.ts +10 -1
  114. package/dist/esm/collection/changes.js +13 -4
  115. package/dist/esm/collection/changes.js.map +1 -1
  116. package/dist/esm/collection/cleanup-queue.d.ts +30 -0
  117. package/dist/esm/collection/cleanup-queue.js +89 -0
  118. package/dist/esm/collection/cleanup-queue.js.map +1 -0
  119. package/dist/esm/collection/events.d.ts +39 -1
  120. package/dist/esm/collection/events.js +14 -0
  121. package/dist/esm/collection/events.js.map +1 -1
  122. package/dist/esm/collection/index.d.ts +49 -36
  123. package/dist/esm/collection/index.js +67 -29
  124. package/dist/esm/collection/index.js.map +1 -1
  125. package/dist/esm/collection/indexes.d.ts +27 -17
  126. package/dist/esm/collection/indexes.js +211 -62
  127. package/dist/esm/collection/indexes.js.map +1 -1
  128. package/dist/esm/collection/lifecycle.d.ts +0 -1
  129. package/dist/esm/collection/lifecycle.js +5 -22
  130. package/dist/esm/collection/lifecycle.js.map +1 -1
  131. package/dist/esm/collection/mutations.d.ts +1 -0
  132. package/dist/esm/collection/mutations.js +18 -0
  133. package/dist/esm/collection/mutations.js.map +1 -1
  134. package/dist/esm/collection/state.d.ts +65 -1
  135. package/dist/esm/collection/state.js +381 -53
  136. package/dist/esm/collection/state.js.map +1 -1
  137. package/dist/esm/collection/subscription.d.ts +4 -0
  138. package/dist/esm/collection/subscription.js +6 -0
  139. package/dist/esm/collection/subscription.js.map +1 -1
  140. package/dist/esm/collection/sync.d.ts +2 -0
  141. package/dist/esm/collection/sync.js +108 -1
  142. package/dist/esm/collection/sync.js.map +1 -1
  143. package/dist/esm/collection/transaction-metadata.d.ts +1 -0
  144. package/dist/esm/collection/transaction-metadata.js +5 -0
  145. package/dist/esm/collection/transaction-metadata.js.map +1 -0
  146. package/dist/esm/errors.d.ts +3 -0
  147. package/dist/esm/errors.js +8 -0
  148. package/dist/esm/errors.js.map +1 -1
  149. package/dist/esm/index.d.ts +11 -3
  150. package/dist/esm/index.js +25 -7
  151. package/dist/esm/index.js.map +1 -1
  152. package/dist/esm/indexes/auto-index.js +13 -6
  153. package/dist/esm/indexes/auto-index.js.map +1 -1
  154. package/dist/esm/indexes/base-index.d.ts +2 -6
  155. package/dist/esm/indexes/base-index.js +1 -4
  156. package/dist/esm/indexes/base-index.js.map +1 -1
  157. package/dist/esm/indexes/basic-index.d.ts +102 -0
  158. package/dist/esm/indexes/basic-index.js +361 -0
  159. package/dist/esm/indexes/basic-index.js.map +1 -0
  160. package/dist/esm/indexes/btree-index.d.ts +1 -1
  161. package/dist/esm/indexes/btree-index.js.map +1 -1
  162. package/dist/esm/indexes/index-options.d.ts +8 -9
  163. package/dist/esm/indexes/index-registry.d.ts +61 -0
  164. package/dist/esm/indexes/index-registry.js +89 -0
  165. package/dist/esm/indexes/index-registry.js.map +1 -0
  166. package/dist/esm/local-only.js +5 -0
  167. package/dist/esm/local-only.js.map +1 -1
  168. package/dist/esm/query/builder/functions.d.ts +25 -3
  169. package/dist/esm/query/builder/functions.js +27 -11
  170. package/dist/esm/query/builder/functions.js.map +1 -1
  171. package/dist/esm/query/builder/index.d.ts +4 -3
  172. package/dist/esm/query/builder/index.js +201 -40
  173. package/dist/esm/query/builder/index.js.map +1 -1
  174. package/dist/esm/query/builder/ref-proxy.d.ts +14 -3
  175. package/dist/esm/query/builder/ref-proxy.js.map +1 -1
  176. package/dist/esm/query/builder/types.d.ts +84 -19
  177. package/dist/esm/query/compiler/evaluators.js +51 -0
  178. package/dist/esm/query/compiler/evaluators.js.map +1 -1
  179. package/dist/esm/query/compiler/group-by.d.ts +4 -2
  180. package/dist/esm/query/compiler/group-by.js +101 -29
  181. package/dist/esm/query/compiler/group-by.js.map +1 -1
  182. package/dist/esm/query/compiler/index.d.ts +30 -2
  183. package/dist/esm/query/compiler/index.js +285 -13
  184. package/dist/esm/query/compiler/index.js.map +1 -1
  185. package/dist/esm/query/compiler/order-by.d.ts +1 -1
  186. package/dist/esm/query/compiler/order-by.js +30 -11
  187. package/dist/esm/query/compiler/order-by.js.map +1 -1
  188. package/dist/esm/query/compiler/select.js +8 -0
  189. package/dist/esm/query/compiler/select.js.map +1 -1
  190. package/dist/esm/query/index.d.ts +2 -1
  191. package/dist/esm/query/ir.d.ts +21 -1
  192. package/dist/esm/query/ir.js +18 -1
  193. package/dist/esm/query/ir.js.map +1 -1
  194. package/dist/esm/query/live/collection-config-builder.d.ts +7 -0
  195. package/dist/esm/query/live/collection-config-builder.js +503 -7
  196. package/dist/esm/query/live/collection-config-builder.js.map +1 -1
  197. package/dist/esm/query/live/types.d.ts +3 -3
  198. package/dist/esm/query/live/utils.d.ts +1 -0
  199. package/dist/esm/query/live/utils.js +43 -3
  200. package/dist/esm/query/live/utils.js.map +1 -1
  201. package/dist/esm/query/live-query-collection.d.ts +9 -6
  202. package/dist/esm/query/live-query-collection.js.map +1 -1
  203. package/dist/esm/query/query-once.d.ts +7 -5
  204. package/dist/esm/query/query-once.js.map +1 -1
  205. package/dist/esm/query/subset-dedupe.js +9 -3
  206. package/dist/esm/query/subset-dedupe.js.map +1 -1
  207. package/dist/esm/types.d.ts +42 -8
  208. package/dist/esm/utils/array-utils.d.ts +16 -0
  209. package/dist/esm/utils/array-utils.js +27 -0
  210. package/dist/esm/utils/array-utils.js.map +1 -0
  211. package/dist/esm/utils/comparison.js +11 -0
  212. package/dist/esm/utils/comparison.js.map +1 -1
  213. package/dist/esm/utils/index-optimization.js +4 -0
  214. package/dist/esm/utils/index-optimization.js.map +1 -1
  215. package/dist/esm/utils.d.ts +6 -1
  216. package/dist/esm/utils.js +7 -9
  217. package/dist/esm/utils.js.map +1 -1
  218. package/dist/esm/virtual-props.d.ts +196 -0
  219. package/dist/esm/virtual-props.js +33 -0
  220. package/dist/esm/virtual-props.js.map +1 -0
  221. package/package.json +2 -2
  222. package/skills/db-core/collection-setup/references/electric-adapter.md +1 -1
  223. package/src/collection/change-events.ts +13 -9
  224. package/src/collection/changes.ts +30 -7
  225. package/src/collection/cleanup-queue.ts +105 -0
  226. package/src/collection/events.ts +65 -0
  227. package/src/collection/index.ts +110 -45
  228. package/src/collection/indexes.ts +283 -76
  229. package/src/collection/lifecycle.ts +5 -26
  230. package/src/collection/mutations.ts +21 -0
  231. package/src/collection/state.ts +545 -71
  232. package/src/collection/subscription.ts +7 -0
  233. package/src/collection/sync.ts +137 -0
  234. package/src/collection/transaction-metadata.ts +1 -0
  235. package/src/errors.ts +9 -0
  236. package/src/index.ts +46 -3
  237. package/src/indexes/auto-index.ts +18 -8
  238. package/src/indexes/base-index.ts +2 -10
  239. package/src/indexes/basic-index.ts +507 -0
  240. package/src/indexes/btree-index.ts +1 -1
  241. package/src/indexes/index-options.ts +17 -37
  242. package/src/indexes/index-registry.ts +174 -0
  243. package/src/local-only.ts +7 -0
  244. package/src/query/builder/functions.ts +84 -7
  245. package/src/query/builder/index.ts +329 -9
  246. package/src/query/builder/ref-proxy.ts +22 -4
  247. package/src/query/builder/types.ts +257 -62
  248. package/src/query/compiler/evaluators.ts +57 -0
  249. package/src/query/compiler/group-by.ts +156 -35
  250. package/src/query/compiler/index.ts +445 -15
  251. package/src/query/compiler/order-by.ts +51 -12
  252. package/src/query/compiler/select.ts +9 -0
  253. package/src/query/index.ts +7 -0
  254. package/src/query/ir.ts +23 -2
  255. package/src/query/live/collection-config-builder.ts +809 -9
  256. package/src/query/live/types.ts +10 -4
  257. package/src/query/live/utils.ts +64 -3
  258. package/src/query/live-query-collection.ts +43 -18
  259. package/src/query/query-once.ts +31 -12
  260. package/src/query/subset-dedupe.ts +11 -7
  261. package/src/types.ts +49 -9
  262. package/src/utils/array-utils.ts +49 -0
  263. package/src/utils/comparison.ts +14 -0
  264. package/src/utils/index-optimization.ts +4 -0
  265. package/src/utils.ts +12 -9
  266. package/src/virtual-props.ts +282 -0
  267. package/dist/cjs/indexes/lazy-index.cjs +0 -190
  268. package/dist/cjs/indexes/lazy-index.cjs.map +0 -1
  269. package/dist/cjs/indexes/lazy-index.d.cts +0 -96
  270. package/dist/esm/indexes/lazy-index.d.ts +0 -96
  271. package/dist/esm/indexes/lazy-index.js +0 -190
  272. package/dist/esm/indexes/lazy-index.js.map +0 -1
  273. package/src/indexes/lazy-index.ts +0 -251
@@ -1 +1 @@
1
- {"version":3,"file":"index.cjs","sources":["../../../../src/query/compiler/index.ts"],"sourcesContent":["import { distinct, filter, map } from '@tanstack/db-ivm'\nimport { optimizeQuery } from '../optimizer.js'\nimport {\n CollectionInputNotFoundError,\n DistinctRequiresSelectError,\n DuplicateAliasInSubqueryError,\n FnSelectWithGroupByError,\n HavingRequiresGroupByError,\n LimitOffsetRequireOrderByError,\n UnsupportedFromTypeError,\n} from '../../errors.js'\nimport { PropRef, Value as ValClass, getWhereExpression } from '../ir.js'\nimport { compileExpression, toBooleanPredicate } from './evaluators.js'\nimport { processJoins } from './joins.js'\nimport { containsAggregate, processGroupBy } from './group-by.js'\nimport { processOrderBy } from './order-by.js'\nimport { processSelect } from './select.js'\nimport type { CollectionSubscription } from '../../collection/subscription.js'\nimport type { OrderByOptimizationInfo } from './order-by.js'\nimport type {\n BasicExpression,\n CollectionRef,\n QueryIR,\n QueryRef,\n} from '../ir.js'\nimport type { LazyCollectionCallbacks } from './joins.js'\nimport type { Collection } from '../../collection/index.js'\nimport type {\n KeyedStream,\n NamespacedAndKeyedStream,\n ResultStream,\n} from '../../types.js'\nimport type { QueryCache, QueryMapping, WindowOptions } from './types.js'\n\nexport type { WindowOptions } from './types.js'\n\n/**\n * Result of query compilation including both the pipeline and source-specific WHERE clauses\n */\nexport interface CompilationResult {\n /** The ID of the main collection */\n collectionId: string\n\n /** The compiled query pipeline (D2 stream) */\n pipeline: ResultStream\n\n /** Map of source aliases to their WHERE clauses for index optimization */\n sourceWhereClauses: Map<string, BasicExpression<boolean>>\n\n /**\n * Maps each source alias to its collection ID. Enables per-alias subscriptions for self-joins.\n * Example: `{ employee: 'employees-col-id', manager: 'employees-col-id' }`\n */\n aliasToCollectionId: Record<string, string>\n\n /**\n * Flattened mapping from outer alias to innermost alias for subqueries.\n * Always provides one-hop lookups, never recursive chains.\n *\n * Example: `{ activeUser: 'user' }` when `.from({ activeUser: subquery })`\n * where the subquery uses `.from({ user: collection })`.\n *\n * For deeply nested subqueries, the mapping goes directly to the innermost alias:\n * `{ author: 'user' }` (not `{ author: 'activeUser' }`), so `aliasRemapping[alias]`\n * always resolves in a single lookup.\n *\n * Used to resolve subscriptions during lazy loading when join aliases differ from\n * the inner aliases where collection subscriptions were created.\n */\n aliasRemapping: Record<string, string>\n}\n\n/**\n * Compiles a query IR into a D2 pipeline\n * @param rawQuery The query IR to compile\n * @param inputs Mapping of source aliases to input streams (e.g., `{ employee: input1, manager: input2 }`)\n * @param collections Mapping of collection IDs to Collection instances\n * @param subscriptions Mapping of source aliases to CollectionSubscription instances\n * @param callbacks Mapping of source aliases to lazy loading callbacks\n * @param lazySources Set of source aliases that should load data lazily\n * @param optimizableOrderByCollections Map of collection IDs to order-by optimization info\n * @param cache Optional cache for compiled subqueries (used internally for recursion)\n * @param queryMapping Optional mapping from optimized queries to original queries\n * @returns A CompilationResult with the pipeline, source WHERE clauses, and alias metadata\n */\nexport function compileQuery(\n rawQuery: QueryIR,\n inputs: Record<string, KeyedStream>,\n collections: Record<string, Collection<any, any, any, any, any>>,\n subscriptions: Record<string, CollectionSubscription>,\n callbacks: Record<string, LazyCollectionCallbacks>,\n lazySources: Set<string>,\n optimizableOrderByCollections: Record<string, OrderByOptimizationInfo>,\n setWindowFn: (windowFn: (options: WindowOptions) => void) => void,\n cache: QueryCache = new WeakMap(),\n queryMapping: QueryMapping = new WeakMap(),\n): CompilationResult {\n // Check if the original raw query has already been compiled\n const cachedResult = cache.get(rawQuery)\n if (cachedResult) {\n return cachedResult\n }\n\n // Validate the raw query BEFORE optimization to check user's original structure.\n // This must happen before optimization because the optimizer may create internal\n // subqueries (e.g., for predicate pushdown) that reuse aliases, which is fine.\n validateQueryStructure(rawQuery)\n\n // Optimize the query before compilation\n const { optimizedQuery: query, sourceWhereClauses } = optimizeQuery(rawQuery)\n\n // Create mapping from optimized query to original for caching\n queryMapping.set(query, rawQuery)\n mapNestedQueries(query, rawQuery, queryMapping)\n\n // Create a copy of the inputs map to avoid modifying the original\n const allInputs = { ...inputs }\n\n // Track alias to collection id relationships discovered during compilation.\n // This includes all user-declared aliases plus inner aliases from subqueries.\n const aliasToCollectionId: Record<string, string> = {}\n\n // Track alias remapping for subqueries (outer alias → inner alias)\n // e.g., when .join({ activeUser: subquery }) where subquery uses .from({ user: collection })\n // we store: aliasRemapping['activeUser'] = 'user'\n const aliasRemapping: Record<string, string> = {}\n\n // Create a map of source aliases to input streams.\n // Inputs MUST be keyed by alias (e.g., `{ employee: input1, manager: input2 }`),\n // not by collection ID. This enables per-alias subscriptions where different aliases\n // of the same collection (e.g., self-joins) maintain independent filtered streams.\n const sources: Record<string, KeyedStream> = {}\n\n // Process the FROM clause to get the main source\n const {\n alias: mainSource,\n input: mainInput,\n collectionId: mainCollectionId,\n } = processFrom(\n query.from,\n allInputs,\n collections,\n subscriptions,\n callbacks,\n lazySources,\n optimizableOrderByCollections,\n setWindowFn,\n cache,\n queryMapping,\n aliasToCollectionId,\n aliasRemapping,\n sourceWhereClauses,\n )\n sources[mainSource] = mainInput\n\n // Prepare the initial pipeline with the main source wrapped in its alias\n let pipeline: NamespacedAndKeyedStream = mainInput.pipe(\n map(([key, row]) => {\n // Initialize the record with a nested structure\n const ret = [key, { [mainSource]: 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 && query.join.length > 0) {\n pipeline = processJoins(\n pipeline,\n query.join,\n sources,\n mainCollectionId,\n mainSource,\n allInputs,\n cache,\n queryMapping,\n collections,\n subscriptions,\n callbacks,\n lazySources,\n optimizableOrderByCollections,\n setWindowFn,\n rawQuery,\n compileQuery,\n aliasToCollectionId,\n aliasRemapping,\n sourceWhereClauses,\n )\n }\n\n // Process the WHERE clause if it exists\n if (query.where && query.where.length > 0) {\n // Apply each WHERE condition as a filter (they are ANDed together)\n for (const where of query.where) {\n const whereExpression = getWhereExpression(where)\n const compiledWhere = compileExpression(whereExpression)\n pipeline = pipeline.pipe(\n filter(([_key, namespacedRow]) => {\n return toBooleanPredicate(compiledWhere(namespacedRow))\n }),\n )\n }\n }\n\n // Process functional WHERE clauses if they exist\n if (query.fnWhere && query.fnWhere.length > 0) {\n for (const fnWhere of query.fnWhere) {\n pipeline = pipeline.pipe(\n filter(([_key, namespacedRow]) => {\n return toBooleanPredicate(fnWhere(namespacedRow))\n }),\n )\n }\n }\n\n if (query.distinct && !query.fnSelect && !query.select) {\n throw new DistinctRequiresSelectError()\n }\n\n if (query.fnSelect && query.groupBy && query.groupBy.length > 0) {\n throw new FnSelectWithGroupByError()\n }\n\n // Process the SELECT clause early - always create $selected\n // This eliminates duplication and allows for DISTINCT implementation\n if (query.fnSelect) {\n // Handle functional select - apply the function to transform the row\n pipeline = pipeline.pipe(\n map(([key, namespacedRow]) => {\n const selectResults = query.fnSelect!(namespacedRow)\n return [\n key,\n {\n ...namespacedRow,\n $selected: selectResults,\n },\n ] as [string, typeof namespacedRow & { $selected: any }]\n }),\n )\n } else if (query.select) {\n pipeline = processSelect(pipeline, query.select, allInputs)\n } else {\n // If no SELECT clause, create $selected with the main table data\n pipeline = pipeline.pipe(\n map(([key, namespacedRow]) => {\n const selectResults =\n !query.join && !query.groupBy\n ? namespacedRow[mainSource]\n : namespacedRow\n\n return [\n key,\n {\n ...namespacedRow,\n $selected: selectResults,\n },\n ] as [string, typeof namespacedRow & { $selected: any }]\n }),\n )\n }\n\n // Process the GROUP BY clause if it exists\n if (query.groupBy && query.groupBy.length > 0) {\n pipeline = processGroupBy(\n pipeline,\n query.groupBy,\n query.having,\n query.select,\n query.fnHaving,\n )\n } else if (query.select) {\n // Check if SELECT contains aggregates but no GROUP BY (implicit single-group aggregation)\n const hasAggregates = Object.values(query.select).some(\n (expr) => expr.type === `agg` || containsAggregate(expr),\n )\n if (hasAggregates) {\n // Handle implicit single-group aggregation\n pipeline = processGroupBy(\n pipeline,\n [], // Empty group by means single group\n query.having,\n query.select,\n query.fnHaving,\n )\n }\n }\n\n // Process the HAVING clause if it exists (only applies after GROUP BY)\n if (query.having && (!query.groupBy || query.groupBy.length === 0)) {\n // Check if we have aggregates in SELECT that would trigger implicit grouping\n const hasAggregates = query.select\n ? Object.values(query.select).some((expr) => expr.type === `agg`)\n : false\n\n if (!hasAggregates) {\n throw new HavingRequiresGroupByError()\n }\n }\n\n // Process functional HAVING clauses outside of GROUP BY (treat as additional WHERE filters)\n if (\n query.fnHaving &&\n query.fnHaving.length > 0 &&\n (!query.groupBy || query.groupBy.length === 0)\n ) {\n // If there's no GROUP BY but there are fnHaving clauses, apply them as filters\n for (const fnHaving of query.fnHaving) {\n pipeline = pipeline.pipe(\n filter(([_key, namespacedRow]) => {\n return fnHaving(namespacedRow)\n }),\n )\n }\n }\n\n // Process the DISTINCT clause if it exists\n if (query.distinct) {\n pipeline = pipeline.pipe(distinct(([_key, row]) => row.$selected))\n }\n\n // Process orderBy parameter if it exists\n if (query.orderBy && query.orderBy.length > 0) {\n const orderedPipeline = processOrderBy(\n rawQuery,\n pipeline,\n query.orderBy,\n query.select || {},\n collections[mainCollectionId]!,\n optimizableOrderByCollections,\n setWindowFn,\n query.limit,\n query.offset,\n )\n\n // Final step: extract the $selected and include orderBy index\n const resultPipeline = orderedPipeline.pipe(\n map(([key, [row, orderByIndex]]) => {\n // Extract the final results from $selected and include orderBy index\n const raw = (row as any).$selected\n const finalResults = unwrapValue(raw)\n return [key, [finalResults, orderByIndex]] as [unknown, [any, string]]\n }),\n )\n\n const result = resultPipeline\n // Cache the result before returning (use original query as key)\n const compilationResult = {\n collectionId: mainCollectionId,\n pipeline: result,\n sourceWhereClauses,\n aliasToCollectionId,\n aliasRemapping,\n }\n cache.set(rawQuery, compilationResult)\n\n return compilationResult\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 LimitOffsetRequireOrderByError()\n }\n\n // Final step: extract the $selected and return tuple format (no orderBy)\n const resultPipeline: ResultStream = pipeline.pipe(\n map(([key, row]) => {\n // Extract the final results from $selected and return [key, [results, undefined]]\n const raw = (row as any).$selected\n const finalResults = unwrapValue(raw)\n return [key, [finalResults, undefined]] as [\n unknown,\n [any, string | undefined],\n ]\n }),\n )\n\n const result = resultPipeline\n // Cache the result before returning (use original query as key)\n const compilationResult = {\n collectionId: mainCollectionId,\n pipeline: result,\n sourceWhereClauses,\n aliasToCollectionId,\n aliasRemapping,\n }\n cache.set(rawQuery, compilationResult)\n\n return compilationResult\n}\n\n/**\n * Collects aliases used for DIRECT collection references (not subqueries).\n * Used to validate that subqueries don't reuse parent query collection aliases.\n * Only direct CollectionRef aliases matter - QueryRef aliases don't cause conflicts.\n */\nfunction collectDirectCollectionAliases(query: QueryIR): Set<string> {\n const aliases = new Set<string>()\n\n // Collect FROM alias only if it's a direct collection reference\n if (query.from.type === `collectionRef`) {\n aliases.add(query.from.alias)\n }\n\n // Collect JOIN aliases only for direct collection references\n if (query.join) {\n for (const joinClause of query.join) {\n if (joinClause.from.type === `collectionRef`) {\n aliases.add(joinClause.from.alias)\n }\n }\n }\n\n return aliases\n}\n\n/**\n * Validates the structure of a query and its subqueries.\n * Checks that subqueries don't reuse collection aliases from parent queries.\n * This must be called on the RAW query before optimization.\n */\nfunction validateQueryStructure(\n query: QueryIR,\n parentCollectionAliases: Set<string> = new Set(),\n): void {\n // Collect direct collection aliases from this query level\n const currentLevelAliases = collectDirectCollectionAliases(query)\n\n // Check if any current alias conflicts with parent aliases\n for (const alias of currentLevelAliases) {\n if (parentCollectionAliases.has(alias)) {\n throw new DuplicateAliasInSubqueryError(\n alias,\n Array.from(parentCollectionAliases),\n )\n }\n }\n\n // Combine parent and current aliases for checking nested subqueries\n const combinedAliases = new Set([\n ...parentCollectionAliases,\n ...currentLevelAliases,\n ])\n\n // Recursively validate FROM subquery\n if (query.from.type === `queryRef`) {\n validateQueryStructure(query.from.query, combinedAliases)\n }\n\n // Recursively validate JOIN subqueries\n if (query.join) {\n for (const joinClause of query.join) {\n if (joinClause.from.type === `queryRef`) {\n validateQueryStructure(joinClause.from.query, combinedAliases)\n }\n }\n }\n}\n\n/**\n * Processes the FROM clause, handling direct collection references and subqueries.\n * Populates `aliasToCollectionId` and `aliasRemapping` for per-alias subscription tracking.\n */\nfunction processFrom(\n from: CollectionRef | QueryRef,\n allInputs: Record<string, KeyedStream>,\n collections: Record<string, Collection>,\n subscriptions: Record<string, CollectionSubscription>,\n callbacks: Record<string, LazyCollectionCallbacks>,\n lazySources: Set<string>,\n optimizableOrderByCollections: Record<string, OrderByOptimizationInfo>,\n setWindowFn: (windowFn: (options: WindowOptions) => void) => void,\n cache: QueryCache,\n queryMapping: QueryMapping,\n aliasToCollectionId: Record<string, string>,\n aliasRemapping: Record<string, string>,\n sourceWhereClauses: Map<string, BasicExpression<boolean>>,\n): { alias: string; input: KeyedStream; collectionId: string } {\n switch (from.type) {\n case `collectionRef`: {\n const input = allInputs[from.alias]\n if (!input) {\n throw new CollectionInputNotFoundError(\n from.alias,\n from.collection.id,\n Object.keys(allInputs),\n )\n }\n aliasToCollectionId[from.alias] = from.collection.id\n return { alias: from.alias, input, collectionId: from.collection.id }\n }\n case `queryRef`: {\n // Find the original query for caching purposes\n const originalQuery = queryMapping.get(from.query) || from.query\n\n // Recursively compile the sub-query with cache\n const subQueryResult = compileQuery(\n originalQuery,\n allInputs,\n collections,\n subscriptions,\n callbacks,\n lazySources,\n optimizableOrderByCollections,\n setWindowFn,\n cache,\n queryMapping,\n )\n\n // Pull up alias mappings from subquery to parent scope.\n // This includes both the innermost alias-to-collection mappings AND\n // any existing remappings from nested subquery levels.\n Object.assign(aliasToCollectionId, subQueryResult.aliasToCollectionId)\n Object.assign(aliasRemapping, subQueryResult.aliasRemapping)\n\n // Pull up source WHERE clauses from subquery to parent scope.\n // This enables loadSubset to receive the correct where clauses for subquery collections.\n //\n // IMPORTANT: Skip pull-up for optimizer-created subqueries. These are detected when:\n // 1. The outer alias (from.alias) matches the inner alias (from.query.from.alias)\n // 2. The subquery was found in queryMapping (it's a user-defined subquery, not optimizer-created)\n //\n // For optimizer-created subqueries, the parent already has the sourceWhereClauses\n // extracted from the original raw query, so pulling up would be redundant.\n // More importantly, pulling up for optimizer-created subqueries can cause issues\n // when the optimizer has restructured the query.\n const isUserDefinedSubquery = queryMapping.has(from.query)\n const subqueryFromAlias = from.query.from.alias\n const isOptimizerCreated =\n !isUserDefinedSubquery && from.alias === subqueryFromAlias\n\n if (!isOptimizerCreated) {\n for (const [alias, whereClause] of subQueryResult.sourceWhereClauses) {\n sourceWhereClauses.set(alias, whereClause)\n }\n }\n\n // Create a FLATTENED remapping from outer alias to innermost alias.\n // For nested subqueries, this ensures one-hop lookups (not recursive chains).\n //\n // Example with 3-level nesting:\n // Inner: .from({ user: usersCollection })\n // Middle: .from({ activeUser: innerSubquery }) → creates: activeUser → user\n // Outer: .from({ author: middleSubquery }) → creates: author → user (not author → activeUser)\n //\n // The key insight: We search through the PULLED-UP aliasToCollectionId (which contains\n // the innermost 'user' alias), so we always map directly to the deepest level.\n // This means aliasRemapping[alias] is always a single lookup, never recursive.\n // Needed for subscription resolution during lazy loading.\n const innerAlias = Object.keys(subQueryResult.aliasToCollectionId).find(\n (alias) =>\n subQueryResult.aliasToCollectionId[alias] ===\n subQueryResult.collectionId,\n )\n if (innerAlias && innerAlias !== from.alias) {\n aliasRemapping[from.alias] = innerAlias\n }\n\n // Extract the pipeline from the compilation result\n const subQueryInput = subQueryResult.pipeline\n\n // Subqueries may return [key, [value, orderByIndex]] (with ORDER BY) or [key, value] (without ORDER BY)\n // We need to extract just the value for use in parent queries\n const extractedInput = subQueryInput.pipe(\n map((data: any) => {\n const [key, [value, _orderByIndex]] = data\n // Unwrap Value expressions that might have leaked through as the entire row\n const unwrapped = unwrapValue(value)\n return [key, unwrapped] as [unknown, any]\n }),\n )\n\n return {\n alias: from.alias,\n input: extractedInput,\n collectionId: subQueryResult.collectionId,\n }\n }\n default:\n throw new UnsupportedFromTypeError((from as any).type)\n }\n}\n\n// Helper to check if a value is a Value expression\nfunction isValue(raw: any): boolean {\n return (\n raw instanceof ValClass ||\n (raw && typeof raw === `object` && `type` in raw && raw.type === `val`)\n )\n}\n\n// Helper to unwrap a Value expression or return the value itself\nfunction unwrapValue(value: any): any {\n return isValue(value) ? value.value : value\n}\n\n/**\n * Recursively maps optimized subqueries to their original queries for proper caching.\n * This ensures that when we encounter the same QueryRef object in different contexts,\n * we can find the original query to check the cache.\n */\nfunction mapNestedQueries(\n optimizedQuery: QueryIR,\n originalQuery: QueryIR,\n queryMapping: QueryMapping,\n): void {\n // Map the FROM clause if it's a QueryRef\n if (\n optimizedQuery.from.type === `queryRef` &&\n originalQuery.from.type === `queryRef`\n ) {\n queryMapping.set(optimizedQuery.from.query, originalQuery.from.query)\n // Recursively map nested queries\n mapNestedQueries(\n optimizedQuery.from.query,\n originalQuery.from.query,\n queryMapping,\n )\n }\n\n // Map JOIN clauses if they exist\n if (optimizedQuery.join && originalQuery.join) {\n for (\n let i = 0;\n i < optimizedQuery.join.length && i < originalQuery.join.length;\n i++\n ) {\n const optimizedJoin = optimizedQuery.join[i]!\n const originalJoin = originalQuery.join[i]!\n\n if (\n optimizedJoin.from.type === `queryRef` &&\n originalJoin.from.type === `queryRef`\n ) {\n queryMapping.set(optimizedJoin.from.query, originalJoin.from.query)\n // Recursively map nested queries in joins\n mapNestedQueries(\n optimizedJoin.from.query,\n originalJoin.from.query,\n queryMapping,\n )\n }\n }\n }\n}\n\nfunction getRefFromAlias(\n query: QueryIR,\n alias: string,\n): CollectionRef | QueryRef | void {\n if (query.from.alias === alias) {\n return query.from\n }\n\n for (const join of query.join || []) {\n if (join.from.alias === alias) {\n return join.from\n }\n }\n}\n\n/**\n * Follows the given reference in a query\n * until its finds the root field the reference points to.\n * @returns The collection, its alias, and the path to the root field in this collection\n */\nexport function followRef(\n query: QueryIR,\n ref: PropRef<any>,\n collection: Collection,\n): { collection: Collection; path: Array<string> } | void {\n if (ref.path.length === 0) {\n return\n }\n\n if (ref.path.length === 1) {\n // This field should be part of this collection\n const field = ref.path[0]!\n // is it part of the select clause?\n if (query.select) {\n const selectedField = query.select[field]\n if (selectedField && selectedField.type === `ref`) {\n return followRef(query, selectedField, collection)\n }\n }\n\n // Either this field is not part of the select clause\n // and thus it must be part of the collection itself\n // or it is part of the select but is not a reference\n // so we can stop here and don't have to follow it\n return { collection, path: [field] }\n }\n\n if (ref.path.length > 1) {\n // This is a nested field\n const [alias, ...rest] = ref.path\n const aliasRef = getRefFromAlias(query, alias!)\n if (!aliasRef) {\n return\n }\n\n if (aliasRef.type === `queryRef`) {\n return followRef(aliasRef.query, new PropRef(rest), collection)\n } else {\n // This is a reference to a collection\n // we can't follow it further\n // so the field must be on the collection itself\n return { collection: aliasRef.collection, path: rest }\n }\n }\n}\n\nexport type CompileQueryFn = typeof compileQuery\n"],"names":["optimizeQuery","map","processJoins","getWhereExpression","compileExpression","filter","toBooleanPredicate","DistinctRequiresSelectError","FnSelectWithGroupByError","processSelect","processGroupBy","containsAggregate","HavingRequiresGroupByError","distinct","processOrderBy","resultPipeline","result","compilationResult","LimitOffsetRequireOrderByError","DuplicateAliasInSubqueryError","CollectionInputNotFoundError","UnsupportedFromTypeError","ValClass"],"mappings":"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"}
1
+ {"version":3,"file":"index.cjs","sources":["../../../../src/query/compiler/index.ts"],"sourcesContent":["import {\n distinct,\n filter,\n join as joinOperator,\n map,\n reduce,\n} from '@tanstack/db-ivm'\nimport { optimizeQuery } from '../optimizer.js'\nimport {\n CollectionInputNotFoundError,\n DistinctRequiresSelectError,\n DuplicateAliasInSubqueryError,\n FnSelectWithGroupByError,\n HavingRequiresGroupByError,\n LimitOffsetRequireOrderByError,\n UnsupportedFromTypeError,\n} from '../../errors.js'\nimport { VIRTUAL_PROP_NAMES } from '../../virtual-props.js'\nimport {\n IncludesSubquery,\n PropRef,\n Value as ValClass,\n getWhereExpression,\n} from '../ir.js'\nimport { compileExpression, toBooleanPredicate } from './evaluators.js'\nimport { processJoins } from './joins.js'\nimport { containsAggregate, processGroupBy } from './group-by.js'\nimport { processOrderBy } from './order-by.js'\nimport { processSelect } from './select.js'\nimport type { CollectionSubscription } from '../../collection/subscription.js'\nimport type { OrderByOptimizationInfo } from './order-by.js'\nimport type {\n BasicExpression,\n CollectionRef,\n IncludesMaterialization,\n QueryIR,\n QueryRef,\n} from '../ir.js'\nimport type { LazyCollectionCallbacks } from './joins.js'\nimport type { Collection } from '../../collection/index.js'\nimport type {\n KeyedStream,\n NamespacedAndKeyedStream,\n ResultStream,\n} from '../../types.js'\nimport type { QueryCache, QueryMapping, WindowOptions } from './types.js'\n\nexport type { WindowOptions } from './types.js'\n\n/** Symbol used to tag parent $selected with routing metadata for includes */\nexport const INCLUDES_ROUTING = Symbol(`includesRouting`)\n\n/**\n * Result of compiling an includes subquery, including the child pipeline\n * and metadata needed to route child results to parent-scoped Collections.\n */\nexport interface IncludesCompilationResult {\n /** Filtered child pipeline (post inner-join with parent keys) */\n pipeline: ResultStream\n /** Result field name on parent (e.g., \"issues\") */\n fieldName: string\n /** Parent-side correlation ref (e.g., project.id) */\n correlationField: PropRef\n /** Child-side correlation ref (e.g., issue.projectId) */\n childCorrelationField: PropRef\n /** Whether the child query has an ORDER BY clause */\n hasOrderBy: boolean\n /** Full compilation result for the child query (for nested includes + alias tracking) */\n childCompilationResult: CompilationResult\n /** Parent-side projection refs for parent-referencing filters */\n parentProjection?: Array<PropRef>\n /** How the output layer materializes the child result on the parent row */\n materialization: IncludesMaterialization\n /** Internal field used to unwrap scalar child selects */\n scalarField?: string\n}\n\n/**\n * Result of query compilation including both the pipeline and source-specific WHERE clauses\n */\nexport interface CompilationResult {\n /** The ID of the main collection */\n collectionId: string\n\n /** The compiled query pipeline (D2 stream) */\n pipeline: ResultStream\n\n /** Map of source aliases to their WHERE clauses for index optimization */\n sourceWhereClauses: Map<string, BasicExpression<boolean>>\n\n /**\n * Maps each source alias to its collection ID. Enables per-alias subscriptions for self-joins.\n * Example: `{ employee: 'employees-col-id', manager: 'employees-col-id' }`\n */\n aliasToCollectionId: Record<string, string>\n\n /**\n * Flattened mapping from outer alias to innermost alias for subqueries.\n * Always provides one-hop lookups, never recursive chains.\n *\n * Example: `{ activeUser: 'user' }` when `.from({ activeUser: subquery })`\n * where the subquery uses `.from({ user: collection })`.\n *\n * For deeply nested subqueries, the mapping goes directly to the innermost alias:\n * `{ author: 'user' }` (not `{ author: 'activeUser' }`), so `aliasRemapping[alias]`\n * always resolves in a single lookup.\n *\n * Used to resolve subscriptions during lazy loading when join aliases differ from\n * the inner aliases where collection subscriptions were created.\n */\n aliasRemapping: Record<string, string>\n\n /** Child pipelines for includes subqueries */\n includes?: Array<IncludesCompilationResult>\n}\n\n/**\n * Compiles a query IR into a D2 pipeline\n * @param rawQuery The query IR to compile\n * @param inputs Mapping of source aliases to input streams (e.g., `{ employee: input1, manager: input2 }`)\n * @param collections Mapping of collection IDs to Collection instances\n * @param subscriptions Mapping of source aliases to CollectionSubscription instances\n * @param callbacks Mapping of source aliases to lazy loading callbacks\n * @param lazySources Set of source aliases that should load data lazily\n * @param optimizableOrderByCollections Map of collection IDs to order-by optimization info\n * @param cache Optional cache for compiled subqueries (used internally for recursion)\n * @param queryMapping Optional mapping from optimized queries to original queries\n * @returns A CompilationResult with the pipeline, source WHERE clauses, and alias metadata\n */\nexport function compileQuery(\n rawQuery: QueryIR,\n inputs: Record<string, KeyedStream>,\n collections: Record<string, Collection<any, any, any, any, any>>,\n subscriptions: Record<string, CollectionSubscription>,\n callbacks: Record<string, LazyCollectionCallbacks>,\n lazySources: Set<string>,\n optimizableOrderByCollections: Record<string, OrderByOptimizationInfo>,\n setWindowFn: (windowFn: (options: WindowOptions) => void) => void,\n cache: QueryCache = new WeakMap(),\n queryMapping: QueryMapping = new WeakMap(),\n // For includes: parent key stream to inner-join with this query's FROM\n parentKeyStream?: KeyedStream,\n childCorrelationField?: PropRef,\n): CompilationResult {\n // Check if the original raw query has already been compiled\n const cachedResult = cache.get(rawQuery)\n if (cachedResult) {\n return cachedResult\n }\n\n // Validate the raw query BEFORE optimization to check user's original structure.\n // This must happen before optimization because the optimizer may create internal\n // subqueries (e.g., for predicate pushdown) that reuse aliases, which is fine.\n validateQueryStructure(rawQuery)\n\n // Optimize the query before compilation\n const { optimizedQuery, sourceWhereClauses } = optimizeQuery(rawQuery)\n // Use a mutable binding so we can shallow-clone select before includes mutation\n let query = optimizedQuery\n\n // Create mapping from optimized query to original for caching\n queryMapping.set(query, rawQuery)\n mapNestedQueries(query, rawQuery, queryMapping)\n\n // Create a copy of the inputs map to avoid modifying the original\n const allInputs = { ...inputs }\n\n // Track alias to collection id relationships discovered during compilation.\n // This includes all user-declared aliases plus inner aliases from subqueries.\n const aliasToCollectionId: Record<string, string> = {}\n\n // Track alias remapping for subqueries (outer alias → inner alias)\n // e.g., when .join({ activeUser: subquery }) where subquery uses .from({ user: collection })\n // we store: aliasRemapping['activeUser'] = 'user'\n const aliasRemapping: Record<string, string> = {}\n\n // Create a map of source aliases to input streams.\n // Inputs MUST be keyed by alias (e.g., `{ employee: input1, manager: input2 }`),\n // not by collection ID. This enables per-alias subscriptions where different aliases\n // of the same collection (e.g., self-joins) maintain independent filtered streams.\n const sources: Record<string, KeyedStream> = {}\n\n // Process the FROM clause to get the main source\n const {\n alias: mainSource,\n input: mainInput,\n collectionId: mainCollectionId,\n } = processFrom(\n query.from,\n allInputs,\n collections,\n subscriptions,\n callbacks,\n lazySources,\n optimizableOrderByCollections,\n setWindowFn,\n cache,\n queryMapping,\n aliasToCollectionId,\n aliasRemapping,\n sourceWhereClauses,\n )\n sources[mainSource] = mainInput\n\n // If this is an includes child query, inner-join the raw input with parent keys.\n // This filters the child collection to only rows matching parents in the result set.\n // The inner join happens BEFORE namespace wrapping / WHERE / SELECT / ORDER BY,\n // so the child pipeline only processes rows that match parents.\n let filteredMainInput = mainInput\n if (parentKeyStream && childCorrelationField) {\n // Re-key child input by correlation field: [correlationValue, [childKey, childRow]]\n const childFieldPath = childCorrelationField.path.slice(1) // remove alias prefix\n const childRekeyed = mainInput.pipe(\n map(([key, row]: [unknown, any]) => {\n const correlationValue = getNestedValue(row, childFieldPath)\n return [correlationValue, [key, row]] as [unknown, [unknown, any]]\n }),\n )\n\n // Inner join: only children whose correlation key exists in parent keys pass through\n const joined = childRekeyed.pipe(joinOperator(parentKeyStream, `inner`))\n\n // Extract: [correlationValue, [[childKey, childRow], parentContext]] → [childKey, childRow]\n // Tag the row with __correlationKey for output routing\n // If parentSide is non-null (parent context projected), attach as __parentContext\n filteredMainInput = joined.pipe(\n filter(([_correlationValue, [childSide]]: any) => {\n return childSide != null\n }),\n map(([correlationValue, [childSide, parentSide]]: any) => {\n const [childKey, childRow] = childSide\n const tagged: any = { ...childRow, __correlationKey: correlationValue }\n if (parentSide != null) {\n tagged.__parentContext = parentSide\n }\n const effectiveKey =\n parentSide != null\n ? `${String(childKey)}::${JSON.stringify(parentSide)}`\n : childKey\n return [effectiveKey, tagged]\n }),\n )\n\n // Update sources so the rest of the pipeline uses the filtered input\n sources[mainSource] = filteredMainInput\n }\n\n // Prepare the initial pipeline with the main source wrapped in its alias\n let pipeline: NamespacedAndKeyedStream = filteredMainInput.pipe(\n map(([key, row]) => {\n // Initialize the record with a nested structure\n // If __parentContext exists (from parent-referencing includes), merge parent\n // aliases into the namespaced row so WHERE can resolve parent refs\n const { __parentContext, ...cleanRow } = row as any\n const nsRow: Record<string, any> = { [mainSource]: cleanRow }\n if (__parentContext) {\n Object.assign(nsRow, __parentContext)\n ;(nsRow as any).__parentContext = __parentContext\n }\n const ret = [key, nsRow] as [string, Record<string, typeof row>]\n return ret\n }),\n )\n\n // Process JOIN clauses if they exist\n if (query.join && query.join.length > 0) {\n pipeline = processJoins(\n pipeline,\n query.join,\n sources,\n mainCollectionId,\n mainSource,\n allInputs,\n cache,\n queryMapping,\n collections,\n subscriptions,\n callbacks,\n lazySources,\n optimizableOrderByCollections,\n setWindowFn,\n rawQuery,\n compileQuery,\n aliasToCollectionId,\n aliasRemapping,\n sourceWhereClauses,\n )\n }\n\n // Process the WHERE clause if it exists\n if (query.where && query.where.length > 0) {\n // Apply each WHERE condition as a filter (they are ANDed together)\n for (const where of query.where) {\n const whereExpression = getWhereExpression(where)\n const compiledWhere = compileExpression(whereExpression)\n pipeline = pipeline.pipe(\n filter(([_key, namespacedRow]) => {\n return toBooleanPredicate(compiledWhere(namespacedRow))\n }),\n )\n }\n }\n\n // Process functional WHERE clauses if they exist\n if (query.fnWhere && query.fnWhere.length > 0) {\n for (const fnWhere of query.fnWhere) {\n pipeline = pipeline.pipe(\n filter(([_key, namespacedRow]) => {\n return toBooleanPredicate(fnWhere(namespacedRow))\n }),\n )\n }\n }\n\n // Extract includes from SELECT, compile child pipelines, and replace with placeholders.\n // This must happen AFTER WHERE (so parent pipeline is filtered) but BEFORE processSelect\n // (so IncludesSubquery nodes are stripped before select compilation).\n const includesResults: Array<IncludesCompilationResult> = []\n const includesRoutingFns: Array<{\n fieldName: string\n getRouting: (nsRow: any) => {\n correlationKey: unknown\n parentContext: Record<string, any> | null\n }\n }> = []\n if (query.select) {\n const includesEntries = extractIncludesFromSelect(query.select)\n // Shallow-clone select before mutating so we don't modify the shared IR\n // (the optimizer copies select by reference, so rawQuery.select === query.select)\n if (includesEntries.length > 0) {\n query = { ...query, select: { ...query.select } }\n }\n for (const { key, subquery } of includesEntries) {\n // Branch parent pipeline: map to [correlationValue, parentContext]\n // When parentProjection exists, project referenced parent fields; otherwise null (zero overhead)\n const compiledCorrelation = compileExpression(subquery.correlationField)\n let parentKeys: any\n if (subquery.parentProjection && subquery.parentProjection.length > 0) {\n const compiledProjections = subquery.parentProjection.map((ref) => ({\n alias: ref.path[0]!,\n field: ref.path.slice(1),\n compiled: compileExpression(ref),\n }))\n parentKeys = pipeline.pipe(\n map(([_key, nsRow]: any) => {\n const parentContext: Record<string, Record<string, any>> = {}\n for (const proj of compiledProjections) {\n if (!parentContext[proj.alias]) {\n parentContext[proj.alias] = {}\n }\n const value = proj.compiled(nsRow)\n // Set nested field in the alias namespace\n let target = parentContext[proj.alias]!\n for (let i = 0; i < proj.field.length - 1; i++) {\n if (!target[proj.field[i]!]) {\n target[proj.field[i]!] = {}\n }\n target = target[proj.field[i]!]\n }\n target[proj.field[proj.field.length - 1]!] = value\n }\n return [compiledCorrelation(nsRow), parentContext] as any\n }),\n )\n } else {\n parentKeys = pipeline.pipe(\n map(\n ([_key, nsRow]: any) => [compiledCorrelation(nsRow), null] as any,\n ),\n )\n }\n\n // Deduplicate: when multiple parents share the same correlation key (and\n // parentContext), clamp multiplicity to 1 so the inner join doesn't\n // produce duplicate child entries that cause incorrect deletions.\n parentKeys = parentKeys.pipe(\n reduce((values: Array<[any, number]>) =>\n values.map(([v, mult]) => [v, mult > 0 ? 1 : 0] as [any, number]),\n ),\n )\n\n // If parent filters exist, append them to the child query's WHERE\n const childQuery =\n subquery.parentFilters && subquery.parentFilters.length > 0\n ? {\n ...subquery.query,\n where: [\n ...(subquery.query.where || []),\n ...subquery.parentFilters,\n ],\n }\n : subquery.query\n\n // Recursively compile child query WITH the parent key stream\n const childResult = compileQuery(\n childQuery,\n allInputs,\n collections,\n subscriptions,\n callbacks,\n lazySources,\n optimizableOrderByCollections,\n setWindowFn,\n cache,\n queryMapping,\n parentKeys,\n subquery.childCorrelationField,\n )\n\n // Merge child's alias metadata into parent's\n Object.assign(aliasToCollectionId, childResult.aliasToCollectionId)\n Object.assign(aliasRemapping, childResult.aliasRemapping)\n\n includesResults.push({\n pipeline: childResult.pipeline,\n fieldName: subquery.fieldName,\n correlationField: subquery.correlationField,\n childCorrelationField: subquery.childCorrelationField,\n hasOrderBy: !!(\n subquery.query.orderBy && subquery.query.orderBy.length > 0\n ),\n childCompilationResult: childResult,\n parentProjection: subquery.parentProjection,\n materialization: subquery.materialization,\n scalarField: subquery.scalarField,\n })\n\n // Capture routing function for INCLUDES_ROUTING tagging\n if (subquery.parentProjection && subquery.parentProjection.length > 0) {\n const compiledProjs = subquery.parentProjection.map((ref) => ({\n alias: ref.path[0]!,\n field: ref.path.slice(1),\n compiled: compileExpression(ref),\n }))\n const compiledCorr = compiledCorrelation\n includesRoutingFns.push({\n fieldName: subquery.fieldName,\n getRouting: (nsRow: any) => {\n const parentContext: Record<string, Record<string, any>> = {}\n for (const proj of compiledProjs) {\n if (!parentContext[proj.alias]) {\n parentContext[proj.alias] = {}\n }\n const value = proj.compiled(nsRow)\n let target = parentContext[proj.alias]!\n for (let i = 0; i < proj.field.length - 1; i++) {\n if (!target[proj.field[i]!]) {\n target[proj.field[i]!] = {}\n }\n target = target[proj.field[i]!]\n }\n target[proj.field[proj.field.length - 1]!] = value\n }\n return { correlationKey: compiledCorr(nsRow), parentContext }\n },\n })\n } else {\n includesRoutingFns.push({\n fieldName: subquery.fieldName,\n getRouting: (nsRow: any) => ({\n correlationKey: compiledCorrelation(nsRow),\n parentContext: null,\n }),\n })\n }\n\n // Replace includes entry in select with a null placeholder\n replaceIncludesInSelect(query.select!, key)\n }\n }\n\n if (query.distinct && !query.fnSelect && !query.select) {\n throw new DistinctRequiresSelectError()\n }\n\n if (query.fnSelect && query.groupBy && query.groupBy.length > 0) {\n throw new FnSelectWithGroupByError()\n }\n\n // Process the SELECT clause early - always create $selected\n // This eliminates duplication and allows for DISTINCT implementation\n if (query.fnSelect) {\n // Handle functional select - apply the function to transform the row\n pipeline = pipeline.pipe(\n map(([key, namespacedRow]) => {\n const selectResults = query.fnSelect!(namespacedRow)\n return [\n key,\n {\n ...namespacedRow,\n $selected: selectResults,\n },\n ] as [string, typeof namespacedRow & { $selected: any }]\n }),\n )\n } else if (query.select) {\n pipeline = processSelect(pipeline, query.select, allInputs)\n } else {\n // If no SELECT clause, create $selected with the main table data\n pipeline = pipeline.pipe(\n map(([key, namespacedRow]) => {\n const selectResults =\n !query.join && !query.groupBy\n ? namespacedRow[mainSource]\n : namespacedRow\n\n return [\n key,\n {\n ...namespacedRow,\n $selected: selectResults,\n },\n ] as [string, typeof namespacedRow & { $selected: any }]\n }),\n )\n }\n\n // Tag $selected with routing metadata for includes.\n // This lets collection-config-builder extract routing info (correlationKey + parentContext)\n // from parent results without depending on the user's select.\n if (includesRoutingFns.length > 0) {\n pipeline = pipeline.pipe(\n map(([key, namespacedRow]: any) => {\n const routing: Record<\n string,\n { correlationKey: unknown; parentContext: Record<string, any> | null }\n > = {}\n for (const { fieldName, getRouting } of includesRoutingFns) {\n routing[fieldName] = getRouting(namespacedRow)\n }\n namespacedRow.$selected[INCLUDES_ROUTING] = routing\n return [key, namespacedRow]\n }),\n )\n }\n\n // Process the GROUP BY clause if it exists.\n // When in includes mode (parentKeyStream), pass mainSource so that groupBy\n // preserves __correlationKey for per-parent aggregation.\n const groupByMainSource = parentKeyStream ? mainSource : undefined\n if (query.groupBy && query.groupBy.length > 0) {\n pipeline = processGroupBy(\n pipeline,\n query.groupBy,\n query.having,\n query.select,\n query.fnHaving,\n mainCollectionId,\n groupByMainSource,\n )\n } else if (query.select) {\n // Check if SELECT contains aggregates but no GROUP BY (implicit single-group aggregation)\n const hasAggregates = Object.values(query.select).some(\n (expr) => expr.type === `agg` || containsAggregate(expr),\n )\n if (hasAggregates) {\n // Handle implicit single-group aggregation\n pipeline = processGroupBy(\n pipeline,\n [], // Empty group by means single group\n query.having,\n query.select,\n query.fnHaving,\n mainCollectionId,\n groupByMainSource,\n )\n }\n }\n\n // Process the HAVING clause if it exists (only applies after GROUP BY)\n if (query.having && (!query.groupBy || query.groupBy.length === 0)) {\n // Check if we have aggregates in SELECT that would trigger implicit grouping\n const hasAggregates = query.select\n ? Object.values(query.select).some((expr) => expr.type === `agg`)\n : false\n\n if (!hasAggregates) {\n throw new HavingRequiresGroupByError()\n }\n }\n\n // Process functional HAVING clauses outside of GROUP BY (treat as additional WHERE filters)\n if (\n query.fnHaving &&\n query.fnHaving.length > 0 &&\n (!query.groupBy || query.groupBy.length === 0)\n ) {\n // If there's no GROUP BY but there are fnHaving clauses, apply them as filters\n for (const fnHaving of query.fnHaving) {\n pipeline = pipeline.pipe(\n filter(([_key, namespacedRow]) => {\n return fnHaving(namespacedRow)\n }),\n )\n }\n }\n\n // Process the DISTINCT clause if it exists\n if (query.distinct) {\n pipeline = pipeline.pipe(distinct(([_key, row]) => row.$selected))\n }\n\n // Process orderBy parameter if it exists\n if (query.orderBy && query.orderBy.length > 0) {\n // When in includes mode with limit/offset, use grouped ordering so that\n // the limit is applied per parent (per correlation key), not globally.\n const includesGroupKeyFn =\n parentKeyStream &&\n (query.limit !== undefined || query.offset !== undefined)\n ? (_key: unknown, row: unknown) => {\n const correlationKey = (row as any)?.[mainSource]?.__correlationKey\n const parentContext = (row as any)?.__parentContext\n if (parentContext != null) {\n return JSON.stringify([correlationKey, parentContext])\n }\n return correlationKey\n }\n : undefined\n\n const orderedPipeline = processOrderBy(\n rawQuery,\n pipeline,\n query.orderBy,\n query.select || {},\n collections[mainCollectionId]!,\n optimizableOrderByCollections,\n setWindowFn,\n query.limit,\n query.offset,\n includesGroupKeyFn,\n )\n\n // Final step: extract the $selected and include orderBy index\n const resultPipeline: ResultStream = orderedPipeline.pipe(\n map(([key, [row, orderByIndex]]) => {\n // Extract the final results from $selected and include orderBy index\n const raw = (row as any).$selected\n const finalResults = attachVirtualPropsToSelected(\n unwrapValue(raw),\n row as Record<string, any>,\n )\n // When in includes mode, embed the correlation key and parentContext\n if (parentKeyStream) {\n const correlationKey = (row as any)[mainSource]?.__correlationKey\n const parentContext = (row as any).__parentContext ?? null\n // Strip internal routing properties that may leak via spread selects\n delete finalResults.__correlationKey\n delete finalResults.__parentContext\n return [\n key,\n [finalResults, orderByIndex, correlationKey, parentContext],\n ] as any\n }\n return [key, [finalResults, orderByIndex]] as [unknown, [any, string]]\n }),\n ) as ResultStream\n\n const result = resultPipeline\n // Cache the result before returning (use original query as key)\n const compilationResult: CompilationResult = {\n collectionId: mainCollectionId,\n pipeline: result,\n sourceWhereClauses,\n aliasToCollectionId,\n aliasRemapping,\n includes: includesResults.length > 0 ? includesResults : undefined,\n }\n cache.set(rawQuery, compilationResult)\n\n return compilationResult\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 LimitOffsetRequireOrderByError()\n }\n\n // Final step: extract the $selected and return tuple format (no orderBy)\n const resultPipeline: ResultStream = pipeline.pipe(\n map(([key, row]) => {\n // Extract the final results from $selected and return [key, [results, undefined]]\n const raw = (row as any).$selected\n const finalResults = attachVirtualPropsToSelected(\n unwrapValue(raw),\n row as Record<string, any>,\n )\n // When in includes mode, embed the correlation key and parentContext\n if (parentKeyStream) {\n const correlationKey = (row as any)[mainSource]?.__correlationKey\n const parentContext = (row as any).__parentContext ?? null\n // Strip internal routing properties that may leak via spread selects\n delete finalResults.__correlationKey\n delete finalResults.__parentContext\n return [\n key,\n [finalResults, undefined, correlationKey, parentContext],\n ] as any\n }\n return [key, [finalResults, undefined]] as [\n unknown,\n [any, string | undefined],\n ]\n }),\n )\n\n const result = resultPipeline\n // Cache the result before returning (use original query as key)\n const compilationResult: CompilationResult = {\n collectionId: mainCollectionId,\n pipeline: result,\n sourceWhereClauses,\n aliasToCollectionId,\n aliasRemapping,\n includes: includesResults.length > 0 ? includesResults : undefined,\n }\n cache.set(rawQuery, compilationResult)\n\n return compilationResult\n}\n\n/**\n * Collects aliases used for DIRECT collection references (not subqueries).\n * Used to validate that subqueries don't reuse parent query collection aliases.\n * Only direct CollectionRef aliases matter - QueryRef aliases don't cause conflicts.\n */\nfunction collectDirectCollectionAliases(query: QueryIR): Set<string> {\n const aliases = new Set<string>()\n\n // Collect FROM alias only if it's a direct collection reference\n if (query.from.type === `collectionRef`) {\n aliases.add(query.from.alias)\n }\n\n // Collect JOIN aliases only for direct collection references\n if (query.join) {\n for (const joinClause of query.join) {\n if (joinClause.from.type === `collectionRef`) {\n aliases.add(joinClause.from.alias)\n }\n }\n }\n\n return aliases\n}\n\n/**\n * Validates the structure of a query and its subqueries.\n * Checks that subqueries don't reuse collection aliases from parent queries.\n * This must be called on the RAW query before optimization.\n */\nfunction validateQueryStructure(\n query: QueryIR,\n parentCollectionAliases: Set<string> = new Set(),\n): void {\n // Collect direct collection aliases from this query level\n const currentLevelAliases = collectDirectCollectionAliases(query)\n\n // Check if any current alias conflicts with parent aliases\n for (const alias of currentLevelAliases) {\n if (parentCollectionAliases.has(alias)) {\n throw new DuplicateAliasInSubqueryError(\n alias,\n Array.from(parentCollectionAliases),\n )\n }\n }\n\n // Combine parent and current aliases for checking nested subqueries\n const combinedAliases = new Set([\n ...parentCollectionAliases,\n ...currentLevelAliases,\n ])\n\n // Recursively validate FROM subquery\n if (query.from.type === `queryRef`) {\n validateQueryStructure(query.from.query, combinedAliases)\n }\n\n // Recursively validate JOIN subqueries\n if (query.join) {\n for (const joinClause of query.join) {\n if (joinClause.from.type === `queryRef`) {\n validateQueryStructure(joinClause.from.query, combinedAliases)\n }\n }\n }\n}\n\n/**\n * Processes the FROM clause, handling direct collection references and subqueries.\n * Populates `aliasToCollectionId` and `aliasRemapping` for per-alias subscription tracking.\n */\nfunction processFrom(\n from: CollectionRef | QueryRef,\n allInputs: Record<string, KeyedStream>,\n collections: Record<string, Collection>,\n subscriptions: Record<string, CollectionSubscription>,\n callbacks: Record<string, LazyCollectionCallbacks>,\n lazySources: Set<string>,\n optimizableOrderByCollections: Record<string, OrderByOptimizationInfo>,\n setWindowFn: (windowFn: (options: WindowOptions) => void) => void,\n cache: QueryCache,\n queryMapping: QueryMapping,\n aliasToCollectionId: Record<string, string>,\n aliasRemapping: Record<string, string>,\n sourceWhereClauses: Map<string, BasicExpression<boolean>>,\n): { alias: string; input: KeyedStream; collectionId: string } {\n switch (from.type) {\n case `collectionRef`: {\n const input = allInputs[from.alias]\n if (!input) {\n throw new CollectionInputNotFoundError(\n from.alias,\n from.collection.id,\n Object.keys(allInputs),\n )\n }\n aliasToCollectionId[from.alias] = from.collection.id\n return { alias: from.alias, input, collectionId: from.collection.id }\n }\n case `queryRef`: {\n // Find the original query for caching purposes\n const originalQuery = queryMapping.get(from.query) || from.query\n\n // Recursively compile the sub-query with cache\n const subQueryResult = compileQuery(\n originalQuery,\n allInputs,\n collections,\n subscriptions,\n callbacks,\n lazySources,\n optimizableOrderByCollections,\n setWindowFn,\n cache,\n queryMapping,\n )\n\n // Pull up alias mappings from subquery to parent scope.\n // This includes both the innermost alias-to-collection mappings AND\n // any existing remappings from nested subquery levels.\n Object.assign(aliasToCollectionId, subQueryResult.aliasToCollectionId)\n Object.assign(aliasRemapping, subQueryResult.aliasRemapping)\n\n // Pull up source WHERE clauses from subquery to parent scope.\n // This enables loadSubset to receive the correct where clauses for subquery collections.\n //\n // IMPORTANT: Skip pull-up for optimizer-created subqueries. These are detected when:\n // 1. The outer alias (from.alias) matches the inner alias (from.query.from.alias)\n // 2. The subquery was found in queryMapping (it's a user-defined subquery, not optimizer-created)\n //\n // For optimizer-created subqueries, the parent already has the sourceWhereClauses\n // extracted from the original raw query, so pulling up would be redundant.\n // More importantly, pulling up for optimizer-created subqueries can cause issues\n // when the optimizer has restructured the query.\n const isUserDefinedSubquery = queryMapping.has(from.query)\n const subqueryFromAlias = from.query.from.alias\n const isOptimizerCreated =\n !isUserDefinedSubquery && from.alias === subqueryFromAlias\n\n if (!isOptimizerCreated) {\n for (const [alias, whereClause] of subQueryResult.sourceWhereClauses) {\n sourceWhereClauses.set(alias, whereClause)\n }\n }\n\n // Create a FLATTENED remapping from outer alias to innermost alias.\n // For nested subqueries, this ensures one-hop lookups (not recursive chains).\n //\n // Example with 3-level nesting:\n // Inner: .from({ user: usersCollection })\n // Middle: .from({ activeUser: innerSubquery }) → creates: activeUser → user\n // Outer: .from({ author: middleSubquery }) → creates: author → user (not author → activeUser)\n //\n // The key insight: We search through the PULLED-UP aliasToCollectionId (which contains\n // the innermost 'user' alias), so we always map directly to the deepest level.\n // This means aliasRemapping[alias] is always a single lookup, never recursive.\n // Needed for subscription resolution during lazy loading.\n const innerAlias = Object.keys(subQueryResult.aliasToCollectionId).find(\n (alias) =>\n subQueryResult.aliasToCollectionId[alias] ===\n subQueryResult.collectionId,\n )\n if (innerAlias && innerAlias !== from.alias) {\n aliasRemapping[from.alias] = innerAlias\n }\n\n // Extract the pipeline from the compilation result\n const subQueryInput = subQueryResult.pipeline\n\n // Subqueries may return [key, [value, orderByIndex]] (with ORDER BY) or [key, value] (without ORDER BY)\n // We need to extract just the value for use in parent queries\n const extractedInput = subQueryInput.pipe(\n map((data: any) => {\n const [key, [value, _orderByIndex]] = data\n // Unwrap Value expressions that might have leaked through as the entire row\n const unwrapped = unwrapValue(value)\n return [key, unwrapped] as [unknown, any]\n }),\n )\n\n return {\n alias: from.alias,\n input: extractedInput,\n collectionId: subQueryResult.collectionId,\n }\n }\n default:\n throw new UnsupportedFromTypeError((from as any).type)\n }\n}\n\n// Helper to check if a value is a Value expression\nfunction isValue(raw: any): boolean {\n return (\n raw instanceof ValClass ||\n (raw && typeof raw === `object` && `type` in raw && raw.type === `val`)\n )\n}\n\n// Helper to unwrap a Value expression or return the value itself\nfunction unwrapValue(value: any): any {\n return isValue(value) ? value.value : value\n}\n\nfunction attachVirtualPropsToSelected(\n selected: any,\n row: Record<string, any>,\n): any {\n if (!selected || typeof selected !== `object`) {\n return selected\n }\n\n let needsMerge = false\n for (const prop of VIRTUAL_PROP_NAMES) {\n if (selected[prop] == null && prop in row) {\n needsMerge = true\n break\n }\n }\n\n if (!needsMerge) {\n return selected\n }\n\n for (const prop of VIRTUAL_PROP_NAMES) {\n if (selected[prop] == null && prop in row) {\n selected[prop] = row[prop]\n }\n }\n\n return selected\n}\n\n/**\n * Recursively maps optimized subqueries to their original queries for proper caching.\n * This ensures that when we encounter the same QueryRef object in different contexts,\n * we can find the original query to check the cache.\n */\nfunction mapNestedQueries(\n optimizedQuery: QueryIR,\n originalQuery: QueryIR,\n queryMapping: QueryMapping,\n): void {\n // Map the FROM clause if it's a QueryRef\n if (\n optimizedQuery.from.type === `queryRef` &&\n originalQuery.from.type === `queryRef`\n ) {\n queryMapping.set(optimizedQuery.from.query, originalQuery.from.query)\n // Recursively map nested queries\n mapNestedQueries(\n optimizedQuery.from.query,\n originalQuery.from.query,\n queryMapping,\n )\n }\n\n // Map JOIN clauses if they exist\n if (optimizedQuery.join && originalQuery.join) {\n for (\n let i = 0;\n i < optimizedQuery.join.length && i < originalQuery.join.length;\n i++\n ) {\n const optimizedJoin = optimizedQuery.join[i]!\n const originalJoin = originalQuery.join[i]!\n\n if (\n optimizedJoin.from.type === `queryRef` &&\n originalJoin.from.type === `queryRef`\n ) {\n queryMapping.set(optimizedJoin.from.query, originalJoin.from.query)\n // Recursively map nested queries in joins\n mapNestedQueries(\n optimizedJoin.from.query,\n originalJoin.from.query,\n queryMapping,\n )\n }\n }\n }\n}\n\nfunction getRefFromAlias(\n query: QueryIR,\n alias: string,\n): CollectionRef | QueryRef | void {\n if (query.from.alias === alias) {\n return query.from\n }\n\n for (const join of query.join || []) {\n if (join.from.alias === alias) {\n return join.from\n }\n }\n}\n\n/**\n * Follows the given reference in a query\n * until its finds the root field the reference points to.\n * @returns The collection, its alias, and the path to the root field in this collection\n */\nexport function followRef(\n query: QueryIR,\n ref: PropRef<any>,\n collection: Collection,\n): { collection: Collection; path: Array<string> } | void {\n if (ref.path.length === 0) {\n return\n }\n\n if (ref.path.length === 1) {\n // This field should be part of this collection\n const field = ref.path[0]!\n // is it part of the select clause?\n if (query.select) {\n const selectedField = query.select[field]\n if (selectedField && selectedField.type === `ref`) {\n return followRef(query, selectedField, collection)\n }\n }\n\n // Either this field is not part of the select clause\n // and thus it must be part of the collection itself\n // or it is part of the select but is not a reference\n // so we can stop here and don't have to follow it\n return { collection, path: [field] }\n }\n\n if (ref.path.length > 1) {\n // This is a nested field\n const [alias, ...rest] = ref.path\n const aliasRef = getRefFromAlias(query, alias!)\n if (!aliasRef) {\n return\n }\n\n if (aliasRef.type === `queryRef`) {\n return followRef(aliasRef.query, new PropRef(rest), collection)\n } else {\n // This is a reference to a collection\n // we can't follow it further\n // so the field must be on the collection itself\n return { collection: aliasRef.collection, path: rest }\n }\n }\n}\n\n/**\n * Walks a Select object to find IncludesSubquery entries at the top level.\n * Throws if an IncludesSubquery is found nested inside a sub-object, since\n * the compiler only supports includes at the top level of a select.\n */\nfunction extractIncludesFromSelect(\n select: Record<string, any>,\n): Array<{ key: string; subquery: IncludesSubquery }> {\n const results: Array<{ key: string; subquery: IncludesSubquery }> = []\n for (const [key, value] of Object.entries(select)) {\n if (key.startsWith(`__SPREAD_SENTINEL__`)) continue\n if (value instanceof IncludesSubquery) {\n results.push({ key, subquery: value })\n } else if (isNestedSelectObject(value)) {\n // Check nested objects for IncludesSubquery — not supported yet\n assertNoNestedIncludes(value, key)\n }\n }\n return results\n}\n\n/** Check if a value is a nested plain object in a select (not an IR expression node) */\nfunction isNestedSelectObject(value: any): value is Record<string, any> {\n return (\n value != null &&\n typeof value === `object` &&\n !Array.isArray(value) &&\n typeof value.type !== `string`\n )\n}\n\nfunction assertNoNestedIncludes(\n obj: Record<string, any>,\n parentPath: string,\n): void {\n for (const [key, value] of Object.entries(obj)) {\n if (key.startsWith(`__SPREAD_SENTINEL__`)) continue\n if (value instanceof IncludesSubquery) {\n throw new Error(\n `Includes subqueries must be at the top level of select(). ` +\n `Found nested includes at \"${parentPath}.${key}\".`,\n )\n }\n if (isNestedSelectObject(value)) {\n assertNoNestedIncludes(value, `${parentPath}.${key}`)\n }\n }\n}\n\n/**\n * Replaces an IncludesSubquery entry in the select object with a null Value placeholder.\n * This ensures processSelect() doesn't encounter it.\n */\nfunction replaceIncludesInSelect(\n select: Record<string, any>,\n key: string,\n): void {\n select[key] = new ValClass(null)\n}\n\n/**\n * Gets a nested value from an object by path segments.\n * For v1 with single-level correlation fields (e.g., `projectId`), it's just `obj[path[0]]`.\n */\nfunction getNestedValue(obj: any, path: Array<string>): any {\n let value = obj\n for (const segment of path) {\n if (value == null) return value\n value = value[segment]\n }\n return value\n}\n\nexport type CompileQueryFn = typeof compileQuery\n"],"names":["optimizeQuery","map","joinOperator","filter","processJoins","getWhereExpression","compileExpression","toBooleanPredicate","reduce","DistinctRequiresSelectError","FnSelectWithGroupByError","processSelect","processGroupBy","containsAggregate","HavingRequiresGroupByError","distinct","processOrderBy","resultPipeline","result","compilationResult","LimitOffsetRequireOrderByError","DuplicateAliasInSubqueryError","CollectionInputNotFoundError","UnsupportedFromTypeError","ValClass","VIRTUAL_PROP_NAMES","select","IncludesSubquery"],"mappings":"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"}
@@ -1,4 +1,4 @@
1
- import { PropRef, BasicExpression, QueryIR } from '../ir.js';
1
+ import { PropRef, BasicExpression, IncludesMaterialization, QueryIR } from '../ir.js';
2
2
  import { CollectionSubscription } from '../../collection/subscription.js';
3
3
  import { OrderByOptimizationInfo } from './order-by.js';
4
4
  import { LazyCollectionCallbacks } from './joins.js';
@@ -6,6 +6,32 @@ import { Collection } from '../../collection/index.js';
6
6
  import { KeyedStream, ResultStream } from '../../types.js';
7
7
  import { QueryCache, QueryMapping, WindowOptions } from './types.js';
8
8
  export type { WindowOptions } from './types.js';
9
+ /** Symbol used to tag parent $selected with routing metadata for includes */
10
+ export declare const INCLUDES_ROUTING: unique symbol;
11
+ /**
12
+ * Result of compiling an includes subquery, including the child pipeline
13
+ * and metadata needed to route child results to parent-scoped Collections.
14
+ */
15
+ export interface IncludesCompilationResult {
16
+ /** Filtered child pipeline (post inner-join with parent keys) */
17
+ pipeline: ResultStream;
18
+ /** Result field name on parent (e.g., "issues") */
19
+ fieldName: string;
20
+ /** Parent-side correlation ref (e.g., project.id) */
21
+ correlationField: PropRef;
22
+ /** Child-side correlation ref (e.g., issue.projectId) */
23
+ childCorrelationField: PropRef;
24
+ /** Whether the child query has an ORDER BY clause */
25
+ hasOrderBy: boolean;
26
+ /** Full compilation result for the child query (for nested includes + alias tracking) */
27
+ childCompilationResult: CompilationResult;
28
+ /** Parent-side projection refs for parent-referencing filters */
29
+ parentProjection?: Array<PropRef>;
30
+ /** How the output layer materializes the child result on the parent row */
31
+ materialization: IncludesMaterialization;
32
+ /** Internal field used to unwrap scalar child selects */
33
+ scalarField?: string;
34
+ }
9
35
  /**
10
36
  * Result of query compilation including both the pipeline and source-specific WHERE clauses
11
37
  */
@@ -36,6 +62,8 @@ export interface CompilationResult {
36
62
  * the inner aliases where collection subscriptions were created.
37
63
  */
38
64
  aliasRemapping: Record<string, string>;
65
+ /** Child pipelines for includes subqueries */
66
+ includes?: Array<IncludesCompilationResult>;
39
67
  }
40
68
  /**
41
69
  * Compiles a query IR into a D2 pipeline
@@ -50,7 +78,7 @@ export interface CompilationResult {
50
78
  * @param queryMapping Optional mapping from optimized queries to original queries
51
79
  * @returns A CompilationResult with the pipeline, source WHERE clauses, and alias metadata
52
80
  */
53
- export declare function compileQuery(rawQuery: QueryIR, inputs: Record<string, KeyedStream>, collections: Record<string, Collection<any, any, any, any, any>>, subscriptions: Record<string, CollectionSubscription>, callbacks: Record<string, LazyCollectionCallbacks>, lazySources: Set<string>, optimizableOrderByCollections: Record<string, OrderByOptimizationInfo>, setWindowFn: (windowFn: (options: WindowOptions) => void) => void, cache?: QueryCache, queryMapping?: QueryMapping): CompilationResult;
81
+ export declare function compileQuery(rawQuery: QueryIR, inputs: Record<string, KeyedStream>, collections: Record<string, Collection<any, any, any, any, any>>, subscriptions: Record<string, CollectionSubscription>, callbacks: Record<string, LazyCollectionCallbacks>, lazySources: Set<string>, optimizableOrderByCollections: Record<string, OrderByOptimizationInfo>, setWindowFn: (windowFn: (options: WindowOptions) => void) => void, cache?: QueryCache, queryMapping?: QueryMapping, parentKeyStream?: KeyedStream, childCorrelationField?: PropRef): CompilationResult;
54
82
  /**
55
83
  * Follows the given reference in a query
56
84
  * until its finds the root field the reference points to.
@@ -7,7 +7,7 @@ const autoIndex = require("../../indexes/auto-index.cjs");
7
7
  const indexOptimization = require("../../utils/index-optimization.cjs");
8
8
  const evaluators = require("./evaluators.cjs");
9
9
  const groupBy = require("./group-by.cjs");
10
- function processOrderBy(rawQuery, pipeline, orderByClause, selectClause, collection, optimizableOrderByCollections, setWindowFn, limit, offset) {
10
+ function processOrderBy(rawQuery, pipeline, orderByClause, selectClause, collection, optimizableOrderByCollections, setWindowFn, limit, offset, groupKeyFn) {
11
11
  const compiledOrderBy = orderByClause.map((clause) => {
12
12
  const clauseWithoutAggregates = groupBy.replaceAggregatesByRefs(
13
13
  clause.expression,
@@ -54,7 +54,7 @@ function processOrderBy(rawQuery, pipeline, orderByClause, selectClause, collect
54
54
  };
55
55
  let setSizeCallback;
56
56
  let orderByOptimizationInfo;
57
- if (limit) {
57
+ if (limit && !groupKeyFn) {
58
58
  let index;
59
59
  let followRefCollection;
60
60
  let firstColumnValueExtractor;
@@ -75,12 +75,13 @@ function processOrderBy(rawQuery, pipeline, orderByClause, selectClause, collect
75
75
  followRefCollection
76
76
  );
77
77
  if (fieldName) {
78
+ const firstColumnCompareFn = comparison.makeComparator(compareOpts);
78
79
  autoIndex.ensureIndexForField(
79
80
  fieldName,
80
81
  followRefResult.path,
81
82
  followRefCollection,
82
83
  compareOpts,
83
- compare
84
+ firstColumnCompareFn
84
85
  );
85
86
  }
86
87
  firstColumnValueExtractor = evaluators.compileExpression(
@@ -153,16 +154,34 @@ function processOrderBy(rawQuery, pipeline, orderByClause, selectClause, collect
153
154
  };
154
155
  const targetCollectionId = followRefCollection?.id ?? collection.id;
155
156
  optimizableOrderByCollections[targetCollectionId] = orderByOptimizationInfo;
156
- if (index) {
157
- setSizeCallback = (getSize) => {
158
- optimizableOrderByCollections[targetCollectionId][`dataNeeded`] = () => {
159
- const size = getSize();
160
- return Math.max(0, orderByOptimizationInfo.limit - size);
161
- };
157
+ setSizeCallback = (getSize) => {
158
+ optimizableOrderByCollections[targetCollectionId][`dataNeeded`] = () => {
159
+ const size = getSize();
160
+ return Math.max(0, orderByOptimizationInfo.limit - size);
162
161
  };
163
- }
162
+ };
164
163
  }
165
164
  }
165
+ if (groupKeyFn) {
166
+ return pipeline.pipe(
167
+ dbIvm.groupedOrderByWithFractionalIndex(valueExtractor, {
168
+ limit,
169
+ offset,
170
+ comparator: compare,
171
+ setSizeCallback,
172
+ groupKeyFn,
173
+ setWindowFn: (windowFn) => {
174
+ setWindowFn((options) => {
175
+ windowFn(options);
176
+ if (orderByOptimizationInfo) {
177
+ orderByOptimizationInfo.offset = options.offset ?? orderByOptimizationInfo.offset;
178
+ orderByOptimizationInfo.limit = options.limit ?? orderByOptimizationInfo.limit;
179
+ }
180
+ });
181
+ }
182
+ })
183
+ );
184
+ }
166
185
  return pipeline.pipe(
167
186
  dbIvm.orderByWithFractionalIndex(valueExtractor, {
168
187
  limit,
@@ -1 +1 @@
1
- {"version":3,"file":"order-by.cjs","sources":["../../../../src/query/compiler/order-by.ts"],"sourcesContent":["import { orderByWithFractionalIndex } from '@tanstack/db-ivm'\nimport { defaultComparator, makeComparator } from '../../utils/comparison.js'\nimport { PropRef, followRef } from '../ir.js'\nimport { ensureIndexForField } from '../../indexes/auto-index.js'\nimport { findIndexForField } from '../../utils/index-optimization.js'\nimport { compileExpression } from './evaluators.js'\nimport { replaceAggregatesByRefs } from './group-by.js'\nimport type { CompareOptions } from '../builder/types.js'\nimport type { WindowOptions } from './types.js'\nimport type { CompiledSingleRowExpression } from './evaluators.js'\nimport type { OrderBy, OrderByClause, QueryIR, Select } from '../ir.js'\nimport type {\n CollectionLike,\n NamespacedAndKeyedStream,\n NamespacedRow,\n} from '../../types.js'\nimport type { IStreamBuilder, KeyValue } from '@tanstack/db-ivm'\nimport type { IndexInterface } from '../../indexes/base-index.js'\nimport type { Collection } from '../../collection/index.js'\n\nexport type OrderByOptimizationInfo = {\n alias: string\n orderBy: OrderBy\n offset: number\n limit: number\n comparator: (\n a: Record<string, unknown> | null | undefined,\n b: Record<string, unknown> | null | undefined,\n ) => number\n /** Extracts all orderBy column values from a raw row (array for multi-column) */\n valueExtractorForRawRow: (row: Record<string, unknown>) => unknown\n /** Extracts only the first column value - used for index-based cursor */\n firstColumnValueExtractor: (row: Record<string, unknown>) => unknown\n /** Index on the first orderBy column - used for lazy loading */\n index?: IndexInterface<string | number>\n dataNeeded?: () => number\n}\n\n/**\n * Processes the ORDER BY clause\n * Works with the new structure that has both namespaced row data and $selected\n * Always uses fractional indexing and adds the index as __ordering_index to the result\n */\nexport function processOrderBy(\n rawQuery: QueryIR,\n pipeline: NamespacedAndKeyedStream,\n orderByClause: Array<OrderByClause>,\n selectClause: Select,\n collection: Collection,\n optimizableOrderByCollections: Record<string, OrderByOptimizationInfo>,\n setWindowFn: (windowFn: (options: WindowOptions) => void) => void,\n limit?: number,\n offset?: number,\n): IStreamBuilder<KeyValue<unknown, [NamespacedRow, string]>> {\n // Pre-compile all order by expressions\n const compiledOrderBy = orderByClause.map((clause) => {\n const clauseWithoutAggregates = replaceAggregatesByRefs(\n clause.expression,\n selectClause,\n `$selected`,\n )\n\n return {\n compiledExpression: compileExpression(clauseWithoutAggregates),\n compareOptions: buildCompareOptions(clause, collection),\n }\n })\n\n // Create a value extractor function for the orderBy operator\n const valueExtractor = (row: NamespacedRow & { $selected?: any }) => {\n // The namespaced row contains:\n // 1. Table aliases as top-level properties (e.g., row[\"tableName\"])\n // 2. SELECT results in $selected (e.g., row.$selected[\"aggregateAlias\"])\n // The replaceAggregatesByRefs function has already transformed:\n // - Aggregate expressions that match SELECT aggregates to use the $selected namespace\n // - $selected ref expressions are passed through unchanged (already using the correct namespace)\n const orderByContext = row\n\n if (orderByClause.length > 1) {\n // For multiple orderBy columns, create a composite key\n return compiledOrderBy.map((compiled) =>\n compiled.compiledExpression(orderByContext),\n )\n } else if (orderByClause.length === 1) {\n // For a single orderBy column, use the value directly\n const compiled = compiledOrderBy[0]!\n return compiled.compiledExpression(orderByContext)\n }\n\n // Default case - no ordering\n return null\n }\n\n // Create a multi-property comparator that respects the order and direction of each property\n const compare = (a: unknown, b: unknown) => {\n // If we're comparing arrays (multiple properties), compare each property in order\n if (orderByClause.length > 1) {\n const arrayA = a as Array<unknown>\n const arrayB = b as Array<unknown>\n for (let i = 0; i < orderByClause.length; i++) {\n const clause = compiledOrderBy[i]!\n const compareFn = makeComparator(clause.compareOptions)\n const result = compareFn(arrayA[i], arrayB[i])\n if (result !== 0) {\n return result\n }\n }\n return arrayA.length - arrayB.length\n }\n\n // Single property comparison\n if (orderByClause.length === 1) {\n const clause = compiledOrderBy[0]!\n const compareFn = makeComparator(clause.compareOptions)\n return compareFn(a, b)\n }\n\n return defaultComparator(a, b)\n }\n\n let setSizeCallback: ((getSize: () => number) => void) | undefined\n\n let orderByOptimizationInfo: OrderByOptimizationInfo | undefined\n\n // When there's a limit, we create orderByOptimizationInfo to pass orderBy/limit\n // to loadSubset so the sync layer can optimize the query.\n // We try to use an index on the FIRST orderBy column for lazy loading,\n // even for multi-column orderBy (using wider bounds on first column).\n if (limit) {\n let index: IndexInterface<string | number> | undefined\n let followRefCollection: Collection | undefined\n let firstColumnValueExtractor: CompiledSingleRowExpression | undefined\n let orderByAlias: string = rawQuery.from.alias\n\n // Try to create/find an index on the FIRST orderBy column for lazy loading\n const firstClause = orderByClause[0]!\n const firstOrderByExpression = firstClause.expression\n\n if (firstOrderByExpression.type === `ref`) {\n const followRefResult = followRef(\n rawQuery,\n firstOrderByExpression,\n collection,\n )\n\n if (followRefResult) {\n followRefCollection = followRefResult.collection\n const fieldName = followRefResult.path[0]\n const compareOpts = buildCompareOptions(\n firstClause,\n followRefCollection,\n )\n\n if (fieldName) {\n ensureIndexForField(\n fieldName,\n followRefResult.path,\n followRefCollection,\n compareOpts,\n compare,\n )\n }\n\n // First column value extractor - used for index cursor\n firstColumnValueExtractor = compileExpression(\n new PropRef(followRefResult.path),\n true,\n ) as CompiledSingleRowExpression\n\n index = findIndexForField(\n followRefCollection,\n followRefResult.path,\n compareOpts,\n )\n\n // Only use the index if it supports range queries\n if (!index?.supports(`gt`)) {\n index = undefined\n }\n\n orderByAlias =\n firstOrderByExpression.path.length > 1\n ? String(firstOrderByExpression.path[0])\n : rawQuery.from.alias\n }\n }\n\n // Only create comparator and value extractors if the first column is a ref expression\n // For aggregate or computed expressions, we can't extract values from raw collection rows\n if (!firstColumnValueExtractor) {\n // Skip optimization for non-ref expressions (aggregates, computed values, etc.)\n // The query will still work, but without lazy loading optimization\n } else {\n // Build value extractors for all columns (must all be ref expressions for multi-column)\n // Check if all orderBy expressions are ref types (required for multi-column extraction)\n const allColumnsAreRefs = orderByClause.every(\n (clause) => clause.expression.type === `ref`,\n )\n\n // Create extractors for all columns if they're all refs\n const allColumnExtractors:\n | Array<CompiledSingleRowExpression>\n | undefined = allColumnsAreRefs\n ? orderByClause.map((clause) => {\n // We know it's a ref since we checked allColumnsAreRefs\n const refExpr = clause.expression as PropRef\n const followResult = followRef(rawQuery, refExpr, collection)\n if (followResult) {\n return compileExpression(\n new PropRef(followResult.path),\n true,\n ) as CompiledSingleRowExpression\n }\n // Fallback for refs that don't follow\n return compileExpression(\n clause.expression,\n true,\n ) as CompiledSingleRowExpression\n })\n : undefined\n\n // Create a comparator for raw rows (used for tracking sent values)\n // This compares ALL orderBy columns for proper ordering\n const comparator = (\n a: Record<string, unknown> | null | undefined,\n b: Record<string, unknown> | null | undefined,\n ) => {\n if (orderByClause.length === 1) {\n // Single column: extract and compare\n const extractedA = a ? firstColumnValueExtractor(a) : a\n const extractedB = b ? firstColumnValueExtractor(b) : b\n return compare(extractedA, extractedB)\n }\n if (allColumnExtractors) {\n // Multi-column with all refs: extract all values and compare\n const extractAll = (\n row: Record<string, unknown> | null | undefined,\n ) => {\n if (!row) return row\n return allColumnExtractors.map((extractor) => extractor(row))\n }\n return compare(extractAll(a), extractAll(b))\n }\n // Fallback: can't compare (shouldn't happen since we skip non-ref cases)\n return 0\n }\n\n // Create a value extractor for raw rows that extracts ALL orderBy column values\n // This is used for tracking sent values and building composite cursors\n const rawRowValueExtractor = (row: Record<string, unknown>): unknown => {\n if (orderByClause.length === 1) {\n // Single column: return single value\n return firstColumnValueExtractor(row)\n }\n if (allColumnExtractors) {\n // Multi-column: return array of all values\n return allColumnExtractors.map((extractor) => extractor(row))\n }\n // Fallback (shouldn't happen)\n return undefined\n }\n\n orderByOptimizationInfo = {\n alias: orderByAlias,\n offset: offset ?? 0,\n limit,\n comparator,\n valueExtractorForRawRow: rawRowValueExtractor,\n firstColumnValueExtractor: firstColumnValueExtractor,\n index,\n orderBy: orderByClause,\n }\n\n // Store the optimization info keyed by collection ID\n // Use the followed collection if available, otherwise use the main collection\n const targetCollectionId = followRefCollection?.id ?? collection.id\n optimizableOrderByCollections[targetCollectionId] =\n orderByOptimizationInfo\n\n // Set up lazy loading callback if we have an index\n if (index) {\n setSizeCallback = (getSize: () => number) => {\n optimizableOrderByCollections[targetCollectionId]![`dataNeeded`] =\n () => {\n const size = getSize()\n return Math.max(0, orderByOptimizationInfo!.limit - size)\n }\n }\n }\n }\n }\n\n // Use fractional indexing and return the tuple [value, index]\n return pipeline.pipe(\n orderByWithFractionalIndex(valueExtractor, {\n limit,\n offset,\n comparator: compare,\n setSizeCallback,\n setWindowFn: (\n windowFn: (options: { offset?: number; limit?: number }) => void,\n ) => {\n setWindowFn(\n // We wrap the move function such that we update the orderByOptimizationInfo\n // because that is used by the `dataNeeded` callback to determine if we need to load more data\n (options) => {\n windowFn(options)\n if (orderByOptimizationInfo) {\n orderByOptimizationInfo.offset =\n options.offset ?? orderByOptimizationInfo.offset\n orderByOptimizationInfo.limit =\n options.limit ?? orderByOptimizationInfo.limit\n }\n },\n )\n },\n }),\n // orderByWithFractionalIndex returns [key, [value, index]] - we keep this format\n )\n}\n\n/**\n * Builds a comparison configuration object that uses the values provided in the orderBy clause.\n * If no string sort configuration is provided it defaults to the collection's string sort configuration.\n */\nexport function buildCompareOptions(\n clause: OrderByClause,\n collection: CollectionLike<any, any>,\n): CompareOptions {\n if (clause.compareOptions.stringSort !== undefined) {\n return clause.compareOptions\n }\n\n return {\n ...collection.compareOptions,\n direction: clause.compareOptions.direction,\n nulls: clause.compareOptions.nulls,\n }\n}\n"],"names":["replaceAggregatesByRefs","compileExpression","makeComparator","defaultComparator","followRef","ensureIndexForField","PropRef","findIndexForField","orderByWithFractionalIndex"],"mappings":"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"}
1
+ {"version":3,"file":"order-by.cjs","sources":["../../../../src/query/compiler/order-by.ts"],"sourcesContent":["import {\n groupedOrderByWithFractionalIndex,\n orderByWithFractionalIndex,\n} from '@tanstack/db-ivm'\nimport { defaultComparator, makeComparator } from '../../utils/comparison.js'\nimport { PropRef, followRef } from '../ir.js'\nimport { ensureIndexForField } from '../../indexes/auto-index.js'\nimport { findIndexForField } from '../../utils/index-optimization.js'\nimport { compileExpression } from './evaluators.js'\nimport { replaceAggregatesByRefs } from './group-by.js'\nimport type { CompareOptions } from '../builder/types.js'\nimport type { WindowOptions } from './types.js'\nimport type { CompiledSingleRowExpression } from './evaluators.js'\nimport type { OrderBy, OrderByClause, QueryIR, Select } from '../ir.js'\nimport type {\n CollectionLike,\n NamespacedAndKeyedStream,\n NamespacedRow,\n} from '../../types.js'\nimport type { IStreamBuilder, KeyValue } from '@tanstack/db-ivm'\nimport type { IndexInterface } from '../../indexes/base-index.js'\nimport type { Collection } from '../../collection/index.js'\n\nexport type OrderByOptimizationInfo = {\n alias: string\n orderBy: OrderBy\n offset: number\n limit: number\n comparator: (\n a: Record<string, unknown> | null | undefined,\n b: Record<string, unknown> | null | undefined,\n ) => number\n /** Extracts all orderBy column values from a raw row (array for multi-column) */\n valueExtractorForRawRow: (row: Record<string, unknown>) => unknown\n /** Extracts only the first column value - used for index-based cursor */\n firstColumnValueExtractor: (row: Record<string, unknown>) => unknown\n /** Index on the first orderBy column - used for lazy loading */\n index?: IndexInterface<string | number>\n dataNeeded?: () => number\n}\n\n/**\n * Processes the ORDER BY clause\n * Works with the new structure that has both namespaced row data and $selected\n * Always uses fractional indexing and adds the index as __ordering_index to the result\n */\nexport function processOrderBy(\n rawQuery: QueryIR,\n pipeline: NamespacedAndKeyedStream,\n orderByClause: Array<OrderByClause>,\n selectClause: Select,\n collection: Collection,\n optimizableOrderByCollections: Record<string, OrderByOptimizationInfo>,\n setWindowFn: (windowFn: (options: WindowOptions) => void) => void,\n limit?: number,\n offset?: number,\n groupKeyFn?: (key: unknown, value: unknown) => unknown,\n): IStreamBuilder<KeyValue<unknown, [NamespacedRow, string]>> {\n // Pre-compile all order by expressions\n const compiledOrderBy = orderByClause.map((clause) => {\n const clauseWithoutAggregates = replaceAggregatesByRefs(\n clause.expression,\n selectClause,\n `$selected`,\n )\n\n return {\n compiledExpression: compileExpression(clauseWithoutAggregates),\n compareOptions: buildCompareOptions(clause, collection),\n }\n })\n\n // Create a value extractor function for the orderBy operator\n const valueExtractor = (row: NamespacedRow & { $selected?: any }) => {\n // The namespaced row contains:\n // 1. Table aliases as top-level properties (e.g., row[\"tableName\"])\n // 2. SELECT results in $selected (e.g., row.$selected[\"aggregateAlias\"])\n // The replaceAggregatesByRefs function has already transformed:\n // - Aggregate expressions that match SELECT aggregates to use the $selected namespace\n // - $selected ref expressions are passed through unchanged (already using the correct namespace)\n const orderByContext = row\n\n if (orderByClause.length > 1) {\n // For multiple orderBy columns, create a composite key\n return compiledOrderBy.map((compiled) =>\n compiled.compiledExpression(orderByContext),\n )\n } else if (orderByClause.length === 1) {\n // For a single orderBy column, use the value directly\n const compiled = compiledOrderBy[0]!\n return compiled.compiledExpression(orderByContext)\n }\n\n // Default case - no ordering\n return null\n }\n\n // Create a multi-property comparator that respects the order and direction of each property\n const compare = (a: unknown, b: unknown) => {\n // If we're comparing arrays (multiple properties), compare each property in order\n if (orderByClause.length > 1) {\n const arrayA = a as Array<unknown>\n const arrayB = b as Array<unknown>\n for (let i = 0; i < orderByClause.length; i++) {\n const clause = compiledOrderBy[i]!\n const compareFn = makeComparator(clause.compareOptions)\n const result = compareFn(arrayA[i], arrayB[i])\n if (result !== 0) {\n return result\n }\n }\n return arrayA.length - arrayB.length\n }\n\n // Single property comparison\n if (orderByClause.length === 1) {\n const clause = compiledOrderBy[0]!\n const compareFn = makeComparator(clause.compareOptions)\n return compareFn(a, b)\n }\n\n return defaultComparator(a, b)\n }\n\n let setSizeCallback: ((getSize: () => number) => void) | undefined\n\n let orderByOptimizationInfo: OrderByOptimizationInfo | undefined\n\n // When there's a limit, we create orderByOptimizationInfo to pass orderBy/limit\n // to loadSubset so the sync layer can optimize the query.\n // We try to use an index on the FIRST orderBy column for lazy loading,\n // even for multi-column orderBy (using wider bounds on first column).\n // Skip this optimization when using grouped ordering (includes with limit),\n // because the limit is per-group, not global — the child collection needs all data loaded.\n if (limit && !groupKeyFn) {\n let index: IndexInterface<string | number> | undefined\n let followRefCollection: Collection | undefined\n let firstColumnValueExtractor: CompiledSingleRowExpression | undefined\n let orderByAlias: string = rawQuery.from.alias\n\n // Try to create/find an index on the FIRST orderBy column for lazy loading\n const firstClause = orderByClause[0]!\n const firstOrderByExpression = firstClause.expression\n\n if (firstOrderByExpression.type === `ref`) {\n const followRefResult = followRef(\n rawQuery,\n firstOrderByExpression,\n collection,\n )\n\n if (followRefResult) {\n followRefCollection = followRefResult.collection\n const fieldName = followRefResult.path[0]\n const compareOpts = buildCompareOptions(\n firstClause,\n followRefCollection,\n )\n\n if (fieldName) {\n // Use a single-column comparator for the index, not the\n // multi-column `compare` function. The multi-column comparator\n // expects array values [col1, col2, ...] but the index stores\n // individual field values. Passing `compare` here causes the\n // BTree to treat all single values as equal (since number[0]\n // === undefined for both sides of the comparison).\n const firstColumnCompareFn = makeComparator(compareOpts)\n ensureIndexForField(\n fieldName,\n followRefResult.path,\n followRefCollection,\n compareOpts,\n firstColumnCompareFn,\n )\n }\n\n // First column value extractor - used for index cursor\n firstColumnValueExtractor = compileExpression(\n new PropRef(followRefResult.path),\n true,\n ) as CompiledSingleRowExpression\n\n index = findIndexForField(\n followRefCollection,\n followRefResult.path,\n compareOpts,\n )\n\n // Only use the index if it supports range queries\n if (!index?.supports(`gt`)) {\n index = undefined\n }\n\n orderByAlias =\n firstOrderByExpression.path.length > 1\n ? String(firstOrderByExpression.path[0])\n : rawQuery.from.alias\n }\n }\n\n // Only create comparator and value extractors if the first column is a ref expression\n // For aggregate or computed expressions, we can't extract values from raw collection rows\n if (!firstColumnValueExtractor) {\n // Skip optimization for non-ref expressions (aggregates, computed values, etc.)\n // The query will still work, but without lazy loading optimization\n } else {\n // Build value extractors for all columns (must all be ref expressions for multi-column)\n // Check if all orderBy expressions are ref types (required for multi-column extraction)\n const allColumnsAreRefs = orderByClause.every(\n (clause) => clause.expression.type === `ref`,\n )\n\n // Create extractors for all columns if they're all refs\n const allColumnExtractors:\n | Array<CompiledSingleRowExpression>\n | undefined = allColumnsAreRefs\n ? orderByClause.map((clause) => {\n // We know it's a ref since we checked allColumnsAreRefs\n const refExpr = clause.expression as PropRef\n const followResult = followRef(rawQuery, refExpr, collection)\n if (followResult) {\n return compileExpression(\n new PropRef(followResult.path),\n true,\n ) as CompiledSingleRowExpression\n }\n // Fallback for refs that don't follow\n return compileExpression(\n clause.expression,\n true,\n ) as CompiledSingleRowExpression\n })\n : undefined\n\n // Create a comparator for raw rows (used for tracking sent values)\n // This compares ALL orderBy columns for proper ordering\n const comparator = (\n a: Record<string, unknown> | null | undefined,\n b: Record<string, unknown> | null | undefined,\n ) => {\n if (orderByClause.length === 1) {\n // Single column: extract and compare\n const extractedA = a ? firstColumnValueExtractor(a) : a\n const extractedB = b ? firstColumnValueExtractor(b) : b\n return compare(extractedA, extractedB)\n }\n if (allColumnExtractors) {\n // Multi-column with all refs: extract all values and compare\n const extractAll = (\n row: Record<string, unknown> | null | undefined,\n ) => {\n if (!row) return row\n return allColumnExtractors.map((extractor) => extractor(row))\n }\n return compare(extractAll(a), extractAll(b))\n }\n // Fallback: can't compare (shouldn't happen since we skip non-ref cases)\n return 0\n }\n\n // Create a value extractor for raw rows that extracts ALL orderBy column values\n // This is used for tracking sent values and building composite cursors\n const rawRowValueExtractor = (row: Record<string, unknown>): unknown => {\n if (orderByClause.length === 1) {\n // Single column: return single value\n return firstColumnValueExtractor(row)\n }\n if (allColumnExtractors) {\n // Multi-column: return array of all values\n return allColumnExtractors.map((extractor) => extractor(row))\n }\n // Fallback (shouldn't happen)\n return undefined\n }\n\n orderByOptimizationInfo = {\n alias: orderByAlias,\n offset: offset ?? 0,\n limit,\n comparator,\n valueExtractorForRawRow: rawRowValueExtractor,\n firstColumnValueExtractor: firstColumnValueExtractor,\n index,\n orderBy: orderByClause,\n }\n\n // Store the optimization info keyed by collection ID\n // Use the followed collection if available, otherwise use the main collection\n const targetCollectionId = followRefCollection?.id ?? collection.id\n optimizableOrderByCollections[targetCollectionId] =\n orderByOptimizationInfo\n\n // Set up lazy loading callback to track how much more data is needed\n // This is used by loadMoreIfNeeded to determine if more data should be loaded\n setSizeCallback = (getSize: () => number) => {\n optimizableOrderByCollections[targetCollectionId]![`dataNeeded`] =\n () => {\n const size = getSize()\n return Math.max(0, orderByOptimizationInfo!.limit - size)\n }\n }\n }\n }\n\n // Use grouped ordering when a groupKeyFn is provided (includes with limit/offset),\n // otherwise use the standard global ordering operator.\n if (groupKeyFn) {\n return pipeline.pipe(\n groupedOrderByWithFractionalIndex(valueExtractor, {\n limit,\n offset,\n comparator: compare,\n setSizeCallback,\n groupKeyFn,\n setWindowFn: (\n windowFn: (options: { offset?: number; limit?: number }) => void,\n ) => {\n setWindowFn((options) => {\n windowFn(options)\n if (orderByOptimizationInfo) {\n orderByOptimizationInfo.offset =\n options.offset ?? orderByOptimizationInfo.offset\n orderByOptimizationInfo.limit =\n options.limit ?? orderByOptimizationInfo.limit\n }\n })\n },\n }),\n )\n }\n\n // Use fractional indexing and return the tuple [value, index]\n return pipeline.pipe(\n orderByWithFractionalIndex(valueExtractor, {\n limit,\n offset,\n comparator: compare,\n setSizeCallback,\n setWindowFn: (\n windowFn: (options: { offset?: number; limit?: number }) => void,\n ) => {\n setWindowFn(\n // We wrap the move function such that we update the orderByOptimizationInfo\n // because that is used by the `dataNeeded` callback to determine if we need to load more data\n (options) => {\n windowFn(options)\n if (orderByOptimizationInfo) {\n orderByOptimizationInfo.offset =\n options.offset ?? orderByOptimizationInfo.offset\n orderByOptimizationInfo.limit =\n options.limit ?? orderByOptimizationInfo.limit\n }\n },\n )\n },\n }),\n // orderByWithFractionalIndex returns [key, [value, index]] - we keep this format\n )\n}\n\n/**\n * Builds a comparison configuration object that uses the values provided in the orderBy clause.\n * If no string sort configuration is provided it defaults to the collection's string sort configuration.\n */\nexport function buildCompareOptions(\n clause: OrderByClause,\n collection: CollectionLike<any, any>,\n): CompareOptions {\n if (clause.compareOptions.stringSort !== undefined) {\n return clause.compareOptions\n }\n\n return {\n ...collection.compareOptions,\n direction: clause.compareOptions.direction,\n nulls: clause.compareOptions.nulls,\n }\n}\n"],"names":["replaceAggregatesByRefs","compileExpression","makeComparator","defaultComparator","followRef","ensureIndexForField","PropRef","findIndexForField","groupedOrderByWithFractionalIndex","orderByWithFractionalIndex"],"mappings":"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"}
@@ -24,7 +24,7 @@ export type OrderByOptimizationInfo = {
24
24
  * Works with the new structure that has both namespaced row data and $selected
25
25
  * Always uses fractional indexing and adds the index as __ordering_index to the result
26
26
  */
27
- export declare function processOrderBy(rawQuery: QueryIR, pipeline: NamespacedAndKeyedStream, orderByClause: Array<OrderByClause>, selectClause: Select, collection: Collection, optimizableOrderByCollections: Record<string, OrderByOptimizationInfo>, setWindowFn: (windowFn: (options: WindowOptions) => void) => void, limit?: number, offset?: number): IStreamBuilder<KeyValue<unknown, [NamespacedRow, string]>>;
27
+ export declare function processOrderBy(rawQuery: QueryIR, pipeline: NamespacedAndKeyedStream, orderByClause: Array<OrderByClause>, selectClause: Select, collection: Collection, optimizableOrderByCollections: Record<string, OrderByOptimizationInfo>, setWindowFn: (windowFn: (options: WindowOptions) => void) => void, limit?: number, offset?: number, groupKeyFn?: (key: unknown, value: unknown) => unknown): IStreamBuilder<KeyValue<unknown, [NamespacedRow, string]>>;
28
28
  /**
29
29
  * Builds a comparison configuration object that uses the values provided in the orderBy clause.
30
30
  * If no string sort configuration is provided it defaults to the collection's string sort configuration.
@@ -107,6 +107,14 @@ function addFromObject(prefixPath, obj, ops) {
107
107
  continue;
108
108
  }
109
109
  const expression = value;
110
+ if (expression && expression.type === `includesSubquery`) {
111
+ ops.push({
112
+ kind: `field`,
113
+ alias: [...prefixPath, key].join(`.`),
114
+ compiled: () => null
115
+ });
116
+ continue;
117
+ }
110
118
  if (isNestedSelectObject(expression)) {
111
119
  addFromObject([...prefixPath, key], expression, ops);
112
120
  continue;
@@ -1 +1 @@
1
- {"version":3,"file":"select.cjs","sources":["../../../../src/query/compiler/select.ts"],"sourcesContent":["import { map } from '@tanstack/db-ivm'\nimport { PropRef, Value as ValClass, isExpressionLike } from '../ir.js'\nimport { AggregateNotSupportedError } from '../../errors.js'\nimport { compileExpression } from './evaluators.js'\nimport { containsAggregate } from './group-by.js'\nimport type { Aggregate, BasicExpression, Select } from '../ir.js'\nimport type {\n KeyedStream,\n NamespacedAndKeyedStream,\n NamespacedRow,\n} from '../../types.js'\n\n/**\n * Type for operations array used in select processing\n */\ntype SelectOp =\n | {\n kind: `merge`\n targetPath: Array<string>\n source: (row: NamespacedRow) => any\n }\n | { kind: `field`; alias: string; compiled: (row: NamespacedRow) => any }\n\n/**\n * Unwraps any Value expressions\n */\nfunction unwrapVal(input: any): any {\n if (input instanceof ValClass) return input.value\n return input\n}\n\n/**\n * Processes a merge operation by merging source values into the target path\n */\nfunction processMerge(\n op: Extract<SelectOp, { kind: `merge` }>,\n namespacedRow: NamespacedRow,\n selectResults: Record<string, any>,\n): void {\n const value = op.source(namespacedRow)\n if (value && typeof value === `object`) {\n // Ensure target object exists\n let cursor: any = selectResults\n const path = op.targetPath\n if (path.length === 0) {\n // Top-level merge\n for (const [k, v] of Object.entries(value)) {\n selectResults[k] = unwrapVal(v)\n }\n } else {\n for (let i = 0; i < path.length; i++) {\n const seg = path[i]!\n if (i === path.length - 1) {\n const dest = (cursor[seg] ??= {})\n if (typeof dest === `object`) {\n for (const [k, v] of Object.entries(value)) {\n dest[k] = unwrapVal(v)\n }\n }\n } else {\n const next = cursor[seg]\n if (next == null || typeof next !== `object`) {\n cursor[seg] = {}\n }\n cursor = cursor[seg]\n }\n }\n }\n }\n}\n\n/**\n * Processes a non-merge operation by setting the field value at the specified alias path\n */\nfunction processNonMergeOp(\n op: Extract<SelectOp, { kind: `field` }>,\n namespacedRow: NamespacedRow,\n selectResults: Record<string, any>,\n): void {\n // Support nested alias paths like \"meta.author.name\"\n const path = op.alias.split(`.`)\n if (path.length === 1) {\n selectResults[op.alias] = op.compiled(namespacedRow)\n } else {\n let cursor: any = selectResults\n for (let i = 0; i < path.length - 1; i++) {\n const seg = path[i]!\n const next = cursor[seg]\n if (next == null || typeof next !== `object`) {\n cursor[seg] = {}\n }\n cursor = cursor[seg]\n }\n cursor[path[path.length - 1]!] = unwrapVal(op.compiled(namespacedRow))\n }\n}\n\n/**\n * Processes a single row to generate select results\n */\nfunction processRow(\n [key, namespacedRow]: [unknown, NamespacedRow],\n ops: Array<SelectOp>,\n): [unknown, typeof namespacedRow & { $selected: any }] {\n const selectResults: Record<string, any> = {}\n\n for (const op of ops) {\n if (op.kind === `merge`) {\n processMerge(op, namespacedRow, selectResults)\n } else {\n processNonMergeOp(op, namespacedRow, selectResults)\n }\n }\n\n // Return the namespaced row with $selected added\n return [\n key,\n {\n ...namespacedRow,\n $selected: selectResults,\n },\n ] as [unknown, typeof namespacedRow & { $selected: typeof selectResults }]\n}\n\n/**\n * Processes the SELECT clause and places results in $selected\n * while preserving the original namespaced row for ORDER BY access\n */\nexport function processSelect(\n pipeline: NamespacedAndKeyedStream,\n select: Select,\n _allInputs: Record<string, KeyedStream>,\n): NamespacedAndKeyedStream {\n // Build ordered operations to preserve authoring order (spreads and fields)\n const ops: Array<SelectOp> = []\n\n addFromObject([], select, ops)\n\n return pipeline.pipe(map((row) => processRow(row, ops)))\n}\n\n/**\n * Helper function to check if an expression is an aggregate\n */\nfunction isAggregateExpression(\n expr: BasicExpression | Aggregate,\n): expr is Aggregate {\n return expr.type === `agg`\n}\n\n/**\n * Processes a single argument in a function context\n */\nexport function processArgument(\n arg: BasicExpression | Aggregate,\n namespacedRow: NamespacedRow,\n): any {\n if (isAggregateExpression(arg)) {\n throw new AggregateNotSupportedError()\n }\n\n // Pre-compile the expression and evaluate immediately\n const compiledExpression = compileExpression(arg)\n const value = compiledExpression(namespacedRow)\n\n return value\n}\n\n/**\n * Helper function to check if an object is a nested select object\n *\n * .select({\n * id: users.id,\n * profile: { // <-- this is a nested select object\n * name: users.name,\n * email: users.email\n * }\n * })\n */\nfunction isNestedSelectObject(obj: any): boolean {\n return obj && typeof obj === `object` && !isExpressionLike(obj)\n}\n\n/**\n * Helper function to process select objects and build operations array\n */\nfunction addFromObject(\n prefixPath: Array<string>,\n obj: any,\n ops: Array<SelectOp>,\n) {\n for (const [key, value] of Object.entries(obj)) {\n if (key.startsWith(`__SPREAD_SENTINEL__`)) {\n const rest = key.slice(`__SPREAD_SENTINEL__`.length)\n const splitIndex = rest.lastIndexOf(`__`)\n const pathStr = splitIndex >= 0 ? rest.slice(0, splitIndex) : rest\n const isRefExpr =\n value &&\n typeof value === `object` &&\n `type` in (value as any) &&\n (value as any).type === `ref`\n if (pathStr.includes(`.`) || isRefExpr) {\n // Merge into the current destination (prefixPath) from the referenced source path\n const targetPath = [...prefixPath]\n const expr = isRefExpr\n ? (value as BasicExpression)\n : (new PropRef(pathStr.split(`.`)) as BasicExpression)\n const compiled = compileExpression(expr)\n ops.push({ kind: `merge`, targetPath, source: compiled })\n } else {\n // Table-level: pathStr is the alias; merge from namespaced row at the current prefix\n const tableAlias = pathStr\n const targetPath = [...prefixPath]\n ops.push({\n kind: `merge`,\n targetPath,\n source: (row) => (row as any)[tableAlias],\n })\n }\n continue\n }\n\n const expression = value as any\n if (isNestedSelectObject(expression)) {\n // Nested selection object\n addFromObject([...prefixPath, key], expression, ops)\n continue\n }\n\n if (isAggregateExpression(expression) || containsAggregate(expression)) {\n // Placeholder for group-by processing later.\n // Both plain aggregates (count(...)) and expressions wrapping\n // aggregates (coalesce(count(...), 0)) are deferred to processGroupBy.\n ops.push({\n kind: `field`,\n alias: [...prefixPath, key].join(`.`),\n compiled: () => null,\n })\n } else {\n if (expression === undefined || !isExpressionLike(expression)) {\n ops.push({\n kind: `field`,\n alias: [...prefixPath, key].join(`.`),\n compiled: () => expression,\n })\n continue\n }\n // If the expression is a Value wrapper, embed the literal to avoid re-compilation mishaps\n if (expression instanceof ValClass) {\n const val = expression.value\n ops.push({\n kind: `field`,\n alias: [...prefixPath, key].join(`.`),\n compiled: () => val,\n })\n } else {\n ops.push({\n kind: `field`,\n alias: [...prefixPath, key].join(`.`),\n compiled: compileExpression(expression as BasicExpression),\n })\n }\n }\n }\n}\n"],"names":["ValClass","map","isExpressionLike","PropRef","compileExpression","containsAggregate"],"mappings":"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"}
1
+ {"version":3,"file":"select.cjs","sources":["../../../../src/query/compiler/select.ts"],"sourcesContent":["import { map } from '@tanstack/db-ivm'\nimport { PropRef, Value as ValClass, isExpressionLike } from '../ir.js'\nimport { AggregateNotSupportedError } from '../../errors.js'\nimport { compileExpression } from './evaluators.js'\nimport { containsAggregate } from './group-by.js'\nimport type { Aggregate, BasicExpression, Select } from '../ir.js'\nimport type {\n KeyedStream,\n NamespacedAndKeyedStream,\n NamespacedRow,\n} from '../../types.js'\n\n/**\n * Type for operations array used in select processing\n */\ntype SelectOp =\n | {\n kind: `merge`\n targetPath: Array<string>\n source: (row: NamespacedRow) => any\n }\n | { kind: `field`; alias: string; compiled: (row: NamespacedRow) => any }\n\n/**\n * Unwraps any Value expressions\n */\nfunction unwrapVal(input: any): any {\n if (input instanceof ValClass) return input.value\n return input\n}\n\n/**\n * Processes a merge operation by merging source values into the target path\n */\nfunction processMerge(\n op: Extract<SelectOp, { kind: `merge` }>,\n namespacedRow: NamespacedRow,\n selectResults: Record<string, any>,\n): void {\n const value = op.source(namespacedRow)\n if (value && typeof value === `object`) {\n // Ensure target object exists\n let cursor: any = selectResults\n const path = op.targetPath\n if (path.length === 0) {\n // Top-level merge\n for (const [k, v] of Object.entries(value)) {\n selectResults[k] = unwrapVal(v)\n }\n } else {\n for (let i = 0; i < path.length; i++) {\n const seg = path[i]!\n if (i === path.length - 1) {\n const dest = (cursor[seg] ??= {})\n if (typeof dest === `object`) {\n for (const [k, v] of Object.entries(value)) {\n dest[k] = unwrapVal(v)\n }\n }\n } else {\n const next = cursor[seg]\n if (next == null || typeof next !== `object`) {\n cursor[seg] = {}\n }\n cursor = cursor[seg]\n }\n }\n }\n }\n}\n\n/**\n * Processes a non-merge operation by setting the field value at the specified alias path\n */\nfunction processNonMergeOp(\n op: Extract<SelectOp, { kind: `field` }>,\n namespacedRow: NamespacedRow,\n selectResults: Record<string, any>,\n): void {\n // Support nested alias paths like \"meta.author.name\"\n const path = op.alias.split(`.`)\n if (path.length === 1) {\n selectResults[op.alias] = op.compiled(namespacedRow)\n } else {\n let cursor: any = selectResults\n for (let i = 0; i < path.length - 1; i++) {\n const seg = path[i]!\n const next = cursor[seg]\n if (next == null || typeof next !== `object`) {\n cursor[seg] = {}\n }\n cursor = cursor[seg]\n }\n cursor[path[path.length - 1]!] = unwrapVal(op.compiled(namespacedRow))\n }\n}\n\n/**\n * Processes a single row to generate select results\n */\nfunction processRow(\n [key, namespacedRow]: [unknown, NamespacedRow],\n ops: Array<SelectOp>,\n): [unknown, typeof namespacedRow & { $selected: any }] {\n const selectResults: Record<string, any> = {}\n\n for (const op of ops) {\n if (op.kind === `merge`) {\n processMerge(op, namespacedRow, selectResults)\n } else {\n processNonMergeOp(op, namespacedRow, selectResults)\n }\n }\n\n // Return the namespaced row with $selected added\n return [\n key,\n {\n ...namespacedRow,\n $selected: selectResults,\n },\n ] as [unknown, typeof namespacedRow & { $selected: typeof selectResults }]\n}\n\n/**\n * Processes the SELECT clause and places results in $selected\n * while preserving the original namespaced row for ORDER BY access\n */\nexport function processSelect(\n pipeline: NamespacedAndKeyedStream,\n select: Select,\n _allInputs: Record<string, KeyedStream>,\n): NamespacedAndKeyedStream {\n // Build ordered operations to preserve authoring order (spreads and fields)\n const ops: Array<SelectOp> = []\n\n addFromObject([], select, ops)\n\n return pipeline.pipe(map((row) => processRow(row, ops)))\n}\n\n/**\n * Helper function to check if an expression is an aggregate\n */\nfunction isAggregateExpression(\n expr: BasicExpression | Aggregate,\n): expr is Aggregate {\n return expr.type === `agg`\n}\n\n/**\n * Processes a single argument in a function context\n */\nexport function processArgument(\n arg: BasicExpression | Aggregate,\n namespacedRow: NamespacedRow,\n): any {\n if (isAggregateExpression(arg)) {\n throw new AggregateNotSupportedError()\n }\n\n // Pre-compile the expression and evaluate immediately\n const compiledExpression = compileExpression(arg)\n const value = compiledExpression(namespacedRow)\n\n return value\n}\n\n/**\n * Helper function to check if an object is a nested select object\n *\n * .select({\n * id: users.id,\n * profile: { // <-- this is a nested select object\n * name: users.name,\n * email: users.email\n * }\n * })\n */\nfunction isNestedSelectObject(obj: any): boolean {\n return obj && typeof obj === `object` && !isExpressionLike(obj)\n}\n\n/**\n * Helper function to process select objects and build operations array\n */\nfunction addFromObject(\n prefixPath: Array<string>,\n obj: any,\n ops: Array<SelectOp>,\n) {\n for (const [key, value] of Object.entries(obj)) {\n if (key.startsWith(`__SPREAD_SENTINEL__`)) {\n const rest = key.slice(`__SPREAD_SENTINEL__`.length)\n const splitIndex = rest.lastIndexOf(`__`)\n const pathStr = splitIndex >= 0 ? rest.slice(0, splitIndex) : rest\n const isRefExpr =\n value &&\n typeof value === `object` &&\n `type` in (value as any) &&\n (value as any).type === `ref`\n if (pathStr.includes(`.`) || isRefExpr) {\n // Merge into the current destination (prefixPath) from the referenced source path\n const targetPath = [...prefixPath]\n const expr = isRefExpr\n ? (value as BasicExpression)\n : (new PropRef(pathStr.split(`.`)) as BasicExpression)\n const compiled = compileExpression(expr)\n ops.push({ kind: `merge`, targetPath, source: compiled })\n } else {\n // Table-level: pathStr is the alias; merge from namespaced row at the current prefix\n const tableAlias = pathStr\n const targetPath = [...prefixPath]\n ops.push({\n kind: `merge`,\n targetPath,\n source: (row) => (row as any)[tableAlias],\n })\n }\n continue\n }\n\n const expression = value as any\n if (expression && expression.type === `includesSubquery`) {\n // Placeholder — field will be set to a child Collection by the output layer\n ops.push({\n kind: `field`,\n alias: [...prefixPath, key].join(`.`),\n compiled: () => null,\n })\n continue\n }\n if (isNestedSelectObject(expression)) {\n // Nested selection object\n addFromObject([...prefixPath, key], expression, ops)\n continue\n }\n\n if (isAggregateExpression(expression) || containsAggregate(expression)) {\n // Placeholder for group-by processing later.\n // Both plain aggregates (count(...)) and expressions wrapping\n // aggregates (coalesce(count(...), 0)) are deferred to processGroupBy.\n ops.push({\n kind: `field`,\n alias: [...prefixPath, key].join(`.`),\n compiled: () => null,\n })\n } else {\n if (expression === undefined || !isExpressionLike(expression)) {\n ops.push({\n kind: `field`,\n alias: [...prefixPath, key].join(`.`),\n compiled: () => expression,\n })\n continue\n }\n // If the expression is a Value wrapper, embed the literal to avoid re-compilation mishaps\n if (expression instanceof ValClass) {\n const val = expression.value\n ops.push({\n kind: `field`,\n alias: [...prefixPath, key].join(`.`),\n compiled: () => val,\n })\n } else {\n ops.push({\n kind: `field`,\n alias: [...prefixPath, key].join(`.`),\n compiled: compileExpression(expression as BasicExpression),\n })\n }\n }\n }\n}\n"],"names":["ValClass","map","isExpressionLike","PropRef","compileExpression","containsAggregate"],"mappings":"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"}
@@ -1,7 +1,8 @@
1
1
  export { BaseQueryBuilder, Query, type InitialQueryBuilder, type QueryBuilder, type Context, type ContextSchema, type Source, type GetResult, type InferResultType, type ExtractContext, type QueryResult, type SchemaFromSource, type InferCollectionType, type MergeContextWithJoinType, type MergeContextForJoinCallback, type ApplyJoinOptionalityToMergedSchema, type ResultTypeFromSelect, type WithResult, type JoinOnCallback, type RefsForContext, type WhereCallback, type OrderByCallback, type GroupByCallback, type SelectObject, type FunctionalHavingRow, type Prettify, } from './builder/index.js';
2
- export { eq, gt, gte, lt, lte, and, or, not, inArray, like, ilike, isUndefined, isNull, upper, lower, length, concat, coalesce, add, count, avg, sum, min, max, } from './builder/functions.js';
2
+ export { eq, gt, gte, lt, lte, and, or, not, inArray, like, ilike, isUndefined, isNull, upper, lower, length, concat, coalesce, add, count, avg, sum, min, max, toArray, } from './builder/functions.js';
3
3
  export type { Ref } from './builder/types.js';
4
4
  export { compileQuery } from './compiler/index.js';
5
+ export { compileExpression, compileSingleRowExpression, toBooleanPredicate, } from './compiler/evaluators.js';
5
6
  export { createLiveQueryCollection, liveQueryCollectionOptions, } from './live-query-collection.js';
6
7
  export { queryOnce, type QueryOnceConfig } from './query-once.js';
7
8
  export { type LiveQueryCollectionConfig } from './live/types.js';
@@ -1,5 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
+ const INCLUDES_SCALAR_FIELD = `__includes_scalar__`;
3
4
  class BaseExpression {
4
5
  }
5
6
  class CollectionRef extends BaseExpression {
@@ -48,8 +49,22 @@ class Aggregate extends BaseExpression {
48
49
  this.type = `agg`;
49
50
  }
50
51
  }
52
+ class IncludesSubquery extends BaseExpression {
53
+ constructor(query, correlationField, childCorrelationField, fieldName, parentFilters, parentProjection, materialization = `collection`, scalarField) {
54
+ super();
55
+ this.query = query;
56
+ this.correlationField = correlationField;
57
+ this.childCorrelationField = childCorrelationField;
58
+ this.fieldName = fieldName;
59
+ this.parentFilters = parentFilters;
60
+ this.parentProjection = parentProjection;
61
+ this.materialization = materialization;
62
+ this.scalarField = scalarField;
63
+ this.type = `includesSubquery`;
64
+ }
65
+ }
51
66
  function isExpressionLike(value) {
52
- return value instanceof Aggregate || value instanceof Func || value instanceof PropRef || value instanceof Value;
67
+ return value instanceof Aggregate || value instanceof Func || value instanceof PropRef || value instanceof Value || value instanceof IncludesSubquery;
53
68
  }
54
69
  function getWhereExpression(where) {
55
70
  return typeof where === `object` && `expression` in where ? where.expression : where;
@@ -103,6 +118,8 @@ function followRef(query, ref, collection) {
103
118
  exports.Aggregate = Aggregate;
104
119
  exports.CollectionRef = CollectionRef;
105
120
  exports.Func = Func;
121
+ exports.INCLUDES_SCALAR_FIELD = INCLUDES_SCALAR_FIELD;
122
+ exports.IncludesSubquery = IncludesSubquery;
106
123
  exports.PropRef = PropRef;
107
124
  exports.QueryRef = QueryRef;
108
125
  exports.Value = Value;