@hypequery/datasets 0.8.0 → 0.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (65) hide show
  1. package/dist/aggregations.d.ts +12 -0
  2. package/dist/aggregations.d.ts.map +1 -1
  3. package/dist/aggregations.js +32 -0
  4. package/dist/api.type-test.js +6 -0
  5. package/dist/catalog.d.ts +8 -1
  6. package/dist/catalog.d.ts.map +1 -1
  7. package/dist/catalog.js +18 -6
  8. package/dist/contract.d.ts +7 -1
  9. package/dist/contract.d.ts.map +1 -1
  10. package/dist/contract.js +5 -1
  11. package/dist/dataset-query.d.ts.map +1 -1
  12. package/dist/dataset-query.js +8 -5
  13. package/dist/dataset.d.ts +4 -3
  14. package/dist/dataset.d.ts.map +1 -1
  15. package/dist/dataset.js +5 -4
  16. package/dist/executor.d.ts +7 -1
  17. package/dist/executor.d.ts.map +1 -1
  18. package/dist/executor.js +39 -10
  19. package/dist/field.d.ts +4 -4
  20. package/dist/field.d.ts.map +1 -1
  21. package/dist/in-memory-backend.d.ts +5 -0
  22. package/dist/in-memory-backend.d.ts.map +1 -1
  23. package/dist/in-memory-backend.js +118 -2
  24. package/dist/index.d.ts +4 -3
  25. package/dist/index.d.ts.map +1 -1
  26. package/dist/index.js +6 -2
  27. package/dist/measure.d.ts +20 -0
  28. package/dist/measure.d.ts.map +1 -1
  29. package/dist/measure.js +51 -0
  30. package/dist/query-builder-protocol.d.ts +22 -0
  31. package/dist/query-builder-protocol.d.ts.map +1 -1
  32. package/dist/query-planner.d.ts +7 -6
  33. package/dist/query-planner.d.ts.map +1 -1
  34. package/dist/query-planner.js +98 -37
  35. package/dist/relationships.d.ts +9 -9
  36. package/dist/relationships.d.ts.map +1 -1
  37. package/dist/semantic-plan.d.ts +54 -9
  38. package/dist/semantic-plan.d.ts.map +1 -1
  39. package/dist/semantic-plan.js +9 -0
  40. package/dist/semantic-planner.d.ts.map +1 -1
  41. package/dist/semantic-planner.js +73 -5
  42. package/dist/tools.d.ts.map +1 -1
  43. package/dist/tools.js +29 -18
  44. package/dist/types.d.ts +38 -13
  45. package/dist/types.d.ts.map +1 -1
  46. package/dist/utils/dataset-normalization.d.ts +1 -1
  47. package/dist/utils/dataset-normalization.d.ts.map +1 -1
  48. package/dist/utils/dataset-normalization.js +13 -1
  49. package/dist/utils/dataset-query-validation.d.ts.map +1 -1
  50. package/dist/utils/dataset-query-validation.js +50 -12
  51. package/dist/utils/dataset-validation.d.ts.map +1 -1
  52. package/dist/utils/dataset-validation.js +21 -1
  53. package/dist/utils/filtered-aggregation-sql.d.ts +2 -1
  54. package/dist/utils/filtered-aggregation-sql.d.ts.map +1 -1
  55. package/dist/utils/filtered-aggregation-sql.js +13 -4
  56. package/dist/utils/relationship-builder-plan.d.ts +56 -0
  57. package/dist/utils/relationship-builder-plan.d.ts.map +1 -0
  58. package/dist/utils/relationship-builder-plan.js +92 -0
  59. package/dist/utils/relationship-fields.d.ts +62 -0
  60. package/dist/utils/relationship-fields.d.ts.map +1 -0
  61. package/dist/utils/relationship-fields.js +98 -0
  62. package/dist/utils/relationship-validation.d.ts +15 -0
  63. package/dist/utils/relationship-validation.d.ts.map +1 -0
  64. package/dist/utils/relationship-validation.js +29 -0
  65. package/package.json +1 -1
@@ -21,4 +21,16 @@ export declare function countDistinct(field: string): AggregationSpec;
21
21
  export declare function avg(field: string): AggregationSpec;
22
22
  export declare function min(field: string): AggregationSpec;
23
23
  export declare function max(field: string): AggregationSpec;
24
+ /** Approximate percentile of a numeric field at `level` in [0, 1]. */
25
+ export declare function percentile(field: string, level: number): AggregationSpec;
26
+ /** Median — sugar for `percentile(field, 0.5)`. */
27
+ export declare function median(field: string): AggregationSpec;
28
+ /** Value of `field` on the row where `by` is greatest. */
29
+ export declare function argMax(field: string, by: string): AggregationSpec;
30
+ /** Value of `field` on the row where `by` is smallest. */
31
+ export declare function argMin(field: string, by: string): AggregationSpec;
32
+ /** Sample standard deviation. */
33
+ export declare function stddev(field: string): AggregationSpec;
34
+ /** Sample variance. */
35
+ export declare function variance(field: string): AggregationSpec;
24
36
  //# sourceMappingURL=aggregations.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"aggregations.d.ts","sourceRoot":"","sources":["../src/aggregations.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,KAAK,EAAE,eAAe,EAAmB,MAAM,YAAY,CAAC;AAUnE,wBAAgB,GAAG,CAAC,KAAK,EAAE,MAAM,GAAG,eAAe,CAElD;AAED,wBAAgB,KAAK,CAAC,KAAK,EAAE,MAAM,GAAG,eAAe,CAEpD;AAED,wBAAgB,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG,eAAe,CAE5D;AAED,wBAAgB,GAAG,CAAC,KAAK,EAAE,MAAM,GAAG,eAAe,CAElD;AAED,wBAAgB,GAAG,CAAC,KAAK,EAAE,MAAM,GAAG,eAAe,CAElD;AAED,wBAAgB,GAAG,CAAC,KAAK,EAAE,MAAM,GAAG,eAAe,CAElD"}
1
+ {"version":3,"file":"aggregations.d.ts","sourceRoot":"","sources":["../src/aggregations.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,KAAK,EAAE,eAAe,EAAmB,MAAM,YAAY,CAAC;AAWnE,wBAAgB,GAAG,CAAC,KAAK,EAAE,MAAM,GAAG,eAAe,CAElD;AAED,wBAAgB,KAAK,CAAC,KAAK,EAAE,MAAM,GAAG,eAAe,CAEpD;AAED,wBAAgB,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG,eAAe,CAE5D;AAED,wBAAgB,GAAG,CAAC,KAAK,EAAE,MAAM,GAAG,eAAe,CAElD;AAED,wBAAgB,GAAG,CAAC,KAAK,EAAE,MAAM,GAAG,eAAe,CAElD;AAED,wBAAgB,GAAG,CAAC,KAAK,EAAE,MAAM,GAAG,eAAe,CAElD;AAED,sEAAsE;AACtE,wBAAgB,UAAU,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,eAAe,CAGxE;AAED,mDAAmD;AACnD,wBAAgB,MAAM,CAAC,KAAK,EAAE,MAAM,GAAG,eAAe,CAErD;AAED,0DAA0D;AAC1D,wBAAgB,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,eAAe,CAKjE;AAED,0DAA0D;AAC1D,wBAAgB,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,eAAe,CAKjE;AAED,iCAAiC;AACjC,wBAAgB,MAAM,CAAC,KAAK,EAAE,MAAM,GAAG,eAAe,CAErD;AAED,uBAAuB;AACvB,wBAAgB,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,eAAe,CAEvD"}
@@ -14,6 +14,7 @@
14
14
  * });
15
15
  * ```
16
16
  */
17
+ import { validatePercentileLevel } from './measure.js';
17
18
  function createAggregation(aggregation, field) {
18
19
  return {
19
20
  __type: 'aggregation_spec',
@@ -39,3 +40,34 @@ export function min(field) {
39
40
  export function max(field) {
40
41
  return createAggregation('max', field);
41
42
  }
43
+ /** Approximate percentile of a numeric field at `level` in [0, 1]. */
44
+ export function percentile(field, level) {
45
+ validatePercentileLevel(level);
46
+ return { ...createAggregation('percentile', field), level };
47
+ }
48
+ /** Median — sugar for `percentile(field, 0.5)`. */
49
+ export function median(field) {
50
+ return percentile(field, 0.5);
51
+ }
52
+ /** Value of `field` on the row where `by` is greatest. */
53
+ export function argMax(field, by) {
54
+ if (typeof by !== 'string' || by.trim().length === 0) {
55
+ throw new Error('argMax(field, by) requires a "by" field.');
56
+ }
57
+ return { ...createAggregation('argMax', field), argField: by };
58
+ }
59
+ /** Value of `field` on the row where `by` is smallest. */
60
+ export function argMin(field, by) {
61
+ if (typeof by !== 'string' || by.trim().length === 0) {
62
+ throw new Error('argMin(field, by) requires a "by" field.');
63
+ }
64
+ return { ...createAggregation('argMin', field), argField: by };
65
+ }
66
+ /** Sample standard deviation. */
67
+ export function stddev(field) {
68
+ return createAggregation('stddev', field);
69
+ }
70
+ /** Sample variance. */
71
+ export function variance(field) {
72
+ return createAggregation('variance', field);
73
+ }
@@ -80,7 +80,13 @@ const builderFactory = {
80
80
  avg: () => builderFactory.table('orders'),
81
81
  min: () => builderFactory.table('orders'),
82
82
  max: () => builderFactory.table('orders'),
83
+ argMax: () => builderFactory.table('orders'),
84
+ argMin: () => builderFactory.table('orders'),
85
+ quantile: () => builderFactory.table('orders'),
86
+ stddev: () => builderFactory.table('orders'),
87
+ variance: () => builderFactory.table('orders'),
83
88
  where: () => builderFactory.table('orders'),
89
+ leftJoin: () => builderFactory.table('orders'),
84
90
  groupBy: () => builderFactory.table('orders'),
85
91
  orderBy: () => builderFactory.table('orders'),
86
92
  limit: () => builderFactory.table('orders'),
package/dist/catalog.d.ts CHANGED
@@ -11,6 +11,10 @@ export interface DimensionCatalogEntry {
11
11
  export interface MeasureCatalogEntry {
12
12
  aggregation: MeasureDefinition['aggregation'];
13
13
  field: string;
14
+ /** Second column for argMax/argMin. */
15
+ argField?: string;
16
+ /** Percentile level in [0, 1]. */
17
+ level?: number;
14
18
  sql?: string;
15
19
  label?: string;
16
20
  description?: string;
@@ -41,7 +45,8 @@ export interface RelationshipCatalogEntry {
41
45
  target: string;
42
46
  from: string;
43
47
  to: string;
44
- execution: 'metadata_only';
48
+ queryable: boolean;
49
+ fields: string[];
45
50
  }
46
51
  export interface DatasetCatalog {
47
52
  name: string;
@@ -63,6 +68,8 @@ export type DatasetCatalogMap = Record<string, DatasetCatalog>;
63
68
  export type DatasetCatalogSource = AnyDatasetInstance & {
64
69
  metrics?: Record<string, MetricHandle>;
65
70
  };
71
+ /** All queryable relationship field names (`<relationship>.<dimension>`) a catalog advertises. */
72
+ export declare function getQueryableRelationshipFields(catalog: DatasetCatalog): string[];
66
73
  export declare function getDatasetCatalog(dataset: DatasetCatalogSource): DatasetCatalog;
67
74
  export declare function getDatasetCatalogs(datasets: Record<string, DatasetCatalogSource>): DatasetCatalogMap;
68
75
  //# sourceMappingURL=catalog.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"catalog.d.ts","sourceRoot":"","sources":["../src/catalog.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"catalog.d.ts","sourceRoot":"","sources":["../src/catalog.ts"],"names":[],"mappings":"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"}
package/dist/catalog.js CHANGED
@@ -1,4 +1,5 @@
1
1
  import { SEMANTIC_FILTER_OPERATORS, SUPPORTED_TIME_GRAINS } from './constants.js';
2
+ import { listQueryableRelationshipFields } from './utils/relationship-fields.js';
2
3
  function dimensionToCatalog(dimension) {
3
4
  return {
4
5
  type: dimension.fieldType,
@@ -14,6 +15,8 @@ function measureToCatalog(measure) {
14
15
  return {
15
16
  aggregation: measure.aggregation,
16
17
  field: measure.field,
18
+ argField: measure.argField,
19
+ level: measure.level,
17
20
  sql: measure.sql,
18
21
  label: measure.label,
19
22
  description: measure.description,
@@ -45,21 +48,32 @@ function metricToCatalog(metric) {
45
48
  requires: contract.requires,
46
49
  };
47
50
  }
48
- function relationshipToCatalog(relationship) {
51
+ function relationshipToCatalog(name, relationship) {
49
52
  return {
50
53
  kind: relationship.kind,
51
54
  target: relationship.target().name,
52
55
  from: relationship.from,
53
56
  to: relationship.to,
54
- execution: 'metadata_only',
57
+ queryable: relationship.kind !== 'hasMany',
58
+ fields: listQueryableRelationshipFields(name, relationship),
55
59
  };
56
60
  }
61
+ /** All queryable relationship field names (`<relationship>.<dimension>`) a catalog advertises. */
62
+ export function getQueryableRelationshipFields(catalog) {
63
+ return Object.values(catalog.relationships)
64
+ .filter(relationship => relationship.queryable)
65
+ .flatMap(relationship => relationship.fields);
66
+ }
57
67
  export function getDatasetCatalog(dataset) {
58
68
  const dimensionNames = Object.keys(dataset.dimensions);
59
69
  const measureNames = Object.keys(dataset.measures);
60
70
  const metricNames = Object.keys(dataset.metrics ?? {});
61
71
  const supportedGrains = dataset.timeKey ? [...SUPPORTED_TIME_GRAINS] : [];
62
72
  const maxLimit = dataset.limits?.maxResultSize;
73
+ const relationships = Object.fromEntries(Object.entries(dataset.relationships).map(([name, relationship]) => [
74
+ name,
75
+ relationshipToCatalog(name, relationship),
76
+ ]));
63
77
  return {
64
78
  name: dataset.name,
65
79
  source: dataset.source,
@@ -81,10 +95,7 @@ export function getDatasetCatalog(dataset) {
81
95
  name,
82
96
  filterToCatalog(filter, dataset.dimensions),
83
97
  ])),
84
- relationships: Object.fromEntries(Object.entries(dataset.relationships).map(([name, relationship]) => [
85
- name,
86
- relationshipToCatalog(relationship),
87
- ])),
98
+ relationships,
88
99
  limits: dataset.limits,
89
100
  requiresTenant: !!dataset.tenantKey,
90
101
  supportedGrains,
@@ -92,6 +103,7 @@ export function getDatasetCatalog(dataset) {
92
103
  ...dimensionNames,
93
104
  ...measureNames,
94
105
  ...metricNames,
106
+ ...Object.values(relationships).flatMap(relationship => relationship.fields),
95
107
  ...(dataset.timeKey ? ['period'] : []),
96
108
  ],
97
109
  maxLimit,
@@ -4,7 +4,7 @@ import type { DatasetLimits } from './types.js';
4
4
  * Version of the semantic contract format. Bump when the serialized shape
5
5
  * changes in a way that snapshot consumers must account for.
6
6
  */
7
- export declare const SEMANTIC_CONTRACT_VERSION = 1;
7
+ export declare const SEMANTIC_CONTRACT_VERSION = 2;
8
8
  export interface ContractDimension {
9
9
  type: DimensionCatalogEntry['type'];
10
10
  column?: string;
@@ -17,6 +17,10 @@ export interface ContractDimension {
17
17
  export interface ContractMeasure {
18
18
  aggregation: MeasureCatalogEntry['aggregation'];
19
19
  field: string;
20
+ /** Second column for argMax/argMin. */
21
+ argField?: string;
22
+ /** Percentile level in [0, 1]. */
23
+ level?: number;
20
24
  sql?: string;
21
25
  label?: string;
22
26
  description?: string;
@@ -45,6 +49,8 @@ export interface ContractRelationship {
45
49
  target: string;
46
50
  from: string;
47
51
  to: string;
52
+ queryable: boolean;
53
+ fields: string[];
48
54
  }
49
55
  export interface ContractDataset {
50
56
  name: string;
@@ -1 +1 @@
1
- {"version":3,"file":"contract.d.ts","sourceRoot":"","sources":["../src/contract.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"contract.d.ts","sourceRoot":"","sources":["../src/contract.ts"],"names":[],"mappings":"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"}
package/dist/contract.js CHANGED
@@ -6,7 +6,7 @@ import { sortedRecord, uniqueSorted } from './utils/canonical-json.js';
6
6
  * Version of the semantic contract format. Bump when the serialized shape
7
7
  * changes in a way that snapshot consumers must account for.
8
8
  */
9
- export const SEMANTIC_CONTRACT_VERSION = 1;
9
+ export const SEMANTIC_CONTRACT_VERSION = 2;
10
10
  /**
11
11
  * Builds a deterministic semantic contract from dataset instances.
12
12
  *
@@ -72,6 +72,8 @@ function measureToContract(entry, includeSql) {
72
72
  return {
73
73
  aggregation: entry.aggregation,
74
74
  field: entry.field,
75
+ ...(entry.argField !== undefined ? { argField: entry.argField } : {}),
76
+ ...(entry.level !== undefined ? { level: entry.level } : {}),
75
77
  ...(includeSql && entry.sql !== undefined ? { sql: normalizeSql(entry.sql) } : {}),
76
78
  ...(entry.label !== undefined ? { label: entry.label } : {}),
77
79
  ...(entry.description !== undefined ? { description: entry.description } : {}),
@@ -106,6 +108,8 @@ function relationshipToContract(entry) {
106
108
  target: entry.target,
107
109
  from: entry.from,
108
110
  to: entry.to,
111
+ queryable: entry.queryable,
112
+ fields: uniqueSorted(entry.fields),
109
113
  };
110
114
  }
111
115
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"dataset-query.d.ts","sourceRoot":"","sources":["../src/dataset-query.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,kBAAkB,EAClB,YAAY,EACZ,kBAAkB,EAClB,gBAAgB,EACjB,MAAM,YAAY,CAAC;AACpB,OAAO,KAAK,EAAE,uBAAuB,EAAE,gBAAgB,EAAE,MAAM,6BAA6B,CAAC;AAQ7F,OAAO,EAAE,KAAK,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AAoBxD,MAAM,WAAW,4BAA4B;IAC3C,cAAc,EAAE,uBAAuB,CAAC;IACxC,OAAO,CAAC,EAAE,gBAAgB,CAAC;IAC3B;;;OAGG;IACH,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB;AAED,wBAAgB,oBAAoB,CAClC,EAAE,EAAE,kBAAkB,EACtB,KAAK,EAAE,YAAY,EACnB,OAAO,CAAC,EAAE,gBAAgB,GACzB,gBAAgB,CAElB;AAED,wBAAgB,wBAAwB,CACtC,EAAE,EAAE,kBAAkB,EACtB,KAAK,EAAE,YAAY,EACnB,OAAO,EAAE,4BAA4B,GACpC,gBAAgB,CAwClB;AAED,wBAAsB,eAAe,CACnC,EAAE,EAAE,kBAAkB,EACtB,KAAK,EAAE,YAAY,EACnB,OAAO,EAAE,4BAA4B,GACpC,OAAO,CAAC,kBAAkB,CAAC,CAa7B"}
1
+ {"version":3,"file":"dataset-query.d.ts","sourceRoot":"","sources":["../src/dataset-query.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,kBAAkB,EAClB,YAAY,EACZ,kBAAkB,EAClB,gBAAgB,EACjB,MAAM,YAAY,CAAC;AACpB,OAAO,KAAK,EAAE,uBAAuB,EAAE,gBAAgB,EAAE,MAAM,6BAA6B,CAAC;AAQ7F,OAAO,EAAE,KAAK,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AAyBxD,MAAM,WAAW,4BAA4B;IAC3C,cAAc,EAAE,uBAAuB,CAAC;IACxC,OAAO,CAAC,EAAE,gBAAgB,CAAC;IAC3B;;;OAGG;IACH,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB;AAED,wBAAgB,oBAAoB,CAClC,EAAE,EAAE,kBAAkB,EACtB,KAAK,EAAE,YAAY,EACnB,OAAO,CAAC,EAAE,gBAAgB,GACzB,gBAAgB,CAElB;AAED,wBAAgB,wBAAwB,CACtC,EAAE,EAAE,kBAAkB,EACtB,KAAK,EAAE,YAAY,EACnB,OAAO,EAAE,4BAA4B,GACpC,gBAAgB,CA4ClB;AAED,wBAAsB,eAAe,CACnC,EAAE,EAAE,kBAAkB,EACtB,KAAK,EAAE,YAAY,EACnB,OAAO,EAAE,4BAA4B,GACpC,OAAO,CAAC,kBAAkB,CAAC,CAa7B"}
@@ -2,6 +2,7 @@ import { appendOrderLimitOffset, applyMeasureDefinition, buildDimensionSelection
2
2
  import { validateDatasetQueryInput } from './utils/dataset-query-validation.js';
3
3
  import { getRuntimeTenantId, getRuntimeTenantPredicate, } from './utils/tenant-runtime.js';
4
4
  import { applyPagination, overfetchLimit } from './utils/pagination.js';
5
+ import { applyRelationshipJoins, buildRelationshipBuilderContext, qualifyBaseColumn, } from './utils/relationship-builder-plan.js';
5
6
  function toResultMeta(qb, timingMs, context) {
6
7
  return {
7
8
  sql: qb.toSQLWithParams().sql,
@@ -17,14 +18,16 @@ export function buildDatasetQueryBuilder(ds, query, options) {
17
18
  if (!validation.valid) {
18
19
  throw new Error(`Invalid dataset query: ${validation.errors.join('; ')}`);
19
20
  }
21
+ const joinCtx = buildRelationshipBuilderContext(ds, query, options.context);
20
22
  let qb = options.builderFactory.table(ds.source);
21
- const { selectParts, groupByParts } = buildDimensionSelectionPlan(ds, query.dimensions ?? [], query.by);
23
+ qb = applyRelationshipJoins(qb, joinCtx);
24
+ const { selectParts, groupByParts } = buildDimensionSelectionPlan(ds, query.dimensions ?? [], query.by, joinCtx);
22
25
  const measureNames = query.measures ?? Object.keys(ds.measures);
23
26
  if (selectParts.length > 0) {
24
27
  qb = qb.select(selectParts);
25
28
  }
26
29
  for (const measureName of measureNames) {
27
- qb = applyMeasureDefinition(qb, ds, measureName, ds.measures[measureName]);
30
+ qb = applyMeasureDefinition(qb, ds, measureName, ds.measures[measureName], joinCtx);
28
31
  }
29
32
  if (groupByParts.length > 0) {
30
33
  qb = qb.groupBy(groupByParts);
@@ -32,13 +35,13 @@ export function buildDatasetQueryBuilder(ds, query, options) {
32
35
  const tenantColumn = resolveTenantFilterColumn(ds, options.context);
33
36
  const tenantPredicate = getRuntimeTenantPredicate(options.context);
34
37
  if (tenantPredicate && tenantColumn) {
35
- qb = qb.where(tenantColumn, tenantPredicate.operator, tenantPredicate.value);
38
+ qb = qb.where(qualifyBaseColumn(joinCtx, tenantColumn), tenantPredicate.operator, tenantPredicate.value);
36
39
  }
37
40
  for (const filter of query.filters ?? []) {
38
- const resolvedField = resolveFilterField(ds, filter.field);
41
+ const resolvedField = resolveFilterField(ds, filter.field, joinCtx);
39
42
  qb = qb.where(resolvedField, filter.operator, filter.value);
40
43
  }
41
- return appendOrderLimitOffset(qb, query.orderBy, query.by, options.executionLimit ?? query.limit, query.offset);
44
+ return appendOrderLimitOffset(qb, query.orderBy, query.by, options.executionLimit ?? query.limit, query.offset, joinCtx);
42
45
  }
43
46
  export async function runDatasetQuery(ds, query, options) {
44
47
  const start = Date.now();
package/dist/dataset.d.ts CHANGED
@@ -1,9 +1,10 @@
1
1
  /**
2
2
  * dataset() — creates a typed semantic model over a physical table.
3
3
  *
4
- * Relationships are modeled on the dataset today, but current query execution
5
- * only supports same-dataset dimensions, measures, and metrics. Joined
6
- * relationship traversal is not part of the shipped execution surface yet.
4
+ * Relationships are modeled on the dataset. To-one relationships (`belongsTo`,
5
+ * `hasOne`) are queryable one hop deep as `<relationship>.<dimension>` and
6
+ * execute as LEFT JOINs; `hasMany` relationships remain metadata-only to avoid
7
+ * fan-out corrupting aggregates.
7
8
  *
8
9
  * @example
9
10
  * ```ts
@@ -1 +1 @@
1
- {"version":3,"file":"dataset.d.ts","sourceRoot":"","sources":["../src/dataset.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AAEH,OAAO,KAAK,EACV,aAAa,EACb,eAAe,EACf,mBAAmB,EACnB,iBAAiB,EACjB,sBAAsB,EAKvB,MAAM,YAAY,CAAC;AAkBpB,wBAAgB,OAAO,CACrB,YAAY,SAAS,MAAM,EAC3B,WAAW,SAAS,MAAM,CAAC,MAAM,EAAE,mBAAmB,CAAC,EACvD,SAAS,SAAS,MAAM,CAAC,MAAM,EAAE,iBAAiB,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC,EAC3E,cAAc,SAAS,MAAM,CAAC,MAAM,EAAE,sBAAsB,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC,EAErF,IAAI,EAAE,YAAY,EAClB,MAAM,EAAE,aAAa,CAAC,WAAW,EAAE,SAAS,EAAE,cAAc,CAAC,GAC5D,eAAe,CAAC,WAAW,EAAE,SAAS,EAAE,cAAc,EAAE,YAAY,CAAC,CA2DvE"}
1
+ {"version":3,"file":"dataset.d.ts","sourceRoot":"","sources":["../src/dataset.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AAEH,OAAO,KAAK,EACV,aAAa,EACb,eAAe,EACf,mBAAmB,EACnB,iBAAiB,EACjB,sBAAsB,EAKvB,MAAM,YAAY,CAAC;AAkBpB,wBAAgB,OAAO,CACrB,YAAY,SAAS,MAAM,EAC3B,WAAW,SAAS,MAAM,CAAC,MAAM,EAAE,mBAAmB,CAAC,EACvD,SAAS,SAAS,MAAM,CAAC,MAAM,EAAE,iBAAiB,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC,EAC3E,cAAc,SAAS,MAAM,CAAC,MAAM,EAAE,sBAAsB,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC,EAErF,IAAI,EAAE,YAAY,EAClB,MAAM,EAAE,aAAa,CAAC,WAAW,EAAE,SAAS,EAAE,cAAc,CAAC,GAC5D,eAAe,CAAC,WAAW,EAAE,SAAS,EAAE,cAAc,EAAE,YAAY,CAAC,CA2DvE"}
package/dist/dataset.js CHANGED
@@ -1,9 +1,10 @@
1
1
  /**
2
2
  * dataset() — creates a typed semantic model over a physical table.
3
3
  *
4
- * Relationships are modeled on the dataset today, but current query execution
5
- * only supports same-dataset dimensions, measures, and metrics. Joined
6
- * relationship traversal is not part of the shipped execution surface yet.
4
+ * Relationships are modeled on the dataset. To-one relationships (`belongsTo`,
5
+ * `hasOne`) are queryable one hop deep as `<relationship>.<dimension>` and
6
+ * execute as LEFT JOINs; `hasMany` relationships remain metadata-only to avoid
7
+ * fan-out corrupting aggregates.
7
8
  *
8
9
  * @example
9
10
  * ```ts
@@ -35,7 +36,7 @@ export function dataset(name, config) {
35
36
  const dimensions = normalizeDimensions(config);
36
37
  const measures = normalizeMeasures(config.measures);
37
38
  const filters = normalizeFilters(dimensions, config.filters);
38
- const relationships = normalizeRelationships(config.relationships);
39
+ const relationships = normalizeRelationships(config.relationships, config.source);
39
40
  function metric(metricName, metricConfig) {
40
41
  if (isDerivedMetricConfig(metricConfig)) {
41
42
  validateDerivedMetric(ds, metricName, metricConfig);
@@ -17,7 +17,13 @@ export interface MetricQueryEngineOptions {
17
17
  export interface CreateDatasetClientOptions {
18
18
  /** Query builder factory for executing semantic metric and dataset queries. */
19
19
  queryBuilder?: QueryBuilderFactoryInput;
20
- /** Semantic backend for executing neutral semantic plans. */
20
+ /**
21
+ * Semantic backend for executing neutral semantic plans.
22
+ *
23
+ * @deprecated Use `queryBuilder` instead — the query-builder path is
24
+ * canonical. The plan/backend path is frozen (bug fixes only) and will not
25
+ * gain new features.
26
+ */
21
27
  backend?: SemanticBackend;
22
28
  /**
23
29
  * Result-cache defaults for this client. Results are keyed by the canonical
@@ -1 +1 @@
1
- {"version":3,"file":"executor.d.ts","sourceRoot":"","sources":["../src/executor.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"executor.d.ts","sourceRoot":"","sources":["../src/executor.ts"],"names":[],"mappings":"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"}
package/dist/executor.js CHANGED
@@ -5,7 +5,7 @@
5
5
  * stays database-agnostic via query-builder and backend protocol interfaces.
6
6
  */
7
7
  import { toQueryBuilderFactory } from './query-builder-protocol.js';
8
- import { validateSQLIdentifier } from './sql-utils.js';
8
+ import { quoteSQLIdentifier, validateSQLIdentifier } from './sql-utils.js';
9
9
  import { SUPPORTED_TIME_GRAINS, isSupportedTimeGrain } from './constants.js';
10
10
  import { validateFilterValue } from './validation.js';
11
11
  import { applyAggregationSpec, appendOrderLimitOffset, buildDimensionSelectionPlan, resolveFilterField, resolveTenantFilterColumn, } from './query-planner.js';
@@ -17,6 +17,9 @@ import { getRuntimeTenantId, getRuntimeTenantPredicate, validateTenantRuntime, }
17
17
  import { applyPagination, overfetchLimit } from './utils/pagination.js';
18
18
  import { SemanticQueryCache, } from './cache/semantic-query-cache.js';
19
19
  import { buildDatasetQuerySignature, buildMetricQuerySignature, } from './cache/query-signature.js';
20
+ import { isQualifiedField, resolveQualifiedField } from './utils/relationship-fields.js';
21
+ import { validateQualifiedFilter } from './utils/relationship-validation.js';
22
+ import { applyRelationshipJoins, buildRelationshipBuilderContext, qualifyBaseColumn, } from './utils/relationship-builder-plan.js';
20
23
  function validateQuery(metric, query, context) {
21
24
  const errors = [];
22
25
  const ref = getMetricRef(metric);
@@ -40,12 +43,26 @@ function validateQuery(metric, query, context) {
40
43
  }
41
44
  // Validate dimensions
42
45
  for (const dim of query.dimensions ?? []) {
46
+ if (isQualifiedField(dim)) {
47
+ const resolution = resolveQualifiedField(ds, dim);
48
+ if (resolution?.error) {
49
+ errors.push(resolution.error);
50
+ }
51
+ continue;
52
+ }
43
53
  if (!dimensionNames.includes(dim)) {
44
54
  errors.push(`Unknown dimension "${dim}". Available: ${dimensionNames.join(', ')}`);
45
55
  }
46
56
  }
47
57
  // Validate filters
48
58
  for (const filter of query.filters ?? []) {
59
+ if (isQualifiedField(filter.field)) {
60
+ const filterError = validateQualifiedFilter(ds, filter, context);
61
+ if (filterError) {
62
+ errors.push(filterError);
63
+ }
64
+ continue;
65
+ }
49
66
  if (!filterNames.includes(filter.field)) {
50
67
  errors.push(`Unknown filter field "${filter.field}". Available: ${filterNames.join(', ')}`);
51
68
  continue;
@@ -206,13 +223,15 @@ export class MetricQueryEngine {
206
223
  }
207
224
  buildBaseQuery(ref, spec, ds, query, grain, context) {
208
225
  const activeBuilderFactory = resolveBuilderFactory(context, this.builderFactory);
226
+ const joinCtx = buildRelationshipBuilderContext(ds, query, context);
209
227
  let qb = activeBuilderFactory.table(ds.source);
210
- const { selectParts, groupByParts } = buildDimensionSelectionPlan(ds, query.dimensions ?? [], grain);
228
+ qb = applyRelationshipJoins(qb, joinCtx);
229
+ const { selectParts, groupByParts } = buildDimensionSelectionPlan(ds, query.dimensions ?? [], grain, joinCtx);
211
230
  if (selectParts.length > 0) {
212
231
  qb = qb.select(selectParts);
213
232
  }
214
233
  // Aggregation (appends to select, auto-sets groupBy on non-agg columns)
215
- qb = applyAggregationSpec(qb, ds, spec, ref.name);
234
+ qb = applyAggregationSpec(qb, ds, spec, ref.name, joinCtx);
216
235
  // Explicit groupBy (ensures period + dims are grouped even if aggregation auto-groupBy misses them)
217
236
  if (groupByParts.length > 0) {
218
237
  qb = qb.groupBy(groupByParts);
@@ -221,26 +240,28 @@ export class MetricQueryEngine {
221
240
  const tenantColumn = resolveTenantFilterColumn(ds, context);
222
241
  const tenantPredicate = getRuntimeTenantPredicate(context);
223
242
  if (tenantPredicate && tenantColumn) {
224
- qb = qb.where(tenantColumn, tenantPredicate.operator, tenantPredicate.value);
243
+ qb = qb.where(qualifyBaseColumn(joinCtx, tenantColumn), tenantPredicate.operator, tenantPredicate.value);
225
244
  }
226
245
  // User filters
227
246
  for (const filter of query.filters ?? []) {
228
247
  if (isTenantScopedFilter(ds, filter, context)) {
229
248
  throw new Error(`Cannot filter on tenant field "${filter.field}" when runtime tenancy enforcement is active.`);
230
249
  }
231
- const resolvedField = resolveFilterField(ds, filter.field);
250
+ const resolvedField = resolveFilterField(ds, filter.field, joinCtx);
232
251
  qb = qb.where(resolvedField, filter.operator, filter.value);
233
252
  }
234
253
  // Order, limit, offset
235
- qb = appendOrderLimitOffset(qb, query.orderBy, grain, query.limit, query.offset);
254
+ qb = appendOrderLimitOffset(qb, query.orderBy, grain, query.limit, query.offset, joinCtx);
236
255
  return qb;
237
256
  }
238
257
  buildDerivedSQLViaBuilder(ref, spec, query, grain, context) {
239
258
  const activeBuilderFactory = resolveBuilderFactory(context, this.builderFactory);
240
259
  const ds = ref.dataset;
260
+ const joinCtx = buildRelationshipBuilderContext(ds, query, context);
241
261
  // Build the CTE inner query using the builder
242
262
  let cteBuilder = activeBuilderFactory.table(ds.source);
243
- const { selectParts, groupByParts } = buildDimensionSelectionPlan(ds, query.dimensions ?? [], grain);
263
+ cteBuilder = applyRelationshipJoins(cteBuilder, joinCtx);
264
+ const { selectParts, groupByParts } = buildDimensionSelectionPlan(ds, query.dimensions ?? [], grain, joinCtx);
244
265
  if (selectParts.length > 0) {
245
266
  cteBuilder = cteBuilder.select(selectParts);
246
267
  }
@@ -252,7 +273,7 @@ export class MetricQueryEngine {
252
273
  if (baseSpec.__type !== 'aggregation_spec') {
253
274
  throw new Error(`Derived metric "${ref.name}" references non-base metric "${alias}".`);
254
275
  }
255
- cteBuilder = applyAggregationSpec(cteBuilder, ds, baseSpec, alias);
276
+ cteBuilder = applyAggregationSpec(cteBuilder, ds, baseSpec, alias, joinCtx);
256
277
  refAliases[alias] = alias;
257
278
  aggregateAliases.push(alias);
258
279
  }
@@ -263,13 +284,13 @@ export class MetricQueryEngine {
263
284
  const tenantColumn = resolveTenantFilterColumn(ds, context);
264
285
  const tenantPredicate = getRuntimeTenantPredicate(context);
265
286
  if (tenantPredicate && tenantColumn) {
266
- cteBuilder = cteBuilder.where(tenantColumn, tenantPredicate.operator, tenantPredicate.value);
287
+ cteBuilder = cteBuilder.where(qualifyBaseColumn(joinCtx, tenantColumn), tenantPredicate.operator, tenantPredicate.value);
267
288
  }
268
289
  for (const filter of query.filters ?? []) {
269
290
  if (isTenantScopedFilter(ds, filter, context)) {
270
291
  throw new Error(`Cannot filter on tenant field "${filter.field}" when runtime tenancy enforcement is active.`);
271
292
  }
272
- const resolvedField = resolveFilterField(ds, filter.field);
293
+ const resolvedField = resolveFilterField(ds, filter.field, joinCtx);
273
294
  cteBuilder = cteBuilder.where(resolvedField, filter.operator, filter.value);
274
295
  }
275
296
  const { sql: cteSql, parameters: cteParams } = cteBuilder.toSQLWithParams();
@@ -283,6 +304,11 @@ export class MetricQueryEngine {
283
304
  if (grain)
284
305
  outerSelectParts.push('period');
285
306
  for (const dim of query.dimensions ?? []) {
307
+ // Joined dimensions surface from the CTE under their quoted qualified alias.
308
+ if (joinCtx && isQualifiedField(dim)) {
309
+ outerSelectParts.push(quoteSQLIdentifier(dim));
310
+ continue;
311
+ }
286
312
  validateSQLIdentifier(dim, 'dimension name');
287
313
  outerSelectParts.push(dim);
288
314
  }
@@ -293,6 +319,9 @@ export class MetricQueryEngine {
293
319
  // ORDER BY
294
320
  if (query.orderBy && query.orderBy.length > 0) {
295
321
  const orderParts = query.orderBy.map(o => {
322
+ if (joinCtx && isQualifiedField(o.field)) {
323
+ return `${quoteSQLIdentifier(o.field)} ${o.direction.toUpperCase()}`;
324
+ }
296
325
  validateSQLIdentifier(o.field, 'order by field');
297
326
  return `${o.field} ${o.direction.toUpperCase()}`;
298
327
  });
package/dist/field.d.ts CHANGED
@@ -17,9 +17,9 @@
17
17
  */
18
18
  import type { DimensionDefinition, DimensionOptions } from './types.js';
19
19
  export declare const dimension: {
20
- readonly string: (opts?: DimensionOptions) => DimensionDefinition<"string">;
21
- readonly number: (opts?: DimensionOptions) => DimensionDefinition<"number">;
22
- readonly boolean: (opts?: DimensionOptions) => DimensionDefinition<"boolean">;
23
- readonly timestamp: (opts?: DimensionOptions) => DimensionDefinition<"timestamp">;
20
+ readonly string: <TOptions extends DimensionOptions | undefined = undefined>(opts?: TOptions | undefined) => DimensionDefinition<"string"> & (TOptions extends DimensionOptions ? TOptions : object);
21
+ readonly number: <TOptions extends DimensionOptions | undefined = undefined>(opts?: TOptions | undefined) => DimensionDefinition<"number"> & (TOptions extends DimensionOptions ? TOptions : object);
22
+ readonly boolean: <TOptions extends DimensionOptions | undefined = undefined>(opts?: TOptions | undefined) => DimensionDefinition<"boolean"> & (TOptions extends DimensionOptions ? TOptions : object);
23
+ readonly timestamp: <TOptions extends DimensionOptions | undefined = undefined>(opts?: TOptions | undefined) => DimensionDefinition<"timestamp"> & (TOptions extends DimensionOptions ? TOptions : object);
24
24
  };
25
25
  //# sourceMappingURL=field.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"field.d.ts","sourceRoot":"","sources":["../src/field.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,OAAO,KAAK,EAAE,mBAAmB,EAAE,gBAAgB,EAAa,MAAM,YAAY,CAAC;AAenF,eAAO,MAAM,SAAS;6BAZL,gBAAgB;6BAAhB,gBAAgB;8BAAhB,gBAAgB;gCAAhB,gBAAgB;CAiBvB,CAAC"}
1
+ {"version":3,"file":"field.d.ts","sourceRoot":"","sources":["../src/field.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,OAAO,KAAK,EAAE,mBAAmB,EAAE,gBAAgB,EAAa,MAAM,YAAY,CAAC;AAiBnF,eAAO,MAAM,SAAS;sBAdZ,QAAQ,SAAS,gBAAgB,GAAG,SAAS;sBAA7C,QAAQ,SAAS,gBAAgB,GAAG,SAAS;uBAA7C,QAAQ,SAAS,gBAAgB,GAAG,SAAS;yBAA7C,QAAQ,SAAS,gBAAgB,GAAG,SAAS;CAmB7C,CAAC"}
@@ -1,5 +1,10 @@
1
1
  import type { SemanticBackend } from './semantic-plan.js';
2
2
  export type InMemoryTable = Array<Record<string, unknown>>;
3
3
  export type InMemoryTables = Record<string, InMemoryTable>;
4
+ /**
5
+ * @deprecated The plan/backend execution path is frozen (bug fixes only); this
6
+ * backend remains as the test harness for that path. New code should use
7
+ * `createDatasetClient({ queryBuilder })`.
8
+ */
4
9
  export declare function createInMemoryBackend(tables: InMemoryTables): SemanticBackend;
5
10
  //# sourceMappingURL=in-memory-backend.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"in-memory-backend.d.ts","sourceRoot":"","sources":["../src/in-memory-backend.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAGV,eAAe,EAIhB,MAAM,oBAAoB,CAAC;AAE5B,MAAM,MAAM,aAAa,GAAG,KAAK,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;AAC3D,MAAM,MAAM,cAAc,GAAG,MAAM,CAAC,MAAM,EAAE,aAAa,CAAC,CAAC;AAoM3D,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,cAAc,GAAG,eAAe,CAuE7E"}
1
+ {"version":3,"file":"in-memory-backend.d.ts","sourceRoot":"","sources":["../src/in-memory-backend.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAGV,eAAe,EAKhB,MAAM,oBAAoB,CAAC;AAE5B,MAAM,MAAM,aAAa,GAAG,KAAK,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;AAC3D,MAAM,MAAM,cAAc,GAAG,MAAM,CAAC,MAAM,EAAE,aAAa,CAAC,CAAC;AA0U3D;;;;GAIG;AACH,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,cAAc,GAAG,eAAe,CAwE7E"}