@hypequery/datasets 0.8.0 → 0.9.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 (57) hide show
  1. package/dist/api.type-test.js +1 -0
  2. package/dist/catalog.d.ts +4 -1
  3. package/dist/catalog.d.ts.map +1 -1
  4. package/dist/catalog.js +16 -6
  5. package/dist/contract.d.ts +3 -1
  6. package/dist/contract.d.ts.map +1 -1
  7. package/dist/contract.js +3 -1
  8. package/dist/dataset-query.d.ts.map +1 -1
  9. package/dist/dataset-query.js +8 -5
  10. package/dist/dataset.d.ts +4 -3
  11. package/dist/dataset.d.ts.map +1 -1
  12. package/dist/dataset.js +5 -4
  13. package/dist/executor.d.ts +7 -1
  14. package/dist/executor.d.ts.map +1 -1
  15. package/dist/executor.js +39 -10
  16. package/dist/field.d.ts +4 -4
  17. package/dist/field.d.ts.map +1 -1
  18. package/dist/in-memory-backend.d.ts +5 -0
  19. package/dist/in-memory-backend.d.ts.map +1 -1
  20. package/dist/in-memory-backend.js +41 -1
  21. package/dist/index.d.ts +3 -2
  22. package/dist/index.d.ts.map +1 -1
  23. package/dist/index.js +5 -1
  24. package/dist/query-builder-protocol.d.ts +12 -0
  25. package/dist/query-builder-protocol.d.ts.map +1 -1
  26. package/dist/query-planner.d.ts +7 -6
  27. package/dist/query-planner.d.ts.map +1 -1
  28. package/dist/query-planner.js +63 -16
  29. package/dist/relationships.d.ts +9 -9
  30. package/dist/relationships.d.ts.map +1 -1
  31. package/dist/semantic-plan.d.ts +50 -9
  32. package/dist/semantic-plan.d.ts.map +1 -1
  33. package/dist/semantic-plan.js +9 -0
  34. package/dist/semantic-planner.d.ts.map +1 -1
  35. package/dist/semantic-planner.js +67 -5
  36. package/dist/tools.d.ts.map +1 -1
  37. package/dist/tools.js +29 -18
  38. package/dist/types.d.ts +29 -12
  39. package/dist/types.d.ts.map +1 -1
  40. package/dist/utils/dataset-normalization.d.ts +1 -1
  41. package/dist/utils/dataset-normalization.d.ts.map +1 -1
  42. package/dist/utils/dataset-normalization.js +11 -1
  43. package/dist/utils/dataset-query-validation.d.ts.map +1 -1
  44. package/dist/utils/dataset-query-validation.js +50 -12
  45. package/dist/utils/filtered-aggregation-sql.d.ts +2 -1
  46. package/dist/utils/filtered-aggregation-sql.d.ts.map +1 -1
  47. package/dist/utils/filtered-aggregation-sql.js +4 -4
  48. package/dist/utils/relationship-builder-plan.d.ts +56 -0
  49. package/dist/utils/relationship-builder-plan.d.ts.map +1 -0
  50. package/dist/utils/relationship-builder-plan.js +92 -0
  51. package/dist/utils/relationship-fields.d.ts +62 -0
  52. package/dist/utils/relationship-fields.d.ts.map +1 -0
  53. package/dist/utils/relationship-fields.js +98 -0
  54. package/dist/utils/relationship-validation.d.ts +15 -0
  55. package/dist/utils/relationship-validation.d.ts.map +1 -0
  56. package/dist/utils/relationship-validation.js +29 -0
  57. package/package.json +1 -1
@@ -81,6 +81,7 @@ const builderFactory = {
81
81
  min: () => builderFactory.table('orders'),
82
82
  max: () => builderFactory.table('orders'),
83
83
  where: () => builderFactory.table('orders'),
84
+ leftJoin: () => builderFactory.table('orders'),
84
85
  groupBy: () => builderFactory.table('orders'),
85
86
  orderBy: () => builderFactory.table('orders'),
86
87
  limit: () => builderFactory.table('orders'),
package/dist/catalog.d.ts CHANGED
@@ -41,7 +41,8 @@ export interface RelationshipCatalogEntry {
41
41
  target: string;
42
42
  from: string;
43
43
  to: string;
44
- execution: 'metadata_only';
44
+ queryable: boolean;
45
+ fields: string[];
45
46
  }
46
47
  export interface DatasetCatalog {
47
48
  name: string;
@@ -63,6 +64,8 @@ export type DatasetCatalogMap = Record<string, DatasetCatalog>;
63
64
  export type DatasetCatalogSource = AnyDatasetInstance & {
64
65
  metrics?: Record<string, MetricHandle>;
65
66
  };
67
+ /** All queryable relationship field names (`<relationship>.<dimension>`) a catalog advertises. */
68
+ export declare function getQueryableRelationshipFields(catalog: DatasetCatalog): string[];
66
69
  export declare function getDatasetCatalog(dataset: DatasetCatalogSource): DatasetCatalog;
67
70
  export declare function getDatasetCatalogs(datasets: Record<string, DatasetCatalogSource>): DatasetCatalogMap;
68
71
  //# 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,
@@ -45,21 +46,32 @@ function metricToCatalog(metric) {
45
46
  requires: contract.requires,
46
47
  };
47
48
  }
48
- function relationshipToCatalog(relationship) {
49
+ function relationshipToCatalog(name, relationship) {
49
50
  return {
50
51
  kind: relationship.kind,
51
52
  target: relationship.target().name,
52
53
  from: relationship.from,
53
54
  to: relationship.to,
54
- execution: 'metadata_only',
55
+ queryable: relationship.kind !== 'hasMany',
56
+ fields: listQueryableRelationshipFields(name, relationship),
55
57
  };
56
58
  }
59
+ /** All queryable relationship field names (`<relationship>.<dimension>`) a catalog advertises. */
60
+ export function getQueryableRelationshipFields(catalog) {
61
+ return Object.values(catalog.relationships)
62
+ .filter(relationship => relationship.queryable)
63
+ .flatMap(relationship => relationship.fields);
64
+ }
57
65
  export function getDatasetCatalog(dataset) {
58
66
  const dimensionNames = Object.keys(dataset.dimensions);
59
67
  const measureNames = Object.keys(dataset.measures);
60
68
  const metricNames = Object.keys(dataset.metrics ?? {});
61
69
  const supportedGrains = dataset.timeKey ? [...SUPPORTED_TIME_GRAINS] : [];
62
70
  const maxLimit = dataset.limits?.maxResultSize;
71
+ const relationships = Object.fromEntries(Object.entries(dataset.relationships).map(([name, relationship]) => [
72
+ name,
73
+ relationshipToCatalog(name, relationship),
74
+ ]));
63
75
  return {
64
76
  name: dataset.name,
65
77
  source: dataset.source,
@@ -81,10 +93,7 @@ export function getDatasetCatalog(dataset) {
81
93
  name,
82
94
  filterToCatalog(filter, dataset.dimensions),
83
95
  ])),
84
- relationships: Object.fromEntries(Object.entries(dataset.relationships).map(([name, relationship]) => [
85
- name,
86
- relationshipToCatalog(relationship),
87
- ])),
96
+ relationships,
88
97
  limits: dataset.limits,
89
98
  requiresTenant: !!dataset.tenantKey,
90
99
  supportedGrains,
@@ -92,6 +101,7 @@ export function getDatasetCatalog(dataset) {
92
101
  ...dimensionNames,
93
102
  ...measureNames,
94
103
  ...metricNames,
104
+ ...Object.values(relationships).flatMap(relationship => relationship.fields),
95
105
  ...(dataset.timeKey ? ['period'] : []),
96
106
  ],
97
107
  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;
@@ -45,6 +45,8 @@ export interface ContractRelationship {
45
45
  target: string;
46
46
  from: string;
47
47
  to: string;
48
+ queryable: boolean;
49
+ fields: string[];
48
50
  }
49
51
  export interface ContractDataset {
50
52
  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
  *
@@ -106,6 +106,8 @@ function relationshipToContract(entry) {
106
106
  target: entry.target,
107
107
  from: entry.from,
108
108
  to: entry.to,
109
+ queryable: entry.queryable,
110
+ fields: uniqueSorted(entry.fields),
109
111
  };
110
112
  }
111
113
  /**
@@ -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;AA6O3D;;;;GAIG;AACH,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,cAAc,GAAG,eAAe,CAwE7E"}
@@ -44,6 +44,40 @@ function applyTenant(rows, tenant) {
44
44
  }
45
45
  return rows.filter((row) => row[tenant.field] === tenant.value);
46
46
  }
47
+ /**
48
+ * Enriches base rows with columns from to-one joined targets, keyed by the
49
+ * qualified name `<relationship>.<column>`. Mirrors a query-time LEFT JOIN:
50
+ * base rows without a matching target keep the joined columns undefined.
51
+ */
52
+ function applyJoins(rows, joins, tables) {
53
+ if (!joins || joins.length === 0) {
54
+ return rows;
55
+ }
56
+ const indexes = joins.map((join) => {
57
+ const targetRows = applyTenant(tables[join.source] ?? [], join.tenant);
58
+ const index = new Map();
59
+ for (const row of targetRows) {
60
+ const key = String(row[join.to]);
61
+ // To-one: first matching target wins; a mis-declared to-many still won't fan out.
62
+ if (!index.has(key)) {
63
+ index.set(key, row);
64
+ }
65
+ }
66
+ return { join, index };
67
+ });
68
+ return rows.map((row) => {
69
+ const enriched = { ...row };
70
+ for (const { join, index } of indexes) {
71
+ const match = index.get(String(row[join.from]));
72
+ if (match) {
73
+ for (const [column, value] of Object.entries(match)) {
74
+ enriched[`${join.relationship}.${column}`] = value;
75
+ }
76
+ }
77
+ }
78
+ return enriched;
79
+ });
80
+ }
47
81
  function periodForValue(value, grain) {
48
82
  const date = new Date(String(value));
49
83
  if (Number.isNaN(date.getTime())) {
@@ -182,10 +216,16 @@ function serializeMeasures(rows, measures) {
182
216
  return next;
183
217
  });
184
218
  }
219
+ /**
220
+ * @deprecated The plan/backend execution path is frozen (bug fixes only); this
221
+ * backend remains as the test harness for that path. New code should use
222
+ * `createDatasetClient({ queryBuilder })`.
223
+ */
185
224
  export function createInMemoryBackend(tables) {
186
225
  function executeAggregate(plan) {
187
226
  const table = tables[plan.source] ?? [];
188
- const filteredRows = applyFilters(applyTenant(table, plan.tenant), plan.filters);
227
+ const joinedRows = applyJoins(table, plan.joins, tables);
228
+ const filteredRows = applyFilters(applyTenant(joinedRows, plan.tenant), plan.filters);
189
229
  const groups = new Map();
190
230
  for (const row of filteredRows) {
191
231
  const key = groupKey(row, plan);
package/dist/index.d.ts CHANGED
@@ -6,7 +6,8 @@ export { sum, count, countDistinct, avg, min, max } from './aggregations.js';
6
6
  export { divide, multiply, subtract, add, nullIfZero, coalesce, round, floor, ceil, } from './formulas.js';
7
7
  export { eq, neq, gt, gte, lt, lte, inList, notInList, between, like, asc, desc, filter, order, } from './query-helpers.js';
8
8
  export { createDatasetRegistry } from './registry.js';
9
- export { getDatasetCatalog, getDatasetCatalogs } from './catalog.js';
9
+ export { getDatasetCatalog, getDatasetCatalogs, getQueryableRelationshipFields } from './catalog.js';
10
+ export { listQueryableRelationshipFields } from './utils/relationship-fields.js';
10
11
  export type { DatasetCatalog, DatasetCatalogMap, DatasetCatalogSource, DimensionCatalogEntry, MeasureCatalogEntry, MetricCatalogEntry, FilterCatalogEntry, RelationshipCatalogEntry, } from './catalog.js';
11
12
  export { serializeSemanticContract, contractToStableJson, hashContract, SEMANTIC_CONTRACT_VERSION, } from './contract.js';
12
13
  export type { SemanticContract, SerializeSemanticContractOptions, ContractDataset, ContractDimension, ContractMeasure, ContractMetric, ContractFilter, ContractRelationship, } from './contract.js';
@@ -26,5 +27,5 @@ export type { QueryBuilderLike, QueryBuilderFactoryLike, QueryBuilderFactoryComp
26
27
  export { toQueryBuilderFactory } from './query-builder-protocol.js';
27
28
  export { validateSQLIdentifier, isSafeSQLIdentifier, quoteSQLIdentifier } from './sql-utils.js';
28
29
  export { GRAIN_FUNCTIONS, SEMANTIC_FILTER_OPERATORS, SUPPORTED_TIME_GRAINS } from './constants.js';
29
- export type { FieldType, DimensionType, DimensionOptions, DimensionDefinition, MeasureOptions, MeasureDefinition, InferDimensionType, RelationshipKind, RelationshipDefinition, AggregationType, MeasureAggregation, AggregationSpec, FormulaExpr, DerivedMetricSpec, TimeGrain, MetricRef, BaseMetricRef, DerivedMetricRef, GrainedMetricRef, MetricContract, MetricFilter, MetricOrderBy, MetricQuery, DatasetQuery, MetricResultMeta, MetricResult, DatasetQueryResult, MetricHandle, ExecutionContext, SemanticExecutionRuntime, SemanticTenantRuntime, SemanticFilterDefinition, SemanticFiltersDefinition, DatasetConfig, DatasetLimits, DatasetInstance, AnyDatasetInstance, BaseMetricConfig, DerivedMetricConfig, DatasetRegistryInstance, DatasetFieldNames, DatasetDimensionNames, DatasetMeasureNames, DatasetOrderableNames, DatasetQueryFor, DatasetRow, DatasetRowFor, DatasetQueryResultFor, MetricQueryFor, MetricRow, MetricRowFor, MetricResultFor, KnownStringKeys, } from './types.js';
30
+ export type { FieldType, DimensionType, DimensionOptions, DimensionDefinition, MeasureOptions, MeasureDefinition, InferDimensionType, RelationshipKind, RelationshipDefinition, AggregationType, MeasureAggregation, AggregationSpec, FormulaExpr, DerivedMetricSpec, TimeGrain, MetricRef, BaseMetricRef, DerivedMetricRef, GrainedMetricRef, MetricContract, MetricFilter, MetricOrderBy, MetricQuery, DatasetQuery, MetricResultMeta, MetricResult, DatasetQueryResult, MetricHandle, ExecutionContext, SemanticExecutionRuntime, SemanticTenantRuntime, SemanticFilterDefinition, SemanticFiltersDefinition, DatasetConfig, DatasetLimits, DatasetInstance, AnyDatasetInstance, BaseMetricConfig, DerivedMetricConfig, DatasetRegistryInstance, DatasetFieldNames, DatasetDimensionNames, DatasetQueryableDimensions, DatasetMeasureNames, DatasetOrderableNames, DatasetQueryFor, DatasetRow, DatasetRowFor, DatasetQueryResultFor, MetricQueryFor, MetricRow, MetricRowFor, MetricResultFor, KnownStringKeys, } from './types.js';
30
31
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAGvC,OAAO,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAGvC,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAGvC,OAAO,EAAE,SAAS,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,oBAAoB,CAAC;AAGhE,OAAO,EAAE,GAAG,EAAE,KAAK,EAAE,aAAa,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,MAAM,mBAAmB,CAAC;AAG7E,OAAO,EACL,MAAM,EAAE,QAAQ,EAAE,QAAQ,EAAE,GAAG,EAC/B,UAAU,EAAE,QAAQ,EACpB,KAAK,EAAE,KAAK,EAAE,IAAI,GACnB,MAAM,eAAe,CAAC;AAGvB,OAAO,EACL,EAAE,EAAE,GAAG,EAAE,EAAE,EAAE,GAAG,EAAE,EAAE,EAAE,GAAG,EACzB,MAAM,EAAE,SAAS,EAAE,OAAO,EAAE,IAAI,EAChC,GAAG,EAAE,IAAI,EACT,MAAM,EAAE,KAAK,GACd,MAAM,oBAAoB,CAAC;AAG5B,OAAO,EAAE,qBAAqB,EAAE,MAAM,eAAe,CAAC;AAGtD,OAAO,EAAE,iBAAiB,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAC;AACrE,YAAY,EACV,cAAc,EACd,iBAAiB,EACjB,oBAAoB,EACpB,qBAAqB,EACrB,mBAAmB,EACnB,kBAAkB,EAClB,kBAAkB,EAClB,wBAAwB,GACzB,MAAM,cAAc,CAAC;AAGtB,OAAO,EACL,yBAAyB,EACzB,oBAAoB,EACpB,YAAY,EACZ,yBAAyB,GAC1B,MAAM,eAAe,CAAC;AACvB,YAAY,EACV,gBAAgB,EAChB,gCAAgC,EAChC,eAAe,EACf,iBAAiB,EACjB,eAAe,EACf,cAAc,EACd,cAAc,EACd,oBAAoB,GACrB,MAAM,eAAe,CAAC;AAGvB,OAAO,EACL,oBAAoB,EACpB,aAAa,EACb,YAAY,EACZ,UAAU,GACX,MAAM,YAAY,CAAC;AACpB,YAAY,EACV,mBAAmB,EACnB,oBAAoB,EACpB,eAAe,EACf,2BAA2B,EAC3B,UAAU,EACV,iBAAiB,EACjB,oBAAoB,EACpB,sBAAsB,GACvB,MAAM,YAAY,CAAC;AAGpB,OAAO,EAAE,mBAAmB,EAAE,MAAM,eAAe,CAAC;AACpD,YAAY,EAAE,aAAa,EAAE,0BAA0B,EAAE,MAAM,eAAe,CAAC;AAG/E,OAAO,EAAE,sBAAsB,EAAE,MAAM,iCAAiC,CAAC;AACzE,YAAY,EACV,kBAAkB,EAClB,qBAAqB,EACrB,oBAAoB,EACpB,oBAAoB,EACpB,kBAAkB,GACnB,MAAM,iCAAiC,CAAC;AACzC,OAAO,EACL,0BAA0B,EAC1B,yBAAyB,GAC1B,MAAM,4BAA4B,CAAC;AACpC,OAAO,EAAE,qBAAqB,EAAE,MAAM,wBAAwB,CAAC;AAC/D,YAAY,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AAC5E,YAAY,EACV,QAAQ,EACR,eAAe,EACf,qBAAqB,EACrB,kBAAkB,EAClB,uBAAuB,EACvB,qBAAqB,EACrB,iBAAiB,GAClB,MAAM,oBAAoB,CAAC;AAG5B,YAAY,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AACxD,OAAO,EAAE,mBAAmB,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AAGxE,YAAY,EACV,gBAAgB,EAChB,uBAAuB,EACvB,6BAA6B,EAC7B,wBAAwB,GACzB,MAAM,6BAA6B,CAAC;AACrC,OAAO,EAAE,qBAAqB,EAAE,MAAM,6BAA6B,CAAC;AAGpE,OAAO,EAAE,qBAAqB,EAAE,mBAAmB,EAAE,kBAAkB,EAAE,MAAM,gBAAgB,CAAC;AAGhG,OAAO,EAAE,eAAe,EAAE,yBAAyB,EAAE,qBAAqB,EAAE,MAAM,gBAAgB,CAAC;AAGnG,YAAY,EACV,SAAS,EACT,aAAa,EACb,gBAAgB,EAChB,mBAAmB,EACnB,cAAc,EACd,iBAAiB,EACjB,kBAAkB,EAClB,gBAAgB,EAChB,sBAAsB,EACtB,eAAe,EACf,kBAAkB,EAClB,eAAe,EACf,WAAW,EACX,iBAAiB,EACjB,SAAS,EACT,SAAS,EACT,aAAa,EACb,gBAAgB,EAChB,gBAAgB,EAChB,cAAc,EACd,YAAY,EACZ,aAAa,EACb,WAAW,EACX,YAAY,EACZ,gBAAgB,EAChB,YAAY,EACZ,kBAAkB,EAClB,YAAY,EACZ,gBAAgB,EAChB,wBAAwB,EACxB,qBAAqB,EACrB,wBAAwB,EACxB,yBAAyB,EACzB,aAAa,EACb,aAAa,EACb,eAAe,EACf,kBAAkB,EAClB,gBAAgB,EAChB,mBAAmB,EACnB,uBAAuB,EACvB,iBAAiB,EACjB,qBAAqB,EACrB,mBAAmB,EACnB,qBAAqB,EACrB,eAAe,EACf,UAAU,EACV,aAAa,EACb,qBAAqB,EACrB,cAAc,EACd,SAAS,EACT,YAAY,EACZ,eAAe,EACf,eAAe,GAChB,MAAM,YAAY,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAGvC,OAAO,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAGvC,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAGvC,OAAO,EAAE,SAAS,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,oBAAoB,CAAC;AAGhE,OAAO,EAAE,GAAG,EAAE,KAAK,EAAE,aAAa,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,MAAM,mBAAmB,CAAC;AAG7E,OAAO,EACL,MAAM,EAAE,QAAQ,EAAE,QAAQ,EAAE,GAAG,EAC/B,UAAU,EAAE,QAAQ,EACpB,KAAK,EAAE,KAAK,EAAE,IAAI,GACnB,MAAM,eAAe,CAAC;AAGvB,OAAO,EACL,EAAE,EAAE,GAAG,EAAE,EAAE,EAAE,GAAG,EAAE,EAAE,EAAE,GAAG,EACzB,MAAM,EAAE,SAAS,EAAE,OAAO,EAAE,IAAI,EAChC,GAAG,EAAE,IAAI,EACT,MAAM,EAAE,KAAK,GACd,MAAM,oBAAoB,CAAC;AAG5B,OAAO,EAAE,qBAAqB,EAAE,MAAM,eAAe,CAAC;AAGtD,OAAO,EAAE,iBAAiB,EAAE,kBAAkB,EAAE,8BAA8B,EAAE,MAAM,cAAc,CAAC;AACrG,OAAO,EAAE,+BAA+B,EAAE,MAAM,gCAAgC,CAAC;AACjF,YAAY,EACV,cAAc,EACd,iBAAiB,EACjB,oBAAoB,EACpB,qBAAqB,EACrB,mBAAmB,EACnB,kBAAkB,EAClB,kBAAkB,EAClB,wBAAwB,GACzB,MAAM,cAAc,CAAC;AAGtB,OAAO,EACL,yBAAyB,EACzB,oBAAoB,EACpB,YAAY,EACZ,yBAAyB,GAC1B,MAAM,eAAe,CAAC;AACvB,YAAY,EACV,gBAAgB,EAChB,gCAAgC,EAChC,eAAe,EACf,iBAAiB,EACjB,eAAe,EACf,cAAc,EACd,cAAc,EACd,oBAAoB,GACrB,MAAM,eAAe,CAAC;AAGvB,OAAO,EACL,oBAAoB,EACpB,aAAa,EACb,YAAY,EACZ,UAAU,GACX,MAAM,YAAY,CAAC;AACpB,YAAY,EACV,mBAAmB,EACnB,oBAAoB,EACpB,eAAe,EACf,2BAA2B,EAC3B,UAAU,EACV,iBAAiB,EACjB,oBAAoB,EACpB,sBAAsB,GACvB,MAAM,YAAY,CAAC;AAGpB,OAAO,EAAE,mBAAmB,EAAE,MAAM,eAAe,CAAC;AACpD,YAAY,EAAE,aAAa,EAAE,0BAA0B,EAAE,MAAM,eAAe,CAAC;AAG/E,OAAO,EAAE,sBAAsB,EAAE,MAAM,iCAAiC,CAAC;AACzE,YAAY,EACV,kBAAkB,EAClB,qBAAqB,EACrB,oBAAoB,EACpB,oBAAoB,EACpB,kBAAkB,GACnB,MAAM,iCAAiC,CAAC;AACzC,OAAO,EACL,0BAA0B,EAC1B,yBAAyB,GAC1B,MAAM,4BAA4B,CAAC;AAIpC,OAAO,EAAE,qBAAqB,EAAE,MAAM,wBAAwB,CAAC;AAC/D,YAAY,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AAC5E,YAAY,EACV,QAAQ,EACR,eAAe,EACf,qBAAqB,EACrB,kBAAkB,EAClB,uBAAuB,EACvB,qBAAqB,EACrB,iBAAiB,GAClB,MAAM,oBAAoB,CAAC;AAG5B,YAAY,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AACxD,OAAO,EAAE,mBAAmB,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AAGxE,YAAY,EACV,gBAAgB,EAChB,uBAAuB,EACvB,6BAA6B,EAC7B,wBAAwB,GACzB,MAAM,6BAA6B,CAAC;AACrC,OAAO,EAAE,qBAAqB,EAAE,MAAM,6BAA6B,CAAC;AAGpE,OAAO,EAAE,qBAAqB,EAAE,mBAAmB,EAAE,kBAAkB,EAAE,MAAM,gBAAgB,CAAC;AAGhG,OAAO,EAAE,eAAe,EAAE,yBAAyB,EAAE,qBAAqB,EAAE,MAAM,gBAAgB,CAAC;AAGnG,YAAY,EACV,SAAS,EACT,aAAa,EACb,gBAAgB,EAChB,mBAAmB,EACnB,cAAc,EACd,iBAAiB,EACjB,kBAAkB,EAClB,gBAAgB,EAChB,sBAAsB,EACtB,eAAe,EACf,kBAAkB,EAClB,eAAe,EACf,WAAW,EACX,iBAAiB,EACjB,SAAS,EACT,SAAS,EACT,aAAa,EACb,gBAAgB,EAChB,gBAAgB,EAChB,cAAc,EACd,YAAY,EACZ,aAAa,EACb,WAAW,EACX,YAAY,EACZ,gBAAgB,EAChB,YAAY,EACZ,kBAAkB,EAClB,YAAY,EACZ,gBAAgB,EAChB,wBAAwB,EACxB,qBAAqB,EACrB,wBAAwB,EACxB,yBAAyB,EACzB,aAAa,EACb,aAAa,EACb,eAAe,EACf,kBAAkB,EAClB,gBAAgB,EAChB,mBAAmB,EACnB,uBAAuB,EACvB,iBAAiB,EACjB,qBAAqB,EACrB,0BAA0B,EAC1B,mBAAmB,EACnB,qBAAqB,EACrB,eAAe,EACf,UAAU,EACV,aAAa,EACb,qBAAqB,EACrB,cAAc,EACd,SAAS,EACT,YAAY,EACZ,eAAe,EACf,eAAe,GAChB,MAAM,YAAY,CAAC"}
package/dist/index.js CHANGED
@@ -15,7 +15,8 @@ export { eq, neq, gt, gte, lt, lte, inList, notInList, between, like, asc, desc,
15
15
  // Registry
16
16
  export { createDatasetRegistry } from './registry.js';
17
17
  // Catalog
18
- export { getDatasetCatalog, getDatasetCatalogs } from './catalog.js';
18
+ export { getDatasetCatalog, getDatasetCatalogs, getQueryableRelationshipFields } from './catalog.js';
19
+ export { listQueryableRelationshipFields } from './utils/relationship-fields.js';
19
20
  // Semantic contract (stable, hashable export for snapshots/diffs/validation)
20
21
  export { serializeSemanticContract, contractToStableJson, hashContract, SEMANTIC_CONTRACT_VERSION, } from './contract.js';
21
22
  // Agent/tool metadata
@@ -25,6 +26,9 @@ export { createDatasetClient } from './executor.js';
25
26
  // Semantic query result cache
26
27
  export { createMemoryCacheStore } from './cache/semantic-query-cache.js';
27
28
  export { buildDatasetQuerySignature, buildMetricQuerySignature, } from './cache/query-signature.js';
29
+ // Plan/backend protocol — FROZEN. Deprecated in favor of the query-builder
30
+ // path (`createDatasetClient({ queryBuilder })`); bug fixes only, no new
31
+ // features. See the deprecation notes on each declaration.
28
32
  export { createInMemoryBackend } from './in-memory-backend.js';
29
33
  export { validateFilterValue, matchesFieldType } from './validation.js';
30
34
  export { toQueryBuilderFactory } from './query-builder-protocol.js';
@@ -18,6 +18,13 @@ export interface QueryBuilderLike {
18
18
  min(column: string, alias?: string): QueryBuilderLike;
19
19
  max(column: string, alias?: string): QueryBuilderLike;
20
20
  where(column: string, operator: string, value: unknown): QueryBuilderLike;
21
+ leftJoin(table: string, leftColumn: string, rightColumn: string, alias?: string, on?: QueryBuilderJoinCondition | QueryBuilderJoinCondition[]): QueryBuilderLike;
22
+ /**
23
+ * Optional single-match LEFT JOIN (ClickHouse `LEFT ANY JOIN`). When a
24
+ * builder provides it, relationship joins use it so duplicate target join
25
+ * keys cannot fan out aggregates; otherwise `leftJoin` is used.
26
+ */
27
+ leftAnyJoin?(table: string, leftColumn: string, rightColumn: string, alias?: string, on?: QueryBuilderJoinCondition | QueryBuilderJoinCondition[]): QueryBuilderLike;
21
28
  groupBy(columns: string | string[]): QueryBuilderLike;
22
29
  orderBy(column: string, direction?: 'ASC' | 'DESC'): QueryBuilderLike;
23
30
  limit(count: number): QueryBuilderLike;
@@ -28,6 +35,11 @@ export interface QueryBuilderLike {
28
35
  };
29
36
  execute<T = Record<string, unknown>>(): Promise<T[]>;
30
37
  }
38
+ export interface QueryBuilderJoinCondition {
39
+ column: string;
40
+ operator: string;
41
+ value: unknown;
42
+ }
31
43
  /** A query builder factory (what `createQueryBuilder(config)` returns). */
32
44
  export interface QueryBuilderFactoryLike {
33
45
  table(name: string): QueryBuilderLike;