yamchart 0.7.2 → 0.8.1
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/CLAUDE.md +51 -0
- package/dist/{advisor-5BTRAICH.js → advisor-57BOMUUF.js} +11 -10
- package/dist/{advisor-5BTRAICH.js.map → advisor-57BOMUUF.js.map} +1 -1
- package/dist/{chunk-5VA43CTW.js → chunk-E2UZXDF6.js} +37 -42
- package/dist/chunk-E2UZXDF6.js.map +1 -0
- package/dist/{chunk-SSUVEADJ.js → chunk-JYQKDWLG.js} +76 -14
- package/dist/chunk-JYQKDWLG.js.map +1 -0
- package/dist/{chunk-565OEBW7.js → chunk-OTAUP5RC.js} +3 -3
- package/dist/{chunk-F6QAWPYU.js → chunk-TZKNU5TD.js} +2 -2
- package/dist/chunk-TZKNU5TD.js.map +1 -0
- package/dist/chunk-UND73EOB.js +449 -0
- package/dist/chunk-UND73EOB.js.map +1 -0
- package/dist/{chunk-UDRJFEJU.js → chunk-X6LQGWUX.js} +349 -176
- package/dist/chunk-X6LQGWUX.js.map +1 -0
- package/dist/{connection-utils-AGSQ5HNN.js → connection-utils-FTSZU2VF.js} +5 -4
- package/dist/{describe-AA4UT4U6.js → describe-TGIOBNJB.js} +5 -4
- package/dist/{describe-AA4UT4U6.js.map → describe-TGIOBNJB.js.map} +1 -1
- package/dist/{dev-EUWIRL4N.js → dev-MRZ76V74.js} +485 -44
- package/dist/dev-MRZ76V74.js.map +1 -0
- package/dist/{dist-LZNDQDH3.js → dist-PVHFUYP2.js} +23 -3
- package/dist/{dist-YTGUIBKG.js → dist-WDTDQDTX.js} +72 -1
- package/dist/dist-WDTDQDTX.js.map +1 -0
- package/dist/index.js +136 -18
- package/dist/index.js.map +1 -1
- package/dist/{init-CI4VARQG.js → init-UYQE5DPU.js} +2 -2
- package/dist/{init-CI4VARQG.js.map → init-UYQE5DPU.js.map} +1 -1
- package/dist/public/assets/EventManagement-BlxJ2TFw.js +18 -0
- package/dist/public/assets/{LoginPage-BwMmpq8e.js → LoginPage-BT8ikmPn.js} +1 -1
- package/dist/public/assets/PublicViewer-B4uFxgbt.js +1 -0
- package/dist/public/assets/{SetupWizard-DU8R2dPp.js → SetupWizard-njrOhCzw.js} +1 -1
- package/dist/public/assets/ShareManagement-Bg16oJhW.js +1 -0
- package/dist/public/assets/UserManagement-tiCIT4UY.js +1 -0
- package/dist/public/assets/index-B41yj3io.js +187 -0
- package/dist/public/assets/{index-C0hWblBI.css → index-BZ25r23j.css} +1 -1
- package/dist/public/assets/{index.es-YeJujQ5o.js → index.es-BuktD_R2.js} +1 -1
- package/dist/public/assets/{jspdf.es.min-BzZgn3ka.js → jspdf.es.min-DFRl2hZQ.js} +3 -3
- package/dist/public/index.html +2 -2
- package/dist/{query-THDVBBVZ.js → query-JRMMNXX6.js} +5 -4
- package/dist/{query-THDVBBVZ.js.map → query-JRMMNXX6.js.map} +1 -1
- package/dist/{sample-6WPQS7PA.js → sample-VGIY4U4J.js} +5 -4
- package/dist/{sample-6WPQS7PA.js.map → sample-VGIY4U4J.js.map} +1 -1
- package/dist/{search-657DXBRS.js → search-QSYNG4SR.js} +5 -4
- package/dist/{search-657DXBRS.js.map → search-QSYNG4SR.js.map} +1 -1
- package/dist/semantic-RAP3S5PQ.js +39 -0
- package/dist/semantic-RAP3S5PQ.js.map +1 -0
- package/dist/{sync-warehouse-4KBV5S3L.js → sync-warehouse-XHTBZH25.js} +5 -4
- package/dist/{sync-warehouse-4KBV5S3L.js.map → sync-warehouse-XHTBZH25.js.map} +1 -1
- package/dist/{tables-KLDBUUSE.js → tables-UOO342TA.js} +5 -4
- package/dist/{tables-KLDBUUSE.js.map → tables-UOO342TA.js.map} +1 -1
- package/dist/templates/default/.claude/skills/databricks/SKILL.md +76 -0
- package/dist/templates/default/.claude/skills/dbt-advisor/SKILL.md +107 -0
- package/dist/templates/default/.claude/skills/duckdb/SKILL.md +67 -0
- package/dist/templates/default/.claude/skills/mysql/SKILL.md +69 -0
- package/dist/templates/default/.claude/skills/postgres/SKILL.md +69 -0
- package/dist/templates/default/.claude/skills/snowflake/SKILL.md +64 -0
- package/dist/templates/default/CLAUDE.md +8 -1
- package/dist/templates/default/docs/yamchart-reference.md +222 -1
- package/dist/{test-JSAWS5ZP.js → test-TXRZWNXK.js} +5 -4
- package/dist/{test-JSAWS5ZP.js.map → test-TXRZWNXK.js.map} +1 -1
- package/dist/update-WMATDZTO.js +220 -0
- package/dist/update-WMATDZTO.js.map +1 -0
- package/package.json +8 -6
- package/dist/chunk-5VA43CTW.js.map +0 -1
- package/dist/chunk-F6QAWPYU.js.map +0 -1
- package/dist/chunk-SSUVEADJ.js.map +0 -1
- package/dist/chunk-UDRJFEJU.js.map +0 -1
- package/dist/dev-EUWIRL4N.js.map +0 -1
- package/dist/dist-YTGUIBKG.js.map +0 -1
- package/dist/public/assets/PublicViewer-vIDojjIR.js +0 -1
- package/dist/public/assets/ShareManagement-7iS6lM2T.js +0 -1
- package/dist/public/assets/UserManagement-By7YRZrF.js +0 -1
- package/dist/public/assets/index-CXx1PiRF.js +0 -174
- package/dist/update-HCR6MYJX.js +0 -88
- package/dist/update-HCR6MYJX.js.map +0 -1
- /package/dist/{chunk-565OEBW7.js.map → chunk-OTAUP5RC.js.map} +0 -0
- /package/dist/{connection-utils-AGSQ5HNN.js.map → connection-utils-FTSZU2VF.js.map} +0 -0
- /package/dist/{dist-LZNDQDH3.js.map → dist-PVHFUYP2.js.map} +0 -0
|
@@ -0,0 +1,449 @@
|
|
|
1
|
+
// ../../packages/query/dist/semantic/field-classifier.js
|
|
2
|
+
var DIMENSION_ID_PATTERN = /(_id|_key|_code)$/i;
|
|
3
|
+
var DIMENSION_NAME_PATTERN = /(_name|_label|_title|_slug)$/i;
|
|
4
|
+
var DIMENSION_TYPE_PATTERN = /(_type|_status|_category|_class|_group|_tier|_level|_kind|_role)$/i;
|
|
5
|
+
var DIMENSION_EXACT = /* @__PURE__ */ new Set([
|
|
6
|
+
"id",
|
|
7
|
+
"name",
|
|
8
|
+
"category",
|
|
9
|
+
"channel",
|
|
10
|
+
"country",
|
|
11
|
+
"region",
|
|
12
|
+
"city",
|
|
13
|
+
"state",
|
|
14
|
+
"source",
|
|
15
|
+
"medium",
|
|
16
|
+
"campaign",
|
|
17
|
+
"status",
|
|
18
|
+
"type",
|
|
19
|
+
"tier",
|
|
20
|
+
"segment",
|
|
21
|
+
"department",
|
|
22
|
+
"team",
|
|
23
|
+
"brand",
|
|
24
|
+
"product",
|
|
25
|
+
"currency",
|
|
26
|
+
"email",
|
|
27
|
+
"url"
|
|
28
|
+
]);
|
|
29
|
+
var TIME_PATTERN = /(_at|_date|_time|_timestamp|_ts|_day|_week|_month|_year|_quarter)$/i;
|
|
30
|
+
var TIME_EXACT = /* @__PURE__ */ new Set([
|
|
31
|
+
"date",
|
|
32
|
+
"period",
|
|
33
|
+
"timestamp",
|
|
34
|
+
"date_day",
|
|
35
|
+
"month",
|
|
36
|
+
"year",
|
|
37
|
+
"quarter",
|
|
38
|
+
"week",
|
|
39
|
+
"created",
|
|
40
|
+
"updated",
|
|
41
|
+
"deleted",
|
|
42
|
+
"started",
|
|
43
|
+
"ended"
|
|
44
|
+
]);
|
|
45
|
+
var MEASURE_COUNT_PATTERN = /(_count$|^count_|^num_|^number_of_|_total_count$)/i;
|
|
46
|
+
var MEASURE_AMOUNT_PATTERN = /(_amount$|_total$|_revenue$|_cost$|_price$|_spend$|_profit$|_margin$|_fee$|_tax$|_discount$|_balance$|_qty$|_quantity$|_sum$|_volume$|_weight$|_size$|_value$|_rate$|_ratio$|_score$|_percent$|_pct$)/i;
|
|
47
|
+
var MEASURE_EXACT = /* @__PURE__ */ new Set([
|
|
48
|
+
"quantity",
|
|
49
|
+
"revenue",
|
|
50
|
+
"amount",
|
|
51
|
+
"total",
|
|
52
|
+
"cost",
|
|
53
|
+
"price",
|
|
54
|
+
"profit",
|
|
55
|
+
"margin",
|
|
56
|
+
"balance",
|
|
57
|
+
"score",
|
|
58
|
+
"rate",
|
|
59
|
+
"ratio",
|
|
60
|
+
"volume",
|
|
61
|
+
"weight",
|
|
62
|
+
"spend",
|
|
63
|
+
"fee"
|
|
64
|
+
]);
|
|
65
|
+
var MEASURE_AVG_PATTERN = /(_avg$|_average$|_mean$|^avg_|^average_|^mean_)/i;
|
|
66
|
+
var MEASURE_MIN_PATTERN = /(^min_|_min$)/i;
|
|
67
|
+
var MEASURE_MAX_PATTERN = /(^max_|_max$)/i;
|
|
68
|
+
var NUMERIC_TYPES = /^(number|numeric|int|integer|bigint|smallint|tinyint|float|double|decimal|real|money)/i;
|
|
69
|
+
var STRING_TYPES = /^(varchar|text|string|char|nchar|nvarchar|clob|binary|varbinary)/i;
|
|
70
|
+
var TIME_TYPES = /^(date|time|timestamp|datetime|interval)/i;
|
|
71
|
+
var BOOLEAN_TYPES = /^(bool|boolean|bit)/i;
|
|
72
|
+
var MEASURE_DESCRIPTION_KEYWORDS = /\b(total|sum of|count of|number of|amount|revenue|average|aggregate)\b/i;
|
|
73
|
+
function classifyField(input) {
|
|
74
|
+
const name = input.name.toLowerCase();
|
|
75
|
+
if (TIME_PATTERN.test(name) || TIME_EXACT.has(name)) {
|
|
76
|
+
return { role: "dimension", dimensionType: "time" };
|
|
77
|
+
}
|
|
78
|
+
if (DIMENSION_ID_PATTERN.test(name) || DIMENSION_NAME_PATTERN.test(name) || DIMENSION_TYPE_PATTERN.test(name) || DIMENSION_EXACT.has(name)) {
|
|
79
|
+
return { role: "dimension", dimensionType: "string" };
|
|
80
|
+
}
|
|
81
|
+
if (MEASURE_COUNT_PATTERN.test(name)) {
|
|
82
|
+
return { role: "measure", measureType: "count" };
|
|
83
|
+
}
|
|
84
|
+
if (MEASURE_AVG_PATTERN.test(name)) {
|
|
85
|
+
return { role: "measure", measureType: "avg" };
|
|
86
|
+
}
|
|
87
|
+
if (MEASURE_MIN_PATTERN.test(name)) {
|
|
88
|
+
return { role: "measure", measureType: "min" };
|
|
89
|
+
}
|
|
90
|
+
if (MEASURE_MAX_PATTERN.test(name)) {
|
|
91
|
+
return { role: "measure", measureType: "max" };
|
|
92
|
+
}
|
|
93
|
+
if (MEASURE_AMOUNT_PATTERN.test(name) || MEASURE_EXACT.has(name)) {
|
|
94
|
+
return { role: "measure", measureType: "sum" };
|
|
95
|
+
}
|
|
96
|
+
if (input.data_type) {
|
|
97
|
+
if (TIME_TYPES.test(input.data_type)) {
|
|
98
|
+
return { role: "dimension", dimensionType: "time" };
|
|
99
|
+
}
|
|
100
|
+
if (BOOLEAN_TYPES.test(input.data_type)) {
|
|
101
|
+
return { role: "dimension", dimensionType: "boolean" };
|
|
102
|
+
}
|
|
103
|
+
if (NUMERIC_TYPES.test(input.data_type)) {
|
|
104
|
+
return { role: "measure", measureType: "sum" };
|
|
105
|
+
}
|
|
106
|
+
if (STRING_TYPES.test(input.data_type)) {
|
|
107
|
+
return { role: "dimension", dimensionType: "string" };
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
if (input.description && MEASURE_DESCRIPTION_KEYWORDS.test(input.description)) {
|
|
111
|
+
return { role: "measure", measureType: "sum" };
|
|
112
|
+
}
|
|
113
|
+
return { role: "dimension", dimensionType: "string" };
|
|
114
|
+
}
|
|
115
|
+
function inferAggregationType(name) {
|
|
116
|
+
const lower = name.toLowerCase();
|
|
117
|
+
if (MEASURE_COUNT_PATTERN.test(lower) || lower.startsWith("num_"))
|
|
118
|
+
return "count";
|
|
119
|
+
if (MEASURE_AVG_PATTERN.test(lower))
|
|
120
|
+
return "avg";
|
|
121
|
+
if (MEASURE_MIN_PATTERN.test(lower))
|
|
122
|
+
return "min";
|
|
123
|
+
if (MEASURE_MAX_PATTERN.test(lower))
|
|
124
|
+
return "max";
|
|
125
|
+
return "sum";
|
|
126
|
+
}
|
|
127
|
+
function classifyCatalogColumns(columns) {
|
|
128
|
+
const measures = [];
|
|
129
|
+
const dimensions = [];
|
|
130
|
+
for (const col of columns) {
|
|
131
|
+
const result = classifyField({
|
|
132
|
+
name: col.name,
|
|
133
|
+
data_type: col.data_type,
|
|
134
|
+
description: col.description
|
|
135
|
+
});
|
|
136
|
+
if (result.role === "measure") {
|
|
137
|
+
measures.push({
|
|
138
|
+
name: col.name,
|
|
139
|
+
type: result.measureType ?? "sum",
|
|
140
|
+
description: col.description || void 0
|
|
141
|
+
});
|
|
142
|
+
} else {
|
|
143
|
+
dimensions.push({
|
|
144
|
+
name: col.name,
|
|
145
|
+
type: result.dimensionType ?? "string",
|
|
146
|
+
description: col.description || void 0
|
|
147
|
+
});
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
return { measures, dimensions };
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
// ../../packages/query/dist/semantic/model-builder.js
|
|
154
|
+
var DEFAULT_FILTER = {
|
|
155
|
+
paths: ["gold/", "marts/", "core/"],
|
|
156
|
+
prefixes: ["dim_", "fct_", "fact_"]
|
|
157
|
+
};
|
|
158
|
+
var SemanticModelBuilder = class {
|
|
159
|
+
filter;
|
|
160
|
+
constructor(config) {
|
|
161
|
+
this.filter = config?.include ?? DEFAULT_FILTER;
|
|
162
|
+
}
|
|
163
|
+
build(input) {
|
|
164
|
+
const { catalog, models, chartHints } = input;
|
|
165
|
+
const catalogEntities = catalog ? this.buildCatalogEntities(catalog) : [];
|
|
166
|
+
const modelEntities = models ? this.buildModelEntities(models) : [];
|
|
167
|
+
const allEntities = [...catalogEntities, ...modelEntities];
|
|
168
|
+
if (chartHints) {
|
|
169
|
+
this.applyChartHints(allEntities, chartHints);
|
|
170
|
+
}
|
|
171
|
+
const merged = this.mergeEntities(catalogEntities, modelEntities);
|
|
172
|
+
const relationships = catalog ? this.extractRelationships(catalog, merged) : [];
|
|
173
|
+
return {
|
|
174
|
+
entities: merged,
|
|
175
|
+
relationships,
|
|
176
|
+
generated_at: (/* @__PURE__ */ new Date()).toISOString()
|
|
177
|
+
};
|
|
178
|
+
}
|
|
179
|
+
isGoldModel(entry) {
|
|
180
|
+
const path = entry.path?.toLowerCase() ?? "";
|
|
181
|
+
const name = entry.name.toLowerCase();
|
|
182
|
+
const pathMatch = this.filter.paths.some((p) => path.includes(p.toLowerCase()));
|
|
183
|
+
const prefixMatch = this.filter.prefixes.some((p) => name.startsWith(p.toLowerCase()));
|
|
184
|
+
return pathMatch || prefixMatch;
|
|
185
|
+
}
|
|
186
|
+
buildCatalogEntities(catalog) {
|
|
187
|
+
return catalog.models.filter((entry) => this.isGoldModel(entry)).filter((entry) => entry.table).map((entry) => {
|
|
188
|
+
const { measures, dimensions } = classifyCatalogColumns(entry.columns ?? []);
|
|
189
|
+
return {
|
|
190
|
+
name: entry.name,
|
|
191
|
+
description: entry.description && entry.description !== "No description" ? entry.description : void 0,
|
|
192
|
+
source: "catalog",
|
|
193
|
+
source_table: entry.table,
|
|
194
|
+
measures,
|
|
195
|
+
dimensions
|
|
196
|
+
};
|
|
197
|
+
});
|
|
198
|
+
}
|
|
199
|
+
buildModelEntities(models) {
|
|
200
|
+
return models.filter((m) => m.metadata.returns && m.metadata.returns.length > 0).map((m) => {
|
|
201
|
+
const measures = [];
|
|
202
|
+
const dimensions = [];
|
|
203
|
+
for (const col of m.metadata.returns) {
|
|
204
|
+
const colType = col.type?.toLowerCase();
|
|
205
|
+
if (colType === "number") {
|
|
206
|
+
measures.push({
|
|
207
|
+
name: col.name,
|
|
208
|
+
type: this.inferAggType(col.name)
|
|
209
|
+
});
|
|
210
|
+
} else if (colType === "date" || colType === "datetime" || colType === "timestamp") {
|
|
211
|
+
dimensions.push({ name: col.name, type: "time" });
|
|
212
|
+
} else {
|
|
213
|
+
dimensions.push({ name: col.name, type: "string" });
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
const parameters = m.metadata.params?.map((p) => ({
|
|
217
|
+
name: p.name,
|
|
218
|
+
type: p.type,
|
|
219
|
+
default: p.default,
|
|
220
|
+
options: p.options
|
|
221
|
+
}));
|
|
222
|
+
return {
|
|
223
|
+
name: m.name,
|
|
224
|
+
description: m.description ?? m.metadata.description,
|
|
225
|
+
source: "model",
|
|
226
|
+
source_table: m.sourceTable ?? m.name,
|
|
227
|
+
measures,
|
|
228
|
+
dimensions,
|
|
229
|
+
parameters: parameters?.length ? parameters : void 0
|
|
230
|
+
};
|
|
231
|
+
});
|
|
232
|
+
}
|
|
233
|
+
inferAggType(name) {
|
|
234
|
+
const lower = name.toLowerCase();
|
|
235
|
+
if (/(_count$|^count_|^num_)/.test(lower))
|
|
236
|
+
return "count";
|
|
237
|
+
if (/(_avg$|_average$|_mean$|^avg_)/.test(lower))
|
|
238
|
+
return "avg";
|
|
239
|
+
if (/(^min_|_min$)/.test(lower))
|
|
240
|
+
return "min";
|
|
241
|
+
if (/(^max_|_max$)/.test(lower))
|
|
242
|
+
return "max";
|
|
243
|
+
return "sum";
|
|
244
|
+
}
|
|
245
|
+
applyChartHints(entities, hints) {
|
|
246
|
+
for (const hint of hints) {
|
|
247
|
+
const entity = entities.find((e) => e.name === hint.modelName || e.source_table.toLowerCase().includes(hint.modelName.toLowerCase()));
|
|
248
|
+
if (!entity)
|
|
249
|
+
continue;
|
|
250
|
+
for (const measureName of hint.measures) {
|
|
251
|
+
const dimIdx = entity.dimensions.findIndex((d) => d.name === measureName);
|
|
252
|
+
if (dimIdx !== -1) {
|
|
253
|
+
const dim = entity.dimensions.splice(dimIdx, 1)[0];
|
|
254
|
+
entity.measures.push({ name: dim.name, type: this.inferAggType(dim.name) });
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
for (const timeDim of hint.timeDimensions) {
|
|
258
|
+
const dim = entity.dimensions.find((d) => d.name === timeDim);
|
|
259
|
+
if (dim)
|
|
260
|
+
dim.type = "time";
|
|
261
|
+
}
|
|
262
|
+
for (const [field, format] of Object.entries(hint.formats)) {
|
|
263
|
+
const measure = entity.measures.find((m) => m.name === field);
|
|
264
|
+
if (measure)
|
|
265
|
+
measure.format = format;
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
mergeEntities(catalogEntities, modelEntities) {
|
|
270
|
+
const modelTables = new Set(modelEntities.map((e) => e.source_table.toLowerCase()));
|
|
271
|
+
const filteredCatalog = catalogEntities.filter((e) => !modelTables.has(e.source_table.toLowerCase()));
|
|
272
|
+
return [...filteredCatalog, ...modelEntities];
|
|
273
|
+
}
|
|
274
|
+
extractRelationships(catalog, entities) {
|
|
275
|
+
const entityNames = new Set(entities.map((e) => e.name));
|
|
276
|
+
const relationships = [];
|
|
277
|
+
for (const entry of catalog.models) {
|
|
278
|
+
if (!entityNames.has(entry.name))
|
|
279
|
+
continue;
|
|
280
|
+
if (!entry.dependsOn)
|
|
281
|
+
continue;
|
|
282
|
+
for (const dep of entry.dependsOn) {
|
|
283
|
+
const parts = dep.split(".");
|
|
284
|
+
const depName = parts[parts.length - 1];
|
|
285
|
+
if (!entityNames.has(depName))
|
|
286
|
+
continue;
|
|
287
|
+
const fromEntity = entities.find((e) => e.name === entry.name);
|
|
288
|
+
const toEntity = entities.find((e) => e.name === depName);
|
|
289
|
+
if (!fromEntity || !toEntity)
|
|
290
|
+
continue;
|
|
291
|
+
const fromFields = [...fromEntity.measures, ...fromEntity.dimensions].map((f) => f.name.toLowerCase());
|
|
292
|
+
const toFields = [...toEntity.measures, ...toEntity.dimensions].map((f) => f.name.toLowerCase());
|
|
293
|
+
const shared = fromFields.filter((f) => toFields.includes(f) && f.endsWith("_id"));
|
|
294
|
+
if (shared.length > 0) {
|
|
295
|
+
relationships.push({
|
|
296
|
+
from: { entity: entry.name, field: shared[0] },
|
|
297
|
+
to: { entity: depName, field: shared[0] },
|
|
298
|
+
type: "many_to_one"
|
|
299
|
+
});
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
return relationships;
|
|
304
|
+
}
|
|
305
|
+
};
|
|
306
|
+
|
|
307
|
+
// ../../packages/query/dist/semantic/query-compiler.js
|
|
308
|
+
var SemanticQueryCompiler = class {
|
|
309
|
+
compile(model, query) {
|
|
310
|
+
const entity = model.entities.find((e) => e.name === query.model);
|
|
311
|
+
if (!entity)
|
|
312
|
+
throw new Error(`Entity '${query.model}' not found`);
|
|
313
|
+
const selectParts = [];
|
|
314
|
+
const groupByParts = [];
|
|
315
|
+
for (const dimName of query.dimensions) {
|
|
316
|
+
selectParts.push(`"${dimName}"`);
|
|
317
|
+
groupByParts.push(`"${dimName}"`);
|
|
318
|
+
}
|
|
319
|
+
for (const measureName of query.measures) {
|
|
320
|
+
const measure = entity.measures.find((m) => m.name === measureName);
|
|
321
|
+
if (!measure)
|
|
322
|
+
throw new Error(`Measure '${measureName}' not found`);
|
|
323
|
+
selectParts.push(`${this.formatMeasure(measure.type, measureName)} AS "${measureName}"`);
|
|
324
|
+
}
|
|
325
|
+
const parts = [];
|
|
326
|
+
parts.push(`SELECT ${selectParts.join(", ")}`);
|
|
327
|
+
parts.push(`FROM ${entity.source_table}`);
|
|
328
|
+
if (query.filters.length > 0) {
|
|
329
|
+
const conditions = query.filters.map((f) => this.compileFilter(f));
|
|
330
|
+
parts.push(`WHERE ${conditions.join(" AND ")}`);
|
|
331
|
+
}
|
|
332
|
+
if (groupByParts.length > 0) {
|
|
333
|
+
parts.push(`GROUP BY ${groupByParts.join(", ")}`);
|
|
334
|
+
}
|
|
335
|
+
if (query.order_by) {
|
|
336
|
+
parts.push(`ORDER BY "${query.order_by.field}" ${query.order_by.direction.toUpperCase()}`);
|
|
337
|
+
}
|
|
338
|
+
parts.push(`LIMIT ${query.limit}`);
|
|
339
|
+
return parts.join("\n");
|
|
340
|
+
}
|
|
341
|
+
formatMeasure(type, column) {
|
|
342
|
+
if (type === "count_distinct") {
|
|
343
|
+
return `COUNT(DISTINCT "${column}")`;
|
|
344
|
+
}
|
|
345
|
+
return `${this.aggFunction(type)}("${column}")`;
|
|
346
|
+
}
|
|
347
|
+
aggFunction(type) {
|
|
348
|
+
switch (type) {
|
|
349
|
+
case "sum":
|
|
350
|
+
return "SUM";
|
|
351
|
+
case "count":
|
|
352
|
+
return "COUNT";
|
|
353
|
+
case "avg":
|
|
354
|
+
return "AVG";
|
|
355
|
+
case "min":
|
|
356
|
+
return "MIN";
|
|
357
|
+
case "max":
|
|
358
|
+
return "MAX";
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
compileFilter(filter) {
|
|
362
|
+
const field = `"${filter.dimension}"`;
|
|
363
|
+
const value = filter.value;
|
|
364
|
+
switch (filter.operator) {
|
|
365
|
+
case "equals":
|
|
366
|
+
return `${field} = ${this.quoteValue(value)}`;
|
|
367
|
+
case "not_equals":
|
|
368
|
+
return `${field} != ${this.quoteValue(value)}`;
|
|
369
|
+
case "contains":
|
|
370
|
+
return `${field} LIKE '%${this.escapeString(String(value))}%'`;
|
|
371
|
+
case "gt":
|
|
372
|
+
return `${field} > ${this.quoteValue(value)}`;
|
|
373
|
+
case "gte":
|
|
374
|
+
return `${field} >= ${this.quoteValue(value)}`;
|
|
375
|
+
case "lt":
|
|
376
|
+
return `${field} < ${this.quoteValue(value)}`;
|
|
377
|
+
case "lte":
|
|
378
|
+
return `${field} <= ${this.quoteValue(value)}`;
|
|
379
|
+
case "in":
|
|
380
|
+
return `${field} IN (${value.map((v) => this.quoteValue(v)).join(", ")})`;
|
|
381
|
+
case "not_in":
|
|
382
|
+
return `${field} NOT IN (${value.map((v) => this.quoteValue(v)).join(", ")})`;
|
|
383
|
+
default:
|
|
384
|
+
throw new Error(`Unknown filter operator: ${filter.operator}`);
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
quoteValue(value) {
|
|
388
|
+
if (typeof value === "number")
|
|
389
|
+
return String(value);
|
|
390
|
+
if (typeof value === "boolean")
|
|
391
|
+
return String(value);
|
|
392
|
+
return `'${this.escapeString(String(value))}'`;
|
|
393
|
+
}
|
|
394
|
+
escapeString(value) {
|
|
395
|
+
return value.replace(/'/g, "''");
|
|
396
|
+
}
|
|
397
|
+
};
|
|
398
|
+
|
|
399
|
+
// ../../packages/query/dist/semantic/validators.js
|
|
400
|
+
var SemanticValidationError = class extends Error {
|
|
401
|
+
code;
|
|
402
|
+
available;
|
|
403
|
+
constructor(code, message, available) {
|
|
404
|
+
super(message);
|
|
405
|
+
this.name = "SemanticValidationError";
|
|
406
|
+
this.code = code;
|
|
407
|
+
this.available = available;
|
|
408
|
+
}
|
|
409
|
+
};
|
|
410
|
+
function validateSemanticQuery(model, query) {
|
|
411
|
+
const entity = model.entities.find((e) => e.name === query.model);
|
|
412
|
+
if (!entity) {
|
|
413
|
+
throw new SemanticValidationError("unknown_model", `Unknown model '${query.model}'. Available models: ${model.entities.map((e) => e.name).join(", ")}`, model.entities.map((e) => e.name));
|
|
414
|
+
}
|
|
415
|
+
const measureNames = entity.measures.map((m) => m.name);
|
|
416
|
+
const dimensionNames = entity.dimensions.map((d) => d.name);
|
|
417
|
+
const allFields = [...measureNames, ...dimensionNames];
|
|
418
|
+
for (const m of query.measures) {
|
|
419
|
+
if (!measureNames.includes(m)) {
|
|
420
|
+
throw new SemanticValidationError("unknown_measure", `Unknown measure '${m}' on entity '${query.model}'. Available measures: ${measureNames.join(", ")}`, measureNames);
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
for (const d of query.dimensions) {
|
|
424
|
+
if (!dimensionNames.includes(d)) {
|
|
425
|
+
throw new SemanticValidationError("unknown_dimension", `Unknown dimension '${d}' on entity '${query.model}'. Available dimensions: ${dimensionNames.join(", ")}`, dimensionNames);
|
|
426
|
+
}
|
|
427
|
+
}
|
|
428
|
+
for (const f of query.filters) {
|
|
429
|
+
if (!dimensionNames.includes(f.dimension)) {
|
|
430
|
+
throw new SemanticValidationError("unknown_filter_dimension", `Unknown filter dimension '${f.dimension}' on entity '${query.model}'. Available dimensions: ${dimensionNames.join(", ")}`, dimensionNames);
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
if (query.order_by) {
|
|
434
|
+
if (!allFields.includes(query.order_by.field)) {
|
|
435
|
+
throw new SemanticValidationError("unknown_order_field", `Unknown order_by field '${query.order_by.field}' on entity '${query.model}'. Available fields: ${allFields.join(", ")}`, allFields);
|
|
436
|
+
}
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
export {
|
|
441
|
+
classifyField,
|
|
442
|
+
inferAggregationType,
|
|
443
|
+
classifyCatalogColumns,
|
|
444
|
+
SemanticModelBuilder,
|
|
445
|
+
SemanticQueryCompiler,
|
|
446
|
+
SemanticValidationError,
|
|
447
|
+
validateSemanticQuery
|
|
448
|
+
};
|
|
449
|
+
//# sourceMappingURL=chunk-UND73EOB.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../packages/query/src/semantic/field-classifier.ts","../../../packages/query/src/semantic/model-builder.ts","../../../packages/query/src/semantic/query-compiler.ts","../../../packages/query/src/semantic/validators.ts"],"sourcesContent":["import type { MeasureType, DimensionType } from '@yamchart/schema';\nimport type { CatalogColumn } from './types.js';\n\nexport interface FieldClassification {\n role: 'measure' | 'dimension';\n measureType?: MeasureType;\n dimensionType?: DimensionType;\n}\n\ninterface ClassifyInput {\n name: string;\n data_type?: string;\n description?: string;\n}\n\n// --- Name pattern matchers ---\n\nconst DIMENSION_ID_PATTERN = /(_id|_key|_code)$/i;\nconst DIMENSION_NAME_PATTERN = /(_name|_label|_title|_slug)$/i;\nconst DIMENSION_TYPE_PATTERN = /(_type|_status|_category|_class|_group|_tier|_level|_kind|_role)$/i;\nconst DIMENSION_EXACT = new Set([\n 'id', 'name', 'category', 'channel', 'country', 'region', 'city', 'state',\n 'source', 'medium', 'campaign', 'status', 'type', 'tier', 'segment',\n 'department', 'team', 'brand', 'product', 'currency', 'email', 'url',\n]);\n\nconst TIME_PATTERN = /(_at|_date|_time|_timestamp|_ts|_day|_week|_month|_year|_quarter)$/i;\nconst TIME_EXACT = new Set([\n 'date', 'period', 'timestamp', 'date_day', 'month', 'year', 'quarter', 'week',\n 'created', 'updated', 'deleted', 'started', 'ended',\n]);\n\nconst MEASURE_COUNT_PATTERN = /(_count$|^count_|^num_|^number_of_|_total_count$)/i;\nconst MEASURE_AMOUNT_PATTERN = /(_amount$|_total$|_revenue$|_cost$|_price$|_spend$|_profit$|_margin$|_fee$|_tax$|_discount$|_balance$|_qty$|_quantity$|_sum$|_volume$|_weight$|_size$|_value$|_rate$|_ratio$|_score$|_percent$|_pct$)/i;\nconst MEASURE_EXACT = new Set([\n 'quantity', 'revenue', 'amount', 'total', 'cost', 'price', 'profit', 'margin',\n 'balance', 'score', 'rate', 'ratio', 'volume', 'weight', 'spend', 'fee',\n]);\nconst MEASURE_AVG_PATTERN = /(_avg$|_average$|_mean$|^avg_|^average_|^mean_)/i;\nconst MEASURE_MIN_PATTERN = /(^min_|_min$)/i;\nconst MEASURE_MAX_PATTERN = /(^max_|_max$)/i;\n\n// --- Data type matchers ---\n\nconst NUMERIC_TYPES = /^(number|numeric|int|integer|bigint|smallint|tinyint|float|double|decimal|real|money)/i;\nconst STRING_TYPES = /^(varchar|text|string|char|nchar|nvarchar|clob|binary|varbinary)/i;\nconst TIME_TYPES = /^(date|time|timestamp|datetime|interval)/i;\nconst BOOLEAN_TYPES = /^(bool|boolean|bit)/i;\n\n// --- Description keyword matchers ---\n\nconst MEASURE_DESCRIPTION_KEYWORDS = /\\b(total|sum of|count of|number of|amount|revenue|average|aggregate)\\b/i;\n\nexport function classifyField(input: ClassifyInput): FieldClassification {\n const name = input.name.toLowerCase();\n\n // Priority 3: Strong name heuristics (check first because they override data_type)\n\n // Time dimensions\n if (TIME_PATTERN.test(name) || TIME_EXACT.has(name)) {\n return { role: 'dimension', dimensionType: 'time' };\n }\n\n // Dimension IDs, names, types\n if (DIMENSION_ID_PATTERN.test(name) || DIMENSION_NAME_PATTERN.test(name) ||\n DIMENSION_TYPE_PATTERN.test(name) || DIMENSION_EXACT.has(name)) {\n return { role: 'dimension', dimensionType: 'string' };\n }\n\n // Measure patterns\n if (MEASURE_COUNT_PATTERN.test(name)) {\n return { role: 'measure', measureType: 'count' };\n }\n if (MEASURE_AVG_PATTERN.test(name)) {\n return { role: 'measure', measureType: 'avg' };\n }\n if (MEASURE_MIN_PATTERN.test(name)) {\n return { role: 'measure', measureType: 'min' };\n }\n if (MEASURE_MAX_PATTERN.test(name)) {\n return { role: 'measure', measureType: 'max' };\n }\n if (MEASURE_AMOUNT_PATTERN.test(name) || MEASURE_EXACT.has(name)) {\n return { role: 'measure', measureType: 'sum' };\n }\n\n // Priority 2: Data type (when name is ambiguous)\n if (input.data_type) {\n if (TIME_TYPES.test(input.data_type)) {\n return { role: 'dimension', dimensionType: 'time' };\n }\n if (BOOLEAN_TYPES.test(input.data_type)) {\n return { role: 'dimension', dimensionType: 'boolean' };\n }\n if (NUMERIC_TYPES.test(input.data_type)) {\n return { role: 'measure', measureType: 'sum' };\n }\n if (STRING_TYPES.test(input.data_type)) {\n return { role: 'dimension', dimensionType: 'string' };\n }\n }\n\n // Priority 4: Description keywords\n if (input.description && MEASURE_DESCRIPTION_KEYWORDS.test(input.description)) {\n return { role: 'measure', measureType: 'sum' };\n }\n\n // Priority 5: Fallback — unknown → dimension (safer default)\n return { role: 'dimension', dimensionType: 'string' };\n}\n\nexport function inferAggregationType(name: string): MeasureType {\n const lower = name.toLowerCase();\n if (MEASURE_COUNT_PATTERN.test(lower) || lower.startsWith('num_')) return 'count';\n if (MEASURE_AVG_PATTERN.test(lower)) return 'avg';\n if (MEASURE_MIN_PATTERN.test(lower)) return 'min';\n if (MEASURE_MAX_PATTERN.test(lower)) return 'max';\n return 'sum';\n}\n\nexport function classifyCatalogColumns(columns: CatalogColumn[]): {\n measures: Array<{ name: string; type: MeasureType; description?: string }>;\n dimensions: Array<{ name: string; type: DimensionType; description?: string }>;\n} {\n const measures: Array<{ name: string; type: MeasureType; description?: string }> = [];\n const dimensions: Array<{ name: string; type: DimensionType; description?: string }> = [];\n\n for (const col of columns) {\n const result = classifyField({\n name: col.name,\n data_type: col.data_type,\n description: col.description,\n });\n\n if (result.role === 'measure') {\n measures.push({\n name: col.name,\n type: result.measureType ?? 'sum',\n description: col.description || undefined,\n });\n } else {\n dimensions.push({\n name: col.name,\n type: result.dimensionType ?? 'string',\n description: col.description || undefined,\n });\n }\n }\n\n return { measures, dimensions };\n}\n","import type { SemanticModel, SemanticEntity, Measure, Dimension, SemanticConfig } from '@yamchart/schema';\nimport type { CatalogData, CatalogEntry, GoldFilterConfig } from './types.js';\nimport { classifyCatalogColumns } from './field-classifier.js';\n\nconst DEFAULT_FILTER: GoldFilterConfig = {\n paths: ['gold/', 'marts/', 'core/'],\n prefixes: ['dim_', 'fct_', 'fact_'],\n};\n\ninterface YamchartModel {\n name: string;\n description?: string;\n sql: string;\n metadata: {\n name: string;\n description?: string;\n returns?: Array<{ name: string; type?: string }>;\n params?: Array<{ name: string; type: string; default?: string | number; options?: string[] }>;\n };\n sourceTable?: string;\n}\n\ninterface ChartHint {\n modelName: string;\n measures: string[];\n dimensions: string[];\n timeDimensions: string[];\n formats: Record<string, string>;\n}\n\nexport interface BuildInput {\n catalog?: CatalogData;\n models?: YamchartModel[];\n chartHints?: ChartHint[];\n}\n\nexport class SemanticModelBuilder {\n private filter: GoldFilterConfig;\n\n constructor(config?: SemanticConfig) {\n this.filter = config?.include ?? DEFAULT_FILTER;\n }\n\n build(input: BuildInput): SemanticModel {\n const { catalog, models, chartHints } = input;\n\n // Step 1: Build catalog entities (gold only)\n const catalogEntities = catalog ? this.buildCatalogEntities(catalog) : [];\n\n // Step 2: Build yamchart model entities\n const modelEntities = models ? this.buildModelEntities(models) : [];\n\n // Step 3: Apply chart hints to enrich classification\n const allEntities = [...catalogEntities, ...modelEntities];\n if (chartHints) {\n this.applyChartHints(allEntities, chartHints);\n }\n\n // Step 4: Merge — yamchart model wins over catalog for same source_table\n const merged = this.mergeEntities(catalogEntities, modelEntities);\n\n // Step 5: Extract relationships\n const relationships = catalog ? this.extractRelationships(catalog, merged) : [];\n\n return {\n entities: merged,\n relationships,\n generated_at: new Date().toISOString(),\n };\n }\n\n private isGoldModel(entry: CatalogEntry): boolean {\n const path = entry.path?.toLowerCase() ?? '';\n const name = entry.name.toLowerCase();\n\n const pathMatch = this.filter.paths.some((p) => path.includes(p.toLowerCase()));\n const prefixMatch = this.filter.prefixes.some((p) => name.startsWith(p.toLowerCase()));\n\n return pathMatch || prefixMatch;\n }\n\n private buildCatalogEntities(catalog: CatalogData): SemanticEntity[] {\n return catalog.models\n .filter((entry) => this.isGoldModel(entry))\n .filter((entry) => entry.table) // must have a table path\n .map((entry) => {\n const { measures, dimensions } = classifyCatalogColumns(entry.columns ?? []);\n return {\n name: entry.name,\n description: entry.description && entry.description !== 'No description'\n ? entry.description\n : undefined,\n source: 'catalog' as const,\n source_table: entry.table!,\n measures,\n dimensions,\n };\n });\n }\n\n private buildModelEntities(models: YamchartModel[]): SemanticEntity[] {\n return models\n .filter((m) => m.metadata.returns && m.metadata.returns.length > 0)\n .map((m) => {\n const measures: Measure[] = [];\n const dimensions: Dimension[] = [];\n\n for (const col of m.metadata.returns!) {\n const colType = col.type?.toLowerCase();\n if (colType === 'number') {\n measures.push({\n name: col.name,\n type: this.inferAggType(col.name),\n });\n } else if (colType === 'date' || colType === 'datetime' || colType === 'timestamp') {\n dimensions.push({ name: col.name, type: 'time' });\n } else {\n dimensions.push({ name: col.name, type: 'string' });\n }\n }\n\n const parameters = m.metadata.params?.map((p) => ({\n name: p.name,\n type: p.type,\n default: p.default,\n options: p.options,\n }));\n\n return {\n name: m.name,\n description: m.description ?? m.metadata.description,\n source: 'model' as const,\n source_table: m.sourceTable ?? m.name,\n measures,\n dimensions,\n parameters: parameters?.length ? parameters : undefined,\n };\n });\n }\n\n private inferAggType(name: string): Measure['type'] {\n const lower = name.toLowerCase();\n if (/(_count$|^count_|^num_)/.test(lower)) return 'count';\n if (/(_avg$|_average$|_mean$|^avg_)/.test(lower)) return 'avg';\n if (/(^min_|_min$)/.test(lower)) return 'min';\n if (/(^max_|_max$)/.test(lower)) return 'max';\n return 'sum';\n }\n\n private applyChartHints(entities: SemanticEntity[], hints: ChartHint[]): void {\n for (const hint of hints) {\n const entity = entities.find((e) =>\n e.name === hint.modelName || e.source_table.toLowerCase().includes(hint.modelName.toLowerCase())\n );\n if (!entity) continue;\n\n // Promote fields to measures if chart uses them as y-axis\n for (const measureName of hint.measures) {\n const dimIdx = entity.dimensions.findIndex((d) => d.name === measureName);\n if (dimIdx !== -1) {\n const dim = entity.dimensions.splice(dimIdx, 1)[0]!;\n entity.measures.push({ name: dim.name, type: this.inferAggType(dim.name) });\n }\n }\n\n // Promote fields to time dimensions if chart uses them as temporal x-axis\n for (const timeDim of hint.timeDimensions) {\n const dim = entity.dimensions.find((d) => d.name === timeDim);\n if (dim) dim.type = 'time';\n }\n\n // Apply format hints\n for (const [field, format] of Object.entries(hint.formats)) {\n const measure = entity.measures.find((m) => m.name === field);\n if (measure) measure.format = format;\n }\n }\n }\n\n private mergeEntities(catalogEntities: SemanticEntity[], modelEntities: SemanticEntity[]): SemanticEntity[] {\n // Collect source tables claimed by yamchart models\n const modelTables = new Set(\n modelEntities.map((e) => e.source_table.toLowerCase())\n );\n\n // Filter out catalog entities whose source_table is claimed by a model entity\n const filteredCatalog = catalogEntities.filter(\n (e) => !modelTables.has(e.source_table.toLowerCase())\n );\n\n return [...filteredCatalog, ...modelEntities];\n }\n\n private extractRelationships(catalog: CatalogData, entities: SemanticEntity[]): SemanticModel['relationships'] {\n const entityNames = new Set(entities.map((e) => e.name));\n const relationships: SemanticModel['relationships'] = [];\n\n for (const entry of catalog.models) {\n if (!entityNames.has(entry.name)) continue;\n if (!entry.dependsOn) continue;\n\n for (const dep of entry.dependsOn) {\n // dependsOn format: \"model.project_name.model_name\"\n const parts = dep.split('.');\n const depName = parts[parts.length - 1]!;\n if (!entityNames.has(depName)) continue;\n\n // Find shared column names (likely join keys)\n const fromEntity = entities.find((e) => e.name === entry.name);\n const toEntity = entities.find((e) => e.name === depName);\n if (!fromEntity || !toEntity) continue;\n\n const fromFields = [...fromEntity.measures, ...fromEntity.dimensions].map((f) => f.name.toLowerCase());\n const toFields = [...toEntity.measures, ...toEntity.dimensions].map((f) => f.name.toLowerCase());\n const shared = fromFields.filter((f) => toFields.includes(f) && f.endsWith('_id'));\n\n if (shared.length > 0) {\n relationships.push({\n from: { entity: entry.name, field: shared[0]! },\n to: { entity: depName, field: shared[0]! },\n type: 'many_to_one',\n });\n }\n }\n }\n\n return relationships;\n }\n}\n","import type { SemanticModel, SemanticQuery, SemanticFilter, MeasureType } from '@yamchart/schema';\n\nexport class SemanticQueryCompiler {\n compile(model: SemanticModel, query: SemanticQuery): string {\n const entity = model.entities.find((e) => e.name === query.model);\n if (!entity) throw new Error(`Entity '${query.model}' not found`);\n\n const selectParts: string[] = [];\n const groupByParts: string[] = [];\n\n // Dimensions\n for (const dimName of query.dimensions) {\n selectParts.push(`\"${dimName}\"`);\n groupByParts.push(`\"${dimName}\"`);\n }\n\n // Measures with aggregation\n for (const measureName of query.measures) {\n const measure = entity.measures.find((m) => m.name === measureName);\n if (!measure) throw new Error(`Measure '${measureName}' not found`);\n selectParts.push(`${this.formatMeasure(measure.type, measureName)} AS \"${measureName}\"`);\n }\n\n // Build SQL\n const parts: string[] = [];\n parts.push(`SELECT ${selectParts.join(', ')}`);\n parts.push(`FROM ${entity.source_table}`);\n\n // WHERE\n if (query.filters.length > 0) {\n const conditions = query.filters.map((f) => this.compileFilter(f));\n parts.push(`WHERE ${conditions.join(' AND ')}`);\n }\n\n // GROUP BY\n if (groupByParts.length > 0) {\n parts.push(`GROUP BY ${groupByParts.join(', ')}`);\n }\n\n // ORDER BY\n if (query.order_by) {\n parts.push(`ORDER BY \"${query.order_by.field}\" ${query.order_by.direction.toUpperCase()}`);\n }\n\n // LIMIT\n parts.push(`LIMIT ${query.limit}`);\n\n return parts.join('\\n');\n }\n\n private formatMeasure(type: MeasureType, column: string): string {\n if (type === 'count_distinct') {\n return `COUNT(DISTINCT \"${column}\")`;\n }\n return `${this.aggFunction(type)}(\"${column}\")`;\n }\n\n private aggFunction(type: Exclude<MeasureType, 'count_distinct'>): string {\n switch (type) {\n case 'sum': return 'SUM';\n case 'count': return 'COUNT';\n case 'avg': return 'AVG';\n case 'min': return 'MIN';\n case 'max': return 'MAX';\n }\n }\n\n private compileFilter(filter: SemanticFilter): string {\n const field = `\"${filter.dimension}\"`;\n const value = filter.value;\n\n switch (filter.operator) {\n case 'equals':\n return `${field} = ${this.quoteValue(value)}`;\n case 'not_equals':\n return `${field} != ${this.quoteValue(value)}`;\n case 'contains':\n return `${field} LIKE '%${this.escapeString(String(value))}%'`;\n case 'gt':\n return `${field} > ${this.quoteValue(value)}`;\n case 'gte':\n return `${field} >= ${this.quoteValue(value)}`;\n case 'lt':\n return `${field} < ${this.quoteValue(value)}`;\n case 'lte':\n return `${field} <= ${this.quoteValue(value)}`;\n case 'in':\n return `${field} IN (${(value as Array<string | number>).map((v) => this.quoteValue(v)).join(', ')})`;\n case 'not_in':\n return `${field} NOT IN (${(value as Array<string | number>).map((v) => this.quoteValue(v)).join(', ')})`;\n default:\n throw new Error(`Unknown filter operator: ${filter.operator}`);\n }\n }\n\n private quoteValue(value: string | number | boolean | Array<string | number>): string {\n if (typeof value === 'number') return String(value);\n if (typeof value === 'boolean') return String(value);\n return `'${this.escapeString(String(value))}'`;\n }\n\n private escapeString(value: string): string {\n return value.replace(/'/g, \"''\");\n }\n}\n","import type { SemanticModel, SemanticQuery } from '@yamchart/schema';\n\nexport class SemanticValidationError extends Error {\n code: string;\n available?: string[];\n\n constructor(code: string, message: string, available?: string[]) {\n super(message);\n this.name = 'SemanticValidationError';\n this.code = code;\n this.available = available;\n }\n}\n\nexport function validateSemanticQuery(model: SemanticModel, query: SemanticQuery): void {\n // Validate model exists\n const entity = model.entities.find((e) => e.name === query.model);\n if (!entity) {\n throw new SemanticValidationError(\n 'unknown_model',\n `Unknown model '${query.model}'. Available models: ${model.entities.map((e) => e.name).join(', ')}`,\n model.entities.map((e) => e.name),\n );\n }\n\n const measureNames = entity.measures.map((m) => m.name);\n const dimensionNames = entity.dimensions.map((d) => d.name);\n const allFields = [...measureNames, ...dimensionNames];\n\n // Validate measures\n for (const m of query.measures) {\n if (!measureNames.includes(m)) {\n throw new SemanticValidationError(\n 'unknown_measure',\n `Unknown measure '${m}' on entity '${query.model}'. Available measures: ${measureNames.join(', ')}`,\n measureNames,\n );\n }\n }\n\n // Validate dimensions\n for (const d of query.dimensions) {\n if (!dimensionNames.includes(d)) {\n throw new SemanticValidationError(\n 'unknown_dimension',\n `Unknown dimension '${d}' on entity '${query.model}'. Available dimensions: ${dimensionNames.join(', ')}`,\n dimensionNames,\n );\n }\n }\n\n // Validate filter dimensions\n for (const f of query.filters) {\n if (!dimensionNames.includes(f.dimension)) {\n throw new SemanticValidationError(\n 'unknown_filter_dimension',\n `Unknown filter dimension '${f.dimension}' on entity '${query.model}'. Available dimensions: ${dimensionNames.join(', ')}`,\n dimensionNames,\n );\n }\n }\n\n // Validate order_by field\n if (query.order_by) {\n if (!allFields.includes(query.order_by.field)) {\n throw new SemanticValidationError(\n 'unknown_order_field',\n `Unknown order_by field '${query.order_by.field}' on entity '${query.model}'. Available fields: ${allFields.join(', ')}`,\n allFields,\n );\n }\n }\n}\n"],"mappings":"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","names":[]}
|