@visactor/vquery 0.1.47 → 0.1.48
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/dataset/convert/applyWhere.d.ts +2 -0
- package/dist/dataset/convert/dslToSQL.d.ts +2 -0
- package/dist/dataset/convert/utils.d.ts +6 -0
- package/dist/dataset/dataset.d.ts +11 -1
- package/dist/index.cjs +67 -0
- package/dist/index.js +67 -0
- package/dist/types/QueryDSL/GroupBy.d.ts +1 -0
- package/dist/types/QueryDSL/OrderBy.d.ts +4 -0
- package/dist/types/QueryDSL/QueryDSL.d.ts +11 -0
- package/dist/types/QueryDSL/Select.d.ts +7 -0
- package/dist/types/QueryDSL/Where.d.ts +24 -0
- package/dist/types/QueryDSL/demo.d.ts +1 -0
- package/dist/types/QueryDSL/index.d.ts +5 -0
- package/dist/types/index.d.ts +1 -0
- package/package.json +2 -2
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { Where, WhereClause, WhereGroup, WhereLeaf } from '../../types';
|
|
2
|
+
import { SelectItem } from '../../types/QueryDSL/Select';
|
|
3
|
+
export declare const isSelectItem: <T>(item: keyof T | SelectItem<T>) => item is SelectItem<T>;
|
|
4
|
+
export declare const isWhereLeaf: <T>(where: Where<T> | WhereClause<T>) => where is WhereLeaf<T>;
|
|
5
|
+
export declare const isWhereGroup: <T>(where: Where<T> | WhereClause<T>) => where is WhereGroup<T>;
|
|
6
|
+
export declare const isStringOrNumber: (value: unknown) => value is string | number;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { DatasetColumn } from '../types';
|
|
1
|
+
import { DatasetColumn, QueryDSL } from '../types';
|
|
2
2
|
import { DuckDB } from '../db/duckDb';
|
|
3
3
|
import { IndexedDB } from '../db/indexedDb';
|
|
4
4
|
export declare class Dataset {
|
|
@@ -16,6 +16,16 @@ export declare class Dataset {
|
|
|
16
16
|
dataset: any[];
|
|
17
17
|
table: any;
|
|
18
18
|
}>;
|
|
19
|
+
convertDSLToSQL<T extends Record<string, number | string>>(queryDSL: QueryDSL<T>): string;
|
|
20
|
+
query<T extends Record<string, number | string>>(queryDSL: QueryDSL<T>): Promise<{
|
|
21
|
+
performance: {
|
|
22
|
+
startAt: string;
|
|
23
|
+
endAt: string;
|
|
24
|
+
duration: number;
|
|
25
|
+
};
|
|
26
|
+
dataset: any[];
|
|
27
|
+
table: any;
|
|
28
|
+
}>;
|
|
19
29
|
disconnect(): Promise<void>;
|
|
20
30
|
get datasetId(): string;
|
|
21
31
|
}
|
package/dist/index.cjs
CHANGED
|
@@ -33,6 +33,65 @@ __webpack_require__.d(__webpack_exports__, {
|
|
|
33
33
|
isUrl: ()=>isUrl,
|
|
34
34
|
VQuery: ()=>VQuery
|
|
35
35
|
});
|
|
36
|
+
const isSelectItem = (item)=>'object' == typeof item && 'field' in item;
|
|
37
|
+
const isWhereLeaf = (where)=>'field' in where && 'op' in where && 'value' in where;
|
|
38
|
+
const isWhereGroup = (where)=>'op' in where && 'conditions' in where;
|
|
39
|
+
const isStringOrNumber = (value)=>'string' == typeof value || 'number' == typeof value;
|
|
40
|
+
const applyWhere = (where)=>{
|
|
41
|
+
const escape = (str)=>{
|
|
42
|
+
if ('string' == typeof str) return `'${str.replace(/'/g, "''")}'`;
|
|
43
|
+
return str;
|
|
44
|
+
};
|
|
45
|
+
if (isWhereGroup(where)) {
|
|
46
|
+
const logicalOp = where.op.toUpperCase();
|
|
47
|
+
return `(${where.conditions.map((c)=>applyWhere(c)).join(` ${logicalOp} `)})`;
|
|
48
|
+
}
|
|
49
|
+
if (isWhereLeaf(where)) {
|
|
50
|
+
const { field, op, value } = where;
|
|
51
|
+
if ('is null' === op || 'is not null' === op) return `${field} ${op}`;
|
|
52
|
+
if ('in' === op || 'not in' === op) {
|
|
53
|
+
if (Array.isArray(value)) return `${field} ${op} (${value.map((v)=>escape(v)).join(', ')})`;
|
|
54
|
+
}
|
|
55
|
+
if ('between' === op || 'not between' === op) {
|
|
56
|
+
if (Array.isArray(value) && 2 === value.length && isStringOrNumber(value[0]) && isStringOrNumber(value[1])) {
|
|
57
|
+
const value0 = value[0];
|
|
58
|
+
const value1 = value[1];
|
|
59
|
+
return `${field} ${op} ${escape(value0)} and ${escape(value1)}`;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
if (isStringOrNumber(value)) {
|
|
63
|
+
const value0 = value;
|
|
64
|
+
return `${field} ${op} ${escape(value0)}`;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
return '';
|
|
68
|
+
};
|
|
69
|
+
const convertDSLToSQL = (dsl, tableName)=>{
|
|
70
|
+
let sql = 'SELECT';
|
|
71
|
+
if (dsl.select && dsl.select.length > 0) {
|
|
72
|
+
const selectFields = dsl.select.map((item)=>{
|
|
73
|
+
if ('string' == typeof item) return item;
|
|
74
|
+
if (isSelectItem(item)) {
|
|
75
|
+
if (item.func) return `${item.func}(${item.field})` + (item.alias ? ` AS "${item.alias}"` : '');
|
|
76
|
+
if (item.alias) return `${item.field} AS "${item.alias}"`;
|
|
77
|
+
return item.field;
|
|
78
|
+
}
|
|
79
|
+
});
|
|
80
|
+
sql += ` ${selectFields.join(', ')}`;
|
|
81
|
+
} else sql += ' *';
|
|
82
|
+
sql += ` FROM ${tableName}`;
|
|
83
|
+
if (dsl.where) {
|
|
84
|
+
const whereClause = applyWhere(dsl.where);
|
|
85
|
+
if (whereClause) sql += ` WHERE ${whereClause}`;
|
|
86
|
+
}
|
|
87
|
+
if (dsl.groupBy && dsl.groupBy.length > 0) sql += ` GROUP BY ${dsl.groupBy.join(', ')}`;
|
|
88
|
+
if (dsl.orderBy && dsl.orderBy.length > 0) {
|
|
89
|
+
const orderByFields = dsl.orderBy.map((item)=>`${item.field}${item.order ? ` ${item.order.toUpperCase()}` : ''}`);
|
|
90
|
+
sql += ` ORDER BY ${orderByFields.join(', ')}`;
|
|
91
|
+
}
|
|
92
|
+
if (dsl.limit) sql += ` LIMIT ${dsl.limit}`;
|
|
93
|
+
return sql;
|
|
94
|
+
};
|
|
36
95
|
class Dataset {
|
|
37
96
|
duckDB;
|
|
38
97
|
indexedDB;
|
|
@@ -82,6 +141,14 @@ class Dataset {
|
|
|
82
141
|
}
|
|
83
142
|
};
|
|
84
143
|
}
|
|
144
|
+
convertDSLToSQL(queryDSL) {
|
|
145
|
+
return convertDSLToSQL(queryDSL, this.datasetId);
|
|
146
|
+
}
|
|
147
|
+
async query(queryDSL) {
|
|
148
|
+
const sql = this.convertDSLToSQL(queryDSL);
|
|
149
|
+
console.log(sql);
|
|
150
|
+
return this.queryBySQL(sql);
|
|
151
|
+
}
|
|
85
152
|
async disconnect() {
|
|
86
153
|
await this.duckDB.query(`DROP VIEW IF EXISTS "${this._datasetId}"`);
|
|
87
154
|
}
|
package/dist/index.js
CHANGED
|
@@ -1,4 +1,63 @@
|
|
|
1
1
|
import { AsyncDuckDB, ConsoleLogger, selectBundle } from "@duckdb/duckdb-wasm";
|
|
2
|
+
const isSelectItem = (item)=>'object' == typeof item && 'field' in item;
|
|
3
|
+
const isWhereLeaf = (where)=>'field' in where && 'op' in where && 'value' in where;
|
|
4
|
+
const isWhereGroup = (where)=>'op' in where && 'conditions' in where;
|
|
5
|
+
const isStringOrNumber = (value)=>'string' == typeof value || 'number' == typeof value;
|
|
6
|
+
const applyWhere = (where)=>{
|
|
7
|
+
const escape = (str)=>{
|
|
8
|
+
if ('string' == typeof str) return `'${str.replace(/'/g, "''")}'`;
|
|
9
|
+
return str;
|
|
10
|
+
};
|
|
11
|
+
if (isWhereGroup(where)) {
|
|
12
|
+
const logicalOp = where.op.toUpperCase();
|
|
13
|
+
return `(${where.conditions.map((c)=>applyWhere(c)).join(` ${logicalOp} `)})`;
|
|
14
|
+
}
|
|
15
|
+
if (isWhereLeaf(where)) {
|
|
16
|
+
const { field, op, value } = where;
|
|
17
|
+
if ('is null' === op || 'is not null' === op) return `${field} ${op}`;
|
|
18
|
+
if ('in' === op || 'not in' === op) {
|
|
19
|
+
if (Array.isArray(value)) return `${field} ${op} (${value.map((v)=>escape(v)).join(', ')})`;
|
|
20
|
+
}
|
|
21
|
+
if ('between' === op || 'not between' === op) {
|
|
22
|
+
if (Array.isArray(value) && 2 === value.length && isStringOrNumber(value[0]) && isStringOrNumber(value[1])) {
|
|
23
|
+
const value0 = value[0];
|
|
24
|
+
const value1 = value[1];
|
|
25
|
+
return `${field} ${op} ${escape(value0)} and ${escape(value1)}`;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
if (isStringOrNumber(value)) {
|
|
29
|
+
const value0 = value;
|
|
30
|
+
return `${field} ${op} ${escape(value0)}`;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
return '';
|
|
34
|
+
};
|
|
35
|
+
const convertDSLToSQL = (dsl, tableName)=>{
|
|
36
|
+
let sql = 'SELECT';
|
|
37
|
+
if (dsl.select && dsl.select.length > 0) {
|
|
38
|
+
const selectFields = dsl.select.map((item)=>{
|
|
39
|
+
if ('string' == typeof item) return item;
|
|
40
|
+
if (isSelectItem(item)) {
|
|
41
|
+
if (item.func) return `${item.func}(${item.field})` + (item.alias ? ` AS "${item.alias}"` : '');
|
|
42
|
+
if (item.alias) return `${item.field} AS "${item.alias}"`;
|
|
43
|
+
return item.field;
|
|
44
|
+
}
|
|
45
|
+
});
|
|
46
|
+
sql += ` ${selectFields.join(', ')}`;
|
|
47
|
+
} else sql += ' *';
|
|
48
|
+
sql += ` FROM ${tableName}`;
|
|
49
|
+
if (dsl.where) {
|
|
50
|
+
const whereClause = applyWhere(dsl.where);
|
|
51
|
+
if (whereClause) sql += ` WHERE ${whereClause}`;
|
|
52
|
+
}
|
|
53
|
+
if (dsl.groupBy && dsl.groupBy.length > 0) sql += ` GROUP BY ${dsl.groupBy.join(', ')}`;
|
|
54
|
+
if (dsl.orderBy && dsl.orderBy.length > 0) {
|
|
55
|
+
const orderByFields = dsl.orderBy.map((item)=>`${item.field}${item.order ? ` ${item.order.toUpperCase()}` : ''}`);
|
|
56
|
+
sql += ` ORDER BY ${orderByFields.join(', ')}`;
|
|
57
|
+
}
|
|
58
|
+
if (dsl.limit) sql += ` LIMIT ${dsl.limit}`;
|
|
59
|
+
return sql;
|
|
60
|
+
};
|
|
2
61
|
class Dataset {
|
|
3
62
|
duckDB;
|
|
4
63
|
indexedDB;
|
|
@@ -48,6 +107,14 @@ class Dataset {
|
|
|
48
107
|
}
|
|
49
108
|
};
|
|
50
109
|
}
|
|
110
|
+
convertDSLToSQL(queryDSL) {
|
|
111
|
+
return convertDSLToSQL(queryDSL, this.datasetId);
|
|
112
|
+
}
|
|
113
|
+
async query(queryDSL) {
|
|
114
|
+
const sql = this.convertDSLToSQL(queryDSL);
|
|
115
|
+
console.log(sql);
|
|
116
|
+
return this.queryBySQL(sql);
|
|
117
|
+
}
|
|
51
118
|
async disconnect() {
|
|
52
119
|
await this.duckDB.query(`DROP VIEW IF EXISTS "${this._datasetId}"`);
|
|
53
120
|
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export type GroupBy<T> = Array<keyof T>;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { GroupBy } from './GroupBy';
|
|
2
|
+
import { OrderBy } from './OrderBy';
|
|
3
|
+
import { Select } from './Select';
|
|
4
|
+
import { Where } from './Where';
|
|
5
|
+
export interface QueryDSL<Table> {
|
|
6
|
+
select: Select<Table>;
|
|
7
|
+
where?: Where<Table>;
|
|
8
|
+
groupBy?: GroupBy<Table>;
|
|
9
|
+
orderBy?: OrderBy<Table>;
|
|
10
|
+
limit?: number;
|
|
11
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
export type Where<T> = WhereGroup<T>;
|
|
2
|
+
export type WhereGroup<T> = {
|
|
3
|
+
op: 'and' | 'or';
|
|
4
|
+
conditions: Array<WhereClause<T>>;
|
|
5
|
+
};
|
|
6
|
+
export type WhereClause<T> = WhereLeaf<T> | WhereGroup<T>;
|
|
7
|
+
export type WhereLeaf<T> = {
|
|
8
|
+
[K in keyof T]: {
|
|
9
|
+
[O in Operator]: {
|
|
10
|
+
field: K;
|
|
11
|
+
op: O;
|
|
12
|
+
} & (O extends 'is null' | 'is not null' ? {
|
|
13
|
+
value?: never;
|
|
14
|
+
} : O extends 'in' | 'not in' ? {
|
|
15
|
+
value: T[K][];
|
|
16
|
+
} : O extends 'between' | 'not between' ? {
|
|
17
|
+
value: [T[K], T[K]];
|
|
18
|
+
} : {
|
|
19
|
+
value: T[K];
|
|
20
|
+
});
|
|
21
|
+
}[Operator];
|
|
22
|
+
}[keyof T];
|
|
23
|
+
type Operator = '=' | '!=' | '>' | '>=' | '<' | '<=' | 'like' | 'not like' | 'ilike' | 'not ilike' | 'in' | 'not in' | 'between' | 'not between' | 'is null' | 'is not null';
|
|
24
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/dist/types/index.d.ts
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@visactor/vquery",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.48",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"exports": {
|
|
6
6
|
".": {
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
"dist"
|
|
16
16
|
],
|
|
17
17
|
"dependencies": {
|
|
18
|
-
"@duckdb/duckdb-wasm": "
|
|
18
|
+
"@duckdb/duckdb-wasm": "1.30.0"
|
|
19
19
|
},
|
|
20
20
|
"devDependencies": {
|
|
21
21
|
"@eslint/js": "^9.35.0",
|