@tanstack/db 0.5.11 → 0.5.12
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/SortedMap.cjs +40 -26
- package/dist/cjs/SortedMap.cjs.map +1 -1
- package/dist/cjs/SortedMap.d.cts +10 -15
- package/dist/cjs/collection/change-events.cjs.map +1 -1
- package/dist/cjs/collection/changes.cjs.map +1 -1
- package/dist/cjs/collection/events.cjs.map +1 -1
- package/dist/cjs/collection/events.d.cts +12 -4
- package/dist/cjs/collection/index.cjs +2 -1
- package/dist/cjs/collection/index.cjs.map +1 -1
- package/dist/cjs/collection/indexes.cjs.map +1 -1
- package/dist/cjs/collection/lifecycle.cjs.map +1 -1
- package/dist/cjs/collection/mutations.cjs +5 -2
- package/dist/cjs/collection/mutations.cjs.map +1 -1
- package/dist/cjs/collection/state.cjs +6 -5
- package/dist/cjs/collection/state.cjs.map +1 -1
- package/dist/cjs/collection/state.d.cts +4 -1
- package/dist/cjs/collection/subscription.cjs +60 -53
- package/dist/cjs/collection/subscription.cjs.map +1 -1
- package/dist/cjs/collection/subscription.d.cts +18 -4
- package/dist/cjs/collection/sync.cjs.map +1 -1
- package/dist/cjs/errors.cjs +9 -0
- package/dist/cjs/errors.cjs.map +1 -1
- package/dist/cjs/errors.d.cts +3 -0
- package/dist/cjs/event-emitter.cjs.map +1 -1
- package/dist/cjs/index.cjs +2 -0
- package/dist/cjs/index.cjs.map +1 -1
- package/dist/cjs/index.d.cts +1 -1
- package/dist/cjs/indexes/auto-index.cjs.map +1 -1
- package/dist/cjs/indexes/base-index.cjs.map +1 -1
- package/dist/cjs/indexes/btree-index.cjs +8 -6
- package/dist/cjs/indexes/btree-index.cjs.map +1 -1
- package/dist/cjs/indexes/lazy-index.cjs.map +1 -1
- package/dist/cjs/indexes/reverse-index.cjs.map +1 -1
- package/dist/cjs/local-only.cjs.map +1 -1
- package/dist/cjs/local-storage.cjs.map +1 -1
- package/dist/cjs/optimistic-action.cjs.map +1 -1
- package/dist/cjs/paced-mutations.cjs.map +1 -1
- package/dist/cjs/proxy.cjs.map +1 -1
- package/dist/cjs/query/builder/functions.cjs.map +1 -1
- package/dist/cjs/query/builder/index.cjs.map +1 -1
- package/dist/cjs/query/builder/ref-proxy.cjs.map +1 -1
- package/dist/cjs/query/compiler/evaluators.cjs.map +1 -1
- package/dist/cjs/query/compiler/expressions.cjs.map +1 -1
- package/dist/cjs/query/compiler/group-by.cjs.map +1 -1
- package/dist/cjs/query/compiler/index.cjs.map +1 -1
- package/dist/cjs/query/compiler/joins.cjs.map +1 -1
- package/dist/cjs/query/compiler/order-by.cjs +91 -38
- package/dist/cjs/query/compiler/order-by.cjs.map +1 -1
- package/dist/cjs/query/compiler/order-by.d.cts +6 -2
- package/dist/cjs/query/compiler/select.cjs.map +1 -1
- package/dist/cjs/query/expression-helpers.cjs.map +1 -1
- package/dist/cjs/query/index.d.cts +1 -1
- package/dist/cjs/query/ir.cjs.map +1 -1
- package/dist/cjs/query/live/collection-config-builder.cjs.map +1 -1
- package/dist/cjs/query/live/collection-registry.cjs.map +1 -1
- package/dist/cjs/query/live/collection-subscriber.cjs +30 -15
- package/dist/cjs/query/live/collection-subscriber.cjs.map +1 -1
- package/dist/cjs/query/live/internal.cjs.map +1 -1
- package/dist/cjs/query/live-query-collection.cjs.map +1 -1
- package/dist/cjs/query/optimizer.cjs.map +1 -1
- package/dist/cjs/query/predicate-utils.cjs +19 -2
- package/dist/cjs/query/predicate-utils.cjs.map +1 -1
- package/dist/cjs/query/predicate-utils.d.cts +32 -1
- package/dist/cjs/query/subset-dedupe.cjs.map +1 -1
- package/dist/cjs/scheduler.cjs.map +1 -1
- package/dist/cjs/strategies/debounceStrategy.cjs.map +1 -1
- package/dist/cjs/strategies/queueStrategy.cjs.map +1 -1
- package/dist/cjs/strategies/throttleStrategy.cjs.map +1 -1
- package/dist/cjs/transactions.cjs.map +1 -1
- package/dist/cjs/types.d.cts +43 -5
- package/dist/cjs/utils/browser-polyfills.cjs.map +1 -1
- package/dist/cjs/utils/btree.cjs.map +1 -1
- package/dist/cjs/utils/comparison.cjs.map +1 -1
- package/dist/cjs/utils/cursor.cjs +39 -0
- package/dist/cjs/utils/cursor.cjs.map +1 -0
- package/dist/cjs/utils/cursor.d.cts +18 -0
- package/dist/cjs/utils/index-optimization.cjs.map +1 -1
- package/dist/cjs/utils.cjs.map +1 -1
- package/dist/esm/SortedMap.d.ts +10 -15
- package/dist/esm/SortedMap.js +40 -26
- package/dist/esm/SortedMap.js.map +1 -1
- package/dist/esm/collection/change-events.js.map +1 -1
- package/dist/esm/collection/changes.js.map +1 -1
- package/dist/esm/collection/events.d.ts +12 -4
- package/dist/esm/collection/events.js.map +1 -1
- package/dist/esm/collection/index.js +2 -1
- package/dist/esm/collection/index.js.map +1 -1
- package/dist/esm/collection/indexes.js.map +1 -1
- package/dist/esm/collection/lifecycle.js.map +1 -1
- package/dist/esm/collection/mutations.js +6 -3
- package/dist/esm/collection/mutations.js.map +1 -1
- package/dist/esm/collection/state.d.ts +4 -1
- package/dist/esm/collection/state.js +6 -5
- package/dist/esm/collection/state.js.map +1 -1
- package/dist/esm/collection/subscription.d.ts +18 -4
- package/dist/esm/collection/subscription.js +61 -54
- package/dist/esm/collection/subscription.js.map +1 -1
- package/dist/esm/collection/sync.js.map +1 -1
- package/dist/esm/errors.d.ts +3 -0
- package/dist/esm/errors.js +9 -0
- package/dist/esm/errors.js.map +1 -1
- package/dist/esm/event-emitter.js.map +1 -1
- package/dist/esm/index.d.ts +1 -1
- package/dist/esm/index.js +4 -2
- package/dist/esm/indexes/auto-index.js.map +1 -1
- package/dist/esm/indexes/base-index.js.map +1 -1
- package/dist/esm/indexes/btree-index.js +8 -6
- package/dist/esm/indexes/btree-index.js.map +1 -1
- package/dist/esm/indexes/lazy-index.js.map +1 -1
- package/dist/esm/indexes/reverse-index.js.map +1 -1
- package/dist/esm/local-only.js.map +1 -1
- package/dist/esm/local-storage.js.map +1 -1
- package/dist/esm/optimistic-action.js.map +1 -1
- package/dist/esm/paced-mutations.js.map +1 -1
- package/dist/esm/proxy.js.map +1 -1
- package/dist/esm/query/builder/functions.js.map +1 -1
- package/dist/esm/query/builder/index.js.map +1 -1
- package/dist/esm/query/builder/ref-proxy.js.map +1 -1
- package/dist/esm/query/compiler/evaluators.js.map +1 -1
- package/dist/esm/query/compiler/expressions.js.map +1 -1
- package/dist/esm/query/compiler/group-by.js.map +1 -1
- package/dist/esm/query/compiler/index.js.map +1 -1
- package/dist/esm/query/compiler/joins.js.map +1 -1
- package/dist/esm/query/compiler/order-by.d.ts +6 -2
- package/dist/esm/query/compiler/order-by.js +91 -38
- package/dist/esm/query/compiler/order-by.js.map +1 -1
- package/dist/esm/query/compiler/select.js.map +1 -1
- package/dist/esm/query/expression-helpers.js.map +1 -1
- package/dist/esm/query/index.d.ts +1 -1
- package/dist/esm/query/ir.js.map +1 -1
- package/dist/esm/query/live/collection-config-builder.js.map +1 -1
- package/dist/esm/query/live/collection-registry.js.map +1 -1
- package/dist/esm/query/live/collection-subscriber.js +30 -15
- package/dist/esm/query/live/collection-subscriber.js.map +1 -1
- package/dist/esm/query/live/internal.js.map +1 -1
- package/dist/esm/query/live-query-collection.js.map +1 -1
- package/dist/esm/query/optimizer.js.map +1 -1
- package/dist/esm/query/predicate-utils.d.ts +32 -1
- package/dist/esm/query/predicate-utils.js +19 -2
- package/dist/esm/query/predicate-utils.js.map +1 -1
- package/dist/esm/query/subset-dedupe.js.map +1 -1
- package/dist/esm/scheduler.js.map +1 -1
- package/dist/esm/strategies/debounceStrategy.js.map +1 -1
- package/dist/esm/strategies/queueStrategy.js.map +1 -1
- package/dist/esm/strategies/throttleStrategy.js.map +1 -1
- package/dist/esm/transactions.js.map +1 -1
- package/dist/esm/types.d.ts +43 -5
- package/dist/esm/utils/browser-polyfills.js.map +1 -1
- package/dist/esm/utils/btree.js.map +1 -1
- package/dist/esm/utils/comparison.js.map +1 -1
- package/dist/esm/utils/cursor.d.ts +18 -0
- package/dist/esm/utils/cursor.js +39 -0
- package/dist/esm/utils/cursor.js.map +1 -0
- package/dist/esm/utils/index-optimization.js.map +1 -1
- package/dist/esm/utils.js.map +1 -1
- package/package.json +30 -28
- package/src/SortedMap.ts +50 -31
- package/src/collection/change-events.ts +20 -20
- package/src/collection/changes.ts +12 -12
- package/src/collection/events.ts +20 -10
- package/src/collection/index.ts +47 -46
- package/src/collection/indexes.ts +14 -14
- package/src/collection/lifecycle.ts +16 -16
- package/src/collection/mutations.ts +25 -20
- package/src/collection/state.ts +43 -36
- package/src/collection/subscription.ts +114 -83
- package/src/collection/sync.ts +13 -13
- package/src/duplicate-instance-check.ts +1 -1
- package/src/errors.ts +49 -40
- package/src/event-emitter.ts +5 -5
- package/src/index.ts +21 -21
- package/src/indexes/auto-index.ts +11 -11
- package/src/indexes/base-index.ts +13 -13
- package/src/indexes/btree-index.ts +21 -17
- package/src/indexes/index-options.ts +3 -3
- package/src/indexes/lazy-index.ts +8 -8
- package/src/indexes/reverse-index.ts +5 -5
- package/src/local-only.ts +12 -12
- package/src/local-storage.ts +17 -17
- package/src/optimistic-action.ts +5 -5
- package/src/paced-mutations.ts +6 -6
- package/src/proxy.ts +43 -43
- package/src/query/builder/functions.ts +28 -28
- package/src/query/builder/index.ts +22 -22
- package/src/query/builder/ref-proxy.ts +4 -4
- package/src/query/builder/types.ts +8 -8
- package/src/query/compiler/evaluators.ts +9 -9
- package/src/query/compiler/expressions.ts +6 -6
- package/src/query/compiler/group-by.ts +24 -24
- package/src/query/compiler/index.ts +44 -44
- package/src/query/compiler/joins.ts +37 -37
- package/src/query/compiler/order-by.ts +170 -77
- package/src/query/compiler/select.ts +13 -13
- package/src/query/compiler/types.ts +2 -2
- package/src/query/expression-helpers.ts +16 -16
- package/src/query/index.ts +10 -9
- package/src/query/ir.ts +13 -13
- package/src/query/live/collection-config-builder.ts +53 -53
- package/src/query/live/collection-registry.ts +6 -6
- package/src/query/live/collection-subscriber.ts +87 -48
- package/src/query/live/internal.ts +1 -1
- package/src/query/live/types.ts +4 -4
- package/src/query/live-query-collection.ts +15 -15
- package/src/query/optimizer.ts +29 -29
- package/src/query/predicate-utils.ts +105 -50
- package/src/query/subset-dedupe.ts +6 -6
- package/src/scheduler.ts +3 -3
- package/src/strategies/debounceStrategy.ts +6 -6
- package/src/strategies/index.ts +4 -4
- package/src/strategies/queueStrategy.ts +5 -5
- package/src/strategies/throttleStrategy.ts +6 -6
- package/src/strategies/types.ts +2 -2
- package/src/transactions.ts +9 -9
- package/src/types.ts +51 -12
- package/src/utils/array-utils.ts +1 -1
- package/src/utils/browser-polyfills.ts +2 -2
- package/src/utils/btree.ts +22 -22
- package/src/utils/comparison.ts +3 -3
- package/src/utils/cursor.ts +78 -0
- package/src/utils/index-optimization.ts +14 -14
- package/src/utils.ts +4 -4
|
@@ -1,22 +1,22 @@
|
|
|
1
|
-
import { orderByWithFractionalIndex } from
|
|
2
|
-
import { defaultComparator, makeComparator } from
|
|
3
|
-
import { PropRef, followRef } from
|
|
4
|
-
import { ensureIndexForField } from
|
|
5
|
-
import { findIndexForField } from
|
|
6
|
-
import { compileExpression } from
|
|
7
|
-
import { replaceAggregatesByRefs } from
|
|
8
|
-
import type { CompareOptions } from
|
|
9
|
-
import type { WindowOptions } from
|
|
10
|
-
import type { CompiledSingleRowExpression } from
|
|
11
|
-
import type { OrderBy, OrderByClause, QueryIR, Select } from
|
|
1
|
+
import { orderByWithFractionalIndex } from '@tanstack/db-ivm'
|
|
2
|
+
import { defaultComparator, makeComparator } from '../../utils/comparison.js'
|
|
3
|
+
import { PropRef, followRef } from '../ir.js'
|
|
4
|
+
import { ensureIndexForField } from '../../indexes/auto-index.js'
|
|
5
|
+
import { findIndexForField } from '../../utils/index-optimization.js'
|
|
6
|
+
import { compileExpression } from './evaluators.js'
|
|
7
|
+
import { replaceAggregatesByRefs } from './group-by.js'
|
|
8
|
+
import type { CompareOptions } from '../builder/types.js'
|
|
9
|
+
import type { WindowOptions } from './types.js'
|
|
10
|
+
import type { CompiledSingleRowExpression } from './evaluators.js'
|
|
11
|
+
import type { OrderBy, OrderByClause, QueryIR, Select } from '../ir.js'
|
|
12
12
|
import type {
|
|
13
13
|
CollectionLike,
|
|
14
14
|
NamespacedAndKeyedStream,
|
|
15
15
|
NamespacedRow,
|
|
16
|
-
} from
|
|
17
|
-
import type { IStreamBuilder, KeyValue } from
|
|
18
|
-
import type { IndexInterface } from
|
|
19
|
-
import type { Collection } from
|
|
16
|
+
} from '../../types.js'
|
|
17
|
+
import type { IStreamBuilder, KeyValue } from '@tanstack/db-ivm'
|
|
18
|
+
import type { IndexInterface } from '../../indexes/base-index.js'
|
|
19
|
+
import type { Collection } from '../../collection/index.js'
|
|
20
20
|
|
|
21
21
|
export type OrderByOptimizationInfo = {
|
|
22
22
|
alias: string
|
|
@@ -25,10 +25,14 @@ export type OrderByOptimizationInfo = {
|
|
|
25
25
|
limit: number
|
|
26
26
|
comparator: (
|
|
27
27
|
a: Record<string, unknown> | null | undefined,
|
|
28
|
-
b: Record<string, unknown> | null | undefined
|
|
28
|
+
b: Record<string, unknown> | null | undefined,
|
|
29
29
|
) => number
|
|
30
|
-
|
|
31
|
-
|
|
30
|
+
/** Extracts all orderBy column values from a raw row (array for multi-column) */
|
|
31
|
+
valueExtractorForRawRow: (row: Record<string, unknown>) => unknown
|
|
32
|
+
/** Extracts only the first column value - used for index-based cursor */
|
|
33
|
+
firstColumnValueExtractor: (row: Record<string, unknown>) => unknown
|
|
34
|
+
/** Index on the first orderBy column - used for lazy loading */
|
|
35
|
+
index?: IndexInterface<string | number>
|
|
32
36
|
dataNeeded?: () => number
|
|
33
37
|
}
|
|
34
38
|
|
|
@@ -46,14 +50,14 @@ export function processOrderBy(
|
|
|
46
50
|
optimizableOrderByCollections: Record<string, OrderByOptimizationInfo>,
|
|
47
51
|
setWindowFn: (windowFn: (options: WindowOptions) => void) => void,
|
|
48
52
|
limit?: number,
|
|
49
|
-
offset?: number
|
|
53
|
+
offset?: number,
|
|
50
54
|
): IStreamBuilder<KeyValue<unknown, [NamespacedRow, string]>> {
|
|
51
55
|
// Pre-compile all order by expressions
|
|
52
56
|
const compiledOrderBy = orderByClause.map((clause) => {
|
|
53
57
|
const clauseWithoutAggregates = replaceAggregatesByRefs(
|
|
54
58
|
clause.expression,
|
|
55
59
|
selectClause,
|
|
56
|
-
`__select_results
|
|
60
|
+
`__select_results`,
|
|
57
61
|
)
|
|
58
62
|
|
|
59
63
|
return {
|
|
@@ -74,7 +78,7 @@ export function processOrderBy(
|
|
|
74
78
|
if (orderByClause.length > 1) {
|
|
75
79
|
// For multiple orderBy columns, create a composite key
|
|
76
80
|
return compiledOrderBy.map((compiled) =>
|
|
77
|
-
compiled.compiledExpression(orderByContext)
|
|
81
|
+
compiled.compiledExpression(orderByContext),
|
|
78
82
|
)
|
|
79
83
|
} else if (orderByClause.length === 1) {
|
|
80
84
|
// For a single orderBy column, use the value directly
|
|
@@ -117,76 +121,165 @@ export function processOrderBy(
|
|
|
117
121
|
|
|
118
122
|
let orderByOptimizationInfo: OrderByOptimizationInfo | undefined
|
|
119
123
|
|
|
120
|
-
//
|
|
121
|
-
//
|
|
122
|
-
//
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
124
|
+
// When there's a limit, we create orderByOptimizationInfo to pass orderBy/limit
|
|
125
|
+
// to loadSubset so the sync layer can optimize the query.
|
|
126
|
+
// We try to use an index on the FIRST orderBy column for lazy loading,
|
|
127
|
+
// even for multi-column orderBy (using wider bounds on first column).
|
|
128
|
+
if (limit) {
|
|
129
|
+
let index: IndexInterface<string | number> | undefined
|
|
130
|
+
let followRefCollection: Collection | undefined
|
|
131
|
+
let firstColumnValueExtractor: CompiledSingleRowExpression | undefined
|
|
132
|
+
let orderByAlias: string = rawQuery.from.alias
|
|
126
133
|
|
|
127
|
-
|
|
134
|
+
// Try to create/find an index on the FIRST orderBy column for lazy loading
|
|
135
|
+
const firstClause = orderByClause[0]!
|
|
136
|
+
const firstOrderByExpression = firstClause.expression
|
|
137
|
+
|
|
138
|
+
if (firstOrderByExpression.type === `ref`) {
|
|
128
139
|
const followRefResult = followRef(
|
|
129
140
|
rawQuery,
|
|
130
|
-
|
|
131
|
-
collection
|
|
132
|
-
)
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
fieldName,
|
|
140
|
-
followRefResult.path,
|
|
141
|
+
firstOrderByExpression,
|
|
142
|
+
collection,
|
|
143
|
+
)
|
|
144
|
+
|
|
145
|
+
if (followRefResult) {
|
|
146
|
+
followRefCollection = followRefResult.collection
|
|
147
|
+
const fieldName = followRefResult.path[0]
|
|
148
|
+
const compareOpts = buildCompareOptions(
|
|
149
|
+
firstClause,
|
|
141
150
|
followRefCollection,
|
|
142
|
-
compareOpts,
|
|
143
|
-
compare
|
|
144
151
|
)
|
|
145
|
-
}
|
|
146
152
|
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
153
|
+
if (fieldName) {
|
|
154
|
+
ensureIndexForField(
|
|
155
|
+
fieldName,
|
|
156
|
+
followRefResult.path,
|
|
157
|
+
followRefCollection,
|
|
158
|
+
compareOpts,
|
|
159
|
+
compare,
|
|
160
|
+
)
|
|
161
|
+
}
|
|
151
162
|
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
const extractedB = b ? valueExtractorForRawRow(b) : b
|
|
158
|
-
return compare(extractedA, extractedB)
|
|
159
|
-
}
|
|
163
|
+
// First column value extractor - used for index cursor
|
|
164
|
+
firstColumnValueExtractor = compileExpression(
|
|
165
|
+
new PropRef(followRefResult.path),
|
|
166
|
+
true,
|
|
167
|
+
) as CompiledSingleRowExpression
|
|
160
168
|
|
|
161
|
-
|
|
162
|
-
findIndexForField(
|
|
169
|
+
index = findIndexForField(
|
|
163
170
|
followRefCollection,
|
|
164
171
|
followRefResult.path,
|
|
165
|
-
compareOpts
|
|
172
|
+
compareOpts,
|
|
166
173
|
)
|
|
167
174
|
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
175
|
+
// Only use the index if it supports range queries
|
|
176
|
+
if (!index?.supports(`gt`)) {
|
|
177
|
+
index = undefined
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
orderByAlias =
|
|
181
|
+
firstOrderByExpression.path.length > 1
|
|
182
|
+
? String(firstOrderByExpression.path[0])
|
|
173
183
|
: rawQuery.from.alias
|
|
184
|
+
}
|
|
185
|
+
}
|
|
174
186
|
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
187
|
+
// Only create comparator and value extractors if the first column is a ref expression
|
|
188
|
+
// For aggregate or computed expressions, we can't extract values from raw collection rows
|
|
189
|
+
if (!firstColumnValueExtractor) {
|
|
190
|
+
// Skip optimization for non-ref expressions (aggregates, computed values, etc.)
|
|
191
|
+
// The query will still work, but without lazy loading optimization
|
|
192
|
+
} else {
|
|
193
|
+
// Build value extractors for all columns (must all be ref expressions for multi-column)
|
|
194
|
+
// Check if all orderBy expressions are ref types (required for multi-column extraction)
|
|
195
|
+
const allColumnsAreRefs = orderByClause.every(
|
|
196
|
+
(clause) => clause.expression.type === `ref`,
|
|
197
|
+
)
|
|
198
|
+
|
|
199
|
+
// Create extractors for all columns if they're all refs
|
|
200
|
+
const allColumnExtractors:
|
|
201
|
+
| Array<CompiledSingleRowExpression>
|
|
202
|
+
| undefined = allColumnsAreRefs
|
|
203
|
+
? orderByClause.map((clause) => {
|
|
204
|
+
// We know it's a ref since we checked allColumnsAreRefs
|
|
205
|
+
const refExpr = clause.expression as PropRef
|
|
206
|
+
const followResult = followRef(rawQuery, refExpr, collection)
|
|
207
|
+
if (followResult) {
|
|
208
|
+
return compileExpression(
|
|
209
|
+
new PropRef(followResult.path),
|
|
210
|
+
true,
|
|
211
|
+
) as CompiledSingleRowExpression
|
|
212
|
+
}
|
|
213
|
+
// Fallback for refs that don't follow
|
|
214
|
+
return compileExpression(
|
|
215
|
+
clause.expression,
|
|
216
|
+
true,
|
|
217
|
+
) as CompiledSingleRowExpression
|
|
218
|
+
})
|
|
219
|
+
: undefined
|
|
220
|
+
|
|
221
|
+
// Create a comparator for raw rows (used for tracking sent values)
|
|
222
|
+
// This compares ALL orderBy columns for proper ordering
|
|
223
|
+
const comparator = (
|
|
224
|
+
a: Record<string, unknown> | null | undefined,
|
|
225
|
+
b: Record<string, unknown> | null | undefined,
|
|
226
|
+
) => {
|
|
227
|
+
if (orderByClause.length === 1) {
|
|
228
|
+
// Single column: extract and compare
|
|
229
|
+
const extractedA = a ? firstColumnValueExtractor(a) : a
|
|
230
|
+
const extractedB = b ? firstColumnValueExtractor(b) : b
|
|
231
|
+
return compare(extractedA, extractedB)
|
|
232
|
+
}
|
|
233
|
+
if (allColumnExtractors) {
|
|
234
|
+
// Multi-column with all refs: extract all values and compare
|
|
235
|
+
const extractAll = (
|
|
236
|
+
row: Record<string, unknown> | null | undefined,
|
|
237
|
+
) => {
|
|
238
|
+
if (!row) return row
|
|
239
|
+
return allColumnExtractors.map((extractor) => extractor(row))
|
|
240
|
+
}
|
|
241
|
+
return compare(extractAll(a), extractAll(b))
|
|
183
242
|
}
|
|
243
|
+
// Fallback: can't compare (shouldn't happen since we skip non-ref cases)
|
|
244
|
+
return 0
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
// Create a value extractor for raw rows that extracts ALL orderBy column values
|
|
248
|
+
// This is used for tracking sent values and building composite cursors
|
|
249
|
+
const rawRowValueExtractor = (row: Record<string, unknown>): unknown => {
|
|
250
|
+
if (orderByClause.length === 1) {
|
|
251
|
+
// Single column: return single value
|
|
252
|
+
return firstColumnValueExtractor(row)
|
|
253
|
+
}
|
|
254
|
+
if (allColumnExtractors) {
|
|
255
|
+
// Multi-column: return array of all values
|
|
256
|
+
return allColumnExtractors.map((extractor) => extractor(row))
|
|
257
|
+
}
|
|
258
|
+
// Fallback (shouldn't happen)
|
|
259
|
+
return undefined
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
orderByOptimizationInfo = {
|
|
263
|
+
alias: orderByAlias,
|
|
264
|
+
offset: offset ?? 0,
|
|
265
|
+
limit,
|
|
266
|
+
comparator,
|
|
267
|
+
valueExtractorForRawRow: rawRowValueExtractor,
|
|
268
|
+
firstColumnValueExtractor: firstColumnValueExtractor,
|
|
269
|
+
index,
|
|
270
|
+
orderBy: orderByClause,
|
|
271
|
+
}
|
|
184
272
|
|
|
185
|
-
|
|
186
|
-
|
|
273
|
+
// Store the optimization info keyed by collection ID
|
|
274
|
+
// Use the followed collection if available, otherwise use the main collection
|
|
275
|
+
const targetCollectionId = followRefCollection?.id ?? collection.id
|
|
276
|
+
optimizableOrderByCollections[targetCollectionId] =
|
|
277
|
+
orderByOptimizationInfo
|
|
187
278
|
|
|
279
|
+
// Set up lazy loading callback if we have an index
|
|
280
|
+
if (index) {
|
|
188
281
|
setSizeCallback = (getSize: () => number) => {
|
|
189
|
-
optimizableOrderByCollections[
|
|
282
|
+
optimizableOrderByCollections[targetCollectionId]![`dataNeeded`] =
|
|
190
283
|
() => {
|
|
191
284
|
const size = getSize()
|
|
192
285
|
return Math.max(0, orderByOptimizationInfo!.limit - size)
|
|
@@ -204,7 +297,7 @@ export function processOrderBy(
|
|
|
204
297
|
comparator: compare,
|
|
205
298
|
setSizeCallback,
|
|
206
299
|
setWindowFn: (
|
|
207
|
-
windowFn: (options: { offset?: number; limit?: number }) => void
|
|
300
|
+
windowFn: (options: { offset?: number; limit?: number }) => void,
|
|
208
301
|
) => {
|
|
209
302
|
setWindowFn(
|
|
210
303
|
// We wrap the move function such that we update the orderByOptimizationInfo
|
|
@@ -217,10 +310,10 @@ export function processOrderBy(
|
|
|
217
310
|
orderByOptimizationInfo.limit =
|
|
218
311
|
options.limit ?? orderByOptimizationInfo.limit
|
|
219
312
|
}
|
|
220
|
-
}
|
|
313
|
+
},
|
|
221
314
|
)
|
|
222
315
|
},
|
|
223
|
-
})
|
|
316
|
+
}),
|
|
224
317
|
// orderByWithFractionalIndex returns [key, [value, index]] - we keep this format
|
|
225
318
|
)
|
|
226
319
|
}
|
|
@@ -231,7 +324,7 @@ export function processOrderBy(
|
|
|
231
324
|
*/
|
|
232
325
|
export function buildCompareOptions(
|
|
233
326
|
clause: OrderByClause,
|
|
234
|
-
collection: CollectionLike<any, any
|
|
327
|
+
collection: CollectionLike<any, any>,
|
|
235
328
|
): CompareOptions {
|
|
236
329
|
if (clause.compareOptions.stringSort !== undefined) {
|
|
237
330
|
return clause.compareOptions
|
|
@@ -1,13 +1,13 @@
|
|
|
1
|
-
import { map } from
|
|
2
|
-
import { PropRef, Value as ValClass, isExpressionLike } from
|
|
3
|
-
import { AggregateNotSupportedError } from
|
|
4
|
-
import { compileExpression } from
|
|
5
|
-
import type { Aggregate, BasicExpression, Select } from
|
|
1
|
+
import { map } from '@tanstack/db-ivm'
|
|
2
|
+
import { PropRef, Value as ValClass, isExpressionLike } from '../ir.js'
|
|
3
|
+
import { AggregateNotSupportedError } from '../../errors.js'
|
|
4
|
+
import { compileExpression } from './evaluators.js'
|
|
5
|
+
import type { Aggregate, BasicExpression, Select } from '../ir.js'
|
|
6
6
|
import type {
|
|
7
7
|
KeyedStream,
|
|
8
8
|
NamespacedAndKeyedStream,
|
|
9
9
|
NamespacedRow,
|
|
10
|
-
} from
|
|
10
|
+
} from '../../types.js'
|
|
11
11
|
|
|
12
12
|
/**
|
|
13
13
|
* Type for operations array used in select processing
|
|
@@ -34,7 +34,7 @@ function unwrapVal(input: any): any {
|
|
|
34
34
|
function processMerge(
|
|
35
35
|
op: Extract<SelectOp, { kind: `merge` }>,
|
|
36
36
|
namespacedRow: NamespacedRow,
|
|
37
|
-
selectResults: Record<string, any
|
|
37
|
+
selectResults: Record<string, any>,
|
|
38
38
|
): void {
|
|
39
39
|
const value = op.source(namespacedRow)
|
|
40
40
|
if (value && typeof value === `object`) {
|
|
@@ -74,7 +74,7 @@ function processMerge(
|
|
|
74
74
|
function processNonMergeOp(
|
|
75
75
|
op: Extract<SelectOp, { kind: `field` }>,
|
|
76
76
|
namespacedRow: NamespacedRow,
|
|
77
|
-
selectResults: Record<string, any
|
|
77
|
+
selectResults: Record<string, any>,
|
|
78
78
|
): void {
|
|
79
79
|
// Support nested alias paths like "meta.author.name"
|
|
80
80
|
const path = op.alias.split(`.`)
|
|
@@ -99,7 +99,7 @@ function processNonMergeOp(
|
|
|
99
99
|
*/
|
|
100
100
|
function processRow(
|
|
101
101
|
[key, namespacedRow]: [unknown, NamespacedRow],
|
|
102
|
-
ops: Array<SelectOp
|
|
102
|
+
ops: Array<SelectOp>,
|
|
103
103
|
): [unknown, typeof namespacedRow & { __select_results: any }] {
|
|
104
104
|
const selectResults: Record<string, any> = {}
|
|
105
105
|
|
|
@@ -131,7 +131,7 @@ function processRow(
|
|
|
131
131
|
export function processSelect(
|
|
132
132
|
pipeline: NamespacedAndKeyedStream,
|
|
133
133
|
select: Select,
|
|
134
|
-
_allInputs: Record<string, KeyedStream
|
|
134
|
+
_allInputs: Record<string, KeyedStream>,
|
|
135
135
|
): NamespacedAndKeyedStream {
|
|
136
136
|
// Build ordered operations to preserve authoring order (spreads and fields)
|
|
137
137
|
const ops: Array<SelectOp> = []
|
|
@@ -145,7 +145,7 @@ export function processSelect(
|
|
|
145
145
|
* Helper function to check if an expression is an aggregate
|
|
146
146
|
*/
|
|
147
147
|
function isAggregateExpression(
|
|
148
|
-
expr: BasicExpression | Aggregate
|
|
148
|
+
expr: BasicExpression | Aggregate,
|
|
149
149
|
): expr is Aggregate {
|
|
150
150
|
return expr.type === `agg`
|
|
151
151
|
}
|
|
@@ -155,7 +155,7 @@ function isAggregateExpression(
|
|
|
155
155
|
*/
|
|
156
156
|
export function processArgument(
|
|
157
157
|
arg: BasicExpression | Aggregate,
|
|
158
|
-
namespacedRow: NamespacedRow
|
|
158
|
+
namespacedRow: NamespacedRow,
|
|
159
159
|
): any {
|
|
160
160
|
if (isAggregateExpression(arg)) {
|
|
161
161
|
throw new AggregateNotSupportedError()
|
|
@@ -189,7 +189,7 @@ function isNestedSelectObject(obj: any): boolean {
|
|
|
189
189
|
function addFromObject(
|
|
190
190
|
prefixPath: Array<string>,
|
|
191
191
|
obj: any,
|
|
192
|
-
ops: Array<SelectOp
|
|
192
|
+
ops: Array<SelectOp>,
|
|
193
193
|
) {
|
|
194
194
|
for (const [key, value] of Object.entries(obj)) {
|
|
195
195
|
if (key.startsWith(`__SPREAD_SENTINEL__`)) {
|
|
@@ -27,7 +27,7 @@
|
|
|
27
27
|
* ```
|
|
28
28
|
*/
|
|
29
29
|
|
|
30
|
-
import type { IR, OperatorName } from
|
|
30
|
+
import type { IR, OperatorName } from '../index.js'
|
|
31
31
|
|
|
32
32
|
type BasicExpression<T = any> = IR.BasicExpression<T>
|
|
33
33
|
type OrderBy = IR.OrderBy
|
|
@@ -149,7 +149,7 @@ export function extractValue(expr: BasicExpression): any {
|
|
|
149
149
|
*/
|
|
150
150
|
export function walkExpression(
|
|
151
151
|
expr: BasicExpression | undefined | null,
|
|
152
|
-
visitor: (node: BasicExpression) => void
|
|
152
|
+
visitor: (node: BasicExpression) => void,
|
|
153
153
|
): void {
|
|
154
154
|
if (!expr) return
|
|
155
155
|
|
|
@@ -200,7 +200,7 @@ export function walkExpression(
|
|
|
200
200
|
*/
|
|
201
201
|
export function parseWhereExpression<T = any>(
|
|
202
202
|
expr: BasicExpression<boolean> | undefined | null,
|
|
203
|
-
options: ParseWhereOptions<T
|
|
203
|
+
options: ParseWhereOptions<T>,
|
|
204
204
|
): T | null {
|
|
205
205
|
if (!expr) return null
|
|
206
206
|
|
|
@@ -226,7 +226,7 @@ export function parseWhereExpression<T = any>(
|
|
|
226
226
|
return onUnknownOperator(name, args)
|
|
227
227
|
}
|
|
228
228
|
throw new Error(
|
|
229
|
-
`No handler provided for operator: ${name}. Available handlers: ${Object.keys(handlers).join(`, `)}
|
|
229
|
+
`No handler provided for operator: ${name}. Available handlers: ${Object.keys(handlers).join(`, `)}`,
|
|
230
230
|
)
|
|
231
231
|
}
|
|
232
232
|
|
|
@@ -263,7 +263,7 @@ export function parseWhereExpression<T = any>(
|
|
|
263
263
|
* ```
|
|
264
264
|
*/
|
|
265
265
|
export function parseOrderByExpression(
|
|
266
|
-
orderBy: OrderBy | undefined | null
|
|
266
|
+
orderBy: OrderBy | undefined | null,
|
|
267
267
|
): Array<ParsedOrderBy> {
|
|
268
268
|
if (!orderBy || orderBy.length === 0) {
|
|
269
269
|
return []
|
|
@@ -274,7 +274,7 @@ export function parseOrderByExpression(
|
|
|
274
274
|
|
|
275
275
|
if (!field) {
|
|
276
276
|
throw new Error(
|
|
277
|
-
`ORDER BY expression must be a field reference, got: ${clause.expression.type}
|
|
277
|
+
`ORDER BY expression must be a field reference, got: ${clause.expression.type}`,
|
|
278
278
|
)
|
|
279
279
|
}
|
|
280
280
|
|
|
@@ -325,7 +325,7 @@ export function parseOrderByExpression(
|
|
|
325
325
|
* ```
|
|
326
326
|
*/
|
|
327
327
|
export function extractSimpleComparisons(
|
|
328
|
-
expr: BasicExpression<boolean> | undefined | null
|
|
328
|
+
expr: BasicExpression<boolean> | undefined | null,
|
|
329
329
|
): Array<SimpleComparison> {
|
|
330
330
|
if (!expr) return []
|
|
331
331
|
|
|
@@ -344,7 +344,7 @@ export function extractSimpleComparisons(
|
|
|
344
344
|
const [arg] = e.args
|
|
345
345
|
if (!arg || arg.type !== `func`) {
|
|
346
346
|
throw new Error(
|
|
347
|
-
`extractSimpleComparisons requires a comparison or null check inside 'not' operator
|
|
347
|
+
`extractSimpleComparisons requires a comparison or null check inside 'not' operator.`,
|
|
348
348
|
)
|
|
349
349
|
}
|
|
350
350
|
|
|
@@ -362,7 +362,7 @@ export function extractSimpleComparisons(
|
|
|
362
362
|
})
|
|
363
363
|
} else {
|
|
364
364
|
throw new Error(
|
|
365
|
-
`extractSimpleComparisons requires a field reference for '${arg.name}' operator
|
|
365
|
+
`extractSimpleComparisons requires a field reference for '${arg.name}' operator.`,
|
|
366
366
|
)
|
|
367
367
|
}
|
|
368
368
|
return
|
|
@@ -383,7 +383,7 @@ export function extractSimpleComparisons(
|
|
|
383
383
|
})
|
|
384
384
|
} else {
|
|
385
385
|
throw new Error(
|
|
386
|
-
`extractSimpleComparisons requires simple field-value comparisons. Found complex expression for 'not(${arg.name})' operator
|
|
386
|
+
`extractSimpleComparisons requires simple field-value comparisons. Found complex expression for 'not(${arg.name})' operator.`,
|
|
387
387
|
)
|
|
388
388
|
}
|
|
389
389
|
return
|
|
@@ -391,7 +391,7 @@ export function extractSimpleComparisons(
|
|
|
391
391
|
|
|
392
392
|
// NOT can only wrap simple comparisons or null checks
|
|
393
393
|
throw new Error(
|
|
394
|
-
`extractSimpleComparisons does not support 'not(${arg.name})'. NOT can only wrap comparison operators (eq, gt, gte, lt, lte, in) or null checks (isNull, isUndefined)
|
|
394
|
+
`extractSimpleComparisons does not support 'not(${arg.name})'. NOT can only wrap comparison operators (eq, gt, gte, lt, lte, in) or null checks (isNull, isUndefined).`,
|
|
395
395
|
)
|
|
396
396
|
}
|
|
397
397
|
|
|
@@ -414,7 +414,7 @@ export function extractSimpleComparisons(
|
|
|
414
414
|
]
|
|
415
415
|
if (unsupportedOps.includes(e.name)) {
|
|
416
416
|
throw new Error(
|
|
417
|
-
`extractSimpleComparisons does not support '${e.name}' operator. Use parseWhereExpression with custom handlers for complex expressions
|
|
417
|
+
`extractSimpleComparisons does not support '${e.name}' operator. Use parseWhereExpression with custom handlers for complex expressions.`,
|
|
418
418
|
)
|
|
419
419
|
}
|
|
420
420
|
|
|
@@ -434,7 +434,7 @@ export function extractSimpleComparisons(
|
|
|
434
434
|
})
|
|
435
435
|
} else {
|
|
436
436
|
throw new Error(
|
|
437
|
-
`extractSimpleComparisons requires a field reference for '${e.name}' operator
|
|
437
|
+
`extractSimpleComparisons requires a field reference for '${e.name}' operator.`,
|
|
438
438
|
)
|
|
439
439
|
}
|
|
440
440
|
return
|
|
@@ -457,13 +457,13 @@ export function extractSimpleComparisons(
|
|
|
457
457
|
})
|
|
458
458
|
} else {
|
|
459
459
|
throw new Error(
|
|
460
|
-
`extractSimpleComparisons requires simple field-value comparisons. Found complex expression for '${e.name}' operator
|
|
460
|
+
`extractSimpleComparisons requires simple field-value comparisons. Found complex expression for '${e.name}' operator.`,
|
|
461
461
|
)
|
|
462
462
|
}
|
|
463
463
|
} else {
|
|
464
464
|
// Unknown operator
|
|
465
465
|
throw new Error(
|
|
466
|
-
`extractSimpleComparisons encountered unknown operator: '${e.name}'
|
|
466
|
+
`extractSimpleComparisons encountered unknown operator: '${e.name}'`,
|
|
467
467
|
)
|
|
468
468
|
}
|
|
469
469
|
}
|
|
@@ -504,7 +504,7 @@ export function parseLoadSubsetOptions(
|
|
|
504
504
|
limit?: number
|
|
505
505
|
}
|
|
506
506
|
| undefined
|
|
507
|
-
| null
|
|
507
|
+
| null,
|
|
508
508
|
): {
|
|
509
509
|
filters: Array<SimpleComparison>
|
|
510
510
|
sorts: Array<ParsedOrderBy>
|
package/src/query/index.ts
CHANGED
|
@@ -10,7 +10,7 @@ export {
|
|
|
10
10
|
type Source,
|
|
11
11
|
type GetResult,
|
|
12
12
|
type InferResultType,
|
|
13
|
-
} from
|
|
13
|
+
} from './builder/index.js'
|
|
14
14
|
|
|
15
15
|
// Expression functions exports
|
|
16
16
|
export {
|
|
@@ -41,22 +41,22 @@ export {
|
|
|
41
41
|
sum,
|
|
42
42
|
min,
|
|
43
43
|
max,
|
|
44
|
-
} from
|
|
44
|
+
} from './builder/functions.js'
|
|
45
45
|
|
|
46
46
|
// Ref proxy utilities
|
|
47
|
-
export type { Ref } from
|
|
47
|
+
export type { Ref } from './builder/types.js'
|
|
48
48
|
|
|
49
49
|
// Compiler
|
|
50
|
-
export { compileQuery } from
|
|
50
|
+
export { compileQuery } from './compiler/index.js'
|
|
51
51
|
|
|
52
52
|
// Live query collection utilities
|
|
53
53
|
export {
|
|
54
54
|
createLiveQueryCollection,
|
|
55
55
|
liveQueryCollectionOptions,
|
|
56
|
-
} from
|
|
56
|
+
} from './live-query-collection.js'
|
|
57
57
|
|
|
58
|
-
export { type LiveQueryCollectionConfig } from
|
|
59
|
-
export { type LiveQueryCollectionUtils } from
|
|
58
|
+
export { type LiveQueryCollectionConfig } from './live/types.js'
|
|
59
|
+
export { type LiveQueryCollectionUtils } from './live/collection-config-builder.js'
|
|
60
60
|
|
|
61
61
|
// Predicate utilities for predicate push-down
|
|
62
62
|
export {
|
|
@@ -65,7 +65,8 @@ export {
|
|
|
65
65
|
minusWherePredicates,
|
|
66
66
|
isOrderBySubset,
|
|
67
67
|
isLimitSubset,
|
|
68
|
+
isOffsetLimitSubset,
|
|
68
69
|
isPredicateSubset,
|
|
69
|
-
} from
|
|
70
|
+
} from './predicate-utils.js'
|
|
70
71
|
|
|
71
|
-
export { DeduplicatedLoadSubset } from
|
|
72
|
+
export { DeduplicatedLoadSubset } from './subset-dedupe.js'
|