@tanstack/db 0.4.20 → 0.5.0

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.
Files changed (127) hide show
  1. package/dist/cjs/collection/change-events.cjs +10 -12
  2. package/dist/cjs/collection/change-events.cjs.map +1 -1
  3. package/dist/cjs/collection/change-events.d.cts +1 -8
  4. package/dist/cjs/collection/index.cjs +18 -0
  5. package/dist/cjs/collection/index.cjs.map +1 -1
  6. package/dist/cjs/collection/index.d.cts +7 -5
  7. package/dist/cjs/index.cjs +21 -3
  8. package/dist/cjs/index.cjs.map +1 -1
  9. package/dist/cjs/index.d.cts +2 -0
  10. package/dist/cjs/indexes/auto-index.cjs +7 -3
  11. package/dist/cjs/indexes/auto-index.cjs.map +1 -1
  12. package/dist/cjs/local-storage.cjs.map +1 -1
  13. package/dist/cjs/local-storage.d.cts +2 -2
  14. package/dist/cjs/query/builder/functions.cjs +34 -0
  15. package/dist/cjs/query/builder/functions.cjs.map +1 -1
  16. package/dist/cjs/query/builder/functions.d.cts +5 -0
  17. package/dist/cjs/query/builder/index.cjs +2 -2
  18. package/dist/cjs/query/builder/index.cjs.map +1 -1
  19. package/dist/cjs/query/builder/types.d.cts +3 -22
  20. package/dist/cjs/query/compiler/evaluators.cjs +57 -4
  21. package/dist/cjs/query/compiler/evaluators.cjs.map +1 -1
  22. package/dist/cjs/query/compiler/evaluators.d.cts +13 -0
  23. package/dist/cjs/query/compiler/expressions.cjs +4 -1
  24. package/dist/cjs/query/compiler/expressions.cjs.map +1 -1
  25. package/dist/cjs/query/compiler/group-by.cjs +3 -3
  26. package/dist/cjs/query/compiler/group-by.cjs.map +1 -1
  27. package/dist/cjs/query/compiler/index.cjs +2 -2
  28. package/dist/cjs/query/compiler/index.cjs.map +1 -1
  29. package/dist/cjs/query/compiler/order-by.cjs +18 -6
  30. package/dist/cjs/query/compiler/order-by.cjs.map +1 -1
  31. package/dist/cjs/query/compiler/order-by.d.cts +7 -1
  32. package/dist/cjs/query/expression-helpers.cjs +217 -0
  33. package/dist/cjs/query/expression-helpers.cjs.map +1 -0
  34. package/dist/cjs/query/expression-helpers.d.cts +216 -0
  35. package/dist/cjs/query/index.d.cts +2 -0
  36. package/dist/cjs/query/live/collection-config-builder.cjs +13 -0
  37. package/dist/cjs/query/live/collection-config-builder.cjs.map +1 -1
  38. package/dist/cjs/query/live/collection-config-builder.d.cts +1 -0
  39. package/dist/cjs/query/live/types.d.cts +6 -1
  40. package/dist/cjs/query/predicate-utils.cjs +816 -0
  41. package/dist/cjs/query/predicate-utils.cjs.map +1 -0
  42. package/dist/cjs/query/predicate-utils.d.cts +116 -0
  43. package/dist/cjs/query/subset-dedupe.cjs +111 -0
  44. package/dist/cjs/query/subset-dedupe.cjs.map +1 -0
  45. package/dist/cjs/query/subset-dedupe.d.cts +66 -0
  46. package/dist/cjs/types.d.cts +29 -0
  47. package/dist/cjs/utils/comparison.cjs +30 -0
  48. package/dist/cjs/utils/comparison.cjs.map +1 -1
  49. package/dist/cjs/utils/comparison.d.cts +7 -1
  50. package/dist/cjs/utils/index-optimization.cjs +26 -22
  51. package/dist/cjs/utils/index-optimization.cjs.map +1 -1
  52. package/dist/cjs/utils/index-optimization.d.cts +5 -4
  53. package/dist/esm/collection/change-events.d.ts +1 -8
  54. package/dist/esm/collection/change-events.js +7 -9
  55. package/dist/esm/collection/change-events.js.map +1 -1
  56. package/dist/esm/collection/index.d.ts +7 -5
  57. package/dist/esm/collection/index.js +18 -0
  58. package/dist/esm/collection/index.js.map +1 -1
  59. package/dist/esm/index.d.ts +2 -0
  60. package/dist/esm/index.js +19 -1
  61. package/dist/esm/index.js.map +1 -1
  62. package/dist/esm/indexes/auto-index.js +7 -3
  63. package/dist/esm/indexes/auto-index.js.map +1 -1
  64. package/dist/esm/local-storage.d.ts +2 -2
  65. package/dist/esm/local-storage.js.map +1 -1
  66. package/dist/esm/query/builder/functions.d.ts +5 -0
  67. package/dist/esm/query/builder/functions.js +34 -0
  68. package/dist/esm/query/builder/functions.js.map +1 -1
  69. package/dist/esm/query/builder/index.js +2 -2
  70. package/dist/esm/query/builder/index.js.map +1 -1
  71. package/dist/esm/query/builder/types.d.ts +3 -22
  72. package/dist/esm/query/compiler/evaluators.d.ts +13 -0
  73. package/dist/esm/query/compiler/evaluators.js +59 -6
  74. package/dist/esm/query/compiler/evaluators.js.map +1 -1
  75. package/dist/esm/query/compiler/expressions.js +4 -1
  76. package/dist/esm/query/compiler/expressions.js.map +1 -1
  77. package/dist/esm/query/compiler/group-by.js +4 -4
  78. package/dist/esm/query/compiler/group-by.js.map +1 -1
  79. package/dist/esm/query/compiler/index.js +3 -3
  80. package/dist/esm/query/compiler/index.js.map +1 -1
  81. package/dist/esm/query/compiler/order-by.d.ts +7 -1
  82. package/dist/esm/query/compiler/order-by.js +18 -6
  83. package/dist/esm/query/compiler/order-by.js.map +1 -1
  84. package/dist/esm/query/expression-helpers.d.ts +216 -0
  85. package/dist/esm/query/expression-helpers.js +217 -0
  86. package/dist/esm/query/expression-helpers.js.map +1 -0
  87. package/dist/esm/query/index.d.ts +2 -0
  88. package/dist/esm/query/live/collection-config-builder.d.ts +1 -0
  89. package/dist/esm/query/live/collection-config-builder.js +13 -0
  90. package/dist/esm/query/live/collection-config-builder.js.map +1 -1
  91. package/dist/esm/query/live/types.d.ts +6 -1
  92. package/dist/esm/query/predicate-utils.d.ts +116 -0
  93. package/dist/esm/query/predicate-utils.js +816 -0
  94. package/dist/esm/query/predicate-utils.js.map +1 -0
  95. package/dist/esm/query/subset-dedupe.d.ts +66 -0
  96. package/dist/esm/query/subset-dedupe.js +111 -0
  97. package/dist/esm/query/subset-dedupe.js.map +1 -0
  98. package/dist/esm/types.d.ts +29 -0
  99. package/dist/esm/utils/comparison.d.ts +7 -1
  100. package/dist/esm/utils/comparison.js +30 -0
  101. package/dist/esm/utils/comparison.js.map +1 -1
  102. package/dist/esm/utils/index-optimization.d.ts +5 -4
  103. package/dist/esm/utils/index-optimization.js +26 -22
  104. package/dist/esm/utils/index-optimization.js.map +1 -1
  105. package/package.json +2 -2
  106. package/src/collection/change-events.ts +14 -24
  107. package/src/collection/index.ts +32 -4
  108. package/src/index.ts +4 -0
  109. package/src/indexes/auto-index.ts +8 -4
  110. package/src/local-storage.ts +11 -3
  111. package/src/query/builder/functions.ts +39 -0
  112. package/src/query/builder/index.ts +2 -2
  113. package/src/query/builder/types.ts +3 -25
  114. package/src/query/compiler/evaluators.ts +103 -5
  115. package/src/query/compiler/expressions.ts +3 -0
  116. package/src/query/compiler/group-by.ts +4 -4
  117. package/src/query/compiler/index.ts +3 -3
  118. package/src/query/compiler/order-by.ts +33 -7
  119. package/src/query/expression-helpers.ts +522 -0
  120. package/src/query/index.ts +12 -0
  121. package/src/query/live/collection-config-builder.ts +27 -0
  122. package/src/query/live/types.ts +11 -1
  123. package/src/query/predicate-utils.ts +1415 -0
  124. package/src/query/subset-dedupe.ts +243 -0
  125. package/src/types.ts +39 -0
  126. package/src/utils/comparison.ts +70 -1
  127. package/src/utils/index-optimization.ts +77 -63
@@ -18,12 +18,9 @@
18
18
  import { DEFAULT_COMPARE_OPTIONS } from "../utils.js"
19
19
  import { ReverseIndex } from "../indexes/reverse-index.js"
20
20
  import type { CompareOptions } from "../query/builder/types.js"
21
- import type {
22
- BaseIndex,
23
- IndexInterface,
24
- IndexOperation,
25
- } from "../indexes/base-index.js"
21
+ import type { IndexInterface, IndexOperation } from "../indexes/base-index.js"
26
22
  import type { BasicExpression } from "../query/ir.js"
23
+ import type { CollectionLike } from "../types.js"
27
24
 
28
25
  /**
29
26
  * Result of index-based query optimization
@@ -37,16 +34,21 @@ export interface OptimizationResult<TKey> {
37
34
  * Finds an index that matches a given field path
38
35
  */
39
36
  export function findIndexForField<TKey extends string | number>(
40
- indexes: Map<number, IndexInterface<TKey>>,
37
+ collection: CollectionLike<any, TKey>,
41
38
  fieldPath: Array<string>,
42
- compareOptions: CompareOptions = DEFAULT_COMPARE_OPTIONS
39
+ compareOptions?: CompareOptions
43
40
  ): IndexInterface<TKey> | undefined {
44
- for (const index of indexes.values()) {
41
+ const compareOpts = compareOptions ?? {
42
+ ...DEFAULT_COMPARE_OPTIONS,
43
+ ...collection.compareOptions,
44
+ }
45
+
46
+ for (const index of collection.indexes.values()) {
45
47
  if (
46
48
  index.matchesField(fieldPath) &&
47
- index.matchesCompareOptions(compareOptions)
49
+ index.matchesCompareOptions(compareOpts)
48
50
  ) {
49
- if (!index.matchesDirection(compareOptions.direction)) {
51
+ if (!index.matchesDirection(compareOpts.direction)) {
50
52
  return new ReverseIndex(index)
51
53
  }
52
54
  return index
@@ -91,19 +93,22 @@ export function unionSets<T>(sets: Array<Set<T>>): Set<T> {
91
93
  /**
92
94
  * Optimizes a query expression using available indexes to find matching keys
93
95
  */
94
- export function optimizeExpressionWithIndexes<TKey extends string | number>(
96
+ export function optimizeExpressionWithIndexes<
97
+ T extends object,
98
+ TKey extends string | number,
99
+ >(
95
100
  expression: BasicExpression,
96
- indexes: Map<number, BaseIndex<TKey>>
101
+ collection: CollectionLike<T, TKey>
97
102
  ): OptimizationResult<TKey> {
98
- return optimizeQueryRecursive(expression, indexes)
103
+ return optimizeQueryRecursive(expression, collection)
99
104
  }
100
105
 
101
106
  /**
102
107
  * Recursively optimizes query expressions
103
108
  */
104
- function optimizeQueryRecursive<TKey extends string | number>(
109
+ function optimizeQueryRecursive<T extends object, TKey extends string | number>(
105
110
  expression: BasicExpression,
106
- indexes: Map<number, BaseIndex<TKey>>
111
+ collection: CollectionLike<T, TKey>
107
112
  ): OptimizationResult<TKey> {
108
113
  if (expression.type === `func`) {
109
114
  switch (expression.name) {
@@ -112,16 +117,16 @@ function optimizeQueryRecursive<TKey extends string | number>(
112
117
  case `gte`:
113
118
  case `lt`:
114
119
  case `lte`:
115
- return optimizeSimpleComparison(expression, indexes)
120
+ return optimizeSimpleComparison(expression, collection)
116
121
 
117
122
  case `and`:
118
- return optimizeAndExpression(expression, indexes)
123
+ return optimizeAndExpression(expression, collection)
119
124
 
120
125
  case `or`:
121
- return optimizeOrExpression(expression, indexes)
126
+ return optimizeOrExpression(expression, collection)
122
127
 
123
128
  case `in`:
124
- return optimizeInArrayExpression(expression, indexes)
129
+ return optimizeInArrayExpression(expression, collection)
125
130
  }
126
131
  }
127
132
 
@@ -131,10 +136,10 @@ function optimizeQueryRecursive<TKey extends string | number>(
131
136
  /**
132
137
  * Checks if an expression can be optimized
133
138
  */
134
- export function canOptimizeExpression<TKey extends string | number>(
135
- expression: BasicExpression,
136
- indexes: Map<number, BaseIndex<TKey>>
137
- ): boolean {
139
+ export function canOptimizeExpression<
140
+ T extends object,
141
+ TKey extends string | number,
142
+ >(expression: BasicExpression, collection: CollectionLike<T, TKey>): boolean {
138
143
  if (expression.type === `func`) {
139
144
  switch (expression.name) {
140
145
  case `eq`:
@@ -142,16 +147,16 @@ export function canOptimizeExpression<TKey extends string | number>(
142
147
  case `gte`:
143
148
  case `lt`:
144
149
  case `lte`:
145
- return canOptimizeSimpleComparison(expression, indexes)
150
+ return canOptimizeSimpleComparison(expression, collection)
146
151
 
147
152
  case `and`:
148
- return canOptimizeAndExpression(expression, indexes)
153
+ return canOptimizeAndExpression(expression, collection)
149
154
 
150
155
  case `or`:
151
- return canOptimizeOrExpression(expression, indexes)
156
+ return canOptimizeOrExpression(expression, collection)
152
157
 
153
158
  case `in`:
154
- return canOptimizeInArrayExpression(expression, indexes)
159
+ return canOptimizeInArrayExpression(expression, collection)
155
160
  }
156
161
  }
157
162
 
@@ -162,9 +167,12 @@ export function canOptimizeExpression<TKey extends string | number>(
162
167
  * Optimizes compound range queries on the same field
163
168
  * Example: WHERE age > 5 AND age < 10
164
169
  */
165
- function optimizeCompoundRangeQuery<TKey extends string | number>(
170
+ function optimizeCompoundRangeQuery<
171
+ T extends object,
172
+ TKey extends string | number,
173
+ >(
166
174
  expression: BasicExpression,
167
- indexes: Map<number, BaseIndex<TKey>>
175
+ collection: CollectionLike<T, TKey>
168
176
  ): OptimizationResult<TKey> {
169
177
  if (expression.type !== `func` || expression.args.length < 2) {
170
178
  return { canOptimize: false, matchingKeys: new Set() }
@@ -236,7 +244,7 @@ function optimizeCompoundRangeQuery<TKey extends string | number>(
236
244
  for (const [fieldKey, operations] of fieldOperations) {
237
245
  if (operations.length >= 2) {
238
246
  const fieldPath = fieldKey.split(`.`)
239
- const index = findIndexForField(indexes, fieldPath)
247
+ const index = findIndexForField(collection, fieldPath)
240
248
 
241
249
  if (index && index.supports(`gt`) && index.supports(`lt`)) {
242
250
  // Build range query options
@@ -292,9 +300,12 @@ function optimizeCompoundRangeQuery<TKey extends string | number>(
292
300
  /**
293
301
  * Optimizes simple comparison expressions (eq, gt, gte, lt, lte)
294
302
  */
295
- function optimizeSimpleComparison<TKey extends string | number>(
303
+ function optimizeSimpleComparison<
304
+ T extends object,
305
+ TKey extends string | number,
306
+ >(
296
307
  expression: BasicExpression,
297
- indexes: Map<number, BaseIndex<TKey>>
308
+ collection: CollectionLike<T, TKey>
298
309
  ): OptimizationResult<TKey> {
299
310
  if (expression.type !== `func` || expression.args.length !== 2) {
300
311
  return { canOptimize: false, matchingKeys: new Set() }
@@ -337,7 +348,7 @@ function optimizeSimpleComparison<TKey extends string | number>(
337
348
 
338
349
  if (fieldArg && valueArg) {
339
350
  const fieldPath = (fieldArg as any).path
340
- const index = findIndexForField(indexes, fieldPath)
351
+ const index = findIndexForField(collection, fieldPath)
341
352
 
342
353
  if (index) {
343
354
  const queryValue = (valueArg as any).value
@@ -361,10 +372,10 @@ function optimizeSimpleComparison<TKey extends string | number>(
361
372
  /**
362
373
  * Checks if a simple comparison can be optimized
363
374
  */
364
- function canOptimizeSimpleComparison<TKey extends string | number>(
365
- expression: BasicExpression,
366
- indexes: Map<number, BaseIndex<TKey>>
367
- ): boolean {
375
+ function canOptimizeSimpleComparison<
376
+ T extends object,
377
+ TKey extends string | number,
378
+ >(expression: BasicExpression, collection: CollectionLike<T, TKey>): boolean {
368
379
  if (expression.type !== `func` || expression.args.length !== 2) {
369
380
  return false
370
381
  }
@@ -382,7 +393,7 @@ function canOptimizeSimpleComparison<TKey extends string | number>(
382
393
  }
383
394
 
384
395
  if (fieldPath) {
385
- const index = findIndexForField(indexes, fieldPath)
396
+ const index = findIndexForField(collection, fieldPath)
386
397
  return index !== undefined
387
398
  }
388
399
 
@@ -392,16 +403,16 @@ function canOptimizeSimpleComparison<TKey extends string | number>(
392
403
  /**
393
404
  * Optimizes AND expressions
394
405
  */
395
- function optimizeAndExpression<TKey extends string | number>(
406
+ function optimizeAndExpression<T extends object, TKey extends string | number>(
396
407
  expression: BasicExpression,
397
- indexes: Map<number, BaseIndex<TKey>>
408
+ collection: CollectionLike<T, TKey>
398
409
  ): OptimizationResult<TKey> {
399
410
  if (expression.type !== `func` || expression.args.length < 2) {
400
411
  return { canOptimize: false, matchingKeys: new Set() }
401
412
  }
402
413
 
403
414
  // First, try to optimize compound range queries on the same field
404
- const compoundRangeResult = optimizeCompoundRangeQuery(expression, indexes)
415
+ const compoundRangeResult = optimizeCompoundRangeQuery(expression, collection)
405
416
  if (compoundRangeResult.canOptimize) {
406
417
  return compoundRangeResult
407
418
  }
@@ -410,7 +421,7 @@ function optimizeAndExpression<TKey extends string | number>(
410
421
 
411
422
  // Try to optimize each part, keep the optimizable ones
412
423
  for (const arg of expression.args) {
413
- const result = optimizeQueryRecursive(arg, indexes)
424
+ const result = optimizeQueryRecursive(arg, collection)
414
425
  if (result.canOptimize) {
415
426
  results.push(result)
416
427
  }
@@ -429,24 +440,24 @@ function optimizeAndExpression<TKey extends string | number>(
429
440
  /**
430
441
  * Checks if an AND expression can be optimized
431
442
  */
432
- function canOptimizeAndExpression<TKey extends string | number>(
433
- expression: BasicExpression,
434
- indexes: Map<number, BaseIndex<TKey>>
435
- ): boolean {
443
+ function canOptimizeAndExpression<
444
+ T extends object,
445
+ TKey extends string | number,
446
+ >(expression: BasicExpression, collection: CollectionLike<T, TKey>): boolean {
436
447
  if (expression.type !== `func` || expression.args.length < 2) {
437
448
  return false
438
449
  }
439
450
 
440
451
  // If any argument can be optimized, we can gain some speedup
441
- return expression.args.some((arg) => canOptimizeExpression(arg, indexes))
452
+ return expression.args.some((arg) => canOptimizeExpression(arg, collection))
442
453
  }
443
454
 
444
455
  /**
445
456
  * Optimizes OR expressions
446
457
  */
447
- function optimizeOrExpression<TKey extends string | number>(
458
+ function optimizeOrExpression<T extends object, TKey extends string | number>(
448
459
  expression: BasicExpression,
449
- indexes: Map<number, BaseIndex<TKey>>
460
+ collection: CollectionLike<T, TKey>
450
461
  ): OptimizationResult<TKey> {
451
462
  if (expression.type !== `func` || expression.args.length < 2) {
452
463
  return { canOptimize: false, matchingKeys: new Set() }
@@ -456,7 +467,7 @@ function optimizeOrExpression<TKey extends string | number>(
456
467
 
457
468
  // Try to optimize each part, keep the optimizable ones
458
469
  for (const arg of expression.args) {
459
- const result = optimizeQueryRecursive(arg, indexes)
470
+ const result = optimizeQueryRecursive(arg, collection)
460
471
  if (result.canOptimize) {
461
472
  results.push(result)
462
473
  }
@@ -475,24 +486,27 @@ function optimizeOrExpression<TKey extends string | number>(
475
486
  /**
476
487
  * Checks if an OR expression can be optimized
477
488
  */
478
- function canOptimizeOrExpression<TKey extends string | number>(
479
- expression: BasicExpression,
480
- indexes: Map<number, BaseIndex<TKey>>
481
- ): boolean {
489
+ function canOptimizeOrExpression<
490
+ T extends object,
491
+ TKey extends string | number,
492
+ >(expression: BasicExpression, collection: CollectionLike<T, TKey>): boolean {
482
493
  if (expression.type !== `func` || expression.args.length < 2) {
483
494
  return false
484
495
  }
485
496
 
486
497
  // If any argument can be optimized, we can gain some speedup
487
- return expression.args.some((arg) => canOptimizeExpression(arg, indexes))
498
+ return expression.args.some((arg) => canOptimizeExpression(arg, collection))
488
499
  }
489
500
 
490
501
  /**
491
502
  * Optimizes IN array expressions
492
503
  */
493
- function optimizeInArrayExpression<TKey extends string | number>(
504
+ function optimizeInArrayExpression<
505
+ T extends object,
506
+ TKey extends string | number,
507
+ >(
494
508
  expression: BasicExpression,
495
- indexes: Map<number, BaseIndex<TKey>>
509
+ collection: CollectionLike<T, TKey>
496
510
  ): OptimizationResult<TKey> {
497
511
  if (expression.type !== `func` || expression.args.length !== 2) {
498
512
  return { canOptimize: false, matchingKeys: new Set() }
@@ -508,7 +522,7 @@ function optimizeInArrayExpression<TKey extends string | number>(
508
522
  ) {
509
523
  const fieldPath = (fieldArg as any).path
510
524
  const values = (arrayArg as any).value
511
- const index = findIndexForField(indexes, fieldPath)
525
+ const index = findIndexForField(collection, fieldPath)
512
526
 
513
527
  if (index) {
514
528
  // Check if the index supports IN operation
@@ -535,10 +549,10 @@ function optimizeInArrayExpression<TKey extends string | number>(
535
549
  /**
536
550
  * Checks if an IN array expression can be optimized
537
551
  */
538
- function canOptimizeInArrayExpression<TKey extends string | number>(
539
- expression: BasicExpression,
540
- indexes: Map<number, BaseIndex<TKey>>
541
- ): boolean {
552
+ function canOptimizeInArrayExpression<
553
+ T extends object,
554
+ TKey extends string | number,
555
+ >(expression: BasicExpression, collection: CollectionLike<T, TKey>): boolean {
542
556
  if (expression.type !== `func` || expression.args.length !== 2) {
543
557
  return false
544
558
  }
@@ -552,7 +566,7 @@ function canOptimizeInArrayExpression<TKey extends string | number>(
552
566
  Array.isArray((arrayArg as any).value)
553
567
  ) {
554
568
  const fieldPath = (fieldArg as any).path
555
- const index = findIndexForField(indexes, fieldPath)
569
+ const index = findIndexForField(collection, fieldPath)
556
570
  return index !== undefined
557
571
  }
558
572