@tanstack/db 0.4.3 → 0.4.4
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 +1 -1
- package/dist/cjs/collection/change-events.cjs.map +1 -1
- package/dist/cjs/collection/changes.cjs +7 -3
- package/dist/cjs/collection/changes.cjs.map +1 -1
- package/dist/cjs/collection/events.cjs +3 -6
- package/dist/cjs/collection/events.cjs.map +1 -1
- package/dist/cjs/collection/index.cjs +4 -1
- package/dist/cjs/collection/index.cjs.map +1 -1
- package/dist/cjs/collection/index.d.cts +16 -4
- package/dist/cjs/collection/mutations.cjs +13 -20
- package/dist/cjs/collection/mutations.cjs.map +1 -1
- package/dist/cjs/collection/state.cjs +9 -2
- package/dist/cjs/collection/state.cjs.map +1 -1
- package/dist/cjs/collection/subscription.cjs +3 -4
- package/dist/cjs/collection/subscription.cjs.map +1 -1
- package/dist/cjs/collection/subscription.d.cts +2 -2
- package/dist/cjs/collection/sync.cjs +10 -2
- package/dist/cjs/collection/sync.cjs.map +1 -1
- package/dist/cjs/indexes/auto-index.cjs +4 -3
- package/dist/cjs/indexes/auto-index.cjs.map +1 -1
- package/dist/cjs/indexes/auto-index.d.cts +2 -1
- package/dist/cjs/indexes/base-index.cjs +26 -0
- package/dist/cjs/indexes/base-index.cjs.map +1 -1
- package/dist/cjs/indexes/base-index.d.cts +47 -2
- package/dist/cjs/indexes/btree-index.cjs +45 -9
- package/dist/cjs/indexes/btree-index.cjs.map +1 -1
- package/dist/cjs/indexes/btree-index.d.cts +15 -0
- package/dist/cjs/indexes/lazy-index.cjs +3 -6
- package/dist/cjs/indexes/lazy-index.cjs.map +1 -1
- package/dist/cjs/indexes/reverse-index.cjs +78 -0
- package/dist/cjs/indexes/reverse-index.cjs.map +1 -0
- package/dist/cjs/indexes/reverse-index.d.cts +30 -0
- package/dist/cjs/proxy.cjs +1 -1
- package/dist/cjs/proxy.cjs.map +1 -1
- package/dist/cjs/query/builder/index.cjs +21 -0
- package/dist/cjs/query/builder/index.cjs.map +1 -1
- package/dist/cjs/query/builder/index.d.cts +16 -1
- package/dist/cjs/query/builder/types.d.cts +7 -0
- package/dist/cjs/query/compiler/evaluators.cjs +1 -1
- package/dist/cjs/query/compiler/evaluators.cjs.map +1 -1
- package/dist/cjs/query/compiler/group-by.cjs +2 -10
- package/dist/cjs/query/compiler/group-by.cjs.map +1 -1
- package/dist/cjs/query/compiler/joins.cjs +6 -6
- package/dist/cjs/query/compiler/joins.cjs.map +1 -1
- package/dist/cjs/query/compiler/order-by.cjs +4 -5
- package/dist/cjs/query/compiler/order-by.cjs.map +1 -1
- package/dist/cjs/query/compiler/order-by.d.cts +2 -2
- package/dist/cjs/query/compiler/select.cjs +1 -1
- package/dist/cjs/query/compiler/select.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/ir.d.cts +1 -0
- package/dist/cjs/query/live/collection-config-builder.cjs +3 -2
- package/dist/cjs/query/live/collection-config-builder.cjs.map +1 -1
- package/dist/cjs/query/live/collection-config-builder.d.cts +2 -2
- package/dist/cjs/query/live/collection-subscriber.cjs +2 -3
- package/dist/cjs/query/live/collection-subscriber.cjs.map +1 -1
- package/dist/cjs/query/live/types.d.cts +4 -0
- package/dist/cjs/query/live-query-collection.cjs.map +1 -1
- package/dist/cjs/query/live-query-collection.d.cts +7 -4
- package/dist/cjs/query/optimizer.cjs +2 -4
- package/dist/cjs/query/optimizer.cjs.map +1 -1
- package/dist/cjs/transactions.cjs +2 -3
- package/dist/cjs/transactions.cjs.map +1 -1
- package/dist/cjs/types.d.cts +13 -0
- package/dist/cjs/utils/btree.cjs +1 -1
- package/dist/cjs/utils/btree.cjs.map +1 -1
- package/dist/cjs/utils/index-optimization.cjs +7 -2
- package/dist/cjs/utils/index-optimization.cjs.map +1 -1
- package/dist/cjs/utils/index-optimization.d.cts +3 -2
- package/dist/cjs/utils.cjs +6 -0
- package/dist/cjs/utils.cjs.map +1 -1
- package/dist/cjs/utils.d.cts +2 -3
- 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 +7 -3
- package/dist/esm/collection/changes.js.map +1 -1
- package/dist/esm/collection/events.js +3 -6
- package/dist/esm/collection/events.js.map +1 -1
- package/dist/esm/collection/index.d.ts +16 -4
- package/dist/esm/collection/index.js +4 -1
- package/dist/esm/collection/index.js.map +1 -1
- package/dist/esm/collection/mutations.js +13 -20
- package/dist/esm/collection/mutations.js.map +1 -1
- package/dist/esm/collection/state.js +9 -2
- package/dist/esm/collection/state.js.map +1 -1
- package/dist/esm/collection/subscription.d.ts +2 -2
- package/dist/esm/collection/subscription.js +3 -4
- package/dist/esm/collection/subscription.js.map +1 -1
- package/dist/esm/collection/sync.js +10 -2
- package/dist/esm/collection/sync.js.map +1 -1
- package/dist/esm/indexes/auto-index.d.ts +2 -1
- package/dist/esm/indexes/auto-index.js +4 -3
- package/dist/esm/indexes/auto-index.js.map +1 -1
- package/dist/esm/indexes/base-index.d.ts +47 -2
- package/dist/esm/indexes/base-index.js +26 -0
- package/dist/esm/indexes/base-index.js.map +1 -1
- package/dist/esm/indexes/btree-index.d.ts +15 -0
- package/dist/esm/indexes/btree-index.js +45 -9
- package/dist/esm/indexes/btree-index.js.map +1 -1
- package/dist/esm/indexes/lazy-index.js +3 -6
- package/dist/esm/indexes/lazy-index.js.map +1 -1
- package/dist/esm/indexes/reverse-index.d.ts +30 -0
- package/dist/esm/indexes/reverse-index.js +78 -0
- package/dist/esm/indexes/reverse-index.js.map +1 -0
- package/dist/esm/proxy.js +1 -1
- package/dist/esm/proxy.js.map +1 -1
- package/dist/esm/query/builder/index.d.ts +16 -1
- package/dist/esm/query/builder/index.js +21 -0
- package/dist/esm/query/builder/index.js.map +1 -1
- package/dist/esm/query/builder/types.d.ts +7 -0
- package/dist/esm/query/compiler/evaluators.js +1 -1
- package/dist/esm/query/compiler/evaluators.js.map +1 -1
- package/dist/esm/query/compiler/group-by.js +3 -11
- package/dist/esm/query/compiler/group-by.js.map +1 -1
- package/dist/esm/query/compiler/joins.js +6 -6
- package/dist/esm/query/compiler/joins.js.map +1 -1
- package/dist/esm/query/compiler/order-by.d.ts +2 -2
- package/dist/esm/query/compiler/order-by.js +4 -5
- package/dist/esm/query/compiler/order-by.js.map +1 -1
- package/dist/esm/query/compiler/select.js +1 -1
- package/dist/esm/query/compiler/select.js.map +1 -1
- package/dist/esm/query/index.d.ts +1 -1
- package/dist/esm/query/ir.d.ts +1 -0
- package/dist/esm/query/ir.js.map +1 -1
- package/dist/esm/query/live/collection-config-builder.d.ts +2 -2
- package/dist/esm/query/live/collection-config-builder.js +3 -2
- package/dist/esm/query/live/collection-config-builder.js.map +1 -1
- package/dist/esm/query/live/collection-subscriber.js +2 -3
- package/dist/esm/query/live/collection-subscriber.js.map +1 -1
- package/dist/esm/query/live/types.d.ts +4 -0
- package/dist/esm/query/live-query-collection.d.ts +7 -4
- package/dist/esm/query/live-query-collection.js.map +1 -1
- package/dist/esm/query/optimizer.js +2 -4
- package/dist/esm/query/optimizer.js.map +1 -1
- package/dist/esm/transactions.js +2 -3
- package/dist/esm/transactions.js.map +1 -1
- package/dist/esm/types.d.ts +13 -0
- package/dist/esm/utils/btree.js +1 -1
- package/dist/esm/utils/btree.js.map +1 -1
- package/dist/esm/utils/index-optimization.d.ts +3 -2
- package/dist/esm/utils/index-optimization.js +7 -2
- package/dist/esm/utils/index-optimization.js.map +1 -1
- package/dist/esm/utils.d.ts +2 -3
- package/dist/esm/utils.js +6 -0
- package/dist/esm/utils.js.map +1 -1
- package/package.json +1 -1
- package/src/collection/changes.ts +10 -4
- package/src/collection/index.ts +38 -5
- package/src/collection/state.ts +22 -5
- package/src/collection/subscription.ts +3 -3
- package/src/collection/sync.ts +17 -2
- package/src/indexes/auto-index.ts +8 -3
- package/src/indexes/base-index.ts +94 -4
- package/src/indexes/btree-index.ts +58 -7
- package/src/indexes/reverse-index.ts +120 -0
- package/src/query/builder/index.ts +30 -2
- package/src/query/builder/types.ts +12 -0
- package/src/query/compiler/group-by.ts +1 -10
- package/src/query/compiler/order-by.ts +15 -18
- package/src/query/index.ts +1 -0
- package/src/query/ir.ts +1 -0
- package/src/query/live/collection-config-builder.ts +3 -2
- package/src/query/live/types.ts +5 -0
- package/src/query/live-query-collection.ts +34 -8
- package/src/types.ts +22 -0
- package/src/utils/index-optimization.ts +19 -5
- package/src/utils.ts +8 -0
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
import type { CompareOptions } from "../query/builder/types"
|
|
2
|
+
import type { OrderByDirection } from "../query/ir"
|
|
3
|
+
import type { IndexInterface, IndexOperation, IndexStats } from "./base-index"
|
|
4
|
+
import type { RangeQueryOptions } from "./btree-index"
|
|
5
|
+
|
|
6
|
+
export class ReverseIndex<TKey extends string | number>
|
|
7
|
+
implements IndexInterface<TKey>
|
|
8
|
+
{
|
|
9
|
+
private originalIndex: IndexInterface<TKey>
|
|
10
|
+
|
|
11
|
+
constructor(index: IndexInterface<TKey>) {
|
|
12
|
+
this.originalIndex = index
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
// Define the reversed operations
|
|
16
|
+
|
|
17
|
+
lookup(operation: IndexOperation, value: any): Set<TKey> {
|
|
18
|
+
const reverseOperation =
|
|
19
|
+
operation === `gt`
|
|
20
|
+
? `lt`
|
|
21
|
+
: operation === `gte`
|
|
22
|
+
? `lte`
|
|
23
|
+
: operation === `lt`
|
|
24
|
+
? `gt`
|
|
25
|
+
: operation === `lte`
|
|
26
|
+
? `gte`
|
|
27
|
+
: operation
|
|
28
|
+
return this.originalIndex.lookup(reverseOperation, value)
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
rangeQuery(options: RangeQueryOptions = {}): Set<TKey> {
|
|
32
|
+
return this.originalIndex.rangeQueryReversed(options)
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
rangeQueryReversed(options: RangeQueryOptions = {}): Set<TKey> {
|
|
36
|
+
return this.originalIndex.rangeQuery(options)
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
take(n: number, from?: any, filterFn?: (key: TKey) => boolean): Array<TKey> {
|
|
40
|
+
return this.originalIndex.takeReversed(n, from, filterFn)
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
takeReversed(
|
|
44
|
+
n: number,
|
|
45
|
+
from?: any,
|
|
46
|
+
filterFn?: (key: TKey) => boolean
|
|
47
|
+
): Array<TKey> {
|
|
48
|
+
return this.originalIndex.take(n, from, filterFn)
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
get orderedEntriesArray(): Array<[any, Set<TKey>]> {
|
|
52
|
+
return this.originalIndex.orderedEntriesArrayReversed
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
get orderedEntriesArrayReversed(): Array<[any, Set<TKey>]> {
|
|
56
|
+
return this.originalIndex.orderedEntriesArray
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// All operations below delegate to the original index
|
|
60
|
+
|
|
61
|
+
supports(operation: IndexOperation): boolean {
|
|
62
|
+
return this.originalIndex.supports(operation)
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
matchesField(fieldPath: Array<string>): boolean {
|
|
66
|
+
return this.originalIndex.matchesField(fieldPath)
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
matchesCompareOptions(compareOptions: CompareOptions): boolean {
|
|
70
|
+
return this.originalIndex.matchesCompareOptions(compareOptions)
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
matchesDirection(direction: OrderByDirection): boolean {
|
|
74
|
+
return this.originalIndex.matchesDirection(direction)
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
getStats(): IndexStats {
|
|
78
|
+
return this.originalIndex.getStats()
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
add(key: TKey, item: any): void {
|
|
82
|
+
this.originalIndex.add(key, item)
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
remove(key: TKey, item: any): void {
|
|
86
|
+
this.originalIndex.remove(key, item)
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
update(key: TKey, oldItem: any, newItem: any): void {
|
|
90
|
+
this.originalIndex.update(key, oldItem, newItem)
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
build(entries: Iterable<[TKey, any]>): void {
|
|
94
|
+
this.originalIndex.build(entries)
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
clear(): void {
|
|
98
|
+
this.originalIndex.clear()
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
get keyCount(): number {
|
|
102
|
+
return this.originalIndex.keyCount
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
equalityLookup(value: any): Set<TKey> {
|
|
106
|
+
return this.originalIndex.equalityLookup(value)
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
inArrayLookup(values: Array<any>): Set<TKey> {
|
|
110
|
+
return this.originalIndex.inArrayLookup(values)
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
get indexedKeysSet(): Set<TKey> {
|
|
114
|
+
return this.originalIndex.indexedKeysSet
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
get valueMapData(): Map<any, Set<TKey>> {
|
|
118
|
+
return this.originalIndex.valueMapData
|
|
119
|
+
}
|
|
120
|
+
}
|
|
@@ -16,7 +16,7 @@ import {
|
|
|
16
16
|
SubQueryMustHaveFromClauseError,
|
|
17
17
|
} from "../../errors.js"
|
|
18
18
|
import { createRefProxy, toExpression } from "./ref-proxy.js"
|
|
19
|
-
import type { NamespacedRow } from "../../types.js"
|
|
19
|
+
import type { NamespacedRow, SingleResult } from "../../types.js"
|
|
20
20
|
import type {
|
|
21
21
|
Aggregate,
|
|
22
22
|
BasicExpression,
|
|
@@ -615,6 +615,28 @@ export class BaseQueryBuilder<TContext extends Context = Context> {
|
|
|
615
615
|
}) as any
|
|
616
616
|
}
|
|
617
617
|
|
|
618
|
+
/**
|
|
619
|
+
* Specify that the query should return a single result
|
|
620
|
+
* @returns A QueryBuilder that returns the first result
|
|
621
|
+
*
|
|
622
|
+
* @example
|
|
623
|
+
* ```ts
|
|
624
|
+
* // Get the user matching the query
|
|
625
|
+
* query
|
|
626
|
+
* .from({ users: usersCollection })
|
|
627
|
+
* .where(({users}) => eq(users.id, 1))
|
|
628
|
+
* .findOne()
|
|
629
|
+
*```
|
|
630
|
+
*/
|
|
631
|
+
findOne(): QueryBuilder<TContext & SingleResult> {
|
|
632
|
+
return new BaseQueryBuilder({
|
|
633
|
+
...this.query,
|
|
634
|
+
// TODO: enforcing return only one result with also a default orderBy if none is specified
|
|
635
|
+
// limit: 1,
|
|
636
|
+
singleResult: true,
|
|
637
|
+
})
|
|
638
|
+
}
|
|
639
|
+
|
|
618
640
|
// Helper methods
|
|
619
641
|
private _getCurrentAliases(): Array<string> {
|
|
620
642
|
const aliases: Array<string> = []
|
|
@@ -817,4 +839,10 @@ export type ExtractContext<T> =
|
|
|
817
839
|
: never
|
|
818
840
|
|
|
819
841
|
// Export the types from types.ts for convenience
|
|
820
|
-
export type {
|
|
842
|
+
export type {
|
|
843
|
+
Context,
|
|
844
|
+
Source,
|
|
845
|
+
GetResult,
|
|
846
|
+
RefLeaf as Ref,
|
|
847
|
+
InferResultType,
|
|
848
|
+
} from "./types.js"
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { CollectionImpl } from "../../collection/index.js"
|
|
2
|
+
import type { SingleResult } from "../../types.js"
|
|
2
3
|
import type {
|
|
3
4
|
Aggregate,
|
|
4
5
|
BasicExpression,
|
|
@@ -47,6 +48,8 @@ export interface Context {
|
|
|
47
48
|
>
|
|
48
49
|
// The result type after select (if select has been called)
|
|
49
50
|
result?: any
|
|
51
|
+
// Single result only (if findOne has been called)
|
|
52
|
+
singleResult?: boolean
|
|
50
53
|
}
|
|
51
54
|
|
|
52
55
|
/**
|
|
@@ -571,6 +574,7 @@ export type MergeContextWithJoinType<
|
|
|
571
574
|
[K in keyof TNewSchema & string]: TJoinType
|
|
572
575
|
}
|
|
573
576
|
result: TContext[`result`]
|
|
577
|
+
singleResult: TContext[`singleResult`] extends true ? true : false
|
|
574
578
|
}
|
|
575
579
|
|
|
576
580
|
/**
|
|
@@ -621,6 +625,14 @@ export type ApplyJoinOptionalityToMergedSchema<
|
|
|
621
625
|
TNewSchema[K]
|
|
622
626
|
}
|
|
623
627
|
|
|
628
|
+
/**
|
|
629
|
+
* Utility type to infer the query result size (single row or an array)
|
|
630
|
+
*/
|
|
631
|
+
export type InferResultType<TContext extends Context> =
|
|
632
|
+
TContext extends SingleResult
|
|
633
|
+
? GetResult<TContext> | undefined
|
|
634
|
+
: Array<GetResult<TContext>>
|
|
635
|
+
|
|
624
636
|
/**
|
|
625
637
|
* GetResult - Determines the final result type of a query
|
|
626
638
|
*
|
|
@@ -417,16 +417,7 @@ export function replaceAggregatesByRefs(
|
|
|
417
417
|
}
|
|
418
418
|
|
|
419
419
|
case `ref`: {
|
|
420
|
-
|
|
421
|
-
// Check if this is a direct reference to a SELECT alias
|
|
422
|
-
if (refExpr.path.length === 1) {
|
|
423
|
-
const alias = refExpr.path[0]!
|
|
424
|
-
if (selectClause[alias]) {
|
|
425
|
-
// This is a reference to a SELECT alias, convert to result.alias
|
|
426
|
-
return new PropRef([resultAlias, alias])
|
|
427
|
-
}
|
|
428
|
-
}
|
|
429
|
-
// Return as-is for other refs
|
|
420
|
+
// Non-aggregate refs are passed through unchanged (they reference table columns)
|
|
430
421
|
return havingExpr as BasicExpression
|
|
431
422
|
}
|
|
432
423
|
|
|
@@ -9,7 +9,7 @@ import type { CompiledSingleRowExpression } from "./evaluators.js"
|
|
|
9
9
|
import type { OrderByClause, QueryIR, Select } from "../ir.js"
|
|
10
10
|
import type { NamespacedAndKeyedStream, NamespacedRow } from "../../types.js"
|
|
11
11
|
import type { IStreamBuilder, KeyValue } from "@tanstack/db-ivm"
|
|
12
|
-
import type {
|
|
12
|
+
import type { IndexInterface } from "../../indexes/base-index.js"
|
|
13
13
|
import type { Collection } from "../../collection/index.js"
|
|
14
14
|
|
|
15
15
|
export type OrderByOptimizationInfo = {
|
|
@@ -20,7 +20,7 @@ export type OrderByOptimizationInfo = {
|
|
|
20
20
|
b: Record<string, unknown> | null | undefined
|
|
21
21
|
) => number
|
|
22
22
|
valueExtractorForRawRow: (row: Record<string, unknown>) => any
|
|
23
|
-
index:
|
|
23
|
+
index: IndexInterface<string | number>
|
|
24
24
|
dataNeeded?: () => number
|
|
25
25
|
}
|
|
26
26
|
|
|
@@ -54,18 +54,12 @@ export function processOrderBy(
|
|
|
54
54
|
|
|
55
55
|
// Create a value extractor function for the orderBy operator
|
|
56
56
|
const valueExtractor = (row: NamespacedRow & { __select_results?: any }) => {
|
|
57
|
-
//
|
|
58
|
-
// 1.
|
|
59
|
-
// 2.
|
|
60
|
-
|
|
61
|
-
//
|
|
62
|
-
const orderByContext =
|
|
63
|
-
|
|
64
|
-
// If there are select results, merge them at the top level for alias access
|
|
65
|
-
if (row.__select_results) {
|
|
66
|
-
// Add select results as top-level properties for alias access
|
|
67
|
-
Object.assign(orderByContext, row.__select_results)
|
|
68
|
-
}
|
|
57
|
+
// The namespaced row contains:
|
|
58
|
+
// 1. Table aliases as top-level properties (e.g., row["tableName"])
|
|
59
|
+
// 2. SELECT results in __select_results (e.g., row.__select_results["aggregateAlias"])
|
|
60
|
+
// The replaceAggregatesByRefs function has already transformed any aggregate expressions
|
|
61
|
+
// that match SELECT aggregates to use the __select_results namespace.
|
|
62
|
+
const orderByContext = row
|
|
69
63
|
|
|
70
64
|
if (orderByClause.length > 1) {
|
|
71
65
|
// For multiple orderBy columns, create a composite key
|
|
@@ -132,6 +126,7 @@ export function processOrderBy(
|
|
|
132
126
|
fieldName,
|
|
133
127
|
followRefResult.path,
|
|
134
128
|
followRefCollection,
|
|
129
|
+
clause.compareOptions,
|
|
135
130
|
compare
|
|
136
131
|
)
|
|
137
132
|
}
|
|
@@ -150,10 +145,12 @@ export function processOrderBy(
|
|
|
150
145
|
return compare(extractedA, extractedB)
|
|
151
146
|
}
|
|
152
147
|
|
|
153
|
-
const index:
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
148
|
+
const index: IndexInterface<string | number> | undefined =
|
|
149
|
+
findIndexForField(
|
|
150
|
+
followRefCollection.indexes,
|
|
151
|
+
followRefResult.path,
|
|
152
|
+
clause.compareOptions
|
|
153
|
+
)
|
|
157
154
|
|
|
158
155
|
if (index && index.supports(`gt`)) {
|
|
159
156
|
// We found an index that we can use to lazily load ordered data
|
package/src/query/index.ts
CHANGED
package/src/query/ir.ts
CHANGED
|
@@ -7,7 +7,7 @@ import type { RootStreamBuilder } from "@tanstack/db-ivm"
|
|
|
7
7
|
import type { OrderByOptimizationInfo } from "../compiler/order-by.js"
|
|
8
8
|
import type { Collection } from "../../collection/index.js"
|
|
9
9
|
import type {
|
|
10
|
-
|
|
10
|
+
CollectionConfigSingleRowOption,
|
|
11
11
|
KeyedStream,
|
|
12
12
|
ResultStream,
|
|
13
13
|
SyncConfig,
|
|
@@ -79,7 +79,7 @@ export class CollectionConfigBuilder<
|
|
|
79
79
|
this.compileBasePipeline()
|
|
80
80
|
}
|
|
81
81
|
|
|
82
|
-
getConfig():
|
|
82
|
+
getConfig(): CollectionConfigSingleRowOption<TResult> {
|
|
83
83
|
return {
|
|
84
84
|
id: this.id,
|
|
85
85
|
getKey:
|
|
@@ -93,6 +93,7 @@ export class CollectionConfigBuilder<
|
|
|
93
93
|
onUpdate: this.config.onUpdate,
|
|
94
94
|
onDelete: this.config.onDelete,
|
|
95
95
|
startSync: this.config.startSync,
|
|
96
|
+
singleResult: this.query.singleResult,
|
|
96
97
|
}
|
|
97
98
|
}
|
|
98
99
|
|
package/src/query/live/types.ts
CHANGED
|
@@ -3,9 +3,29 @@ import { CollectionConfigBuilder } from "./live/collection-config-builder.js"
|
|
|
3
3
|
import type { LiveQueryCollectionConfig } from "./live/types.js"
|
|
4
4
|
import type { InitialQueryBuilder, QueryBuilder } from "./builder/index.js"
|
|
5
5
|
import type { Collection } from "../collection/index.js"
|
|
6
|
-
import type {
|
|
6
|
+
import type {
|
|
7
|
+
CollectionConfig,
|
|
8
|
+
CollectionConfigSingleRowOption,
|
|
9
|
+
NonSingleResult,
|
|
10
|
+
SingleResult,
|
|
11
|
+
UtilsRecord,
|
|
12
|
+
} from "../types.js"
|
|
7
13
|
import type { Context, GetResult } from "./builder/types.js"
|
|
8
14
|
|
|
15
|
+
type CollectionConfigForContext<
|
|
16
|
+
TContext extends Context,
|
|
17
|
+
TResult extends object,
|
|
18
|
+
> = TContext extends SingleResult
|
|
19
|
+
? CollectionConfigSingleRowOption<TResult> & SingleResult
|
|
20
|
+
: CollectionConfigSingleRowOption<TResult> & NonSingleResult
|
|
21
|
+
|
|
22
|
+
type CollectionForContext<
|
|
23
|
+
TContext extends Context,
|
|
24
|
+
TResult extends object,
|
|
25
|
+
> = TContext extends SingleResult
|
|
26
|
+
? Collection<TResult> & SingleResult
|
|
27
|
+
: Collection<TResult> & NonSingleResult
|
|
28
|
+
|
|
9
29
|
/**
|
|
10
30
|
* Creates live query collection options for use with createCollection
|
|
11
31
|
*
|
|
@@ -35,12 +55,15 @@ export function liveQueryCollectionOptions<
|
|
|
35
55
|
TResult extends object = GetResult<TContext>,
|
|
36
56
|
>(
|
|
37
57
|
config: LiveQueryCollectionConfig<TContext, TResult>
|
|
38
|
-
):
|
|
58
|
+
): CollectionConfigForContext<TContext, TResult> {
|
|
39
59
|
const collectionConfigBuilder = new CollectionConfigBuilder<
|
|
40
60
|
TContext,
|
|
41
61
|
TResult
|
|
42
62
|
>(config)
|
|
43
|
-
return collectionConfigBuilder.getConfig()
|
|
63
|
+
return collectionConfigBuilder.getConfig() as CollectionConfigForContext<
|
|
64
|
+
TContext,
|
|
65
|
+
TResult
|
|
66
|
+
>
|
|
44
67
|
}
|
|
45
68
|
|
|
46
69
|
/**
|
|
@@ -83,7 +106,7 @@ export function createLiveQueryCollection<
|
|
|
83
106
|
TResult extends object = GetResult<TContext>,
|
|
84
107
|
>(
|
|
85
108
|
query: (q: InitialQueryBuilder) => QueryBuilder<TContext>
|
|
86
|
-
):
|
|
109
|
+
): CollectionForContext<TContext, TResult>
|
|
87
110
|
|
|
88
111
|
// Overload 2: Accept full config object with optional utilities
|
|
89
112
|
export function createLiveQueryCollection<
|
|
@@ -92,7 +115,7 @@ export function createLiveQueryCollection<
|
|
|
92
115
|
TUtils extends UtilsRecord = {},
|
|
93
116
|
>(
|
|
94
117
|
config: LiveQueryCollectionConfig<TContext, TResult> & { utils?: TUtils }
|
|
95
|
-
):
|
|
118
|
+
): CollectionForContext<TContext, TResult>
|
|
96
119
|
|
|
97
120
|
// Implementation
|
|
98
121
|
export function createLiveQueryCollection<
|
|
@@ -103,7 +126,7 @@ export function createLiveQueryCollection<
|
|
|
103
126
|
configOrQuery:
|
|
104
127
|
| (LiveQueryCollectionConfig<TContext, TResult> & { utils?: TUtils })
|
|
105
128
|
| ((q: InitialQueryBuilder) => QueryBuilder<TContext>)
|
|
106
|
-
):
|
|
129
|
+
): CollectionForContext<TContext, TResult> {
|
|
107
130
|
// Determine if the argument is a function (query) or a config object
|
|
108
131
|
if (typeof configOrQuery === `function`) {
|
|
109
132
|
// Simple query function case
|
|
@@ -113,7 +136,10 @@ export function createLiveQueryCollection<
|
|
|
113
136
|
) => QueryBuilder<TContext>,
|
|
114
137
|
}
|
|
115
138
|
const options = liveQueryCollectionOptions<TContext, TResult>(config)
|
|
116
|
-
return bridgeToCreateCollection(options)
|
|
139
|
+
return bridgeToCreateCollection(options) as CollectionForContext<
|
|
140
|
+
TContext,
|
|
141
|
+
TResult
|
|
142
|
+
>
|
|
117
143
|
} else {
|
|
118
144
|
// Config object case
|
|
119
145
|
const config = configOrQuery as LiveQueryCollectionConfig<
|
|
@@ -124,7 +150,7 @@ export function createLiveQueryCollection<
|
|
|
124
150
|
return bridgeToCreateCollection({
|
|
125
151
|
...options,
|
|
126
152
|
utils: config.utils,
|
|
127
|
-
})
|
|
153
|
+
}) as CollectionForContext<TContext, TResult>
|
|
128
154
|
}
|
|
129
155
|
}
|
|
130
156
|
|
package/src/types.ts
CHANGED
|
@@ -503,6 +503,28 @@ export interface CollectionConfig<
|
|
|
503
503
|
sync: SyncConfig<T, TKey>
|
|
504
504
|
}
|
|
505
505
|
|
|
506
|
+
export type SingleResult = {
|
|
507
|
+
singleResult: true
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
export type NonSingleResult = {
|
|
511
|
+
singleResult?: never
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
export type MaybeSingleResult = {
|
|
515
|
+
/**
|
|
516
|
+
* If enabled the collection will return a single object instead of an array
|
|
517
|
+
*/
|
|
518
|
+
singleResult?: true
|
|
519
|
+
}
|
|
520
|
+
|
|
521
|
+
// Only used for live query collections
|
|
522
|
+
export type CollectionConfigSingleRowOption<
|
|
523
|
+
T extends object = Record<string, unknown>,
|
|
524
|
+
TKey extends string | number = string | number,
|
|
525
|
+
TSchema extends StandardSchemaV1 = never,
|
|
526
|
+
> = CollectionConfig<T, TKey, TSchema> & MaybeSingleResult
|
|
527
|
+
|
|
506
528
|
export type ChangesPayload<T extends object = Record<string, unknown>> = Array<
|
|
507
529
|
ChangeMessage<T>
|
|
508
530
|
>
|
|
@@ -15,7 +15,14 @@
|
|
|
15
15
|
* - Optimizes IN array expressions
|
|
16
16
|
*/
|
|
17
17
|
|
|
18
|
-
import
|
|
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 {
|
|
22
|
+
BaseIndex,
|
|
23
|
+
IndexInterface,
|
|
24
|
+
IndexOperation,
|
|
25
|
+
} from "../indexes/base-index.js"
|
|
19
26
|
import type { BasicExpression } from "../query/ir.js"
|
|
20
27
|
|
|
21
28
|
/**
|
|
@@ -30,11 +37,18 @@ export interface OptimizationResult<TKey> {
|
|
|
30
37
|
* Finds an index that matches a given field path
|
|
31
38
|
*/
|
|
32
39
|
export function findIndexForField<TKey extends string | number>(
|
|
33
|
-
indexes: Map<number,
|
|
34
|
-
fieldPath: Array<string
|
|
35
|
-
|
|
40
|
+
indexes: Map<number, IndexInterface<TKey>>,
|
|
41
|
+
fieldPath: Array<string>,
|
|
42
|
+
compareOptions: CompareOptions = DEFAULT_COMPARE_OPTIONS
|
|
43
|
+
): IndexInterface<TKey> | undefined {
|
|
36
44
|
for (const index of indexes.values()) {
|
|
37
|
-
if (
|
|
45
|
+
if (
|
|
46
|
+
index.matchesField(fieldPath) &&
|
|
47
|
+
index.matchesCompareOptions(compareOptions)
|
|
48
|
+
) {
|
|
49
|
+
if (!index.matchesDirection(compareOptions.direction)) {
|
|
50
|
+
return new ReverseIndex(index)
|
|
51
|
+
}
|
|
38
52
|
return index
|
|
39
53
|
}
|
|
40
54
|
}
|
package/src/utils.ts
CHANGED
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
* Generic utility functions
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
|
+
import type { CompareOptions } from "./query/builder/types"
|
|
6
|
+
|
|
5
7
|
interface TypedArray {
|
|
6
8
|
length: number
|
|
7
9
|
[index: number]: number
|
|
@@ -209,3 +211,9 @@ export function isTemporal(a: any): boolean {
|
|
|
209
211
|
const tag = getStringTag(a)
|
|
210
212
|
return typeof tag === `string` && temporalTypes.includes(tag)
|
|
211
213
|
}
|
|
214
|
+
|
|
215
|
+
export const DEFAULT_COMPARE_OPTIONS: CompareOptions = {
|
|
216
|
+
direction: `asc`,
|
|
217
|
+
nulls: `first`,
|
|
218
|
+
stringSort: `locale`,
|
|
219
|
+
}
|