@visactor/vquery 0.4.8 → 0.4.10
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 +92 -9
- package/dist/browser/esm/sql-builder/builders/having.d.ts +3 -0
- package/dist/browser/esm/sql-builder/builders/index.d.ts +2 -0
- package/dist/browser/esm/sql-builder/builders/order.d.ts +7 -0
- package/dist/browser/esm/sql-builder/builders/where.d.ts +2 -2
- package/dist/browser/esm/sql-builder/dslToSQL.d.ts +1 -1
- package/dist/browser/esm/types/dsl/Having.d.ts +35 -0
- package/dist/browser/esm/types/dsl/QueryDSL.d.ts +2 -0
- package/dist/browser/esm/types/dsl/index.d.ts +1 -0
- package/dist/node/cjs/node.cjs +92 -9
- package/dist/node/cjs/sql-builder/builders/having.d.ts +3 -0
- package/dist/node/cjs/sql-builder/builders/index.d.ts +2 -0
- package/dist/node/cjs/sql-builder/builders/order.d.ts +7 -0
- package/dist/node/cjs/sql-builder/builders/where.d.ts +2 -2
- package/dist/node/cjs/sql-builder/dslToSQL.d.ts +1 -1
- package/dist/node/cjs/types/dsl/Having.d.ts +35 -0
- package/dist/node/cjs/types/dsl/QueryDSL.d.ts +2 -0
- package/dist/node/cjs/types/dsl/index.d.ts +1 -0
- package/dist/node/esm/node.js +92 -9
- package/dist/node/esm/sql-builder/builders/having.d.ts +3 -0
- package/dist/node/esm/sql-builder/builders/index.d.ts +2 -0
- package/dist/node/esm/sql-builder/builders/order.d.ts +7 -0
- package/dist/node/esm/sql-builder/builders/where.d.ts +2 -2
- package/dist/node/esm/sql-builder/dslToSQL.d.ts +1 -1
- package/dist/node/esm/types/dsl/Having.d.ts +35 -0
- package/dist/node/esm/types/dsl/QueryDSL.d.ts +2 -0
- package/dist/node/esm/types/dsl/index.d.ts +1 -0
- package/package.json +1 -1
|
@@ -51,7 +51,17 @@ const inlineParameters = (sql, params)=>{
|
|
|
51
51
|
});
|
|
52
52
|
return sql;
|
|
53
53
|
};
|
|
54
|
-
const
|
|
54
|
+
const operatorMap = {
|
|
55
|
+
gt: '>',
|
|
56
|
+
gte: '>=',
|
|
57
|
+
lt: '<',
|
|
58
|
+
lte: '<=',
|
|
59
|
+
eq: '=',
|
|
60
|
+
neq: '!='
|
|
61
|
+
};
|
|
62
|
+
const toSqlOperator = (op)=>operatorMap[op] ?? op;
|
|
63
|
+
const applyWhere = (qb, where)=>{
|
|
64
|
+
if (!where) return qb;
|
|
55
65
|
const toRaw = (w)=>{
|
|
56
66
|
if ('op' in w && 'conditions' in w) {
|
|
57
67
|
const parts = w.conditions.map((c)=>toRaw(c));
|
|
@@ -61,6 +71,7 @@ const applyWhere = (where)=>{
|
|
|
61
71
|
const leaf = w;
|
|
62
72
|
const field = leaf.field;
|
|
63
73
|
const value = leaf.value;
|
|
74
|
+
const sqlOp = toSqlOperator(leaf.op);
|
|
64
75
|
switch(leaf.op){
|
|
65
76
|
case 'is null':
|
|
66
77
|
return external_kysely_sql`${external_kysely_sql.ref(field)} is null`;
|
|
@@ -91,10 +102,10 @@ const applyWhere = (where)=>{
|
|
|
91
102
|
return external_kysely_sql`${external_kysely_sql.ref(field)} not between ${external_kysely_sql.val(a)} and ${external_kysely_sql.val(b)}`;
|
|
92
103
|
}
|
|
93
104
|
default:
|
|
94
|
-
return external_kysely_sql`${external_kysely_sql.ref(field)} ${external_kysely_sql.raw(
|
|
105
|
+
return external_kysely_sql`${external_kysely_sql.ref(field)} ${external_kysely_sql.raw(sqlOp)} ${external_kysely_sql.val(value)}`;
|
|
95
106
|
}
|
|
96
107
|
};
|
|
97
|
-
return toRaw(where);
|
|
108
|
+
return qb.where(toRaw(where));
|
|
98
109
|
};
|
|
99
110
|
const applyGroupBy = (qb, fields)=>{
|
|
100
111
|
if (fields && fields.length > 0) {
|
|
@@ -103,10 +114,82 @@ const applyGroupBy = (qb, fields)=>{
|
|
|
103
114
|
}
|
|
104
115
|
return qb;
|
|
105
116
|
};
|
|
117
|
+
const having_operatorMap = {
|
|
118
|
+
gt: '>',
|
|
119
|
+
gte: '>=',
|
|
120
|
+
lt: '<',
|
|
121
|
+
lte: '<=',
|
|
122
|
+
eq: '=',
|
|
123
|
+
neq: '!='
|
|
124
|
+
};
|
|
125
|
+
const having_toSqlOperator = (op)=>having_operatorMap[op] ?? op;
|
|
126
|
+
const applyHaving = (qb, having)=>{
|
|
127
|
+
if (!having) return qb;
|
|
128
|
+
const toRaw = (h)=>{
|
|
129
|
+
if ('op' in h && 'conditions' in h) {
|
|
130
|
+
const parts = h.conditions.map((c)=>toRaw(c));
|
|
131
|
+
const sep = external_kysely_sql` ${external_kysely_sql.raw(h.op)} `;
|
|
132
|
+
return external_kysely_sql`(${external_kysely_sql.join(parts, sep)})`;
|
|
133
|
+
}
|
|
134
|
+
const leaf = h;
|
|
135
|
+
const field = leaf.field;
|
|
136
|
+
const value = leaf.value;
|
|
137
|
+
const op = having_toSqlOperator(leaf.op);
|
|
138
|
+
if ([
|
|
139
|
+
'sum',
|
|
140
|
+
'avg',
|
|
141
|
+
'count',
|
|
142
|
+
'min',
|
|
143
|
+
'max'
|
|
144
|
+
].includes(op)) {
|
|
145
|
+
const aggrExpr = external_kysely_sql`${external_kysely_sql.raw(op.toLowerCase())}(${external_kysely_sql.ref(field)})`;
|
|
146
|
+
return external_kysely_sql`${aggrExpr} = ${external_kysely_sql.val(value)}`;
|
|
147
|
+
}
|
|
148
|
+
switch(leaf.op){
|
|
149
|
+
case 'is null':
|
|
150
|
+
return external_kysely_sql`${external_kysely_sql.ref(field)} is null`;
|
|
151
|
+
case 'is not null':
|
|
152
|
+
return external_kysely_sql`${external_kysely_sql.ref(field)} is not null`;
|
|
153
|
+
case 'in':
|
|
154
|
+
{
|
|
155
|
+
const items = Array.isArray(value) ? value : [
|
|
156
|
+
value
|
|
157
|
+
];
|
|
158
|
+
return external_kysely_sql`${external_kysely_sql.ref(field)} in (${external_kysely_sql.join(items.map((v)=>external_kysely_sql.val(v)))})`;
|
|
159
|
+
}
|
|
160
|
+
case 'not in':
|
|
161
|
+
{
|
|
162
|
+
const items = Array.isArray(value) ? value : [
|
|
163
|
+
value
|
|
164
|
+
];
|
|
165
|
+
return external_kysely_sql`not ${external_kysely_sql.ref(field)} in (${external_kysely_sql.join(items.map((v)=>external_kysely_sql.val(v)))})`;
|
|
166
|
+
}
|
|
167
|
+
case 'between':
|
|
168
|
+
{
|
|
169
|
+
const [a, b] = value;
|
|
170
|
+
return external_kysely_sql`${external_kysely_sql.ref(field)} between ${external_kysely_sql.val(a)} and ${external_kysely_sql.val(b)}`;
|
|
171
|
+
}
|
|
172
|
+
case 'not between':
|
|
173
|
+
{
|
|
174
|
+
const [a, b] = value;
|
|
175
|
+
return external_kysely_sql`${external_kysely_sql.ref(field)} not between ${external_kysely_sql.val(a)} and ${external_kysely_sql.val(b)}`;
|
|
176
|
+
}
|
|
177
|
+
default:
|
|
178
|
+
return external_kysely_sql`${external_kysely_sql.ref(field)} ${external_kysely_sql.raw(op)} ${external_kysely_sql.val(value)}`;
|
|
179
|
+
}
|
|
180
|
+
};
|
|
181
|
+
return qb.having(toRaw(having));
|
|
182
|
+
};
|
|
106
183
|
const applyLimit = (qb, limit)=>{
|
|
107
184
|
if (limit && 'number' == typeof limit) qb = qb.limit(limit);
|
|
108
185
|
return qb;
|
|
109
186
|
};
|
|
187
|
+
const applyOrder = (qb, orderBy)=>{
|
|
188
|
+
if (orderBy && orderBy.length > 0) orderBy.forEach((o)=>{
|
|
189
|
+
qb = qb.orderBy(o.field, o.order ?? 'asc');
|
|
190
|
+
});
|
|
191
|
+
return qb;
|
|
192
|
+
};
|
|
110
193
|
const DATE_FORMAT_MAP = {
|
|
111
194
|
year: '%Y',
|
|
112
195
|
month: '%Y-%m',
|
|
@@ -121,9 +204,9 @@ const applySelect = (qb, select)=>{
|
|
|
121
204
|
if (isSelectItem(item)) {
|
|
122
205
|
const field = item.field;
|
|
123
206
|
const expression = eb.ref(field);
|
|
207
|
+
const alias = item.alias ?? field;
|
|
124
208
|
if (item.aggr) {
|
|
125
209
|
const { func } = item.aggr;
|
|
126
|
-
const alias = item.alias ?? field;
|
|
127
210
|
if ([
|
|
128
211
|
'avg',
|
|
129
212
|
'sum',
|
|
@@ -150,7 +233,6 @@ const applySelect = (qb, select)=>{
|
|
|
150
233
|
if ('quarter' === dateTrunc) return external_kysely_sql`strftime(CAST(${expression} AS TIMESTAMP), '%Y') || '-Q' || date_part('quarter', CAST(${expression} AS TIMESTAMP))`.as(alias);
|
|
151
234
|
}
|
|
152
235
|
}
|
|
153
|
-
const alias = item.alias ?? field;
|
|
154
236
|
return expression.as(alias);
|
|
155
237
|
}
|
|
156
238
|
return item;
|
|
@@ -163,12 +245,13 @@ const convertDSLToSQL = (dsl, tableName)=>{
|
|
|
163
245
|
});
|
|
164
246
|
let qb = db.selectFrom(tableName);
|
|
165
247
|
qb = applySelect(qb, dsl.select);
|
|
166
|
-
|
|
248
|
+
qb = applyWhere(qb, dsl.where);
|
|
167
249
|
qb = applyGroupBy(qb, dsl.groupBy);
|
|
168
|
-
|
|
250
|
+
qb = applyHaving(qb, dsl.having);
|
|
251
|
+
qb = applyOrder(qb, dsl.orderBy);
|
|
169
252
|
qb = applyLimit(qb, dsl.limit);
|
|
170
|
-
const
|
|
171
|
-
return inlineParameters(
|
|
253
|
+
const { sql, parameters } = qb.compile();
|
|
254
|
+
return inlineParameters(sql, parameters);
|
|
172
255
|
};
|
|
173
256
|
const READ_FUNCTION_MAP = {
|
|
174
257
|
csv: 'read_csv_auto',
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import type { SelectQueryBuilder } from 'kysely';
|
|
2
|
+
import { Having, HavingClause } from '../../types';
|
|
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>;
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { SelectQueryBuilder } from 'kysely';
|
|
2
|
+
type OrderByItem = {
|
|
3
|
+
field: string;
|
|
4
|
+
order?: 'asc' | 'desc';
|
|
5
|
+
};
|
|
6
|
+
export declare const applyOrder: <DB, TB extends keyof DB & string, O>(qb: SelectQueryBuilder<DB, TB, O>, orderBy?: Array<OrderByItem>) => SelectQueryBuilder<DB, TB, O>;
|
|
7
|
+
export {};
|
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
import { Where, WhereClause } from '../../types';
|
|
2
|
-
import type {
|
|
3
|
-
export declare const applyWhere: <T>(
|
|
2
|
+
import type { SelectQueryBuilder } from 'kysely';
|
|
3
|
+
export declare const applyWhere: <DB, TB extends keyof DB & string, O, T>(qb: SelectQueryBuilder<DB, TB, O>, where?: Where<T> | WhereClause<T>) => SelectQueryBuilder<DB, TB, O>;
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { QueryDSL, VQueryDSL } from '../types';
|
|
1
|
+
import type { QueryDSL, VQueryDSL } from '../types';
|
|
2
2
|
export declare const convertDSLToSQL: <T, TableName extends string>(dsl: QueryDSL<T> | VQueryDSL<T>, tableName: TableName) => string;
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Having 子句类型定义
|
|
3
|
+
* 用于聚合后的数据筛选
|
|
4
|
+
*/
|
|
5
|
+
export type Having<T> = HavingGroup<T>;
|
|
6
|
+
export type HavingGroup<T> = {
|
|
7
|
+
op: 'and' | 'or';
|
|
8
|
+
conditions: Array<HavingClause<T>>;
|
|
9
|
+
};
|
|
10
|
+
export type HavingClause<T> = HavingLeaf<T> | HavingGroup<T>;
|
|
11
|
+
/**
|
|
12
|
+
* Having 叶子节点
|
|
13
|
+
* 支持聚合函数操作符
|
|
14
|
+
*/
|
|
15
|
+
export type HavingLeaf<T> = {
|
|
16
|
+
[K in keyof T]: {
|
|
17
|
+
[O in HavingOperator]: {
|
|
18
|
+
field: K;
|
|
19
|
+
op: O;
|
|
20
|
+
} & (O extends 'is null' | 'is not null' ? {
|
|
21
|
+
value?: never;
|
|
22
|
+
} : O extends 'in' | 'not in' ? {
|
|
23
|
+
value: T[K] | T[K][];
|
|
24
|
+
} : O extends 'between' | 'not between' ? {
|
|
25
|
+
value: [T[K], T[K]];
|
|
26
|
+
} : {
|
|
27
|
+
value: T[K];
|
|
28
|
+
});
|
|
29
|
+
}[HavingOperator];
|
|
30
|
+
}[keyof T];
|
|
31
|
+
/**
|
|
32
|
+
* Having 操作符
|
|
33
|
+
* 聚合函数操作符
|
|
34
|
+
*/
|
|
35
|
+
export type HavingOperator = 'sum' | 'avg' | 'count' | 'min' | 'max' | '=' | '!=' | '>' | '>=' | '<' | '<=' | 'between' | 'not between' | 'in' | 'not in' | 'is null' | 'is not null';
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { GroupBy } from './GroupBy';
|
|
2
|
+
import { Having } from './Having';
|
|
2
3
|
import { OrderBy } from './OrderBy';
|
|
3
4
|
import { Select } from './Select';
|
|
4
5
|
import { Where } from './Where';
|
|
@@ -6,6 +7,7 @@ export interface QueryDSL<Table> {
|
|
|
6
7
|
select: Select<Table>;
|
|
7
8
|
where?: Where<Table>;
|
|
8
9
|
groupBy?: GroupBy<Table>;
|
|
10
|
+
having?: Having<Table>;
|
|
9
11
|
orderBy?: OrderBy<Table>;
|
|
10
12
|
limit?: number;
|
|
11
13
|
}
|
|
@@ -3,3 +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';
|
package/dist/node/cjs/node.cjs
CHANGED
|
@@ -89,7 +89,17 @@ const inlineParameters = (sql, params)=>{
|
|
|
89
89
|
});
|
|
90
90
|
return sql;
|
|
91
91
|
};
|
|
92
|
-
const
|
|
92
|
+
const operatorMap = {
|
|
93
|
+
gt: '>',
|
|
94
|
+
gte: '>=',
|
|
95
|
+
lt: '<',
|
|
96
|
+
lte: '<=',
|
|
97
|
+
eq: '=',
|
|
98
|
+
neq: '!='
|
|
99
|
+
};
|
|
100
|
+
const toSqlOperator = (op)=>operatorMap[op] ?? op;
|
|
101
|
+
const applyWhere = (qb, where)=>{
|
|
102
|
+
if (!where) return qb;
|
|
93
103
|
const toRaw = (w)=>{
|
|
94
104
|
if ('op' in w && 'conditions' in w) {
|
|
95
105
|
const parts = w.conditions.map((c)=>toRaw(c));
|
|
@@ -99,6 +109,7 @@ const applyWhere = (where)=>{
|
|
|
99
109
|
const leaf = w;
|
|
100
110
|
const field = leaf.field;
|
|
101
111
|
const value = leaf.value;
|
|
112
|
+
const sqlOp = toSqlOperator(leaf.op);
|
|
102
113
|
switch(leaf.op){
|
|
103
114
|
case 'is null':
|
|
104
115
|
return (0, external_kysely_namespaceObject.sql)`${external_kysely_namespaceObject.sql.ref(field)} is null`;
|
|
@@ -129,10 +140,10 @@ const applyWhere = (where)=>{
|
|
|
129
140
|
return (0, external_kysely_namespaceObject.sql)`${external_kysely_namespaceObject.sql.ref(field)} not between ${external_kysely_namespaceObject.sql.val(a)} and ${external_kysely_namespaceObject.sql.val(b)}`;
|
|
130
141
|
}
|
|
131
142
|
default:
|
|
132
|
-
return (0, external_kysely_namespaceObject.sql)`${external_kysely_namespaceObject.sql.ref(field)} ${external_kysely_namespaceObject.sql.raw(
|
|
143
|
+
return (0, external_kysely_namespaceObject.sql)`${external_kysely_namespaceObject.sql.ref(field)} ${external_kysely_namespaceObject.sql.raw(sqlOp)} ${external_kysely_namespaceObject.sql.val(value)}`;
|
|
133
144
|
}
|
|
134
145
|
};
|
|
135
|
-
return toRaw(where);
|
|
146
|
+
return qb.where(toRaw(where));
|
|
136
147
|
};
|
|
137
148
|
const applyGroupBy = (qb, fields)=>{
|
|
138
149
|
if (fields && fields.length > 0) {
|
|
@@ -141,10 +152,82 @@ const applyGroupBy = (qb, fields)=>{
|
|
|
141
152
|
}
|
|
142
153
|
return qb;
|
|
143
154
|
};
|
|
155
|
+
const having_operatorMap = {
|
|
156
|
+
gt: '>',
|
|
157
|
+
gte: '>=',
|
|
158
|
+
lt: '<',
|
|
159
|
+
lte: '<=',
|
|
160
|
+
eq: '=',
|
|
161
|
+
neq: '!='
|
|
162
|
+
};
|
|
163
|
+
const having_toSqlOperator = (op)=>having_operatorMap[op] ?? op;
|
|
164
|
+
const applyHaving = (qb, having)=>{
|
|
165
|
+
if (!having) return qb;
|
|
166
|
+
const toRaw = (h)=>{
|
|
167
|
+
if ('op' in h && 'conditions' in h) {
|
|
168
|
+
const parts = h.conditions.map((c)=>toRaw(c));
|
|
169
|
+
const sep = (0, external_kysely_namespaceObject.sql)` ${external_kysely_namespaceObject.sql.raw(h.op)} `;
|
|
170
|
+
return (0, external_kysely_namespaceObject.sql)`(${external_kysely_namespaceObject.sql.join(parts, sep)})`;
|
|
171
|
+
}
|
|
172
|
+
const leaf = h;
|
|
173
|
+
const field = leaf.field;
|
|
174
|
+
const value = leaf.value;
|
|
175
|
+
const op = having_toSqlOperator(leaf.op);
|
|
176
|
+
if ([
|
|
177
|
+
'sum',
|
|
178
|
+
'avg',
|
|
179
|
+
'count',
|
|
180
|
+
'min',
|
|
181
|
+
'max'
|
|
182
|
+
].includes(op)) {
|
|
183
|
+
const aggrExpr = (0, external_kysely_namespaceObject.sql)`${external_kysely_namespaceObject.sql.raw(op.toLowerCase())}(${external_kysely_namespaceObject.sql.ref(field)})`;
|
|
184
|
+
return (0, external_kysely_namespaceObject.sql)`${aggrExpr} = ${external_kysely_namespaceObject.sql.val(value)}`;
|
|
185
|
+
}
|
|
186
|
+
switch(leaf.op){
|
|
187
|
+
case 'is null':
|
|
188
|
+
return (0, external_kysely_namespaceObject.sql)`${external_kysely_namespaceObject.sql.ref(field)} is null`;
|
|
189
|
+
case 'is not null':
|
|
190
|
+
return (0, external_kysely_namespaceObject.sql)`${external_kysely_namespaceObject.sql.ref(field)} is not null`;
|
|
191
|
+
case 'in':
|
|
192
|
+
{
|
|
193
|
+
const items = Array.isArray(value) ? value : [
|
|
194
|
+
value
|
|
195
|
+
];
|
|
196
|
+
return (0, external_kysely_namespaceObject.sql)`${external_kysely_namespaceObject.sql.ref(field)} in (${external_kysely_namespaceObject.sql.join(items.map((v)=>external_kysely_namespaceObject.sql.val(v)))})`;
|
|
197
|
+
}
|
|
198
|
+
case 'not in':
|
|
199
|
+
{
|
|
200
|
+
const items = Array.isArray(value) ? value : [
|
|
201
|
+
value
|
|
202
|
+
];
|
|
203
|
+
return (0, external_kysely_namespaceObject.sql)`not ${external_kysely_namespaceObject.sql.ref(field)} in (${external_kysely_namespaceObject.sql.join(items.map((v)=>external_kysely_namespaceObject.sql.val(v)))})`;
|
|
204
|
+
}
|
|
205
|
+
case 'between':
|
|
206
|
+
{
|
|
207
|
+
const [a, b] = value;
|
|
208
|
+
return (0, external_kysely_namespaceObject.sql)`${external_kysely_namespaceObject.sql.ref(field)} between ${external_kysely_namespaceObject.sql.val(a)} and ${external_kysely_namespaceObject.sql.val(b)}`;
|
|
209
|
+
}
|
|
210
|
+
case 'not between':
|
|
211
|
+
{
|
|
212
|
+
const [a, b] = value;
|
|
213
|
+
return (0, external_kysely_namespaceObject.sql)`${external_kysely_namespaceObject.sql.ref(field)} not between ${external_kysely_namespaceObject.sql.val(a)} and ${external_kysely_namespaceObject.sql.val(b)}`;
|
|
214
|
+
}
|
|
215
|
+
default:
|
|
216
|
+
return (0, external_kysely_namespaceObject.sql)`${external_kysely_namespaceObject.sql.ref(field)} ${external_kysely_namespaceObject.sql.raw(op)} ${external_kysely_namespaceObject.sql.val(value)}`;
|
|
217
|
+
}
|
|
218
|
+
};
|
|
219
|
+
return qb.having(toRaw(having));
|
|
220
|
+
};
|
|
144
221
|
const applyLimit = (qb, limit)=>{
|
|
145
222
|
if (limit && 'number' == typeof limit) qb = qb.limit(limit);
|
|
146
223
|
return qb;
|
|
147
224
|
};
|
|
225
|
+
const applyOrder = (qb, orderBy)=>{
|
|
226
|
+
if (orderBy && orderBy.length > 0) orderBy.forEach((o)=>{
|
|
227
|
+
qb = qb.orderBy(o.field, o.order ?? 'asc');
|
|
228
|
+
});
|
|
229
|
+
return qb;
|
|
230
|
+
};
|
|
148
231
|
const DATE_FORMAT_MAP = {
|
|
149
232
|
year: '%Y',
|
|
150
233
|
month: '%Y-%m',
|
|
@@ -159,9 +242,9 @@ const applySelect = (qb, select)=>{
|
|
|
159
242
|
if (isSelectItem(item)) {
|
|
160
243
|
const field = item.field;
|
|
161
244
|
const expression = eb.ref(field);
|
|
245
|
+
const alias = item.alias ?? field;
|
|
162
246
|
if (item.aggr) {
|
|
163
247
|
const { func } = item.aggr;
|
|
164
|
-
const alias = item.alias ?? field;
|
|
165
248
|
if ([
|
|
166
249
|
'avg',
|
|
167
250
|
'sum',
|
|
@@ -188,7 +271,6 @@ const applySelect = (qb, select)=>{
|
|
|
188
271
|
if ('quarter' === dateTrunc) return (0, external_kysely_namespaceObject.sql)`strftime(CAST(${expression} AS TIMESTAMP), '%Y') || '-Q' || date_part('quarter', CAST(${expression} AS TIMESTAMP))`.as(alias);
|
|
189
272
|
}
|
|
190
273
|
}
|
|
191
|
-
const alias = item.alias ?? field;
|
|
192
274
|
return expression.as(alias);
|
|
193
275
|
}
|
|
194
276
|
return item;
|
|
@@ -201,12 +283,13 @@ const convertDSLToSQL = (dsl, tableName)=>{
|
|
|
201
283
|
});
|
|
202
284
|
let qb = db.selectFrom(tableName);
|
|
203
285
|
qb = applySelect(qb, dsl.select);
|
|
204
|
-
|
|
286
|
+
qb = applyWhere(qb, dsl.where);
|
|
205
287
|
qb = applyGroupBy(qb, dsl.groupBy);
|
|
206
|
-
|
|
288
|
+
qb = applyHaving(qb, dsl.having);
|
|
289
|
+
qb = applyOrder(qb, dsl.orderBy);
|
|
207
290
|
qb = applyLimit(qb, dsl.limit);
|
|
208
|
-
const
|
|
209
|
-
return inlineParameters(
|
|
291
|
+
const { sql, parameters } = qb.compile();
|
|
292
|
+
return inlineParameters(sql, parameters);
|
|
210
293
|
};
|
|
211
294
|
const READ_FUNCTION_MAP = {
|
|
212
295
|
csv: 'read_csv_auto',
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import type { SelectQueryBuilder } from 'kysely';
|
|
2
|
+
import { Having, HavingClause } from '../../types';
|
|
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>;
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { SelectQueryBuilder } from 'kysely';
|
|
2
|
+
type OrderByItem = {
|
|
3
|
+
field: string;
|
|
4
|
+
order?: 'asc' | 'desc';
|
|
5
|
+
};
|
|
6
|
+
export declare const applyOrder: <DB, TB extends keyof DB & string, O>(qb: SelectQueryBuilder<DB, TB, O>, orderBy?: Array<OrderByItem>) => SelectQueryBuilder<DB, TB, O>;
|
|
7
|
+
export {};
|
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
import { Where, WhereClause } from '../../types';
|
|
2
|
-
import type {
|
|
3
|
-
export declare const applyWhere: <T>(
|
|
2
|
+
import type { SelectQueryBuilder } from 'kysely';
|
|
3
|
+
export declare const applyWhere: <DB, TB extends keyof DB & string, O, T>(qb: SelectQueryBuilder<DB, TB, O>, where?: Where<T> | WhereClause<T>) => SelectQueryBuilder<DB, TB, O>;
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { QueryDSL, VQueryDSL } from '../types';
|
|
1
|
+
import type { QueryDSL, VQueryDSL } from '../types';
|
|
2
2
|
export declare const convertDSLToSQL: <T, TableName extends string>(dsl: QueryDSL<T> | VQueryDSL<T>, tableName: TableName) => string;
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Having 子句类型定义
|
|
3
|
+
* 用于聚合后的数据筛选
|
|
4
|
+
*/
|
|
5
|
+
export type Having<T> = HavingGroup<T>;
|
|
6
|
+
export type HavingGroup<T> = {
|
|
7
|
+
op: 'and' | 'or';
|
|
8
|
+
conditions: Array<HavingClause<T>>;
|
|
9
|
+
};
|
|
10
|
+
export type HavingClause<T> = HavingLeaf<T> | HavingGroup<T>;
|
|
11
|
+
/**
|
|
12
|
+
* Having 叶子节点
|
|
13
|
+
* 支持聚合函数操作符
|
|
14
|
+
*/
|
|
15
|
+
export type HavingLeaf<T> = {
|
|
16
|
+
[K in keyof T]: {
|
|
17
|
+
[O in HavingOperator]: {
|
|
18
|
+
field: K;
|
|
19
|
+
op: O;
|
|
20
|
+
} & (O extends 'is null' | 'is not null' ? {
|
|
21
|
+
value?: never;
|
|
22
|
+
} : O extends 'in' | 'not in' ? {
|
|
23
|
+
value: T[K] | T[K][];
|
|
24
|
+
} : O extends 'between' | 'not between' ? {
|
|
25
|
+
value: [T[K], T[K]];
|
|
26
|
+
} : {
|
|
27
|
+
value: T[K];
|
|
28
|
+
});
|
|
29
|
+
}[HavingOperator];
|
|
30
|
+
}[keyof T];
|
|
31
|
+
/**
|
|
32
|
+
* Having 操作符
|
|
33
|
+
* 聚合函数操作符
|
|
34
|
+
*/
|
|
35
|
+
export type HavingOperator = 'sum' | 'avg' | 'count' | 'min' | 'max' | '=' | '!=' | '>' | '>=' | '<' | '<=' | 'between' | 'not between' | 'in' | 'not in' | 'is null' | 'is not null';
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { GroupBy } from './GroupBy';
|
|
2
|
+
import { Having } from './Having';
|
|
2
3
|
import { OrderBy } from './OrderBy';
|
|
3
4
|
import { Select } from './Select';
|
|
4
5
|
import { Where } from './Where';
|
|
@@ -6,6 +7,7 @@ export interface QueryDSL<Table> {
|
|
|
6
7
|
select: Select<Table>;
|
|
7
8
|
where?: Where<Table>;
|
|
8
9
|
groupBy?: GroupBy<Table>;
|
|
10
|
+
having?: Having<Table>;
|
|
9
11
|
orderBy?: OrderBy<Table>;
|
|
10
12
|
limit?: number;
|
|
11
13
|
}
|
|
@@ -3,3 +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';
|
package/dist/node/esm/node.js
CHANGED
|
@@ -52,7 +52,17 @@ const inlineParameters = (sql, params)=>{
|
|
|
52
52
|
});
|
|
53
53
|
return sql;
|
|
54
54
|
};
|
|
55
|
-
const
|
|
55
|
+
const operatorMap = {
|
|
56
|
+
gt: '>',
|
|
57
|
+
gte: '>=',
|
|
58
|
+
lt: '<',
|
|
59
|
+
lte: '<=',
|
|
60
|
+
eq: '=',
|
|
61
|
+
neq: '!='
|
|
62
|
+
};
|
|
63
|
+
const toSqlOperator = (op)=>operatorMap[op] ?? op;
|
|
64
|
+
const applyWhere = (qb, where)=>{
|
|
65
|
+
if (!where) return qb;
|
|
56
66
|
const toRaw = (w)=>{
|
|
57
67
|
if ('op' in w && 'conditions' in w) {
|
|
58
68
|
const parts = w.conditions.map((c)=>toRaw(c));
|
|
@@ -62,6 +72,7 @@ const applyWhere = (where)=>{
|
|
|
62
72
|
const leaf = w;
|
|
63
73
|
const field = leaf.field;
|
|
64
74
|
const value = leaf.value;
|
|
75
|
+
const sqlOp = toSqlOperator(leaf.op);
|
|
65
76
|
switch(leaf.op){
|
|
66
77
|
case 'is null':
|
|
67
78
|
return external_kysely_sql`${external_kysely_sql.ref(field)} is null`;
|
|
@@ -92,10 +103,10 @@ const applyWhere = (where)=>{
|
|
|
92
103
|
return external_kysely_sql`${external_kysely_sql.ref(field)} not between ${external_kysely_sql.val(a)} and ${external_kysely_sql.val(b)}`;
|
|
93
104
|
}
|
|
94
105
|
default:
|
|
95
|
-
return external_kysely_sql`${external_kysely_sql.ref(field)} ${external_kysely_sql.raw(
|
|
106
|
+
return external_kysely_sql`${external_kysely_sql.ref(field)} ${external_kysely_sql.raw(sqlOp)} ${external_kysely_sql.val(value)}`;
|
|
96
107
|
}
|
|
97
108
|
};
|
|
98
|
-
return toRaw(where);
|
|
109
|
+
return qb.where(toRaw(where));
|
|
99
110
|
};
|
|
100
111
|
const applyGroupBy = (qb, fields)=>{
|
|
101
112
|
if (fields && fields.length > 0) {
|
|
@@ -104,10 +115,82 @@ const applyGroupBy = (qb, fields)=>{
|
|
|
104
115
|
}
|
|
105
116
|
return qb;
|
|
106
117
|
};
|
|
118
|
+
const having_operatorMap = {
|
|
119
|
+
gt: '>',
|
|
120
|
+
gte: '>=',
|
|
121
|
+
lt: '<',
|
|
122
|
+
lte: '<=',
|
|
123
|
+
eq: '=',
|
|
124
|
+
neq: '!='
|
|
125
|
+
};
|
|
126
|
+
const having_toSqlOperator = (op)=>having_operatorMap[op] ?? op;
|
|
127
|
+
const applyHaving = (qb, having)=>{
|
|
128
|
+
if (!having) return qb;
|
|
129
|
+
const toRaw = (h)=>{
|
|
130
|
+
if ('op' in h && 'conditions' in h) {
|
|
131
|
+
const parts = h.conditions.map((c)=>toRaw(c));
|
|
132
|
+
const sep = external_kysely_sql` ${external_kysely_sql.raw(h.op)} `;
|
|
133
|
+
return external_kysely_sql`(${external_kysely_sql.join(parts, sep)})`;
|
|
134
|
+
}
|
|
135
|
+
const leaf = h;
|
|
136
|
+
const field = leaf.field;
|
|
137
|
+
const value = leaf.value;
|
|
138
|
+
const op = having_toSqlOperator(leaf.op);
|
|
139
|
+
if ([
|
|
140
|
+
'sum',
|
|
141
|
+
'avg',
|
|
142
|
+
'count',
|
|
143
|
+
'min',
|
|
144
|
+
'max'
|
|
145
|
+
].includes(op)) {
|
|
146
|
+
const aggrExpr = external_kysely_sql`${external_kysely_sql.raw(op.toLowerCase())}(${external_kysely_sql.ref(field)})`;
|
|
147
|
+
return external_kysely_sql`${aggrExpr} = ${external_kysely_sql.val(value)}`;
|
|
148
|
+
}
|
|
149
|
+
switch(leaf.op){
|
|
150
|
+
case 'is null':
|
|
151
|
+
return external_kysely_sql`${external_kysely_sql.ref(field)} is null`;
|
|
152
|
+
case 'is not null':
|
|
153
|
+
return external_kysely_sql`${external_kysely_sql.ref(field)} is not null`;
|
|
154
|
+
case 'in':
|
|
155
|
+
{
|
|
156
|
+
const items = Array.isArray(value) ? value : [
|
|
157
|
+
value
|
|
158
|
+
];
|
|
159
|
+
return external_kysely_sql`${external_kysely_sql.ref(field)} in (${external_kysely_sql.join(items.map((v)=>external_kysely_sql.val(v)))})`;
|
|
160
|
+
}
|
|
161
|
+
case 'not in':
|
|
162
|
+
{
|
|
163
|
+
const items = Array.isArray(value) ? value : [
|
|
164
|
+
value
|
|
165
|
+
];
|
|
166
|
+
return external_kysely_sql`not ${external_kysely_sql.ref(field)} in (${external_kysely_sql.join(items.map((v)=>external_kysely_sql.val(v)))})`;
|
|
167
|
+
}
|
|
168
|
+
case 'between':
|
|
169
|
+
{
|
|
170
|
+
const [a, b] = value;
|
|
171
|
+
return external_kysely_sql`${external_kysely_sql.ref(field)} between ${external_kysely_sql.val(a)} and ${external_kysely_sql.val(b)}`;
|
|
172
|
+
}
|
|
173
|
+
case 'not between':
|
|
174
|
+
{
|
|
175
|
+
const [a, b] = value;
|
|
176
|
+
return external_kysely_sql`${external_kysely_sql.ref(field)} not between ${external_kysely_sql.val(a)} and ${external_kysely_sql.val(b)}`;
|
|
177
|
+
}
|
|
178
|
+
default:
|
|
179
|
+
return external_kysely_sql`${external_kysely_sql.ref(field)} ${external_kysely_sql.raw(op)} ${external_kysely_sql.val(value)}`;
|
|
180
|
+
}
|
|
181
|
+
};
|
|
182
|
+
return qb.having(toRaw(having));
|
|
183
|
+
};
|
|
107
184
|
const applyLimit = (qb, limit)=>{
|
|
108
185
|
if (limit && 'number' == typeof limit) qb = qb.limit(limit);
|
|
109
186
|
return qb;
|
|
110
187
|
};
|
|
188
|
+
const applyOrder = (qb, orderBy)=>{
|
|
189
|
+
if (orderBy && orderBy.length > 0) orderBy.forEach((o)=>{
|
|
190
|
+
qb = qb.orderBy(o.field, o.order ?? 'asc');
|
|
191
|
+
});
|
|
192
|
+
return qb;
|
|
193
|
+
};
|
|
111
194
|
const DATE_FORMAT_MAP = {
|
|
112
195
|
year: '%Y',
|
|
113
196
|
month: '%Y-%m',
|
|
@@ -122,9 +205,9 @@ const applySelect = (qb, select)=>{
|
|
|
122
205
|
if (isSelectItem(item)) {
|
|
123
206
|
const field = item.field;
|
|
124
207
|
const expression = eb.ref(field);
|
|
208
|
+
const alias = item.alias ?? field;
|
|
125
209
|
if (item.aggr) {
|
|
126
210
|
const { func } = item.aggr;
|
|
127
|
-
const alias = item.alias ?? field;
|
|
128
211
|
if ([
|
|
129
212
|
'avg',
|
|
130
213
|
'sum',
|
|
@@ -151,7 +234,6 @@ const applySelect = (qb, select)=>{
|
|
|
151
234
|
if ('quarter' === dateTrunc) return external_kysely_sql`strftime(CAST(${expression} AS TIMESTAMP), '%Y') || '-Q' || date_part('quarter', CAST(${expression} AS TIMESTAMP))`.as(alias);
|
|
152
235
|
}
|
|
153
236
|
}
|
|
154
|
-
const alias = item.alias ?? field;
|
|
155
237
|
return expression.as(alias);
|
|
156
238
|
}
|
|
157
239
|
return item;
|
|
@@ -164,12 +246,13 @@ const convertDSLToSQL = (dsl, tableName)=>{
|
|
|
164
246
|
});
|
|
165
247
|
let qb = db.selectFrom(tableName);
|
|
166
248
|
qb = applySelect(qb, dsl.select);
|
|
167
|
-
|
|
249
|
+
qb = applyWhere(qb, dsl.where);
|
|
168
250
|
qb = applyGroupBy(qb, dsl.groupBy);
|
|
169
|
-
|
|
251
|
+
qb = applyHaving(qb, dsl.having);
|
|
252
|
+
qb = applyOrder(qb, dsl.orderBy);
|
|
170
253
|
qb = applyLimit(qb, dsl.limit);
|
|
171
|
-
const
|
|
172
|
-
return inlineParameters(
|
|
254
|
+
const { sql, parameters } = qb.compile();
|
|
255
|
+
return inlineParameters(sql, parameters);
|
|
173
256
|
};
|
|
174
257
|
const READ_FUNCTION_MAP = {
|
|
175
258
|
csv: 'read_csv_auto',
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import type { SelectQueryBuilder } from 'kysely';
|
|
2
|
+
import { Having, HavingClause } from '../../types';
|
|
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>;
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { SelectQueryBuilder } from 'kysely';
|
|
2
|
+
type OrderByItem = {
|
|
3
|
+
field: string;
|
|
4
|
+
order?: 'asc' | 'desc';
|
|
5
|
+
};
|
|
6
|
+
export declare const applyOrder: <DB, TB extends keyof DB & string, O>(qb: SelectQueryBuilder<DB, TB, O>, orderBy?: Array<OrderByItem>) => SelectQueryBuilder<DB, TB, O>;
|
|
7
|
+
export {};
|
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
import { Where, WhereClause } from '../../types';
|
|
2
|
-
import type {
|
|
3
|
-
export declare const applyWhere: <T>(
|
|
2
|
+
import type { SelectQueryBuilder } from 'kysely';
|
|
3
|
+
export declare const applyWhere: <DB, TB extends keyof DB & string, O, T>(qb: SelectQueryBuilder<DB, TB, O>, where?: Where<T> | WhereClause<T>) => SelectQueryBuilder<DB, TB, O>;
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { QueryDSL, VQueryDSL } from '../types';
|
|
1
|
+
import type { QueryDSL, VQueryDSL } from '../types';
|
|
2
2
|
export declare const convertDSLToSQL: <T, TableName extends string>(dsl: QueryDSL<T> | VQueryDSL<T>, tableName: TableName) => string;
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Having 子句类型定义
|
|
3
|
+
* 用于聚合后的数据筛选
|
|
4
|
+
*/
|
|
5
|
+
export type Having<T> = HavingGroup<T>;
|
|
6
|
+
export type HavingGroup<T> = {
|
|
7
|
+
op: 'and' | 'or';
|
|
8
|
+
conditions: Array<HavingClause<T>>;
|
|
9
|
+
};
|
|
10
|
+
export type HavingClause<T> = HavingLeaf<T> | HavingGroup<T>;
|
|
11
|
+
/**
|
|
12
|
+
* Having 叶子节点
|
|
13
|
+
* 支持聚合函数操作符
|
|
14
|
+
*/
|
|
15
|
+
export type HavingLeaf<T> = {
|
|
16
|
+
[K in keyof T]: {
|
|
17
|
+
[O in HavingOperator]: {
|
|
18
|
+
field: K;
|
|
19
|
+
op: O;
|
|
20
|
+
} & (O extends 'is null' | 'is not null' ? {
|
|
21
|
+
value?: never;
|
|
22
|
+
} : O extends 'in' | 'not in' ? {
|
|
23
|
+
value: T[K] | T[K][];
|
|
24
|
+
} : O extends 'between' | 'not between' ? {
|
|
25
|
+
value: [T[K], T[K]];
|
|
26
|
+
} : {
|
|
27
|
+
value: T[K];
|
|
28
|
+
});
|
|
29
|
+
}[HavingOperator];
|
|
30
|
+
}[keyof T];
|
|
31
|
+
/**
|
|
32
|
+
* Having 操作符
|
|
33
|
+
* 聚合函数操作符
|
|
34
|
+
*/
|
|
35
|
+
export type HavingOperator = 'sum' | 'avg' | 'count' | 'min' | 'max' | '=' | '!=' | '>' | '>=' | '<' | '<=' | 'between' | 'not between' | 'in' | 'not in' | 'is null' | 'is not null';
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { GroupBy } from './GroupBy';
|
|
2
|
+
import { Having } from './Having';
|
|
2
3
|
import { OrderBy } from './OrderBy';
|
|
3
4
|
import { Select } from './Select';
|
|
4
5
|
import { Where } from './Where';
|
|
@@ -6,6 +7,7 @@ export interface QueryDSL<Table> {
|
|
|
6
7
|
select: Select<Table>;
|
|
7
8
|
where?: Where<Table>;
|
|
8
9
|
groupBy?: GroupBy<Table>;
|
|
10
|
+
having?: Having<Table>;
|
|
9
11
|
orderBy?: OrderBy<Table>;
|
|
10
12
|
limit?: number;
|
|
11
13
|
}
|
|
@@ -3,3 +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';
|