@visactor/vquery 0.1.47 → 0.1.49

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.
@@ -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 {
@@ -6,7 +6,7 @@ export declare class Dataset {
6
6
  private indexedDB;
7
7
  private _datasetId;
8
8
  constructor(duckDB: DuckDB, indexedDB: IndexedDB, datasetId: string);
9
- init(temporaryStructs?: DatasetColumn[]): Promise<void>;
9
+ init(temporaryColumns?: DatasetColumn[]): Promise<void>;
10
10
  queryBySQL(sql: string): Promise<{
11
11
  performance: {
12
12
  startAt: string;
@@ -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
  }
@@ -1 +1,2 @@
1
1
  export { Dataset } from './dataset';
2
+ export { convertDSLToSQL } from '../sql-builder';
package/dist/index.cjs CHANGED
@@ -29,10 +29,153 @@ __webpack_require__.r(__webpack_exports__);
29
29
  __webpack_require__.d(__webpack_exports__, {
30
30
  DataSourceBuilder: ()=>DataSourceBuilder,
31
31
  isHttpUrl: ()=>isHttpUrl,
32
+ convertDSLToSQL: ()=>convertDSLToSQL,
32
33
  isBase64Url: ()=>isBase64Url,
33
34
  isUrl: ()=>isUrl,
34
35
  VQuery: ()=>VQuery
35
36
  });
37
+ const external_kysely_namespaceObject = require("kysely");
38
+ class PostgresDialect {
39
+ createDriver() {
40
+ return new external_kysely_namespaceObject.DummyDriver();
41
+ }
42
+ createQueryCompiler() {
43
+ return new external_kysely_namespaceObject.PostgresQueryCompiler();
44
+ }
45
+ createAdapter() {
46
+ return new external_kysely_namespaceObject.PostgresAdapter();
47
+ }
48
+ createIntrospector(db) {
49
+ class NullIntrospector {
50
+ async getSchemas() {
51
+ return [];
52
+ }
53
+ async getTables(options) {
54
+ options?.withInternalKyselyTables;
55
+ return [];
56
+ }
57
+ async getMetadata(options) {
58
+ options?.withInternalKyselyTables;
59
+ return {
60
+ tables: []
61
+ };
62
+ }
63
+ }
64
+ return new NullIntrospector();
65
+ }
66
+ }
67
+ const isSelectItem = (item)=>'object' == typeof item && 'field' in item;
68
+ const escapeValue = (value)=>{
69
+ if (null === value) return 'null';
70
+ if ('string' == typeof value) return `'${value.replace(/'/g, "''")}'`;
71
+ if ('number' == typeof value) return `${value}`;
72
+ if ('boolean' == typeof value) return value ? 'TRUE' : 'FALSE';
73
+ return `'${String(value).replace(/'/g, "''")}'`;
74
+ };
75
+ const inlineParameters = (sql, params)=>{
76
+ if (0 === params.length) return sql;
77
+ if (sql.includes('?')) {
78
+ let out = sql;
79
+ for (const p of params)out = out.replace(/\?/, escapeValue(p));
80
+ return out;
81
+ }
82
+ if (/\$\d+/.test(sql)) return sql.replace(/\$(\d+)/g, (_, idx)=>{
83
+ const i = Number(idx) - 1;
84
+ const v = params[i];
85
+ return escapeValue(v);
86
+ });
87
+ return sql;
88
+ };
89
+ const applyWhere = (where)=>{
90
+ const toRaw = (w)=>{
91
+ if ('op' in w && 'conditions' in w) {
92
+ const parts = w.conditions.map((c)=>toRaw(c));
93
+ const sep = (0, external_kysely_namespaceObject.sql)` ${external_kysely_namespaceObject.sql.raw(w.op)} `;
94
+ return (0, external_kysely_namespaceObject.sql)`(${external_kysely_namespaceObject.sql.join(parts, sep)})`;
95
+ }
96
+ const leaf = w;
97
+ const field = leaf.field;
98
+ const value = leaf.value;
99
+ switch(leaf.op){
100
+ case 'is null':
101
+ return (0, external_kysely_namespaceObject.sql)`${external_kysely_namespaceObject.sql.ref(field)} is null`;
102
+ case 'is not null':
103
+ return (0, external_kysely_namespaceObject.sql)`${external_kysely_namespaceObject.sql.ref(field)} is not null`;
104
+ case 'in':
105
+ {
106
+ const items = Array.isArray(value) ? value : [
107
+ value
108
+ ];
109
+ 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)))})`;
110
+ }
111
+ case 'not in':
112
+ {
113
+ const items = Array.isArray(value) ? value : [
114
+ value
115
+ ];
116
+ 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)))})`;
117
+ }
118
+ case 'between':
119
+ {
120
+ const [a, b] = value;
121
+ return (0, external_kysely_namespaceObject.sql)`${external_kysely_namespaceObject.sql.ref(field)} between (${external_kysely_namespaceObject.sql.val(a)}, ${external_kysely_namespaceObject.sql.val(b)})`;
122
+ }
123
+ case 'not between':
124
+ {
125
+ const [a, b] = value;
126
+ return (0, external_kysely_namespaceObject.sql)`not ${external_kysely_namespaceObject.sql.ref(field)} between (${external_kysely_namespaceObject.sql.val(a)}, ${external_kysely_namespaceObject.sql.val(b)})`;
127
+ }
128
+ default:
129
+ return (0, external_kysely_namespaceObject.sql)`${external_kysely_namespaceObject.sql.ref(field)} ${external_kysely_namespaceObject.sql.raw(leaf.op)} ${external_kysely_namespaceObject.sql.val(value)}`;
130
+ }
131
+ };
132
+ return toRaw(where);
133
+ };
134
+ const applyGroupBy = (qb, fields)=>{
135
+ if (fields && fields.length > 0) {
136
+ const exprs = fields.map((f)=>external_kysely_namespaceObject.sql.id(f));
137
+ qb = qb.groupBy(exprs);
138
+ }
139
+ return qb;
140
+ };
141
+ const applyLimit = (qb, limit)=>{
142
+ if (limit && 'number' == typeof limit) qb = qb.limit(limit);
143
+ return qb;
144
+ };
145
+ const convertDSLToSQL = (dsl, tableName)=>{
146
+ const db = new external_kysely_namespaceObject.Kysely({
147
+ dialect: new PostgresDialect()
148
+ });
149
+ let qb = db.selectFrom(tableName);
150
+ qb = dsl.select && dsl.select.length > 0 ? qb.select((eb)=>dsl.select.map((item)=>{
151
+ if (isSelectItem(item)) {
152
+ const field = item.field;
153
+ if (item.func) {
154
+ const alias = item.alias ?? field;
155
+ switch(item.func){
156
+ case 'avg':
157
+ return eb.fn.avg(field).as(alias);
158
+ case 'sum':
159
+ return eb.fn.sum(field).as(alias);
160
+ case 'min':
161
+ return eb.fn.min(field).as(alias);
162
+ case 'max':
163
+ return eb.fn.max(field).as(alias);
164
+ case 'count':
165
+ return eb.fn.count(field).as(alias);
166
+ }
167
+ }
168
+ return item.alias ? eb.ref(field).as(item.alias) : field;
169
+ }
170
+ return item;
171
+ })) : qb.selectAll();
172
+ if (dsl.where) qb = qb.where(applyWhere(dsl.where));
173
+ qb = applyGroupBy(qb, dsl.groupBy);
174
+ if (dsl.orderBy && dsl.orderBy.length > 0) for (const o of dsl.orderBy)qb = qb.orderBy(o.field, o.order ?? 'asc');
175
+ qb = applyLimit(qb, dsl.limit);
176
+ const compiled = qb.compile();
177
+ return inlineParameters(compiled.sql, compiled.parameters);
178
+ };
36
179
  class Dataset {
37
180
  duckDB;
38
181
  indexedDB;
@@ -42,7 +185,7 @@ class Dataset {
42
185
  this.indexedDB = indexedDB1;
43
186
  this._datasetId = datasetId;
44
187
  }
45
- async init(temporaryStructs) {
188
+ async init(temporaryColumns = []) {
46
189
  const readFunctionMap = {
47
190
  csv: 'read_csv_auto',
48
191
  json: 'read_json_auto',
@@ -60,7 +203,7 @@ class Dataset {
60
203
  if (!datasetInfo) throw new Error(`Dataset ${this._datasetId} not found`);
61
204
  const { dataSource } = datasetInfo;
62
205
  const datasetSchema = datasetInfo.datasetSchema;
63
- const columns = temporaryStructs || datasetSchema.columns;
206
+ const columns = temporaryColumns.length > 0 ? temporaryColumns : datasetSchema.columns;
64
207
  const readFunction = readFunctionMap[dataSource.type];
65
208
  if (!readFunction) throw new Error(`Unsupported dataSource type: ${dataSource.type}`);
66
209
  await this.duckDB.writeFile(this._datasetId, dataSource.blob);
@@ -82,6 +225,13 @@ class Dataset {
82
225
  }
83
226
  };
84
227
  }
228
+ convertDSLToSQL(queryDSL) {
229
+ return convertDSLToSQL(queryDSL, this.datasetId);
230
+ }
231
+ async query(queryDSL) {
232
+ const sql = this.convertDSLToSQL(queryDSL);
233
+ return this.queryBySQL(sql);
234
+ }
85
235
  async disconnect() {
86
236
  await this.duckDB.query(`DROP VIEW IF EXISTS "${this._datasetId}"`);
87
237
  }
@@ -353,9 +503,14 @@ class VQuery {
353
503
  this.isInitialized = true;
354
504
  }
355
505
  }
356
- async createDataset(datasetId, data, type, datasetSchema) {
506
+ async createDataset(datasetId, data, type, columns = []) {
357
507
  await this.ensureInitialized();
358
508
  const dataSource = await DataSourceBuilder.from(type, data).build();
509
+ const datasetSchema = {
510
+ datasetId,
511
+ datasetAlias: datasetId,
512
+ columns: columns
513
+ };
359
514
  await this.indexedDB.writeDataset(datasetId, dataSource, datasetSchema);
360
515
  }
361
516
  async updateDataset(datasetId, data, type, datasetSchema) {
@@ -363,24 +518,23 @@ class VQuery {
363
518
  const dataSource = await DataSourceBuilder.from(type, data).build();
364
519
  await this.indexedDB.writeDataset(datasetId, dataSource, datasetSchema);
365
520
  }
366
- async deleteDataset(datasetId) {
521
+ async dropDataset(datasetId) {
367
522
  await this.ensureInitialized();
368
523
  await this.indexedDB.deleteDataset(datasetId);
369
524
  }
370
- async listDatasets() {
525
+ async hasDataset(datasetId) {
371
526
  await this.ensureInitialized();
372
- return this.indexedDB.listDatasets();
527
+ const datasets = await this.indexedDB.listDatasets();
528
+ return datasets.some((item)=>item.datasetId === datasetId);
373
529
  }
374
- async connectDataset(datasetId) {
530
+ async listDatasets() {
375
531
  await this.ensureInitialized();
376
- const dataset = new Dataset(this.duckDB, this.indexedDB, datasetId);
377
- await dataset.init();
378
- return dataset;
532
+ return this.indexedDB.listDatasets();
379
533
  }
380
- async connectTemporaryDataset(datasetId, temporaryDatasetSchema) {
534
+ async connectDataset(datasetId, temporaryColumns = []) {
381
535
  await this.ensureInitialized();
382
536
  const dataset = new Dataset(this.duckDB, this.indexedDB, datasetId);
383
- await dataset.init(temporaryDatasetSchema);
537
+ await dataset.init(temporaryColumns);
384
538
  return dataset;
385
539
  }
386
540
  async close() {
@@ -390,12 +544,14 @@ class VQuery {
390
544
  }
391
545
  exports.DataSourceBuilder = __webpack_exports__.DataSourceBuilder;
392
546
  exports.VQuery = __webpack_exports__.VQuery;
547
+ exports.convertDSLToSQL = __webpack_exports__.convertDSLToSQL;
393
548
  exports.isBase64Url = __webpack_exports__.isBase64Url;
394
549
  exports.isHttpUrl = __webpack_exports__.isHttpUrl;
395
550
  exports.isUrl = __webpack_exports__.isUrl;
396
551
  for(var __webpack_i__ in __webpack_exports__)if (-1 === [
397
552
  "DataSourceBuilder",
398
553
  "VQuery",
554
+ "convertDSLToSQL",
399
555
  "isBase64Url",
400
556
  "isHttpUrl",
401
557
  "isUrl"
package/dist/index.d.ts CHANGED
@@ -1,3 +1,4 @@
1
1
  export { VQuery } from './vquery';
2
- export { DataSourceBuilder } from './dataSourceBuilder/dataSourceBuilder';
2
+ export { convertDSLToSQL } from './dataset';
3
+ export { DataSourceBuilder } from './data-source-builder/dataSourceBuilder';
3
4
  export * from './utils';
package/dist/index.js CHANGED
@@ -1,4 +1,146 @@
1
+ import { DummyDriver, Kysely, PostgresAdapter, PostgresQueryCompiler, sql as external_kysely_sql } from "kysely";
1
2
  import { AsyncDuckDB, ConsoleLogger, selectBundle } from "@duckdb/duckdb-wasm";
3
+ class PostgresDialect {
4
+ createDriver() {
5
+ return new DummyDriver();
6
+ }
7
+ createQueryCompiler() {
8
+ return new PostgresQueryCompiler();
9
+ }
10
+ createAdapter() {
11
+ return new PostgresAdapter();
12
+ }
13
+ createIntrospector(db) {
14
+ class NullIntrospector {
15
+ async getSchemas() {
16
+ return [];
17
+ }
18
+ async getTables(options) {
19
+ options?.withInternalKyselyTables;
20
+ return [];
21
+ }
22
+ async getMetadata(options) {
23
+ options?.withInternalKyselyTables;
24
+ return {
25
+ tables: []
26
+ };
27
+ }
28
+ }
29
+ return new NullIntrospector();
30
+ }
31
+ }
32
+ const isSelectItem = (item)=>'object' == typeof item && 'field' in item;
33
+ const escapeValue = (value)=>{
34
+ if (null === value) return 'null';
35
+ if ('string' == typeof value) return `'${value.replace(/'/g, "''")}'`;
36
+ if ('number' == typeof value) return `${value}`;
37
+ if ('boolean' == typeof value) return value ? 'TRUE' : 'FALSE';
38
+ return `'${String(value).replace(/'/g, "''")}'`;
39
+ };
40
+ const inlineParameters = (sql, params)=>{
41
+ if (0 === params.length) return sql;
42
+ if (sql.includes('?')) {
43
+ let out = sql;
44
+ for (const p of params)out = out.replace(/\?/, escapeValue(p));
45
+ return out;
46
+ }
47
+ if (/\$\d+/.test(sql)) return sql.replace(/\$(\d+)/g, (_, idx)=>{
48
+ const i = Number(idx) - 1;
49
+ const v = params[i];
50
+ return escapeValue(v);
51
+ });
52
+ return sql;
53
+ };
54
+ const applyWhere = (where)=>{
55
+ const toRaw = (w)=>{
56
+ if ('op' in w && 'conditions' in w) {
57
+ const parts = w.conditions.map((c)=>toRaw(c));
58
+ const sep = external_kysely_sql` ${external_kysely_sql.raw(w.op)} `;
59
+ return external_kysely_sql`(${external_kysely_sql.join(parts, sep)})`;
60
+ }
61
+ const leaf = w;
62
+ const field = leaf.field;
63
+ const value = leaf.value;
64
+ switch(leaf.op){
65
+ case 'is null':
66
+ return external_kysely_sql`${external_kysely_sql.ref(field)} is null`;
67
+ case 'is not null':
68
+ return external_kysely_sql`${external_kysely_sql.ref(field)} is not null`;
69
+ case 'in':
70
+ {
71
+ const items = Array.isArray(value) ? value : [
72
+ value
73
+ ];
74
+ return external_kysely_sql`${external_kysely_sql.ref(field)} in (${external_kysely_sql.join(items.map((v)=>external_kysely_sql.val(v)))})`;
75
+ }
76
+ case 'not in':
77
+ {
78
+ const items = Array.isArray(value) ? value : [
79
+ value
80
+ ];
81
+ return external_kysely_sql`not ${external_kysely_sql.ref(field)} in (${external_kysely_sql.join(items.map((v)=>external_kysely_sql.val(v)))})`;
82
+ }
83
+ case 'between':
84
+ {
85
+ const [a, b] = value;
86
+ return external_kysely_sql`${external_kysely_sql.ref(field)} between (${external_kysely_sql.val(a)}, ${external_kysely_sql.val(b)})`;
87
+ }
88
+ case 'not between':
89
+ {
90
+ const [a, b] = value;
91
+ return external_kysely_sql`not ${external_kysely_sql.ref(field)} between (${external_kysely_sql.val(a)}, ${external_kysely_sql.val(b)})`;
92
+ }
93
+ default:
94
+ return external_kysely_sql`${external_kysely_sql.ref(field)} ${external_kysely_sql.raw(leaf.op)} ${external_kysely_sql.val(value)}`;
95
+ }
96
+ };
97
+ return toRaw(where);
98
+ };
99
+ const applyGroupBy = (qb, fields)=>{
100
+ if (fields && fields.length > 0) {
101
+ const exprs = fields.map((f)=>external_kysely_sql.id(f));
102
+ qb = qb.groupBy(exprs);
103
+ }
104
+ return qb;
105
+ };
106
+ const applyLimit = (qb, limit)=>{
107
+ if (limit && 'number' == typeof limit) qb = qb.limit(limit);
108
+ return qb;
109
+ };
110
+ const convertDSLToSQL = (dsl, tableName)=>{
111
+ const db = new Kysely({
112
+ dialect: new PostgresDialect()
113
+ });
114
+ let qb = db.selectFrom(tableName);
115
+ qb = dsl.select && dsl.select.length > 0 ? qb.select((eb)=>dsl.select.map((item)=>{
116
+ if (isSelectItem(item)) {
117
+ const field = item.field;
118
+ if (item.func) {
119
+ const alias = item.alias ?? field;
120
+ switch(item.func){
121
+ case 'avg':
122
+ return eb.fn.avg(field).as(alias);
123
+ case 'sum':
124
+ return eb.fn.sum(field).as(alias);
125
+ case 'min':
126
+ return eb.fn.min(field).as(alias);
127
+ case 'max':
128
+ return eb.fn.max(field).as(alias);
129
+ case 'count':
130
+ return eb.fn.count(field).as(alias);
131
+ }
132
+ }
133
+ return item.alias ? eb.ref(field).as(item.alias) : field;
134
+ }
135
+ return item;
136
+ })) : qb.selectAll();
137
+ if (dsl.where) qb = qb.where(applyWhere(dsl.where));
138
+ qb = applyGroupBy(qb, dsl.groupBy);
139
+ if (dsl.orderBy && dsl.orderBy.length > 0) for (const o of dsl.orderBy)qb = qb.orderBy(o.field, o.order ?? 'asc');
140
+ qb = applyLimit(qb, dsl.limit);
141
+ const compiled = qb.compile();
142
+ return inlineParameters(compiled.sql, compiled.parameters);
143
+ };
2
144
  class Dataset {
3
145
  duckDB;
4
146
  indexedDB;
@@ -8,7 +150,7 @@ class Dataset {
8
150
  this.indexedDB = indexedDB1;
9
151
  this._datasetId = datasetId;
10
152
  }
11
- async init(temporaryStructs) {
153
+ async init(temporaryColumns = []) {
12
154
  const readFunctionMap = {
13
155
  csv: 'read_csv_auto',
14
156
  json: 'read_json_auto',
@@ -26,7 +168,7 @@ class Dataset {
26
168
  if (!datasetInfo) throw new Error(`Dataset ${this._datasetId} not found`);
27
169
  const { dataSource } = datasetInfo;
28
170
  const datasetSchema = datasetInfo.datasetSchema;
29
- const columns = temporaryStructs || datasetSchema.columns;
171
+ const columns = temporaryColumns.length > 0 ? temporaryColumns : datasetSchema.columns;
30
172
  const readFunction = readFunctionMap[dataSource.type];
31
173
  if (!readFunction) throw new Error(`Unsupported dataSource type: ${dataSource.type}`);
32
174
  await this.duckDB.writeFile(this._datasetId, dataSource.blob);
@@ -48,6 +190,13 @@ class Dataset {
48
190
  }
49
191
  };
50
192
  }
193
+ convertDSLToSQL(queryDSL) {
194
+ return convertDSLToSQL(queryDSL, this.datasetId);
195
+ }
196
+ async query(queryDSL) {
197
+ const sql = this.convertDSLToSQL(queryDSL);
198
+ return this.queryBySQL(sql);
199
+ }
51
200
  async disconnect() {
52
201
  await this.duckDB.query(`DROP VIEW IF EXISTS "${this._datasetId}"`);
53
202
  }
@@ -318,9 +467,14 @@ class VQuery {
318
467
  this.isInitialized = true;
319
468
  }
320
469
  }
321
- async createDataset(datasetId, data, type, datasetSchema) {
470
+ async createDataset(datasetId, data, type, columns = []) {
322
471
  await this.ensureInitialized();
323
472
  const dataSource = await DataSourceBuilder.from(type, data).build();
473
+ const datasetSchema = {
474
+ datasetId,
475
+ datasetAlias: datasetId,
476
+ columns: columns
477
+ };
324
478
  await this.indexedDB.writeDataset(datasetId, dataSource, datasetSchema);
325
479
  }
326
480
  async updateDataset(datasetId, data, type, datasetSchema) {
@@ -328,24 +482,23 @@ class VQuery {
328
482
  const dataSource = await DataSourceBuilder.from(type, data).build();
329
483
  await this.indexedDB.writeDataset(datasetId, dataSource, datasetSchema);
330
484
  }
331
- async deleteDataset(datasetId) {
485
+ async dropDataset(datasetId) {
332
486
  await this.ensureInitialized();
333
487
  await this.indexedDB.deleteDataset(datasetId);
334
488
  }
335
- async listDatasets() {
489
+ async hasDataset(datasetId) {
336
490
  await this.ensureInitialized();
337
- return this.indexedDB.listDatasets();
491
+ const datasets = await this.indexedDB.listDatasets();
492
+ return datasets.some((item)=>item.datasetId === datasetId);
338
493
  }
339
- async connectDataset(datasetId) {
494
+ async listDatasets() {
340
495
  await this.ensureInitialized();
341
- const dataset = new Dataset(this.duckDB, this.indexedDB, datasetId);
342
- await dataset.init();
343
- return dataset;
496
+ return this.indexedDB.listDatasets();
344
497
  }
345
- async connectTemporaryDataset(datasetId, temporaryDatasetSchema) {
498
+ async connectDataset(datasetId, temporaryColumns = []) {
346
499
  await this.ensureInitialized();
347
500
  const dataset = new Dataset(this.duckDB, this.indexedDB, datasetId);
348
- await dataset.init(temporaryDatasetSchema);
501
+ await dataset.init(temporaryColumns);
349
502
  return dataset;
350
503
  }
351
504
  async close() {
@@ -353,4 +506,4 @@ class VQuery {
353
506
  await this.duckDB.close();
354
507
  }
355
508
  }
356
- export { DataSourceBuilder, VQuery, isBase64Url, isHttpUrl, isUrl };
509
+ export { DataSourceBuilder, VQuery, convertDSLToSQL, isBase64Url, isHttpUrl, isUrl };
@@ -0,0 +1,2 @@
1
+ import type { SelectQueryBuilder } from 'kysely';
2
+ export declare const applyGroupBy: <DB, TB extends keyof DB & string, O>(qb: SelectQueryBuilder<DB, TB, O>, fields?: Array<string>) => SelectQueryBuilder<DB, TB, O>;
@@ -0,0 +1,3 @@
1
+ export { applyWhere } from './where';
2
+ export { applyGroupBy } from './groupBy';
3
+ export { applyLimit } from './limit';
@@ -0,0 +1,2 @@
1
+ import type { SelectQueryBuilder } from 'kysely';
2
+ export declare const applyLimit: <DB, TB extends keyof DB & string, O>(qb: SelectQueryBuilder<DB, TB, O>, limit?: number) => SelectQueryBuilder<DB, TB, O>;
@@ -0,0 +1,3 @@
1
+ import { Where, WhereClause } from '../../types';
2
+ import type { RawBuilder } from 'kysely';
3
+ export declare const applyWhere: <T>(where: Where<T> | WhereClause<T>) => RawBuilder<boolean>;
@@ -0,0 +1 @@
1
+ export { inlineParameters } from './inlineParameters';
@@ -0,0 +1 @@
1
+ export declare const inlineParameters: (sql: string, params: readonly unknown[]) => string;
@@ -0,0 +1 @@
1
+ export { PostgresDialect } from './postgresDialect';
@@ -0,0 +1,11 @@
1
+ import { Dialect, DummyDriver } from 'kysely';
2
+ import { PostgresQueryCompiler } from 'kysely';
3
+ import { PostgresAdapter } from 'kysely';
4
+ import { Kysely } from 'kysely';
5
+ import type { DatabaseIntrospector } from 'kysely';
6
+ export declare class PostgresDialect implements Dialect {
7
+ createDriver(): DummyDriver;
8
+ createQueryCompiler(): PostgresQueryCompiler;
9
+ createAdapter(): PostgresAdapter;
10
+ createIntrospector<DB = unknown>(db: Kysely<DB>): DatabaseIntrospector;
11
+ }
@@ -0,0 +1,2 @@
1
+ import { QueryDSL } from '../types';
2
+ export declare const convertDSLToSQL: <T, TableName extends string>(dsl: QueryDSL<T>, tableName: TableName) => string;
@@ -0,0 +1 @@
1
+ export { convertDSLToSQL } from './dslToSQL';
@@ -0,0 +1,8 @@
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;
7
+ export declare const escapeLiteral: <T>(value: T[keyof T]) => T[keyof T];
8
+ export declare const escapeValue: (value: unknown) => string;
@@ -0,0 +1 @@
1
+ export type GroupBy<T> = Array<keyof T>;
@@ -0,0 +1,4 @@
1
+ export type OrderBy<T> = Array<{
2
+ field: keyof T;
3
+ order?: 'asc' | 'desc';
4
+ }>;
@@ -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,7 @@
1
+ export type AggregateFunction = 'count' | 'sum' | 'avg' | 'min' | 'max';
2
+ export type SelectItem<T> = {
3
+ field: keyof T;
4
+ alias?: string;
5
+ func?: AggregateFunction;
6
+ };
7
+ export type Select<T> = Array<keyof T | SelectItem<T>>;
@@ -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] | 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 {};
@@ -0,0 +1,5 @@
1
+ export type { QueryDSL } from './QueryDSL';
2
+ export type { GroupBy } from './GroupBy';
3
+ export type { OrderBy } from './OrderBy';
4
+ export type { Select } from './Select';
5
+ export type { Where, WhereClause, WhereLeaf, WhereGroup } from './Where';
@@ -1,3 +1,4 @@
1
1
  export * from './DataSet';
2
2
  export * from './DataSource';
3
3
  export * from './QueryResult';
4
+ export * from './QueryDSL';
package/dist/vquery.d.ts CHANGED
@@ -9,7 +9,7 @@ export declare class VQuery {
9
9
  /**
10
10
  * 创建数据集,根据表结构和数据,存储信息到indexedDB
11
11
  */
12
- createDataset(datasetId: string, data: string | ArrayBuffer | Blob | TidyDatum[], type: DataSourceType, datasetSchema: DatasetSchema): Promise<void>;
12
+ createDataset(datasetId: string, data: string | ArrayBuffer | Blob | TidyDatum[], type: DataSourceType, columns?: DatasetColumn[]): Promise<void>;
13
13
  /**
14
14
  * 修改数据集,更新信息到indexedDB内
15
15
  */
@@ -17,7 +17,11 @@ export declare class VQuery {
17
17
  /**
18
18
  * 删除数据集,从indexdb移除数据集
19
19
  */
20
- deleteDataset(datasetId: string): Promise<void>;
20
+ dropDataset(datasetId: string): Promise<void>;
21
+ /**
22
+ * 检查数据集是否存在
23
+ */
24
+ hasDataset(datasetId: string): Promise<boolean>;
21
25
  /**
22
26
  * 获取所有可用数据集
23
27
  */
@@ -29,13 +33,7 @@ export declare class VQuery {
29
33
  /**
30
34
  * 连接数据集,返回数据集信息,从indexedDB获取表结构,使用DuckDB在内存中创建表
31
35
  */
32
- connectDataset(datasetId: string): Promise<Dataset>;
33
- /**
34
- * 连接临时数据集,返回数据集信息,从indexedDB获取表结构,使用DuckDB在内存中创建表
35
- * @param datasetId
36
- * @returns
37
- */
38
- connectTemporaryDataset(datasetId: string, temporaryDatasetSchema?: DatasetColumn[]): Promise<Dataset>;
36
+ connectDataset(datasetId: string, temporaryColumns?: DatasetColumn[]): Promise<Dataset>;
39
37
  /**
40
38
  * 关闭所有数据集连接,释放DuckDB资源
41
39
  */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@visactor/vquery",
3
- "version": "0.1.47",
3
+ "version": "0.1.49",
4
4
  "type": "module",
5
5
  "exports": {
6
6
  ".": {
@@ -15,14 +15,15 @@
15
15
  "dist"
16
16
  ],
17
17
  "dependencies": {
18
- "@duckdb/duckdb-wasm": "latest"
18
+ "@duckdb/duckdb-wasm": "1.30.0",
19
+ "kysely": "^0.27.2"
19
20
  },
20
21
  "devDependencies": {
21
22
  "@eslint/js": "^9.35.0",
22
23
  "@rslib/core": "^0.15.1",
23
- "@rstest/core": "^0.5.1",
24
+ "@rstest/core": "0.6.5",
24
25
  "@types/node": "^22.18.10",
25
- "@rstest/coverage-istanbul": "^0.0.2",
26
+ "@rstest/coverage-istanbul": "0.0.5",
26
27
  "eslint": "^9.35.0",
27
28
  "globals": "^16.3.0",
28
29
  "typescript": "^5.9.3",
@@ -33,6 +34,8 @@
33
34
  "dev": "rslib build --watch",
34
35
  "lint": "eslint .",
35
36
  "test": "rstest",
37
+ "test:update": "rstest --update",
38
+ "test:coverage": "rstest --coverage && open ./coverage/index.html",
36
39
  "type-check": "tsc --noEmit"
37
40
  }
38
41
  }