@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.
@@ -0,0 +1,2 @@
1
+ import { Where, WhereClause } from '../../types';
2
+ export declare const applyWhere: <T>(where: Where<T> | WhereClause<T>) => string;
@@ -0,0 +1,2 @@
1
+ import { QueryDSL } from '../../types';
2
+ export declare const convertDSLToSQL: <T>(dsl: QueryDSL<T>, tableName: string) => string;
@@ -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,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][];
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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@visactor/vquery",
3
- "version": "0.1.47",
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": "latest"
18
+ "@duckdb/duckdb-wasm": "1.30.0"
19
19
  },
20
20
  "devDependencies": {
21
21
  "@eslint/js": "^9.35.0",