Skip to content

Commit 7bef68f

Browse files
panotitokarenko
andauthored
feat(): upgrade to Cubejs latest version (#4)
* feat(): add support for pre-aggregation in Cube Store (#3) * feat(): upgrade driver with @cubejs-backend/base-driver * feat(): improve filtering, measures support (#2) * Improve filtering, measures support - Add support for all the filtering capabilities of the Cube, including parametrized filters; - Improve support of measures defined in either of the following forms: ... measures: { count: { sql: 'column_name', type: 'count', }, }, ... measures: { count: { sql: 'COUNT(column_name)', type: 'number', }, }, ... * Add support for COUNT DISTINCT measure measures: { ... countDistinctMeasure: { sql: 'column_name', type: 'countDistinct', }, ... } --------- Co-authored-by: Thanh Pham <[email protected]> * feat(): update sql-utils, add unit tests * ci(): fix workflow * ci(): test node * ci(): remove support node 14 * fix(): import enum CollectionType --------- Co-authored-by: Dmitrii <[email protected]>
1 parent 833258e commit 7bef68f

10 files changed

+6734
-5203
lines changed

.github/workflows/release.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ on:
1515
# - tsconfig.json
1616

1717
env:
18-
NODE_VERSION: "14.x"
18+
NODE_VERSION: "16.x"
1919

2020
jobs:
2121
publish-npm:

.github/workflows/test.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ jobs:
4141

4242
strategy:
4343
matrix:
44-
node-version: [12.x, 14.x, 16.x]
44+
node-version: [18.x, 16.x]
4545
# See supported Node.js release schedule at https://nodejs.org/en/about/releases/
4646

4747
steps:

docker-compose.yml

+1-2
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
version: '3'
2+
23
services:
34
arangodb:
45
image: arangodb/arangodb:3.9.1
@@ -19,8 +20,6 @@ services:
1920
retries: 3
2021
start_period: 10s
2122

22-
23-
2423
volumes:
2524
arangodb_data:
2625
driver: local

package-lock.json

+6,378-5,061
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

+11-12
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
"name": "arangodb-cubejs-driver",
33
"description": "Cube.js arangodb driver",
44
"author": "pnthanh",
5-
"version": "0.0.5",
5+
"version": "0.1.0",
66
"repository": {
77
"type": "git",
88
"url": "https://github.com/panoti/cubejs-arangodb-driver.git"
@@ -24,20 +24,19 @@
2424
"dist"
2525
],
2626
"dependencies": {
27-
"arangojs": "^7.8.0",
28-
"pgsql-ast-parser": "^10.5.2"
27+
"@cubejs-backend/base-driver": "^0.33.41",
28+
"arangojs": "^8.4.0",
29+
"pgsql-ast-parser": "^11.1.0"
2930
},
3031
"license": "MIT",
3132
"devDependencies": {
32-
"@cubejs-backend/linter": "^0.30.0",
33-
"@cubejs-backend/query-orchestrator": "^0.30.34",
34-
"@cubejs-backend/schema-compiler": "^0.30.34",
35-
"@types/jest": "^28.1.6",
36-
"jest": "^28.1.3",
37-
"rimraf": "^3.0.2",
38-
"testcontainers": "^8.12.0",
39-
"ts-jest": "^28.0.7",
40-
"typescript": "^4.7.4"
33+
"@cubejs-backend/linter": "^0.33.0",
34+
"@types/jest": "^29.5.3",
35+
"jest": "^29.6.2",
36+
"rimraf": "^5.0.1",
37+
"testcontainers": "^9.12.0",
38+
"ts-jest": "^29.1.1",
39+
"typescript": "~4.9.5"
4140
},
4241
"eslintConfig": {
4342
"extends": "./node_modules/@cubejs-backend/linter/index.js"

src/arangodb-driver.ts

+79-86
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,20 @@
1-
import { DownloadQueryResultsOptions, DownloadQueryResultsResult, DownloadTableCSVData, DownloadTableData, DownloadTableMemoryData, DriverInterface, ExternalDriverCompatibilities, IndexesSQL, QueryOptions, StreamOptions, StreamTableData, TableColumn, TableStructure, UnloadOptions } from '@cubejs-backend/query-orchestrator';
2-
import { CollectionType, Database } from 'arangojs';
1+
import { BaseDriver, DownloadQueryResultsOptions, DownloadQueryResultsResult, QueryOptions, Row, TableColumn, TableStructure } from '@cubejs-backend/base-driver';
2+
import { Database } from 'arangojs';
3+
import { CollectionType } from 'arangojs/collection';
34
import { Config } from 'arangojs/connection';
4-
import { ArangoDbQuery } from './arangodb-query';
55
import { sql2aql } from './sql-utils';
66

77
export declare type TableMap = Record<string, TableColumn[]>;
88
export declare type SchemaStructure = Record<string, TableMap>;
99

10-
const DbTypeToGenericType = {
10+
const ArangoToGenericType = {
1111
number: 'double',
1212
string: 'text',
1313
bool: 'boolean'
1414
};
1515

16-
const sortByKeys = (unordered) => {
17-
const ordered = {};
16+
const sortByKeys = (unordered: any) => {
17+
const ordered: any = {};
1818

1919
Object.keys(unordered).sort().forEach((key) => {
2020
ordered[key] = unordered[key];
@@ -23,29 +23,38 @@ const sortByKeys = (unordered) => {
2323
return ordered;
2424
};
2525

26-
export class ArangoDbDriver implements DriverInterface {
26+
export class ArangoDbDriver extends BaseDriver {
2727
/**
2828
* Returns default concurrency value.
29+
* @return {number}
2930
*/
3031
public static getDefaultConcurrency(): number {
3132
return 2;
3233
}
3334

3435
public static driverEnvVariables() {
3536
return [
36-
'CUBEJS_DB_URL'
37+
'CUBEJS_DB_URL',
3738
];
3839
}
3940

40-
public static dialectClass() {
41-
return ArangoDbQuery;
42-
}
43-
4441
private config: Config;
4542

4643
private client: Database;
4744

48-
public constructor(config: Config = {}) {
45+
public constructor(
46+
config: Partial<Config> & {
47+
/**
48+
* Time to wait for a response from a connection after validation
49+
* request before determining it as not valid. Default - 60000 ms.
50+
*/
51+
testConnectionTimeout?: number,
52+
} = {}
53+
) {
54+
super({
55+
testConnectionTimeout: config.testConnectionTimeout || 60000,
56+
});
57+
4958
const auth = {
5059
username: process.env.CUBEJS_DB_USER,
5160
password: process.env.CUBEJS_DB_PASS,
@@ -75,6 +84,39 @@ export class ArangoDbDriver implements DriverInterface {
7584
return await cursor.next();
7685
}
7786

87+
public async query<R = unknown>(_query: string, _values?: unknown[], _options?: QueryOptions): Promise<R[]> {
88+
// console.log(_query, _values, _options);
89+
const aqlQuery = sql2aql(_query, _values);
90+
const cursor = await this.client.query(aqlQuery);
91+
const result = await cursor.all();
92+
93+
await cursor.kill();
94+
95+
return result;
96+
}
97+
98+
public async downloadQueryResults(query: string, values: unknown[], _options: DownloadQueryResultsOptions): Promise<DownloadQueryResultsResult> {
99+
const rows = await this.query<Row>(query, values);
100+
const columnTypes: TableStructure = [];
101+
102+
Object.entries(rows[0]).forEach((cols) => {
103+
const [column, value] = cols;
104+
const type = typeof value; // TODO: check float and integer
105+
const genericType = ArangoToGenericType[type];
106+
107+
if (!genericType) {
108+
throw new Error(`Unable to translate type for column "${column}" with type: ${type}`);
109+
}
110+
111+
columnTypes.push({ name: column, type: genericType });
112+
});
113+
114+
return {
115+
rows,
116+
types: columnTypes
117+
};
118+
};
119+
78120
public async release() {
79121
await this.client.close();
80122
}
@@ -94,94 +136,49 @@ export class ArangoDbDriver implements DriverInterface {
94136
for (const collection of collections) {
95137
const collectionMeta = await collection.get();
96138

97-
if (collectionMeta.type !== CollectionType.DOCUMENT_COLLECTION) continue;
98-
99-
schema[collection.name] = await this.tableColumnTypes(collection.name);
139+
if (collectionMeta.type === CollectionType.DOCUMENT_COLLECTION) {
140+
schema[collection.name] = await this.tableColumnTypes(collection.name);
141+
}
100142
}
101143

102144
schema = sortByKeys(schema);
103145
result[schemaName] = schema;
104146

105-
return sortByKeys(result);
106-
}
107-
108-
public async createSchemaIfNotExists(schemaName: string): Promise<any> {
109-
throw new Error('Method not implemented.');
110-
}
111-
112-
public async uploadTableWithIndexes(table: string, columns: TableStructure, tableData: DownloadTableData, indexesSql: IndexesSQL, uniqueKeyColumns: string[], queryTracingObj: any): Promise<void> {
113-
throw new Error('Method not implemented.');
114-
}
115-
116-
public loadPreAggregationIntoTable: (preAggregationTableName: string, loadSql: string, params: any, options: any) => Promise<any> =
117-
async (preAggregationTableName: string, loadSql: string, params: any, options: any) => {
118-
throw new Error('Method not implemented.');
119-
};
120-
121-
public async query<R = unknown>(query: string, params: unknown[], options?: QueryOptions): Promise<R[]> {
122-
console.log(query, params, options);
123-
const aqlQuery = sql2aql(query);
124-
const cursor = await this.client.query(aqlQuery);
125-
const result = cursor.all();
126-
127-
await cursor.kill();
128-
129147
return result;
130148
}
131149

132-
public tableColumnTypes: (table: string) => Promise<TableStructure> =
133-
async (table: string) => {
134-
const columns: TableStructure = [];
135-
// TODO: can optimize by schema registry or swagger json schema
136-
const attrMap = await this.aggrAttrs(table);
137-
const attrNames = Object.keys(attrMap);
150+
public async tableColumnTypes(table: string): Promise<TableStructure> {
151+
const columns: TableStructure = [];
152+
// TODO: can optimize by schema registry or swagger json schema
153+
const attrMap = await this.aggrAttrs(table);
154+
const attrNames = Object.keys(attrMap);
138155

139-
for (const attrName of attrNames) {
140-
const attrType = attrMap[attrName];
156+
for (const attrName of attrNames) {
157+
const attrType = attrMap[attrName];
141158

142-
if (this.toGenericType(attrType)) {
143-
columns.push({ name: attrName, type: attrType, attributes: [] });
144-
}
159+
if (this.toGenericType(attrType)) {
160+
columns.push({ name: attrName, type: attrType, attributes: [] });
145161
}
162+
}
146163

147-
return columns.sort();
148-
};
149-
150-
public getTablesQuery: (schemaName: string) => Promise<{ table_name?: string; TABLE_NAME?: string; }[]> =
151-
async (schemaName: string) => {
152-
const collections = await this.client.collections();
153-
return collections.map((col) => ({ table_name: col.name }));
154-
};
155-
156-
public dropTable: (tableName: string, options?: QueryOptions) => Promise<unknown> =
157-
(tableName: string, options?: QueryOptions) => {
158-
throw new Error('Method not implemented.');
159-
};
160-
161-
public downloadQueryResults: (query: string, values: unknown[], options: DownloadQueryResultsOptions) => Promise<DownloadQueryResultsResult> =
162-
async (query: string, values: unknown[], options: DownloadQueryResultsOptions) => {
163-
throw new Error('Method not implemented.');
164-
};
165-
166-
public downloadTable: (table: string, options: ExternalDriverCompatibilities) => Promise<DownloadTableMemoryData | DownloadTableCSVData> =
167-
async (table: string, options: ExternalDriverCompatibilities) => {
168-
throw new Error('Method not implemented.');
169-
};
164+
return columns.sort();
165+
}
170166

171167
// public stream?: (table: string, values: unknown[], options: StreamOptions) => Promise<StreamTableData>;
172168
// public unload?: (table: string, options: UnloadOptions) => Promise<DownloadTableCSVData>;
173169
// public isUnloadSupported?: (options: UnloadOptions) => Promise<boolean>;
174170

175-
public nowTimestamp(): number {
176-
return Date.now();
177-
}
171+
public toGenericType(columnType: string): string {
172+
columnType = columnType.toLowerCase();
173+
174+
if (columnType in ArangoToGenericType) {
175+
return ArangoToGenericType[columnType];
176+
}
178177

179-
// TODO: add to interface too
180-
public quoteIdentifier(identifier: string) {
181-
return `"${identifier}"`;
178+
return super.toGenericType(columnType);
182179
}
183180

184-
private async aggrAttrs(collectionName: string) {
181+
private async aggrAttrs(collectionName: string): Promise<Record<string, string>> {
185182
const cursor = await this.client.query(`
186183
FOR i IN [1]
187184
LET attrMaps = (
@@ -196,7 +193,7 @@ FOR i IN [1]
196193
RETURN ZIP(attributes[*].name, attributes[*].type)
197194
)
198195
RETURN MERGE(attrMaps)`);
199-
let result: any = { id: 'string' };
196+
let result: Record<string, string> = { id: 'string' };
200197

201198
if (cursor.hasNext) {
202199
result = {
@@ -208,8 +205,4 @@ FOR i IN [1]
208205
await cursor.kill();
209206
return result;
210207
}
211-
212-
private toGenericType(columnType) {
213-
return DbTypeToGenericType[columnType.toLowerCase()] || columnType;
214-
}
215208
}

src/arangodb-query.ts

-4
This file was deleted.

src/index.ts

+1-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
import { ArangoDbDriver } from './arangodb-driver';
22

33
export * from './arangodb-driver';
4-
export * from './arangodb-query';
54

6-
export default ArangoDbDriver;
5+
export default ArangoDbDriver;

0 commit comments

Comments
 (0)