@visactor/vquery 0.1.46 → 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 +31 -0
- package/dist/dataset/index.d.ts +1 -0
- package/dist/index.cjs +113 -40
- package/dist/index.js +113 -40
- 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/dist/vquery.d.ts +12 -2
- package/package.json +2 -2
- package/dist/dataset.d.ts +0 -19
|
@@ -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;
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { DatasetColumn, QueryDSL } from '../types';
|
|
2
|
+
import { DuckDB } from '../db/duckDb';
|
|
3
|
+
import { IndexedDB } from '../db/indexedDb';
|
|
4
|
+
export declare class Dataset {
|
|
5
|
+
private duckDB;
|
|
6
|
+
private indexedDB;
|
|
7
|
+
private _datasetId;
|
|
8
|
+
constructor(duckDB: DuckDB, indexedDB: IndexedDB, datasetId: string);
|
|
9
|
+
init(temporaryStructs?: DatasetColumn[]): Promise<void>;
|
|
10
|
+
queryBySQL(sql: string): Promise<{
|
|
11
|
+
performance: {
|
|
12
|
+
startAt: string;
|
|
13
|
+
endAt: string;
|
|
14
|
+
duration: number;
|
|
15
|
+
};
|
|
16
|
+
dataset: any[];
|
|
17
|
+
table: any;
|
|
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
|
+
}>;
|
|
29
|
+
disconnect(): Promise<void>;
|
|
30
|
+
get datasetId(): string;
|
|
31
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { Dataset } from './dataset';
|
package/dist/index.cjs
CHANGED
|
@@ -33,14 +33,100 @@ __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;
|
|
97
|
+
indexedDB;
|
|
38
98
|
_datasetId;
|
|
39
|
-
|
|
40
|
-
constructor(duckDB, datasetId, tableName){
|
|
99
|
+
constructor(duckDB, indexedDB1, datasetId){
|
|
41
100
|
this.duckDB = duckDB;
|
|
101
|
+
this.indexedDB = indexedDB1;
|
|
42
102
|
this._datasetId = datasetId;
|
|
43
|
-
|
|
103
|
+
}
|
|
104
|
+
async init(temporaryStructs) {
|
|
105
|
+
const readFunctionMap = {
|
|
106
|
+
csv: 'read_csv_auto',
|
|
107
|
+
json: 'read_json_auto',
|
|
108
|
+
xlsx: 'read_excel',
|
|
109
|
+
parquet: 'read_parquet'
|
|
110
|
+
};
|
|
111
|
+
const dataTypeMap = {
|
|
112
|
+
number: 'DOUBLE',
|
|
113
|
+
string: 'VARCHAR',
|
|
114
|
+
date: 'DATE',
|
|
115
|
+
datetime: 'TIMESTAMP',
|
|
116
|
+
timestamp: 'TIMESTAMP'
|
|
117
|
+
};
|
|
118
|
+
const datasetInfo = await this.indexedDB.readDataset(this._datasetId);
|
|
119
|
+
if (!datasetInfo) throw new Error(`Dataset ${this._datasetId} not found`);
|
|
120
|
+
const { dataSource } = datasetInfo;
|
|
121
|
+
const datasetSchema = datasetInfo.datasetSchema;
|
|
122
|
+
const columns = temporaryStructs || datasetSchema.columns;
|
|
123
|
+
const readFunction = readFunctionMap[dataSource.type];
|
|
124
|
+
if (!readFunction) throw new Error(`Unsupported dataSource type: ${dataSource.type}`);
|
|
125
|
+
await this.duckDB.writeFile(this._datasetId, dataSource.blob);
|
|
126
|
+
const columnsStruct = `{${columns.map((c)=>`'${c.name}': '${dataTypeMap[c.type] || 'VARCHAR'}'`).join(', ')}}`;
|
|
127
|
+
const columnNames = columns.map((c)=>`"${c.name}"`).join(', ');
|
|
128
|
+
const createViewSql = `CREATE OR REPLACE VIEW "${this._datasetId}" AS SELECT ${columnNames} FROM ${readFunction}('${this._datasetId}', columns=${columnsStruct})`;
|
|
129
|
+
await this.duckDB.query(createViewSql);
|
|
44
130
|
}
|
|
45
131
|
async queryBySQL(sql) {
|
|
46
132
|
const start = performance?.now?.()?.toFixed(3) ?? Date.now().toFixed(3);
|
|
@@ -55,13 +141,20 @@ class Dataset {
|
|
|
55
141
|
}
|
|
56
142
|
};
|
|
57
143
|
}
|
|
58
|
-
|
|
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
|
+
}
|
|
152
|
+
async disconnect() {
|
|
153
|
+
await this.duckDB.query(`DROP VIEW IF EXISTS "${this._datasetId}"`);
|
|
154
|
+
}
|
|
59
155
|
get datasetId() {
|
|
60
156
|
return this._datasetId;
|
|
61
157
|
}
|
|
62
|
-
get tableName() {
|
|
63
|
-
return this._tableName;
|
|
64
|
-
}
|
|
65
158
|
}
|
|
66
159
|
const duckdb_wasm_namespaceObject = require("@duckdb/duckdb-wasm");
|
|
67
160
|
class DuckDB {
|
|
@@ -312,22 +405,6 @@ class DataSourceBuilder {
|
|
|
312
405
|
return await response.blob();
|
|
313
406
|
}
|
|
314
407
|
}
|
|
315
|
-
function mapDataTypeToDuckDB(type) {
|
|
316
|
-
switch(type){
|
|
317
|
-
case 'number':
|
|
318
|
-
return 'DOUBLE';
|
|
319
|
-
case 'string':
|
|
320
|
-
return 'VARCHAR';
|
|
321
|
-
case 'date':
|
|
322
|
-
return 'DATE';
|
|
323
|
-
case 'datetime':
|
|
324
|
-
return 'TIMESTAMP';
|
|
325
|
-
case 'timestamp':
|
|
326
|
-
return 'TIMESTAMP';
|
|
327
|
-
default:
|
|
328
|
-
return 'VARCHAR';
|
|
329
|
-
}
|
|
330
|
-
}
|
|
331
408
|
class VQuery {
|
|
332
409
|
duckDB;
|
|
333
410
|
indexedDB;
|
|
@@ -363,23 +440,19 @@ class VQuery {
|
|
|
363
440
|
}
|
|
364
441
|
async connectDataset(datasetId) {
|
|
365
442
|
await this.ensureInitialized();
|
|
366
|
-
const
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
await this.
|
|
378
|
-
|
|
379
|
-
const columnNames = datasetSchema.columns.map((c)=>`"${c.name}"`).join(', ');
|
|
380
|
-
const createViewSql = `CREATE OR REPLACE VIEW "${datasetId}" AS SELECT ${columnNames} FROM ${readFunction}('${datasetId}', columns=${columnsStruct})`;
|
|
381
|
-
await this.duckDB.query(createViewSql);
|
|
382
|
-
return new Dataset(this.duckDB, datasetId, datasetSchema.datasetAlias || datasetId);
|
|
443
|
+
const dataset = new Dataset(this.duckDB, this.indexedDB, datasetId);
|
|
444
|
+
await dataset.init();
|
|
445
|
+
return dataset;
|
|
446
|
+
}
|
|
447
|
+
async connectTemporaryDataset(datasetId, temporaryDatasetSchema) {
|
|
448
|
+
await this.ensureInitialized();
|
|
449
|
+
const dataset = new Dataset(this.duckDB, this.indexedDB, datasetId);
|
|
450
|
+
await dataset.init(temporaryDatasetSchema);
|
|
451
|
+
return dataset;
|
|
452
|
+
}
|
|
453
|
+
async close() {
|
|
454
|
+
await this.ensureInitialized();
|
|
455
|
+
await this.duckDB.close();
|
|
383
456
|
}
|
|
384
457
|
}
|
|
385
458
|
exports.DataSourceBuilder = __webpack_exports__.DataSourceBuilder;
|
package/dist/index.js
CHANGED
|
@@ -1,12 +1,98 @@
|
|
|
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;
|
|
63
|
+
indexedDB;
|
|
4
64
|
_datasetId;
|
|
5
|
-
|
|
6
|
-
constructor(duckDB, datasetId, tableName){
|
|
65
|
+
constructor(duckDB, indexedDB1, datasetId){
|
|
7
66
|
this.duckDB = duckDB;
|
|
67
|
+
this.indexedDB = indexedDB1;
|
|
8
68
|
this._datasetId = datasetId;
|
|
9
|
-
|
|
69
|
+
}
|
|
70
|
+
async init(temporaryStructs) {
|
|
71
|
+
const readFunctionMap = {
|
|
72
|
+
csv: 'read_csv_auto',
|
|
73
|
+
json: 'read_json_auto',
|
|
74
|
+
xlsx: 'read_excel',
|
|
75
|
+
parquet: 'read_parquet'
|
|
76
|
+
};
|
|
77
|
+
const dataTypeMap = {
|
|
78
|
+
number: 'DOUBLE',
|
|
79
|
+
string: 'VARCHAR',
|
|
80
|
+
date: 'DATE',
|
|
81
|
+
datetime: 'TIMESTAMP',
|
|
82
|
+
timestamp: 'TIMESTAMP'
|
|
83
|
+
};
|
|
84
|
+
const datasetInfo = await this.indexedDB.readDataset(this._datasetId);
|
|
85
|
+
if (!datasetInfo) throw new Error(`Dataset ${this._datasetId} not found`);
|
|
86
|
+
const { dataSource } = datasetInfo;
|
|
87
|
+
const datasetSchema = datasetInfo.datasetSchema;
|
|
88
|
+
const columns = temporaryStructs || datasetSchema.columns;
|
|
89
|
+
const readFunction = readFunctionMap[dataSource.type];
|
|
90
|
+
if (!readFunction) throw new Error(`Unsupported dataSource type: ${dataSource.type}`);
|
|
91
|
+
await this.duckDB.writeFile(this._datasetId, dataSource.blob);
|
|
92
|
+
const columnsStruct = `{${columns.map((c)=>`'${c.name}': '${dataTypeMap[c.type] || 'VARCHAR'}'`).join(', ')}}`;
|
|
93
|
+
const columnNames = columns.map((c)=>`"${c.name}"`).join(', ');
|
|
94
|
+
const createViewSql = `CREATE OR REPLACE VIEW "${this._datasetId}" AS SELECT ${columnNames} FROM ${readFunction}('${this._datasetId}', columns=${columnsStruct})`;
|
|
95
|
+
await this.duckDB.query(createViewSql);
|
|
10
96
|
}
|
|
11
97
|
async queryBySQL(sql) {
|
|
12
98
|
const start = performance?.now?.()?.toFixed(3) ?? Date.now().toFixed(3);
|
|
@@ -21,13 +107,20 @@ class Dataset {
|
|
|
21
107
|
}
|
|
22
108
|
};
|
|
23
109
|
}
|
|
24
|
-
|
|
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
|
+
}
|
|
118
|
+
async disconnect() {
|
|
119
|
+
await this.duckDB.query(`DROP VIEW IF EXISTS "${this._datasetId}"`);
|
|
120
|
+
}
|
|
25
121
|
get datasetId() {
|
|
26
122
|
return this._datasetId;
|
|
27
123
|
}
|
|
28
|
-
get tableName() {
|
|
29
|
-
return this._tableName;
|
|
30
|
-
}
|
|
31
124
|
}
|
|
32
125
|
class DuckDB {
|
|
33
126
|
db = null;
|
|
@@ -277,22 +370,6 @@ class DataSourceBuilder {
|
|
|
277
370
|
return await response.blob();
|
|
278
371
|
}
|
|
279
372
|
}
|
|
280
|
-
function mapDataTypeToDuckDB(type) {
|
|
281
|
-
switch(type){
|
|
282
|
-
case 'number':
|
|
283
|
-
return 'DOUBLE';
|
|
284
|
-
case 'string':
|
|
285
|
-
return 'VARCHAR';
|
|
286
|
-
case 'date':
|
|
287
|
-
return 'DATE';
|
|
288
|
-
case 'datetime':
|
|
289
|
-
return 'TIMESTAMP';
|
|
290
|
-
case 'timestamp':
|
|
291
|
-
return 'TIMESTAMP';
|
|
292
|
-
default:
|
|
293
|
-
return 'VARCHAR';
|
|
294
|
-
}
|
|
295
|
-
}
|
|
296
373
|
class VQuery {
|
|
297
374
|
duckDB;
|
|
298
375
|
indexedDB;
|
|
@@ -328,23 +405,19 @@ class VQuery {
|
|
|
328
405
|
}
|
|
329
406
|
async connectDataset(datasetId) {
|
|
330
407
|
await this.ensureInitialized();
|
|
331
|
-
const
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
await this.
|
|
343
|
-
|
|
344
|
-
const columnNames = datasetSchema.columns.map((c)=>`"${c.name}"`).join(', ');
|
|
345
|
-
const createViewSql = `CREATE OR REPLACE VIEW "${datasetId}" AS SELECT ${columnNames} FROM ${readFunction}('${datasetId}', columns=${columnsStruct})`;
|
|
346
|
-
await this.duckDB.query(createViewSql);
|
|
347
|
-
return new Dataset(this.duckDB, datasetId, datasetSchema.datasetAlias || datasetId);
|
|
408
|
+
const dataset = new Dataset(this.duckDB, this.indexedDB, datasetId);
|
|
409
|
+
await dataset.init();
|
|
410
|
+
return dataset;
|
|
411
|
+
}
|
|
412
|
+
async connectTemporaryDataset(datasetId, temporaryDatasetSchema) {
|
|
413
|
+
await this.ensureInitialized();
|
|
414
|
+
const dataset = new Dataset(this.duckDB, this.indexedDB, datasetId);
|
|
415
|
+
await dataset.init(temporaryDatasetSchema);
|
|
416
|
+
return dataset;
|
|
417
|
+
}
|
|
418
|
+
async close() {
|
|
419
|
+
await this.ensureInitialized();
|
|
420
|
+
await this.duckDB.close();
|
|
348
421
|
}
|
|
349
422
|
}
|
|
350
423
|
export { DataSourceBuilder, VQuery, isBase64Url, isHttpUrl, isUrl };
|
|
@@ -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/dist/vquery.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { Dataset } from './dataset';
|
|
2
|
-
import { DatasetSchema, TidyDatum, DataSourceType } from './types';
|
|
1
|
+
import { Dataset } from './dataset/dataset';
|
|
2
|
+
import { DatasetSchema, TidyDatum, DataSourceType, DatasetColumn } from './types';
|
|
3
3
|
export declare class VQuery {
|
|
4
4
|
private duckDB;
|
|
5
5
|
private indexedDB;
|
|
@@ -30,4 +30,14 @@ export declare class VQuery {
|
|
|
30
30
|
* 连接数据集,返回数据集信息,从indexedDB获取表结构,使用DuckDB在内存中创建表
|
|
31
31
|
*/
|
|
32
32
|
connectDataset(datasetId: string): Promise<Dataset>;
|
|
33
|
+
/**
|
|
34
|
+
* 连接临时数据集,返回数据集信息,从indexedDB获取表结构,使用DuckDB在内存中创建表
|
|
35
|
+
* @param datasetId
|
|
36
|
+
* @returns
|
|
37
|
+
*/
|
|
38
|
+
connectTemporaryDataset(datasetId: string, temporaryDatasetSchema?: DatasetColumn[]): Promise<Dataset>;
|
|
39
|
+
/**
|
|
40
|
+
* 关闭所有数据集连接,释放DuckDB资源
|
|
41
|
+
*/
|
|
42
|
+
close(): Promise<void>;
|
|
33
43
|
}
|
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",
|
package/dist/dataset.d.ts
DELETED
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
import { DuckDB } from './db/duckDb';
|
|
2
|
-
export declare class Dataset {
|
|
3
|
-
private duckDB;
|
|
4
|
-
private _datasetId;
|
|
5
|
-
private _tableName;
|
|
6
|
-
constructor(duckDB: DuckDB, datasetId: string, tableName: string);
|
|
7
|
-
queryBySQL(sql: string): Promise<{
|
|
8
|
-
performance: {
|
|
9
|
-
startAt: string;
|
|
10
|
-
endAt: string;
|
|
11
|
-
duration: number;
|
|
12
|
-
};
|
|
13
|
-
dataset: any[];
|
|
14
|
-
table: any;
|
|
15
|
-
}>;
|
|
16
|
-
disConnect(): Promise<void>;
|
|
17
|
-
get datasetId(): string;
|
|
18
|
-
get tableName(): string;
|
|
19
|
-
}
|