@webda/ql 4.0.0-beta.5 → 4.0.0-beta.6
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 +68 -27
- package/lib/WebdaQLLexer.d.ts +44 -30
- package/lib/WebdaQLLexer.d.ts.map +1 -1
- package/lib/WebdaQLLexer.js +257 -152
- package/lib/WebdaQLLexer.js.map +1 -1
- package/lib/WebdaQLParserLexer.d.ts +44 -30
- package/lib/WebdaQLParserLexer.d.ts.map +1 -1
- package/lib/WebdaQLParserLexer.js +257 -152
- package/lib/WebdaQLParserLexer.js.map +1 -1
- package/lib/WebdaQLParserListener.d.ts +203 -30
- package/lib/WebdaQLParserListener.d.ts.map +1 -1
- package/lib/WebdaQLParserParser.d.ts +242 -44
- package/lib/WebdaQLParserParser.d.ts.map +1 -1
- package/lib/WebdaQLParserParser.js +2054 -715
- package/lib/WebdaQLParserParser.js.map +1 -1
- package/lib/WebdaQLParserVisitor.d.ts +126 -17
- package/lib/WebdaQLParserVisitor.d.ts.map +1 -1
- package/lib/aggregation-query.d.ts +95 -0
- package/lib/aggregation-query.d.ts.map +1 -0
- package/lib/aggregation-query.js +104 -0
- package/lib/aggregation-query.js.map +1 -0
- package/lib/aggregation-select.spec.d.ts +2 -0
- package/lib/aggregation-select.spec.d.ts.map +1 -0
- package/lib/aggregation-select.spec.js +164 -0
- package/lib/aggregation-select.spec.js.map +1 -0
- package/lib/aggregation.d.ts +97 -0
- package/lib/aggregation.d.ts.map +1 -0
- package/lib/aggregation.js +241 -0
- package/lib/aggregation.js.map +1 -0
- package/lib/aggregation.spec.d.ts +2 -0
- package/lib/aggregation.spec.d.ts.map +1 -0
- package/lib/aggregation.spec.js +360 -0
- package/lib/aggregation.spec.js.map +1 -0
- package/lib/bind.d.ts.map +1 -1
- package/lib/bind.js +29 -0
- package/lib/bind.js.map +1 -1
- package/lib/index.d.ts +1 -0
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +1 -0
- package/lib/index.js.map +1 -1
- package/lib/query.d.ts +162 -8
- package/lib/query.d.ts.map +1 -1
- package/lib/query.js +489 -45
- package/lib/query.js.map +1 -1
- package/lib/query.spec.js +6 -4
- package/lib/query.spec.js.map +1 -1
- package/lib/statements.spec.d.ts +2 -0
- package/lib/statements.spec.d.ts.map +1 -0
- package/lib/statements.spec.js +422 -0
- package/lib/statements.spec.js.map +1 -0
- package/lib/webdaql-string.d.ts +9 -0
- package/lib/webdaql-string.d.ts.map +1 -1
- package/lib/webdaql-string.js +9 -8
- package/lib/webdaql-string.js.map +1 -1
- package/package.json +2 -2
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import type { Expression } from "./query.js";
|
|
2
|
+
import { WebdaQLError } from "./webdaql-string.js";
|
|
3
|
+
/**
|
|
4
|
+
* Aggregation functions supported on every backend
|
|
5
|
+
*/
|
|
6
|
+
export type AggregateFunction = "COUNT" | "COUNT_DISTINCT" | "SUM" | "AVG" | "MIN" | "MAX";
|
|
7
|
+
/**
|
|
8
|
+
* One computed column of an aggregation
|
|
9
|
+
*/
|
|
10
|
+
export interface Metric {
|
|
11
|
+
fn: AggregateFunction;
|
|
12
|
+
/**
|
|
13
|
+
* Dotted path of the aggregated attribute; omitted only for COUNT, meaning COUNT(*)
|
|
14
|
+
*/
|
|
15
|
+
field?: string;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Ordering of aggregated rows
|
|
19
|
+
*/
|
|
20
|
+
export interface AggregationOrder {
|
|
21
|
+
/**
|
|
22
|
+
* A groupBy path or a metric alias
|
|
23
|
+
*/
|
|
24
|
+
key: string;
|
|
25
|
+
direction: "ASC" | "DESC";
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Canonical aggregation AST consumed by every store
|
|
29
|
+
*/
|
|
30
|
+
export interface AggregationQuery {
|
|
31
|
+
/**
|
|
32
|
+
* Filter applied before grouping; `new AndExpression([])` matches everything
|
|
33
|
+
*/
|
|
34
|
+
filter: Expression;
|
|
35
|
+
/**
|
|
36
|
+
* Dotted paths; an empty array produces a single global row
|
|
37
|
+
*/
|
|
38
|
+
groupBy: string[];
|
|
39
|
+
/**
|
|
40
|
+
* alias → metric
|
|
41
|
+
*/
|
|
42
|
+
metrics: Record<string, Metric>;
|
|
43
|
+
orderBy?: AggregationOrder[];
|
|
44
|
+
limit?: number;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Result of an aggregation
|
|
48
|
+
*/
|
|
49
|
+
export interface AggregationResult<Row = Record<string, unknown>> {
|
|
50
|
+
rows: Row[];
|
|
51
|
+
/**
|
|
52
|
+
* false when the in-memory fallback ran, fully or partially
|
|
53
|
+
*/
|
|
54
|
+
native: boolean;
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Error codes raised by aggregations
|
|
58
|
+
*/
|
|
59
|
+
export type AggregationErrorCode = "AGGREGATION_NOT_NATIVE" | "AGGREGATION_TOO_MANY_GROUPS";
|
|
60
|
+
/**
|
|
61
|
+
* Error raised when an aggregation is refused at runtime
|
|
62
|
+
*/
|
|
63
|
+
export declare class AggregationError extends WebdaQLError {
|
|
64
|
+
readonly code: AggregationErrorCode;
|
|
65
|
+
/**
|
|
66
|
+
* @param code - machine readable code
|
|
67
|
+
* @param message - human readable message
|
|
68
|
+
*/
|
|
69
|
+
constructor(code: AggregationErrorCode, message: string);
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Valid attribute path: dotted identifiers, the only form translators interpolate
|
|
73
|
+
*/
|
|
74
|
+
export declare const AGGREGATION_PATH: RegExp;
|
|
75
|
+
/**
|
|
76
|
+
* Valid metric alias
|
|
77
|
+
*/
|
|
78
|
+
export declare const AGGREGATION_ALIAS: RegExp;
|
|
79
|
+
/**
|
|
80
|
+
* Check a metric alias
|
|
81
|
+
* @param alias - the alias
|
|
82
|
+
* @throws WebdaQLError when invalid or reserved
|
|
83
|
+
*/
|
|
84
|
+
export declare function checkAlias(alias: string): void;
|
|
85
|
+
/**
|
|
86
|
+
* Validate an aggregation query
|
|
87
|
+
*
|
|
88
|
+
* Paths and aliases that pass are safe to interpolate in native queries. Path segments cannot be
|
|
89
|
+
* all digits (array indexes are not portable). A missing `groupBy` is normalized to `[]`.
|
|
90
|
+
* @param query - the query to validate
|
|
91
|
+
* @returns the same query
|
|
92
|
+
* @throws WebdaQLError when the query is invalid
|
|
93
|
+
*/
|
|
94
|
+
export declare function validateAggregation(query: AggregationQuery): AggregationQuery;
|
|
95
|
+
//# sourceMappingURL=aggregation-query.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"aggregation-query.d.ts","sourceRoot":"","sources":["../src/aggregation-query.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAC7C,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAEnD;;GAEG;AACH,MAAM,MAAM,iBAAiB,GAAG,OAAO,GAAG,gBAAgB,GAAG,KAAK,GAAG,KAAK,GAAG,KAAK,GAAG,KAAK,CAAC;AAE3F;;GAEG;AACH,MAAM,WAAW,MAAM;IACrB,EAAE,EAAE,iBAAiB,CAAC;IACtB;;OAEG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC/B;;OAEG;IACH,GAAG,EAAE,MAAM,CAAC;IACZ,SAAS,EAAE,KAAK,GAAG,MAAM,CAAC;CAC3B;AAED;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC/B;;OAEG;IACH,MAAM,EAAE,UAAU,CAAC;IACnB;;OAEG;IACH,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB;;OAEG;IACH,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAChC,OAAO,CAAC,EAAE,gBAAgB,EAAE,CAAC;IAC7B,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED;;GAEG;AACH,MAAM,WAAW,iBAAiB,CAAC,GAAG,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;IAC9D,IAAI,EAAE,GAAG,EAAE,CAAC;IACZ;;OAEG;IACH,MAAM,EAAE,OAAO,CAAC;CACjB;AAED;;GAEG;AACH,MAAM,MAAM,oBAAoB,GAAG,wBAAwB,GAAG,6BAA6B,CAAC;AAE5F;;GAEG;AACH,qBAAa,gBAAiB,SAAQ,YAAY;aAM9B,IAAI,EAAE,oBAAoB;IAL5C;;;OAGG;IACH,YACkB,IAAI,EAAE,oBAAoB,EAC1C,OAAO,EAAE,MAAM,EAIhB;CACF;AAED;;GAEG;AACH,eAAO,MAAM,gBAAgB,QAAsC,CAAC;AACpE;;GAEG;AACH,eAAO,MAAM,iBAAiB,QAA4B,CAAC;AAwB3D;;;;GAIG;AACH,wBAAgB,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAI9C;AAED;;;;;;;;GAQG;AACH,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,gBAAgB,GAAG,gBAAgB,CAwC7E"}
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import { WebdaQLError } from "./webdaql-string.js";
|
|
2
|
+
/**
|
|
3
|
+
* Error raised when an aggregation is refused at runtime
|
|
4
|
+
*/
|
|
5
|
+
export class AggregationError extends WebdaQLError {
|
|
6
|
+
code;
|
|
7
|
+
/**
|
|
8
|
+
* @param code - machine readable code
|
|
9
|
+
* @param message - human readable message
|
|
10
|
+
*/
|
|
11
|
+
constructor(code, message) {
|
|
12
|
+
super(message);
|
|
13
|
+
this.code = code;
|
|
14
|
+
this.name = "AggregationError";
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Valid attribute path: dotted identifiers, the only form translators interpolate
|
|
19
|
+
*/
|
|
20
|
+
export const AGGREGATION_PATH = /^[a-zA-Z0-9_]+(\.[a-zA-Z0-9_]+)*$/;
|
|
21
|
+
/**
|
|
22
|
+
* Valid metric alias
|
|
23
|
+
*/
|
|
24
|
+
export const AGGREGATION_ALIAS = /^[a-zA-Z][a-zA-Z0-9_]*$/;
|
|
25
|
+
const FORBIDDEN_SEGMENTS = new Set(["__proto__", "prototype", "constructor"]);
|
|
26
|
+
/**
|
|
27
|
+
* All-digit segment: MongoDB `$a.0` does not index arrays, so such paths would differ across backends
|
|
28
|
+
*/
|
|
29
|
+
const DIGITS = /^[0-9]+$/;
|
|
30
|
+
const FUNCTIONS = new Set(["COUNT", "COUNT_DISTINCT", "SUM", "AVG", "MIN", "MAX"]);
|
|
31
|
+
/**
|
|
32
|
+
* Check an attribute path
|
|
33
|
+
* @param path - the path
|
|
34
|
+
* @param what - description for the error
|
|
35
|
+
*/
|
|
36
|
+
function checkPath(path, what) {
|
|
37
|
+
if (typeof path !== "string" ||
|
|
38
|
+
!AGGREGATION_PATH.test(path) ||
|
|
39
|
+
path.split(".").some(segment => FORBIDDEN_SEGMENTS.has(segment) || DIGITS.test(segment))) {
|
|
40
|
+
throw new WebdaQLError(`Invalid ${what} '${path}'`);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Check a metric alias
|
|
45
|
+
* @param alias - the alias
|
|
46
|
+
* @throws WebdaQLError when invalid or reserved
|
|
47
|
+
*/
|
|
48
|
+
export function checkAlias(alias) {
|
|
49
|
+
if (!AGGREGATION_ALIAS.test(alias) || FORBIDDEN_SEGMENTS.has(alias)) {
|
|
50
|
+
throw new WebdaQLError(`Invalid metric alias '${alias}'`);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Validate an aggregation query
|
|
55
|
+
*
|
|
56
|
+
* Paths and aliases that pass are safe to interpolate in native queries. Path segments cannot be
|
|
57
|
+
* all digits (array indexes are not portable). A missing `groupBy` is normalized to `[]`.
|
|
58
|
+
* @param query - the query to validate
|
|
59
|
+
* @returns the same query
|
|
60
|
+
* @throws WebdaQLError when the query is invalid
|
|
61
|
+
*/
|
|
62
|
+
export function validateAggregation(query) {
|
|
63
|
+
query.groupBy ??= [];
|
|
64
|
+
const groupBy = query.groupBy;
|
|
65
|
+
groupBy.forEach(path => checkPath(path, "group by path"));
|
|
66
|
+
if (new Set(groupBy).size !== groupBy.length) {
|
|
67
|
+
throw new WebdaQLError("Duplicate group by path");
|
|
68
|
+
}
|
|
69
|
+
const aliases = Object.keys(query.metrics ?? {});
|
|
70
|
+
if (!aliases.length) {
|
|
71
|
+
throw new WebdaQLError("An aggregation needs at least one metric");
|
|
72
|
+
}
|
|
73
|
+
for (const alias of aliases) {
|
|
74
|
+
checkAlias(alias);
|
|
75
|
+
if (groupBy.includes(alias)) {
|
|
76
|
+
throw new WebdaQLError(`Metric alias '${alias}' collides with a group by path`);
|
|
77
|
+
}
|
|
78
|
+
const metric = query.metrics[alias];
|
|
79
|
+
if (!FUNCTIONS.has(metric?.fn)) {
|
|
80
|
+
throw new WebdaQLError(`Unknown aggregate function '${metric?.fn}'`);
|
|
81
|
+
}
|
|
82
|
+
if (metric.field === undefined) {
|
|
83
|
+
if (metric.fn !== "COUNT") {
|
|
84
|
+
throw new WebdaQLError(`${metric.fn} needs a field`);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
else {
|
|
88
|
+
checkPath(metric.field, "metric field");
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
for (const order of query.orderBy ?? []) {
|
|
92
|
+
if (!groupBy.includes(order.key) && !aliases.includes(order.key)) {
|
|
93
|
+
throw new WebdaQLError(`ORDER BY '${order.key}' is neither a group by path nor a metric alias`);
|
|
94
|
+
}
|
|
95
|
+
if (order.direction !== "ASC" && order.direction !== "DESC") {
|
|
96
|
+
throw new WebdaQLError(`Invalid ORDER BY direction '${order.direction}'`);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
if (query.limit !== undefined && !(Number.isInteger(query.limit) && query.limit > 0)) {
|
|
100
|
+
throw new WebdaQLError(`Invalid LIMIT '${query.limit}'`);
|
|
101
|
+
}
|
|
102
|
+
return query;
|
|
103
|
+
}
|
|
104
|
+
//# sourceMappingURL=aggregation-query.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"aggregation-query.js","sourceRoot":"","sources":["../src/aggregation-query.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"aggregation-select.spec.d.ts","sourceRoot":"","sources":["../src/aggregation-select.spec.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
var __runInitializers = (this && this.__runInitializers) || function (thisArg, initializers, value) {
|
|
2
|
+
var useValue = arguments.length > 2;
|
|
3
|
+
for (var i = 0; i < initializers.length; i++) {
|
|
4
|
+
value = useValue ? initializers[i].call(thisArg, value) : initializers[i].call(thisArg);
|
|
5
|
+
}
|
|
6
|
+
return useValue ? value : void 0;
|
|
7
|
+
};
|
|
8
|
+
var __esDecorate = (this && this.__esDecorate) || function (ctor, descriptorIn, decorators, contextIn, initializers, extraInitializers) {
|
|
9
|
+
function accept(f) { if (f !== void 0 && typeof f !== "function") throw new TypeError("Function expected"); return f; }
|
|
10
|
+
var kind = contextIn.kind, key = kind === "getter" ? "get" : kind === "setter" ? "set" : "value";
|
|
11
|
+
var target = !descriptorIn && ctor ? contextIn["static"] ? ctor : ctor.prototype : null;
|
|
12
|
+
var descriptor = descriptorIn || (target ? Object.getOwnPropertyDescriptor(target, contextIn.name) : {});
|
|
13
|
+
var _, done = false;
|
|
14
|
+
for (var i = decorators.length - 1; i >= 0; i--) {
|
|
15
|
+
var context = {};
|
|
16
|
+
for (var p in contextIn) context[p] = p === "access" ? {} : contextIn[p];
|
|
17
|
+
for (var p in contextIn.access) context.access[p] = contextIn.access[p];
|
|
18
|
+
context.addInitializer = function (f) { if (done) throw new TypeError("Cannot add initializers after decoration has completed"); extraInitializers.push(accept(f || null)); };
|
|
19
|
+
var result = (0, decorators[i])(kind === "accessor" ? { get: descriptor.get, set: descriptor.set } : descriptor[key], context);
|
|
20
|
+
if (kind === "accessor") {
|
|
21
|
+
if (result === void 0) continue;
|
|
22
|
+
if (result === null || typeof result !== "object") throw new TypeError("Object expected");
|
|
23
|
+
if (_ = accept(result.get)) descriptor.get = _;
|
|
24
|
+
if (_ = accept(result.set)) descriptor.set = _;
|
|
25
|
+
if (_ = accept(result.init)) initializers.unshift(_);
|
|
26
|
+
}
|
|
27
|
+
else if (_ = accept(result)) {
|
|
28
|
+
if (kind === "field") initializers.unshift(_);
|
|
29
|
+
else descriptor[key] = _;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
if (target) Object.defineProperty(target, contextIn.name, descriptor);
|
|
33
|
+
done = true;
|
|
34
|
+
};
|
|
35
|
+
import { suite, test } from "@webda/test";
|
|
36
|
+
import * as assert from "assert";
|
|
37
|
+
import { toAggregationQuery } from "./aggregation.js";
|
|
38
|
+
import { assertFilterQuery, normalizeQuery, parse } from "./query.js";
|
|
39
|
+
/**
|
|
40
|
+
* Comparable form of an aggregation query
|
|
41
|
+
* @param query - the query
|
|
42
|
+
* @returns a plain object
|
|
43
|
+
*/
|
|
44
|
+
function plain(query) {
|
|
45
|
+
return { ...query, filter: query.filter.toString() };
|
|
46
|
+
}
|
|
47
|
+
let AggregationSelectTest = (() => {
|
|
48
|
+
let _classDecorators = [suite];
|
|
49
|
+
let _classDescriptor;
|
|
50
|
+
let _classExtraInitializers = [];
|
|
51
|
+
let _classThis;
|
|
52
|
+
let _instanceExtraInitializers = [];
|
|
53
|
+
let _roundTrip_decorators;
|
|
54
|
+
let _globalAggregation_decorators;
|
|
55
|
+
let _plainSelectUnchanged_decorators;
|
|
56
|
+
let _rejects_decorators;
|
|
57
|
+
let _reservedAliases_decorators;
|
|
58
|
+
let _allowedFields_decorators;
|
|
59
|
+
let _keywords_decorators;
|
|
60
|
+
var AggregationSelectTest = class {
|
|
61
|
+
static { _classThis = this; }
|
|
62
|
+
static {
|
|
63
|
+
const _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create(null) : void 0;
|
|
64
|
+
_roundTrip_decorators = [test];
|
|
65
|
+
_globalAggregation_decorators = [test];
|
|
66
|
+
_plainSelectUnchanged_decorators = [test];
|
|
67
|
+
_rejects_decorators = [test];
|
|
68
|
+
_reservedAliases_decorators = [test];
|
|
69
|
+
_allowedFields_decorators = [test];
|
|
70
|
+
_keywords_decorators = [test];
|
|
71
|
+
__esDecorate(this, null, _roundTrip_decorators, { kind: "method", name: "roundTrip", static: false, private: false, access: { has: obj => "roundTrip" in obj, get: obj => obj.roundTrip }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
72
|
+
__esDecorate(this, null, _globalAggregation_decorators, { kind: "method", name: "globalAggregation", static: false, private: false, access: { has: obj => "globalAggregation" in obj, get: obj => obj.globalAggregation }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
73
|
+
__esDecorate(this, null, _plainSelectUnchanged_decorators, { kind: "method", name: "plainSelectUnchanged", static: false, private: false, access: { has: obj => "plainSelectUnchanged" in obj, get: obj => obj.plainSelectUnchanged }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
74
|
+
__esDecorate(this, null, _rejects_decorators, { kind: "method", name: "rejects", static: false, private: false, access: { has: obj => "rejects" in obj, get: obj => obj.rejects }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
75
|
+
__esDecorate(this, null, _reservedAliases_decorators, { kind: "method", name: "reservedAliases", static: false, private: false, access: { has: obj => "reservedAliases" in obj, get: obj => obj.reservedAliases }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
76
|
+
__esDecorate(this, null, _allowedFields_decorators, { kind: "method", name: "allowedFields", static: false, private: false, access: { has: obj => "allowedFields" in obj, get: obj => obj.allowedFields }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
77
|
+
__esDecorate(this, null, _keywords_decorators, { kind: "method", name: "keywords", static: false, private: false, access: { has: obj => "keywords" in obj, get: obj => obj.keywords }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
78
|
+
__esDecorate(null, _classDescriptor = { value: _classThis }, _classDecorators, { kind: "class", name: _classThis.name, metadata: _metadata }, null, _classExtraInitializers);
|
|
79
|
+
AggregationSelectTest = _classThis = _classDescriptor.value;
|
|
80
|
+
if (_metadata) Object.defineProperty(_classThis, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata });
|
|
81
|
+
__runInitializers(_classThis, _classExtraInitializers);
|
|
82
|
+
}
|
|
83
|
+
roundTrip() {
|
|
84
|
+
const query = parse("SELECT status, owner.uuid, COUNT(*) AS n, COUNT(tag) AS withTag, COUNT(DISTINCT tag) AS tags, " +
|
|
85
|
+
"SUM(points) AS total, AVG(points) AS mean, MIN(createdAt) AS first, MAX(points) AS top " +
|
|
86
|
+
"WHERE completed = FALSE AND owner.uuid = 'u1' GROUP BY status, owner.uuid ORDER BY n DESC, status LIMIT 10");
|
|
87
|
+
assert.strictEqual(query.type, "SELECT");
|
|
88
|
+
assert.deepStrictEqual(query.fields, ["status", "owner.uuid"]);
|
|
89
|
+
assert.deepStrictEqual(plain(query.aggregation), plain(toAggregationQuery({
|
|
90
|
+
filter: "completed = FALSE AND owner.uuid = 'u1'",
|
|
91
|
+
groupBy: ["status", "owner.uuid"],
|
|
92
|
+
metrics: {
|
|
93
|
+
n: { count: "*" },
|
|
94
|
+
withTag: { count: "tag" },
|
|
95
|
+
tags: { countDistinct: "tag" },
|
|
96
|
+
total: { sum: "points" },
|
|
97
|
+
mean: { avg: "points" },
|
|
98
|
+
first: { min: "createdAt" },
|
|
99
|
+
top: { max: "points" }
|
|
100
|
+
},
|
|
101
|
+
orderBy: [
|
|
102
|
+
{ key: "n", direction: "DESC" },
|
|
103
|
+
{ key: "status", direction: "ASC" }
|
|
104
|
+
],
|
|
105
|
+
limit: 10
|
|
106
|
+
})));
|
|
107
|
+
assert.deepStrictEqual(plain(parse(query.toString()).aggregation), plain(query.aggregation));
|
|
108
|
+
}
|
|
109
|
+
globalAggregation() {
|
|
110
|
+
const query = parse("SELECT COUNT(*) AS n");
|
|
111
|
+
assert.deepStrictEqual(query.fields, []);
|
|
112
|
+
assert.deepStrictEqual(plain(query.aggregation), {
|
|
113
|
+
filter: "",
|
|
114
|
+
groupBy: [],
|
|
115
|
+
metrics: { n: { fn: "COUNT" } },
|
|
116
|
+
orderBy: undefined,
|
|
117
|
+
limit: undefined
|
|
118
|
+
});
|
|
119
|
+
// Repository query() / iterate() refuse it like any SELECT field list
|
|
120
|
+
assert.throws(() => assertFilterQuery(query));
|
|
121
|
+
assert.deepStrictEqual(plain(parse(query.toString()).aggregation), plain(query.aggregation));
|
|
122
|
+
assert.throws(() => normalizeQuery(query), /Aggregation/);
|
|
123
|
+
}
|
|
124
|
+
plainSelectUnchanged() {
|
|
125
|
+
const query = parse("SELECT a, b WHERE a = 1");
|
|
126
|
+
assert.deepStrictEqual(query.fields, ["a", "b"]);
|
|
127
|
+
assert.strictEqual(query.aggregation, undefined);
|
|
128
|
+
}
|
|
129
|
+
rejects() {
|
|
130
|
+
for (const bad of [
|
|
131
|
+
"SELECT status, COUNT(*) AS n",
|
|
132
|
+
"SELECT COUNT(*) AS n GROUP BY status",
|
|
133
|
+
"SELECT status, owner GROUP BY status",
|
|
134
|
+
"SELECT COUNT(*)",
|
|
135
|
+
"SELECT SUM(*) AS s",
|
|
136
|
+
"SELECT COUNT(*) AS n, COUNT(a) AS n",
|
|
137
|
+
"SELECT COUNT(*) AS n OFFSET 'x'",
|
|
138
|
+
"SELECT COUNT(*) AS n ORDER BY nope",
|
|
139
|
+
"SELECT status GROUP BY status"
|
|
140
|
+
]) {
|
|
141
|
+
assert.throws(() => parse(bad), undefined, bad);
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
reservedAliases() {
|
|
145
|
+
for (const bad of ["SELECT COUNT(*) AS n, SUM(x) AS __proto__", "SELECT SUM(x) AS __proto__"]) {
|
|
146
|
+
assert.throws(() => parse(bad), /Invalid metric alias/, bad);
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
allowedFields() {
|
|
150
|
+
assert.throws(() => parse("SELECT SUM(secret) AS s", ["points"]), /secret/);
|
|
151
|
+
assert.ok(parse("SELECT SUM(points) AS s", ["points"]).aggregation);
|
|
152
|
+
}
|
|
153
|
+
keywords() {
|
|
154
|
+
assert.strictEqual(parse("count = 1 AND min = 2").filter.toString(), "count = 1 AND min = 2", "lowercase stays an attribute");
|
|
155
|
+
assert.throws(() => parse("COUNT = 1"), undefined, "uppercase COUNT is now a keyword");
|
|
156
|
+
assert.throws(() => parse("a = 1 GROUP BY a"), undefined, "GROUP BY only in a SELECT");
|
|
157
|
+
}
|
|
158
|
+
constructor() {
|
|
159
|
+
__runInitializers(this, _instanceExtraInitializers);
|
|
160
|
+
}
|
|
161
|
+
};
|
|
162
|
+
return AggregationSelectTest = _classThis;
|
|
163
|
+
})();
|
|
164
|
+
//# sourceMappingURL=aggregation-select.spec.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"aggregation-select.spec.js","sourceRoot":"","sources":["../src/aggregation-select.spec.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import { type QueryParameters } from "./bind.js";
|
|
2
|
+
import { type AggregationOrder, type AggregationQuery } from "./aggregation-query.js";
|
|
3
|
+
export * from "./aggregation-query.js";
|
|
4
|
+
/**
|
|
5
|
+
* Compare two values the way every backend orders aggregated rows
|
|
6
|
+
*
|
|
7
|
+
* null / undefined first, then numbers, strings (code unit order), booleans, then anything
|
|
8
|
+
* else by its JSON representation.
|
|
9
|
+
* @param a - first value
|
|
10
|
+
* @param b - second value
|
|
11
|
+
* @returns negative, zero or positive
|
|
12
|
+
*/
|
|
13
|
+
export declare function compareValues(a: unknown, b: unknown): number;
|
|
14
|
+
/**
|
|
15
|
+
* Running computation of one metric
|
|
16
|
+
*/
|
|
17
|
+
interface Accumulator {
|
|
18
|
+
add(item: any): void;
|
|
19
|
+
result(): unknown;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Streaming reference implementation of an aggregation
|
|
23
|
+
*
|
|
24
|
+
* Items must already match the query filter. Memory is O(groups), plus one Set per group per
|
|
25
|
+
* COUNT_DISTINCT. It is the fallback engine of repositories and the oracle of the
|
|
26
|
+
* conformance tests.
|
|
27
|
+
*/
|
|
28
|
+
export declare class Aggregator {
|
|
29
|
+
protected query: AggregationQuery;
|
|
30
|
+
protected maxGroups: number;
|
|
31
|
+
protected groups: Map<string, {
|
|
32
|
+
keys: unknown[];
|
|
33
|
+
accumulators: [string, Accumulator][];
|
|
34
|
+
}>;
|
|
35
|
+
/**
|
|
36
|
+
* @param query - the aggregation; validated here
|
|
37
|
+
* @param maxGroups - throw AGGREGATION_TOO_MANY_GROUPS beyond this number of groups
|
|
38
|
+
*/
|
|
39
|
+
constructor(query: AggregationQuery, maxGroups?: number);
|
|
40
|
+
/**
|
|
41
|
+
* Create an empty group
|
|
42
|
+
* @param keys - group key values
|
|
43
|
+
* @returns the group
|
|
44
|
+
*/
|
|
45
|
+
protected createGroup(keys: unknown[]): {
|
|
46
|
+
keys: unknown[];
|
|
47
|
+
accumulators: [string, Accumulator][];
|
|
48
|
+
};
|
|
49
|
+
/**
|
|
50
|
+
* Add a matching item
|
|
51
|
+
* @param item - the item
|
|
52
|
+
*/
|
|
53
|
+
add(item: any): void;
|
|
54
|
+
/**
|
|
55
|
+
* Compute the rows, ordered and limited
|
|
56
|
+
* @returns flat rows keyed by group path and metric alias
|
|
57
|
+
*/
|
|
58
|
+
rows(): Record<string, unknown>[];
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Ergonomic metric definition used by the object API
|
|
62
|
+
*/
|
|
63
|
+
export type MetricInput = {
|
|
64
|
+
count: string;
|
|
65
|
+
} | {
|
|
66
|
+
countDistinct: string;
|
|
67
|
+
} | {
|
|
68
|
+
sum: string;
|
|
69
|
+
} | {
|
|
70
|
+
avg: string;
|
|
71
|
+
} | {
|
|
72
|
+
min: string;
|
|
73
|
+
} | {
|
|
74
|
+
max: string;
|
|
75
|
+
};
|
|
76
|
+
/**
|
|
77
|
+
* Untyped object form of an aggregation, as accepted by repositories
|
|
78
|
+
*/
|
|
79
|
+
export interface AggregationInput {
|
|
80
|
+
/**
|
|
81
|
+
* WebdaQL filter, without ORDER BY / LIMIT / OFFSET
|
|
82
|
+
*/
|
|
83
|
+
filter?: string;
|
|
84
|
+
groupBy?: readonly string[];
|
|
85
|
+
metrics: Record<string, MetricInput>;
|
|
86
|
+
orderBy?: readonly AggregationOrder[];
|
|
87
|
+
limit?: number;
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Convert the object form of an aggregation to the canonical AST
|
|
91
|
+
* @param input - the object form
|
|
92
|
+
* @param params - values for the `?` / `:name` placeholders of the filter
|
|
93
|
+
* @returns the validated AST
|
|
94
|
+
* @throws WebdaQLError when the input is invalid
|
|
95
|
+
*/
|
|
96
|
+
export declare function toAggregationQuery(input: AggregationInput, params?: QueryParameters): AggregationQuery;
|
|
97
|
+
//# sourceMappingURL=aggregation.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"aggregation.d.ts","sourceRoot":"","sources":["../src/aggregation.ts"],"names":[],"mappings":"AACA,OAAO,EAAQ,KAAK,eAAe,EAAE,MAAM,WAAW,CAAC;AAEvD,OAAO,EAGL,KAAK,gBAAgB,EACrB,KAAK,gBAAgB,EAItB,MAAM,wBAAwB,CAAC;AAGhC,cAAc,wBAAwB,CAAC;AAIvC;;;;;;;;GAQG;AACH,wBAAgB,aAAa,CAAC,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,OAAO,GAAG,MAAM,CAc5D;AAED;;GAEG;AACH,UAAU,WAAW;IACnB,GAAG,CAAC,IAAI,EAAE,GAAG,GAAG,IAAI,CAAC;IACrB,MAAM,IAAI,OAAO,CAAC;CACnB;AAkFD;;;;;;GAMG;AACH,qBAAa,UAAU;IAQnB,SAAS,CAAC,KAAK,EAAE,gBAAgB;IACjC,SAAS,CAAC,SAAS,EAAE,MAAM;IAR7B,SAAS,CAAC,MAAM;cAA2B,OAAO,EAAE;sBAAgB,CAAC,MAAM,EAAE,WAAW,CAAC,EAAE;OAAM;IAEjG;;;OAGG;IACH,YACY,KAAK,EAAE,gBAAgB,EACvB,SAAS,GAAE,MAAiC,EAGvD;IAED;;;;OAIG;IACH,SAAS,CAAC,WAAW,CAAC,IAAI,EAAE,OAAO,EAAE;;;MAOpC;IAED;;;OAGG;IACH,GAAG,CAAC,IAAI,EAAE,GAAG,GAAG,IAAI,CAcnB;IAED;;;OAGG;IACH,IAAI,IAAI,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,CAyBhC;CACF;AAED;;GAEG;AACH,MAAM,MAAM,WAAW,GACrB;IAAE,KAAK,EAAE,MAAM,CAAA;CAAE,GAAG;IAAE,aAAa,EAAE,MAAM,CAAA;CAAE,GAAG;IAAE,GAAG,EAAE,MAAM,CAAA;CAAE,GAAG;IAAE,GAAG,EAAE,MAAM,CAAA;CAAE,GAAG;IAAE,GAAG,EAAE,MAAM,CAAA;CAAE,GAAG;IAAE,GAAG,EAAE,MAAM,CAAA;CAAE,CAAC;AAExH;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC/B;;OAEG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAC5B,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC;IACrC,OAAO,CAAC,EAAE,SAAS,gBAAgB,EAAE,CAAC;IACtC,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAWD;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,gBAAgB,EAAE,MAAM,CAAC,EAAE,eAAe,GAAG,gBAAgB,CA8BtG"}
|