@tanstack/db 0.5.33 → 0.6.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.
- package/dist/cjs/collection/change-events.cjs.map +1 -1
- package/dist/cjs/collection/change-events.d.cts +3 -2
- package/dist/cjs/collection/changes.cjs +13 -4
- package/dist/cjs/collection/changes.cjs.map +1 -1
- package/dist/cjs/collection/changes.d.cts +10 -1
- package/dist/cjs/collection/cleanup-queue.cjs +89 -0
- package/dist/cjs/collection/cleanup-queue.cjs.map +1 -0
- package/dist/cjs/collection/cleanup-queue.d.cts +30 -0
- package/dist/cjs/collection/events.cjs +14 -0
- package/dist/cjs/collection/events.cjs.map +1 -1
- package/dist/cjs/collection/events.d.cts +39 -1
- package/dist/cjs/collection/index.cjs +66 -28
- package/dist/cjs/collection/index.cjs.map +1 -1
- package/dist/cjs/collection/index.d.cts +49 -36
- package/dist/cjs/collection/indexes.cjs +211 -62
- package/dist/cjs/collection/indexes.cjs.map +1 -1
- package/dist/cjs/collection/indexes.d.cts +27 -17
- package/dist/cjs/collection/lifecycle.cjs +5 -22
- package/dist/cjs/collection/lifecycle.cjs.map +1 -1
- package/dist/cjs/collection/lifecycle.d.cts +0 -1
- package/dist/cjs/collection/mutations.cjs +18 -0
- package/dist/cjs/collection/mutations.cjs.map +1 -1
- package/dist/cjs/collection/mutations.d.cts +1 -0
- package/dist/cjs/collection/state.cjs +381 -53
- package/dist/cjs/collection/state.cjs.map +1 -1
- package/dist/cjs/collection/state.d.cts +65 -1
- package/dist/cjs/collection/subscription.cjs +6 -0
- package/dist/cjs/collection/subscription.cjs.map +1 -1
- package/dist/cjs/collection/subscription.d.cts +4 -0
- package/dist/cjs/collection/sync.cjs +108 -1
- package/dist/cjs/collection/sync.cjs.map +1 -1
- package/dist/cjs/collection/sync.d.cts +2 -0
- package/dist/cjs/collection/transaction-metadata.cjs +5 -0
- package/dist/cjs/collection/transaction-metadata.cjs.map +1 -0
- package/dist/cjs/collection/transaction-metadata.d.cts +1 -0
- package/dist/cjs/errors.cjs +8 -0
- package/dist/cjs/errors.cjs.map +1 -1
- package/dist/cjs/errors.d.cts +3 -0
- package/dist/cjs/index.cjs +22 -4
- package/dist/cjs/index.cjs.map +1 -1
- package/dist/cjs/index.d.cts +11 -3
- package/dist/cjs/indexes/auto-index.cjs +13 -6
- package/dist/cjs/indexes/auto-index.cjs.map +1 -1
- package/dist/cjs/indexes/base-index.cjs +0 -3
- package/dist/cjs/indexes/base-index.cjs.map +1 -1
- package/dist/cjs/indexes/base-index.d.cts +2 -6
- package/dist/cjs/indexes/basic-index.cjs +361 -0
- package/dist/cjs/indexes/basic-index.cjs.map +1 -0
- package/dist/cjs/indexes/basic-index.d.cts +102 -0
- package/dist/cjs/indexes/btree-index.cjs.map +1 -1
- package/dist/cjs/indexes/btree-index.d.cts +1 -1
- package/dist/cjs/indexes/index-options.d.cts +8 -9
- package/dist/cjs/indexes/index-registry.cjs +89 -0
- package/dist/cjs/indexes/index-registry.cjs.map +1 -0
- package/dist/cjs/indexes/index-registry.d.cts +61 -0
- package/dist/cjs/local-only.cjs +5 -0
- package/dist/cjs/local-only.cjs.map +1 -1
- package/dist/cjs/query/builder/functions.cjs +27 -11
- package/dist/cjs/query/builder/functions.cjs.map +1 -1
- package/dist/cjs/query/builder/functions.d.cts +25 -3
- package/dist/cjs/query/builder/index.cjs +200 -39
- package/dist/cjs/query/builder/index.cjs.map +1 -1
- package/dist/cjs/query/builder/index.d.cts +4 -3
- package/dist/cjs/query/builder/ref-proxy.cjs.map +1 -1
- package/dist/cjs/query/builder/ref-proxy.d.cts +14 -3
- package/dist/cjs/query/builder/types.d.cts +84 -19
- package/dist/cjs/query/compiler/evaluators.cjs +51 -0
- package/dist/cjs/query/compiler/evaluators.cjs.map +1 -1
- package/dist/cjs/query/compiler/group-by.cjs +100 -28
- package/dist/cjs/query/compiler/group-by.cjs.map +1 -1
- package/dist/cjs/query/compiler/group-by.d.cts +4 -2
- package/dist/cjs/query/compiler/index.cjs +283 -11
- package/dist/cjs/query/compiler/index.cjs.map +1 -1
- package/dist/cjs/query/compiler/index.d.cts +30 -2
- package/dist/cjs/query/compiler/order-by.cjs +29 -10
- package/dist/cjs/query/compiler/order-by.cjs.map +1 -1
- package/dist/cjs/query/compiler/order-by.d.cts +1 -1
- package/dist/cjs/query/compiler/select.cjs +8 -0
- package/dist/cjs/query/compiler/select.cjs.map +1 -1
- package/dist/cjs/query/index.d.cts +2 -1
- package/dist/cjs/query/ir.cjs +18 -1
- package/dist/cjs/query/ir.cjs.map +1 -1
- package/dist/cjs/query/ir.d.cts +21 -1
- package/dist/cjs/query/live/collection-config-builder.cjs +501 -5
- package/dist/cjs/query/live/collection-config-builder.cjs.map +1 -1
- package/dist/cjs/query/live/collection-config-builder.d.cts +7 -0
- package/dist/cjs/query/live/types.d.cts +3 -3
- package/dist/cjs/query/live/utils.cjs +43 -3
- package/dist/cjs/query/live/utils.cjs.map +1 -1
- package/dist/cjs/query/live/utils.d.cts +1 -0
- package/dist/cjs/query/live-query-collection.cjs.map +1 -1
- package/dist/cjs/query/live-query-collection.d.cts +9 -6
- package/dist/cjs/query/query-once.cjs.map +1 -1
- package/dist/cjs/query/query-once.d.cts +7 -5
- package/dist/cjs/query/subset-dedupe.cjs +9 -3
- package/dist/cjs/query/subset-dedupe.cjs.map +1 -1
- package/dist/cjs/types.d.cts +42 -8
- package/dist/cjs/utils/array-utils.cjs +27 -0
- package/dist/cjs/utils/array-utils.cjs.map +1 -0
- package/dist/cjs/utils/array-utils.d.cts +16 -0
- package/dist/cjs/utils/comparison.cjs +11 -0
- package/dist/cjs/utils/comparison.cjs.map +1 -1
- package/dist/cjs/utils/index-optimization.cjs +4 -0
- package/dist/cjs/utils/index-optimization.cjs.map +1 -1
- package/dist/cjs/utils.cjs +7 -9
- package/dist/cjs/utils.cjs.map +1 -1
- package/dist/cjs/utils.d.cts +6 -1
- package/dist/cjs/virtual-props.cjs +33 -0
- package/dist/cjs/virtual-props.cjs.map +1 -0
- package/dist/cjs/virtual-props.d.cts +196 -0
- package/dist/esm/collection/change-events.d.ts +3 -2
- package/dist/esm/collection/change-events.js.map +1 -1
- package/dist/esm/collection/changes.d.ts +10 -1
- package/dist/esm/collection/changes.js +13 -4
- package/dist/esm/collection/changes.js.map +1 -1
- package/dist/esm/collection/cleanup-queue.d.ts +30 -0
- package/dist/esm/collection/cleanup-queue.js +89 -0
- package/dist/esm/collection/cleanup-queue.js.map +1 -0
- package/dist/esm/collection/events.d.ts +39 -1
- package/dist/esm/collection/events.js +14 -0
- package/dist/esm/collection/events.js.map +1 -1
- package/dist/esm/collection/index.d.ts +49 -36
- package/dist/esm/collection/index.js +67 -29
- package/dist/esm/collection/index.js.map +1 -1
- package/dist/esm/collection/indexes.d.ts +27 -17
- package/dist/esm/collection/indexes.js +211 -62
- package/dist/esm/collection/indexes.js.map +1 -1
- package/dist/esm/collection/lifecycle.d.ts +0 -1
- package/dist/esm/collection/lifecycle.js +5 -22
- package/dist/esm/collection/lifecycle.js.map +1 -1
- package/dist/esm/collection/mutations.d.ts +1 -0
- package/dist/esm/collection/mutations.js +18 -0
- package/dist/esm/collection/mutations.js.map +1 -1
- package/dist/esm/collection/state.d.ts +65 -1
- package/dist/esm/collection/state.js +381 -53
- package/dist/esm/collection/state.js.map +1 -1
- package/dist/esm/collection/subscription.d.ts +4 -0
- package/dist/esm/collection/subscription.js +6 -0
- package/dist/esm/collection/subscription.js.map +1 -1
- package/dist/esm/collection/sync.d.ts +2 -0
- package/dist/esm/collection/sync.js +108 -1
- package/dist/esm/collection/sync.js.map +1 -1
- package/dist/esm/collection/transaction-metadata.d.ts +1 -0
- package/dist/esm/collection/transaction-metadata.js +5 -0
- package/dist/esm/collection/transaction-metadata.js.map +1 -0
- package/dist/esm/errors.d.ts +3 -0
- package/dist/esm/errors.js +8 -0
- package/dist/esm/errors.js.map +1 -1
- package/dist/esm/index.d.ts +11 -3
- package/dist/esm/index.js +25 -7
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/indexes/auto-index.js +13 -6
- package/dist/esm/indexes/auto-index.js.map +1 -1
- package/dist/esm/indexes/base-index.d.ts +2 -6
- package/dist/esm/indexes/base-index.js +1 -4
- package/dist/esm/indexes/base-index.js.map +1 -1
- package/dist/esm/indexes/basic-index.d.ts +102 -0
- package/dist/esm/indexes/basic-index.js +361 -0
- package/dist/esm/indexes/basic-index.js.map +1 -0
- package/dist/esm/indexes/btree-index.d.ts +1 -1
- package/dist/esm/indexes/btree-index.js.map +1 -1
- package/dist/esm/indexes/index-options.d.ts +8 -9
- package/dist/esm/indexes/index-registry.d.ts +61 -0
- package/dist/esm/indexes/index-registry.js +89 -0
- package/dist/esm/indexes/index-registry.js.map +1 -0
- package/dist/esm/local-only.js +5 -0
- package/dist/esm/local-only.js.map +1 -1
- package/dist/esm/query/builder/functions.d.ts +25 -3
- package/dist/esm/query/builder/functions.js +27 -11
- package/dist/esm/query/builder/functions.js.map +1 -1
- package/dist/esm/query/builder/index.d.ts +4 -3
- package/dist/esm/query/builder/index.js +201 -40
- package/dist/esm/query/builder/index.js.map +1 -1
- package/dist/esm/query/builder/ref-proxy.d.ts +14 -3
- package/dist/esm/query/builder/ref-proxy.js.map +1 -1
- package/dist/esm/query/builder/types.d.ts +84 -19
- package/dist/esm/query/compiler/evaluators.js +51 -0
- package/dist/esm/query/compiler/evaluators.js.map +1 -1
- package/dist/esm/query/compiler/group-by.d.ts +4 -2
- package/dist/esm/query/compiler/group-by.js +101 -29
- package/dist/esm/query/compiler/group-by.js.map +1 -1
- package/dist/esm/query/compiler/index.d.ts +30 -2
- package/dist/esm/query/compiler/index.js +285 -13
- package/dist/esm/query/compiler/index.js.map +1 -1
- package/dist/esm/query/compiler/order-by.d.ts +1 -1
- package/dist/esm/query/compiler/order-by.js +30 -11
- package/dist/esm/query/compiler/order-by.js.map +1 -1
- package/dist/esm/query/compiler/select.js +8 -0
- package/dist/esm/query/compiler/select.js.map +1 -1
- package/dist/esm/query/index.d.ts +2 -1
- package/dist/esm/query/ir.d.ts +21 -1
- package/dist/esm/query/ir.js +18 -1
- package/dist/esm/query/ir.js.map +1 -1
- package/dist/esm/query/live/collection-config-builder.d.ts +7 -0
- package/dist/esm/query/live/collection-config-builder.js +503 -7
- package/dist/esm/query/live/collection-config-builder.js.map +1 -1
- package/dist/esm/query/live/types.d.ts +3 -3
- package/dist/esm/query/live/utils.d.ts +1 -0
- package/dist/esm/query/live/utils.js +43 -3
- package/dist/esm/query/live/utils.js.map +1 -1
- package/dist/esm/query/live-query-collection.d.ts +9 -6
- package/dist/esm/query/live-query-collection.js.map +1 -1
- package/dist/esm/query/query-once.d.ts +7 -5
- package/dist/esm/query/query-once.js.map +1 -1
- package/dist/esm/query/subset-dedupe.js +9 -3
- package/dist/esm/query/subset-dedupe.js.map +1 -1
- package/dist/esm/types.d.ts +42 -8
- package/dist/esm/utils/array-utils.d.ts +16 -0
- package/dist/esm/utils/array-utils.js +27 -0
- package/dist/esm/utils/array-utils.js.map +1 -0
- package/dist/esm/utils/comparison.js +11 -0
- package/dist/esm/utils/comparison.js.map +1 -1
- package/dist/esm/utils/index-optimization.js +4 -0
- package/dist/esm/utils/index-optimization.js.map +1 -1
- package/dist/esm/utils.d.ts +6 -1
- package/dist/esm/utils.js +7 -9
- package/dist/esm/utils.js.map +1 -1
- package/dist/esm/virtual-props.d.ts +196 -0
- package/dist/esm/virtual-props.js +33 -0
- package/dist/esm/virtual-props.js.map +1 -0
- package/package.json +2 -2
- package/skills/db-core/collection-setup/references/electric-adapter.md +1 -1
- package/src/collection/change-events.ts +13 -9
- package/src/collection/changes.ts +30 -7
- package/src/collection/cleanup-queue.ts +105 -0
- package/src/collection/events.ts +65 -0
- package/src/collection/index.ts +110 -45
- package/src/collection/indexes.ts +283 -76
- package/src/collection/lifecycle.ts +5 -26
- package/src/collection/mutations.ts +21 -0
- package/src/collection/state.ts +545 -71
- package/src/collection/subscription.ts +7 -0
- package/src/collection/sync.ts +137 -0
- package/src/collection/transaction-metadata.ts +1 -0
- package/src/errors.ts +9 -0
- package/src/index.ts +46 -3
- package/src/indexes/auto-index.ts +18 -8
- package/src/indexes/base-index.ts +2 -10
- package/src/indexes/basic-index.ts +507 -0
- package/src/indexes/btree-index.ts +1 -1
- package/src/indexes/index-options.ts +17 -37
- package/src/indexes/index-registry.ts +174 -0
- package/src/local-only.ts +7 -0
- package/src/query/builder/functions.ts +84 -7
- package/src/query/builder/index.ts +329 -9
- package/src/query/builder/ref-proxy.ts +22 -4
- package/src/query/builder/types.ts +257 -62
- package/src/query/compiler/evaluators.ts +57 -0
- package/src/query/compiler/group-by.ts +156 -35
- package/src/query/compiler/index.ts +445 -15
- package/src/query/compiler/order-by.ts +51 -12
- package/src/query/compiler/select.ts +9 -0
- package/src/query/index.ts +7 -0
- package/src/query/ir.ts +23 -2
- package/src/query/live/collection-config-builder.ts +809 -9
- package/src/query/live/types.ts +10 -4
- package/src/query/live/utils.ts +64 -3
- package/src/query/live-query-collection.ts +43 -18
- package/src/query/query-once.ts +31 -12
- package/src/query/subset-dedupe.ts +11 -7
- package/src/types.ts +49 -9
- package/src/utils/array-utils.ts +49 -0
- package/src/utils/comparison.ts +14 -0
- package/src/utils/index-optimization.ts +4 -0
- package/src/utils.ts +12 -9
- package/src/virtual-props.ts +282 -0
- package/dist/cjs/indexes/lazy-index.cjs +0 -190
- package/dist/cjs/indexes/lazy-index.cjs.map +0 -1
- package/dist/cjs/indexes/lazy-index.d.cts +0 -96
- package/dist/esm/indexes/lazy-index.d.ts +0 -96
- package/dist/esm/indexes/lazy-index.js +0 -190
- package/dist/esm/indexes/lazy-index.js.map +0 -1
- package/src/indexes/lazy-index.ts +0 -251
|
@@ -1,7 +1,9 @@
|
|
|
1
|
-
import { CollectionImpl } from '../../collection/index.js';
|
|
1
|
+
import { Collection, CollectionImpl } from '../../collection/index.js';
|
|
2
2
|
import { SingleResult, StringCollationConfig } from '../../types.js';
|
|
3
3
|
import { Aggregate, BasicExpression, Func, OrderByDirection, PropRef, Value } from '../ir.js';
|
|
4
|
-
import { QueryBuilder } from './index.js';
|
|
4
|
+
import { InitialQueryBuilder, QueryBuilder } from './index.js';
|
|
5
|
+
import { VirtualRowProps, WithVirtualProps } from '../../virtual-props.js';
|
|
6
|
+
import { ConcatToArrayWrapper, ToArrayWrapper } from './functions.js';
|
|
5
7
|
/**
|
|
6
8
|
* Context - The central state container for query builder operations
|
|
7
9
|
*
|
|
@@ -31,6 +33,7 @@ export interface Context {
|
|
|
31
33
|
hasJoins?: boolean;
|
|
32
34
|
joinTypes?: Record<string, `inner` | `left` | `right` | `full` | `outer` | `cross`>;
|
|
33
35
|
result?: any;
|
|
36
|
+
hasResult?: true;
|
|
34
37
|
singleResult?: boolean;
|
|
35
38
|
}
|
|
36
39
|
/**
|
|
@@ -61,7 +64,7 @@ export type Source = {
|
|
|
61
64
|
* This helper ensures we get the same type that was used when creating the collection itself.
|
|
62
65
|
* This can be an explicit type passed by the user or the schema output type.
|
|
63
66
|
*/
|
|
64
|
-
export type InferCollectionType<T> = T extends CollectionImpl<infer TOutput,
|
|
67
|
+
export type InferCollectionType<T> = T extends CollectionImpl<infer TOutput, infer TKey, any, any, any> ? WithVirtualProps<TOutput, TKey> : never;
|
|
65
68
|
/**
|
|
66
69
|
* SchemaFromSource - Converts a Source definition into a ContextSchema
|
|
67
70
|
*
|
|
@@ -132,10 +135,12 @@ export type WhereCallback<TContext extends Context> = (refs: RefsForContext<TCon
|
|
|
132
135
|
*/
|
|
133
136
|
type SelectValue = BasicExpression | Aggregate | Ref | RefLeaf<any> | string | number | boolean | null | undefined | {
|
|
134
137
|
[key: string]: SelectValue;
|
|
135
|
-
} | Array<RefLeaf<any
|
|
138
|
+
} | Array<RefLeaf<any>> | ToArrayWrapper | ConcatToArrayWrapper | QueryBuilder<any>;
|
|
136
139
|
type SelectShape = {
|
|
137
140
|
[key: string]: SelectValue | SelectShape;
|
|
138
141
|
};
|
|
142
|
+
export type ScalarSelectValue = BasicExpression | Aggregate | Ref | RefLeaf<any> | string | number | boolean | null | undefined;
|
|
143
|
+
export type StringifiableScalar = string | number | boolean | null | undefined;
|
|
139
144
|
/**
|
|
140
145
|
* SelectObject - Wrapper type for select clause objects
|
|
141
146
|
*
|
|
@@ -144,6 +149,11 @@ type SelectShape = {
|
|
|
144
149
|
* messages when invalid selections are attempted.
|
|
145
150
|
*/
|
|
146
151
|
export type SelectObject<T extends SelectShape = SelectShape> = T;
|
|
152
|
+
type RefBrandKeys = typeof RefBrand | typeof NullableBrand;
|
|
153
|
+
type HasNamedSelectKeys<T> = Exclude<keyof T, RefBrandKeys> extends never ? false : true;
|
|
154
|
+
type IsScalarSelectLike<T> = T extends BasicExpression | Aggregate ? true : T extends string | number | boolean | null | undefined ? true : typeof RefBrand extends keyof T ? HasNamedSelectKeys<T> extends true ? false : true : false;
|
|
155
|
+
export type NonScalarSelectObject<T> = T extends SelectObject ? IsScalarSelectLike<T> extends true ? never : T : never;
|
|
156
|
+
export type ResultTypeFromSelectValue<TSelectValue> = IsAny<TSelectValue> extends true ? any : WithoutRefBrand<NeedsExtraction<TSelectValue> extends true ? ExtractExpressionType<TSelectValue> : TSelectValue extends ToArrayWrapper<infer T> ? Array<T> : TSelectValue extends ConcatToArrayWrapper<any> ? string : TSelectValue extends QueryBuilder<infer TChildContext> ? Collection<GetResult<TChildContext>> : TSelectValue extends Ref<infer _T> ? ExtractRef<TSelectValue> : TSelectValue extends RefLeaf<infer T> ? IsNullableRef<TSelectValue> extends true ? T | undefined : T : TSelectValue extends RefLeaf<infer T> | undefined ? T | undefined : TSelectValue extends RefLeaf<infer T> | null ? IsNullableRef<Exclude<TSelectValue, null>> extends true ? T | null | undefined : T | null : TSelectValue extends Ref<infer _T> | undefined ? ExtractRef<Exclude<TSelectValue, undefined>> | undefined : TSelectValue extends Ref<infer _T> | null ? ExtractRef<Exclude<TSelectValue, null>> | null : TSelectValue extends Aggregate<infer T> ? T : TSelectValue extends string | number | boolean | null | undefined ? TSelectValue : TSelectValue extends Record<string, any> ? ResultTypeFromSelect<TSelectValue> : never>;
|
|
147
157
|
/**
|
|
148
158
|
* ResultTypeFromSelect - Infers the result type from a select object
|
|
149
159
|
*
|
|
@@ -180,9 +190,10 @@ export type SelectObject<T extends SelectShape = SelectShape> = T;
|
|
|
180
190
|
* { id: number, name: string, status: 'active', count: 42, profile: { bio: string } }
|
|
181
191
|
* ```
|
|
182
192
|
*/
|
|
183
|
-
export type ResultTypeFromSelect<TSelectObject> = WithoutRefBrand<Prettify<{
|
|
184
|
-
[K in keyof TSelectObject]: NeedsExtraction<TSelectObject[K]> extends true ? ExtractExpressionType<TSelectObject[K]> : TSelectObject[K] extends Ref<infer _T> ? ExtractRef<TSelectObject[K]> : TSelectObject[K] extends RefLeaf<infer T> ? IsNullableRef<TSelectObject[K]> extends true ? T | undefined : T : TSelectObject[K] extends RefLeaf<infer T> | undefined ? T | undefined : TSelectObject[K] extends RefLeaf<infer T> | null ? IsNullableRef<Exclude<TSelectObject[K], null>> extends true ? T | null | undefined : T | null : TSelectObject[K] extends Ref<infer _T> | undefined ? ExtractRef<Exclude<TSelectObject[K], undefined>> | undefined : TSelectObject[K] extends Ref<infer _T> | null ? ExtractRef<Exclude<TSelectObject[K], null>> | null : TSelectObject[K] extends Aggregate<infer T> ? T : TSelectObject[K] extends string | number | boolean | null | undefined ? TSelectObject[K] : TSelectObject[K] extends Record<string, any> ? ResultTypeFromSelect<TSelectObject[K]> : never;
|
|
193
|
+
export type ResultTypeFromSelect<TSelectObject> = IsAny<TSelectObject> extends true ? any : WithoutRefBrand<Prettify<{
|
|
194
|
+
[K in keyof TSelectObject]: NeedsExtraction<TSelectObject[K]> extends true ? ExtractExpressionType<TSelectObject[K]> : TSelectObject[K] extends ToArrayWrapper<infer T> ? Array<T> : TSelectObject[K] extends ConcatToArrayWrapper<any> ? string : TSelectObject[K] extends QueryBuilder<infer TChildContext> ? Collection<GetResult<TChildContext>> : TSelectObject[K] extends Ref<infer _T> ? ExtractRef<TSelectObject[K]> : TSelectObject[K] extends RefLeaf<infer T> ? IsNullableRef<TSelectObject[K]> extends true ? T | undefined : T : TSelectObject[K] extends RefLeaf<infer T> | undefined ? T | undefined : TSelectObject[K] extends RefLeaf<infer T> | null ? IsNullableRef<Exclude<TSelectObject[K], null>> extends true ? T | null | undefined : T | null : TSelectObject[K] extends Ref<infer _T> | undefined ? ExtractRef<Exclude<TSelectObject[K], undefined>> | undefined : TSelectObject[K] extends Ref<infer _T> | null ? ExtractRef<Exclude<TSelectObject[K], null>> | null : TSelectObject[K] extends Aggregate<infer T> ? T : TSelectObject[K] extends string | number | boolean | null | undefined ? TSelectObject[K] : TSelectObject[K] extends Record<string, any> ? ResultTypeFromSelect<TSelectObject[K]> : never;
|
|
185
195
|
}>>;
|
|
196
|
+
export type SelectResult<TSelect> = IsPlainObject<TSelect> extends true ? ResultTypeFromSelect<TSelect> : ResultTypeFromSelectValue<TSelect>;
|
|
186
197
|
type ExtractRef<T> = Prettify<ResultTypeFromSelect<WithoutRefBrand<T>>>;
|
|
187
198
|
type ExtractExpressionType<T> = T extends PropRef<infer U> ? U : T extends Value<infer U> ? U : T extends Func<infer U> ? U : T extends Aggregate<infer U> ? U : T extends BasicExpression<infer U> ? U : T;
|
|
188
199
|
type NeedsExtraction<T> = T extends PropRef<any> | Value<any> | Func<any> | Aggregate<any> | BasicExpression<any> ? true : false;
|
|
@@ -256,7 +267,7 @@ export type JoinOnCallback<TContext extends Context> = (refs: RefsForContext<TCo
|
|
|
256
267
|
* Example: `({ $selected }) => $selected.sessionCount > 2`
|
|
257
268
|
* Example (no GROUP BY): `(row) => row.user.salary > 70000 && row.$selected.user_count > 2`
|
|
258
269
|
*/
|
|
259
|
-
export type FunctionalHavingRow<TContext extends Context> = TContext[`schema`] & (TContext[`
|
|
270
|
+
export type FunctionalHavingRow<TContext extends Context> = TContext[`schema`] & (TContext[`hasResult`] extends true ? {
|
|
260
271
|
$selected: TContext[`result`];
|
|
261
272
|
} : {});
|
|
262
273
|
/**
|
|
@@ -277,7 +288,7 @@ export type FunctionalHavingRow<TContext extends Context> = TContext[`schema`] &
|
|
|
277
288
|
*/
|
|
278
289
|
export type RefsForContext<TContext extends Context> = {
|
|
279
290
|
[K in keyof TContext[`schema`]]: IsNonExactOptional<TContext[`schema`][K]> extends true ? IsNonExactNullable<TContext[`schema`][K]> extends true ? Ref<NonNullable<TContext[`schema`][K]>, true> : Ref<NonUndefined<TContext[`schema`][K]>, true> : IsNonExactNullable<TContext[`schema`][K]> extends true ? Ref<NonNull<TContext[`schema`][K]>, true> : Ref<TContext[`schema`][K]>;
|
|
280
|
-
} & (TContext[`
|
|
291
|
+
} & (TContext[`hasResult`] extends true ? {
|
|
281
292
|
$selected: Ref<TContext[`result`]>;
|
|
282
293
|
} : {});
|
|
283
294
|
/**
|
|
@@ -313,6 +324,28 @@ type IsNonExactNullable<T> = IsNullable<T> extends true ? IsExactlyNull<T> exten
|
|
|
313
324
|
*/
|
|
314
325
|
type NonUndefined<T> = T extends undefined ? never : T;
|
|
315
326
|
type NonNull<T> = T extends null ? never : T;
|
|
327
|
+
/**
|
|
328
|
+
* Virtual properties available on all Ref types in query builders.
|
|
329
|
+
* These allow querying on sync status, origin, key, and collection ID.
|
|
330
|
+
*
|
|
331
|
+
* @example
|
|
332
|
+
* ```typescript
|
|
333
|
+
* // Filter by sync status
|
|
334
|
+
* .where(({ user }) => eq(user.$synced, true))
|
|
335
|
+
*
|
|
336
|
+
* // Filter by origin
|
|
337
|
+
* .where(({ order }) => eq(order.$origin, 'local'))
|
|
338
|
+
*
|
|
339
|
+
* // Access key in select
|
|
340
|
+
* .select(({ user }) => ({
|
|
341
|
+
* key: user.$key,
|
|
342
|
+
* collectionId: user.$collectionId,
|
|
343
|
+
* }))
|
|
344
|
+
* ```
|
|
345
|
+
*/
|
|
346
|
+
type VirtualPropsRef<TKey extends string | number = string | number> = {
|
|
347
|
+
readonly [K in keyof VirtualRowProps<TKey>]: RefLeaf<VirtualRowProps<TKey>[K]>;
|
|
348
|
+
};
|
|
316
349
|
/**
|
|
317
350
|
* Ref - The user-facing ref interface for the query builder
|
|
318
351
|
*
|
|
@@ -326,12 +359,18 @@ type NonNull<T> = T extends null ? never : T;
|
|
|
326
359
|
* through all nested property accesses, ensuring the result type includes
|
|
327
360
|
* `| undefined` for all fields accessed through this ref.
|
|
328
361
|
*
|
|
362
|
+
* Includes virtual properties ($synced, $origin, $key, $collectionId) for
|
|
363
|
+
* querying on sync status and row metadata.
|
|
364
|
+
*
|
|
329
365
|
* Example usage:
|
|
330
366
|
* ```typescript
|
|
331
|
-
* //
|
|
332
|
-
*
|
|
367
|
+
* // Clean interface - no internal properties visible
|
|
368
|
+
* const users: Ref<{ id: number; profile?: { bio: string } }> = { ... }
|
|
369
|
+
* users.id // Ref<number> - clean display
|
|
370
|
+
* users.profile?.bio // Ref<string> - nested optional access works
|
|
371
|
+
* users.$synced // RefLeaf<boolean> - virtual property access
|
|
333
372
|
*
|
|
334
|
-
* // Nullable ref (left join
|
|
373
|
+
* // Nullable ref (left/right/full join side):
|
|
335
374
|
* select(({ dept }) => ({ name: dept.name })) // result: string | undefined
|
|
336
375
|
*
|
|
337
376
|
* // Spread operations work cleanly:
|
|
@@ -340,7 +379,7 @@ type NonNull<T> = T extends null ? never : T;
|
|
|
340
379
|
*/
|
|
341
380
|
export type Ref<T = any, Nullable extends boolean = false> = {
|
|
342
381
|
[K in keyof T]: IsNonExactOptional<T[K]> extends true ? IsNonExactNullable<T[K]> extends true ? IsPlainObject<NonNullable<T[K]>> extends true ? Ref<NonNullable<T[K]>, Nullable> | undefined : RefLeaf<NonNullable<T[K]>, Nullable> | undefined : IsPlainObject<NonUndefined<T[K]>> extends true ? Ref<NonUndefined<T[K]>, Nullable> | undefined : RefLeaf<NonUndefined<T[K]>, Nullable> | undefined : IsNonExactNullable<T[K]> extends true ? IsPlainObject<NonNull<T[K]>> extends true ? Ref<NonNull<T[K]>, Nullable> | null : RefLeaf<NonNull<T[K]>, Nullable> | null : IsPlainObject<T[K]> extends true ? Ref<T[K], Nullable> : RefLeaf<T[K], Nullable>;
|
|
343
|
-
} & RefLeaf<T, Nullable
|
|
382
|
+
} & RefLeaf<T, Nullable> & VirtualPropsRef;
|
|
344
383
|
/**
|
|
345
384
|
* Ref - The user-facing ref type with clean IDE display
|
|
346
385
|
*
|
|
@@ -360,7 +399,7 @@ export type RefLeaf<T = any, Nullable extends boolean = false> = {
|
|
|
360
399
|
readonly [NullableBrand]?: true;
|
|
361
400
|
} : {});
|
|
362
401
|
type IsNullableRef<T> = typeof NullableBrand extends keyof T ? true : false;
|
|
363
|
-
type WithoutRefBrand<T> = T extends
|
|
402
|
+
type WithoutRefBrand<T> = IsPlainObject<T> extends true ? Omit<T, typeof RefBrand | typeof NullableBrand> : T;
|
|
364
403
|
/**
|
|
365
404
|
* PreserveSingleResultFlag - Conditionally includes the singleResult flag
|
|
366
405
|
*
|
|
@@ -374,6 +413,9 @@ type WithoutRefBrand<T> = T extends Record<string, any> ? Omit<T, typeof RefBran
|
|
|
374
413
|
type PreserveSingleResultFlag<TFlag> = [TFlag] extends [true] ? {
|
|
375
414
|
singleResult: true;
|
|
376
415
|
} : {};
|
|
416
|
+
type PreserveHasResultFlag<TFlag> = [TFlag] extends [true] ? {
|
|
417
|
+
hasResult: true;
|
|
418
|
+
} : {};
|
|
377
419
|
/**
|
|
378
420
|
* MergeContextWithJoinType - Creates a new context after a join operation
|
|
379
421
|
*
|
|
@@ -406,7 +448,7 @@ export type MergeContextWithJoinType<TContext extends Context, TNewSchema extend
|
|
|
406
448
|
[K in keyof TNewSchema & string]: TJoinType;
|
|
407
449
|
};
|
|
408
450
|
result: TContext[`result`];
|
|
409
|
-
} & PreserveSingleResultFlag<TContext[`singleResult`]>;
|
|
451
|
+
} & PreserveSingleResultFlag<TContext[`singleResult`]> & PreserveHasResultFlag<TContext[`hasResult`]>;
|
|
410
452
|
/**
|
|
411
453
|
* ApplyJoinOptionalityToMergedSchema - Applies optionality rules when merging schemas
|
|
412
454
|
*
|
|
@@ -442,6 +484,9 @@ export type ApplyJoinOptionalityToMergedSchema<TExistingSchema extends ContextSc
|
|
|
442
484
|
* Utility type to infer the query result size (single row or an array)
|
|
443
485
|
*/
|
|
444
486
|
export type InferResultType<TContext extends Context> = TContext extends SingleResult ? GetResult<TContext> | undefined : Array<GetResult<TContext>>;
|
|
487
|
+
type WithVirtualPropsIfObject<TResult> = TResult extends object ? WithVirtualProps<TResult, string | number> : TResult;
|
|
488
|
+
type PrettifyIfPlainObject<T> = IsPlainObject<T> extends true ? Prettify<T> : T;
|
|
489
|
+
type ResultValue<TContext extends Context> = TContext[`hasResult`] extends true ? WithVirtualPropsIfObject<TContext[`result`]> : TContext[`hasJoins`] extends true ? TContext[`schema`] : TContext[`schema`][TContext[`fromSourceName`]];
|
|
445
490
|
/**
|
|
446
491
|
* GetResult - Determines the final result type of a query
|
|
447
492
|
*
|
|
@@ -467,7 +512,17 @@ export type InferResultType<TContext extends Context> = TContext extends SingleR
|
|
|
467
512
|
* The `Prettify` wrapper ensures clean type display in IDEs by flattening
|
|
468
513
|
* complex intersection types into readable object types.
|
|
469
514
|
*/
|
|
470
|
-
export type
|
|
515
|
+
export type GetRawResult<TContext extends Context> = ResultValue<TContext>;
|
|
516
|
+
export type GetResult<TContext extends Context> = Prettify<ResultValue<TContext>>;
|
|
517
|
+
type IsExactlyContext<TContext extends Context> = [Context] extends [TContext] ? [TContext] extends [Context] ? true : false : false;
|
|
518
|
+
type RootScalarResultError = {
|
|
519
|
+
readonly __tanstackDbRootQueryError__: `Top-level scalar results are not supported by createLiveQueryCollection() or queryOnce(). Return an object, or use the scalar query inside toArray(...) or concat(toArray(...)).`;
|
|
520
|
+
};
|
|
521
|
+
export type RootObjectResultConstraint<TContext extends Context> = IsExactlyContext<TContext> extends true ? unknown : GetResult<TContext> extends object ? unknown : RootScalarResultError;
|
|
522
|
+
type ContextFromQueryBuilder<TQuery extends QueryBuilder<any>> = TQuery extends QueryBuilder<infer TContext> ? TContext : never;
|
|
523
|
+
export type RootQueryBuilder<TQuery extends QueryBuilder<any>> = TQuery & RootObjectResultConstraint<ContextFromQueryBuilder<TQuery>>;
|
|
524
|
+
export type RootQueryFn<TQuery extends QueryBuilder<any>> = (q: InitialQueryBuilder) => RootQueryBuilder<TQuery>;
|
|
525
|
+
export type RootQueryResult<TContext extends Context> = IsExactlyContext<TContext> extends true ? any : GetResult<TContext> extends object ? GetResult<TContext> : never;
|
|
471
526
|
/**
|
|
472
527
|
* ApplyJoinOptionalityToSchema - Legacy helper for complex join scenarios
|
|
473
528
|
*
|
|
@@ -556,7 +611,7 @@ export type MergeContextForJoinCallback<TContext extends Context, TNewSchema ext
|
|
|
556
611
|
hasJoins: true;
|
|
557
612
|
joinTypes: TContext[`joinTypes`] extends Record<string, any> ? TContext[`joinTypes`] : {};
|
|
558
613
|
result: TContext[`result`];
|
|
559
|
-
}
|
|
614
|
+
} & PreserveHasResultFlag<TContext[`hasResult`]>;
|
|
560
615
|
/**
|
|
561
616
|
* WithResult - Updates a context with a new result type after select()
|
|
562
617
|
*
|
|
@@ -571,8 +626,9 @@ export type MergeContextForJoinCallback<TContext extends Context, TNewSchema ext
|
|
|
571
626
|
* The double `Prettify` ensures both the overall context and the nested
|
|
572
627
|
* result type display cleanly in IDEs.
|
|
573
628
|
*/
|
|
574
|
-
export type WithResult<TContext extends Context, TResult> = Prettify<Omit<TContext, `result`> & {
|
|
575
|
-
result:
|
|
629
|
+
export type WithResult<TContext extends Context, TResult> = Prettify<Omit<TContext, `result` | `hasResult`> & {
|
|
630
|
+
result: PrettifyIfPlainObject<TResult>;
|
|
631
|
+
hasResult: true;
|
|
576
632
|
}>;
|
|
577
633
|
/**
|
|
578
634
|
* Prettify - Utility type for clean IDE display
|
|
@@ -582,8 +638,17 @@ export type Prettify<T> = {
|
|
|
582
638
|
} & {};
|
|
583
639
|
/**
|
|
584
640
|
* IsPlainObject - Utility type to check if T is a plain object
|
|
641
|
+
*
|
|
642
|
+
* Returns `false` for:
|
|
643
|
+
* - Arrays (ReadonlyArray)
|
|
644
|
+
* - JavaScript built-ins (Date, Map, Set, etc.)
|
|
645
|
+
* - Objects with `Symbol.toStringTag` (class instances like Temporal types,
|
|
646
|
+
* TypedArrays not already in JsBuiltIns, etc.) — these are not plain data objects
|
|
585
647
|
*/
|
|
586
|
-
type IsPlainObject<T> = T extends unknown ? T extends object ? T extends ReadonlyArray<any> ? false : T extends JsBuiltIns ? false :
|
|
648
|
+
type IsPlainObject<T> = T extends unknown ? T extends object ? T extends ReadonlyArray<any> ? false : T extends JsBuiltIns ? false : T extends {
|
|
649
|
+
readonly [Symbol.toStringTag]: string;
|
|
650
|
+
} ? false : true : false : false;
|
|
651
|
+
type IsAny<T> = 0 extends 1 & T ? true : false;
|
|
587
652
|
/**
|
|
588
653
|
* JsBuiltIns - List of JavaScript built-ins
|
|
589
654
|
*/
|
|
@@ -3,6 +3,25 @@ import { normalizeValue, areValuesEqual } from "../../utils/comparison.js";
|
|
|
3
3
|
function isUnknown(value) {
|
|
4
4
|
return value === null || value === void 0;
|
|
5
5
|
}
|
|
6
|
+
function toDateValue(value) {
|
|
7
|
+
if (value instanceof Date) {
|
|
8
|
+
return Number.isNaN(value.getTime()) ? null : value;
|
|
9
|
+
}
|
|
10
|
+
if (typeof value === `string` || typeof value === `number`) {
|
|
11
|
+
const parsed = new Date(value);
|
|
12
|
+
return Number.isNaN(parsed.getTime()) ? null : parsed;
|
|
13
|
+
}
|
|
14
|
+
return null;
|
|
15
|
+
}
|
|
16
|
+
function evaluateStrftime(format, date) {
|
|
17
|
+
if (format === `%Y-%m-%d`) {
|
|
18
|
+
return date.toISOString().slice(0, 10);
|
|
19
|
+
}
|
|
20
|
+
if (format === `%Y-%m-%dT%H:%M:%fZ`) {
|
|
21
|
+
return date.toISOString();
|
|
22
|
+
}
|
|
23
|
+
return date.toISOString();
|
|
24
|
+
}
|
|
6
25
|
function toBooleanPredicate(result) {
|
|
7
26
|
return result === true;
|
|
8
27
|
}
|
|
@@ -343,6 +362,38 @@ function compileFunction(func, isSingleRow) {
|
|
|
343
362
|
return divisor !== 0 ? (a ?? 0) / divisor : null;
|
|
344
363
|
};
|
|
345
364
|
}
|
|
365
|
+
case `date`: {
|
|
366
|
+
const arg = compiledArgs[0];
|
|
367
|
+
return (data) => {
|
|
368
|
+
const value = arg(data);
|
|
369
|
+
const dateValue = toDateValue(value);
|
|
370
|
+
return dateValue ? dateValue.toISOString().slice(0, 10) : null;
|
|
371
|
+
};
|
|
372
|
+
}
|
|
373
|
+
case `datetime`: {
|
|
374
|
+
const arg = compiledArgs[0];
|
|
375
|
+
return (data) => {
|
|
376
|
+
const value = arg(data);
|
|
377
|
+
const dateValue = toDateValue(value);
|
|
378
|
+
return dateValue ? dateValue.toISOString() : null;
|
|
379
|
+
};
|
|
380
|
+
}
|
|
381
|
+
case `strftime`: {
|
|
382
|
+
const formatArg = compiledArgs[0];
|
|
383
|
+
const sourceArg = compiledArgs[1];
|
|
384
|
+
return (data) => {
|
|
385
|
+
const format = formatArg(data);
|
|
386
|
+
if (typeof format !== `string`) {
|
|
387
|
+
return null;
|
|
388
|
+
}
|
|
389
|
+
const sourceValue = sourceArg(data);
|
|
390
|
+
const dateValue = toDateValue(sourceValue);
|
|
391
|
+
if (!dateValue) {
|
|
392
|
+
return null;
|
|
393
|
+
}
|
|
394
|
+
return evaluateStrftime(format, dateValue);
|
|
395
|
+
};
|
|
396
|
+
}
|
|
346
397
|
// Null/undefined checking functions
|
|
347
398
|
case `isUndefined`: {
|
|
348
399
|
const arg = compiledArgs[0];
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"evaluators.js","sources":["../../../../src/query/compiler/evaluators.ts"],"sourcesContent":["import {\n EmptyReferencePathError,\n UnknownExpressionTypeError,\n UnknownFunctionError,\n} from '../../errors.js'\nimport { areValuesEqual, normalizeValue } from '../../utils/comparison.js'\nimport type { BasicExpression, Func, PropRef } from '../ir.js'\nimport type { NamespacedRow } from '../../types.js'\n\n/**\n * Helper function to check if a value is null or undefined (represents UNKNOWN in 3-valued logic)\n */\nfunction isUnknown(value: any): boolean {\n return value === null || value === undefined\n}\n\n/**\n * Converts a 3-valued logic result to a boolean for use in WHERE/HAVING filters.\n * In SQL, UNKNOWN (null) values in WHERE clauses exclude rows, matching false behavior.\n *\n * @param result - The 3-valued logic result: true, false, or null (UNKNOWN)\n * @returns true only if result is explicitly true, false otherwise\n *\n * Truth table:\n * - true → true (include row)\n * - false → false (exclude row)\n * - null (UNKNOWN) → false (exclude row, matching SQL behavior)\n */\nexport function toBooleanPredicate(result: boolean | null): boolean {\n return result === true\n}\n\n/**\n * Compiled expression evaluator function type\n */\nexport type CompiledExpression = (namespacedRow: NamespacedRow) => any\n\n/**\n * Compiled single-row expression evaluator function type\n */\nexport type CompiledSingleRowExpression = (item: Record<string, unknown>) => any\n\n/**\n * Compiles an expression into an optimized evaluator function.\n * This eliminates branching during evaluation by pre-compiling the expression structure.\n */\nexport function compileExpression(\n expr: BasicExpression,\n isSingleRow: boolean = false,\n): CompiledExpression | CompiledSingleRowExpression {\n const compiledFn = compileExpressionInternal(expr, isSingleRow)\n return compiledFn\n}\n\n/**\n * Compiles a single-row expression into an optimized evaluator function.\n */\nexport function compileSingleRowExpression(\n expr: BasicExpression,\n): CompiledSingleRowExpression {\n const compiledFn = compileExpressionInternal(expr, true)\n return compiledFn as CompiledSingleRowExpression\n}\n\n/**\n * Internal unified expression compiler that handles both namespaced and single-row evaluation\n */\nfunction compileExpressionInternal(\n expr: BasicExpression,\n isSingleRow: boolean,\n): (data: any) => any {\n switch (expr.type) {\n case `val`: {\n // For constant values, return a function that just returns the value\n const value = expr.value\n return () => value\n }\n\n case `ref`: {\n // For references, compile based on evaluation mode\n return isSingleRow ? compileSingleRowRef(expr) : compileRef(expr)\n }\n\n case `func`: {\n // For functions, use the unified compiler\n return compileFunction(expr, isSingleRow)\n }\n\n default:\n throw new UnknownExpressionTypeError((expr as any).type)\n }\n}\n\n/**\n * Compiles a reference expression into an optimized evaluator\n */\nfunction compileRef(ref: PropRef): CompiledExpression {\n const [namespace, ...propertyPath] = ref.path\n\n if (!namespace) {\n throw new EmptyReferencePathError()\n }\n\n // Handle $selected namespace - references SELECT result fields\n if (namespace === `$selected`) {\n // Access $selected directly\n if (propertyPath.length === 0) {\n // Just $selected - return entire $selected object\n return (namespacedRow) => (namespacedRow as any).$selected\n } else if (propertyPath.length === 1) {\n // Single property access - most common case\n const prop = propertyPath[0]!\n return (namespacedRow) => {\n const selectResults = (namespacedRow as any).$selected\n return selectResults?.[prop]\n }\n } else {\n // Multiple property navigation (nested SELECT fields)\n return (namespacedRow) => {\n const selectResults = (namespacedRow as any).$selected\n if (selectResults === undefined) {\n return undefined\n }\n\n let value: any = selectResults\n for (const prop of propertyPath) {\n if (value == null) {\n return value\n }\n value = value[prop]\n }\n return value\n }\n }\n }\n\n // Handle table alias namespace (existing logic)\n const tableAlias = namespace\n\n // Pre-compile the property path navigation\n if (propertyPath.length === 0) {\n // Simple table reference\n return (namespacedRow) => namespacedRow[tableAlias]\n } else if (propertyPath.length === 1) {\n // Single property access - most common case\n const prop = propertyPath[0]!\n return (namespacedRow) => {\n const tableData = namespacedRow[tableAlias]\n return tableData?.[prop]\n }\n } else {\n // Multiple property navigation\n return (namespacedRow) => {\n const tableData = namespacedRow[tableAlias]\n if (tableData === undefined) {\n return undefined\n }\n\n let value: any = tableData\n for (const prop of propertyPath) {\n if (value == null) {\n return value\n }\n value = value[prop]\n }\n return value\n }\n }\n}\n\n/**\n * Compiles a reference expression for single-row evaluation\n */\nfunction compileSingleRowRef(ref: PropRef): CompiledSingleRowExpression {\n const propertyPath = ref.path\n\n // This function works for all path lengths including empty path\n return (item) => {\n let value: any = item\n for (const prop of propertyPath) {\n if (value == null) {\n return value\n }\n value = value[prop]\n }\n return value\n }\n}\n\n/**\n * Compiles a function expression for both namespaced and single-row evaluation\n */\nfunction compileFunction(func: Func, isSingleRow: boolean): (data: any) => any {\n // Pre-compile all arguments using the appropriate compiler\n const compiledArgs = func.args.map((arg) =>\n compileExpressionInternal(arg, isSingleRow),\n )\n\n switch (func.name) {\n // Comparison operators\n case `eq`: {\n const argA = compiledArgs[0]!\n const argB = compiledArgs[1]!\n return (data) => {\n const a = normalizeValue(argA(data))\n const b = normalizeValue(argB(data))\n // In 3-valued logic, any comparison with null/undefined returns UNKNOWN\n if (isUnknown(a) || isUnknown(b)) {\n return null\n }\n // Use areValuesEqual for proper Uint8Array/Buffer comparison\n return areValuesEqual(a, b)\n }\n }\n case `gt`: {\n const argA = compiledArgs[0]!\n const argB = compiledArgs[1]!\n return (data) => {\n const a = argA(data)\n const b = argB(data)\n // In 3-valued logic, any comparison with null/undefined returns UNKNOWN\n if (isUnknown(a) || isUnknown(b)) {\n return null\n }\n return a > b\n }\n }\n case `gte`: {\n const argA = compiledArgs[0]!\n const argB = compiledArgs[1]!\n return (data) => {\n const a = argA(data)\n const b = argB(data)\n // In 3-valued logic, any comparison with null/undefined returns UNKNOWN\n if (isUnknown(a) || isUnknown(b)) {\n return null\n }\n return a >= b\n }\n }\n case `lt`: {\n const argA = compiledArgs[0]!\n const argB = compiledArgs[1]!\n return (data) => {\n const a = argA(data)\n const b = argB(data)\n // In 3-valued logic, any comparison with null/undefined returns UNKNOWN\n if (isUnknown(a) || isUnknown(b)) {\n return null\n }\n return a < b\n }\n }\n case `lte`: {\n const argA = compiledArgs[0]!\n const argB = compiledArgs[1]!\n return (data) => {\n const a = argA(data)\n const b = argB(data)\n // In 3-valued logic, any comparison with null/undefined returns UNKNOWN\n if (isUnknown(a) || isUnknown(b)) {\n return null\n }\n return a <= b\n }\n }\n\n // Boolean operators\n case `and`:\n return (data) => {\n // 3-valued logic for AND:\n // - false AND anything = false (short-circuit)\n // - null AND false = false\n // - null AND anything (except false) = null\n // - anything (except false) AND null = null\n // - true AND true = true\n let hasUnknown = false\n for (const compiledArg of compiledArgs) {\n const result = compiledArg(data)\n if (result === false) {\n return false\n }\n if (isUnknown(result)) {\n hasUnknown = true\n }\n }\n // If we got here, no operand was false\n // If any operand was null, return null (UNKNOWN)\n if (hasUnknown) {\n return null\n }\n\n return true\n }\n case `or`:\n return (data) => {\n // 3-valued logic for OR:\n // - true OR anything = true (short-circuit)\n // - null OR anything (except true) = null\n // - false OR false = false\n let hasUnknown = false\n for (const compiledArg of compiledArgs) {\n const result = compiledArg(data)\n if (result === true) {\n return true\n }\n if (isUnknown(result)) {\n hasUnknown = true\n }\n }\n // If we got here, no operand was true\n // If any operand was null, return null (UNKNOWN)\n if (hasUnknown) {\n return null\n }\n\n return false\n }\n case `not`: {\n const arg = compiledArgs[0]!\n return (data) => {\n // 3-valued logic for NOT:\n // - NOT null = null\n // - NOT true = false\n // - NOT false = true\n const result = arg(data)\n if (isUnknown(result)) {\n return null\n }\n return !result\n }\n }\n\n // Array operators\n case `in`: {\n const valueEvaluator = compiledArgs[0]!\n const arrayEvaluator = compiledArgs[1]!\n return (data) => {\n const value = normalizeValue(valueEvaluator(data))\n const array = arrayEvaluator(data)\n // In 3-valued logic, if the value is null/undefined, return UNKNOWN\n if (isUnknown(value)) {\n return null\n }\n if (!Array.isArray(array)) {\n return false\n }\n return array.some((item) => normalizeValue(item) === value)\n }\n }\n\n // String operators\n case `like`: {\n const valueEvaluator = compiledArgs[0]!\n const patternEvaluator = compiledArgs[1]!\n return (data) => {\n const value = valueEvaluator(data)\n const pattern = patternEvaluator(data)\n // In 3-valued logic, if value or pattern is null/undefined, return UNKNOWN\n if (isUnknown(value) || isUnknown(pattern)) {\n return null\n }\n return evaluateLike(value, pattern, false)\n }\n }\n case `ilike`: {\n const valueEvaluator = compiledArgs[0]!\n const patternEvaluator = compiledArgs[1]!\n return (data) => {\n const value = valueEvaluator(data)\n const pattern = patternEvaluator(data)\n // In 3-valued logic, if value or pattern is null/undefined, return UNKNOWN\n if (isUnknown(value) || isUnknown(pattern)) {\n return null\n }\n return evaluateLike(value, pattern, true)\n }\n }\n\n // String functions\n case `upper`: {\n const arg = compiledArgs[0]!\n return (data) => {\n const value = arg(data)\n return typeof value === `string` ? value.toUpperCase() : value\n }\n }\n case `lower`: {\n const arg = compiledArgs[0]!\n return (data) => {\n const value = arg(data)\n return typeof value === `string` ? value.toLowerCase() : value\n }\n }\n case `length`: {\n const arg = compiledArgs[0]!\n return (data) => {\n const value = arg(data)\n if (typeof value === `string`) {\n return value.length\n }\n if (Array.isArray(value)) {\n return value.length\n }\n return 0\n }\n }\n case `concat`:\n return (data) => {\n return compiledArgs\n .map((evaluator) => {\n const arg = evaluator(data)\n try {\n return String(arg ?? ``)\n } catch {\n try {\n return JSON.stringify(arg) || ``\n } catch {\n return `[object]`\n }\n }\n })\n .join(``)\n }\n case `coalesce`:\n return (data) => {\n for (const evaluator of compiledArgs) {\n const value = evaluator(data)\n if (value !== null && value !== undefined) {\n return value\n }\n }\n return null\n }\n\n // Math functions\n case `add`: {\n const argA = compiledArgs[0]!\n const argB = compiledArgs[1]!\n return (data) => {\n const a = argA(data)\n const b = argB(data)\n return (a ?? 0) + (b ?? 0)\n }\n }\n case `subtract`: {\n const argA = compiledArgs[0]!\n const argB = compiledArgs[1]!\n return (data) => {\n const a = argA(data)\n const b = argB(data)\n return (a ?? 0) - (b ?? 0)\n }\n }\n case `multiply`: {\n const argA = compiledArgs[0]!\n const argB = compiledArgs[1]!\n return (data) => {\n const a = argA(data)\n const b = argB(data)\n return (a ?? 0) * (b ?? 0)\n }\n }\n case `divide`: {\n const argA = compiledArgs[0]!\n const argB = compiledArgs[1]!\n return (data) => {\n const a = argA(data)\n const b = argB(data)\n const divisor = b ?? 0\n return divisor !== 0 ? (a ?? 0) / divisor : null\n }\n }\n\n // Null/undefined checking functions\n case `isUndefined`: {\n const arg = compiledArgs[0]!\n return (data) => {\n const value = arg(data)\n return value === undefined\n }\n }\n case `isNull`: {\n const arg = compiledArgs[0]!\n return (data) => {\n const value = arg(data)\n return value === null\n }\n }\n\n default:\n throw new UnknownFunctionError(func.name)\n }\n}\n\n/**\n * Evaluates LIKE/ILIKE patterns\n */\nfunction evaluateLike(\n value: any,\n pattern: any,\n caseInsensitive: boolean,\n): boolean {\n if (typeof value !== `string` || typeof pattern !== `string`) {\n return false\n }\n\n const searchValue = caseInsensitive ? value.toLowerCase() : value\n const searchPattern = caseInsensitive ? pattern.toLowerCase() : pattern\n\n // Convert SQL LIKE pattern to regex\n // First escape all regex special chars except % and _\n let regexPattern = searchPattern.replace(/[.*+?^${}()|[\\]\\\\]/g, `\\\\$&`)\n\n // Then convert SQL wildcards to regex\n regexPattern = regexPattern.replace(/%/g, `.*`) // % matches any sequence\n regexPattern = regexPattern.replace(/_/g, `.`) // _ matches any single char\n\n // 's' (dotAll flag) makes '.' match all characters including line terminations\n const regex = new RegExp(`^${regexPattern}$`, 's')\n return regex.test(searchValue)\n}\n"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"evaluators.js","sources":["../../../../src/query/compiler/evaluators.ts"],"sourcesContent":["import {\n EmptyReferencePathError,\n UnknownExpressionTypeError,\n UnknownFunctionError,\n} from '../../errors.js'\nimport { areValuesEqual, normalizeValue } from '../../utils/comparison.js'\nimport type { BasicExpression, Func, PropRef } from '../ir.js'\nimport type { NamespacedRow } from '../../types.js'\n\n/**\n * Helper function to check if a value is null or undefined (represents UNKNOWN in 3-valued logic)\n */\nfunction isUnknown(value: any): boolean {\n return value === null || value === undefined\n}\n\nfunction toDateValue(value: any): Date | null {\n if (value instanceof Date) {\n return Number.isNaN(value.getTime()) ? null : value\n }\n\n if (typeof value === `string` || typeof value === `number`) {\n const parsed = new Date(value)\n return Number.isNaN(parsed.getTime()) ? null : parsed\n }\n\n return null\n}\n\nfunction evaluateStrftime(format: string, date: Date): string {\n if (format === `%Y-%m-%d`) {\n return date.toISOString().slice(0, 10)\n }\n\n if (format === `%Y-%m-%dT%H:%M:%fZ`) {\n return date.toISOString()\n }\n\n return date.toISOString()\n}\n\n/**\n * Converts a 3-valued logic result to a boolean for use in WHERE/HAVING filters.\n * In SQL, UNKNOWN (null) values in WHERE clauses exclude rows, matching false behavior.\n *\n * @param result - The 3-valued logic result: true, false, or null (UNKNOWN)\n * @returns true only if result is explicitly true, false otherwise\n *\n * Truth table:\n * - true → true (include row)\n * - false → false (exclude row)\n * - null (UNKNOWN) → false (exclude row, matching SQL behavior)\n */\nexport function toBooleanPredicate(result: boolean | null): boolean {\n return result === true\n}\n\n/**\n * Compiled expression evaluator function type\n */\nexport type CompiledExpression = (namespacedRow: NamespacedRow) => any\n\n/**\n * Compiled single-row expression evaluator function type\n */\nexport type CompiledSingleRowExpression = (item: Record<string, unknown>) => any\n\n/**\n * Compiles an expression into an optimized evaluator function.\n * This eliminates branching during evaluation by pre-compiling the expression structure.\n */\nexport function compileExpression(\n expr: BasicExpression,\n isSingleRow: boolean = false,\n): CompiledExpression | CompiledSingleRowExpression {\n const compiledFn = compileExpressionInternal(expr, isSingleRow)\n return compiledFn\n}\n\n/**\n * Compiles a single-row expression into an optimized evaluator function.\n */\nexport function compileSingleRowExpression(\n expr: BasicExpression,\n): CompiledSingleRowExpression {\n const compiledFn = compileExpressionInternal(expr, true)\n return compiledFn as CompiledSingleRowExpression\n}\n\n/**\n * Internal unified expression compiler that handles both namespaced and single-row evaluation\n */\nfunction compileExpressionInternal(\n expr: BasicExpression,\n isSingleRow: boolean,\n): (data: any) => any {\n switch (expr.type) {\n case `val`: {\n // For constant values, return a function that just returns the value\n const value = expr.value\n return () => value\n }\n\n case `ref`: {\n // For references, compile based on evaluation mode\n return isSingleRow ? compileSingleRowRef(expr) : compileRef(expr)\n }\n\n case `func`: {\n // For functions, use the unified compiler\n return compileFunction(expr, isSingleRow)\n }\n\n default:\n throw new UnknownExpressionTypeError((expr as any).type)\n }\n}\n\n/**\n * Compiles a reference expression into an optimized evaluator\n */\nfunction compileRef(ref: PropRef): CompiledExpression {\n const [namespace, ...propertyPath] = ref.path\n\n if (!namespace) {\n throw new EmptyReferencePathError()\n }\n\n // Handle $selected namespace - references SELECT result fields\n if (namespace === `$selected`) {\n // Access $selected directly\n if (propertyPath.length === 0) {\n // Just $selected - return entire $selected object\n return (namespacedRow) => (namespacedRow as any).$selected\n } else if (propertyPath.length === 1) {\n // Single property access - most common case\n const prop = propertyPath[0]!\n return (namespacedRow) => {\n const selectResults = (namespacedRow as any).$selected\n return selectResults?.[prop]\n }\n } else {\n // Multiple property navigation (nested SELECT fields)\n return (namespacedRow) => {\n const selectResults = (namespacedRow as any).$selected\n if (selectResults === undefined) {\n return undefined\n }\n\n let value: any = selectResults\n for (const prop of propertyPath) {\n if (value == null) {\n return value\n }\n value = value[prop]\n }\n return value\n }\n }\n }\n\n // Handle table alias namespace (existing logic)\n const tableAlias = namespace\n\n // Pre-compile the property path navigation\n if (propertyPath.length === 0) {\n // Simple table reference\n return (namespacedRow) => namespacedRow[tableAlias]\n } else if (propertyPath.length === 1) {\n // Single property access - most common case\n const prop = propertyPath[0]!\n return (namespacedRow) => {\n const tableData = namespacedRow[tableAlias]\n return tableData?.[prop]\n }\n } else {\n // Multiple property navigation\n return (namespacedRow) => {\n const tableData = namespacedRow[tableAlias]\n if (tableData === undefined) {\n return undefined\n }\n\n let value: any = tableData\n for (const prop of propertyPath) {\n if (value == null) {\n return value\n }\n value = value[prop]\n }\n return value\n }\n }\n}\n\n/**\n * Compiles a reference expression for single-row evaluation\n */\nfunction compileSingleRowRef(ref: PropRef): CompiledSingleRowExpression {\n const propertyPath = ref.path\n\n // This function works for all path lengths including empty path\n return (item) => {\n let value: any = item\n for (const prop of propertyPath) {\n if (value == null) {\n return value\n }\n value = value[prop]\n }\n return value\n }\n}\n\n/**\n * Compiles a function expression for both namespaced and single-row evaluation\n */\nfunction compileFunction(func: Func, isSingleRow: boolean): (data: any) => any {\n // Pre-compile all arguments using the appropriate compiler\n const compiledArgs = func.args.map((arg) =>\n compileExpressionInternal(arg, isSingleRow),\n )\n\n switch (func.name) {\n // Comparison operators\n case `eq`: {\n const argA = compiledArgs[0]!\n const argB = compiledArgs[1]!\n return (data) => {\n const a = normalizeValue(argA(data))\n const b = normalizeValue(argB(data))\n // In 3-valued logic, any comparison with null/undefined returns UNKNOWN\n if (isUnknown(a) || isUnknown(b)) {\n return null\n }\n // Use areValuesEqual for proper Uint8Array/Buffer comparison\n return areValuesEqual(a, b)\n }\n }\n case `gt`: {\n const argA = compiledArgs[0]!\n const argB = compiledArgs[1]!\n return (data) => {\n const a = argA(data)\n const b = argB(data)\n // In 3-valued logic, any comparison with null/undefined returns UNKNOWN\n if (isUnknown(a) || isUnknown(b)) {\n return null\n }\n return a > b\n }\n }\n case `gte`: {\n const argA = compiledArgs[0]!\n const argB = compiledArgs[1]!\n return (data) => {\n const a = argA(data)\n const b = argB(data)\n // In 3-valued logic, any comparison with null/undefined returns UNKNOWN\n if (isUnknown(a) || isUnknown(b)) {\n return null\n }\n return a >= b\n }\n }\n case `lt`: {\n const argA = compiledArgs[0]!\n const argB = compiledArgs[1]!\n return (data) => {\n const a = argA(data)\n const b = argB(data)\n // In 3-valued logic, any comparison with null/undefined returns UNKNOWN\n if (isUnknown(a) || isUnknown(b)) {\n return null\n }\n return a < b\n }\n }\n case `lte`: {\n const argA = compiledArgs[0]!\n const argB = compiledArgs[1]!\n return (data) => {\n const a = argA(data)\n const b = argB(data)\n // In 3-valued logic, any comparison with null/undefined returns UNKNOWN\n if (isUnknown(a) || isUnknown(b)) {\n return null\n }\n return a <= b\n }\n }\n\n // Boolean operators\n case `and`:\n return (data) => {\n // 3-valued logic for AND:\n // - false AND anything = false (short-circuit)\n // - null AND false = false\n // - null AND anything (except false) = null\n // - anything (except false) AND null = null\n // - true AND true = true\n let hasUnknown = false\n for (const compiledArg of compiledArgs) {\n const result = compiledArg(data)\n if (result === false) {\n return false\n }\n if (isUnknown(result)) {\n hasUnknown = true\n }\n }\n // If we got here, no operand was false\n // If any operand was null, return null (UNKNOWN)\n if (hasUnknown) {\n return null\n }\n\n return true\n }\n case `or`:\n return (data) => {\n // 3-valued logic for OR:\n // - true OR anything = true (short-circuit)\n // - null OR anything (except true) = null\n // - false OR false = false\n let hasUnknown = false\n for (const compiledArg of compiledArgs) {\n const result = compiledArg(data)\n if (result === true) {\n return true\n }\n if (isUnknown(result)) {\n hasUnknown = true\n }\n }\n // If we got here, no operand was true\n // If any operand was null, return null (UNKNOWN)\n if (hasUnknown) {\n return null\n }\n\n return false\n }\n case `not`: {\n const arg = compiledArgs[0]!\n return (data) => {\n // 3-valued logic for NOT:\n // - NOT null = null\n // - NOT true = false\n // - NOT false = true\n const result = arg(data)\n if (isUnknown(result)) {\n return null\n }\n return !result\n }\n }\n\n // Array operators\n case `in`: {\n const valueEvaluator = compiledArgs[0]!\n const arrayEvaluator = compiledArgs[1]!\n return (data) => {\n const value = normalizeValue(valueEvaluator(data))\n const array = arrayEvaluator(data)\n // In 3-valued logic, if the value is null/undefined, return UNKNOWN\n if (isUnknown(value)) {\n return null\n }\n if (!Array.isArray(array)) {\n return false\n }\n return array.some((item) => normalizeValue(item) === value)\n }\n }\n\n // String operators\n case `like`: {\n const valueEvaluator = compiledArgs[0]!\n const patternEvaluator = compiledArgs[1]!\n return (data) => {\n const value = valueEvaluator(data)\n const pattern = patternEvaluator(data)\n // In 3-valued logic, if value or pattern is null/undefined, return UNKNOWN\n if (isUnknown(value) || isUnknown(pattern)) {\n return null\n }\n return evaluateLike(value, pattern, false)\n }\n }\n case `ilike`: {\n const valueEvaluator = compiledArgs[0]!\n const patternEvaluator = compiledArgs[1]!\n return (data) => {\n const value = valueEvaluator(data)\n const pattern = patternEvaluator(data)\n // In 3-valued logic, if value or pattern is null/undefined, return UNKNOWN\n if (isUnknown(value) || isUnknown(pattern)) {\n return null\n }\n return evaluateLike(value, pattern, true)\n }\n }\n\n // String functions\n case `upper`: {\n const arg = compiledArgs[0]!\n return (data) => {\n const value = arg(data)\n return typeof value === `string` ? value.toUpperCase() : value\n }\n }\n case `lower`: {\n const arg = compiledArgs[0]!\n return (data) => {\n const value = arg(data)\n return typeof value === `string` ? value.toLowerCase() : value\n }\n }\n case `length`: {\n const arg = compiledArgs[0]!\n return (data) => {\n const value = arg(data)\n if (typeof value === `string`) {\n return value.length\n }\n if (Array.isArray(value)) {\n return value.length\n }\n return 0\n }\n }\n case `concat`:\n return (data) => {\n return compiledArgs\n .map((evaluator) => {\n const arg = evaluator(data)\n try {\n return String(arg ?? ``)\n } catch {\n try {\n return JSON.stringify(arg) || ``\n } catch {\n return `[object]`\n }\n }\n })\n .join(``)\n }\n case `coalesce`:\n return (data) => {\n for (const evaluator of compiledArgs) {\n const value = evaluator(data)\n if (value !== null && value !== undefined) {\n return value\n }\n }\n return null\n }\n\n // Math functions\n case `add`: {\n const argA = compiledArgs[0]!\n const argB = compiledArgs[1]!\n return (data) => {\n const a = argA(data)\n const b = argB(data)\n return (a ?? 0) + (b ?? 0)\n }\n }\n case `subtract`: {\n const argA = compiledArgs[0]!\n const argB = compiledArgs[1]!\n return (data) => {\n const a = argA(data)\n const b = argB(data)\n return (a ?? 0) - (b ?? 0)\n }\n }\n case `multiply`: {\n const argA = compiledArgs[0]!\n const argB = compiledArgs[1]!\n return (data) => {\n const a = argA(data)\n const b = argB(data)\n return (a ?? 0) * (b ?? 0)\n }\n }\n case `divide`: {\n const argA = compiledArgs[0]!\n const argB = compiledArgs[1]!\n return (data) => {\n const a = argA(data)\n const b = argB(data)\n const divisor = b ?? 0\n return divisor !== 0 ? (a ?? 0) / divisor : null\n }\n }\n case `date`: {\n const arg = compiledArgs[0]!\n return (data) => {\n const value = arg(data)\n const dateValue = toDateValue(value)\n return dateValue ? dateValue.toISOString().slice(0, 10) : null\n }\n }\n case `datetime`: {\n const arg = compiledArgs[0]!\n return (data) => {\n const value = arg(data)\n const dateValue = toDateValue(value)\n return dateValue ? dateValue.toISOString() : null\n }\n }\n case `strftime`: {\n const formatArg = compiledArgs[0]!\n const sourceArg = compiledArgs[1]!\n return (data) => {\n const format = formatArg(data)\n if (typeof format !== `string`) {\n return null\n }\n const sourceValue = sourceArg(data)\n const dateValue = toDateValue(sourceValue)\n if (!dateValue) {\n return null\n }\n return evaluateStrftime(format, dateValue)\n }\n }\n\n // Null/undefined checking functions\n case `isUndefined`: {\n const arg = compiledArgs[0]!\n return (data) => {\n const value = arg(data)\n return value === undefined\n }\n }\n case `isNull`: {\n const arg = compiledArgs[0]!\n return (data) => {\n const value = arg(data)\n return value === null\n }\n }\n\n default:\n throw new UnknownFunctionError(func.name)\n }\n}\n\n/**\n * Evaluates LIKE/ILIKE patterns\n */\nfunction evaluateLike(\n value: any,\n pattern: any,\n caseInsensitive: boolean,\n): boolean {\n if (typeof value !== `string` || typeof pattern !== `string`) {\n return false\n }\n\n const searchValue = caseInsensitive ? value.toLowerCase() : value\n const searchPattern = caseInsensitive ? pattern.toLowerCase() : pattern\n\n // Convert SQL LIKE pattern to regex\n // First escape all regex special chars except % and _\n let regexPattern = searchPattern.replace(/[.*+?^${}()|[\\]\\\\]/g, `\\\\$&`)\n\n // Then convert SQL wildcards to regex\n regexPattern = regexPattern.replace(/%/g, `.*`) // % matches any sequence\n regexPattern = regexPattern.replace(/_/g, `.`) // _ matches any single char\n\n // 's' (dotAll flag) makes '.' match all characters including line terminations\n const regex = new RegExp(`^${regexPattern}$`, 's')\n return regex.test(searchValue)\n}\n"],"names":[],"mappings":"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"}
|
|
@@ -4,7 +4,7 @@ import { NamespacedAndKeyedStream } from '../../types.js';
|
|
|
4
4
|
* Processes the GROUP BY clause with optional HAVING and SELECT
|
|
5
5
|
* Works with the new $selected structure from early SELECT processing
|
|
6
6
|
*/
|
|
7
|
-
export declare function processGroupBy(pipeline: NamespacedAndKeyedStream, groupByClause: GroupBy, havingClauses?: Array<Having>, selectClause?: Select, fnHavingClauses?: Array<(row: any) => any
|
|
7
|
+
export declare function processGroupBy(pipeline: NamespacedAndKeyedStream, groupByClause: GroupBy, havingClauses?: Array<Having>, selectClause?: Select, fnHavingClauses?: Array<(row: any) => any>, aggregateCollectionId?: string, mainSource?: string): NamespacedAndKeyedStream;
|
|
8
8
|
/**
|
|
9
9
|
* Transforms expressions to replace aggregate functions with references to computed values.
|
|
10
10
|
*
|
|
@@ -25,4 +25,6 @@ export declare function replaceAggregatesByRefs(havingExpr: BasicExpression | Ag
|
|
|
25
25
|
* Returns true for a top-level Aggregate, or a Func whose args (recursively)
|
|
26
26
|
* contain an Aggregate. Safely returns false for nested Select objects.
|
|
27
27
|
*/
|
|
28
|
-
export declare function containsAggregate(expr: BasicExpression | Aggregate | Select
|
|
28
|
+
export declare function containsAggregate(expr: BasicExpression | Aggregate | Select | {
|
|
29
|
+
type: string;
|
|
30
|
+
}): boolean;
|
|
@@ -1,7 +1,34 @@
|
|
|
1
|
-
import { groupBy, map,
|
|
1
|
+
import { groupBy, map, serializeValue, filter, groupByOperators } from "@tanstack/db-ivm";
|
|
2
2
|
import { getHavingExpression, isExpressionLike, PropRef, Func } from "../ir.js";
|
|
3
3
|
import { UnsupportedAggregateFunctionError, UnknownHavingExpressionTypeError, AggregateFunctionNotInSelectError, NonAggregateExpressionNotInGroupByError } from "../../errors.js";
|
|
4
4
|
import { compileExpression, toBooleanPredicate } from "./evaluators.js";
|
|
5
|
+
const VIRTUAL_SYNCED_KEY = `__virtual_synced__`;
|
|
6
|
+
const VIRTUAL_HAS_LOCAL_KEY = `__virtual_has_local__`;
|
|
7
|
+
function getRowVirtualMetadata(row) {
|
|
8
|
+
let found = false;
|
|
9
|
+
let allSynced = true;
|
|
10
|
+
let hasLocal = false;
|
|
11
|
+
for (const [alias, value] of Object.entries(row)) {
|
|
12
|
+
if (alias === `$selected`) continue;
|
|
13
|
+
const asRecord = value;
|
|
14
|
+
const hasSyncedProp = `$synced` in asRecord;
|
|
15
|
+
const hasOriginProp = `$origin` in asRecord;
|
|
16
|
+
if (!hasSyncedProp && !hasOriginProp) {
|
|
17
|
+
continue;
|
|
18
|
+
}
|
|
19
|
+
found = true;
|
|
20
|
+
if (asRecord.$synced === false) {
|
|
21
|
+
allSynced = false;
|
|
22
|
+
}
|
|
23
|
+
if (asRecord.$origin === `local`) {
|
|
24
|
+
hasLocal = true;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
return {
|
|
28
|
+
synced: found ? allSynced : true,
|
|
29
|
+
hasLocal
|
|
30
|
+
};
|
|
31
|
+
}
|
|
5
32
|
const { sum, count, avg, min, max } = groupByOperators;
|
|
6
33
|
function validateAndCreateMapping(groupByClause, selectClause) {
|
|
7
34
|
const selectToGroupByIndex = /* @__PURE__ */ new Map();
|
|
@@ -23,9 +50,33 @@ function validateAndCreateMapping(groupByClause, selectClause) {
|
|
|
23
50
|
}
|
|
24
51
|
return { selectToGroupByIndex, groupByExpressions };
|
|
25
52
|
}
|
|
26
|
-
function processGroupBy(pipeline, groupByClause, havingClauses, selectClause, fnHavingClauses) {
|
|
53
|
+
function processGroupBy(pipeline, groupByClause, havingClauses, selectClause, fnHavingClauses, aggregateCollectionId, mainSource) {
|
|
54
|
+
const virtualAggregates = {
|
|
55
|
+
[VIRTUAL_SYNCED_KEY]: {
|
|
56
|
+
preMap: ([, row]) => getRowVirtualMetadata(row).synced,
|
|
57
|
+
reduce: (values) => {
|
|
58
|
+
for (const [isSynced, multiplicity] of values) {
|
|
59
|
+
if (!isSynced && multiplicity > 0) {
|
|
60
|
+
return false;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
return true;
|
|
64
|
+
}
|
|
65
|
+
},
|
|
66
|
+
[VIRTUAL_HAS_LOCAL_KEY]: {
|
|
67
|
+
preMap: ([, row]) => getRowVirtualMetadata(row).hasLocal,
|
|
68
|
+
reduce: (values) => {
|
|
69
|
+
for (const [isLocal, multiplicity] of values) {
|
|
70
|
+
if (isLocal && multiplicity > 0) {
|
|
71
|
+
return true;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
return false;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
};
|
|
27
78
|
if (groupByClause.length === 0) {
|
|
28
|
-
const aggregates2 =
|
|
79
|
+
const aggregates2 = virtualAggregates;
|
|
29
80
|
const wrappedAggExprs2 = {};
|
|
30
81
|
const aggCounter2 = { value: 0 };
|
|
31
82
|
if (selectClause) {
|
|
@@ -44,7 +95,10 @@ function processGroupBy(pipeline, groupByClause, havingClauses, selectClause, fn
|
|
|
44
95
|
}
|
|
45
96
|
}
|
|
46
97
|
}
|
|
47
|
-
const keyExtractor2 = () => ({
|
|
98
|
+
const keyExtractor2 = mainSource ? ([, row]) => ({
|
|
99
|
+
__singleGroup: true,
|
|
100
|
+
__correlationKey: row?.[mainSource]?.__correlationKey
|
|
101
|
+
}) : () => ({ __singleGroup: true });
|
|
48
102
|
pipeline = pipeline.pipe(
|
|
49
103
|
groupBy(keyExtractor2, aggregates2)
|
|
50
104
|
);
|
|
@@ -64,13 +118,22 @@ function processGroupBy(pipeline, groupByClause, havingClauses, selectClause, fn
|
|
|
64
118
|
wrappedAggExprs2
|
|
65
119
|
);
|
|
66
120
|
}
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
];
|
|
121
|
+
const correlationKey = mainSource ? aggregatedRow.__correlationKey : void 0;
|
|
122
|
+
const resultKey = correlationKey !== void 0 ? `single_group_${serializeValue(correlationKey)}` : `single_group`;
|
|
123
|
+
const resultRow = {
|
|
124
|
+
...aggregatedRow,
|
|
125
|
+
$selected: finalResults
|
|
126
|
+
};
|
|
127
|
+
const groupSynced = aggregatedRow[VIRTUAL_SYNCED_KEY];
|
|
128
|
+
const groupHasLocal = aggregatedRow[VIRTUAL_HAS_LOCAL_KEY];
|
|
129
|
+
resultRow.$synced = groupSynced ?? true;
|
|
130
|
+
resultRow.$origin = groupHasLocal ? `local` : `remote`;
|
|
131
|
+
resultRow.$key = resultKey;
|
|
132
|
+
resultRow.$collectionId = aggregateCollectionId ?? resultRow.$collectionId;
|
|
133
|
+
if (mainSource && correlationKey !== void 0) {
|
|
134
|
+
resultRow[mainSource] = { __correlationKey: correlationKey };
|
|
135
|
+
}
|
|
136
|
+
return [resultKey, resultRow];
|
|
74
137
|
})
|
|
75
138
|
);
|
|
76
139
|
if (havingClauses && havingClauses.length > 0) {
|
|
@@ -115,9 +178,12 @@ function processGroupBy(pipeline, groupByClause, havingClauses, selectClause, fn
|
|
|
115
178
|
const value = compiledExpr(namespacedRow);
|
|
116
179
|
key[`__key_${i}`] = value;
|
|
117
180
|
}
|
|
181
|
+
if (mainSource) {
|
|
182
|
+
key.__correlationKey = row?.[mainSource]?.__correlationKey;
|
|
183
|
+
}
|
|
118
184
|
return key;
|
|
119
185
|
};
|
|
120
|
-
const aggregates =
|
|
186
|
+
const aggregates = virtualAggregates;
|
|
121
187
|
const wrappedAggExprs = {};
|
|
122
188
|
const aggCounter = { value: 0 };
|
|
123
189
|
if (selectClause) {
|
|
@@ -164,23 +230,29 @@ function processGroupBy(pipeline, groupByClause, havingClauses, selectClause, fn
|
|
|
164
230
|
finalResults[`__key_${i}`] = aggregatedRow[`__key_${i}`];
|
|
165
231
|
}
|
|
166
232
|
}
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
const keyParts = [];
|
|
172
|
-
for (let i = 0; i < groupByClause.length; i++) {
|
|
173
|
-
keyParts.push(aggregatedRow[`__key_${i}`]);
|
|
174
|
-
}
|
|
175
|
-
finalKey = serializeValue(keyParts);
|
|
233
|
+
const correlationKey = mainSource ? aggregatedRow.__correlationKey : void 0;
|
|
234
|
+
const keyParts = [];
|
|
235
|
+
for (let i = 0; i < groupByClause.length; i++) {
|
|
236
|
+
keyParts.push(aggregatedRow[`__key_${i}`]);
|
|
176
237
|
}
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
238
|
+
if (correlationKey !== void 0) {
|
|
239
|
+
keyParts.push(correlationKey);
|
|
240
|
+
}
|
|
241
|
+
const finalKey = keyParts.length === 1 ? keyParts[0] : serializeValue(keyParts);
|
|
242
|
+
const resultRow = {
|
|
243
|
+
...aggregatedRow,
|
|
244
|
+
$selected: finalResults
|
|
245
|
+
};
|
|
246
|
+
const groupSynced = aggregatedRow[VIRTUAL_SYNCED_KEY];
|
|
247
|
+
const groupHasLocal = aggregatedRow[VIRTUAL_HAS_LOCAL_KEY];
|
|
248
|
+
resultRow.$synced = groupSynced ?? true;
|
|
249
|
+
resultRow.$origin = groupHasLocal ? `local` : `remote`;
|
|
250
|
+
resultRow.$key = finalKey;
|
|
251
|
+
resultRow.$collectionId = aggregateCollectionId ?? resultRow.$collectionId;
|
|
252
|
+
if (mainSource && correlationKey !== void 0) {
|
|
253
|
+
resultRow[mainSource] = { __correlationKey: correlationKey };
|
|
254
|
+
}
|
|
255
|
+
return [finalKey, resultRow];
|
|
184
256
|
})
|
|
185
257
|
);
|
|
186
258
|
if (havingClauses && havingClauses.length > 0) {
|
|
@@ -315,7 +387,7 @@ function containsAggregate(expr) {
|
|
|
315
387
|
if (expr.type === `agg`) {
|
|
316
388
|
return true;
|
|
317
389
|
}
|
|
318
|
-
if (expr.type === `func`) {
|
|
390
|
+
if (expr.type === `func` && `args` in expr) {
|
|
319
391
|
return expr.args.some(
|
|
320
392
|
(arg) => containsAggregate(arg)
|
|
321
393
|
);
|