@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.
- package/dist/cjs/collection/change-events.cjs.map +1 -1
- package/dist/cjs/collection/change-events.d.cts +3 -2
- package/dist/cjs/collection/changes.cjs +13 -4
- package/dist/cjs/collection/changes.cjs.map +1 -1
- package/dist/cjs/collection/changes.d.cts +10 -1
- package/dist/cjs/collection/cleanup-queue.cjs +89 -0
- package/dist/cjs/collection/cleanup-queue.cjs.map +1 -0
- package/dist/cjs/collection/cleanup-queue.d.cts +30 -0
- package/dist/cjs/collection/events.cjs +14 -0
- package/dist/cjs/collection/events.cjs.map +1 -1
- package/dist/cjs/collection/events.d.cts +39 -1
- package/dist/cjs/collection/index.cjs +66 -28
- package/dist/cjs/collection/index.cjs.map +1 -1
- package/dist/cjs/collection/index.d.cts +49 -36
- package/dist/cjs/collection/indexes.cjs +211 -62
- package/dist/cjs/collection/indexes.cjs.map +1 -1
- package/dist/cjs/collection/indexes.d.cts +27 -17
- package/dist/cjs/collection/lifecycle.cjs +5 -22
- package/dist/cjs/collection/lifecycle.cjs.map +1 -1
- package/dist/cjs/collection/lifecycle.d.cts +0 -1
- package/dist/cjs/collection/mutations.cjs +18 -0
- package/dist/cjs/collection/mutations.cjs.map +1 -1
- package/dist/cjs/collection/mutations.d.cts +1 -0
- package/dist/cjs/collection/state.cjs +381 -53
- package/dist/cjs/collection/state.cjs.map +1 -1
- package/dist/cjs/collection/state.d.cts +65 -1
- package/dist/cjs/collection/subscription.cjs +6 -0
- package/dist/cjs/collection/subscription.cjs.map +1 -1
- package/dist/cjs/collection/subscription.d.cts +4 -0
- package/dist/cjs/collection/sync.cjs +108 -1
- package/dist/cjs/collection/sync.cjs.map +1 -1
- package/dist/cjs/collection/sync.d.cts +2 -0
- package/dist/cjs/collection/transaction-metadata.cjs +5 -0
- package/dist/cjs/collection/transaction-metadata.cjs.map +1 -0
- package/dist/cjs/collection/transaction-metadata.d.cts +1 -0
- package/dist/cjs/errors.cjs +8 -0
- package/dist/cjs/errors.cjs.map +1 -1
- package/dist/cjs/errors.d.cts +3 -0
- package/dist/cjs/index.cjs +22 -4
- package/dist/cjs/index.cjs.map +1 -1
- package/dist/cjs/index.d.cts +11 -3
- package/dist/cjs/indexes/auto-index.cjs +13 -6
- package/dist/cjs/indexes/auto-index.cjs.map +1 -1
- package/dist/cjs/indexes/base-index.cjs +0 -3
- package/dist/cjs/indexes/base-index.cjs.map +1 -1
- package/dist/cjs/indexes/base-index.d.cts +2 -6
- package/dist/cjs/indexes/basic-index.cjs +361 -0
- package/dist/cjs/indexes/basic-index.cjs.map +1 -0
- package/dist/cjs/indexes/basic-index.d.cts +102 -0
- package/dist/cjs/indexes/btree-index.cjs.map +1 -1
- package/dist/cjs/indexes/btree-index.d.cts +1 -1
- package/dist/cjs/indexes/index-options.d.cts +8 -9
- package/dist/cjs/indexes/index-registry.cjs +89 -0
- package/dist/cjs/indexes/index-registry.cjs.map +1 -0
- package/dist/cjs/indexes/index-registry.d.cts +61 -0
- package/dist/cjs/local-only.cjs +5 -0
- package/dist/cjs/local-only.cjs.map +1 -1
- package/dist/cjs/query/builder/functions.cjs +27 -11
- package/dist/cjs/query/builder/functions.cjs.map +1 -1
- package/dist/cjs/query/builder/functions.d.cts +25 -3
- package/dist/cjs/query/builder/index.cjs +200 -39
- package/dist/cjs/query/builder/index.cjs.map +1 -1
- package/dist/cjs/query/builder/index.d.cts +4 -3
- package/dist/cjs/query/builder/ref-proxy.cjs.map +1 -1
- package/dist/cjs/query/builder/ref-proxy.d.cts +14 -3
- package/dist/cjs/query/builder/types.d.cts +84 -19
- package/dist/cjs/query/compiler/evaluators.cjs +51 -0
- package/dist/cjs/query/compiler/evaluators.cjs.map +1 -1
- package/dist/cjs/query/compiler/group-by.cjs +100 -28
- package/dist/cjs/query/compiler/group-by.cjs.map +1 -1
- package/dist/cjs/query/compiler/group-by.d.cts +4 -2
- package/dist/cjs/query/compiler/index.cjs +283 -11
- package/dist/cjs/query/compiler/index.cjs.map +1 -1
- package/dist/cjs/query/compiler/index.d.cts +30 -2
- package/dist/cjs/query/compiler/order-by.cjs +29 -10
- package/dist/cjs/query/compiler/order-by.cjs.map +1 -1
- package/dist/cjs/query/compiler/order-by.d.cts +1 -1
- package/dist/cjs/query/compiler/select.cjs +8 -0
- package/dist/cjs/query/compiler/select.cjs.map +1 -1
- package/dist/cjs/query/index.d.cts +2 -1
- package/dist/cjs/query/ir.cjs +18 -1
- package/dist/cjs/query/ir.cjs.map +1 -1
- package/dist/cjs/query/ir.d.cts +21 -1
- package/dist/cjs/query/live/collection-config-builder.cjs +501 -5
- package/dist/cjs/query/live/collection-config-builder.cjs.map +1 -1
- package/dist/cjs/query/live/collection-config-builder.d.cts +7 -0
- package/dist/cjs/query/live/types.d.cts +3 -3
- package/dist/cjs/query/live/utils.cjs +43 -3
- package/dist/cjs/query/live/utils.cjs.map +1 -1
- package/dist/cjs/query/live/utils.d.cts +1 -0
- package/dist/cjs/query/live-query-collection.cjs.map +1 -1
- package/dist/cjs/query/live-query-collection.d.cts +9 -6
- package/dist/cjs/query/query-once.cjs.map +1 -1
- package/dist/cjs/query/query-once.d.cts +7 -5
- package/dist/cjs/query/subset-dedupe.cjs +9 -3
- package/dist/cjs/query/subset-dedupe.cjs.map +1 -1
- package/dist/cjs/types.d.cts +42 -8
- package/dist/cjs/utils/array-utils.cjs +27 -0
- package/dist/cjs/utils/array-utils.cjs.map +1 -0
- package/dist/cjs/utils/array-utils.d.cts +16 -0
- package/dist/cjs/utils/comparison.cjs +11 -0
- package/dist/cjs/utils/comparison.cjs.map +1 -1
- package/dist/cjs/utils/index-optimization.cjs +4 -0
- package/dist/cjs/utils/index-optimization.cjs.map +1 -1
- package/dist/cjs/utils.cjs +7 -9
- package/dist/cjs/utils.cjs.map +1 -1
- package/dist/cjs/utils.d.cts +6 -1
- package/dist/cjs/virtual-props.cjs +33 -0
- package/dist/cjs/virtual-props.cjs.map +1 -0
- package/dist/cjs/virtual-props.d.cts +196 -0
- package/dist/esm/collection/change-events.d.ts +3 -2
- package/dist/esm/collection/change-events.js.map +1 -1
- package/dist/esm/collection/changes.d.ts +10 -1
- package/dist/esm/collection/changes.js +13 -4
- package/dist/esm/collection/changes.js.map +1 -1
- package/dist/esm/collection/cleanup-queue.d.ts +30 -0
- package/dist/esm/collection/cleanup-queue.js +89 -0
- package/dist/esm/collection/cleanup-queue.js.map +1 -0
- package/dist/esm/collection/events.d.ts +39 -1
- package/dist/esm/collection/events.js +14 -0
- package/dist/esm/collection/events.js.map +1 -1
- package/dist/esm/collection/index.d.ts +49 -36
- package/dist/esm/collection/index.js +67 -29
- package/dist/esm/collection/index.js.map +1 -1
- package/dist/esm/collection/indexes.d.ts +27 -17
- package/dist/esm/collection/indexes.js +211 -62
- package/dist/esm/collection/indexes.js.map +1 -1
- package/dist/esm/collection/lifecycle.d.ts +0 -1
- package/dist/esm/collection/lifecycle.js +5 -22
- package/dist/esm/collection/lifecycle.js.map +1 -1
- package/dist/esm/collection/mutations.d.ts +1 -0
- package/dist/esm/collection/mutations.js +18 -0
- package/dist/esm/collection/mutations.js.map +1 -1
- package/dist/esm/collection/state.d.ts +65 -1
- package/dist/esm/collection/state.js +381 -53
- package/dist/esm/collection/state.js.map +1 -1
- package/dist/esm/collection/subscription.d.ts +4 -0
- package/dist/esm/collection/subscription.js +6 -0
- package/dist/esm/collection/subscription.js.map +1 -1
- package/dist/esm/collection/sync.d.ts +2 -0
- package/dist/esm/collection/sync.js +108 -1
- package/dist/esm/collection/sync.js.map +1 -1
- package/dist/esm/collection/transaction-metadata.d.ts +1 -0
- package/dist/esm/collection/transaction-metadata.js +5 -0
- package/dist/esm/collection/transaction-metadata.js.map +1 -0
- package/dist/esm/errors.d.ts +3 -0
- package/dist/esm/errors.js +8 -0
- package/dist/esm/errors.js.map +1 -1
- package/dist/esm/index.d.ts +11 -3
- package/dist/esm/index.js +25 -7
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/indexes/auto-index.js +13 -6
- package/dist/esm/indexes/auto-index.js.map +1 -1
- package/dist/esm/indexes/base-index.d.ts +2 -6
- package/dist/esm/indexes/base-index.js +1 -4
- package/dist/esm/indexes/base-index.js.map +1 -1
- package/dist/esm/indexes/basic-index.d.ts +102 -0
- package/dist/esm/indexes/basic-index.js +361 -0
- package/dist/esm/indexes/basic-index.js.map +1 -0
- package/dist/esm/indexes/btree-index.d.ts +1 -1
- package/dist/esm/indexes/btree-index.js.map +1 -1
- package/dist/esm/indexes/index-options.d.ts +8 -9
- package/dist/esm/indexes/index-registry.d.ts +61 -0
- package/dist/esm/indexes/index-registry.js +89 -0
- package/dist/esm/indexes/index-registry.js.map +1 -0
- package/dist/esm/local-only.js +5 -0
- package/dist/esm/local-only.js.map +1 -1
- package/dist/esm/query/builder/functions.d.ts +25 -3
- package/dist/esm/query/builder/functions.js +27 -11
- package/dist/esm/query/builder/functions.js.map +1 -1
- package/dist/esm/query/builder/index.d.ts +4 -3
- package/dist/esm/query/builder/index.js +201 -40
- package/dist/esm/query/builder/index.js.map +1 -1
- package/dist/esm/query/builder/ref-proxy.d.ts +14 -3
- package/dist/esm/query/builder/ref-proxy.js.map +1 -1
- package/dist/esm/query/builder/types.d.ts +84 -19
- package/dist/esm/query/compiler/evaluators.js +51 -0
- package/dist/esm/query/compiler/evaluators.js.map +1 -1
- package/dist/esm/query/compiler/group-by.d.ts +4 -2
- package/dist/esm/query/compiler/group-by.js +101 -29
- package/dist/esm/query/compiler/group-by.js.map +1 -1
- package/dist/esm/query/compiler/index.d.ts +30 -2
- package/dist/esm/query/compiler/index.js +285 -13
- package/dist/esm/query/compiler/index.js.map +1 -1
- package/dist/esm/query/compiler/order-by.d.ts +1 -1
- package/dist/esm/query/compiler/order-by.js +30 -11
- package/dist/esm/query/compiler/order-by.js.map +1 -1
- package/dist/esm/query/compiler/select.js +8 -0
- package/dist/esm/query/compiler/select.js.map +1 -1
- package/dist/esm/query/index.d.ts +2 -1
- package/dist/esm/query/ir.d.ts +21 -1
- package/dist/esm/query/ir.js +18 -1
- package/dist/esm/query/ir.js.map +1 -1
- package/dist/esm/query/live/collection-config-builder.d.ts +7 -0
- package/dist/esm/query/live/collection-config-builder.js +503 -7
- package/dist/esm/query/live/collection-config-builder.js.map +1 -1
- package/dist/esm/query/live/types.d.ts +3 -3
- package/dist/esm/query/live/utils.d.ts +1 -0
- package/dist/esm/query/live/utils.js +43 -3
- package/dist/esm/query/live/utils.js.map +1 -1
- package/dist/esm/query/live-query-collection.d.ts +9 -6
- package/dist/esm/query/live-query-collection.js.map +1 -1
- package/dist/esm/query/query-once.d.ts +7 -5
- package/dist/esm/query/query-once.js.map +1 -1
- package/dist/esm/query/subset-dedupe.js +9 -3
- package/dist/esm/query/subset-dedupe.js.map +1 -1
- package/dist/esm/types.d.ts +42 -8
- package/dist/esm/utils/array-utils.d.ts +16 -0
- package/dist/esm/utils/array-utils.js +27 -0
- package/dist/esm/utils/array-utils.js.map +1 -0
- package/dist/esm/utils/comparison.js +11 -0
- package/dist/esm/utils/comparison.js.map +1 -1
- package/dist/esm/utils/index-optimization.js +4 -0
- package/dist/esm/utils/index-optimization.js.map +1 -1
- package/dist/esm/utils.d.ts +6 -1
- package/dist/esm/utils.js +7 -9
- package/dist/esm/utils.js.map +1 -1
- package/dist/esm/virtual-props.d.ts +196 -0
- package/dist/esm/virtual-props.js +33 -0
- package/dist/esm/virtual-props.js.map +1 -0
- package/package.json +2 -2
- package/skills/db-core/collection-setup/references/electric-adapter.md +1 -1
- package/src/collection/change-events.ts +13 -9
- package/src/collection/changes.ts +30 -7
- package/src/collection/cleanup-queue.ts +105 -0
- package/src/collection/events.ts +65 -0
- package/src/collection/index.ts +110 -45
- package/src/collection/indexes.ts +283 -76
- package/src/collection/lifecycle.ts +5 -26
- package/src/collection/mutations.ts +21 -0
- package/src/collection/state.ts +545 -71
- package/src/collection/subscription.ts +7 -0
- package/src/collection/sync.ts +137 -0
- package/src/collection/transaction-metadata.ts +1 -0
- package/src/errors.ts +9 -0
- package/src/index.ts +46 -3
- package/src/indexes/auto-index.ts +18 -8
- package/src/indexes/base-index.ts +2 -10
- package/src/indexes/basic-index.ts +507 -0
- package/src/indexes/btree-index.ts +1 -1
- package/src/indexes/index-options.ts +17 -37
- package/src/indexes/index-registry.ts +174 -0
- package/src/local-only.ts +7 -0
- package/src/query/builder/functions.ts +84 -7
- package/src/query/builder/index.ts +329 -9
- package/src/query/builder/ref-proxy.ts +22 -4
- package/src/query/builder/types.ts +257 -62
- package/src/query/compiler/evaluators.ts +57 -0
- package/src/query/compiler/group-by.ts +156 -35
- package/src/query/compiler/index.ts +445 -15
- package/src/query/compiler/order-by.ts +51 -12
- package/src/query/compiler/select.ts +9 -0
- package/src/query/index.ts +7 -0
- package/src/query/ir.ts +23 -2
- package/src/query/live/collection-config-builder.ts +809 -9
- package/src/query/live/types.ts +10 -4
- package/src/query/live/utils.ts +64 -3
- package/src/query/live-query-collection.ts +43 -18
- package/src/query/query-once.ts +31 -12
- package/src/query/subset-dedupe.ts +11 -7
- package/src/types.ts +49 -9
- package/src/utils/array-utils.ts +49 -0
- package/src/utils/comparison.ts +14 -0
- package/src/utils/index-optimization.ts +4 -0
- package/src/utils.ts +12 -9
- package/src/virtual-props.ts +282 -0
- package/dist/cjs/indexes/lazy-index.cjs +0 -190
- package/dist/cjs/indexes/lazy-index.cjs.map +0 -1
- package/dist/cjs/indexes/lazy-index.d.cts +0 -96
- package/dist/esm/indexes/lazy-index.d.ts +0 -96
- package/dist/esm/indexes/lazy-index.js +0 -190
- package/dist/esm/indexes/lazy-index.js.map +0 -1
- package/src/indexes/lazy-index.ts +0 -251
|
@@ -21,6 +21,43 @@ import type {
|
|
|
21
21
|
Select,
|
|
22
22
|
} from '../ir.js'
|
|
23
23
|
import type { NamespacedAndKeyedStream, NamespacedRow } from '../../types.js'
|
|
24
|
+
import type { VirtualOrigin } from '../../virtual-props.js'
|
|
25
|
+
|
|
26
|
+
const VIRTUAL_SYNCED_KEY = `__virtual_synced__`
|
|
27
|
+
const VIRTUAL_HAS_LOCAL_KEY = `__virtual_has_local__`
|
|
28
|
+
|
|
29
|
+
type RowVirtualMetadata = {
|
|
30
|
+
synced: boolean
|
|
31
|
+
hasLocal: boolean
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function getRowVirtualMetadata(row: NamespacedRow): RowVirtualMetadata {
|
|
35
|
+
let found = false
|
|
36
|
+
let allSynced = true
|
|
37
|
+
let hasLocal = false
|
|
38
|
+
|
|
39
|
+
for (const [alias, value] of Object.entries(row)) {
|
|
40
|
+
if (alias === `$selected`) continue
|
|
41
|
+
const asRecord = value
|
|
42
|
+
const hasSyncedProp = `$synced` in asRecord
|
|
43
|
+
const hasOriginProp = `$origin` in asRecord
|
|
44
|
+
if (!hasSyncedProp && !hasOriginProp) {
|
|
45
|
+
continue
|
|
46
|
+
}
|
|
47
|
+
found = true
|
|
48
|
+
if (asRecord.$synced === false) {
|
|
49
|
+
allSynced = false
|
|
50
|
+
}
|
|
51
|
+
if (asRecord.$origin === `local`) {
|
|
52
|
+
hasLocal = true
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
return {
|
|
57
|
+
synced: found ? allSynced : true,
|
|
58
|
+
hasLocal,
|
|
59
|
+
}
|
|
60
|
+
}
|
|
24
61
|
|
|
25
62
|
const { sum, count, avg, min, max } = groupByOperators
|
|
26
63
|
|
|
@@ -80,11 +117,40 @@ export function processGroupBy(
|
|
|
80
117
|
havingClauses?: Array<Having>,
|
|
81
118
|
selectClause?: Select,
|
|
82
119
|
fnHavingClauses?: Array<(row: any) => any>,
|
|
120
|
+
aggregateCollectionId?: string,
|
|
121
|
+
mainSource?: string,
|
|
83
122
|
): NamespacedAndKeyedStream {
|
|
123
|
+
const virtualAggregates: Record<string, any> = {
|
|
124
|
+
[VIRTUAL_SYNCED_KEY]: {
|
|
125
|
+
preMap: ([, row]: [string, NamespacedRow]) =>
|
|
126
|
+
getRowVirtualMetadata(row).synced,
|
|
127
|
+
reduce: (values: Array<[boolean, number]>) => {
|
|
128
|
+
for (const [isSynced, multiplicity] of values) {
|
|
129
|
+
if (!isSynced && multiplicity > 0) {
|
|
130
|
+
return false
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
return true
|
|
134
|
+
},
|
|
135
|
+
},
|
|
136
|
+
[VIRTUAL_HAS_LOCAL_KEY]: {
|
|
137
|
+
preMap: ([, row]: [string, NamespacedRow]) =>
|
|
138
|
+
getRowVirtualMetadata(row).hasLocal,
|
|
139
|
+
reduce: (values: Array<[boolean, number]>) => {
|
|
140
|
+
for (const [isLocal, multiplicity] of values) {
|
|
141
|
+
if (isLocal && multiplicity > 0) {
|
|
142
|
+
return true
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
return false
|
|
146
|
+
},
|
|
147
|
+
},
|
|
148
|
+
}
|
|
149
|
+
|
|
84
150
|
// Handle empty GROUP BY (single-group aggregation)
|
|
85
151
|
if (groupByClause.length === 0) {
|
|
86
152
|
// For single-group aggregation, create a single group with all data
|
|
87
|
-
const aggregates: Record<string, any> =
|
|
153
|
+
const aggregates: Record<string, any> = virtualAggregates
|
|
88
154
|
|
|
89
155
|
// Expressions that wrap aggregates (e.g. coalesce(count(...), 0)).
|
|
90
156
|
// Keys are the original SELECT aliases; values are pre-compiled evaluators
|
|
@@ -110,8 +176,15 @@ export function processGroupBy(
|
|
|
110
176
|
}
|
|
111
177
|
}
|
|
112
178
|
|
|
113
|
-
// Use a constant key for single group
|
|
114
|
-
|
|
179
|
+
// Use a constant key for single group.
|
|
180
|
+
// When mainSource is set (includes mode), include __correlationKey so that
|
|
181
|
+
// rows from different parents aggregate separately.
|
|
182
|
+
const keyExtractor = mainSource
|
|
183
|
+
? ([, row]: [string, NamespacedRow]) => ({
|
|
184
|
+
__singleGroup: true,
|
|
185
|
+
__correlationKey: (row as any)?.[mainSource]?.__correlationKey,
|
|
186
|
+
})
|
|
187
|
+
: () => ({ __singleGroup: true })
|
|
115
188
|
|
|
116
189
|
// Apply the groupBy operator with single group
|
|
117
190
|
pipeline = pipeline.pipe(
|
|
@@ -139,14 +212,37 @@ export function processGroupBy(
|
|
|
139
212
|
)
|
|
140
213
|
}
|
|
141
214
|
|
|
142
|
-
// Use a single key for the result and update $selected
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
215
|
+
// Use a single key for the result and update $selected.
|
|
216
|
+
// When in includes mode, restore the namespaced source structure with
|
|
217
|
+
// __correlationKey so output extraction can route results per-parent.
|
|
218
|
+
const correlationKey = mainSource
|
|
219
|
+
? (aggregatedRow as any).__correlationKey
|
|
220
|
+
: undefined
|
|
221
|
+
const resultKey =
|
|
222
|
+
correlationKey !== undefined
|
|
223
|
+
? `single_group_${serializeValue(correlationKey)}`
|
|
224
|
+
: `single_group`
|
|
225
|
+
const resultRow: Record<string, any> = {
|
|
226
|
+
...(aggregatedRow as Record<string, any>),
|
|
227
|
+
$selected: finalResults,
|
|
228
|
+
}
|
|
229
|
+
const groupSynced = (aggregatedRow as Record<string, any>)[
|
|
230
|
+
VIRTUAL_SYNCED_KEY
|
|
231
|
+
]
|
|
232
|
+
const groupHasLocal = (aggregatedRow as Record<string, any>)[
|
|
233
|
+
VIRTUAL_HAS_LOCAL_KEY
|
|
234
|
+
]
|
|
235
|
+
resultRow.$synced = groupSynced ?? true
|
|
236
|
+
resultRow.$origin = (
|
|
237
|
+
groupHasLocal ? `local` : `remote`
|
|
238
|
+
) satisfies VirtualOrigin
|
|
239
|
+
resultRow.$key = resultKey
|
|
240
|
+
resultRow.$collectionId =
|
|
241
|
+
aggregateCollectionId ?? resultRow.$collectionId
|
|
242
|
+
if (mainSource && correlationKey !== undefined) {
|
|
243
|
+
resultRow[mainSource] = { __correlationKey: correlationKey }
|
|
244
|
+
}
|
|
245
|
+
return [resultKey, resultRow] as [unknown, Record<string, any>]
|
|
150
246
|
}),
|
|
151
247
|
)
|
|
152
248
|
|
|
@@ -196,7 +292,9 @@ export function processGroupBy(
|
|
|
196
292
|
compileExpression(e),
|
|
197
293
|
)
|
|
198
294
|
|
|
199
|
-
// Create a key extractor function using simple __key_X format
|
|
295
|
+
// Create a key extractor function using simple __key_X format.
|
|
296
|
+
// When mainSource is set (includes mode), include __correlationKey so that
|
|
297
|
+
// rows from different parents with the same group key aggregate separately.
|
|
200
298
|
const keyExtractor = ([, row]: [
|
|
201
299
|
string,
|
|
202
300
|
NamespacedRow & { $selected?: any },
|
|
@@ -214,11 +312,15 @@ export function processGroupBy(
|
|
|
214
312
|
key[`__key_${i}`] = value
|
|
215
313
|
}
|
|
216
314
|
|
|
315
|
+
if (mainSource) {
|
|
316
|
+
key.__correlationKey = (row as any)?.[mainSource]?.__correlationKey
|
|
317
|
+
}
|
|
318
|
+
|
|
217
319
|
return key
|
|
218
320
|
}
|
|
219
321
|
|
|
220
322
|
// Create aggregate functions for any aggregated columns in the SELECT clause
|
|
221
|
-
const aggregates: Record<string, any> =
|
|
323
|
+
const aggregates: Record<string, any> = virtualAggregates
|
|
222
324
|
const wrappedAggExprs: Record<string, (data: any) => any> = {}
|
|
223
325
|
const aggCounter = { value: 0 }
|
|
224
326
|
|
|
@@ -278,25 +380,44 @@ export function processGroupBy(
|
|
|
278
380
|
}
|
|
279
381
|
}
|
|
280
382
|
|
|
281
|
-
// Generate a simple key for the live collection using group values
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
}
|
|
290
|
-
finalKey = serializeValue(keyParts)
|
|
383
|
+
// Generate a simple key for the live collection using group values.
|
|
384
|
+
// When in includes mode, include the correlation key so that groups
|
|
385
|
+
// from different parents don't collide.
|
|
386
|
+
const correlationKey = mainSource
|
|
387
|
+
? (aggregatedRow as any).__correlationKey
|
|
388
|
+
: undefined
|
|
389
|
+
const keyParts: Array<unknown> = []
|
|
390
|
+
for (let i = 0; i < groupByClause.length; i++) {
|
|
391
|
+
keyParts.push(aggregatedRow[`__key_${i}`])
|
|
291
392
|
}
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
393
|
+
if (correlationKey !== undefined) {
|
|
394
|
+
keyParts.push(correlationKey)
|
|
395
|
+
}
|
|
396
|
+
const finalKey =
|
|
397
|
+
keyParts.length === 1 ? keyParts[0] : serializeValue(keyParts)
|
|
398
|
+
|
|
399
|
+
// When in includes mode, restore the namespaced source structure with
|
|
400
|
+
// __correlationKey so output extraction can route results per-parent.
|
|
401
|
+
const resultRow: Record<string, any> = {
|
|
402
|
+
...(aggregatedRow as Record<string, any>),
|
|
403
|
+
$selected: finalResults,
|
|
404
|
+
}
|
|
405
|
+
const groupSynced = (aggregatedRow as Record<string, any>)[
|
|
406
|
+
VIRTUAL_SYNCED_KEY
|
|
407
|
+
]
|
|
408
|
+
const groupHasLocal = (aggregatedRow as Record<string, any>)[
|
|
409
|
+
VIRTUAL_HAS_LOCAL_KEY
|
|
410
|
+
]
|
|
411
|
+
resultRow.$synced = groupSynced ?? true
|
|
412
|
+
resultRow.$origin = (
|
|
413
|
+
groupHasLocal ? `local` : `remote`
|
|
414
|
+
) satisfies VirtualOrigin
|
|
415
|
+
resultRow.$key = finalKey
|
|
416
|
+
resultRow.$collectionId = aggregateCollectionId ?? resultRow.$collectionId
|
|
417
|
+
if (mainSource && correlationKey !== undefined) {
|
|
418
|
+
resultRow[mainSource] = { __correlationKey: correlationKey }
|
|
419
|
+
}
|
|
420
|
+
return [finalKey, resultRow] as [unknown, Record<string, any>]
|
|
300
421
|
}),
|
|
301
422
|
)
|
|
302
423
|
|
|
@@ -519,7 +640,7 @@ function evaluateWrappedAggregates(
|
|
|
519
640
|
* contain an Aggregate. Safely returns false for nested Select objects.
|
|
520
641
|
*/
|
|
521
642
|
export function containsAggregate(
|
|
522
|
-
expr: BasicExpression | Aggregate | Select,
|
|
643
|
+
expr: BasicExpression | Aggregate | Select | { type: string },
|
|
523
644
|
): boolean {
|
|
524
645
|
if (!isExpressionLike(expr)) {
|
|
525
646
|
return false
|
|
@@ -527,9 +648,9 @@ export function containsAggregate(
|
|
|
527
648
|
if (expr.type === `agg`) {
|
|
528
649
|
return true
|
|
529
650
|
}
|
|
530
|
-
if (expr.type === `func`) {
|
|
531
|
-
return expr.args
|
|
532
|
-
containsAggregate(arg),
|
|
651
|
+
if (expr.type === `func` && `args` in expr) {
|
|
652
|
+
return (expr.args as Array<BasicExpression | Aggregate>).some(
|
|
653
|
+
(arg: BasicExpression | Aggregate) => containsAggregate(arg),
|
|
533
654
|
)
|
|
534
655
|
}
|
|
535
656
|
return false
|