@tanstack/db 0.5.10 → 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 +1 -1
- 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 +4 -0
- package/dist/cjs/index.cjs.map +1 -1
- package/dist/cjs/index.d.cts +2 -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 +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 +2 -1
- package/dist/esm/index.js +6 -2
- package/dist/esm/index.js.map +1 -1
- 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 +23 -21
- 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 -20
- 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
package/src/types.ts
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import type { IStreamBuilder } from
|
|
2
|
-
import type { Collection } from
|
|
3
|
-
import type { StandardSchemaV1 } from
|
|
4
|
-
import type { Transaction } from
|
|
5
|
-
import type { BasicExpression, OrderBy } from
|
|
6
|
-
import type { EventEmitter } from
|
|
1
|
+
import type { IStreamBuilder } from '@tanstack/db-ivm'
|
|
2
|
+
import type { Collection } from './collection/index.js'
|
|
3
|
+
import type { StandardSchemaV1 } from '@standard-schema/spec'
|
|
4
|
+
import type { Transaction } from './transactions'
|
|
5
|
+
import type { BasicExpression, OrderBy } from './query/ir.js'
|
|
6
|
+
import type { EventEmitter } from './event-emitter.js'
|
|
7
7
|
|
|
8
8
|
/**
|
|
9
9
|
* Interface for a collection-like object that provides the necessary methods
|
|
@@ -124,7 +124,7 @@ export type MutationFnParams<T extends object = Record<string, unknown>> = {
|
|
|
124
124
|
}
|
|
125
125
|
|
|
126
126
|
export type MutationFn<T extends object = Record<string, unknown>> = (
|
|
127
|
-
params: MutationFnParams<T
|
|
127
|
+
params: MutationFnParams<T>,
|
|
128
128
|
) => Promise<any>
|
|
129
129
|
|
|
130
130
|
/**
|
|
@@ -238,9 +238,9 @@ export interface SubscriptionUnsubscribedEvent {
|
|
|
238
238
|
* All subscription events
|
|
239
239
|
*/
|
|
240
240
|
export type SubscriptionEvents = {
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
241
|
+
'status:change': SubscriptionStatusChangeEvent
|
|
242
|
+
'status:ready': SubscriptionStatusEvent<`ready`>
|
|
243
|
+
'status:loadingSubset': SubscriptionStatusEvent<`loadingSubset`>
|
|
244
244
|
unsubscribed: SubscriptionUnsubscribedEvent
|
|
245
245
|
}
|
|
246
246
|
|
|
@@ -253,13 +253,52 @@ export interface Subscription extends EventEmitter<SubscriptionEvents> {
|
|
|
253
253
|
readonly status: SubscriptionStatus
|
|
254
254
|
}
|
|
255
255
|
|
|
256
|
+
/**
|
|
257
|
+
* Cursor expressions for pagination, passed separately from the main `where` clause.
|
|
258
|
+
* The sync layer can choose to use cursor-based pagination (combining these with the where)
|
|
259
|
+
* or offset-based pagination (ignoring these and using the `offset` parameter).
|
|
260
|
+
*
|
|
261
|
+
* Neither expression includes the main `where` clause - they are cursor-specific only.
|
|
262
|
+
*/
|
|
263
|
+
export type CursorExpressions = {
|
|
264
|
+
/**
|
|
265
|
+
* Expression for rows greater than (after) the cursor value.
|
|
266
|
+
* For multi-column orderBy, this is a composite cursor using OR of conditions.
|
|
267
|
+
* Example for [col1 ASC, col2 DESC] with values [v1, v2]:
|
|
268
|
+
* or(gt(col1, v1), and(eq(col1, v1), lt(col2, v2)))
|
|
269
|
+
*/
|
|
270
|
+
whereFrom: BasicExpression<boolean>
|
|
271
|
+
/**
|
|
272
|
+
* Expression for rows equal to the current cursor value (first orderBy column only).
|
|
273
|
+
* Used to handle tie-breaking/duplicates at the boundary.
|
|
274
|
+
* Example: eq(col1, v1) or for Dates: and(gte(col1, v1), lt(col1, v1+1ms))
|
|
275
|
+
*/
|
|
276
|
+
whereCurrent: BasicExpression<boolean>
|
|
277
|
+
/**
|
|
278
|
+
* The key of the last item that was loaded.
|
|
279
|
+
* Can be used by sync layers for tracking or deduplication.
|
|
280
|
+
*/
|
|
281
|
+
lastKey?: string | number
|
|
282
|
+
}
|
|
283
|
+
|
|
256
284
|
export type LoadSubsetOptions = {
|
|
257
|
-
/** The where expression to filter the data */
|
|
285
|
+
/** The where expression to filter the data (does NOT include cursor expressions) */
|
|
258
286
|
where?: BasicExpression<boolean>
|
|
259
287
|
/** The order by clause to sort the data */
|
|
260
288
|
orderBy?: OrderBy
|
|
261
289
|
/** The limit of the data to load */
|
|
262
290
|
limit?: number
|
|
291
|
+
/**
|
|
292
|
+
* Cursor expressions for cursor-based pagination.
|
|
293
|
+
* These are separate from `where` - the sync layer should combine them if using cursor-based pagination.
|
|
294
|
+
* Neither expression includes the main `where` clause.
|
|
295
|
+
*/
|
|
296
|
+
cursor?: CursorExpressions
|
|
297
|
+
/**
|
|
298
|
+
* Row offset for offset-based pagination.
|
|
299
|
+
* The sync layer can use this instead of `cursor` if it prefers offset-based pagination.
|
|
300
|
+
*/
|
|
301
|
+
offset?: number
|
|
263
302
|
/**
|
|
264
303
|
* The subscription that triggered the load.
|
|
265
304
|
* Advanced sync implementations can use this for:
|
|
@@ -334,7 +373,7 @@ export interface OptimisticChangeMessage<
|
|
|
334
373
|
* This follows the standard-schema specification: https://github.com/standard-schema/standard-schema
|
|
335
374
|
*/
|
|
336
375
|
export type StandardSchema<T> = StandardSchemaV1 & {
|
|
337
|
-
|
|
376
|
+
'~standard': {
|
|
338
377
|
types?: {
|
|
339
378
|
input: T
|
|
340
379
|
output: T
|
package/src/utils/array-utils.ts
CHANGED
|
@@ -7,7 +7,7 @@ export type IdleCallbackDeadline = {
|
|
|
7
7
|
export type IdleCallbackFunction = (deadline: IdleCallbackDeadline) => void
|
|
8
8
|
|
|
9
9
|
const requestIdleCallbackPolyfill = (
|
|
10
|
-
callback: IdleCallbackFunction
|
|
10
|
+
callback: IdleCallbackFunction,
|
|
11
11
|
): number => {
|
|
12
12
|
// Use a very small timeout for the polyfill to simulate idle time
|
|
13
13
|
const timeout = 0
|
|
@@ -26,7 +26,7 @@ const cancelIdleCallbackPolyfill = (id: number): void => {
|
|
|
26
26
|
|
|
27
27
|
export const safeRequestIdleCallback: (
|
|
28
28
|
callback: IdleCallbackFunction,
|
|
29
|
-
options?: { timeout?: number }
|
|
29
|
+
options?: { timeout?: number },
|
|
30
30
|
) => number =
|
|
31
31
|
typeof window !== `undefined` && `requestIdleCallback` in window
|
|
32
32
|
? (callback, options) =>
|
package/src/utils/btree.ts
CHANGED
|
@@ -117,7 +117,7 @@ export class BTree<K = any, V = any> {
|
|
|
117
117
|
public constructor(
|
|
118
118
|
compare: (a: K, b: K) => number,
|
|
119
119
|
entries?: Array<[K, V]>,
|
|
120
|
-
maxNodeSize?: number
|
|
120
|
+
maxNodeSize?: number,
|
|
121
121
|
) {
|
|
122
122
|
this._maxNodeSize = maxNodeSize! >= 4 ? Math.min(maxNodeSize!, 256) : 32
|
|
123
123
|
this._compare = compare
|
|
@@ -230,7 +230,7 @@ export class BTree<K = any, V = any> {
|
|
|
230
230
|
0,
|
|
231
231
|
(k, _v) => {
|
|
232
232
|
results.push(k)
|
|
233
|
-
}
|
|
233
|
+
},
|
|
234
234
|
)
|
|
235
235
|
return results
|
|
236
236
|
}
|
|
@@ -250,7 +250,7 @@ export class BTree<K = any, V = any> {
|
|
|
250
250
|
key,
|
|
251
251
|
this._compare,
|
|
252
252
|
false,
|
|
253
|
-
reusedArray
|
|
253
|
+
reusedArray,
|
|
254
254
|
)
|
|
255
255
|
}
|
|
256
256
|
|
|
@@ -306,7 +306,7 @@ export class BTree<K = any, V = any> {
|
|
|
306
306
|
high: K,
|
|
307
307
|
includeHigh: boolean,
|
|
308
308
|
onFound?: (k: K, v: V, counter: number) => void,
|
|
309
|
-
initialCounter?: number
|
|
309
|
+
initialCounter?: number,
|
|
310
310
|
): number
|
|
311
311
|
|
|
312
312
|
/**
|
|
@@ -331,7 +331,7 @@ export class BTree<K = any, V = any> {
|
|
|
331
331
|
high: K,
|
|
332
332
|
includeHigh: boolean,
|
|
333
333
|
onFound?: (k: K, v: V, counter: number) => { break?: R } | void,
|
|
334
|
-
initialCounter?: number
|
|
334
|
+
initialCounter?: number,
|
|
335
335
|
): R | number {
|
|
336
336
|
const r = this._root.forRange(
|
|
337
337
|
low,
|
|
@@ -340,7 +340,7 @@ export class BTree<K = any, V = any> {
|
|
|
340
340
|
false,
|
|
341
341
|
this,
|
|
342
342
|
initialCounter || 0,
|
|
343
|
-
onFound
|
|
343
|
+
onFound,
|
|
344
344
|
)
|
|
345
345
|
return typeof r === `number` ? r : r.break!
|
|
346
346
|
}
|
|
@@ -379,7 +379,7 @@ export class BTree<K = any, V = any> {
|
|
|
379
379
|
high: K,
|
|
380
380
|
includeHigh: boolean,
|
|
381
381
|
onFound: (k: K, v: V, counter: number) => EditRangeResult<V, R> | void,
|
|
382
|
-
initialCounter?: number
|
|
382
|
+
initialCounter?: number,
|
|
383
383
|
): R | number {
|
|
384
384
|
let root = this._root
|
|
385
385
|
if (root.isShared) this._root = root = root.clone()
|
|
@@ -391,7 +391,7 @@ export class BTree<K = any, V = any> {
|
|
|
391
391
|
true,
|
|
392
392
|
this,
|
|
393
393
|
initialCounter || 0,
|
|
394
|
-
onFound
|
|
394
|
+
onFound,
|
|
395
395
|
)
|
|
396
396
|
return typeof r === `number` ? r : r.break!
|
|
397
397
|
} finally {
|
|
@@ -500,7 +500,7 @@ class BNode<K, V> {
|
|
|
500
500
|
key: K,
|
|
501
501
|
compare: (a: K, b: K) => number,
|
|
502
502
|
inclusive: boolean,
|
|
503
|
-
reusedArray: [K, V]
|
|
503
|
+
reusedArray: [K, V],
|
|
504
504
|
): [K, V] | undefined {
|
|
505
505
|
const i = this.indexOf(key, -1, compare)
|
|
506
506
|
const indexOrLower = i < 0 ? ~i - 1 : inclusive ? i : i - 1
|
|
@@ -516,7 +516,7 @@ class BNode<K, V> {
|
|
|
516
516
|
key: K,
|
|
517
517
|
compare: (a: K, b: K) => number,
|
|
518
518
|
inclusive: boolean,
|
|
519
|
-
reusedArray: [K, V]
|
|
519
|
+
reusedArray: [K, V],
|
|
520
520
|
): [K, V] | undefined {
|
|
521
521
|
const i = this.indexOf(key, -1, compare)
|
|
522
522
|
const indexOrLower = i < 0 ? ~i : inclusive ? i : i + 1
|
|
@@ -536,7 +536,7 @@ class BNode<K, V> {
|
|
|
536
536
|
key: K,
|
|
537
537
|
value: V,
|
|
538
538
|
overwrite: boolean | undefined,
|
|
539
|
-
tree: BTree<K, V
|
|
539
|
+
tree: BTree<K, V>,
|
|
540
540
|
): boolean | BNode<K, V> {
|
|
541
541
|
let i = this.indexOf(key, -1, tree._compare)
|
|
542
542
|
if (i < 0) {
|
|
@@ -636,7 +636,7 @@ class BNode<K, V> {
|
|
|
636
636
|
editMode: boolean,
|
|
637
637
|
tree: BTree<K, V>,
|
|
638
638
|
count: number,
|
|
639
|
-
onFound?: (k: K, v: V, counter: number) => EditRangeResult<V, R> | void
|
|
639
|
+
onFound?: (k: K, v: V, counter: number) => EditRangeResult<V, R> | void,
|
|
640
640
|
): EditRangeResult<V, R> | number {
|
|
641
641
|
const cmp = tree._compare
|
|
642
642
|
let iLow, iHigh
|
|
@@ -732,7 +732,7 @@ class BNodeInternal<K, V> extends BNode<K, V> {
|
|
|
732
732
|
key: K,
|
|
733
733
|
compare: (a: K, b: K) => number,
|
|
734
734
|
inclusive: boolean,
|
|
735
|
-
reusedArray: [K, V]
|
|
735
|
+
reusedArray: [K, V],
|
|
736
736
|
): [K, V] | undefined {
|
|
737
737
|
const i = this.indexOf(key, 0, compare),
|
|
738
738
|
children = this.children
|
|
@@ -741,7 +741,7 @@ class BNodeInternal<K, V> extends BNode<K, V> {
|
|
|
741
741
|
key,
|
|
742
742
|
compare,
|
|
743
743
|
inclusive,
|
|
744
|
-
reusedArray
|
|
744
|
+
reusedArray,
|
|
745
745
|
)
|
|
746
746
|
if (result === undefined && i > 0) {
|
|
747
747
|
return children[i - 1]!.maxPair(reusedArray)
|
|
@@ -753,7 +753,7 @@ class BNodeInternal<K, V> extends BNode<K, V> {
|
|
|
753
753
|
key: K,
|
|
754
754
|
compare: (a: K, b: K) => number,
|
|
755
755
|
inclusive: boolean,
|
|
756
|
-
reusedArray: [K, V]
|
|
756
|
+
reusedArray: [K, V],
|
|
757
757
|
): [K, V] | undefined {
|
|
758
758
|
const i = this.indexOf(key, 0, compare),
|
|
759
759
|
children = this.children,
|
|
@@ -763,7 +763,7 @@ class BNodeInternal<K, V> extends BNode<K, V> {
|
|
|
763
763
|
key,
|
|
764
764
|
compare,
|
|
765
765
|
inclusive,
|
|
766
|
-
reusedArray
|
|
766
|
+
reusedArray,
|
|
767
767
|
)
|
|
768
768
|
if (result === undefined && i < length - 1) {
|
|
769
769
|
return children[i + 1]!.minPair(reusedArray)
|
|
@@ -778,7 +778,7 @@ class BNodeInternal<K, V> extends BNode<K, V> {
|
|
|
778
778
|
key: K,
|
|
779
779
|
value: V,
|
|
780
780
|
overwrite: boolean | undefined,
|
|
781
|
-
tree: BTree<K, V
|
|
781
|
+
tree: BTree<K, V>,
|
|
782
782
|
): boolean | BNodeInternal<K, V> {
|
|
783
783
|
const c = this.children,
|
|
784
784
|
max = tree._maxNodeSize,
|
|
@@ -854,7 +854,7 @@ class BNodeInternal<K, V> extends BNode<K, V> {
|
|
|
854
854
|
const half = this.children.length >> 1
|
|
855
855
|
return new BNodeInternal<K, V>(
|
|
856
856
|
this.children.splice(half),
|
|
857
|
-
this.keys.splice(half)
|
|
857
|
+
this.keys.splice(half),
|
|
858
858
|
)
|
|
859
859
|
}
|
|
860
860
|
|
|
@@ -887,7 +887,7 @@ class BNodeInternal<K, V> extends BNode<K, V> {
|
|
|
887
887
|
editMode: boolean,
|
|
888
888
|
tree: BTree<K, V>,
|
|
889
889
|
count: number,
|
|
890
|
-
onFound?: (k: K, v: V, counter: number) => EditRangeResult<V, R> | void
|
|
890
|
+
onFound?: (k: K, v: V, counter: number) => EditRangeResult<V, R> | void,
|
|
891
891
|
): EditRangeResult<V, R> | number {
|
|
892
892
|
const cmp = tree._compare
|
|
893
893
|
const keys = this.keys,
|
|
@@ -896,7 +896,7 @@ class BNodeInternal<K, V> extends BNode<K, V> {
|
|
|
896
896
|
i = iLow
|
|
897
897
|
const iHigh = Math.min(
|
|
898
898
|
high === low ? iLow : this.indexOf(high, 0, cmp),
|
|
899
|
-
keys.length - 1
|
|
899
|
+
keys.length - 1,
|
|
900
900
|
)
|
|
901
901
|
if (!editMode) {
|
|
902
902
|
// Simple case
|
|
@@ -908,7 +908,7 @@ class BNodeInternal<K, V> extends BNode<K, V> {
|
|
|
908
908
|
editMode,
|
|
909
909
|
tree,
|
|
910
910
|
count,
|
|
911
|
-
onFound
|
|
911
|
+
onFound,
|
|
912
912
|
)
|
|
913
913
|
if (typeof result !== `number`) return result
|
|
914
914
|
count = result
|
|
@@ -924,7 +924,7 @@ class BNodeInternal<K, V> extends BNode<K, V> {
|
|
|
924
924
|
editMode,
|
|
925
925
|
tree,
|
|
926
926
|
count,
|
|
927
|
-
onFound
|
|
927
|
+
onFound,
|
|
928
928
|
)
|
|
929
929
|
// Note: if children[i] is empty then keys[i]=undefined.
|
|
930
930
|
// This is an invalid state, but it is fixed below.
|
package/src/utils/comparison.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { CompareOptions } from
|
|
1
|
+
import type { CompareOptions } from '../query/builder/types'
|
|
2
2
|
|
|
3
3
|
// WeakMap to store stable IDs for objects
|
|
4
4
|
const objectIds = new WeakMap<object, number>()
|
|
@@ -84,7 +84,7 @@ export const ascComparator = (a: any, b: any, opts: CompareOptions): number => {
|
|
|
84
84
|
export const descComparator = (
|
|
85
85
|
a: unknown,
|
|
86
86
|
b: unknown,
|
|
87
|
-
opts: CompareOptions
|
|
87
|
+
opts: CompareOptions,
|
|
88
88
|
): number => {
|
|
89
89
|
return ascComparator(b, a, {
|
|
90
90
|
...opts,
|
|
@@ -93,7 +93,7 @@ export const descComparator = (
|
|
|
93
93
|
}
|
|
94
94
|
|
|
95
95
|
export function makeComparator(
|
|
96
|
-
opts: CompareOptions
|
|
96
|
+
opts: CompareOptions,
|
|
97
97
|
): (a: any, b: any) => number {
|
|
98
98
|
return (a, b) => {
|
|
99
99
|
if (opts.direction === `asc`) {
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { and, eq, gt, lt, or } from '../query/builder/functions.js'
|
|
2
|
+
import { Value } from '../query/ir.js'
|
|
3
|
+
import type { BasicExpression, OrderBy } from '../query/ir.js'
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Builds a cursor expression for paginating through ordered results.
|
|
7
|
+
* For multi-column orderBy, creates a composite cursor that respects all columns.
|
|
8
|
+
*
|
|
9
|
+
* For [col1 ASC, col2 DESC] with values [v1, v2], produces:
|
|
10
|
+
* or(
|
|
11
|
+
* gt(col1, v1), // col1 > v1
|
|
12
|
+
* and(eq(col1, v1), lt(col2, v2)) // col1 = v1 AND col2 < v2 (DESC)
|
|
13
|
+
* )
|
|
14
|
+
*
|
|
15
|
+
* This creates a precise cursor that works with composite indexes on the backend.
|
|
16
|
+
*
|
|
17
|
+
* @param orderBy - The order-by clauses defining sort columns and directions
|
|
18
|
+
* @param values - The cursor values corresponding to each order-by column
|
|
19
|
+
* @returns A filter expression for rows after the cursor position, or undefined if empty
|
|
20
|
+
*/
|
|
21
|
+
export function buildCursor(
|
|
22
|
+
orderBy: OrderBy,
|
|
23
|
+
values: Array<unknown>,
|
|
24
|
+
): BasicExpression<boolean> | undefined {
|
|
25
|
+
if (values.length === 0 || orderBy.length === 0) {
|
|
26
|
+
return undefined
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// For single column, just use simple gt/lt
|
|
30
|
+
if (orderBy.length === 1) {
|
|
31
|
+
const { expression, compareOptions } = orderBy[0]!
|
|
32
|
+
const operator = compareOptions.direction === `asc` ? gt : lt
|
|
33
|
+
return operator(expression, new Value(values[0]))
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// For multi-column, build the composite cursor:
|
|
37
|
+
// or(
|
|
38
|
+
// gt(col1, v1),
|
|
39
|
+
// and(eq(col1, v1), gt(col2, v2)),
|
|
40
|
+
// and(eq(col1, v1), eq(col2, v2), gt(col3, v3)),
|
|
41
|
+
// ...
|
|
42
|
+
// )
|
|
43
|
+
const clauses: Array<BasicExpression<boolean>> = []
|
|
44
|
+
|
|
45
|
+
for (let i = 0; i < orderBy.length && i < values.length; i++) {
|
|
46
|
+
const clause = orderBy[i]!
|
|
47
|
+
const value = values[i]
|
|
48
|
+
|
|
49
|
+
// Build equality conditions for all previous columns
|
|
50
|
+
const eqConditions: Array<BasicExpression<boolean>> = []
|
|
51
|
+
for (let j = 0; j < i; j++) {
|
|
52
|
+
const prevClause = orderBy[j]!
|
|
53
|
+
const prevValue = values[j]
|
|
54
|
+
eqConditions.push(eq(prevClause.expression, new Value(prevValue)))
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// Add the comparison for the current column (respecting direction)
|
|
58
|
+
const operator = clause.compareOptions.direction === `asc` ? gt : lt
|
|
59
|
+
const comparison = operator(clause.expression, new Value(value))
|
|
60
|
+
|
|
61
|
+
if (eqConditions.length === 0) {
|
|
62
|
+
// First column: just the comparison
|
|
63
|
+
clauses.push(comparison)
|
|
64
|
+
} else {
|
|
65
|
+
// Subsequent columns: and(eq(prev...), comparison)
|
|
66
|
+
// We need to spread into and() which expects at least 2 args
|
|
67
|
+
const allConditions = [...eqConditions, comparison]
|
|
68
|
+
clauses.push(allConditions.reduce((acc, cond) => and(acc, cond)))
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// Combine all clauses with OR
|
|
73
|
+
if (clauses.length === 1) {
|
|
74
|
+
return clauses[0]!
|
|
75
|
+
}
|
|
76
|
+
// Use reduce to combine with or() which expects exactly 2 args
|
|
77
|
+
return clauses.reduce((acc, clause) => or(acc, clause))
|
|
78
|
+
}
|
|
@@ -15,12 +15,12 @@
|
|
|
15
15
|
* - Optimizes IN array expressions
|
|
16
16
|
*/
|
|
17
17
|
|
|
18
|
-
import { DEFAULT_COMPARE_OPTIONS } from
|
|
19
|
-
import { ReverseIndex } from
|
|
20
|
-
import type { CompareOptions } from
|
|
21
|
-
import type { IndexInterface, IndexOperation } from
|
|
22
|
-
import type { BasicExpression } from
|
|
23
|
-
import type { CollectionLike } from
|
|
18
|
+
import { DEFAULT_COMPARE_OPTIONS } from '../utils.js'
|
|
19
|
+
import { ReverseIndex } from '../indexes/reverse-index.js'
|
|
20
|
+
import type { CompareOptions } from '../query/builder/types.js'
|
|
21
|
+
import type { IndexInterface, IndexOperation } from '../indexes/base-index.js'
|
|
22
|
+
import type { BasicExpression } from '../query/ir.js'
|
|
23
|
+
import type { CollectionLike } from '../types.js'
|
|
24
24
|
|
|
25
25
|
/**
|
|
26
26
|
* Result of index-based query optimization
|
|
@@ -36,7 +36,7 @@ export interface OptimizationResult<TKey> {
|
|
|
36
36
|
export function findIndexForField<TKey extends string | number>(
|
|
37
37
|
collection: CollectionLike<any, TKey>,
|
|
38
38
|
fieldPath: Array<string>,
|
|
39
|
-
compareOptions?: CompareOptions
|
|
39
|
+
compareOptions?: CompareOptions,
|
|
40
40
|
): IndexInterface<TKey> | undefined {
|
|
41
41
|
const compareOpts = compareOptions ?? {
|
|
42
42
|
...DEFAULT_COMPARE_OPTIONS,
|
|
@@ -98,7 +98,7 @@ export function optimizeExpressionWithIndexes<
|
|
|
98
98
|
TKey extends string | number,
|
|
99
99
|
>(
|
|
100
100
|
expression: BasicExpression,
|
|
101
|
-
collection: CollectionLike<T, TKey
|
|
101
|
+
collection: CollectionLike<T, TKey>,
|
|
102
102
|
): OptimizationResult<TKey> {
|
|
103
103
|
return optimizeQueryRecursive(expression, collection)
|
|
104
104
|
}
|
|
@@ -108,7 +108,7 @@ export function optimizeExpressionWithIndexes<
|
|
|
108
108
|
*/
|
|
109
109
|
function optimizeQueryRecursive<T extends object, TKey extends string | number>(
|
|
110
110
|
expression: BasicExpression,
|
|
111
|
-
collection: CollectionLike<T, TKey
|
|
111
|
+
collection: CollectionLike<T, TKey>,
|
|
112
112
|
): OptimizationResult<TKey> {
|
|
113
113
|
if (expression.type === `func`) {
|
|
114
114
|
switch (expression.name) {
|
|
@@ -172,7 +172,7 @@ function optimizeCompoundRangeQuery<
|
|
|
172
172
|
TKey extends string | number,
|
|
173
173
|
>(
|
|
174
174
|
expression: BasicExpression,
|
|
175
|
-
collection: CollectionLike<T, TKey
|
|
175
|
+
collection: CollectionLike<T, TKey>,
|
|
176
176
|
): OptimizationResult<TKey> {
|
|
177
177
|
if (expression.type !== `func` || expression.args.length < 2) {
|
|
178
178
|
return { canOptimize: false, matchingKeys: new Set() }
|
|
@@ -305,7 +305,7 @@ function optimizeSimpleComparison<
|
|
|
305
305
|
TKey extends string | number,
|
|
306
306
|
>(
|
|
307
307
|
expression: BasicExpression,
|
|
308
|
-
collection: CollectionLike<T, TKey
|
|
308
|
+
collection: CollectionLike<T, TKey>,
|
|
309
309
|
): OptimizationResult<TKey> {
|
|
310
310
|
if (expression.type !== `func` || expression.args.length !== 2) {
|
|
311
311
|
return { canOptimize: false, matchingKeys: new Set() }
|
|
@@ -405,7 +405,7 @@ function canOptimizeSimpleComparison<
|
|
|
405
405
|
*/
|
|
406
406
|
function optimizeAndExpression<T extends object, TKey extends string | number>(
|
|
407
407
|
expression: BasicExpression,
|
|
408
|
-
collection: CollectionLike<T, TKey
|
|
408
|
+
collection: CollectionLike<T, TKey>,
|
|
409
409
|
): OptimizationResult<TKey> {
|
|
410
410
|
if (expression.type !== `func` || expression.args.length < 2) {
|
|
411
411
|
return { canOptimize: false, matchingKeys: new Set() }
|
|
@@ -457,7 +457,7 @@ function canOptimizeAndExpression<
|
|
|
457
457
|
*/
|
|
458
458
|
function optimizeOrExpression<T extends object, TKey extends string | number>(
|
|
459
459
|
expression: BasicExpression,
|
|
460
|
-
collection: CollectionLike<T, TKey
|
|
460
|
+
collection: CollectionLike<T, TKey>,
|
|
461
461
|
): OptimizationResult<TKey> {
|
|
462
462
|
if (expression.type !== `func` || expression.args.length < 2) {
|
|
463
463
|
return { canOptimize: false, matchingKeys: new Set() }
|
|
@@ -506,7 +506,7 @@ function optimizeInArrayExpression<
|
|
|
506
506
|
TKey extends string | number,
|
|
507
507
|
>(
|
|
508
508
|
expression: BasicExpression,
|
|
509
|
-
collection: CollectionLike<T, TKey
|
|
509
|
+
collection: CollectionLike<T, TKey>,
|
|
510
510
|
): OptimizationResult<TKey> {
|
|
511
511
|
if (expression.type !== `func` || expression.args.length !== 2) {
|
|
512
512
|
return { canOptimize: false, matchingKeys: new Set() }
|
package/src/utils.ts
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* Generic utility functions
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
|
-
import type { CompareOptions } from
|
|
5
|
+
import type { CompareOptions } from './query/builder/types'
|
|
6
6
|
|
|
7
7
|
interface TypedArray {
|
|
8
8
|
length: number
|
|
@@ -36,7 +36,7 @@ export function deepEquals(a: any, b: any): boolean {
|
|
|
36
36
|
function deepEqualsInternal(
|
|
37
37
|
a: any,
|
|
38
38
|
b: any,
|
|
39
|
-
visited: Map<object, object
|
|
39
|
+
visited: Map<object, object>,
|
|
40
40
|
): boolean {
|
|
41
41
|
// Handle strict equality (primitives, same reference)
|
|
42
42
|
if (a === b) return true
|
|
@@ -154,7 +154,7 @@ function deepEqualsInternal(
|
|
|
154
154
|
visited.set(a, b)
|
|
155
155
|
|
|
156
156
|
const result = a.every((item, index) =>
|
|
157
|
-
deepEqualsInternal(item, b[index], visited)
|
|
157
|
+
deepEqualsInternal(item, b[index], visited),
|
|
158
158
|
)
|
|
159
159
|
visited.delete(a)
|
|
160
160
|
return result
|
|
@@ -180,7 +180,7 @@ function deepEqualsInternal(
|
|
|
180
180
|
|
|
181
181
|
// Check if all keys exist in both objects and their values are equal
|
|
182
182
|
const result = keysA.every(
|
|
183
|
-
(key) => key in b && deepEqualsInternal(a[key], b[key], visited)
|
|
183
|
+
(key) => key in b && deepEqualsInternal(a[key], b[key], visited),
|
|
184
184
|
)
|
|
185
185
|
|
|
186
186
|
visited.delete(a)
|