@narrative.io/data-collaboration-sdk-ts 0.0.122 → 0.0.124
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/build/datasets/index.d.ts +2 -2
- package/build/datasets/types.d.ts +17 -17
- package/build/index.d.ts +2 -2
- package/build/nql/Ast.d.ts +184 -0
- package/build/nql/Ast.js +3 -0
- package/build/nql/AstParser.d.ts +186 -0
- package/build/nql/AstParser.js +538 -0
- package/build/nql/DataRulesConverter.d.ts +15 -0
- package/build/nql/DataRulesConverter.js +149 -0
- package/build/nql/NQLParser.js +1 -1
- package/build/nql/NqlBuilder.d.ts +8 -0
- package/build/nql/NqlBuilder.js +297 -0
- package/build/nql/index.d.ts +10 -1
- package/build/nql/index.js +11 -0
- package/build/nql/types.d.ts +298 -298
- package/package.json +1 -1
|
@@ -0,0 +1,538 @@
|
|
|
1
|
+
import * as ast from "./Ast";
|
|
2
|
+
export function parseSelect(n) {
|
|
3
|
+
const select = {
|
|
4
|
+
type: "select",
|
|
5
|
+
budget: n.budget,
|
|
6
|
+
columns: n.columns.map((col) => parseExpression(col)),
|
|
7
|
+
from: n.from !== null ? parseFrom(n.from) : [],
|
|
8
|
+
where: n.where !== null ? parseWhere(n.where) : null,
|
|
9
|
+
};
|
|
10
|
+
return select;
|
|
11
|
+
}
|
|
12
|
+
export function parseFrom(n) {
|
|
13
|
+
try {
|
|
14
|
+
switch (n.type) {
|
|
15
|
+
case "join":
|
|
16
|
+
return parseJoin(n);
|
|
17
|
+
case "table":
|
|
18
|
+
return [parseTable(n)];
|
|
19
|
+
default:
|
|
20
|
+
return [parseRawTable(n)];
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
catch (e) {
|
|
24
|
+
console.debug(`failed to parse 'from' clause`, e);
|
|
25
|
+
return [parseRawTable(n)];
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
export function parseWhere(n) {
|
|
29
|
+
try {
|
|
30
|
+
return parseBooleanExpression(n);
|
|
31
|
+
}
|
|
32
|
+
catch (e) {
|
|
33
|
+
console.debug(`failed to where from clause`, e);
|
|
34
|
+
return nqlOrFail(n);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
export function parseExpression(n) {
|
|
38
|
+
const parse = oneOf(parseArithmetic, parseBooleanExpression, parseColumnRef, parseFunctionCall, parseLit, parseStringExpression);
|
|
39
|
+
return parse(n);
|
|
40
|
+
}
|
|
41
|
+
function parseBooleanExpression(n) {
|
|
42
|
+
switch (n.type) {
|
|
43
|
+
case "binary_op": {
|
|
44
|
+
switch (n.name) {
|
|
45
|
+
case "=":
|
|
46
|
+
return parseEquals(n);
|
|
47
|
+
case "<>":
|
|
48
|
+
return parseNotEquals(n);
|
|
49
|
+
case "<":
|
|
50
|
+
return parseLt(n);
|
|
51
|
+
case "<=":
|
|
52
|
+
return parseLte(n);
|
|
53
|
+
case ">":
|
|
54
|
+
return parseGt(n);
|
|
55
|
+
case ">=":
|
|
56
|
+
return parseGte(n);
|
|
57
|
+
case "AND":
|
|
58
|
+
return parseAnd(n);
|
|
59
|
+
case "IN":
|
|
60
|
+
return parseIn(n, false);
|
|
61
|
+
case "LIKE":
|
|
62
|
+
return parseLike(n, false);
|
|
63
|
+
case "NOT IN":
|
|
64
|
+
return parseIn(n, true);
|
|
65
|
+
case "NOT LIKE":
|
|
66
|
+
return parseLike(n, true);
|
|
67
|
+
case "OR":
|
|
68
|
+
return parseOr(n);
|
|
69
|
+
default:
|
|
70
|
+
return nqlOrFail(n);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
case "operator": {
|
|
74
|
+
switch (n.name) {
|
|
75
|
+
case "BETWEEN ASYMMETRIC":
|
|
76
|
+
return parseBetween(n);
|
|
77
|
+
case "IS NULL":
|
|
78
|
+
return parseIsNull(n, false);
|
|
79
|
+
case "IS NOT NULL":
|
|
80
|
+
return parseIsNull(n, true);
|
|
81
|
+
case "NOT":
|
|
82
|
+
return parseNot(n);
|
|
83
|
+
default:
|
|
84
|
+
return nqlOrFail(n);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
default:
|
|
88
|
+
return nqlOrFail(n);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
function parseStringExpression(n) {
|
|
92
|
+
switch (n.type) {
|
|
93
|
+
case "binary_op": {
|
|
94
|
+
switch (n.name) {
|
|
95
|
+
case "||":
|
|
96
|
+
return parseConcat(n);
|
|
97
|
+
default:
|
|
98
|
+
return nqlOrFail(n);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
default:
|
|
102
|
+
return nqlOrFail(n);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
function parseConcat(n) {
|
|
106
|
+
const operands = parseOperands(n, "||");
|
|
107
|
+
const concat = {
|
|
108
|
+
type: "||",
|
|
109
|
+
as: n.as,
|
|
110
|
+
operands,
|
|
111
|
+
};
|
|
112
|
+
return concat;
|
|
113
|
+
}
|
|
114
|
+
function parseArithmetic(n) {
|
|
115
|
+
switch (n.type) {
|
|
116
|
+
case "binary_op": {
|
|
117
|
+
switch (n.name) {
|
|
118
|
+
case "+":
|
|
119
|
+
return parseAdd(n);
|
|
120
|
+
case "/":
|
|
121
|
+
return parseDivide(n);
|
|
122
|
+
case "*":
|
|
123
|
+
return parseMultiply(n);
|
|
124
|
+
case "-":
|
|
125
|
+
return parseSubtract(n);
|
|
126
|
+
default:
|
|
127
|
+
return nqlOrFail(n);
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
case "operator": {
|
|
131
|
+
switch (n.name) {
|
|
132
|
+
case "-":
|
|
133
|
+
return parseUnaryMinus(n);
|
|
134
|
+
default:
|
|
135
|
+
return nqlOrFail(n);
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
default:
|
|
139
|
+
return nqlOrFail(n);
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
function parseAdd(n) {
|
|
143
|
+
const operands = parseOperands(n, "+");
|
|
144
|
+
const add = {
|
|
145
|
+
type: "+",
|
|
146
|
+
as: n.as,
|
|
147
|
+
operands,
|
|
148
|
+
};
|
|
149
|
+
return add;
|
|
150
|
+
}
|
|
151
|
+
function parseDivide(n) {
|
|
152
|
+
const operands = parseOperands(n, "/");
|
|
153
|
+
const divide = {
|
|
154
|
+
type: "/",
|
|
155
|
+
as: n.as,
|
|
156
|
+
operands,
|
|
157
|
+
};
|
|
158
|
+
return divide;
|
|
159
|
+
}
|
|
160
|
+
function parseMultiply(n) {
|
|
161
|
+
const operands = parseOperands(n, "*");
|
|
162
|
+
const mult = {
|
|
163
|
+
type: "*",
|
|
164
|
+
as: n.as,
|
|
165
|
+
operands,
|
|
166
|
+
};
|
|
167
|
+
return mult;
|
|
168
|
+
}
|
|
169
|
+
function parseSubtract(n) {
|
|
170
|
+
const operands = parseOperands(n, "-");
|
|
171
|
+
const sub = {
|
|
172
|
+
type: "-",
|
|
173
|
+
as: n.as,
|
|
174
|
+
operands,
|
|
175
|
+
};
|
|
176
|
+
return sub;
|
|
177
|
+
}
|
|
178
|
+
function parseUnaryMinus(n) {
|
|
179
|
+
const operand = parseExpression(n.args[0]);
|
|
180
|
+
const minus = {
|
|
181
|
+
type: "unary_-",
|
|
182
|
+
as: n.as,
|
|
183
|
+
operand,
|
|
184
|
+
};
|
|
185
|
+
return minus;
|
|
186
|
+
}
|
|
187
|
+
function parseBetween(n) {
|
|
188
|
+
const operand = parseExpression(n.args[0]);
|
|
189
|
+
const lower = parseExpression(n.args[1]);
|
|
190
|
+
const upper = parseExpression(n.args[2]);
|
|
191
|
+
const between = {
|
|
192
|
+
type: "between",
|
|
193
|
+
as: n.as,
|
|
194
|
+
operand,
|
|
195
|
+
lower,
|
|
196
|
+
upper,
|
|
197
|
+
};
|
|
198
|
+
return between;
|
|
199
|
+
}
|
|
200
|
+
function parseColumnRef(n) {
|
|
201
|
+
switch (n.type) {
|
|
202
|
+
case "column": {
|
|
203
|
+
if (n.schema === "narrative" && n.table === "rosetta_stone") {
|
|
204
|
+
const name = n.column.split(".")[0];
|
|
205
|
+
const attrRef = {
|
|
206
|
+
type: "attribute_ref",
|
|
207
|
+
as: n.as,
|
|
208
|
+
name,
|
|
209
|
+
column: n.column,
|
|
210
|
+
};
|
|
211
|
+
return attrRef;
|
|
212
|
+
}
|
|
213
|
+
if (n.schema === "company_data" && !isNaN(parseInt(n.table))) {
|
|
214
|
+
const datasetId = parseInt(n.table);
|
|
215
|
+
const datasetColumnRef = {
|
|
216
|
+
type: "dataset_column_ref",
|
|
217
|
+
as: n.as,
|
|
218
|
+
datasetId,
|
|
219
|
+
column: n.column,
|
|
220
|
+
};
|
|
221
|
+
return datasetColumnRef;
|
|
222
|
+
}
|
|
223
|
+
const rawRef = {
|
|
224
|
+
type: "raw_ref",
|
|
225
|
+
as: n.as,
|
|
226
|
+
nql: n.nql,
|
|
227
|
+
};
|
|
228
|
+
return rawRef;
|
|
229
|
+
}
|
|
230
|
+
default:
|
|
231
|
+
return nqlOrFail(n);
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
function parseFunctionCall(n) {
|
|
235
|
+
switch (n.type) {
|
|
236
|
+
case "function": {
|
|
237
|
+
const args = n.args.map((arg) => parseExpression(arg));
|
|
238
|
+
const fn = {
|
|
239
|
+
type: "function",
|
|
240
|
+
as: n.as,
|
|
241
|
+
args,
|
|
242
|
+
name: n.name,
|
|
243
|
+
};
|
|
244
|
+
return fn;
|
|
245
|
+
}
|
|
246
|
+
default:
|
|
247
|
+
return nqlOrFail(n);
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
function parseLit(n) {
|
|
251
|
+
switch (n.type) {
|
|
252
|
+
case "literal": {
|
|
253
|
+
const lit = {
|
|
254
|
+
type: "lit",
|
|
255
|
+
as: n.as,
|
|
256
|
+
value_type: n.value_type,
|
|
257
|
+
value: n.value,
|
|
258
|
+
};
|
|
259
|
+
return lit;
|
|
260
|
+
}
|
|
261
|
+
default:
|
|
262
|
+
return nqlOrFail(n);
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
function parseAnd(n) {
|
|
266
|
+
const operands = parseOperands(n, "and");
|
|
267
|
+
const and = {
|
|
268
|
+
type: "and",
|
|
269
|
+
as: n.as,
|
|
270
|
+
operands,
|
|
271
|
+
};
|
|
272
|
+
return and;
|
|
273
|
+
}
|
|
274
|
+
function parseOr(n) {
|
|
275
|
+
const operands = parseOperands(n, "or");
|
|
276
|
+
const or = {
|
|
277
|
+
type: "or",
|
|
278
|
+
as: n.as,
|
|
279
|
+
operands,
|
|
280
|
+
};
|
|
281
|
+
return or;
|
|
282
|
+
}
|
|
283
|
+
function parseEquals(n) {
|
|
284
|
+
const eq = {
|
|
285
|
+
type: "=",
|
|
286
|
+
left: parseExpression(n.left),
|
|
287
|
+
right: parseExpression(n.right),
|
|
288
|
+
};
|
|
289
|
+
return eq;
|
|
290
|
+
}
|
|
291
|
+
function parseNotEquals(n) {
|
|
292
|
+
const neq = {
|
|
293
|
+
type: "<>",
|
|
294
|
+
as: n.as,
|
|
295
|
+
left: parseExpression(n.left),
|
|
296
|
+
right: parseExpression(n.right),
|
|
297
|
+
};
|
|
298
|
+
return neq;
|
|
299
|
+
}
|
|
300
|
+
function parseGt(n) {
|
|
301
|
+
const gt = {
|
|
302
|
+
type: ">",
|
|
303
|
+
as: n.as,
|
|
304
|
+
left: parseExpression(n.left),
|
|
305
|
+
right: parseExpression(n.right),
|
|
306
|
+
};
|
|
307
|
+
return gt;
|
|
308
|
+
}
|
|
309
|
+
function parseGte(n) {
|
|
310
|
+
const gte = {
|
|
311
|
+
type: ">=",
|
|
312
|
+
as: n.as,
|
|
313
|
+
left: parseExpression(n.left),
|
|
314
|
+
right: parseExpression(n.right),
|
|
315
|
+
};
|
|
316
|
+
return gte;
|
|
317
|
+
}
|
|
318
|
+
function parseLt(n) {
|
|
319
|
+
const lt = {
|
|
320
|
+
type: "<",
|
|
321
|
+
as: n.as,
|
|
322
|
+
left: parseExpression(n.left),
|
|
323
|
+
right: parseExpression(n.right),
|
|
324
|
+
};
|
|
325
|
+
return lt;
|
|
326
|
+
}
|
|
327
|
+
function parseLte(n) {
|
|
328
|
+
const lte = {
|
|
329
|
+
type: "<=",
|
|
330
|
+
as: n.as,
|
|
331
|
+
left: parseExpression(n.left),
|
|
332
|
+
right: parseExpression(n.right),
|
|
333
|
+
};
|
|
334
|
+
return lte;
|
|
335
|
+
}
|
|
336
|
+
function parseIn(n, negated) {
|
|
337
|
+
const expression = parseExpression(n.left);
|
|
338
|
+
let values;
|
|
339
|
+
if (ast.isAstArray(n.right)) {
|
|
340
|
+
values = n.right.map((value) => parseExpression(value));
|
|
341
|
+
}
|
|
342
|
+
else {
|
|
343
|
+
values = nqlOrFail(n.right);
|
|
344
|
+
}
|
|
345
|
+
const _in = {
|
|
346
|
+
type: "in",
|
|
347
|
+
as: n.as,
|
|
348
|
+
negated,
|
|
349
|
+
expression,
|
|
350
|
+
values,
|
|
351
|
+
};
|
|
352
|
+
return _in;
|
|
353
|
+
}
|
|
354
|
+
function parseIsNull(n, negated) {
|
|
355
|
+
// IS NULL/IS NOT NULL is always supplied a single argument
|
|
356
|
+
const operand = parseExpression(n.args[0]);
|
|
357
|
+
const isNull = {
|
|
358
|
+
type: "is_null",
|
|
359
|
+
as: n.as,
|
|
360
|
+
negated,
|
|
361
|
+
operand,
|
|
362
|
+
};
|
|
363
|
+
return isNull;
|
|
364
|
+
}
|
|
365
|
+
function parseNot(n) {
|
|
366
|
+
const operand = parseBooleanExpression(n.args[0]);
|
|
367
|
+
const not = {
|
|
368
|
+
type: "not",
|
|
369
|
+
as: n.as,
|
|
370
|
+
operand,
|
|
371
|
+
};
|
|
372
|
+
return not;
|
|
373
|
+
}
|
|
374
|
+
function parseLike(n, negated) {
|
|
375
|
+
const value = parseExpression(n.left);
|
|
376
|
+
switch (n.right.type) {
|
|
377
|
+
case "literal": {
|
|
378
|
+
if (n.right.value_type.type === "string" && n.right.value !== null) {
|
|
379
|
+
const like = {
|
|
380
|
+
type: "like",
|
|
381
|
+
as: n.as,
|
|
382
|
+
negated,
|
|
383
|
+
value,
|
|
384
|
+
pattern: n.right.value,
|
|
385
|
+
};
|
|
386
|
+
return like;
|
|
387
|
+
}
|
|
388
|
+
else {
|
|
389
|
+
// if the pattern is not a string literal, fail parsing of the entire expression
|
|
390
|
+
return nqlOrFail(n);
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
default:
|
|
394
|
+
return nqlOrFail(n);
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
/**
|
|
398
|
+
* Parses the operands of a binary expression and returns an array of Expressions.
|
|
399
|
+
*
|
|
400
|
+
* If the `left` or `right` arguments to `n` are of type `type`, then the operands are flattened
|
|
401
|
+
* to become operands for the top level expression.
|
|
402
|
+
*
|
|
403
|
+
* E.g., given
|
|
404
|
+
* AND
|
|
405
|
+
* / \
|
|
406
|
+
* OR AND
|
|
407
|
+
* / \ / \
|
|
408
|
+
* a b AND c
|
|
409
|
+
* / \
|
|
410
|
+
* d e
|
|
411
|
+
* Returns an expression of the form:
|
|
412
|
+
*
|
|
413
|
+
* (a OR b) AND c AND d AND e
|
|
414
|
+
*
|
|
415
|
+
* A naive implementation would return:
|
|
416
|
+
*
|
|
417
|
+
* ((((A OR b) AND c) AND d) AND e)
|
|
418
|
+
*
|
|
419
|
+
* The practical reason for doing this is to simply the expression to eliminate the need to insert parentheses
|
|
420
|
+
* around every subtree.
|
|
421
|
+
* @param {ast.NqlBinaryOp} n - The binary operation to parse.
|
|
422
|
+
* @param {string} type - The type to match when collecting operands.
|
|
423
|
+
* @returns {Expression[]} - An array of Expression objects representing the operands.
|
|
424
|
+
*/
|
|
425
|
+
function parseOperands(n, type) {
|
|
426
|
+
const left = parseExpression(n.left);
|
|
427
|
+
const right = parseExpression(n.right);
|
|
428
|
+
const operands = [];
|
|
429
|
+
// NB: the `"operands" in left` check provides evidence to the typescript compile that we can call `.operands` on `left`
|
|
430
|
+
if (left.type === type && "operands" in left) {
|
|
431
|
+
operands.push(...left.operands);
|
|
432
|
+
}
|
|
433
|
+
else {
|
|
434
|
+
operands.push(left);
|
|
435
|
+
}
|
|
436
|
+
if (right.type === type && "operands" in right) {
|
|
437
|
+
operands.push(...right.operands);
|
|
438
|
+
}
|
|
439
|
+
else {
|
|
440
|
+
operands.push(right);
|
|
441
|
+
}
|
|
442
|
+
return operands;
|
|
443
|
+
}
|
|
444
|
+
function parseJoin(n) {
|
|
445
|
+
const left = parseFrom(n.left);
|
|
446
|
+
const right = parseFrom(n.right);
|
|
447
|
+
const condition = n.condition !== null ? parseBooleanExpression(n.condition) : null;
|
|
448
|
+
const join = {
|
|
449
|
+
type: "join",
|
|
450
|
+
condition,
|
|
451
|
+
conditionType: n.condition_type,
|
|
452
|
+
joinType: n.join_type,
|
|
453
|
+
};
|
|
454
|
+
right[0].join = join;
|
|
455
|
+
return left.concat(right);
|
|
456
|
+
}
|
|
457
|
+
function parseTable(n) {
|
|
458
|
+
if (n.schema === "narrative" && n.table === "rosetta_stone") {
|
|
459
|
+
const table = {
|
|
460
|
+
type: "rosetta_stone",
|
|
461
|
+
as: n.as,
|
|
462
|
+
};
|
|
463
|
+
return table;
|
|
464
|
+
}
|
|
465
|
+
if (n.schema === "company_data" && !isNaN(parseInt(n.table))) {
|
|
466
|
+
const datasetId = parseInt(n.table);
|
|
467
|
+
const table = {
|
|
468
|
+
type: "dataset",
|
|
469
|
+
as: n.as,
|
|
470
|
+
datasetId,
|
|
471
|
+
};
|
|
472
|
+
return table;
|
|
473
|
+
}
|
|
474
|
+
return parseRawTable(n);
|
|
475
|
+
}
|
|
476
|
+
function parseRawTable(n) {
|
|
477
|
+
const raw = nqlOrFail(n);
|
|
478
|
+
const as = "as" in n ? n.as : undefined;
|
|
479
|
+
const table = {
|
|
480
|
+
type: "raw_table",
|
|
481
|
+
as,
|
|
482
|
+
nql: raw.nql,
|
|
483
|
+
};
|
|
484
|
+
return table;
|
|
485
|
+
}
|
|
486
|
+
/**
|
|
487
|
+
* Selects the first successful expression from a list of parsers.
|
|
488
|
+
*
|
|
489
|
+
* @template A - The type of the input object that extends `ast.NqlAst`.
|
|
490
|
+
* @param {...Array<(a: A) => Expression>} fs - An array of functions that take an object of type `A` and return an `Expression`.
|
|
491
|
+
* @returns {(a: A) => Expression} - A function that takes an object of type `A` and returns the first successful `Expression` produced by one of the input functions.
|
|
492
|
+
*
|
|
493
|
+
* @example
|
|
494
|
+
* // Define two functions that take an NqlAst object and return an Expression
|
|
495
|
+
* const func1 = (a: NqlAst) => ...;
|
|
496
|
+
* const func2 = (a: NqlAst) => ...;
|
|
497
|
+
*
|
|
498
|
+
* // Create a function that selects the first successful expression between func1 and func2
|
|
499
|
+
* const selectExpression = oneOf(func1, func2);
|
|
500
|
+
*
|
|
501
|
+
* // Use the selectExpression function to get the result
|
|
502
|
+
* const result = selectExpression(myNqlAstObject);
|
|
503
|
+
*
|
|
504
|
+
* if (result.type !== 'nql') {
|
|
505
|
+
* console.log('Successfully selected an expression:', result);
|
|
506
|
+
* } else {
|
|
507
|
+
* console.error('All input functions failed.');
|
|
508
|
+
* }
|
|
509
|
+
*/
|
|
510
|
+
function oneOf(...fs) {
|
|
511
|
+
return (a) => {
|
|
512
|
+
const expressions = [];
|
|
513
|
+
for (const f of fs) {
|
|
514
|
+
expressions.push(f(a));
|
|
515
|
+
}
|
|
516
|
+
const successes = expressions.filter((e) => e.type !== "nql");
|
|
517
|
+
if (successes.length > 0) {
|
|
518
|
+
return successes[0];
|
|
519
|
+
}
|
|
520
|
+
else {
|
|
521
|
+
return expressions[0];
|
|
522
|
+
}
|
|
523
|
+
};
|
|
524
|
+
}
|
|
525
|
+
function nqlOrFail(n) {
|
|
526
|
+
if ("nql" in n) {
|
|
527
|
+
const as = "as" in n ? n.as : undefined;
|
|
528
|
+
const nql = {
|
|
529
|
+
type: "nql",
|
|
530
|
+
as,
|
|
531
|
+
nql: n.nql,
|
|
532
|
+
};
|
|
533
|
+
return nql;
|
|
534
|
+
}
|
|
535
|
+
else {
|
|
536
|
+
throw new Error(`no raw nql available for node '${n.type}'`);
|
|
537
|
+
}
|
|
538
|
+
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { type ObjectAttribute } from "src/attributes/types";
|
|
2
2
|
import { type Attribute } from "../attributes";
|
|
3
3
|
import type { DataRulesAttributesField, ForecastBody, Nql } from "./types";
|
|
4
|
+
import type { DataRules } from "../data-streams/types";
|
|
4
5
|
/**
|
|
5
6
|
* Convert an NQL object to a Forecast body.
|
|
6
7
|
* @param {Nql} nqlObject - The NQL object to convert.
|
|
@@ -48,3 +49,17 @@ export declare function findAttributeProperties(attributes: Attribute[], name: s
|
|
|
48
49
|
* buildField('person', ['name', 'age']);
|
|
49
50
|
*/
|
|
50
51
|
export declare function buildField(attribute: string, properties: string[]): DataRulesAttributesField[];
|
|
52
|
+
/**
|
|
53
|
+
* Generates the SQL FROM clause based on the given data rules.
|
|
54
|
+
*
|
|
55
|
+
* @param {DataRules} dataRules - The data rules specifying the datasets, attributes, and filters.
|
|
56
|
+
* @returns {string} The SQL FROM clause.
|
|
57
|
+
*/
|
|
58
|
+
export declare function generateFromClause(dataRules: DataRules): string;
|
|
59
|
+
/**
|
|
60
|
+
* Converts the provided data rules into an SQL query.
|
|
61
|
+
*
|
|
62
|
+
* @param {DataRules} dataRules - The data rules specifying the datasets, attributes, and filters.
|
|
63
|
+
* @returns {string} The generated SQL query.
|
|
64
|
+
*/
|
|
65
|
+
export declare function dataRulesToSQL(dataRules: DataRules): string;
|
|
@@ -299,3 +299,152 @@ export function buildField(attribute, properties) {
|
|
|
299
299
|
};
|
|
300
300
|
});
|
|
301
301
|
}
|
|
302
|
+
/**
|
|
303
|
+
* Generates the SQL FROM clause based on the given data rules.
|
|
304
|
+
*
|
|
305
|
+
* @param {DataRules} dataRules - The data rules specifying the datasets, attributes, and filters.
|
|
306
|
+
* @returns {string} The SQL FROM clause.
|
|
307
|
+
*/
|
|
308
|
+
export function generateFromClause(dataRules) {
|
|
309
|
+
const fromClause = [];
|
|
310
|
+
// Handle Column Sets
|
|
311
|
+
if (dataRules.column_sets !== undefined) {
|
|
312
|
+
for (const columnSet of dataRules.column_sets) {
|
|
313
|
+
// eslint-disable-next-line @typescript-eslint/naming-convention
|
|
314
|
+
const { dataset_id } = columnSet;
|
|
315
|
+
fromClause.push(`company_data.${dataset_id}`);
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
// Handle Attributes
|
|
319
|
+
if (dataRules.attributes !== undefined) {
|
|
320
|
+
fromClause.push("narrative.rosetta_stone");
|
|
321
|
+
}
|
|
322
|
+
// Handle JOINs
|
|
323
|
+
if (dataRules.dataset_filter !== undefined) {
|
|
324
|
+
// eslint-disable-next-line @typescript-eslint/naming-convention
|
|
325
|
+
const { dataset_id, attribute } = dataRules.dataset_filter;
|
|
326
|
+
const joinClause = `JOIN company_data.${dataset_id} ON company_data.${dataset_id}."${attribute.field}" = narrative.rosetta_stone."${attribute.field}"`;
|
|
327
|
+
fromClause.push(joinClause);
|
|
328
|
+
}
|
|
329
|
+
return `FROM ${fromClause.join(", ")}`;
|
|
330
|
+
}
|
|
331
|
+
/**
|
|
332
|
+
* Converts the provided data rules into an SQL query.
|
|
333
|
+
*
|
|
334
|
+
* @param {DataRules} dataRules - The data rules specifying the datasets, attributes, and filters.
|
|
335
|
+
* @returns {string} The generated SQL query.
|
|
336
|
+
*/
|
|
337
|
+
export function dataRulesToSQL(dataRules) {
|
|
338
|
+
const selectClause = generateSelectClause(dataRules);
|
|
339
|
+
const fromClause = generateFromClause(dataRules);
|
|
340
|
+
const whereClause = generateWhereClause(dataRules);
|
|
341
|
+
let sql = `EXPLAIN ${selectClause} ${fromClause}`;
|
|
342
|
+
if (whereClause !== undefined) {
|
|
343
|
+
sql += ` ${whereClause} AND narrative.rosetta_stone._price_cpm_usd <= 2.0 LIMIT 50 USD PER CALENDAR_MONTH`;
|
|
344
|
+
}
|
|
345
|
+
return sql;
|
|
346
|
+
}
|
|
347
|
+
/**
|
|
348
|
+
* Generates the SQL SELECT clause based on the given data rules.
|
|
349
|
+
*
|
|
350
|
+
* @param {DataRules} dataRules - The data rules specifying the datasets, attributes, and filters.
|
|
351
|
+
* @returns {string} The SQL SELECT clause.
|
|
352
|
+
*/
|
|
353
|
+
function generateSelectClause(dataRules) {
|
|
354
|
+
let selectColumns = [];
|
|
355
|
+
const quoteField = (field) => field.includes(".")
|
|
356
|
+
? `${field.split(".")[0]}.` + '"' + field.split(".")[1] + '"'
|
|
357
|
+
: `"${field}"`;
|
|
358
|
+
// Handle Column Sets
|
|
359
|
+
if (dataRules.column_sets !== undefined) {
|
|
360
|
+
for (const columnSet of dataRules.column_sets) {
|
|
361
|
+
// eslint-disable-next-line @typescript-eslint/naming-convention
|
|
362
|
+
const { dataset_id, fields } = columnSet;
|
|
363
|
+
const qualifiedFields = fields
|
|
364
|
+
.filter((f) => f.exported)
|
|
365
|
+
.map((f) => `company_data.${dataset_id}.${quoteField(f.field)}`);
|
|
366
|
+
selectColumns = [...selectColumns, ...qualifiedFields];
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
// Handle Attributes
|
|
370
|
+
if (dataRules.attributes !== undefined) {
|
|
371
|
+
for (const attributeSet of dataRules.attributes) {
|
|
372
|
+
const { fields } = attributeSet;
|
|
373
|
+
const qualifiedFields = fields
|
|
374
|
+
.filter((f) => f.exported)
|
|
375
|
+
.map((f) => `narrative.rosetta_stone.${quoteField(f.field)}`);
|
|
376
|
+
selectColumns = [...selectColumns, ...qualifiedFields];
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
return `SELECT ${selectColumns.join(", ")}`;
|
|
380
|
+
}
|
|
381
|
+
/**
|
|
382
|
+
* Converts a filter definition into an SQL condition.
|
|
383
|
+
*
|
|
384
|
+
* @param {string} columnName - The name of the column the filter applies to.
|
|
385
|
+
* @param {FilterDefinition} filter - The filter definition.
|
|
386
|
+
* @returns {string | null} The SQL condition, or null if the filter does not translate into a condition.
|
|
387
|
+
*/
|
|
388
|
+
function convertFilterToSQLCondition(columnName, filter) {
|
|
389
|
+
const quoteField = (field) => {
|
|
390
|
+
const parts = field.split(".");
|
|
391
|
+
if (parts.length > 1) {
|
|
392
|
+
const lastPart = parts.pop();
|
|
393
|
+
return `${parts.join(".")}.` + `"${lastPart}"`;
|
|
394
|
+
}
|
|
395
|
+
return field;
|
|
396
|
+
};
|
|
397
|
+
const quotedColumnName = quoteField(columnName);
|
|
398
|
+
switch (filter) {
|
|
399
|
+
// Define your conversion logic here for each filter type
|
|
400
|
+
case "include_all_values_including_nulls_filter":
|
|
401
|
+
return null;
|
|
402
|
+
case "include_only_if_not_null_filter":
|
|
403
|
+
return `${quotedColumnName} IS NOT NULL`;
|
|
404
|
+
default:
|
|
405
|
+
// Handle other complex filter types
|
|
406
|
+
return null;
|
|
407
|
+
}
|
|
408
|
+
}
|
|
409
|
+
/**
|
|
410
|
+
* Generates the SQL WHERE clause based on the given data rules.
|
|
411
|
+
*
|
|
412
|
+
* @param {DataRules} dataRules - The data rules specifying the datasets, attributes, and filters.
|
|
413
|
+
* @returns {string} The SQL WHERE clause.
|
|
414
|
+
*/
|
|
415
|
+
function generateWhereClause(dataRules) {
|
|
416
|
+
const whereConditions = [];
|
|
417
|
+
// Handle Column Sets
|
|
418
|
+
if (dataRules.column_sets !== undefined) {
|
|
419
|
+
for (const columnSet of dataRules.column_sets) {
|
|
420
|
+
// eslint-disable-next-line @typescript-eslint/naming-convention
|
|
421
|
+
const { dataset_id, fields } = columnSet;
|
|
422
|
+
for (const field of fields) {
|
|
423
|
+
if (field.filter !== undefined) {
|
|
424
|
+
const condition = convertFilterToSQLCondition(`company_data.${dataset_id}.${field.field}`, field.filter);
|
|
425
|
+
if (condition !== null) {
|
|
426
|
+
whereConditions.push(condition);
|
|
427
|
+
}
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
// Handle Attributes
|
|
433
|
+
if (dataRules.attributes !== undefined) {
|
|
434
|
+
for (const attributeSet of dataRules.attributes) {
|
|
435
|
+
const { fields } = attributeSet;
|
|
436
|
+
for (const field of fields) {
|
|
437
|
+
if (field.filter !== undefined) {
|
|
438
|
+
const condition = convertFilterToSQLCondition(`narrative.rosetta_stone.${field.field}`, field.filter);
|
|
439
|
+
if (condition != null) {
|
|
440
|
+
whereConditions.push(condition);
|
|
441
|
+
}
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
}
|
|
445
|
+
}
|
|
446
|
+
if (whereConditions.length === 0) {
|
|
447
|
+
return "";
|
|
448
|
+
}
|
|
449
|
+
return `WHERE ${whereConditions.join(" AND ")}`;
|
|
450
|
+
}
|