@tanstack/db 0.0.23 → 0.0.25
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.cjs +60 -19
- package/dist/cjs/collection.cjs.map +1 -1
- package/dist/cjs/collection.d.cts +27 -6
- package/dist/cjs/local-only.cjs +2 -1
- package/dist/cjs/local-only.cjs.map +1 -1
- package/dist/cjs/local-storage.cjs +2 -1
- package/dist/cjs/local-storage.cjs.map +1 -1
- package/dist/cjs/proxy.cjs +105 -11
- package/dist/cjs/proxy.cjs.map +1 -1
- package/dist/cjs/proxy.d.cts +8 -0
- package/dist/cjs/query/builder/index.cjs +72 -0
- package/dist/cjs/query/builder/index.cjs.map +1 -1
- package/dist/cjs/query/builder/index.d.cts +64 -0
- package/dist/cjs/query/compiler/index.cjs +44 -8
- package/dist/cjs/query/compiler/index.cjs.map +1 -1
- package/dist/cjs/query/compiler/index.d.cts +4 -7
- package/dist/cjs/query/compiler/joins.cjs +14 -6
- package/dist/cjs/query/compiler/joins.cjs.map +1 -1
- package/dist/cjs/query/compiler/joins.d.cts +4 -8
- package/dist/cjs/query/compiler/types.d.cts +10 -0
- package/dist/cjs/query/live-query-collection.cjs +2 -1
- package/dist/cjs/query/live-query-collection.cjs.map +1 -1
- package/dist/cjs/query/optimizer.cjs +283 -0
- package/dist/cjs/query/optimizer.cjs.map +1 -0
- package/dist/cjs/query/optimizer.d.cts +42 -0
- package/dist/cjs/types.d.cts +1 -0
- package/dist/cjs/utils.cjs +42 -0
- package/dist/cjs/utils.cjs.map +1 -0
- package/dist/cjs/utils.d.cts +18 -0
- package/dist/esm/collection.d.ts +27 -6
- package/dist/esm/collection.js +60 -19
- package/dist/esm/collection.js.map +1 -1
- package/dist/esm/local-only.js +2 -1
- package/dist/esm/local-only.js.map +1 -1
- package/dist/esm/local-storage.js +2 -1
- package/dist/esm/local-storage.js.map +1 -1
- package/dist/esm/proxy.d.ts +8 -0
- package/dist/esm/proxy.js +105 -11
- package/dist/esm/proxy.js.map +1 -1
- package/dist/esm/query/builder/index.d.ts +64 -0
- package/dist/esm/query/builder/index.js +72 -0
- package/dist/esm/query/builder/index.js.map +1 -1
- package/dist/esm/query/compiler/index.d.ts +4 -7
- package/dist/esm/query/compiler/index.js +44 -8
- package/dist/esm/query/compiler/index.js.map +1 -1
- package/dist/esm/query/compiler/joins.d.ts +4 -8
- package/dist/esm/query/compiler/joins.js +14 -6
- package/dist/esm/query/compiler/joins.js.map +1 -1
- package/dist/esm/query/compiler/types.d.ts +10 -0
- package/dist/esm/query/live-query-collection.js +2 -1
- package/dist/esm/query/live-query-collection.js.map +1 -1
- package/dist/esm/query/optimizer.d.ts +42 -0
- package/dist/esm/query/optimizer.js +283 -0
- package/dist/esm/query/optimizer.js.map +1 -0
- package/dist/esm/types.d.ts +1 -0
- package/dist/esm/utils.d.ts +18 -0
- package/dist/esm/utils.js +42 -0
- package/dist/esm/utils.js.map +1 -0
- package/package.json +1 -1
- package/src/collection.ts +75 -26
- package/src/local-only.ts +4 -1
- package/src/local-storage.ts +4 -1
- package/src/proxy.ts +152 -24
- package/src/query/builder/index.ts +104 -0
- package/src/query/compiler/index.ts +85 -18
- package/src/query/compiler/joins.ts +21 -13
- package/src/query/compiler/types.ts +12 -0
- package/src/query/live-query-collection.ts +3 -1
- package/src/query/optimizer.ts +738 -0
- package/src/types.ts +1 -0
- package/src/utils.ts +86 -0
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { distinct, filter, map } from "@electric-sql/d2mini"
|
|
2
|
+
import { optimizeQuery } from "../optimizer.js"
|
|
2
3
|
import { compileExpression } from "./evaluators.js"
|
|
3
4
|
import { processJoins } from "./joins.js"
|
|
4
5
|
import { processGroupBy } from "./group-by.js"
|
|
@@ -10,30 +11,35 @@ import type {
|
|
|
10
11
|
NamespacedAndKeyedStream,
|
|
11
12
|
ResultStream,
|
|
12
13
|
} from "../../types.js"
|
|
13
|
-
|
|
14
|
-
/**
|
|
15
|
-
* Cache for compiled subqueries to avoid duplicate compilation
|
|
16
|
-
*/
|
|
17
|
-
type QueryCache = WeakMap<QueryIR, ResultStream>
|
|
14
|
+
import type { QueryCache, QueryMapping } from "./types.js"
|
|
18
15
|
|
|
19
16
|
/**
|
|
20
17
|
* Compiles a query2 IR into a D2 pipeline
|
|
21
|
-
* @param
|
|
18
|
+
* @param rawQuery The query IR to compile
|
|
22
19
|
* @param inputs Mapping of collection names to input streams
|
|
23
20
|
* @param cache Optional cache for compiled subqueries (used internally for recursion)
|
|
21
|
+
* @param queryMapping Optional mapping from optimized queries to original queries
|
|
24
22
|
* @returns A stream builder representing the compiled query
|
|
25
23
|
*/
|
|
26
24
|
export function compileQuery(
|
|
27
|
-
|
|
25
|
+
rawQuery: QueryIR,
|
|
28
26
|
inputs: Record<string, KeyedStream>,
|
|
29
|
-
cache: QueryCache = new WeakMap()
|
|
27
|
+
cache: QueryCache = new WeakMap(),
|
|
28
|
+
queryMapping: QueryMapping = new WeakMap()
|
|
30
29
|
): ResultStream {
|
|
31
|
-
// Check if
|
|
32
|
-
const cachedResult = cache.get(
|
|
30
|
+
// Check if the original raw query has already been compiled
|
|
31
|
+
const cachedResult = cache.get(rawQuery)
|
|
33
32
|
if (cachedResult) {
|
|
34
33
|
return cachedResult
|
|
35
34
|
}
|
|
36
35
|
|
|
36
|
+
// Optimize the query before compilation
|
|
37
|
+
const query = optimizeQuery(rawQuery)
|
|
38
|
+
|
|
39
|
+
// Create mapping from optimized query to original for caching
|
|
40
|
+
queryMapping.set(query, rawQuery)
|
|
41
|
+
mapNestedQueries(query, rawQuery, queryMapping)
|
|
42
|
+
|
|
37
43
|
// Create a copy of the inputs map to avoid modifying the original
|
|
38
44
|
const allInputs = { ...inputs }
|
|
39
45
|
|
|
@@ -44,7 +50,8 @@ export function compileQuery(
|
|
|
44
50
|
const { alias: mainTableAlias, input: mainInput } = processFrom(
|
|
45
51
|
query.from,
|
|
46
52
|
allInputs,
|
|
47
|
-
cache
|
|
53
|
+
cache,
|
|
54
|
+
queryMapping
|
|
48
55
|
)
|
|
49
56
|
tables[mainTableAlias] = mainInput
|
|
50
57
|
|
|
@@ -68,7 +75,8 @@ export function compileQuery(
|
|
|
68
75
|
tables,
|
|
69
76
|
mainTableAlias,
|
|
70
77
|
allInputs,
|
|
71
|
-
cache
|
|
78
|
+
cache,
|
|
79
|
+
queryMapping
|
|
72
80
|
)
|
|
73
81
|
}
|
|
74
82
|
|
|
@@ -218,8 +226,8 @@ export function compileQuery(
|
|
|
218
226
|
)
|
|
219
227
|
|
|
220
228
|
const result = resultPipeline
|
|
221
|
-
// Cache the result before returning
|
|
222
|
-
cache.set(
|
|
229
|
+
// Cache the result before returning (use original query as key)
|
|
230
|
+
cache.set(rawQuery, result)
|
|
223
231
|
return result
|
|
224
232
|
} else if (query.limit !== undefined || query.offset !== undefined) {
|
|
225
233
|
// If there's a limit or offset without orderBy, throw an error
|
|
@@ -241,8 +249,8 @@ export function compileQuery(
|
|
|
241
249
|
)
|
|
242
250
|
|
|
243
251
|
const result = resultPipeline
|
|
244
|
-
// Cache the result before returning
|
|
245
|
-
cache.set(
|
|
252
|
+
// Cache the result before returning (use original query as key)
|
|
253
|
+
cache.set(rawQuery, result)
|
|
246
254
|
return result
|
|
247
255
|
}
|
|
248
256
|
|
|
@@ -252,7 +260,8 @@ export function compileQuery(
|
|
|
252
260
|
function processFrom(
|
|
253
261
|
from: CollectionRef | QueryRef,
|
|
254
262
|
allInputs: Record<string, KeyedStream>,
|
|
255
|
-
cache: QueryCache
|
|
263
|
+
cache: QueryCache,
|
|
264
|
+
queryMapping: QueryMapping
|
|
256
265
|
): { alias: string; input: KeyedStream } {
|
|
257
266
|
switch (from.type) {
|
|
258
267
|
case `collectionRef`: {
|
|
@@ -265,8 +274,16 @@ function processFrom(
|
|
|
265
274
|
return { alias: from.alias, input }
|
|
266
275
|
}
|
|
267
276
|
case `queryRef`: {
|
|
277
|
+
// Find the original query for caching purposes
|
|
278
|
+
const originalQuery = queryMapping.get(from.query) || from.query
|
|
279
|
+
|
|
268
280
|
// Recursively compile the sub-query with cache
|
|
269
|
-
const subQueryInput = compileQuery(
|
|
281
|
+
const subQueryInput = compileQuery(
|
|
282
|
+
originalQuery,
|
|
283
|
+
allInputs,
|
|
284
|
+
cache,
|
|
285
|
+
queryMapping
|
|
286
|
+
)
|
|
270
287
|
|
|
271
288
|
// Subqueries may return [key, [value, orderByIndex]] (with ORDER BY) or [key, value] (without ORDER BY)
|
|
272
289
|
// We need to extract just the value for use in parent queries
|
|
@@ -283,3 +300,53 @@ function processFrom(
|
|
|
283
300
|
throw new Error(`Unsupported FROM type: ${(from as any).type}`)
|
|
284
301
|
}
|
|
285
302
|
}
|
|
303
|
+
|
|
304
|
+
/**
|
|
305
|
+
* Recursively maps optimized subqueries to their original queries for proper caching.
|
|
306
|
+
* This ensures that when we encounter the same QueryRef object in different contexts,
|
|
307
|
+
* we can find the original query to check the cache.
|
|
308
|
+
*/
|
|
309
|
+
function mapNestedQueries(
|
|
310
|
+
optimizedQuery: QueryIR,
|
|
311
|
+
originalQuery: QueryIR,
|
|
312
|
+
queryMapping: QueryMapping
|
|
313
|
+
): void {
|
|
314
|
+
// Map the FROM clause if it's a QueryRef
|
|
315
|
+
if (
|
|
316
|
+
optimizedQuery.from.type === `queryRef` &&
|
|
317
|
+
originalQuery.from.type === `queryRef`
|
|
318
|
+
) {
|
|
319
|
+
queryMapping.set(optimizedQuery.from.query, originalQuery.from.query)
|
|
320
|
+
// Recursively map nested queries
|
|
321
|
+
mapNestedQueries(
|
|
322
|
+
optimizedQuery.from.query,
|
|
323
|
+
originalQuery.from.query,
|
|
324
|
+
queryMapping
|
|
325
|
+
)
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
// Map JOIN clauses if they exist
|
|
329
|
+
if (optimizedQuery.join && originalQuery.join) {
|
|
330
|
+
for (
|
|
331
|
+
let i = 0;
|
|
332
|
+
i < optimizedQuery.join.length && i < originalQuery.join.length;
|
|
333
|
+
i++
|
|
334
|
+
) {
|
|
335
|
+
const optimizedJoin = optimizedQuery.join[i]!
|
|
336
|
+
const originalJoin = originalQuery.join[i]!
|
|
337
|
+
|
|
338
|
+
if (
|
|
339
|
+
optimizedJoin.from.type === `queryRef` &&
|
|
340
|
+
originalJoin.from.type === `queryRef`
|
|
341
|
+
) {
|
|
342
|
+
queryMapping.set(optimizedJoin.from.query, originalJoin.from.query)
|
|
343
|
+
// Recursively map nested queries in joins
|
|
344
|
+
mapNestedQueries(
|
|
345
|
+
optimizedJoin.from.query,
|
|
346
|
+
originalJoin.from.query,
|
|
347
|
+
queryMapping
|
|
348
|
+
)
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
}
|
|
@@ -7,18 +7,13 @@ import {
|
|
|
7
7
|
import { compileExpression } from "./evaluators.js"
|
|
8
8
|
import { compileQuery } from "./index.js"
|
|
9
9
|
import type { IStreamBuilder, JoinType } from "@electric-sql/d2mini"
|
|
10
|
-
import type { CollectionRef, JoinClause,
|
|
10
|
+
import type { CollectionRef, JoinClause, QueryRef } from "../ir.js"
|
|
11
11
|
import type {
|
|
12
12
|
KeyedStream,
|
|
13
13
|
NamespacedAndKeyedStream,
|
|
14
14
|
NamespacedRow,
|
|
15
|
-
ResultStream,
|
|
16
15
|
} from "../../types.js"
|
|
17
|
-
|
|
18
|
-
/**
|
|
19
|
-
* Cache for compiled subqueries to avoid duplicate compilation
|
|
20
|
-
*/
|
|
21
|
-
type QueryCache = WeakMap<QueryIR, ResultStream>
|
|
16
|
+
import type { QueryCache, QueryMapping } from "./types.js"
|
|
22
17
|
|
|
23
18
|
/**
|
|
24
19
|
* Processes all join clauses in a query
|
|
@@ -29,7 +24,8 @@ export function processJoins(
|
|
|
29
24
|
tables: Record<string, KeyedStream>,
|
|
30
25
|
mainTableAlias: string,
|
|
31
26
|
allInputs: Record<string, KeyedStream>,
|
|
32
|
-
cache: QueryCache
|
|
27
|
+
cache: QueryCache,
|
|
28
|
+
queryMapping: QueryMapping
|
|
33
29
|
): NamespacedAndKeyedStream {
|
|
34
30
|
let resultPipeline = pipeline
|
|
35
31
|
|
|
@@ -40,7 +36,8 @@ export function processJoins(
|
|
|
40
36
|
tables,
|
|
41
37
|
mainTableAlias,
|
|
42
38
|
allInputs,
|
|
43
|
-
cache
|
|
39
|
+
cache,
|
|
40
|
+
queryMapping
|
|
44
41
|
)
|
|
45
42
|
}
|
|
46
43
|
|
|
@@ -56,13 +53,15 @@ function processJoin(
|
|
|
56
53
|
tables: Record<string, KeyedStream>,
|
|
57
54
|
mainTableAlias: string,
|
|
58
55
|
allInputs: Record<string, KeyedStream>,
|
|
59
|
-
cache: QueryCache
|
|
56
|
+
cache: QueryCache,
|
|
57
|
+
queryMapping: QueryMapping
|
|
60
58
|
): NamespacedAndKeyedStream {
|
|
61
59
|
// Get the joined table alias and input stream
|
|
62
60
|
const { alias: joinedTableAlias, input: joinedInput } = processJoinSource(
|
|
63
61
|
joinClause.from,
|
|
64
62
|
allInputs,
|
|
65
|
-
cache
|
|
63
|
+
cache,
|
|
64
|
+
queryMapping
|
|
66
65
|
)
|
|
67
66
|
|
|
68
67
|
// Add the joined table to the tables map
|
|
@@ -128,7 +127,8 @@ function processJoin(
|
|
|
128
127
|
function processJoinSource(
|
|
129
128
|
from: CollectionRef | QueryRef,
|
|
130
129
|
allInputs: Record<string, KeyedStream>,
|
|
131
|
-
cache: QueryCache
|
|
130
|
+
cache: QueryCache,
|
|
131
|
+
queryMapping: QueryMapping
|
|
132
132
|
): { alias: string; input: KeyedStream } {
|
|
133
133
|
switch (from.type) {
|
|
134
134
|
case `collectionRef`: {
|
|
@@ -141,8 +141,16 @@ function processJoinSource(
|
|
|
141
141
|
return { alias: from.alias, input }
|
|
142
142
|
}
|
|
143
143
|
case `queryRef`: {
|
|
144
|
+
// Find the original query for caching purposes
|
|
145
|
+
const originalQuery = queryMapping.get(from.query) || from.query
|
|
146
|
+
|
|
144
147
|
// Recursively compile the sub-query with cache
|
|
145
|
-
const subQueryInput = compileQuery(
|
|
148
|
+
const subQueryInput = compileQuery(
|
|
149
|
+
originalQuery,
|
|
150
|
+
allInputs,
|
|
151
|
+
cache,
|
|
152
|
+
queryMapping
|
|
153
|
+
)
|
|
146
154
|
|
|
147
155
|
// Subqueries may return [key, [value, orderByIndex]] (with ORDER BY) or [key, value] (without ORDER BY)
|
|
148
156
|
// We need to extract just the value for use in parent queries
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { QueryIR } from "../ir.js"
|
|
2
|
+
import type { ResultStream } from "../../types.js"
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Cache for compiled subqueries to avoid duplicate compilation
|
|
6
|
+
*/
|
|
7
|
+
export type QueryCache = WeakMap<QueryIR, ResultStream>
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Mapping from optimized queries back to their original queries for caching
|
|
11
|
+
*/
|
|
12
|
+
export type QueryMapping = WeakMap<QueryIR, QueryIR>
|
|
@@ -203,7 +203,7 @@ export function liveQueryCollectionOptions<
|
|
|
203
203
|
// Create the sync configuration
|
|
204
204
|
const sync: SyncConfig<TResult> = {
|
|
205
205
|
rowUpdateMode: `full`,
|
|
206
|
-
sync: ({ begin, write, commit, collection: theCollection }) => {
|
|
206
|
+
sync: ({ begin, write, commit, markReady, collection: theCollection }) => {
|
|
207
207
|
const { graph, inputs, pipeline } = maybeCompileBasePipeline()
|
|
208
208
|
let messagesCount = 0
|
|
209
209
|
pipeline.pipe(
|
|
@@ -295,6 +295,8 @@ export function liveQueryCollectionOptions<
|
|
|
295
295
|
begin()
|
|
296
296
|
commit()
|
|
297
297
|
}
|
|
298
|
+
// Mark the collection as ready after the first successful run
|
|
299
|
+
markReady()
|
|
298
300
|
}
|
|
299
301
|
}
|
|
300
302
|
|