@tanstack/db 0.4.2 → 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 +4 -5
- 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/index.cjs +2 -39
- package/dist/cjs/query/compiler/index.cjs.map +1 -1
- package/dist/cjs/query/compiler/index.d.cts +1 -0
- package/dist/cjs/query/compiler/joins.cjs +15 -14
- package/dist/cjs/query/compiler/joins.cjs.map +1 -1
- package/dist/cjs/query/compiler/joins.d.cts +2 -1
- package/dist/cjs/query/compiler/order-by.cjs +8 -10
- 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 +38 -0
- package/dist/cjs/query/ir.cjs.map +1 -1
- package/dist/cjs/query/ir.d.cts +11 -1
- 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 +4 -5
- 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/index.d.ts +1 -0
- package/dist/esm/query/compiler/index.js +4 -41
- package/dist/esm/query/compiler/index.js.map +1 -1
- package/dist/esm/query/compiler/joins.d.ts +2 -1
- package/dist/esm/query/compiler/joins.js +15 -14
- 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 +5 -7
- 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 +11 -1
- package/dist/esm/query/ir.js +38 -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 +4 -4
- 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/index.ts +4 -1
- package/src/query/compiler/joins.ts +13 -8
- package/src/query/compiler/order-by.ts +16 -20
- package/src/query/index.ts +1 -0
- package/src/query/ir.ts +68 -1
- 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
|
@@ -17,10 +17,10 @@ import {
|
|
|
17
17
|
UnsupportedJoinTypeError,
|
|
18
18
|
} from "../../errors.js"
|
|
19
19
|
import { ensureIndexForField } from "../../indexes/auto-index.js"
|
|
20
|
-
import { PropRef } from "../ir.js"
|
|
20
|
+
import { PropRef, followRef } from "../ir.js"
|
|
21
21
|
import { inArray } from "../builder/functions.js"
|
|
22
22
|
import { compileExpression } from "./evaluators.js"
|
|
23
|
-
import {
|
|
23
|
+
import type { CompileQueryFn } from "./index.js"
|
|
24
24
|
import type { OrderByOptimizationInfo } from "./order-by.js"
|
|
25
25
|
import type {
|
|
26
26
|
BasicExpression,
|
|
@@ -62,7 +62,8 @@ export function processJoins(
|
|
|
62
62
|
callbacks: Record<string, LazyCollectionCallbacks>,
|
|
63
63
|
lazyCollections: Set<string>,
|
|
64
64
|
optimizableOrderByCollections: Record<string, OrderByOptimizationInfo>,
|
|
65
|
-
rawQuery: QueryIR
|
|
65
|
+
rawQuery: QueryIR,
|
|
66
|
+
onCompileSubquery: CompileQueryFn
|
|
66
67
|
): NamespacedAndKeyedStream {
|
|
67
68
|
let resultPipeline = pipeline
|
|
68
69
|
|
|
@@ -81,7 +82,8 @@ export function processJoins(
|
|
|
81
82
|
callbacks,
|
|
82
83
|
lazyCollections,
|
|
83
84
|
optimizableOrderByCollections,
|
|
84
|
-
rawQuery
|
|
85
|
+
rawQuery,
|
|
86
|
+
onCompileSubquery
|
|
85
87
|
)
|
|
86
88
|
}
|
|
87
89
|
|
|
@@ -105,7 +107,8 @@ function processJoin(
|
|
|
105
107
|
callbacks: Record<string, LazyCollectionCallbacks>,
|
|
106
108
|
lazyCollections: Set<string>,
|
|
107
109
|
optimizableOrderByCollections: Record<string, OrderByOptimizationInfo>,
|
|
108
|
-
rawQuery: QueryIR
|
|
110
|
+
rawQuery: QueryIR,
|
|
111
|
+
onCompileSubquery: CompileQueryFn
|
|
109
112
|
): NamespacedAndKeyedStream {
|
|
110
113
|
// Get the joined table alias and input stream
|
|
111
114
|
const {
|
|
@@ -121,7 +124,8 @@ function processJoin(
|
|
|
121
124
|
lazyCollections,
|
|
122
125
|
optimizableOrderByCollections,
|
|
123
126
|
cache,
|
|
124
|
-
queryMapping
|
|
127
|
+
queryMapping,
|
|
128
|
+
onCompileSubquery
|
|
125
129
|
)
|
|
126
130
|
|
|
127
131
|
// Add the joined table to the tables map
|
|
@@ -392,7 +396,8 @@ function processJoinSource(
|
|
|
392
396
|
lazyCollections: Set<string>,
|
|
393
397
|
optimizableOrderByCollections: Record<string, OrderByOptimizationInfo>,
|
|
394
398
|
cache: QueryCache,
|
|
395
|
-
queryMapping: QueryMapping
|
|
399
|
+
queryMapping: QueryMapping,
|
|
400
|
+
onCompileSubquery: CompileQueryFn
|
|
396
401
|
): { alias: string; input: KeyedStream; collectionId: string } {
|
|
397
402
|
switch (from.type) {
|
|
398
403
|
case `collectionRef`: {
|
|
@@ -407,7 +412,7 @@ function processJoinSource(
|
|
|
407
412
|
const originalQuery = queryMapping.get(from.query) || from.query
|
|
408
413
|
|
|
409
414
|
// Recursively compile the sub-query with cache
|
|
410
|
-
const subQueryResult =
|
|
415
|
+
const subQueryResult = onCompileSubquery(
|
|
411
416
|
originalQuery,
|
|
412
417
|
allInputs,
|
|
413
418
|
collections,
|
|
@@ -1,16 +1,15 @@
|
|
|
1
1
|
import { orderByWithFractionalIndex } from "@tanstack/db-ivm"
|
|
2
2
|
import { defaultComparator, makeComparator } from "../../utils/comparison.js"
|
|
3
|
-
import { PropRef } from "../ir.js"
|
|
3
|
+
import { PropRef, followRef } from "../ir.js"
|
|
4
4
|
import { ensureIndexForField } from "../../indexes/auto-index.js"
|
|
5
5
|
import { findIndexForField } from "../../utils/index-optimization.js"
|
|
6
6
|
import { compileExpression } from "./evaluators.js"
|
|
7
7
|
import { replaceAggregatesByRefs } from "./group-by.js"
|
|
8
|
-
import { followRef } from "./index.js"
|
|
9
8
|
import type { CompiledSingleRowExpression } from "./evaluators.js"
|
|
10
9
|
import type { OrderByClause, QueryIR, Select } from "../ir.js"
|
|
11
10
|
import type { NamespacedAndKeyedStream, NamespacedRow } from "../../types.js"
|
|
12
11
|
import type { IStreamBuilder, KeyValue } from "@tanstack/db-ivm"
|
|
13
|
-
import type {
|
|
12
|
+
import type { IndexInterface } from "../../indexes/base-index.js"
|
|
14
13
|
import type { Collection } from "../../collection/index.js"
|
|
15
14
|
|
|
16
15
|
export type OrderByOptimizationInfo = {
|
|
@@ -21,7 +20,7 @@ export type OrderByOptimizationInfo = {
|
|
|
21
20
|
b: Record<string, unknown> | null | undefined
|
|
22
21
|
) => number
|
|
23
22
|
valueExtractorForRawRow: (row: Record<string, unknown>) => any
|
|
24
|
-
index:
|
|
23
|
+
index: IndexInterface<string | number>
|
|
25
24
|
dataNeeded?: () => number
|
|
26
25
|
}
|
|
27
26
|
|
|
@@ -55,18 +54,12 @@ export function processOrderBy(
|
|
|
55
54
|
|
|
56
55
|
// Create a value extractor function for the orderBy operator
|
|
57
56
|
const valueExtractor = (row: NamespacedRow & { __select_results?: any }) => {
|
|
58
|
-
//
|
|
59
|
-
// 1.
|
|
60
|
-
// 2.
|
|
61
|
-
|
|
62
|
-
//
|
|
63
|
-
const orderByContext =
|
|
64
|
-
|
|
65
|
-
// If there are select results, merge them at the top level for alias access
|
|
66
|
-
if (row.__select_results) {
|
|
67
|
-
// Add select results as top-level properties for alias access
|
|
68
|
-
Object.assign(orderByContext, row.__select_results)
|
|
69
|
-
}
|
|
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
|
|
70
63
|
|
|
71
64
|
if (orderByClause.length > 1) {
|
|
72
65
|
// For multiple orderBy columns, create a composite key
|
|
@@ -133,6 +126,7 @@ export function processOrderBy(
|
|
|
133
126
|
fieldName,
|
|
134
127
|
followRefResult.path,
|
|
135
128
|
followRefCollection,
|
|
129
|
+
clause.compareOptions,
|
|
136
130
|
compare
|
|
137
131
|
)
|
|
138
132
|
}
|
|
@@ -151,10 +145,12 @@ export function processOrderBy(
|
|
|
151
145
|
return compare(extractedA, extractedB)
|
|
152
146
|
}
|
|
153
147
|
|
|
154
|
-
const index:
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
148
|
+
const index: IndexInterface<string | number> | undefined =
|
|
149
|
+
findIndexForField(
|
|
150
|
+
followRefCollection.indexes,
|
|
151
|
+
followRefResult.path,
|
|
152
|
+
clause.compareOptions
|
|
153
|
+
)
|
|
158
154
|
|
|
159
155
|
if (index && index.supports(`gt`)) {
|
|
160
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
|
@@ -3,7 +3,7 @@ This is the intermediate representation of the query.
|
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
5
|
import type { CompareOptions } from "./builder/types"
|
|
6
|
-
import type { CollectionImpl } from "../collection/index.js"
|
|
6
|
+
import type { Collection, CollectionImpl } from "../collection/index.js"
|
|
7
7
|
import type { NamespacedRow } from "../types"
|
|
8
8
|
|
|
9
9
|
export interface QueryIR {
|
|
@@ -17,6 +17,7 @@ export interface QueryIR {
|
|
|
17
17
|
limit?: Limit
|
|
18
18
|
offset?: Offset
|
|
19
19
|
distinct?: true
|
|
20
|
+
singleResult?: true
|
|
20
21
|
|
|
21
22
|
// Functional variants
|
|
22
23
|
fnSelect?: (row: NamespacedRow) => any
|
|
@@ -188,3 +189,69 @@ export function createResidualWhere(
|
|
|
188
189
|
): Where {
|
|
189
190
|
return { expression, residual: true }
|
|
190
191
|
}
|
|
192
|
+
|
|
193
|
+
function getRefFromAlias(
|
|
194
|
+
query: QueryIR,
|
|
195
|
+
alias: string
|
|
196
|
+
): CollectionRef | QueryRef | void {
|
|
197
|
+
if (query.from.alias === alias) {
|
|
198
|
+
return query.from
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
for (const join of query.join || []) {
|
|
202
|
+
if (join.from.alias === alias) {
|
|
203
|
+
return join.from
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
/**
|
|
209
|
+
* Follows the given reference in a query
|
|
210
|
+
* until its finds the root field the reference points to.
|
|
211
|
+
* @returns The collection, its alias, and the path to the root field in this collection
|
|
212
|
+
*/
|
|
213
|
+
export function followRef(
|
|
214
|
+
query: QueryIR,
|
|
215
|
+
ref: PropRef<any>,
|
|
216
|
+
collection: Collection
|
|
217
|
+
): { collection: Collection; path: Array<string> } | void {
|
|
218
|
+
if (ref.path.length === 0) {
|
|
219
|
+
return
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
if (ref.path.length === 1) {
|
|
223
|
+
// This field should be part of this collection
|
|
224
|
+
const field = ref.path[0]!
|
|
225
|
+
// is it part of the select clause?
|
|
226
|
+
if (query.select) {
|
|
227
|
+
const selectedField = query.select[field]
|
|
228
|
+
if (selectedField && selectedField.type === `ref`) {
|
|
229
|
+
return followRef(query, selectedField, collection)
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
// Either this field is not part of the select clause
|
|
234
|
+
// and thus it must be part of the collection itself
|
|
235
|
+
// or it is part of the select but is not a reference
|
|
236
|
+
// so we can stop here and don't have to follow it
|
|
237
|
+
return { collection, path: [field] }
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
if (ref.path.length > 1) {
|
|
241
|
+
// This is a nested field
|
|
242
|
+
const [alias, ...rest] = ref.path
|
|
243
|
+
const aliasRef = getRefFromAlias(query, alias!)
|
|
244
|
+
if (!aliasRef) {
|
|
245
|
+
return
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
if (aliasRef.type === `queryRef`) {
|
|
249
|
+
return followRef(aliasRef.query, new PropRef(rest), collection)
|
|
250
|
+
} else {
|
|
251
|
+
// This is a reference to a collection
|
|
252
|
+
// we can't follow it further
|
|
253
|
+
// so the field must be on the collection itself
|
|
254
|
+
return { collection: aliasRef.collection, path: rest }
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
}
|
|
@@ -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
|
+
}
|