@visactor/vquery 0.4.16 → 0.4.19
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/dist/browser/esm/browser.js +43 -21
- package/dist/browser/esm/sql-builder/builders/having.d.ts +1 -1
- package/dist/browser/esm/types/dsl/Having.d.ts +19 -6
- package/dist/browser/esm/types/dsl/Select.d.ts +1 -1
- package/dist/browser/esm/types/dsl/index.d.ts +1 -1
- package/dist/node/cjs/node.cjs +43 -21
- package/dist/node/cjs/sql-builder/builders/having.d.ts +1 -1
- package/dist/node/cjs/types/dsl/Having.d.ts +19 -6
- package/dist/node/cjs/types/dsl/Select.d.ts +1 -1
- package/dist/node/cjs/types/dsl/index.d.ts +1 -1
- package/dist/node/esm/node.js +43 -21
- package/dist/node/esm/sql-builder/builders/having.d.ts +1 -1
- package/dist/node/esm/types/dsl/Having.d.ts +19 -6
- package/dist/node/esm/types/dsl/Select.d.ts +1 -1
- package/dist/node/esm/types/dsl/index.d.ts +1 -1
- package/package.json +1 -1
|
@@ -114,6 +114,36 @@ const applyGroupBy = (qb, fields)=>{
|
|
|
114
114
|
}
|
|
115
115
|
return qb;
|
|
116
116
|
};
|
|
117
|
+
const AGGREGATE_FUNCTIONS = [
|
|
118
|
+
'sum',
|
|
119
|
+
'avg',
|
|
120
|
+
'count',
|
|
121
|
+
'count_distinct',
|
|
122
|
+
'min',
|
|
123
|
+
'max',
|
|
124
|
+
'variance',
|
|
125
|
+
'variance_pop',
|
|
126
|
+
'stddev',
|
|
127
|
+
'median',
|
|
128
|
+
'quantile'
|
|
129
|
+
];
|
|
130
|
+
const isAggregateFunction = (func)=>AGGREGATE_FUNCTIONS.includes(func);
|
|
131
|
+
const toAggregateExpression = (func, field, aggr)=>{
|
|
132
|
+
switch(func){
|
|
133
|
+
case 'count':
|
|
134
|
+
return external_kysely_sql`count(${external_kysely_sql.ref(field)})`;
|
|
135
|
+
case 'count_distinct':
|
|
136
|
+
return external_kysely_sql`count(distinct ${external_kysely_sql.ref(field)})`;
|
|
137
|
+
case 'variance':
|
|
138
|
+
return external_kysely_sql`var_samp(${external_kysely_sql.ref(field)})`;
|
|
139
|
+
case 'variance_pop':
|
|
140
|
+
return external_kysely_sql`var_pop(${external_kysely_sql.ref(field)})`;
|
|
141
|
+
case 'quantile':
|
|
142
|
+
return external_kysely_sql`quantile(${external_kysely_sql.ref(field)}, ${aggr?.quantile ?? 0.5})`;
|
|
143
|
+
default:
|
|
144
|
+
return external_kysely_sql`${external_kysely_sql.raw(func)}(${external_kysely_sql.ref(field)})`;
|
|
145
|
+
}
|
|
146
|
+
};
|
|
117
147
|
const applyHaving = (qb, having)=>{
|
|
118
148
|
if (!having) return qb;
|
|
119
149
|
const toRaw = (h)=>{
|
|
@@ -125,48 +155,40 @@ const applyHaving = (qb, having)=>{
|
|
|
125
155
|
const leaf = h;
|
|
126
156
|
const field = leaf.field;
|
|
127
157
|
const value = leaf.value;
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
'count',
|
|
133
|
-
'min',
|
|
134
|
-
'max'
|
|
135
|
-
].includes(op)) {
|
|
136
|
-
const aggrExpr = external_kysely_sql`${external_kysely_sql.raw(op.toLowerCase())}(${external_kysely_sql.ref(field)})`;
|
|
137
|
-
return external_kysely_sql`${aggrExpr} = ${external_kysely_sql.val(value)}`;
|
|
138
|
-
}
|
|
139
|
-
switch(leaf.op){
|
|
158
|
+
if (!leaf.aggr?.func || !isAggregateFunction(leaf.aggr.func)) throw new Error(`Invalid having clause for field "${String(field)}": aggr.func is required`);
|
|
159
|
+
const compareOp = toSqlOperator(leaf.op.trim().toLowerCase());
|
|
160
|
+
const leftExpr = toAggregateExpression(leaf.aggr.func, field, leaf.aggr);
|
|
161
|
+
switch(compareOp){
|
|
140
162
|
case 'is null':
|
|
141
|
-
return external_kysely_sql`${
|
|
163
|
+
return external_kysely_sql`${leftExpr} is null`;
|
|
142
164
|
case 'is not null':
|
|
143
|
-
return external_kysely_sql`${
|
|
165
|
+
return external_kysely_sql`${leftExpr} is not null`;
|
|
144
166
|
case 'in':
|
|
145
167
|
{
|
|
146
168
|
const items = Array.isArray(value) ? value : [
|
|
147
169
|
value
|
|
148
170
|
];
|
|
149
|
-
return external_kysely_sql`${
|
|
171
|
+
return external_kysely_sql`${leftExpr} in (${external_kysely_sql.join(items.map((v)=>external_kysely_sql.val(v)))})`;
|
|
150
172
|
}
|
|
151
173
|
case 'not in':
|
|
152
174
|
{
|
|
153
175
|
const items = Array.isArray(value) ? value : [
|
|
154
176
|
value
|
|
155
177
|
];
|
|
156
|
-
return external_kysely_sql`not ${
|
|
178
|
+
return external_kysely_sql`not ${leftExpr} in (${external_kysely_sql.join(items.map((v)=>external_kysely_sql.val(v)))})`;
|
|
157
179
|
}
|
|
158
180
|
case 'between':
|
|
159
181
|
{
|
|
160
182
|
const [a, b] = value;
|
|
161
|
-
return external_kysely_sql`${
|
|
183
|
+
return external_kysely_sql`${leftExpr} between ${external_kysely_sql.val(a)} and ${external_kysely_sql.val(b)}`;
|
|
162
184
|
}
|
|
163
185
|
case 'not between':
|
|
164
186
|
{
|
|
165
187
|
const [a, b] = value;
|
|
166
|
-
return external_kysely_sql`${
|
|
188
|
+
return external_kysely_sql`${leftExpr} not between ${external_kysely_sql.val(a)} and ${external_kysely_sql.val(b)}`;
|
|
167
189
|
}
|
|
168
190
|
default:
|
|
169
|
-
return external_kysely_sql`${
|
|
191
|
+
return external_kysely_sql`${leftExpr} ${external_kysely_sql.raw(compareOp)} ${external_kysely_sql.val(value)}`;
|
|
170
192
|
}
|
|
171
193
|
};
|
|
172
194
|
return qb.having(toRaw(having));
|
|
@@ -204,12 +226,12 @@ const applySelect = (qb, select)=>{
|
|
|
204
226
|
'min',
|
|
205
227
|
'max',
|
|
206
228
|
'variance',
|
|
207
|
-
'
|
|
229
|
+
'variance_pop',
|
|
208
230
|
'stddev',
|
|
209
231
|
'median'
|
|
210
232
|
].includes(func)) {
|
|
211
233
|
if ('variance' === func) return external_kysely_sql`var_samp(${expression})`.as(alias);
|
|
212
|
-
if ('
|
|
234
|
+
if ('variance_pop' === func) return external_kysely_sql`var_pop(${expression})`.as(alias);
|
|
213
235
|
return external_kysely_sql`${external_kysely_sql.raw(func)}(${expression})`.as(alias);
|
|
214
236
|
}
|
|
215
237
|
if ('count' === func) return external_kysely_sql`CAST(count(${expression}) AS INTEGER)`.as(alias);
|
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
import type { SelectQueryBuilder } from 'kysely';
|
|
2
|
-
import { Having, HavingClause } from '../../types';
|
|
2
|
+
import type { Having, HavingClause } from '../../types';
|
|
3
3
|
export declare const applyHaving: <DB, TB extends keyof DB & string, O, T>(qb: SelectQueryBuilder<DB, TB, O>, having?: Having<T> | HavingClause<T>) => SelectQueryBuilder<DB, TB, O>;
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
* Having 子句类型定义
|
|
3
3
|
* 用于聚合后的数据筛选
|
|
4
4
|
*/
|
|
5
|
+
import type { BaseAggregateFunction } from './Select';
|
|
5
6
|
export type Having<T> = HavingGroup<T>;
|
|
6
7
|
export type HavingGroup<T> = {
|
|
7
8
|
op: 'and' | 'or';
|
|
@@ -10,13 +11,14 @@ export type HavingGroup<T> = {
|
|
|
10
11
|
export type HavingClause<T> = HavingLeaf<T> | HavingGroup<T>;
|
|
11
12
|
/**
|
|
12
13
|
* Having 叶子节点
|
|
13
|
-
*
|
|
14
|
+
* 通过 aggr 指定聚合方式,通过 op 指定比较操作符
|
|
14
15
|
*/
|
|
15
16
|
export type HavingLeaf<T> = {
|
|
16
17
|
[K in keyof T]: {
|
|
17
|
-
[O in
|
|
18
|
+
[O in HavingComparisonOperator]: {
|
|
18
19
|
field: K;
|
|
19
20
|
op: O;
|
|
21
|
+
aggr: HavingAggregation;
|
|
20
22
|
} & (O extends 'is null' | 'is not null' ? {
|
|
21
23
|
value?: never;
|
|
22
24
|
} : O extends 'in' | 'not in' ? {
|
|
@@ -26,10 +28,21 @@ export type HavingLeaf<T> = {
|
|
|
26
28
|
} : {
|
|
27
29
|
value: T[K];
|
|
28
30
|
});
|
|
29
|
-
}[
|
|
31
|
+
}[HavingComparisonOperator];
|
|
30
32
|
}[keyof T];
|
|
31
33
|
/**
|
|
32
|
-
* Having
|
|
33
|
-
* 聚合函数操作符
|
|
34
|
+
* Having 聚合函数
|
|
34
35
|
*/
|
|
35
|
-
export type
|
|
36
|
+
export type HavingAggregateFunction = BaseAggregateFunction;
|
|
37
|
+
export type HavingAggregation = {
|
|
38
|
+
func: HavingAggregateFunction;
|
|
39
|
+
quantile?: number;
|
|
40
|
+
};
|
|
41
|
+
/**
|
|
42
|
+
* Having 比较操作符
|
|
43
|
+
*/
|
|
44
|
+
export type HavingComparisonOperator = '=' | '!=' | '>' | '>=' | '<' | '<=' | 'between' | 'not between' | 'in' | 'not in' | 'is null' | 'is not null';
|
|
45
|
+
/**
|
|
46
|
+
* Having 操作符(仅比较操作符)
|
|
47
|
+
*/
|
|
48
|
+
export type HavingOperator = HavingComparisonOperator;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export type BaseAggregateFunction = 'count' | 'count_distinct' | 'sum' | 'avg' | 'min' | 'max' | 'variance' | '
|
|
1
|
+
export type BaseAggregateFunction = 'count' | 'count_distinct' | 'sum' | 'avg' | 'min' | 'max' | 'variance' | 'variance_pop' | 'stddev' | 'median' | 'quantile';
|
|
2
2
|
export type DateAggregateFunction = 'to_year' | 'to_quarter' | 'to_month' | 'to_week' | 'to_day' | 'to_hour' | 'to_minute' | 'to_second';
|
|
3
3
|
export type AggregateFunction = BaseAggregateFunction | DateAggregateFunction;
|
|
4
4
|
export type SelectItem<T> = {
|
|
@@ -3,4 +3,4 @@ export type { GroupBy } from './GroupBy';
|
|
|
3
3
|
export type { OrderBy } from './OrderBy';
|
|
4
4
|
export type { Select } from './Select';
|
|
5
5
|
export type { Where, WhereClause, WhereLeaf, WhereGroup } from './Where';
|
|
6
|
-
export type { Having, HavingClause, HavingLeaf, HavingGroup, HavingOperator } from './Having';
|
|
6
|
+
export type { Having, HavingClause, HavingLeaf, HavingGroup, HavingOperator, HavingAggregation, HavingAggregateFunction, HavingComparisonOperator, } from './Having';
|
package/dist/node/cjs/node.cjs
CHANGED
|
@@ -151,6 +151,36 @@ const applyGroupBy = (qb, fields)=>{
|
|
|
151
151
|
}
|
|
152
152
|
return qb;
|
|
153
153
|
};
|
|
154
|
+
const AGGREGATE_FUNCTIONS = [
|
|
155
|
+
'sum',
|
|
156
|
+
'avg',
|
|
157
|
+
'count',
|
|
158
|
+
'count_distinct',
|
|
159
|
+
'min',
|
|
160
|
+
'max',
|
|
161
|
+
'variance',
|
|
162
|
+
'variance_pop',
|
|
163
|
+
'stddev',
|
|
164
|
+
'median',
|
|
165
|
+
'quantile'
|
|
166
|
+
];
|
|
167
|
+
const isAggregateFunction = (func)=>AGGREGATE_FUNCTIONS.includes(func);
|
|
168
|
+
const toAggregateExpression = (func, field, aggr)=>{
|
|
169
|
+
switch(func){
|
|
170
|
+
case 'count':
|
|
171
|
+
return (0, external_kysely_namespaceObject.sql)`count(${external_kysely_namespaceObject.sql.ref(field)})`;
|
|
172
|
+
case 'count_distinct':
|
|
173
|
+
return (0, external_kysely_namespaceObject.sql)`count(distinct ${external_kysely_namespaceObject.sql.ref(field)})`;
|
|
174
|
+
case 'variance':
|
|
175
|
+
return (0, external_kysely_namespaceObject.sql)`var_samp(${external_kysely_namespaceObject.sql.ref(field)})`;
|
|
176
|
+
case 'variance_pop':
|
|
177
|
+
return (0, external_kysely_namespaceObject.sql)`var_pop(${external_kysely_namespaceObject.sql.ref(field)})`;
|
|
178
|
+
case 'quantile':
|
|
179
|
+
return (0, external_kysely_namespaceObject.sql)`quantile(${external_kysely_namespaceObject.sql.ref(field)}, ${aggr?.quantile ?? 0.5})`;
|
|
180
|
+
default:
|
|
181
|
+
return (0, external_kysely_namespaceObject.sql)`${external_kysely_namespaceObject.sql.raw(func)}(${external_kysely_namespaceObject.sql.ref(field)})`;
|
|
182
|
+
}
|
|
183
|
+
};
|
|
154
184
|
const applyHaving = (qb, having)=>{
|
|
155
185
|
if (!having) return qb;
|
|
156
186
|
const toRaw = (h)=>{
|
|
@@ -162,48 +192,40 @@ const applyHaving = (qb, having)=>{
|
|
|
162
192
|
const leaf = h;
|
|
163
193
|
const field = leaf.field;
|
|
164
194
|
const value = leaf.value;
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
'count',
|
|
170
|
-
'min',
|
|
171
|
-
'max'
|
|
172
|
-
].includes(op)) {
|
|
173
|
-
const aggrExpr = (0, external_kysely_namespaceObject.sql)`${external_kysely_namespaceObject.sql.raw(op.toLowerCase())}(${external_kysely_namespaceObject.sql.ref(field)})`;
|
|
174
|
-
return (0, external_kysely_namespaceObject.sql)`${aggrExpr} = ${external_kysely_namespaceObject.sql.val(value)}`;
|
|
175
|
-
}
|
|
176
|
-
switch(leaf.op){
|
|
195
|
+
if (!leaf.aggr?.func || !isAggregateFunction(leaf.aggr.func)) throw new Error(`Invalid having clause for field "${String(field)}": aggr.func is required`);
|
|
196
|
+
const compareOp = toSqlOperator(leaf.op.trim().toLowerCase());
|
|
197
|
+
const leftExpr = toAggregateExpression(leaf.aggr.func, field, leaf.aggr);
|
|
198
|
+
switch(compareOp){
|
|
177
199
|
case 'is null':
|
|
178
|
-
return (0, external_kysely_namespaceObject.sql)`${
|
|
200
|
+
return (0, external_kysely_namespaceObject.sql)`${leftExpr} is null`;
|
|
179
201
|
case 'is not null':
|
|
180
|
-
return (0, external_kysely_namespaceObject.sql)`${
|
|
202
|
+
return (0, external_kysely_namespaceObject.sql)`${leftExpr} is not null`;
|
|
181
203
|
case 'in':
|
|
182
204
|
{
|
|
183
205
|
const items = Array.isArray(value) ? value : [
|
|
184
206
|
value
|
|
185
207
|
];
|
|
186
|
-
return (0, external_kysely_namespaceObject.sql)`${
|
|
208
|
+
return (0, external_kysely_namespaceObject.sql)`${leftExpr} in (${external_kysely_namespaceObject.sql.join(items.map((v)=>external_kysely_namespaceObject.sql.val(v)))})`;
|
|
187
209
|
}
|
|
188
210
|
case 'not in':
|
|
189
211
|
{
|
|
190
212
|
const items = Array.isArray(value) ? value : [
|
|
191
213
|
value
|
|
192
214
|
];
|
|
193
|
-
return (0, external_kysely_namespaceObject.sql)`not ${
|
|
215
|
+
return (0, external_kysely_namespaceObject.sql)`not ${leftExpr} in (${external_kysely_namespaceObject.sql.join(items.map((v)=>external_kysely_namespaceObject.sql.val(v)))})`;
|
|
194
216
|
}
|
|
195
217
|
case 'between':
|
|
196
218
|
{
|
|
197
219
|
const [a, b] = value;
|
|
198
|
-
return (0, external_kysely_namespaceObject.sql)`${
|
|
220
|
+
return (0, external_kysely_namespaceObject.sql)`${leftExpr} between ${external_kysely_namespaceObject.sql.val(a)} and ${external_kysely_namespaceObject.sql.val(b)}`;
|
|
199
221
|
}
|
|
200
222
|
case 'not between':
|
|
201
223
|
{
|
|
202
224
|
const [a, b] = value;
|
|
203
|
-
return (0, external_kysely_namespaceObject.sql)`${
|
|
225
|
+
return (0, external_kysely_namespaceObject.sql)`${leftExpr} not between ${external_kysely_namespaceObject.sql.val(a)} and ${external_kysely_namespaceObject.sql.val(b)}`;
|
|
204
226
|
}
|
|
205
227
|
default:
|
|
206
|
-
return (0, external_kysely_namespaceObject.sql)`${
|
|
228
|
+
return (0, external_kysely_namespaceObject.sql)`${leftExpr} ${external_kysely_namespaceObject.sql.raw(compareOp)} ${external_kysely_namespaceObject.sql.val(value)}`;
|
|
207
229
|
}
|
|
208
230
|
};
|
|
209
231
|
return qb.having(toRaw(having));
|
|
@@ -242,12 +264,12 @@ const applySelect = (qb, select)=>{
|
|
|
242
264
|
'min',
|
|
243
265
|
'max',
|
|
244
266
|
'variance',
|
|
245
|
-
'
|
|
267
|
+
'variance_pop',
|
|
246
268
|
'stddev',
|
|
247
269
|
'median'
|
|
248
270
|
].includes(func)) {
|
|
249
271
|
if ('variance' === func) return (0, external_kysely_namespaceObject.sql)`var_samp(${expression})`.as(alias);
|
|
250
|
-
if ('
|
|
272
|
+
if ('variance_pop' === func) return (0, external_kysely_namespaceObject.sql)`var_pop(${expression})`.as(alias);
|
|
251
273
|
return (0, external_kysely_namespaceObject.sql)`${external_kysely_namespaceObject.sql.raw(func)}(${expression})`.as(alias);
|
|
252
274
|
}
|
|
253
275
|
if ('count' === func) return (0, external_kysely_namespaceObject.sql)`CAST(count(${expression}) AS INTEGER)`.as(alias);
|
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
import type { SelectQueryBuilder } from 'kysely';
|
|
2
|
-
import { Having, HavingClause } from '../../types';
|
|
2
|
+
import type { Having, HavingClause } from '../../types';
|
|
3
3
|
export declare const applyHaving: <DB, TB extends keyof DB & string, O, T>(qb: SelectQueryBuilder<DB, TB, O>, having?: Having<T> | HavingClause<T>) => SelectQueryBuilder<DB, TB, O>;
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
* Having 子句类型定义
|
|
3
3
|
* 用于聚合后的数据筛选
|
|
4
4
|
*/
|
|
5
|
+
import type { BaseAggregateFunction } from './Select';
|
|
5
6
|
export type Having<T> = HavingGroup<T>;
|
|
6
7
|
export type HavingGroup<T> = {
|
|
7
8
|
op: 'and' | 'or';
|
|
@@ -10,13 +11,14 @@ export type HavingGroup<T> = {
|
|
|
10
11
|
export type HavingClause<T> = HavingLeaf<T> | HavingGroup<T>;
|
|
11
12
|
/**
|
|
12
13
|
* Having 叶子节点
|
|
13
|
-
*
|
|
14
|
+
* 通过 aggr 指定聚合方式,通过 op 指定比较操作符
|
|
14
15
|
*/
|
|
15
16
|
export type HavingLeaf<T> = {
|
|
16
17
|
[K in keyof T]: {
|
|
17
|
-
[O in
|
|
18
|
+
[O in HavingComparisonOperator]: {
|
|
18
19
|
field: K;
|
|
19
20
|
op: O;
|
|
21
|
+
aggr: HavingAggregation;
|
|
20
22
|
} & (O extends 'is null' | 'is not null' ? {
|
|
21
23
|
value?: never;
|
|
22
24
|
} : O extends 'in' | 'not in' ? {
|
|
@@ -26,10 +28,21 @@ export type HavingLeaf<T> = {
|
|
|
26
28
|
} : {
|
|
27
29
|
value: T[K];
|
|
28
30
|
});
|
|
29
|
-
}[
|
|
31
|
+
}[HavingComparisonOperator];
|
|
30
32
|
}[keyof T];
|
|
31
33
|
/**
|
|
32
|
-
* Having
|
|
33
|
-
* 聚合函数操作符
|
|
34
|
+
* Having 聚合函数
|
|
34
35
|
*/
|
|
35
|
-
export type
|
|
36
|
+
export type HavingAggregateFunction = BaseAggregateFunction;
|
|
37
|
+
export type HavingAggregation = {
|
|
38
|
+
func: HavingAggregateFunction;
|
|
39
|
+
quantile?: number;
|
|
40
|
+
};
|
|
41
|
+
/**
|
|
42
|
+
* Having 比较操作符
|
|
43
|
+
*/
|
|
44
|
+
export type HavingComparisonOperator = '=' | '!=' | '>' | '>=' | '<' | '<=' | 'between' | 'not between' | 'in' | 'not in' | 'is null' | 'is not null';
|
|
45
|
+
/**
|
|
46
|
+
* Having 操作符(仅比较操作符)
|
|
47
|
+
*/
|
|
48
|
+
export type HavingOperator = HavingComparisonOperator;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export type BaseAggregateFunction = 'count' | 'count_distinct' | 'sum' | 'avg' | 'min' | 'max' | 'variance' | '
|
|
1
|
+
export type BaseAggregateFunction = 'count' | 'count_distinct' | 'sum' | 'avg' | 'min' | 'max' | 'variance' | 'variance_pop' | 'stddev' | 'median' | 'quantile';
|
|
2
2
|
export type DateAggregateFunction = 'to_year' | 'to_quarter' | 'to_month' | 'to_week' | 'to_day' | 'to_hour' | 'to_minute' | 'to_second';
|
|
3
3
|
export type AggregateFunction = BaseAggregateFunction | DateAggregateFunction;
|
|
4
4
|
export type SelectItem<T> = {
|
|
@@ -3,4 +3,4 @@ export type { GroupBy } from './GroupBy';
|
|
|
3
3
|
export type { OrderBy } from './OrderBy';
|
|
4
4
|
export type { Select } from './Select';
|
|
5
5
|
export type { Where, WhereClause, WhereLeaf, WhereGroup } from './Where';
|
|
6
|
-
export type { Having, HavingClause, HavingLeaf, HavingGroup, HavingOperator } from './Having';
|
|
6
|
+
export type { Having, HavingClause, HavingLeaf, HavingGroup, HavingOperator, HavingAggregation, HavingAggregateFunction, HavingComparisonOperator, } from './Having';
|
package/dist/node/esm/node.js
CHANGED
|
@@ -115,6 +115,36 @@ const applyGroupBy = (qb, fields)=>{
|
|
|
115
115
|
}
|
|
116
116
|
return qb;
|
|
117
117
|
};
|
|
118
|
+
const AGGREGATE_FUNCTIONS = [
|
|
119
|
+
'sum',
|
|
120
|
+
'avg',
|
|
121
|
+
'count',
|
|
122
|
+
'count_distinct',
|
|
123
|
+
'min',
|
|
124
|
+
'max',
|
|
125
|
+
'variance',
|
|
126
|
+
'variance_pop',
|
|
127
|
+
'stddev',
|
|
128
|
+
'median',
|
|
129
|
+
'quantile'
|
|
130
|
+
];
|
|
131
|
+
const isAggregateFunction = (func)=>AGGREGATE_FUNCTIONS.includes(func);
|
|
132
|
+
const toAggregateExpression = (func, field, aggr)=>{
|
|
133
|
+
switch(func){
|
|
134
|
+
case 'count':
|
|
135
|
+
return external_kysely_sql`count(${external_kysely_sql.ref(field)})`;
|
|
136
|
+
case 'count_distinct':
|
|
137
|
+
return external_kysely_sql`count(distinct ${external_kysely_sql.ref(field)})`;
|
|
138
|
+
case 'variance':
|
|
139
|
+
return external_kysely_sql`var_samp(${external_kysely_sql.ref(field)})`;
|
|
140
|
+
case 'variance_pop':
|
|
141
|
+
return external_kysely_sql`var_pop(${external_kysely_sql.ref(field)})`;
|
|
142
|
+
case 'quantile':
|
|
143
|
+
return external_kysely_sql`quantile(${external_kysely_sql.ref(field)}, ${aggr?.quantile ?? 0.5})`;
|
|
144
|
+
default:
|
|
145
|
+
return external_kysely_sql`${external_kysely_sql.raw(func)}(${external_kysely_sql.ref(field)})`;
|
|
146
|
+
}
|
|
147
|
+
};
|
|
118
148
|
const applyHaving = (qb, having)=>{
|
|
119
149
|
if (!having) return qb;
|
|
120
150
|
const toRaw = (h)=>{
|
|
@@ -126,48 +156,40 @@ const applyHaving = (qb, having)=>{
|
|
|
126
156
|
const leaf = h;
|
|
127
157
|
const field = leaf.field;
|
|
128
158
|
const value = leaf.value;
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
'count',
|
|
134
|
-
'min',
|
|
135
|
-
'max'
|
|
136
|
-
].includes(op)) {
|
|
137
|
-
const aggrExpr = external_kysely_sql`${external_kysely_sql.raw(op.toLowerCase())}(${external_kysely_sql.ref(field)})`;
|
|
138
|
-
return external_kysely_sql`${aggrExpr} = ${external_kysely_sql.val(value)}`;
|
|
139
|
-
}
|
|
140
|
-
switch(leaf.op){
|
|
159
|
+
if (!leaf.aggr?.func || !isAggregateFunction(leaf.aggr.func)) throw new Error(`Invalid having clause for field "${String(field)}": aggr.func is required`);
|
|
160
|
+
const compareOp = toSqlOperator(leaf.op.trim().toLowerCase());
|
|
161
|
+
const leftExpr = toAggregateExpression(leaf.aggr.func, field, leaf.aggr);
|
|
162
|
+
switch(compareOp){
|
|
141
163
|
case 'is null':
|
|
142
|
-
return external_kysely_sql`${
|
|
164
|
+
return external_kysely_sql`${leftExpr} is null`;
|
|
143
165
|
case 'is not null':
|
|
144
|
-
return external_kysely_sql`${
|
|
166
|
+
return external_kysely_sql`${leftExpr} is not null`;
|
|
145
167
|
case 'in':
|
|
146
168
|
{
|
|
147
169
|
const items = Array.isArray(value) ? value : [
|
|
148
170
|
value
|
|
149
171
|
];
|
|
150
|
-
return external_kysely_sql`${
|
|
172
|
+
return external_kysely_sql`${leftExpr} in (${external_kysely_sql.join(items.map((v)=>external_kysely_sql.val(v)))})`;
|
|
151
173
|
}
|
|
152
174
|
case 'not in':
|
|
153
175
|
{
|
|
154
176
|
const items = Array.isArray(value) ? value : [
|
|
155
177
|
value
|
|
156
178
|
];
|
|
157
|
-
return external_kysely_sql`not ${
|
|
179
|
+
return external_kysely_sql`not ${leftExpr} in (${external_kysely_sql.join(items.map((v)=>external_kysely_sql.val(v)))})`;
|
|
158
180
|
}
|
|
159
181
|
case 'between':
|
|
160
182
|
{
|
|
161
183
|
const [a, b] = value;
|
|
162
|
-
return external_kysely_sql`${
|
|
184
|
+
return external_kysely_sql`${leftExpr} between ${external_kysely_sql.val(a)} and ${external_kysely_sql.val(b)}`;
|
|
163
185
|
}
|
|
164
186
|
case 'not between':
|
|
165
187
|
{
|
|
166
188
|
const [a, b] = value;
|
|
167
|
-
return external_kysely_sql`${
|
|
189
|
+
return external_kysely_sql`${leftExpr} not between ${external_kysely_sql.val(a)} and ${external_kysely_sql.val(b)}`;
|
|
168
190
|
}
|
|
169
191
|
default:
|
|
170
|
-
return external_kysely_sql`${
|
|
192
|
+
return external_kysely_sql`${leftExpr} ${external_kysely_sql.raw(compareOp)} ${external_kysely_sql.val(value)}`;
|
|
171
193
|
}
|
|
172
194
|
};
|
|
173
195
|
return qb.having(toRaw(having));
|
|
@@ -205,12 +227,12 @@ const applySelect = (qb, select)=>{
|
|
|
205
227
|
'min',
|
|
206
228
|
'max',
|
|
207
229
|
'variance',
|
|
208
|
-
'
|
|
230
|
+
'variance_pop',
|
|
209
231
|
'stddev',
|
|
210
232
|
'median'
|
|
211
233
|
].includes(func)) {
|
|
212
234
|
if ('variance' === func) return external_kysely_sql`var_samp(${expression})`.as(alias);
|
|
213
|
-
if ('
|
|
235
|
+
if ('variance_pop' === func) return external_kysely_sql`var_pop(${expression})`.as(alias);
|
|
214
236
|
return external_kysely_sql`${external_kysely_sql.raw(func)}(${expression})`.as(alias);
|
|
215
237
|
}
|
|
216
238
|
if ('count' === func) return external_kysely_sql`CAST(count(${expression}) AS INTEGER)`.as(alias);
|
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
import type { SelectQueryBuilder } from 'kysely';
|
|
2
|
-
import { Having, HavingClause } from '../../types';
|
|
2
|
+
import type { Having, HavingClause } from '../../types';
|
|
3
3
|
export declare const applyHaving: <DB, TB extends keyof DB & string, O, T>(qb: SelectQueryBuilder<DB, TB, O>, having?: Having<T> | HavingClause<T>) => SelectQueryBuilder<DB, TB, O>;
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
* Having 子句类型定义
|
|
3
3
|
* 用于聚合后的数据筛选
|
|
4
4
|
*/
|
|
5
|
+
import type { BaseAggregateFunction } from './Select';
|
|
5
6
|
export type Having<T> = HavingGroup<T>;
|
|
6
7
|
export type HavingGroup<T> = {
|
|
7
8
|
op: 'and' | 'or';
|
|
@@ -10,13 +11,14 @@ export type HavingGroup<T> = {
|
|
|
10
11
|
export type HavingClause<T> = HavingLeaf<T> | HavingGroup<T>;
|
|
11
12
|
/**
|
|
12
13
|
* Having 叶子节点
|
|
13
|
-
*
|
|
14
|
+
* 通过 aggr 指定聚合方式,通过 op 指定比较操作符
|
|
14
15
|
*/
|
|
15
16
|
export type HavingLeaf<T> = {
|
|
16
17
|
[K in keyof T]: {
|
|
17
|
-
[O in
|
|
18
|
+
[O in HavingComparisonOperator]: {
|
|
18
19
|
field: K;
|
|
19
20
|
op: O;
|
|
21
|
+
aggr: HavingAggregation;
|
|
20
22
|
} & (O extends 'is null' | 'is not null' ? {
|
|
21
23
|
value?: never;
|
|
22
24
|
} : O extends 'in' | 'not in' ? {
|
|
@@ -26,10 +28,21 @@ export type HavingLeaf<T> = {
|
|
|
26
28
|
} : {
|
|
27
29
|
value: T[K];
|
|
28
30
|
});
|
|
29
|
-
}[
|
|
31
|
+
}[HavingComparisonOperator];
|
|
30
32
|
}[keyof T];
|
|
31
33
|
/**
|
|
32
|
-
* Having
|
|
33
|
-
* 聚合函数操作符
|
|
34
|
+
* Having 聚合函数
|
|
34
35
|
*/
|
|
35
|
-
export type
|
|
36
|
+
export type HavingAggregateFunction = BaseAggregateFunction;
|
|
37
|
+
export type HavingAggregation = {
|
|
38
|
+
func: HavingAggregateFunction;
|
|
39
|
+
quantile?: number;
|
|
40
|
+
};
|
|
41
|
+
/**
|
|
42
|
+
* Having 比较操作符
|
|
43
|
+
*/
|
|
44
|
+
export type HavingComparisonOperator = '=' | '!=' | '>' | '>=' | '<' | '<=' | 'between' | 'not between' | 'in' | 'not in' | 'is null' | 'is not null';
|
|
45
|
+
/**
|
|
46
|
+
* Having 操作符(仅比较操作符)
|
|
47
|
+
*/
|
|
48
|
+
export type HavingOperator = HavingComparisonOperator;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export type BaseAggregateFunction = 'count' | 'count_distinct' | 'sum' | 'avg' | 'min' | 'max' | 'variance' | '
|
|
1
|
+
export type BaseAggregateFunction = 'count' | 'count_distinct' | 'sum' | 'avg' | 'min' | 'max' | 'variance' | 'variance_pop' | 'stddev' | 'median' | 'quantile';
|
|
2
2
|
export type DateAggregateFunction = 'to_year' | 'to_quarter' | 'to_month' | 'to_week' | 'to_day' | 'to_hour' | 'to_minute' | 'to_second';
|
|
3
3
|
export type AggregateFunction = BaseAggregateFunction | DateAggregateFunction;
|
|
4
4
|
export type SelectItem<T> = {
|
|
@@ -3,4 +3,4 @@ export type { GroupBy } from './GroupBy';
|
|
|
3
3
|
export type { OrderBy } from './OrderBy';
|
|
4
4
|
export type { Select } from './Select';
|
|
5
5
|
export type { Where, WhereClause, WhereLeaf, WhereGroup } from './Where';
|
|
6
|
-
export type { Having, HavingClause, HavingLeaf, HavingGroup, HavingOperator } from './Having';
|
|
6
|
+
export type { Having, HavingClause, HavingLeaf, HavingGroup, HavingOperator, HavingAggregation, HavingAggregateFunction, HavingComparisonOperator, } from './Having';
|