@hypequery/datasets 0.21.0 → 0.23.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/README.md +6 -0
- package/dist/api.type-test.js +7 -0
- package/dist/cache/protocol-cache-keys.d.ts.map +1 -1
- package/dist/cache/protocol-cache-keys.js +5 -0
- package/dist/cache/query-signature.d.ts.map +1 -1
- package/dist/cache/query-signature.js +9 -0
- package/dist/catalog.d.ts +12 -0
- package/dist/catalog.d.ts.map +1 -1
- package/dist/catalog.js +14 -1
- package/dist/constants.d.ts +12 -1
- package/dist/constants.d.ts.map +1 -1
- package/dist/constants.js +24 -0
- package/dist/contract.d.ts +4 -1
- package/dist/contract.d.ts.map +1 -1
- package/dist/contract.js +4 -0
- package/dist/dataset-compilation.d.ts +50 -0
- package/dist/dataset-compilation.d.ts.map +1 -0
- package/dist/dataset-compilation.js +1 -0
- package/dist/dataset-query.d.ts +3 -3
- package/dist/dataset-query.d.ts.map +1 -1
- package/dist/dataset-query.js +7 -71
- package/dist/dataset-sql-dialect.d.ts +11 -0
- package/dist/dataset-sql-dialect.d.ts.map +1 -0
- package/dist/dataset-sql-dialect.js +1 -0
- package/dist/dataset.d.ts +3 -2
- package/dist/dataset.d.ts.map +1 -1
- package/dist/execution-capabilities.d.ts +28 -0
- package/dist/execution-capabilities.d.ts.map +1 -0
- package/dist/execution-capabilities.js +17 -0
- package/dist/execution-capabilities.json +13 -0
- package/dist/executor.d.ts +17 -38
- package/dist/executor.d.ts.map +1 -1
- package/dist/executor.js +40 -400
- package/dist/index.d.ts +10 -5
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -3
- package/dist/internal.d.ts +3 -0
- package/dist/internal.d.ts.map +1 -1
- package/dist/internal.js +2 -0
- package/dist/measure.d.ts +54 -17
- package/dist/measure.d.ts.map +1 -1
- package/dist/measure.js +3 -63
- package/dist/metric-query-engine.d.ts +29 -0
- package/dist/metric-query-engine.d.ts.map +1 -0
- package/dist/metric-query-engine.js +106 -0
- package/dist/portable-executor.d.ts +20 -6
- package/dist/portable-executor.d.ts.map +1 -1
- package/dist/portable-executor.js +29 -9
- package/dist/protocol-adapter.d.ts.map +1 -1
- package/dist/protocol-adapter.js +1 -0
- package/dist/protocol-rehydrate.d.ts.map +1 -1
- package/dist/protocol-rehydrate.js +9 -2
- package/dist/query-builder-protocol.d.ts +11 -0
- package/dist/query-builder-protocol.d.ts.map +1 -1
- package/dist/query-helpers.d.ts +10 -10
- package/dist/query-helpers.d.ts.map +1 -1
- package/dist/query-helpers.js +1 -3
- package/dist/query-planner.d.ts +7 -3
- package/dist/query-planner.d.ts.map +1 -1
- package/dist/query-planner.js +10 -12
- package/dist/relationship-check.d.ts +3 -2
- package/dist/relationship-check.d.ts.map +1 -1
- package/dist/relationship-check.js +8 -3
- package/dist/relationships.d.ts +6 -16
- package/dist/relationships.d.ts.map +1 -1
- package/dist/relationships.js +3 -12
- package/dist/semantic-planner.d.ts.map +1 -1
- package/dist/semantic-planner.js +4 -0
- package/dist/semantic-query-schema.d.ts +6 -0
- package/dist/semantic-query-schema.d.ts.map +1 -1
- package/dist/semantic-query-schema.js +31 -4
- package/dist/types.d.ts +67 -11
- package/dist/types.d.ts.map +1 -1
- package/dist/utils/agent-catalog-projection.d.ts +1 -0
- package/dist/utils/agent-catalog-projection.d.ts.map +1 -1
- package/dist/utils/agent-catalog-projection.js +2 -1
- package/dist/utils/base-measure-definition.d.ts +23 -0
- package/dist/utils/base-measure-definition.d.ts.map +1 -0
- package/dist/utils/base-measure-definition.js +65 -0
- package/dist/utils/build-dataset-query-builder.d.ts +5 -0
- package/dist/utils/build-dataset-query-builder.d.ts.map +1 -0
- package/dist/utils/build-dataset-query-builder.js +65 -0
- package/dist/utils/calendar-shift-sql.d.ts +18 -0
- package/dist/utils/calendar-shift-sql.d.ts.map +1 -0
- package/dist/utils/calendar-shift-sql.js +21 -0
- package/dist/utils/clickhouse-dataset-sql-dialect.d.ts +4 -0
- package/dist/utils/clickhouse-dataset-sql-dialect.d.ts.map +1 -0
- package/dist/utils/clickhouse-dataset-sql-dialect.js +6 -0
- package/dist/utils/compile-dataset-query.d.ts +10 -0
- package/dist/utils/compile-dataset-query.d.ts.map +1 -0
- package/dist/utils/compile-dataset-query.js +72 -0
- package/dist/utils/composite-time-measure-sql.d.ts.map +1 -1
- package/dist/utils/composite-time-measure-sql.js +2 -1
- package/dist/utils/dataset-definition-validation.d.ts.map +1 -1
- package/dist/utils/dataset-definition-validation.js +52 -2
- package/dist/utils/dataset-derived-query.d.ts +6 -2
- package/dist/utils/dataset-derived-query.d.ts.map +1 -1
- package/dist/utils/dataset-derived-query.js +28 -26
- package/dist/utils/dataset-filter-types.d.ts +28 -0
- package/dist/utils/dataset-filter-types.d.ts.map +1 -0
- package/dist/utils/dataset-filter-types.js +1 -0
- package/dist/utils/dataset-having.d.ts +21 -0
- package/dist/utils/dataset-having.d.ts.map +1 -0
- package/dist/utils/dataset-having.js +106 -0
- package/dist/utils/dataset-normalization.d.ts.map +1 -1
- package/dist/utils/dataset-normalization.js +5 -1
- package/dist/utils/dataset-query-validation.d.ts.map +1 -1
- package/dist/utils/dataset-query-validation.js +17 -3
- package/dist/utils/dataset-sql-dialect.d.ts +5 -0
- package/dist/utils/dataset-sql-dialect.d.ts.map +1 -0
- package/dist/utils/dataset-sql-dialect.js +5 -0
- package/dist/utils/dataset-target.d.ts +3 -0
- package/dist/utils/dataset-target.d.ts.map +1 -0
- package/dist/utils/dataset-target.js +3 -0
- package/dist/utils/filter-definition.d.ts +3 -0
- package/dist/utils/filter-definition.d.ts.map +1 -0
- package/dist/utils/filter-definition.js +3 -0
- package/dist/utils/metric-derived-query.d.ts +8 -0
- package/dist/utils/metric-derived-query.d.ts.map +1 -0
- package/dist/utils/metric-derived-query.js +106 -0
- package/dist/utils/metric-query-builder.d.ts +5 -0
- package/dist/utils/metric-query-builder.d.ts.map +1 -0
- package/dist/utils/metric-query-builder.js +50 -0
- package/dist/utils/metric-query-result.d.ts +10 -0
- package/dist/utils/metric-query-result.d.ts.map +1 -0
- package/dist/utils/metric-query-result.js +18 -0
- package/dist/utils/metric-query-validation.d.ts +6 -0
- package/dist/utils/metric-query-validation.d.ts.map +1 -0
- package/dist/utils/metric-query-validation.js +116 -0
- package/dist/utils/protocol-metric-expressions.d.ts +8 -0
- package/dist/utils/protocol-metric-expressions.d.ts.map +1 -1
- package/dist/utils/protocol-metric-expressions.js +23 -11
- package/dist/utils/relationship-builder-plan.d.ts +6 -2
- package/dist/utils/relationship-builder-plan.d.ts.map +1 -1
- package/dist/utils/relationship-builder-plan.js +24 -15
- package/dist/utils/relationship-definition.d.ts +6 -0
- package/dist/utils/relationship-definition.d.ts.map +1 -0
- package/dist/utils/relationship-definition.js +9 -0
- package/dist/utils/relationship-key-check.d.ts +4 -0
- package/dist/utils/relationship-key-check.d.ts.map +1 -1
- package/dist/utils/relationship-key-check.js +19 -7
- package/dist/utils/relationship-keys.d.ts +17 -0
- package/dist/utils/relationship-keys.d.ts.map +1 -0
- package/dist/utils/relationship-keys.js +63 -0
- package/dist/utils/relationship-measure-sql.d.ts +14 -0
- package/dist/utils/relationship-measure-sql.d.ts.map +1 -0
- package/dist/utils/relationship-measure-sql.js +44 -0
- package/dist/utils/relationship-measure-types.d.ts +36 -0
- package/dist/utils/relationship-measure-types.d.ts.map +1 -0
- package/dist/utils/relationship-measure-types.js +1 -0
- package/dist/utils/relationship-measures.d.ts +23 -0
- package/dist/utils/relationship-measures.d.ts.map +1 -0
- package/dist/utils/relationship-measures.js +62 -0
- package/dist/utils/relationship-validation.d.ts +1 -1
- package/dist/utils/relationship-validation.d.ts.map +1 -1
- package/dist/utils/relationship-validation.js +3 -5
- package/dist/utils/semantic-query-syntax.d.ts +6 -0
- package/dist/utils/semantic-query-syntax.d.ts.map +1 -0
- package/dist/utils/semantic-query-syntax.js +24 -0
- package/dist/utils/shift-measure-sql.d.ts.map +1 -1
- package/dist/utils/shift-measure-sql.js +10 -3
- package/dist/utils/snapshot-execution-context.d.ts +4 -0
- package/dist/utils/snapshot-execution-context.d.ts.map +1 -0
- package/dist/utils/snapshot-execution-context.js +16 -0
- package/dist/utils/time-measure-context-sql.d.ts.map +1 -1
- package/dist/utils/time-measure-context-sql.js +8 -2
- package/dist/utils/time-measure-dataset-sql.d.ts.map +1 -1
- package/dist/utils/time-measure-dataset-sql.js +3 -2
- package/dist/utils/time-measure-result-sql.d.ts.map +1 -1
- package/dist/utils/time-measure-result-sql.js +2 -1
- package/dist/utils/time-measure-source-sql.d.ts.map +1 -1
- package/dist/utils/time-measure-source-sql.js +8 -4
- package/dist/validation.js +1 -1
- package/package.json +10 -5
|
@@ -13,6 +13,7 @@
|
|
|
13
13
|
*/
|
|
14
14
|
import { validateProtocolDeploymentContract } from '@hypequery/protocol';
|
|
15
15
|
import { dataset } from './dataset.js';
|
|
16
|
+
import { rehydrateCompositeRelationshipJoin } from './utils/relationship-keys.js';
|
|
16
17
|
import { snapshotSemanticMetadata } from './utils/semantic-metadata.js';
|
|
17
18
|
import { withContractCapabilities } from './utils/protocol-metric-capabilities.js';
|
|
18
19
|
import { rehydrateDerivedFormula } from './utils/protocol-rehydrate-derivation.js';
|
|
@@ -107,14 +108,20 @@ function rehydrateRelationships(contract, resolve) {
|
|
|
107
108
|
const relationships = {};
|
|
108
109
|
for (const relationship of contract.relationships) {
|
|
109
110
|
const target = String(relationship.target);
|
|
111
|
+
const from = String(relationship.from);
|
|
112
|
+
const to = String(relationship.to);
|
|
113
|
+
const compositeJoin = relationship.keys
|
|
114
|
+
? rehydrateCompositeRelationshipJoin(from, to, relationship.keys.map(key => ({ from: String(key.from), to: String(key.to) })))
|
|
115
|
+
: undefined;
|
|
110
116
|
relationships[String(relationship.name)] = {
|
|
111
117
|
__type: 'relationship',
|
|
112
118
|
kind: relationship.kind,
|
|
113
119
|
// Resolved on call: datasets in one contract may reference each other, so
|
|
114
120
|
// the target may not be built yet when this relationship is created.
|
|
115
121
|
target: () => resolve(target),
|
|
116
|
-
from
|
|
117
|
-
to
|
|
122
|
+
from,
|
|
123
|
+
to,
|
|
124
|
+
...compositeJoin,
|
|
118
125
|
};
|
|
119
126
|
}
|
|
120
127
|
return relationships;
|
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
*
|
|
9
9
|
* This keeps @hypequery/serve DB-agnostic while enabling first-class builder usage.
|
|
10
10
|
*/
|
|
11
|
+
import type { DatasetSqlDialect } from './dataset-sql-dialect.js';
|
|
11
12
|
/** A chainable query builder instance (what `.table(name)` returns). */
|
|
12
13
|
export interface QueryBuilderLike {
|
|
13
14
|
select(columns: string[] | string): QueryBuilderLike;
|
|
@@ -37,8 +38,15 @@ export interface QueryBuilderLike {
|
|
|
37
38
|
* aggregates. A builder without it can still run every query that does not
|
|
38
39
|
* traverse a relationship; one that does is rejected with a clear error
|
|
39
40
|
* instead of falling back to `leftJoin`.
|
|
41
|
+
*
|
|
42
|
+
* Composite relationships use the key-pair form, ANDing every
|
|
43
|
+
* `[leftColumn, rightColumn]` equality (`@hypequery/clickhouse` >= 2.13.0).
|
|
44
|
+
* A builder must implement both overloads before composite relationships
|
|
45
|
+
* are used with it: the call cannot be told apart from a single-key-only
|
|
46
|
+
* implementation, which would receive the key pairs as a column name.
|
|
40
47
|
*/
|
|
41
48
|
leftAnyJoin?(table: string, leftColumn: string, rightColumn: string, alias?: string, on?: QueryBuilderJoinCondition | QueryBuilderJoinCondition[]): QueryBuilderLike;
|
|
49
|
+
leftAnyJoin?(table: string, keys: readonly [readonly [string, string], ...(readonly [string, string])[]], alias?: string, on?: QueryBuilderJoinCondition | QueryBuilderJoinCondition[]): QueryBuilderLike;
|
|
42
50
|
groupBy(columns: string | string[]): QueryBuilderLike;
|
|
43
51
|
orderBy(column: string, direction?: 'ASC' | 'DESC'): QueryBuilderLike;
|
|
44
52
|
limit(count: number): QueryBuilderLike;
|
|
@@ -58,6 +66,8 @@ export interface QueryBuilderJoinCondition {
|
|
|
58
66
|
}
|
|
59
67
|
/** A query builder factory (what `createQueryBuilder(config)` returns). */
|
|
60
68
|
export interface QueryBuilderFactoryLike {
|
|
69
|
+
/** Advanced datasets rendering hook. Omission preserves legacy ClickHouse SQL. */
|
|
70
|
+
readonly datasetSqlDialect?: DatasetSqlDialect;
|
|
61
71
|
table(name: string): QueryBuilderLike;
|
|
62
72
|
rawQuery<T = Record<string, unknown>>(sql: string, params?: unknown[], options?: {
|
|
63
73
|
abortSignal?: AbortSignal;
|
|
@@ -74,6 +84,7 @@ export interface QueryBuilderFactoryLike {
|
|
|
74
84
|
* schema-typed builders; entry points adapt with `toQueryBuilderFactory`.
|
|
75
85
|
*/
|
|
76
86
|
export interface QueryBuilderFactoryCompatible {
|
|
87
|
+
readonly datasetSqlDialect?: DatasetSqlDialect;
|
|
77
88
|
table(name: never): unknown;
|
|
78
89
|
rawQuery(sql: string, params?: never): Promise<unknown>;
|
|
79
90
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"query-builder-protocol.d.ts","sourceRoot":"","sources":["../src/query-builder-protocol.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,wEAAwE;AACxE,MAAM,WAAW,gBAAgB;IAC/B,MAAM,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,MAAM,GAAG,gBAAgB,CAAC;IAGrD,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,gBAAgB,CAAC;IACtD,KAAK,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,gBAAgB,CAAC;IACxD,aAAa,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,gBAAgB,CAAC;IAChE,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,gBAAgB,CAAC;IACtD,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,gBAAgB,CAAC;IACtD,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,gBAAgB,CAAC;IAGtD,kEAAkE;IAClE,MAAM,CAAC,CAAC,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,gBAAgB,CAAC;IAC7E,kEAAkE;IAClE,MAAM,CAAC,CAAC,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,gBAAgB,CAAC;IAC7E,+DAA+D;IAC/D,QAAQ,CAAC,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,gBAAgB,CAAC;IAC3E,6CAA6C;IAC7C,MAAM,CAAC,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,gBAAgB,CAAC;IAC1D,mCAAmC;IACnC,QAAQ,CAAC,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,gBAAgB,CAAC;IAC5D,gEAAgE;IAChE,mBAAmB,CAAC,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,gBAAgB,CAAC;IAGvE,KAAK,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,GAAG,gBAAgB,CAAC;IAI1E,QAAQ,CACN,KAAK,EAAE,MAAM,EACb,UAAU,EAAE,MAAM,EAClB,WAAW,EAAE,MAAM,EACnB,KAAK,CAAC,EAAE,MAAM,EACd,EAAE,CAAC,EAAE,yBAAyB,GAAG,yBAAyB,EAAE,GAC3D,gBAAgB,CAAC;IAEpB
|
|
1
|
+
{"version":3,"file":"query-builder-protocol.d.ts","sourceRoot":"","sources":["../src/query-builder-protocol.ts"],"names":[],"mappings":"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"}
|
package/dist/query-helpers.d.ts
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
import type { MetricFilter, MetricOrderBy } from './types.js';
|
|
2
|
-
export declare function eq<const TField extends string, const TValue>(field: TField, value: TValue): MetricFilter<TField, TValue>;
|
|
3
|
-
export declare function neq<const TField extends string, const TValue>(field: TField, value: TValue): MetricFilter<TField, TValue>;
|
|
4
|
-
export declare function gt<const TField extends string, const TValue>(field: TField, value: TValue): MetricFilter<TField, TValue>;
|
|
5
|
-
export declare function gte<const TField extends string, const TValue>(field: TField, value: TValue): MetricFilter<TField, TValue>;
|
|
6
|
-
export declare function lt<const TField extends string, const TValue>(field: TField, value: TValue): MetricFilter<TField, TValue>;
|
|
7
|
-
export declare function lte<const TField extends string, const TValue>(field: TField, value: TValue): MetricFilter<TField, TValue>;
|
|
8
|
-
export declare function inList<const TField extends string, const TValue>(field: TField, value: TValue[]): MetricFilter<TField, TValue[]>;
|
|
9
|
-
export declare function notInList<const TField extends string, const TValue>(field: TField, value: TValue[]): MetricFilter<TField, TValue[]>;
|
|
10
|
-
export declare function between<const TField extends string, const TLower, const TUpper>(field: TField, lower: TLower, upper: TUpper): MetricFilter<TField, [TLower, TUpper]>;
|
|
11
|
-
export declare function like<const TField extends string>(field: TField, value: string): MetricFilter<TField, string>;
|
|
2
|
+
export declare function eq<const TField extends string, const TValue>(field: TField, value: TValue): MetricFilter<TField, TValue, 'eq'>;
|
|
3
|
+
export declare function neq<const TField extends string, const TValue>(field: TField, value: TValue): MetricFilter<TField, TValue, 'neq'>;
|
|
4
|
+
export declare function gt<const TField extends string, const TValue>(field: TField, value: TValue): MetricFilter<TField, TValue, 'gt'>;
|
|
5
|
+
export declare function gte<const TField extends string, const TValue>(field: TField, value: TValue): MetricFilter<TField, TValue, 'gte'>;
|
|
6
|
+
export declare function lt<const TField extends string, const TValue>(field: TField, value: TValue): MetricFilter<TField, TValue, 'lt'>;
|
|
7
|
+
export declare function lte<const TField extends string, const TValue>(field: TField, value: TValue): MetricFilter<TField, TValue, 'lte'>;
|
|
8
|
+
export declare function inList<const TField extends string, const TValue>(field: TField, value: TValue[]): MetricFilter<TField, TValue[], 'in'>;
|
|
9
|
+
export declare function notInList<const TField extends string, const TValue>(field: TField, value: TValue[]): MetricFilter<TField, TValue[], 'notIn'>;
|
|
10
|
+
export declare function between<const TField extends string, const TLower, const TUpper>(field: TField, lower: TLower, upper: TUpper): MetricFilter<TField, [TLower, TUpper], 'between'>;
|
|
11
|
+
export declare function like<const TField extends string>(field: TField, value: string): MetricFilter<TField, string, 'like'>;
|
|
12
12
|
export declare function asc<const TField extends string>(field: TField): MetricOrderBy<TField>;
|
|
13
13
|
export declare function desc<const TField extends string>(field: TField): MetricOrderBy<TField>;
|
|
14
14
|
export declare const filter: {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"query-helpers.d.ts","sourceRoot":"","sources":["../src/query-helpers.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"query-helpers.d.ts","sourceRoot":"","sources":["../src/query-helpers.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAE9D,wBAAgB,EAAE,CAAC,KAAK,CAAC,MAAM,SAAS,MAAM,EAAE,KAAK,CAAC,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,YAAY,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,CAAC,CAE9H;AAED,wBAAgB,GAAG,CAAC,KAAK,CAAC,MAAM,SAAS,MAAM,EAAE,KAAK,CAAC,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,YAAY,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,CAAC,CAEhI;AAED,wBAAgB,EAAE,CAAC,KAAK,CAAC,MAAM,SAAS,MAAM,EAAE,KAAK,CAAC,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,YAAY,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,CAAC,CAE9H;AAED,wBAAgB,GAAG,CAAC,KAAK,CAAC,MAAM,SAAS,MAAM,EAAE,KAAK,CAAC,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,YAAY,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,CAAC,CAEhI;AAED,wBAAgB,EAAE,CAAC,KAAK,CAAC,MAAM,SAAS,MAAM,EAAE,KAAK,CAAC,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,YAAY,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,CAAC,CAE9H;AAED,wBAAgB,GAAG,CAAC,KAAK,CAAC,MAAM,SAAS,MAAM,EAAE,KAAK,CAAC,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,YAAY,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,CAAC,CAEhI;AAED,wBAAgB,MAAM,CAAC,KAAK,CAAC,MAAM,SAAS,MAAM,EAAE,KAAK,CAAC,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,YAAY,CAAC,MAAM,EAAE,MAAM,EAAE,EAAE,IAAI,CAAC,CAEtI;AAED,wBAAgB,SAAS,CAAC,KAAK,CAAC,MAAM,SAAS,MAAM,EAAE,KAAK,CAAC,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,YAAY,CAAC,MAAM,EAAE,MAAM,EAAE,EAAE,OAAO,CAAC,CAE5I;AAED,wBAAgB,OAAO,CAAC,KAAK,CAAC,MAAM,SAAS,MAAM,EAAE,KAAK,CAAC,MAAM,EAAE,KAAK,CAAC,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,YAAY,CAAC,MAAM,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,EAAE,SAAS,CAAC,CAE/K;AAED,wBAAgB,IAAI,CAAC,KAAK,CAAC,MAAM,SAAS,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,YAAY,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAEpH;AAED,wBAAgB,GAAG,CAAC,KAAK,CAAC,MAAM,SAAS,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,aAAa,CAAC,MAAM,CAAC,CAErF;AAED,wBAAgB,IAAI,CAAC,KAAK,CAAC,MAAM,SAAS,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,aAAa,CAAC,MAAM,CAAC,CAEtF;AAED,eAAO,MAAM,MAAM;;;;;;;;;;;CAWT,CAAC;AAEX,eAAO,MAAM,KAAK;;;CAGR,CAAC"}
|
package/dist/query-helpers.js
CHANGED
package/dist/query-planner.d.ts
CHANGED
|
@@ -1,16 +1,20 @@
|
|
|
1
1
|
import type { AggregationSpec, AnyDatasetInstance, ExecutionContext, MeasureDefinition, MetricOrderBy, TimeGrain } from "./types.js";
|
|
2
2
|
import type { QueryBuilderLike } from "./query-builder-protocol.js";
|
|
3
|
+
import type { DatasetSqlDialect } from './dataset-sql-dialect.js';
|
|
3
4
|
import { type RelationshipBuilderContext } from './utils/relationship-builder-plan.js';
|
|
4
5
|
type DatasetShape = AnyDatasetInstance;
|
|
5
6
|
export declare function resolveDimensionExpression(ds: DatasetShape, dimensionName: string, joinCtx?: RelationshipBuilderContext): string;
|
|
6
7
|
export declare function resolveFilterField(ds: DatasetShape, filterField: string, joinCtx?: RelationshipBuilderContext): string;
|
|
7
|
-
export declare function buildDimensionSelectionPlan(ds: DatasetShape, dimensions: string[], grain: TimeGrain | undefined, joinCtx?: RelationshipBuilderContext, timezone?: string): {
|
|
8
|
+
export declare function buildDimensionSelectionPlan(ds: DatasetShape, dimensions: string[], grain: TimeGrain | undefined, joinCtx?: RelationshipBuilderContext, timezone?: string, dialect?: DatasetSqlDialect): {
|
|
8
9
|
selectParts: string[];
|
|
9
10
|
groupByParts: string[];
|
|
10
11
|
};
|
|
11
|
-
export declare function applyAggregationSpec(qb: QueryBuilderLike, ds: DatasetShape, spec: AggregationSpec, alias: string, joinCtx?: RelationshipBuilderContext
|
|
12
|
+
export declare function applyAggregationSpec(qb: QueryBuilderLike, ds: DatasetShape, spec: AggregationSpec, alias: string, joinCtx?: RelationshipBuilderContext, expressions?: {
|
|
13
|
+
field: string;
|
|
14
|
+
arg?: string;
|
|
15
|
+
}): QueryBuilderLike;
|
|
12
16
|
export declare function applyMeasureDefinition(qb: QueryBuilderLike, ds: DatasetShape, name: string, definition: MeasureDefinition, joinCtx?: RelationshipBuilderContext): QueryBuilderLike;
|
|
13
|
-
export declare function appendOrderLimitOffset(qb: QueryBuilderLike, orderBy: MetricOrderBy[] | undefined, grain: TimeGrain | undefined, limit?: number, offset?: number, joinCtx?: RelationshipBuilderContext): QueryBuilderLike;
|
|
17
|
+
export declare function appendOrderLimitOffset(qb: QueryBuilderLike, orderBy: MetricOrderBy[] | undefined, grain: TimeGrain | undefined, limit?: number, offset?: number, joinCtx?: RelationshipBuilderContext, dialect?: DatasetSqlDialect): QueryBuilderLike;
|
|
14
18
|
export declare function resolveTenantFilterColumn(ds: DatasetShape, context?: ExecutionContext): string | undefined;
|
|
15
19
|
export {};
|
|
16
20
|
//# sourceMappingURL=query-planner.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"query-planner.d.ts","sourceRoot":"","sources":["../src/query-planner.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"query-planner.d.ts","sourceRoot":"","sources":["../src/query-planner.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EACV,eAAe,EACf,kBAAkB,EAClB,gBAAgB,EAChB,iBAAiB,EACjB,aAAa,EACb,SAAS,EACV,MAAM,YAAY,CAAC;AACpB,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,6BAA6B,CAAC;AAIpE,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AAGlE,OAAO,EAGL,KAAK,0BAA0B,EAChC,MAAM,sCAAsC,CAAC;AAI9C,KAAK,YAAY,GAAG,kBAAkB,CAAC;AAEvC,wBAAgB,0BAA0B,CACxC,EAAE,EAAE,YAAY,EAChB,aAAa,EAAE,MAAM,EACrB,OAAO,CAAC,EAAE,0BAA0B,GACnC,MAAM,CAUR;AAED,wBAAgB,kBAAkB,CAChC,EAAE,EAAE,YAAY,EAChB,WAAW,EAAE,MAAM,EACnB,OAAO,CAAC,EAAE,0BAA0B,GACnC,MAAM,CAMR;AAED,wBAAgB,2BAA2B,CACzC,EAAE,EAAE,YAAY,EAChB,UAAU,EAAE,MAAM,EAAE,EACpB,KAAK,EAAE,SAAS,GAAG,SAAS,EAC5B,OAAO,CAAC,EAAE,0BAA0B,EACpC,QAAQ,CAAC,EAAE,MAAM,EACjB,OAAO,GAAE,iBAA+C,GACvD;IAAE,WAAW,EAAE,MAAM,EAAE,CAAC;IAAC,YAAY,EAAE,MAAM,EAAE,CAAA;CAAE,CAmCnD;AAED,wBAAgB,oBAAoB,CAClC,EAAE,EAAE,gBAAgB,EACpB,EAAE,EAAE,YAAY,EAChB,IAAI,EAAE,eAAe,EACrB,KAAK,EAAE,MAAM,EACb,OAAO,CAAC,EAAE,0BAA0B,EACpC,WAAW,CAAC,EAAE;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,GAAG,CAAC,EAAE,MAAM,CAAA;CAAE,GAC5C,gBAAgB,CAqElB;AAED,wBAAgB,sBAAsB,CACpC,EAAE,EAAE,gBAAgB,EACpB,EAAE,EAAE,YAAY,EAChB,IAAI,EAAE,MAAM,EACZ,UAAU,EAAE,iBAAiB,EAC7B,OAAO,CAAC,EAAE,0BAA0B,GACnC,gBAAgB,CAElB;AAED,wBAAgB,sBAAsB,CACpC,EAAE,EAAE,gBAAgB,EACpB,OAAO,EAAE,aAAa,EAAE,GAAG,SAAS,EACpC,KAAK,EAAE,SAAS,GAAG,SAAS,EAC5B,KAAK,CAAC,EAAE,MAAM,EACd,MAAM,CAAC,EAAE,MAAM,EACf,OAAO,CAAC,EAAE,0BAA0B,EACpC,OAAO,GAAE,iBAA+C,GACvD,gBAAgB,CAoBlB;AAED,wBAAgB,yBAAyB,CACvC,EAAE,EAAE,YAAY,EAChB,OAAO,CAAC,EAAE,gBAAgB,GACzB,MAAM,GAAG,SAAS,CAMpB"}
|
package/dist/query-planner.js
CHANGED
|
@@ -1,16 +1,14 @@
|
|
|
1
|
+
import { semanticOrderDirection } from './utils/semantic-query-syntax.js';
|
|
1
2
|
import { queryTimeSql } from './utils/query-timezone.js';
|
|
2
3
|
import { assertNoRawSqlUnderJoins } from './utils/sql-under-joins.js';
|
|
3
4
|
import { GRAIN_FUNCTIONS } from "./constants.js";
|
|
4
5
|
import { applyFilteredAggregationExpression } from './utils/filtered-aggregation-sql.js';
|
|
5
6
|
import { getRuntimeTenantPredicate } from './utils/tenant-runtime.js';
|
|
6
|
-
import {
|
|
7
|
+
import { clickhouseDatasetSqlDialect } from './utils/clickhouse-dataset-sql-dialect.js';
|
|
7
8
|
import { isQualifiedField } from './utils/relationship-fields.js';
|
|
8
9
|
import { qualifyBaseColumn, resolveQualifiedColumn, } from './utils/relationship-builder-plan.js';
|
|
9
10
|
import { measureToAggregationSpec } from './utils/dataset-normalization.js';
|
|
10
11
|
import { validatePercentileLevel } from './measure.js';
|
|
11
|
-
function toOrderDirection(direction) {
|
|
12
|
-
return direction === 'asc' ? 'ASC' : 'DESC';
|
|
13
|
-
}
|
|
14
12
|
export function resolveDimensionExpression(ds, dimensionName, joinCtx) {
|
|
15
13
|
if (joinCtx && isQualifiedField(dimensionName)) {
|
|
16
14
|
return resolveQualifiedColumn(ds, dimensionName);
|
|
@@ -29,7 +27,7 @@ export function resolveFilterField(ds, filterField, joinCtx) {
|
|
|
29
27
|
const resolvedField = ds.filters[filterField]?.field ?? filterField;
|
|
30
28
|
return resolveDimensionExpression(ds, resolvedField, joinCtx);
|
|
31
29
|
}
|
|
32
|
-
export function buildDimensionSelectionPlan(ds, dimensions, grain, joinCtx, timezone) {
|
|
30
|
+
export function buildDimensionSelectionPlan(ds, dimensions, grain, joinCtx, timezone, dialect = clickhouseDatasetSqlDialect) {
|
|
33
31
|
const selectParts = [];
|
|
34
32
|
const groupByParts = new Set();
|
|
35
33
|
if (grain) {
|
|
@@ -47,7 +45,7 @@ export function buildDimensionSelectionPlan(ds, dimensions, grain, joinCtx, time
|
|
|
47
45
|
// With joins in scope, every selection is table-qualified and aliased so
|
|
48
46
|
// grouping/ordering can reference the alias unambiguously.
|
|
49
47
|
const alias = isQualifiedField(dimensionName)
|
|
50
|
-
?
|
|
48
|
+
? dialect.quoteIdentifier(dimensionName)
|
|
51
49
|
: dimensionName;
|
|
52
50
|
selectParts.push(`${expression} AS ${alias}`);
|
|
53
51
|
groupByParts.add(alias);
|
|
@@ -63,11 +61,11 @@ export function buildDimensionSelectionPlan(ds, dimensions, grain, joinCtx, time
|
|
|
63
61
|
}
|
|
64
62
|
return { selectParts, groupByParts: Array.from(groupByParts) };
|
|
65
63
|
}
|
|
66
|
-
export function applyAggregationSpec(qb, ds, spec, alias, joinCtx) {
|
|
64
|
+
export function applyAggregationSpec(qb, ds, spec, alias, joinCtx, expressions) {
|
|
67
65
|
if (spec.sql) {
|
|
68
66
|
assertNoRawSqlUnderJoins('measure', alias, spec.sql, joinCtx);
|
|
69
67
|
}
|
|
70
|
-
const fieldOrExpr = applyFilteredAggregationExpression(ds, spec, spec.sql ?? resolveDimensionExpression(ds, spec.field, joinCtx), joinCtx);
|
|
68
|
+
const fieldOrExpr = expressions?.field ?? applyFilteredAggregationExpression(ds, spec, spec.sql ?? resolveDimensionExpression(ds, spec.field, joinCtx), joinCtx);
|
|
71
69
|
switch (spec.aggregation) {
|
|
72
70
|
case "sum":
|
|
73
71
|
return qb.sum(fieldOrExpr, alias);
|
|
@@ -86,7 +84,7 @@ export function applyAggregationSpec(qb, ds, spec, alias, joinCtx) {
|
|
|
86
84
|
if (!spec.argField) {
|
|
87
85
|
throw new Error(`Aggregation "${spec.aggregation}" for "${alias}" requires an argField ("by" column).`);
|
|
88
86
|
}
|
|
89
|
-
const argExpr = resolveDimensionExpression(ds, spec.argField, joinCtx);
|
|
87
|
+
const argExpr = expressions?.arg ?? resolveDimensionExpression(ds, spec.argField, joinCtx);
|
|
90
88
|
if (spec.aggregation === "argMax") {
|
|
91
89
|
if (!qb.argMax) {
|
|
92
90
|
throw new Error('Query builder does not support argMax aggregations.');
|
|
@@ -130,13 +128,13 @@ export function applyAggregationSpec(qb, ds, spec, alias, joinCtx) {
|
|
|
130
128
|
export function applyMeasureDefinition(qb, ds, name, definition, joinCtx) {
|
|
131
129
|
return applyAggregationSpec(qb, ds, measureToAggregationSpec(name, definition), name, joinCtx);
|
|
132
130
|
}
|
|
133
|
-
export function appendOrderLimitOffset(qb, orderBy, grain, limit, offset, joinCtx) {
|
|
131
|
+
export function appendOrderLimitOffset(qb, orderBy, grain, limit, offset, joinCtx, dialect = clickhouseDatasetSqlDialect) {
|
|
134
132
|
if (orderBy && orderBy.length > 0) {
|
|
135
133
|
for (const order of orderBy) {
|
|
136
134
|
const column = joinCtx && isQualifiedField(order.field)
|
|
137
|
-
?
|
|
135
|
+
? dialect.quoteIdentifier(order.field)
|
|
138
136
|
: order.field;
|
|
139
|
-
qb = qb.orderBy(column,
|
|
137
|
+
qb = qb.orderBy(column, semanticOrderDirection(order.direction));
|
|
140
138
|
}
|
|
141
139
|
}
|
|
142
140
|
else if (grain) {
|
|
@@ -39,9 +39,10 @@ export interface CheckRelationshipsResult {
|
|
|
39
39
|
issues: RelationshipKeyIssue[];
|
|
40
40
|
}
|
|
41
41
|
/**
|
|
42
|
-
* Checks that the target join
|
|
42
|
+
* Checks that the target join key of each `belongsTo` and `hasOne`
|
|
43
43
|
* relationship on `ds` is unique. NULL keys are ignored because they never
|
|
44
|
-
* match a join
|
|
44
|
+
* match a join; a composite key is ignored when any component is NULL.
|
|
45
|
+
* `hasMany` relationships are skipped.
|
|
45
46
|
*/
|
|
46
47
|
export declare function checkRelationships(ds: AnyDatasetInstance, options: CheckRelationshipsOptions): Promise<CheckRelationshipsResult>;
|
|
47
48
|
//# sourceMappingURL=relationship-check.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"relationship-check.d.ts","sourceRoot":"","sources":["../src/relationship-check.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAEH,OAAO,KAAK,EAAE,kBAAkB,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AACvE,OAAO,EAAyB,KAAK,wBAAwB,EAAE,MAAM,6BAA6B,CAAC;AAEnG,OAAO,EAIL,KAAK,oBAAoB,EAC1B,MAAM,mCAAmC,CAAC;AAE3C,YAAY,EAAE,oBAAoB,EAAE,MAAM,mCAAmC,CAAC;AAE9E,MAAM,WAAW,yBAAyB;IACxC,uDAAuD;IACvD,YAAY,EAAE,wBAAwB,CAAC;IACvC;;;OAGG;IACH,OAAO,CAAC,EAAE,gBAAgB,CAAC;IAC3B,6DAA6D;IAC7D,aAAa,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;CACnC;AAED,MAAM,WAAW,wBAAwB;IACvC,oEAAoE;IACpE,EAAE,EAAE,OAAO,CAAC;IACZ,4CAA4C;IAC5C,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,MAAM,EAAE,oBAAoB,EAAE,CAAC;CAChC;AAKD
|
|
1
|
+
{"version":3,"file":"relationship-check.d.ts","sourceRoot":"","sources":["../src/relationship-check.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAEH,OAAO,KAAK,EAAE,kBAAkB,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AACvE,OAAO,EAAyB,KAAK,wBAAwB,EAAE,MAAM,6BAA6B,CAAC;AAEnG,OAAO,EAIL,KAAK,oBAAoB,EAC1B,MAAM,mCAAmC,CAAC;AAE3C,YAAY,EAAE,oBAAoB,EAAE,MAAM,mCAAmC,CAAC;AAE9E,MAAM,WAAW,yBAAyB;IACxC,uDAAuD;IACvD,YAAY,EAAE,wBAAwB,CAAC;IACvC;;;OAGG;IACH,OAAO,CAAC,EAAE,gBAAgB,CAAC;IAC3B,6DAA6D;IAC7D,aAAa,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;CACnC;AAED,MAAM,WAAW,wBAAwB;IACvC,oEAAoE;IACpE,EAAE,EAAE,OAAO,CAAC;IACZ,4CAA4C;IAC5C,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,MAAM,EAAE,oBAAoB,EAAE,CAAC;CAChC;AAKD;;;;;GAKG;AACH,wBAAsB,kBAAkB,CACtC,EAAE,EAAE,kBAAkB,EACtB,OAAO,EAAE,yBAAyB,GACjC,OAAO,CAAC,wBAAwB,CAAC,CAqCnC"}
|
|
@@ -22,9 +22,10 @@ import { listToOneRelationships, readCount, relationshipKeyIssue, } from './util
|
|
|
22
22
|
const ROWS_ALIAS = '__hq_rows';
|
|
23
23
|
const KEYS_ALIAS = '__hq_keys';
|
|
24
24
|
/**
|
|
25
|
-
* Checks that the target join
|
|
25
|
+
* Checks that the target join key of each `belongsTo` and `hasOne`
|
|
26
26
|
* relationship on `ds` is unique. NULL keys are ignored because they never
|
|
27
|
-
* match a join
|
|
27
|
+
* match a join; a composite key is ignored when any component is NULL.
|
|
28
|
+
* `hasMany` relationships are skipped.
|
|
28
29
|
*/
|
|
29
30
|
export async function checkRelationships(ds, options) {
|
|
30
31
|
const factory = toQueryBuilderFactory(options.queryBuilder);
|
|
@@ -39,7 +40,11 @@ export async function checkRelationships(ds, options) {
|
|
|
39
40
|
let qb = factory
|
|
40
41
|
.table(entry.target.source)
|
|
41
42
|
.count(entry.column, ROWS_ALIAS)
|
|
42
|
-
.countDistinct(entry.column, KEYS_ALIAS);
|
|
43
|
+
.countDistinct(entry.columns ? `tuple(${entry.columns.join(', ')})` : entry.column, KEYS_ALIAS);
|
|
44
|
+
if (entry.columns) {
|
|
45
|
+
for (const column of entry.columns)
|
|
46
|
+
qb = qb.where(column, 'isNotNull', undefined);
|
|
47
|
+
}
|
|
43
48
|
if (entry.target.tenantKey && tenantPredicate) {
|
|
44
49
|
qb = qb.where(entry.target.tenantKey, tenantPredicate.operator, tenantPredicate.value);
|
|
45
50
|
}
|
package/dist/relationships.d.ts
CHANGED
|
@@ -6,9 +6,8 @@
|
|
|
6
6
|
* only: joining it would fan out and corrupt aggregates, so it is refused at
|
|
7
7
|
* query time.
|
|
8
8
|
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
* metrics.
|
|
9
|
+
* Target dimensions and safe base measures are reachable one hop deep.
|
|
10
|
+
* Use `keys` for multiple physical column equalities joined with AND.
|
|
12
11
|
*
|
|
13
12
|
* Note that the `kind` passed here is a declaration, not something checked at
|
|
14
13
|
* query time. Joins are single-match (`leftAnyJoin`, ClickHouse
|
|
@@ -37,29 +36,20 @@
|
|
|
37
36
|
* await client.execute(Orders, { dimensions: ["customer.country"] });
|
|
38
37
|
* ```
|
|
39
38
|
*/
|
|
40
|
-
import type { RelationshipDefinition } from './types.js';
|
|
39
|
+
import type { RelationshipDefinition, RelationshipJoin } from './types.js';
|
|
41
40
|
/** Many-to-one relationship (FK on this table). */
|
|
42
41
|
export declare function belongsTo<TTarget extends {
|
|
43
42
|
__type: 'dataset';
|
|
44
43
|
name: string;
|
|
45
|
-
}>(target: () => TTarget, join:
|
|
46
|
-
from: string;
|
|
47
|
-
to: string;
|
|
48
|
-
}): RelationshipDefinition<TTarget, 'belongsTo'>;
|
|
44
|
+
}>(target: () => TTarget, join: RelationshipJoin): RelationshipDefinition<TTarget, 'belongsTo'>;
|
|
49
45
|
/** One-to-many relationship (FK on target table). */
|
|
50
46
|
export declare function hasMany<TTarget extends {
|
|
51
47
|
__type: 'dataset';
|
|
52
48
|
name: string;
|
|
53
|
-
}>(target: () => TTarget, join:
|
|
54
|
-
from: string;
|
|
55
|
-
to: string;
|
|
56
|
-
}): RelationshipDefinition<TTarget, 'hasMany'>;
|
|
49
|
+
}>(target: () => TTarget, join: RelationshipJoin): RelationshipDefinition<TTarget, 'hasMany'>;
|
|
57
50
|
/** One-to-one relationship (FK on target table). */
|
|
58
51
|
export declare function hasOne<TTarget extends {
|
|
59
52
|
__type: 'dataset';
|
|
60
53
|
name: string;
|
|
61
|
-
}>(target: () => TTarget, join:
|
|
62
|
-
from: string;
|
|
63
|
-
to: string;
|
|
64
|
-
}): RelationshipDefinition<TTarget, 'hasOne'>;
|
|
54
|
+
}>(target: () => TTarget, join: RelationshipJoin): RelationshipDefinition<TTarget, 'hasOne'>;
|
|
65
55
|
//# sourceMappingURL=relationships.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"relationships.d.ts","sourceRoot":"","sources":["../src/relationships.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"relationships.d.ts","sourceRoot":"","sources":["../src/relationships.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqCG;AAEH,OAAO,KAAK,EAAE,sBAAsB,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAG3E,mDAAmD;AACnD,wBAAgB,SAAS,CAAC,OAAO,SAAS;IAAE,MAAM,EAAE,SAAS,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,EAC3E,MAAM,EAAE,MAAM,OAAO,EACrB,IAAI,EAAE,gBAAgB,GACrB,sBAAsB,CAAC,OAAO,EAAE,WAAW,CAAC,CAE9C;AAED,qDAAqD;AACrD,wBAAgB,OAAO,CAAC,OAAO,SAAS;IAAE,MAAM,EAAE,SAAS,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,EACzE,MAAM,EAAE,MAAM,OAAO,EACrB,IAAI,EAAE,gBAAgB,GACrB,sBAAsB,CAAC,OAAO,EAAE,SAAS,CAAC,CAE5C;AAED,oDAAoD;AACpD,wBAAgB,MAAM,CAAC,OAAO,SAAS;IAAE,MAAM,EAAE,SAAS,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,EACxE,MAAM,EAAE,MAAM,OAAO,EACrB,IAAI,EAAE,gBAAgB,GACrB,sBAAsB,CAAC,OAAO,EAAE,QAAQ,CAAC,CAE3C"}
|
package/dist/relationships.js
CHANGED
|
@@ -6,9 +6,8 @@
|
|
|
6
6
|
* only: joining it would fan out and corrupt aggregates, so it is refused at
|
|
7
7
|
* query time.
|
|
8
8
|
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
* metrics.
|
|
9
|
+
* Target dimensions and safe base measures are reachable one hop deep.
|
|
10
|
+
* Use `keys` for multiple physical column equalities joined with AND.
|
|
12
11
|
*
|
|
13
12
|
* Note that the `kind` passed here is a declaration, not something checked at
|
|
14
13
|
* query time. Joins are single-match (`leftAnyJoin`, ClickHouse
|
|
@@ -37,15 +36,7 @@
|
|
|
37
36
|
* await client.execute(Orders, { dimensions: ["customer.country"] });
|
|
38
37
|
* ```
|
|
39
38
|
*/
|
|
40
|
-
|
|
41
|
-
return {
|
|
42
|
-
__type: 'relationship',
|
|
43
|
-
kind,
|
|
44
|
-
target,
|
|
45
|
-
from: join.from,
|
|
46
|
-
to: join.to,
|
|
47
|
-
};
|
|
48
|
-
}
|
|
39
|
+
import { createRelationship } from './utils/relationship-definition.js';
|
|
49
40
|
/** Many-to-one relationship (FK on this table). */
|
|
50
41
|
export function belongsTo(target, join) {
|
|
51
42
|
return createRelationship('belongsTo', target, join);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"semantic-planner.d.ts","sourceRoot":"","sources":["../src/semantic-planner.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EACV,kBAAkB,EAClB,YAAY,EACZ,gBAAgB,EAGhB,WAAW,EACX,SAAS,EACT,gBAAgB,EACjB,MAAM,YAAY,CAAC;AACpB,OAAO,KAAK,EACV,QAAQ,EAIT,MAAM,oBAAoB,CAAC;
|
|
1
|
+
{"version":3,"file":"semantic-planner.d.ts","sourceRoot":"","sources":["../src/semantic-planner.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EACV,kBAAkB,EAClB,YAAY,EACZ,gBAAgB,EAGhB,WAAW,EACX,SAAS,EACT,gBAAgB,EACjB,MAAM,YAAY,CAAC;AACpB,OAAO,KAAK,EACV,QAAQ,EAIT,MAAM,oBAAoB,CAAC;AA8L5B,wBAAgB,gBAAgB,CAC9B,EAAE,EAAE,kBAAkB,EACtB,KAAK,GAAE,YAAiB,EACxB,OAAO,CAAC,EAAE,gBAAgB,GACzB,QAAQ,CAmBV;AA2FD,wBAAgB,eAAe,CAC7B,MAAM,EAAE,SAAS,GAAG,gBAAgB,EACpC,KAAK,GAAE,WAAgB,EACvB,OAAO,CAAC,EAAE,gBAAgB,GACzB,QAAQ,CAaV"}
|
package/dist/semantic-planner.js
CHANGED
|
@@ -75,6 +75,8 @@ function collectJoins(ds, dimensions = [], filters = [], orderBy = [], context)
|
|
|
75
75
|
const joins = new Map();
|
|
76
76
|
for (const name of referenced) {
|
|
77
77
|
const { relationshipName, relationship, target } = resolveQualifiedPlanField(ds, name);
|
|
78
|
+
if (relationship.keys && relationship.keys.length > 1)
|
|
79
|
+
throw new Error('Composite relationships require the queryBuilder execution path.');
|
|
78
80
|
if (joins.has(relationshipName)) {
|
|
79
81
|
continue;
|
|
80
82
|
}
|
|
@@ -151,6 +153,8 @@ function aggregatePlan(ds, query, aggregations, context) {
|
|
|
151
153
|
};
|
|
152
154
|
}
|
|
153
155
|
export function buildDatasetPlan(ds, query = {}, context) {
|
|
156
|
+
if ((query.measures ?? []).some(name => name.includes('.')))
|
|
157
|
+
throw new Error('Relationship measures require the queryBuilder execution path.');
|
|
154
158
|
if (query.timezone !== undefined)
|
|
155
159
|
throw new Error('Execution timezone requires the queryBuilder execution path.');
|
|
156
160
|
rejectTimeMeasuresOnBackend(ds, query);
|
|
@@ -14,6 +14,12 @@ export interface SemanticQuerySchemaLimits {
|
|
|
14
14
|
export interface SemanticQuerySchemaOptions extends SemanticQuerySchemaLimits {
|
|
15
15
|
/** Input field used for time grain. Serve uses `by`; agent tools use `grain`. */
|
|
16
16
|
grainField?: 'by' | 'grain';
|
|
17
|
+
/**
|
|
18
|
+
* Accept `having` conditions on dataset queries. Off by default: enable it
|
|
19
|
+
* only on surfaces that execute through the dataset client. The deployment
|
|
20
|
+
* protocol does not carry post-aggregation conditions yet.
|
|
21
|
+
*/
|
|
22
|
+
having?: boolean;
|
|
17
23
|
/** Include Serve's opt-in result metadata flag. */
|
|
18
24
|
includeMeta?: boolean;
|
|
19
25
|
/** Require at least one dimension or measure for dataset queries. */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"semantic-query-schema.d.ts","sourceRoot":"","sources":["../src/semantic-query-schema.ts"],"names":[],"mappings":"AAGA,OAAO,EAAK,KAAK,UAAU,EAAE,MAAM,KAAK,CAAC;AAEzC,OAAO,
|
|
1
|
+
{"version":3,"file":"semantic-query-schema.d.ts","sourceRoot":"","sources":["../src/semantic-query-schema.ts"],"names":[],"mappings":"AAGA,OAAO,EAAK,KAAK,UAAU,EAAE,MAAM,KAAK,CAAC;AAEzC,OAAO,EAKL,KAAK,cAAc,EACnB,KAAK,oBAAoB,EACzB,KAAK,kBAAkB,EACxB,MAAM,cAAc,CAAC;AAEtB,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAE7C,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAI1D,MAAM,WAAW,yBAAyB;IACxC,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,0BAA2B,SAAQ,yBAAyB;IAC3E,iFAAiF;IACjF,UAAU,CAAC,EAAE,IAAI,GAAG,OAAO,CAAC;IAC5B;;;;OAIG;IACH,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,mDAAmD;IACnD,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,qEAAqE;IACrE,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,iFAAiF;IACjF,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAC7B;;;;;;;;;;OAUG;IACH,iBAAiB,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;CACvC;AAED,gGAAgG;AAChG,MAAM,MAAM,2BAA2B,GAAG,IAAI,CAC5C,kBAAkB,EAClB,YAAY,GAAG,SAAS,GAAG,QAAQ,GAAG,OAAO,CAC9C,CAAC;AAEF,eAAO,MAAM,oCAAoC;;;;;;;EAO/C,CAAC;AAEH,MAAM,WAAW,6BAA6B;IAC5C,QAAQ,CAAC,YAAY,EAAE,UAAU,CAAC;IAClC,QAAQ,CAAC,WAAW,EAAE,UAAU,CAAC;IACjC,QAAQ,CAAC,sBAAsB,EAAE,UAAU,CAAC;IAC5C,QAAQ,CAAC,qBAAqB,EAAE,UAAU,CAAC;IAC3C,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;CAC/B;AAED,oFAAoF;AACpF,MAAM,MAAM,yBAAyB,GAAG,oBAAoB,GAAG,cAAc,CAAC;AA+M9E,wBAAgB,uBAAuB,CACrC,OAAO,EAAE,yBAAyB,EAClC,OAAO,GAAE,0BAA+B,GACvC,UAAU,CASZ;AAED,wBAAgB,sBAAsB,CACpC,OAAO,EAAE,yBAAyB,EAClC,UAAU,EAAE,MAAM,EAClB,OAAO,GAAE,0BAA+B,EACxC,cAAc,CAAC,EAAE,2BAA2B,GAC3C,UAAU,CAcZ;AAsBD,wBAAgB,oBAAoB,CAClC,MAAM,EAAE,UAAU,EAClB,OAAO,GAAE;IAAE,gBAAgB,CAAC,EAAE,OAAO,CAAA;CAAO,GAC3C,UAAU,CAMZ;AAyBD,wBAAgB,kCAAkC,CAChD,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,yBAAyB,CAAC,EACnD,OAAO,GAAE,0BAA+B,GACvC,6BAA6B,CAgD/B;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,+BAA+B,CAC7C,OAAO,EAAE,yBAAyB,EAClC,OAAO,GAAE,0BAA+B,EACxC,IAAI,SAAkB,GACrB,cAAc,CAEhB;AAED,kEAAkE;AAClE,wBAAgB,8BAA8B,CAC5C,OAAO,EAAE,yBAAyB,EAClC,UAAU,EAAE,MAAM,EAClB,OAAO,GAAE,0BAA+B,EACxC,cAAc,CAAC,EAAE,2BAA2B,EAC5C,IAAI,SAAiB,GACpB,cAAc,CAKhB"}
|
|
@@ -3,8 +3,8 @@ import { sha256 } from '@noble/hashes/sha2';
|
|
|
3
3
|
import { bytesToHex } from '@noble/hashes/utils';
|
|
4
4
|
import { z } from 'zod';
|
|
5
5
|
import { zodToJsonSchema } from 'zod-to-json-schema';
|
|
6
|
-
import { getDatasetCatalog, getGroupableRelationshipFields, getQueryableRelationshipFields, } from './catalog.js';
|
|
7
|
-
import { SEMANTIC_FILTER_OPERATORS } from './constants.js';
|
|
6
|
+
import { getDatasetCatalog, getGroupableRelationshipFields, getQueryableRelationshipFields, getQueryableRelationshipMeasures, } from './catalog.js';
|
|
7
|
+
import { SEMANTIC_FILTER_OPERATORS, SEMANTIC_HAVING_OPERATORS } from './constants.js';
|
|
8
8
|
import { zodToProtocolSchema } from './protocol-schema-adapter.js';
|
|
9
9
|
import { compareStrings, stableStringify, uniqueSorted } from './utils/canonical-json.js';
|
|
10
10
|
export const DEFAULT_SEMANTIC_QUERY_SCHEMA_LIMITS = Object.freeze({
|
|
@@ -78,6 +78,23 @@ function filterSchema(catalog, fields, relationshipOperators) {
|
|
|
78
78
|
return variants[0];
|
|
79
79
|
return z.union(variants);
|
|
80
80
|
}
|
|
81
|
+
/**
|
|
82
|
+
* Post-aggregation conditions on selected measures. That a condition names a
|
|
83
|
+
* *selected* measure depends on the rest of the input, so it is enforced by
|
|
84
|
+
* query validation rather than expressed here.
|
|
85
|
+
*/
|
|
86
|
+
function havingSchema(measures, maximum) {
|
|
87
|
+
const measure = fieldEnum(measures);
|
|
88
|
+
const number = z.number().finite();
|
|
89
|
+
const comparisons = SEMANTIC_HAVING_OPERATORS.filter(operator => operator !== 'between' && operator !== 'in' && operator !== 'notIn');
|
|
90
|
+
const conditions = boundedArray(z.union([
|
|
91
|
+
z.object({ measure, operator: z.enum(comparisons), value: number }).strict(),
|
|
92
|
+
z.object({ measure, operator: z.literal('between'), value: z.array(number).min(2).max(2) }).strict(),
|
|
93
|
+
z.object({ measure, operator: z.enum(['in', 'notIn']), value: z.array(number).min(1) }).strict(),
|
|
94
|
+
]), maximum);
|
|
95
|
+
return conditions.describe('Conditions on aggregated measure values, applied after grouping (SQL HAVING). '
|
|
96
|
+
+ 'Each condition must name a measure selected in this query.');
|
|
97
|
+
}
|
|
81
98
|
function queryShape(catalog, metricName, options, relationshipOperators, metric, localRelationshipFields = []) {
|
|
82
99
|
const limits = { ...DEFAULT_SEMANTIC_QUERY_SCHEMA_LIMITS, ...options };
|
|
83
100
|
const relationshipFields = getQueryableRelationshipFields(catalog);
|
|
@@ -119,10 +136,15 @@ function queryShape(catalog, metricName, options, relationshipOperators, metric,
|
|
|
119
136
|
? uniqueSorted([...dimensions, metricName, ...(grains.length > 0 ? ['period'] : [])])
|
|
120
137
|
: uniqueSorted([
|
|
121
138
|
...dimensions,
|
|
122
|
-
...Object.keys(catalog.measures),
|
|
139
|
+
...Object.keys(catalog.measures), ...getQueryableRelationshipMeasures(catalog),
|
|
123
140
|
...Object.keys(catalog.derivedMeasures ?? {}),
|
|
124
141
|
...(catalog.supportedGrains.length > 0 ? ['period'] : []),
|
|
125
142
|
]);
|
|
143
|
+
const selectableMeasures = [
|
|
144
|
+
...Object.keys(catalog.measures),
|
|
145
|
+
...getQueryableRelationshipMeasures(catalog),
|
|
146
|
+
...Object.keys(catalog.derivedMeasures ?? {}),
|
|
147
|
+
];
|
|
126
148
|
const maxResultSize = options.enforceResultLimit === false
|
|
127
149
|
? undefined
|
|
128
150
|
: lowerLimit(catalog.limits?.maxResultSize, limits.maxResultSize);
|
|
@@ -136,9 +158,14 @@ function queryShape(catalog, metricName, options, relationshipOperators, metric,
|
|
|
136
158
|
return {
|
|
137
159
|
dimensions: boundedArray(fieldEnum(dimensions), lowerLimit(catalog.limits?.maxDimensions, limits.maxDimensions)),
|
|
138
160
|
...(metricName ? {} : {
|
|
139
|
-
measures: boundedArray(fieldEnum(
|
|
161
|
+
measures: boundedArray(fieldEnum(selectableMeasures), lowerLimit(catalog.limits?.maxMeasures, limits.maxMeasures)),
|
|
140
162
|
}),
|
|
141
163
|
filters: boundedArray(filterSchema(catalog, filterFields, relationshipOperators), lowerLimit(catalog.limits?.maxFilters, limits.maxFilters)),
|
|
164
|
+
// Opt-in, so existing schemas and manifest hashes are unchanged and a
|
|
165
|
+
// surface only advertises conditions its executor can run.
|
|
166
|
+
...(!metricName && options.having ? {
|
|
167
|
+
having: havingSchema(selectableMeasures, lowerLimit(catalog.limits?.maxFilters, limits.maxFilters)),
|
|
168
|
+
} : {}),
|
|
142
169
|
// Only datasets that declare segments accept the field, so every other
|
|
143
170
|
// schema, including hosted catalogs, is unchanged.
|
|
144
171
|
...(Object.keys(catalog.segments ?? {}).length > 0 ? {
|