@vaultgradient/pipequery-lang 1.12.2 → 1.13.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +75 -3
- package/dist/builder/index.cjs +926 -0
- package/dist/builder/index.d.cts +458 -0
- package/dist/builder/index.d.ts +458 -0
- package/dist/builder/index.js +926 -0
- package/dist/chunk-KNVFXND5.js +2384 -0
- package/dist/chunk-LWM2ZXGD.cjs +2384 -0
- package/dist/engine/index.cjs +12 -2084
- package/dist/engine/index.d.cts +16 -140
- package/dist/engine/index.d.ts +16 -140
- package/dist/engine/index.js +19 -2091
- package/dist/react/index.cjs +18 -2
- package/dist/react/index.js +17 -1
- package/dist/types-CukH_v-n.d.cts +141 -0
- package/dist/types-CukH_v-n.d.ts +141 -0
- package/package.json +11 -1
|
@@ -0,0 +1,926 @@
|
|
|
1
|
+
"use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _nullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } } function _optionalChain(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; }
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
var _chunkLWM2ZXGDcjs = require('../chunk-LWM2ZXGD.cjs');
|
|
11
|
+
|
|
12
|
+
// src/builder/raw.ts
|
|
13
|
+
var BuilderError = class extends Error {
|
|
14
|
+
constructor(message) {
|
|
15
|
+
super(message);
|
|
16
|
+
this.name = "BuilderError";
|
|
17
|
+
}
|
|
18
|
+
};
|
|
19
|
+
var WRAPPER = "_data | ";
|
|
20
|
+
function relabel(fn2) {
|
|
21
|
+
try {
|
|
22
|
+
return fn2();
|
|
23
|
+
} catch (e) {
|
|
24
|
+
if (e instanceof Error && e.name === "Error" && e.message.startsWith("printQuery: ")) {
|
|
25
|
+
throw new BuilderError(e.message.slice("printQuery: ".length));
|
|
26
|
+
}
|
|
27
|
+
throw e;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
function checkIdentifier(name, what) {
|
|
31
|
+
return relabel(() => _chunkLWM2ZXGDcjs.assertIdentifier.call(void 0, name, what));
|
|
32
|
+
}
|
|
33
|
+
function validateOperation(op) {
|
|
34
|
+
relabel(() => _chunkLWM2ZXGDcjs.printQuery.call(void 0, { kind: "Pipeline", source: "_data", operations: [op] }));
|
|
35
|
+
}
|
|
36
|
+
function validateSource(name) {
|
|
37
|
+
return checkIdentifier(name, "source name");
|
|
38
|
+
}
|
|
39
|
+
function parseOperations(raw, wrapperLength = 0, label, internalName) {
|
|
40
|
+
if (typeof raw !== "string") {
|
|
41
|
+
throw new BuilderError(`${label ? `${label}: ` : ""}expected a pipequery string but got ${typeof raw}`);
|
|
42
|
+
}
|
|
43
|
+
const shift = WRAPPER.length + wrapperLength;
|
|
44
|
+
try {
|
|
45
|
+
return _chunkLWM2ZXGDcjs.parseQuery.call(void 0, WRAPPER + raw).operations;
|
|
46
|
+
} catch (e) {
|
|
47
|
+
if (e instanceof _chunkLWM2ZXGDcjs.DataWeaveError) {
|
|
48
|
+
if (e.line === 1 && e.position >= shift) {
|
|
49
|
+
e.position -= shift;
|
|
50
|
+
e.column -= shift;
|
|
51
|
+
}
|
|
52
|
+
if (label) {
|
|
53
|
+
let message = e.message;
|
|
54
|
+
if (internalName) {
|
|
55
|
+
const method = _optionalChain([/^(\w+)\(\)/, 'access', _ => _.exec, 'call', _2 => _2(label), 'optionalAccess', _3 => _3[1]]);
|
|
56
|
+
message = method ? message.replace(new RegExp(`\\b${internalName}\\b`, "g"), method) : message.replace(new RegExp(`\\s*\\b${internalName} arguments`, "g"), " the expression");
|
|
57
|
+
}
|
|
58
|
+
e.message = `${label}: ${message}`;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
throw e;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
function parseSingleOp(name, raw, kind, prefix = "", label = `${name}()`) {
|
|
65
|
+
const ops = parseOperations(`${name}(${prefix}${raw})`, name.length + 1 + prefix.length, label, name);
|
|
66
|
+
if (ops.length !== 1 || ops[0].kind !== kind) {
|
|
67
|
+
throw new (0, _chunkLWM2ZXGDcjs.ParseError)(
|
|
68
|
+
`${label}: invalid argument ${JSON.stringify(raw)}: expected a single argument list`,
|
|
69
|
+
0,
|
|
70
|
+
1,
|
|
71
|
+
1
|
|
72
|
+
);
|
|
73
|
+
}
|
|
74
|
+
return ops[0];
|
|
75
|
+
}
|
|
76
|
+
function parseExprList(raw, label = "select()") {
|
|
77
|
+
return parseSingleOp("select", raw, "SelectOp", "", label).fields;
|
|
78
|
+
}
|
|
79
|
+
function parseOrExprList(raw, label = "groupBy()") {
|
|
80
|
+
return parseSingleOp("groupBy", raw, "GroupByOp", "", label).keys;
|
|
81
|
+
}
|
|
82
|
+
function parseSortCriteria(raw, label = "sort()") {
|
|
83
|
+
return parseSingleOp("sort", raw, "SortOp", "", label).criteria;
|
|
84
|
+
}
|
|
85
|
+
function parseFieldExpr(raw, label = "field") {
|
|
86
|
+
const keys = parseOrExprList(raw, label);
|
|
87
|
+
if (keys.length !== 1) {
|
|
88
|
+
throw new (0, _chunkLWM2ZXGDcjs.ParseError)(
|
|
89
|
+
`${label}: expected exactly one expression but got ${keys.length} in ${JSON.stringify(raw)}`,
|
|
90
|
+
0,
|
|
91
|
+
1,
|
|
92
|
+
1
|
|
93
|
+
);
|
|
94
|
+
}
|
|
95
|
+
return keys[0];
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// src/builder/expr.ts
|
|
99
|
+
var NODE = /* @__PURE__ */ Symbol.for("pipequery.builder.node");
|
|
100
|
+
var SortKey = class {
|
|
101
|
+
constructor(expression, direction) {
|
|
102
|
+
this.expression = expression;
|
|
103
|
+
this.direction = direction;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
};
|
|
108
|
+
function toAst(value, what = "operand") {
|
|
109
|
+
if (value instanceof Expr) return value[NODE];
|
|
110
|
+
switch (typeof value) {
|
|
111
|
+
case "string":
|
|
112
|
+
return { kind: "StringLiteral", value };
|
|
113
|
+
case "boolean":
|
|
114
|
+
return { kind: "BooleanLiteral", value };
|
|
115
|
+
case "number":
|
|
116
|
+
if (!Number.isFinite(value)) {
|
|
117
|
+
throw new BuilderError(`invalid ${what}: ${String(value)} is not a finite number`);
|
|
118
|
+
}
|
|
119
|
+
if (value < 0 || Object.is(value, -0)) {
|
|
120
|
+
return {
|
|
121
|
+
kind: "UnaryExpr",
|
|
122
|
+
operator: "-",
|
|
123
|
+
operand: { kind: "NumberLiteral", value: -value }
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
return { kind: "NumberLiteral", value };
|
|
127
|
+
}
|
|
128
|
+
if (value === null) return { kind: "NullLiteral" };
|
|
129
|
+
if (value instanceof SortKey) {
|
|
130
|
+
throw new BuilderError(`invalid ${what}: asc()/desc() keys are only valid in sort()`);
|
|
131
|
+
}
|
|
132
|
+
throw new BuilderError(
|
|
133
|
+
`invalid ${what}: expected an expression, string, number, boolean or null but got ${value === void 0 ? "undefined" : typeof value}`
|
|
134
|
+
);
|
|
135
|
+
}
|
|
136
|
+
function toFieldAst(value, what) {
|
|
137
|
+
if (typeof value === "string") return parseFieldExpr(value, what);
|
|
138
|
+
return toAst(value, what);
|
|
139
|
+
}
|
|
140
|
+
function fieldPath(path) {
|
|
141
|
+
if (typeof path !== "string" || path === "") {
|
|
142
|
+
throw new BuilderError("invalid field path: expected a non-empty string");
|
|
143
|
+
}
|
|
144
|
+
return path.split(".").map((seg) => checkIdentifier(seg, "field name"));
|
|
145
|
+
}
|
|
146
|
+
function bin(operator, left, right) {
|
|
147
|
+
return new Expr({ kind: "BinaryExpr", operator, left: toAst(left), right: toAst(right) });
|
|
148
|
+
}
|
|
149
|
+
var ARITY = {
|
|
150
|
+
if: [2, 3],
|
|
151
|
+
coalesce: [1, Infinity],
|
|
152
|
+
lower: [1, 1],
|
|
153
|
+
upper: [1, 1],
|
|
154
|
+
len: [1, 1],
|
|
155
|
+
abs: [1, 1],
|
|
156
|
+
round: [1, 2],
|
|
157
|
+
concat: [1, Infinity],
|
|
158
|
+
contains: [2, 2],
|
|
159
|
+
startsWith: [2, 2],
|
|
160
|
+
endsWith: [2, 2],
|
|
161
|
+
trim: [1, 1],
|
|
162
|
+
substring: [2, 3],
|
|
163
|
+
replace: [3, 3]
|
|
164
|
+
};
|
|
165
|
+
function scalarCall(name, args) {
|
|
166
|
+
const [min, max] = ARITY[name];
|
|
167
|
+
if (args.length < min || args.length > max) {
|
|
168
|
+
const expected = min === max ? `${min}` : max === Infinity ? `at least ${min}` : `${min} or ${max}`;
|
|
169
|
+
throw new BuilderError(`${name}() expects ${expected} argument(s), got ${args.length}`);
|
|
170
|
+
}
|
|
171
|
+
return new Expr({
|
|
172
|
+
kind: "FunctionCall",
|
|
173
|
+
name,
|
|
174
|
+
args: args.map((a, i) => toAst(a, `argument ${i + 1} of ${name}()`))
|
|
175
|
+
});
|
|
176
|
+
}
|
|
177
|
+
function trimArgs(args) {
|
|
178
|
+
let n = args.length;
|
|
179
|
+
while (n > 0 && args[n - 1] === void 0) n--;
|
|
180
|
+
return args.slice(0, n);
|
|
181
|
+
}
|
|
182
|
+
function isBooleanNode(node) {
|
|
183
|
+
switch (node.kind) {
|
|
184
|
+
case "BooleanLiteral":
|
|
185
|
+
return true;
|
|
186
|
+
case "UnaryExpr":
|
|
187
|
+
return node.operator === "!";
|
|
188
|
+
case "BinaryExpr":
|
|
189
|
+
if (["==", "!=", "<", "<=", ">", ">="].includes(node.operator)) return true;
|
|
190
|
+
return (node.operator === "&&" || node.operator === "||") && isBooleanNode(node.left) && isBooleanNode(node.right);
|
|
191
|
+
case "FunctionCall":
|
|
192
|
+
if (["contains", "startsWith", "endsWith"].includes(node.name)) return true;
|
|
193
|
+
return node.name === "if" && node.args.length === 3 && isBooleanNode(node.args[0]) && isBooleanNode(node.args[1]) && isBooleanNode(node.args[2]);
|
|
194
|
+
default:
|
|
195
|
+
return false;
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
function isOrNode(n) {
|
|
199
|
+
return n.kind === "FunctionCall" && n.name === "if" && n.args.length === 3 && n.args[0] === n.args[1];
|
|
200
|
+
}
|
|
201
|
+
function orNode(left, right) {
|
|
202
|
+
if (isBooleanNode(left)) {
|
|
203
|
+
return { kind: "FunctionCall", name: "if", args: [left, { kind: "BooleanLiteral", value: true }, right] };
|
|
204
|
+
}
|
|
205
|
+
if (isOrNode(left)) {
|
|
206
|
+
const [cond, , rest] = left.args;
|
|
207
|
+
return { kind: "FunctionCall", name: "if", args: [cond, cond, orNode(rest, right)] };
|
|
208
|
+
}
|
|
209
|
+
return { kind: "FunctionCall", name: "if", args: [left, left, right] };
|
|
210
|
+
}
|
|
211
|
+
var Expr = class _Expr {
|
|
212
|
+
|
|
213
|
+
constructor(node) {
|
|
214
|
+
this[NODE] = node;
|
|
215
|
+
}
|
|
216
|
+
// comparison
|
|
217
|
+
gt(other) {
|
|
218
|
+
return bin(">", this, other);
|
|
219
|
+
}
|
|
220
|
+
gte(other) {
|
|
221
|
+
return bin(">=", this, other);
|
|
222
|
+
}
|
|
223
|
+
lt(other) {
|
|
224
|
+
return bin("<", this, other);
|
|
225
|
+
}
|
|
226
|
+
lte(other) {
|
|
227
|
+
return bin("<=", this, other);
|
|
228
|
+
}
|
|
229
|
+
eq(other) {
|
|
230
|
+
return bin("==", this, other);
|
|
231
|
+
}
|
|
232
|
+
ne(other) {
|
|
233
|
+
return bin("!=", this, other);
|
|
234
|
+
}
|
|
235
|
+
// logical
|
|
236
|
+
and(other) {
|
|
237
|
+
return bin("&&", this, other);
|
|
238
|
+
}
|
|
239
|
+
/**
|
|
240
|
+
* Logical or. The AST uses `if(a, true, b)` for proven boolean `a`, and
|
|
241
|
+
* `if(a, a, b)` otherwise: the lexer cannot parse `||` (it reads two PIPE tokens).
|
|
242
|
+
* Both forms have the engine's `a || b` value semantics and round-trip through
|
|
243
|
+
* `parseQuery`. In the latter, `a` is evaluated twice; expressions are pure.
|
|
244
|
+
*
|
|
245
|
+
* Unknown-valued chains stay linear by associating right: `a.or(b).or(c)`
|
|
246
|
+
* becomes `if(a, a, if(b, b, c))`.
|
|
247
|
+
*/
|
|
248
|
+
or(other) {
|
|
249
|
+
return new _Expr(orNode(this[NODE], toAst(other)));
|
|
250
|
+
}
|
|
251
|
+
not() {
|
|
252
|
+
return new _Expr({ kind: "UnaryExpr", operator: "!", operand: this[NODE] });
|
|
253
|
+
}
|
|
254
|
+
// arithmetic
|
|
255
|
+
plus(other) {
|
|
256
|
+
return bin("+", this, other);
|
|
257
|
+
}
|
|
258
|
+
minus(other) {
|
|
259
|
+
return bin("-", this, other);
|
|
260
|
+
}
|
|
261
|
+
times(other) {
|
|
262
|
+
return bin("*", this, other);
|
|
263
|
+
}
|
|
264
|
+
div(other) {
|
|
265
|
+
return bin("/", this, other);
|
|
266
|
+
}
|
|
267
|
+
mod(other) {
|
|
268
|
+
return bin("%", this, other);
|
|
269
|
+
}
|
|
270
|
+
neg() {
|
|
271
|
+
return new _Expr({ kind: "UnaryExpr", operator: "-", operand: this[NODE] });
|
|
272
|
+
}
|
|
273
|
+
// scalar functions
|
|
274
|
+
lower() {
|
|
275
|
+
return scalarCall("lower", [this]);
|
|
276
|
+
}
|
|
277
|
+
upper() {
|
|
278
|
+
return scalarCall("upper", [this]);
|
|
279
|
+
}
|
|
280
|
+
len() {
|
|
281
|
+
return scalarCall("len", [this]);
|
|
282
|
+
}
|
|
283
|
+
abs() {
|
|
284
|
+
return scalarCall("abs", [this]);
|
|
285
|
+
}
|
|
286
|
+
round(precision) {
|
|
287
|
+
return scalarCall("round", trimArgs([this, precision]));
|
|
288
|
+
}
|
|
289
|
+
trim() {
|
|
290
|
+
return scalarCall("trim", [this]);
|
|
291
|
+
}
|
|
292
|
+
concat(...rest) {
|
|
293
|
+
return scalarCall("concat", [this, ...rest]);
|
|
294
|
+
}
|
|
295
|
+
coalesce(...rest) {
|
|
296
|
+
return scalarCall("coalesce", [this, ...rest]);
|
|
297
|
+
}
|
|
298
|
+
contains(search) {
|
|
299
|
+
return scalarCall("contains", [this, search]);
|
|
300
|
+
}
|
|
301
|
+
startsWith(prefix) {
|
|
302
|
+
return scalarCall("startsWith", [this, prefix]);
|
|
303
|
+
}
|
|
304
|
+
endsWith(suffix) {
|
|
305
|
+
return scalarCall("endsWith", [this, suffix]);
|
|
306
|
+
}
|
|
307
|
+
substring(start, length) {
|
|
308
|
+
return scalarCall("substring", trimArgs([this, start, length]));
|
|
309
|
+
}
|
|
310
|
+
replace(search, replacement) {
|
|
311
|
+
return scalarCall("replace", [this, search, replacement]);
|
|
312
|
+
}
|
|
313
|
+
// naming and ordering
|
|
314
|
+
/** `expr as name`. Re-aliasing replaces the previous alias. */
|
|
315
|
+
as(name) {
|
|
316
|
+
checkIdentifier(name, "alias");
|
|
317
|
+
const node = this[NODE];
|
|
318
|
+
return new _Expr({
|
|
319
|
+
kind: "AliasExpr",
|
|
320
|
+
expression: node.kind === "AliasExpr" ? node.expression : node,
|
|
321
|
+
alias: name
|
|
322
|
+
});
|
|
323
|
+
}
|
|
324
|
+
asc() {
|
|
325
|
+
return new SortKey(this[NODE], "asc");
|
|
326
|
+
}
|
|
327
|
+
desc() {
|
|
328
|
+
return new SortKey(this[NODE], "desc");
|
|
329
|
+
}
|
|
330
|
+
/** Extend a field path with names that collide with `Expr` methods: `o.customer.$('len')`. */
|
|
331
|
+
$(...segments) {
|
|
332
|
+
const node = this[NODE];
|
|
333
|
+
if (node.kind !== "FieldAccess") {
|
|
334
|
+
throw new BuilderError("$() can only extend a field access");
|
|
335
|
+
}
|
|
336
|
+
return fieldProxy([...node.path, ...segments.map((s) => checkIdentifier(s, "field name"))]);
|
|
337
|
+
}
|
|
338
|
+
/** The pipequery text of this expression. */
|
|
339
|
+
toString() {
|
|
340
|
+
const text = _chunkLWM2ZXGDcjs.printQuery.call(void 0, {
|
|
341
|
+
kind: "Pipeline",
|
|
342
|
+
source: "_data",
|
|
343
|
+
operations: [{ kind: "SelectOp", fields: [this[NODE]] }]
|
|
344
|
+
});
|
|
345
|
+
const prefix = "_data | select(";
|
|
346
|
+
return text.slice(prefix.length, -1);
|
|
347
|
+
}
|
|
348
|
+
};
|
|
349
|
+
function fieldProxy(path) {
|
|
350
|
+
const target = new Expr({ kind: "FieldAccess", path });
|
|
351
|
+
return new Proxy(target, {
|
|
352
|
+
get(t, prop, receiver) {
|
|
353
|
+
if (typeof prop === "symbol" || prop in t) return Reflect.get(t, prop, receiver);
|
|
354
|
+
return fieldProxy([...path, prop]);
|
|
355
|
+
}
|
|
356
|
+
});
|
|
357
|
+
}
|
|
358
|
+
function rowProxy() {
|
|
359
|
+
return new Proxy({}, {
|
|
360
|
+
get(_t, prop) {
|
|
361
|
+
return typeof prop === "symbol" ? void 0 : fieldProxy([prop]);
|
|
362
|
+
}
|
|
363
|
+
});
|
|
364
|
+
}
|
|
365
|
+
function col(...path) {
|
|
366
|
+
if (path.length === 0) throw new BuilderError("col() requires a field name");
|
|
367
|
+
return fieldProxy(path.flatMap(fieldPath));
|
|
368
|
+
}
|
|
369
|
+
function lit(value) {
|
|
370
|
+
return new Expr(toAst(value, "literal"));
|
|
371
|
+
}
|
|
372
|
+
function aggregateArgs(name, args) {
|
|
373
|
+
if (!_chunkLWM2ZXGDcjs.AGGREGATE_NAMES.has(name)) throw new BuilderError(`unknown aggregate "${name}"`);
|
|
374
|
+
const defined = trimArgs(args);
|
|
375
|
+
if (defined.length !== args.length) {
|
|
376
|
+
throw new BuilderError(`${name}(): undefined argument`);
|
|
377
|
+
}
|
|
378
|
+
if (_chunkLWM2ZXGDcjs.TWO_ARG_AGGREGATES.has(name)) {
|
|
379
|
+
if (args.length !== 2) throw new BuilderError(`${name}() expects exactly 2 arguments, got ${args.length}`);
|
|
380
|
+
} else if (name === "count") {
|
|
381
|
+
if (args.length > 1) throw new BuilderError(`count() expects at most 1 argument, got ${args.length}`);
|
|
382
|
+
} else if (args.length !== 1) {
|
|
383
|
+
throw new BuilderError(`${name}() expects exactly 1 field argument, got ${args.length}`);
|
|
384
|
+
}
|
|
385
|
+
return args.map((a, i) => toFieldAst(a, `argument ${i + 1} of ${name}()`));
|
|
386
|
+
}
|
|
387
|
+
function makeAggregates() {
|
|
388
|
+
const helpers = {};
|
|
389
|
+
for (const name of _chunkLWM2ZXGDcjs.AGGREGATE_NAMES) {
|
|
390
|
+
helpers[name] = (...args) => new Expr({ kind: "FunctionCall", name, args: aggregateArgs(name, args) });
|
|
391
|
+
}
|
|
392
|
+
return helpers;
|
|
393
|
+
}
|
|
394
|
+
var agg = makeAggregates();
|
|
395
|
+
var fnImpl = {
|
|
396
|
+
if: (cond, then, otherwise) => scalarCall("if", trimArgs([cond, then, otherwise])),
|
|
397
|
+
coalesce: (...args) => scalarCall("coalesce", args),
|
|
398
|
+
lower: (value) => scalarCall("lower", [value]),
|
|
399
|
+
upper: (value) => scalarCall("upper", [value]),
|
|
400
|
+
len: (value) => scalarCall("len", [value]),
|
|
401
|
+
abs: (value) => scalarCall("abs", [value]),
|
|
402
|
+
round: (value, precision) => scalarCall("round", trimArgs([value, precision])),
|
|
403
|
+
concat: (...args) => scalarCall("concat", args),
|
|
404
|
+
contains: (value, search) => scalarCall("contains", [value, search]),
|
|
405
|
+
startsWith: (value, prefix) => scalarCall("startsWith", [value, prefix]),
|
|
406
|
+
endsWith: (value, suffix) => scalarCall("endsWith", [value, suffix]),
|
|
407
|
+
trim: (value) => scalarCall("trim", [value]),
|
|
408
|
+
substring: (value, start, length) => scalarCall("substring", trimArgs([value, start, length])),
|
|
409
|
+
replace: (value, search, replacement) => scalarCall("replace", [value, search, replacement])
|
|
410
|
+
};
|
|
411
|
+
var fn = fnImpl;
|
|
412
|
+
function windowCall(name, field, offset) {
|
|
413
|
+
const args = [];
|
|
414
|
+
if (field !== void 0) args.push(toFieldAst(field, `field of ${name}()`));
|
|
415
|
+
if (offset !== void 0) {
|
|
416
|
+
if (!Number.isInteger(offset) || offset < 1) {
|
|
417
|
+
throw new BuilderError(`${name}(): offset must be an integer >= 1, got ${String(offset)}`);
|
|
418
|
+
}
|
|
419
|
+
args.push({ kind: "NumberLiteral", value: offset });
|
|
420
|
+
}
|
|
421
|
+
return new Expr({ kind: "FunctionCall", name, args });
|
|
422
|
+
}
|
|
423
|
+
var winImpl = {
|
|
424
|
+
running_sum: (field) => windowCall("running_sum", field),
|
|
425
|
+
running_avg: (field) => windowCall("running_avg", field),
|
|
426
|
+
running_min: (field) => windowCall("running_min", field),
|
|
427
|
+
running_max: (field) => windowCall("running_max", field),
|
|
428
|
+
running_count: (field) => windowCall("running_count", field),
|
|
429
|
+
row_number: () => windowCall("row_number", void 0),
|
|
430
|
+
lag: (field, offset) => windowCall("lag", field, offset),
|
|
431
|
+
lead: (field, offset) => windowCall("lead", field, offset)
|
|
432
|
+
};
|
|
433
|
+
var win = winImpl;
|
|
434
|
+
var WINDOW_NAMES = /* @__PURE__ */ new Set([
|
|
435
|
+
"running_sum",
|
|
436
|
+
"running_avg",
|
|
437
|
+
"running_count",
|
|
438
|
+
"running_min",
|
|
439
|
+
"running_max",
|
|
440
|
+
"row_number",
|
|
441
|
+
"lag",
|
|
442
|
+
"lead"
|
|
443
|
+
]);
|
|
444
|
+
function unalias(expr) {
|
|
445
|
+
return expr.kind === "AliasExpr" ? expr.expression : expr;
|
|
446
|
+
}
|
|
447
|
+
function findCall(expr, pred) {
|
|
448
|
+
switch (expr.kind) {
|
|
449
|
+
case "FunctionCall": {
|
|
450
|
+
if (pred(expr.name)) return expr.name;
|
|
451
|
+
for (const a of expr.args) {
|
|
452
|
+
const hit = findCall(a, pred);
|
|
453
|
+
if (hit) return hit;
|
|
454
|
+
}
|
|
455
|
+
return void 0;
|
|
456
|
+
}
|
|
457
|
+
case "BinaryExpr":
|
|
458
|
+
return _nullishCoalesce(findCall(expr.left, pred), () => ( findCall(expr.right, pred)));
|
|
459
|
+
case "UnaryExpr":
|
|
460
|
+
return findCall(expr.operand, pred);
|
|
461
|
+
case "AliasExpr":
|
|
462
|
+
return findCall(expr.expression, pred);
|
|
463
|
+
default:
|
|
464
|
+
return void 0;
|
|
465
|
+
}
|
|
466
|
+
}
|
|
467
|
+
function hasFieldAccess(expr) {
|
|
468
|
+
switch (expr.kind) {
|
|
469
|
+
case "FieldAccess":
|
|
470
|
+
return true;
|
|
471
|
+
case "FunctionCall":
|
|
472
|
+
return expr.args.some(hasFieldAccess);
|
|
473
|
+
case "BinaryExpr":
|
|
474
|
+
return hasFieldAccess(expr.left) || hasFieldAccess(expr.right);
|
|
475
|
+
case "UnaryExpr":
|
|
476
|
+
return hasFieldAccess(expr.operand);
|
|
477
|
+
case "AliasExpr":
|
|
478
|
+
return hasFieldAccess(expr.expression);
|
|
479
|
+
default:
|
|
480
|
+
return false;
|
|
481
|
+
}
|
|
482
|
+
}
|
|
483
|
+
function isAggregateCall(expr) {
|
|
484
|
+
const e = unalias(expr);
|
|
485
|
+
return e.kind === "FunctionCall" && _chunkLWM2ZXGDcjs.AGGREGATE_NAMES.has(e.name);
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
// src/builder/query.ts
|
|
489
|
+
function collect(args, proxies = () => [rowProxy()]) {
|
|
490
|
+
const out = [];
|
|
491
|
+
for (const arg of args) {
|
|
492
|
+
const fromCallback = typeof arg === "function";
|
|
493
|
+
const value = fromCallback ? arg(...proxies()) : arg;
|
|
494
|
+
const items = Array.isArray(value) ? value : [value];
|
|
495
|
+
for (const item of items) {
|
|
496
|
+
if (fromCallback && typeof item === "string") {
|
|
497
|
+
throw new BuilderError(
|
|
498
|
+
"callbacks must return expressions, not strings (a string is a raw pipequery fragment only when passed directly)"
|
|
499
|
+
);
|
|
500
|
+
}
|
|
501
|
+
out.push(item);
|
|
502
|
+
}
|
|
503
|
+
}
|
|
504
|
+
return out;
|
|
505
|
+
}
|
|
506
|
+
function isPlainObject(value) {
|
|
507
|
+
if (typeof value !== "object" || value === null) return false;
|
|
508
|
+
const proto = Object.getPrototypeOf(value);
|
|
509
|
+
return proto === Object.prototype || proto === null;
|
|
510
|
+
}
|
|
511
|
+
function aliasEntries(obj) {
|
|
512
|
+
return Object.entries(obj).map(([name, value]) => {
|
|
513
|
+
checkIdentifier(name, "alias");
|
|
514
|
+
const node = unalias(toAst(value, `value of "${name}"`));
|
|
515
|
+
if (node.kind === "FieldAccess" && node.path[node.path.length - 1] === name) return node;
|
|
516
|
+
return { kind: "AliasExpr", expression: node, alias: name };
|
|
517
|
+
});
|
|
518
|
+
}
|
|
519
|
+
function toExprs(items, parseRaw, allowObjects = false) {
|
|
520
|
+
const out = [];
|
|
521
|
+
for (const item of items) {
|
|
522
|
+
if (typeof item === "string") {
|
|
523
|
+
const parsed = parseRaw(item);
|
|
524
|
+
if (parsed.length === 0) throw new BuilderError("empty expression string");
|
|
525
|
+
out.push(...parsed);
|
|
526
|
+
} else if (allowObjects && isPlainObject(item)) {
|
|
527
|
+
out.push(...aliasEntries(item));
|
|
528
|
+
} else {
|
|
529
|
+
out.push(toAst(item, "expression"));
|
|
530
|
+
}
|
|
531
|
+
}
|
|
532
|
+
return out;
|
|
533
|
+
}
|
|
534
|
+
var keysParser = (label) => (raw) => parseOrExprList(raw, label);
|
|
535
|
+
var exprsParser = (label) => (raw) => parseExprList(raw, label);
|
|
536
|
+
function one(arg, parseRaw, what) {
|
|
537
|
+
const exprs = toExprs(collect([arg]), parseRaw);
|
|
538
|
+
if (exprs.length !== 1) {
|
|
539
|
+
throw new BuilderError(`${what} expects exactly one expression, got ${exprs.length}`);
|
|
540
|
+
}
|
|
541
|
+
return exprs[0];
|
|
542
|
+
}
|
|
543
|
+
function toCriteria(items) {
|
|
544
|
+
const out = [];
|
|
545
|
+
for (const item of items) {
|
|
546
|
+
if (typeof item === "string") {
|
|
547
|
+
out.push(...parseSortCriteria(item));
|
|
548
|
+
} else if (item instanceof SortKey) {
|
|
549
|
+
out.push({ expression: item.expression, direction: item.direction });
|
|
550
|
+
} else {
|
|
551
|
+
out.push({ expression: toAst(item, "sort key"), direction: "asc" });
|
|
552
|
+
}
|
|
553
|
+
}
|
|
554
|
+
return out;
|
|
555
|
+
}
|
|
556
|
+
function nonNegativeInteger(value, what) {
|
|
557
|
+
if (typeof value !== "number" || !Number.isInteger(value) || value < 0) {
|
|
558
|
+
throw new BuilderError(`${what} must be a non-negative integer, got ${String(value)}`);
|
|
559
|
+
}
|
|
560
|
+
return value;
|
|
561
|
+
}
|
|
562
|
+
function fieldRef(value, what) {
|
|
563
|
+
if (typeof value === "string") return { kind: "FieldAccess", path: fieldPath(value) };
|
|
564
|
+
const node = toAst(value, what);
|
|
565
|
+
if (node.kind !== "FieldAccess") {
|
|
566
|
+
throw new BuilderError(`${what} must be a field reference`);
|
|
567
|
+
}
|
|
568
|
+
return node;
|
|
569
|
+
}
|
|
570
|
+
function producesGrouped(op, wasGrouped) {
|
|
571
|
+
if (op.kind === "GroupByOp") return true;
|
|
572
|
+
if (op.kind === "MapOp") return wasGrouped;
|
|
573
|
+
return false;
|
|
574
|
+
}
|
|
575
|
+
function noAggregate(exprs, where) {
|
|
576
|
+
for (const e of exprs) {
|
|
577
|
+
const hit = findCall(e, (n) => _chunkLWM2ZXGDcjs.AGGREGATE_NAMES.has(n));
|
|
578
|
+
if (hit) {
|
|
579
|
+
throw new BuilderError(
|
|
580
|
+
`${where}: aggregate ${hit}() is not allowed here (aggregates belong in rollup(), pivot(), select()/map() after groupBy(), or a terminal aggregate)`
|
|
581
|
+
);
|
|
582
|
+
}
|
|
583
|
+
}
|
|
584
|
+
}
|
|
585
|
+
function windowOffsets(expr) {
|
|
586
|
+
const e = unalias(expr);
|
|
587
|
+
if (e.kind !== "FunctionCall" || e.name !== "lag" && e.name !== "lead" || e.args.length < 2) return;
|
|
588
|
+
const offset = e.args[1];
|
|
589
|
+
if (offset.kind !== "NumberLiteral" || !Number.isInteger(offset.value) || offset.value < 1) {
|
|
590
|
+
throw new BuilderError(`${e.name}(): offset must be an integer >= 1`);
|
|
591
|
+
}
|
|
592
|
+
}
|
|
593
|
+
function noWindow(exprs, where) {
|
|
594
|
+
for (const e of exprs) {
|
|
595
|
+
const hit = findCall(e, (n) => WINDOW_NAMES.has(n));
|
|
596
|
+
if (hit) {
|
|
597
|
+
throw new BuilderError(`${where}: window function ${hit}() is only supported as a top-level field of select()`);
|
|
598
|
+
}
|
|
599
|
+
}
|
|
600
|
+
}
|
|
601
|
+
function validateContext(op, grouped) {
|
|
602
|
+
switch (op.kind) {
|
|
603
|
+
case "WhereOp":
|
|
604
|
+
noAggregate([op.condition], "where");
|
|
605
|
+
noWindow([op.condition], "where");
|
|
606
|
+
break;
|
|
607
|
+
case "SortOp": {
|
|
608
|
+
const exprs = op.criteria.map((c) => c.expression);
|
|
609
|
+
noAggregate(exprs, "sort");
|
|
610
|
+
noWindow(exprs, "sort");
|
|
611
|
+
break;
|
|
612
|
+
}
|
|
613
|
+
case "GroupByOp":
|
|
614
|
+
noAggregate(op.keys, "groupBy");
|
|
615
|
+
noWindow(op.keys, "groupBy");
|
|
616
|
+
break;
|
|
617
|
+
case "JoinOp":
|
|
618
|
+
noAggregate([op.condition], "join");
|
|
619
|
+
noWindow([op.condition], "join");
|
|
620
|
+
break;
|
|
621
|
+
case "FirstOp":
|
|
622
|
+
nonNegativeInteger(op.count, "first() count");
|
|
623
|
+
break;
|
|
624
|
+
case "LastOp":
|
|
625
|
+
nonNegativeInteger(op.count, "last() count");
|
|
626
|
+
break;
|
|
627
|
+
case "DistinctOp":
|
|
628
|
+
noAggregate(_nullishCoalesce(op.fields, () => ( [])), "distinct");
|
|
629
|
+
noWindow(_nullishCoalesce(op.fields, () => ( [])), "distinct");
|
|
630
|
+
break;
|
|
631
|
+
case "FlattenOp":
|
|
632
|
+
noAggregate(op.field ? [op.field] : [], "flatten");
|
|
633
|
+
noWindow(op.field ? [op.field] : [], "flatten");
|
|
634
|
+
break;
|
|
635
|
+
case "TransposeOp":
|
|
636
|
+
noAggregate(op.headerField ? [op.headerField] : [], "transpose");
|
|
637
|
+
noWindow(op.headerField ? [op.headerField] : [], "transpose");
|
|
638
|
+
break;
|
|
639
|
+
case "SelectOp":
|
|
640
|
+
case "MapOp": {
|
|
641
|
+
const label = op.kind === "SelectOp" ? "select" : "map";
|
|
642
|
+
if (!grouped) noAggregate(op.fields, `${label} (outside groupBy)`);
|
|
643
|
+
for (const f of op.fields) windowOffsets(f);
|
|
644
|
+
if (op.kind === "MapOp") {
|
|
645
|
+
noWindow(op.fields, "map");
|
|
646
|
+
} else {
|
|
647
|
+
for (const f of op.fields) {
|
|
648
|
+
const e = unalias(f);
|
|
649
|
+
noWindow(e.kind === "FunctionCall" && WINDOW_NAMES.has(e.name) ? e.args : [f], "select");
|
|
650
|
+
}
|
|
651
|
+
}
|
|
652
|
+
break;
|
|
653
|
+
}
|
|
654
|
+
case "RollupOp":
|
|
655
|
+
noAggregate(op.keys, "rollup keys");
|
|
656
|
+
noWindow([...op.keys, ...op.aggregates], "rollup");
|
|
657
|
+
break;
|
|
658
|
+
case "PivotOp":
|
|
659
|
+
noAggregate([op.pivotField], "pivot field");
|
|
660
|
+
noWindow([op.pivotField, ...op.aggregates], "pivot");
|
|
661
|
+
for (const a of op.aggregates) {
|
|
662
|
+
if (!findCall(a, (n) => _chunkLWM2ZXGDcjs.AGGREGATE_NAMES.has(n))) {
|
|
663
|
+
throw new BuilderError("pivot: every value must be an aggregate expression such as sum(amount)");
|
|
664
|
+
}
|
|
665
|
+
}
|
|
666
|
+
break;
|
|
667
|
+
case "ReduceOp":
|
|
668
|
+
if (hasFieldAccess(op.initial)) {
|
|
669
|
+
throw new BuilderError("reduce: the initial value is evaluated before any row and cannot reference fields");
|
|
670
|
+
}
|
|
671
|
+
noAggregate([op.initial, op.accumulator], "reduce");
|
|
672
|
+
noWindow([op.initial, op.accumulator], "reduce");
|
|
673
|
+
break;
|
|
674
|
+
case "AggregateOp": {
|
|
675
|
+
const exprs = [..._nullishCoalesce(op.args, () => ( [])), ...op.field ? [op.field] : []];
|
|
676
|
+
noAggregate(exprs, op.function);
|
|
677
|
+
noWindow(exprs, op.function);
|
|
678
|
+
break;
|
|
679
|
+
}
|
|
680
|
+
default:
|
|
681
|
+
break;
|
|
682
|
+
}
|
|
683
|
+
}
|
|
684
|
+
var Built = class {
|
|
685
|
+
constructor(sourceName, ops) {
|
|
686
|
+
this.sourceName = sourceName;
|
|
687
|
+
this.ops = ops;
|
|
688
|
+
}
|
|
689
|
+
|
|
690
|
+
|
|
691
|
+
|
|
692
|
+
/** The pipeline AST (a fresh object each time; expression nodes are shared). */
|
|
693
|
+
get ast() {
|
|
694
|
+
return { kind: "Pipeline", source: this.sourceName, operations: [...this.ops] };
|
|
695
|
+
}
|
|
696
|
+
/** Canonical pipequery text, via `printQuery`. */
|
|
697
|
+
toString() {
|
|
698
|
+
return _chunkLWM2ZXGDcjs.printQuery.call(void 0, this.ast);
|
|
699
|
+
}
|
|
700
|
+
/** Compile the AST directly (no print + re-parse). */
|
|
701
|
+
compile() {
|
|
702
|
+
return this.compiled ??= _chunkLWM2ZXGDcjs.compileAst.call(void 0, this.ast);
|
|
703
|
+
}
|
|
704
|
+
execute(context) {
|
|
705
|
+
const ctx = Array.isArray(context) ? { [this.sourceName]: context } : context;
|
|
706
|
+
return this.compile()(ctx);
|
|
707
|
+
}
|
|
708
|
+
};
|
|
709
|
+
var Scalar = class extends Built {
|
|
710
|
+
/** @internal */
|
|
711
|
+
constructor(source, ops) {
|
|
712
|
+
super(source, ops);
|
|
713
|
+
}
|
|
714
|
+
/** Run against `{ source: rows, ... }` or, for plain arrays, the rows of this query's source. */
|
|
715
|
+
run(context) {
|
|
716
|
+
return this.execute(context);
|
|
717
|
+
}
|
|
718
|
+
};
|
|
719
|
+
var Query = class _Query extends Built {
|
|
720
|
+
/** @internal use `from()` */
|
|
721
|
+
constructor(source, ops = []) {
|
|
722
|
+
super(source, ops);
|
|
723
|
+
}
|
|
724
|
+
isGrouped() {
|
|
725
|
+
let grouped = false;
|
|
726
|
+
for (const op of this.ops) grouped = producesGrouped(op, grouped);
|
|
727
|
+
return grouped;
|
|
728
|
+
}
|
|
729
|
+
/** Validate and append operations. */
|
|
730
|
+
extend(added) {
|
|
731
|
+
let grouped = this.isGrouped();
|
|
732
|
+
for (const op of added) {
|
|
733
|
+
validateOperation(op);
|
|
734
|
+
validateContext(op, grouped);
|
|
735
|
+
grouped = producesGrouped(op, grouped);
|
|
736
|
+
}
|
|
737
|
+
return new _Query(this.sourceName, [...this.ops, ...added]);
|
|
738
|
+
}
|
|
739
|
+
terminal(op) {
|
|
740
|
+
validateOperation(op);
|
|
741
|
+
validateContext(op, this.isGrouped());
|
|
742
|
+
return new Scalar(this.sourceName, [...this.ops, op]);
|
|
743
|
+
}
|
|
744
|
+
/** Run against `{ source: rows, ... }` or, for plain arrays, the rows of this query's source. */
|
|
745
|
+
run(context) {
|
|
746
|
+
return this.execute(context);
|
|
747
|
+
}
|
|
748
|
+
// ── pipe ops ──────────────────────────────────────────────────────────
|
|
749
|
+
/** Rows matching the condition. Aggregates and window functions are not allowed here. */
|
|
750
|
+
where(condition) {
|
|
751
|
+
const expr = one(condition, (raw) => [parseSingleOp("where", raw, "WhereOp").condition], "where()");
|
|
752
|
+
return this.extend([{ kind: "WhereOp", condition: expr }]);
|
|
753
|
+
}
|
|
754
|
+
select(...fields) {
|
|
755
|
+
const exprs = toExprs(collect(fields), parseExprList, true);
|
|
756
|
+
if (exprs.length === 0) throw new BuilderError("select() requires at least one field");
|
|
757
|
+
return this.extend([{ kind: "SelectOp", fields: exprs }]);
|
|
758
|
+
}
|
|
759
|
+
/** Sort keys must be row expressions: no aliases, no aggregates. */
|
|
760
|
+
sort(...keys) {
|
|
761
|
+
const criteria = toCriteria(collect(keys));
|
|
762
|
+
if (criteria.length === 0) throw new BuilderError("sort() requires at least one key");
|
|
763
|
+
return this.extend([{ kind: "SortOp", criteria }]);
|
|
764
|
+
}
|
|
765
|
+
/**
|
|
766
|
+
* Group rows. Follow with `select`/`map` (aggregates allowed), `rollup` or `pivot`.
|
|
767
|
+
* A bare `groupBy` yields `{ ...keys, _group }` rows and is rarely what you want to run.
|
|
768
|
+
* No keys groups everything into one group.
|
|
769
|
+
*/
|
|
770
|
+
groupBy(...keys) {
|
|
771
|
+
const exprs = toExprs(collect(keys), parseOrExprList);
|
|
772
|
+
return this.extend([{ kind: "GroupByOp", keys: exprs }]);
|
|
773
|
+
}
|
|
774
|
+
/**
|
|
775
|
+
* Join with another source from the run context. The engine merges rows as
|
|
776
|
+
* `{ ...left, ...right }`, so on a name collision the right value wins.
|
|
777
|
+
* `FieldAccess == FieldAccess` conditions (also `{ on: [left, right] }`) use the hash join.
|
|
778
|
+
* In callbacks both arguments are plain row proxies; for non-equi conditions the
|
|
779
|
+
* condition sees the merged row, so qualify nothing and avoid colliding names.
|
|
780
|
+
*/
|
|
781
|
+
join(right, on) {
|
|
782
|
+
validateSource(right);
|
|
783
|
+
let condition;
|
|
784
|
+
if (typeof on === "string") {
|
|
785
|
+
condition = parseSingleOp("join", on, "JoinOp", `${right}, `).condition;
|
|
786
|
+
} else if (typeof on === "function") {
|
|
787
|
+
const result = on(rowProxy(), rowProxy());
|
|
788
|
+
if (typeof result === "string") {
|
|
789
|
+
throw new BuilderError("join callbacks must return an expression, not a string");
|
|
790
|
+
}
|
|
791
|
+
condition = toAst(result, "join condition");
|
|
792
|
+
} else if (on instanceof Expr) {
|
|
793
|
+
condition = toAst(on, "join condition");
|
|
794
|
+
} else if (isPlainObject(on) && Array.isArray(on.on)) {
|
|
795
|
+
const pair = on.on;
|
|
796
|
+
if (pair.length !== 2) throw new BuilderError("join({ on }) expects [leftField, rightField]");
|
|
797
|
+
condition = {
|
|
798
|
+
kind: "BinaryExpr",
|
|
799
|
+
operator: "==",
|
|
800
|
+
left: fieldRef(pair[0], "left join key"),
|
|
801
|
+
right: fieldRef(pair[1], "right join key")
|
|
802
|
+
};
|
|
803
|
+
} else {
|
|
804
|
+
throw new BuilderError("join() condition must be a string, expression, callback or { on: [left, right] }");
|
|
805
|
+
}
|
|
806
|
+
return this.extend([{ kind: "JoinOp", right, condition }]);
|
|
807
|
+
}
|
|
808
|
+
first(count) {
|
|
809
|
+
return this.extend([{ kind: "FirstOp", count: nonNegativeInteger(count, "first() count") }]);
|
|
810
|
+
}
|
|
811
|
+
/**
|
|
812
|
+
* The last `count` rows. Engine quirk, passed through unchanged: `last(0)` returns
|
|
813
|
+
* every row (the engine does `slice(-0)`), unlike `first(0)` which returns none.
|
|
814
|
+
*/
|
|
815
|
+
last(count) {
|
|
816
|
+
return this.extend([{ kind: "LastOp", count: nonNegativeInteger(count, "last() count") }]);
|
|
817
|
+
}
|
|
818
|
+
/** Distinct rows, or distinct by the given fields. */
|
|
819
|
+
distinct(...fields) {
|
|
820
|
+
const exprs = toExprs(collect(fields), keysParser("distinct()"));
|
|
821
|
+
return this.extend([{ kind: "DistinctOp", fields: exprs.length > 0 ? exprs : void 0 }]);
|
|
822
|
+
}
|
|
823
|
+
/** Flatten nested arrays, or the array held in `field`. */
|
|
824
|
+
flatten(field) {
|
|
825
|
+
const expr = field === void 0 ? void 0 : one(field, keysParser("flatten()"), "flatten()");
|
|
826
|
+
return this.extend([{ kind: "FlattenOp", field: expr }]);
|
|
827
|
+
}
|
|
828
|
+
transpose(headerField) {
|
|
829
|
+
const expr = headerField === void 0 ? void 0 : one(headerField, keysParser("transpose()"), "transpose()");
|
|
830
|
+
return this.extend([{ kind: "TransposeOp", headerField: expr }]);
|
|
831
|
+
}
|
|
832
|
+
/**
|
|
833
|
+
* Group keys first, then at least one aggregate:
|
|
834
|
+
* `rollup('region', agg.sum(o.total).as('total'))`.
|
|
835
|
+
*/
|
|
836
|
+
rollup(...items) {
|
|
837
|
+
const exprs = toExprs(collect(items), exprsParser("rollup()"));
|
|
838
|
+
const keys = [];
|
|
839
|
+
const aggregates = [];
|
|
840
|
+
for (const e of exprs) {
|
|
841
|
+
if (isAggregateCall(e)) {
|
|
842
|
+
aggregates.push(e);
|
|
843
|
+
} else if (aggregates.length > 0) {
|
|
844
|
+
throw new BuilderError("rollup(): group keys must come before aggregate expressions");
|
|
845
|
+
} else {
|
|
846
|
+
keys.push(e);
|
|
847
|
+
}
|
|
848
|
+
}
|
|
849
|
+
if (aggregates.length === 0) {
|
|
850
|
+
throw new BuilderError("rollup(): at least one aggregate expression is required");
|
|
851
|
+
}
|
|
852
|
+
return this.extend([{ kind: "RollupOp", keys, aggregates }]);
|
|
853
|
+
}
|
|
854
|
+
/** Pivot on `field`, computing the given aggregates for each pivot value. */
|
|
855
|
+
pivot(field, ...aggregates) {
|
|
856
|
+
const pivotField = one(field, keysParser("pivot() field"), "pivot() field");
|
|
857
|
+
const exprs = toExprs(collect(aggregates), exprsParser("pivot()"));
|
|
858
|
+
if (exprs.length === 0) throw new BuilderError("pivot(): at least one aggregate expression is required");
|
|
859
|
+
return this.extend([{ kind: "PivotOp", pivotField, aggregates: exprs }]);
|
|
860
|
+
}
|
|
861
|
+
map(...fields) {
|
|
862
|
+
const exprs = toExprs(collect(fields), exprsParser("map()"), true);
|
|
863
|
+
if (exprs.length === 0) throw new BuilderError("map() requires at least one field");
|
|
864
|
+
return this.extend([{ kind: "MapOp", fields: exprs }]);
|
|
865
|
+
}
|
|
866
|
+
/**
|
|
867
|
+
* Fold rows into one value. `initial` cannot reference fields; the accumulator sees the
|
|
868
|
+
* row's fields plus `_acc`: `reduce(0, (o) => o._acc.plus(o.total))`.
|
|
869
|
+
* Strings are raw pipequery fragments; use `lit()` for string literals.
|
|
870
|
+
*/
|
|
871
|
+
reduce(initial, accumulator) {
|
|
872
|
+
const initialExpr = typeof initial === "string" ? one(initial, exprsParser("reduce() initial value"), "reduce() initial value") : typeof initial === "function" ? one(initial, exprsParser("reduce() initial value"), "reduce() initial value") : toAst(initial, "reduce() initial value");
|
|
873
|
+
const accumulatorExpr = one(accumulator, exprsParser("reduce() accumulator"), "reduce() accumulator");
|
|
874
|
+
return this.terminal({ kind: "ReduceOp", initial: initialExpr, accumulator: accumulatorExpr });
|
|
875
|
+
}
|
|
876
|
+
/**
|
|
877
|
+
* Escape hatch: splice raw pipequery operations, parsed with the real parser.
|
|
878
|
+
* Accepts `'where(x > 1)'`, `'where(x > 1) | first(3)'` (optional leading `|`), or an AST operation.
|
|
879
|
+
*/
|
|
880
|
+
pipe(raw) {
|
|
881
|
+
if (typeof raw === "string") {
|
|
882
|
+
const text = raw.replace(/^\s*\|/, "");
|
|
883
|
+
const ops = parseOperations(text, 0, "pipe()");
|
|
884
|
+
if (ops.length === 0) throw new BuilderError("pipe() requires at least one operation");
|
|
885
|
+
return this.extend(ops);
|
|
886
|
+
}
|
|
887
|
+
if (typeof raw !== "object" || raw === null || typeof raw.kind !== "string") {
|
|
888
|
+
throw new BuilderError("pipe() expects a pipequery string or an operation node");
|
|
889
|
+
}
|
|
890
|
+
return this.extend([raw]);
|
|
891
|
+
}
|
|
892
|
+
/** @internal used by the terminal aggregate methods installed below. */
|
|
893
|
+
aggregate(name, args) {
|
|
894
|
+
const resolved = collect(args);
|
|
895
|
+
const asts = aggregateArgs(name, resolved);
|
|
896
|
+
const op = _chunkLWM2ZXGDcjs.TWO_ARG_AGGREGATES.has(name) ? { kind: "AggregateOp", function: name, args: asts } : { kind: "AggregateOp", function: name, field: asts[0] };
|
|
897
|
+
return this.terminal(op);
|
|
898
|
+
}
|
|
899
|
+
};
|
|
900
|
+
for (const name of _chunkLWM2ZXGDcjs.AGGREGATE_NAMES) {
|
|
901
|
+
Object.defineProperty(Query.prototype, name, {
|
|
902
|
+
configurable: true,
|
|
903
|
+
writable: true,
|
|
904
|
+
value: function(...args) {
|
|
905
|
+
return this.aggregate(name, args);
|
|
906
|
+
}
|
|
907
|
+
});
|
|
908
|
+
}
|
|
909
|
+
function from(source) {
|
|
910
|
+
const name = source === void 0 ? "_data" : validateSource(source);
|
|
911
|
+
return new Query(name);
|
|
912
|
+
}
|
|
913
|
+
|
|
914
|
+
|
|
915
|
+
|
|
916
|
+
|
|
917
|
+
|
|
918
|
+
|
|
919
|
+
|
|
920
|
+
|
|
921
|
+
|
|
922
|
+
|
|
923
|
+
|
|
924
|
+
|
|
925
|
+
|
|
926
|
+
exports.BuilderError = BuilderError; exports.Expr = Expr; exports.Query = Query; exports.Scalar = Scalar; exports.SortKey = SortKey; exports.agg = agg; exports.col = col; exports.fn = fn; exports.from = from; exports.lit = lit; exports.toAst = toAst; exports.win = win;
|