@hypequery/clickhouse 1.4.0-beta.2 → 1.5.0
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/README.md +65 -2
- package/dist/cli/bin.js +65 -23
- package/dist/cli/generate-types.js +42 -4
- package/dist/core/cache/cache-manager.d.ts +4 -0
- package/dist/core/cache/cache-manager.d.ts.map +1 -0
- package/dist/core/cache/cache-manager.js +176 -0
- package/dist/core/cache/controller.d.ts +15 -0
- package/dist/core/cache/controller.d.ts.map +1 -0
- package/dist/core/cache/controller.js +58 -0
- package/dist/core/cache/key.d.ts +11 -0
- package/dist/core/cache/key.d.ts.map +1 -0
- package/dist/core/cache/key.js +26 -0
- package/dist/core/cache/providers/memory-lru.d.ts +31 -0
- package/dist/core/cache/providers/memory-lru.d.ts.map +1 -0
- package/dist/core/cache/providers/memory-lru.js +156 -0
- package/dist/core/cache/providers/noop.d.ts +7 -0
- package/dist/core/cache/providers/noop.d.ts.map +1 -0
- package/dist/core/cache/providers/noop.js +11 -0
- package/dist/core/cache/runtime-context.d.ts +30 -0
- package/dist/core/cache/runtime-context.d.ts.map +1 -0
- package/dist/core/cache/runtime-context.js +58 -0
- package/dist/core/cache/serialization.d.ts +6 -0
- package/dist/core/cache/serialization.d.ts.map +1 -0
- package/dist/core/cache/serialization.js +166 -0
- package/dist/core/cache/types.d.ts +52 -0
- package/dist/core/cache/types.d.ts.map +1 -0
- package/dist/core/cache/types.js +1 -0
- package/dist/core/cache/utils.d.ts +9 -0
- package/dist/core/cache/utils.d.ts.map +1 -0
- package/dist/core/cache/utils.js +30 -0
- package/dist/core/connection.d.ts.map +1 -1
- package/dist/core/connection.js +4 -3
- package/dist/core/cross-filter.d.ts +12 -9
- package/dist/core/cross-filter.d.ts.map +1 -1
- package/dist/core/cross-filter.js +9 -6
- package/dist/core/env/auto-client.browser.d.ts +3 -0
- package/dist/core/env/auto-client.browser.d.ts.map +1 -0
- package/dist/core/env/auto-client.browser.js +3 -0
- package/dist/core/env/auto-client.d.ts +9 -0
- package/dist/core/env/auto-client.d.ts.map +1 -0
- package/dist/core/env/auto-client.js +21 -0
- package/dist/core/features/aggregations.d.ts +18 -22
- package/dist/core/features/aggregations.d.ts.map +1 -1
- package/dist/core/features/aggregations.js +6 -6
- package/dist/core/features/analytics.d.ts +15 -19
- package/dist/core/features/analytics.d.ts.map +1 -1
- package/dist/core/features/analytics.js +2 -2
- package/dist/core/features/cross-filtering.d.ts +4 -24
- package/dist/core/features/cross-filtering.d.ts.map +1 -1
- package/dist/core/features/cross-filtering.js +0 -34
- package/dist/core/features/executor.d.ts +11 -9
- package/dist/core/features/executor.d.ts.map +1 -1
- package/dist/core/features/executor.js +14 -5
- package/dist/core/features/filtering.d.ts +32 -28
- package/dist/core/features/filtering.d.ts.map +1 -1
- package/dist/core/features/filtering.js +27 -26
- package/dist/core/features/joins.d.ts +7 -10
- package/dist/core/features/joins.d.ts.map +1 -1
- package/dist/core/features/query-modifiers.d.ts +18 -21
- package/dist/core/features/query-modifiers.d.ts.map +1 -1
- package/dist/core/formatters/sql-formatter.d.ts.map +1 -1
- package/dist/core/formatters/sql-formatter.js +6 -0
- package/dist/core/join-relationships.d.ts +2 -1
- package/dist/core/join-relationships.d.ts.map +1 -1
- package/dist/core/query-builder.d.ts +67 -84
- package/dist/core/query-builder.d.ts.map +1 -1
- package/dist/core/query-builder.js +165 -135
- package/dist/core/tests/integration/setup.d.ts +9 -1
- package/dist/core/tests/integration/setup.d.ts.map +1 -1
- package/dist/core/tests/integration/setup.js +56 -22
- package/dist/core/tests/integration/test-config.d.ts +2 -2
- package/dist/core/tests/integration/test-config.d.ts.map +1 -1
- package/dist/core/tests/integration/test-config.js +3 -4
- package/dist/core/tests/integration/test-data.json +190 -0
- package/dist/core/tests/test-utils.d.ts +18 -3
- package/dist/core/tests/test-utils.d.ts.map +1 -1
- package/dist/core/tests/test-utils.js +37 -10
- package/dist/core/types/builder-state.d.ts +25 -0
- package/dist/core/types/builder-state.d.ts.map +1 -0
- package/dist/core/types/builder-state.js +1 -0
- package/dist/core/types/select-types.d.ts +32 -0
- package/dist/core/types/select-types.d.ts.map +1 -0
- package/dist/core/types/select-types.js +1 -0
- package/dist/core/types/type-helpers.d.ts +5 -0
- package/dist/core/types/type-helpers.d.ts.map +1 -0
- package/dist/core/types/type-helpers.js +1 -0
- package/dist/core/utils/logger.d.ts +6 -0
- package/dist/core/utils/logger.d.ts.map +1 -1
- package/dist/core/utils/logger.js +7 -2
- package/dist/core/utils/predicate-builder.d.ts +29 -0
- package/dist/core/utils/predicate-builder.d.ts.map +1 -0
- package/dist/core/utils/predicate-builder.js +92 -0
- package/dist/core/utils/sql-expressions.d.ts +24 -10
- package/dist/core/utils/sql-expressions.d.ts.map +1 -1
- package/dist/core/utils/sql-expressions.js +7 -30
- package/dist/core/utils/streaming-helpers.d.ts +2 -0
- package/dist/core/utils/streaming-helpers.d.ts.map +1 -0
- package/dist/core/utils/streaming-helpers.js +137 -0
- package/dist/core/validators/filter-validator.d.ts +2 -1
- package/dist/core/validators/filter-validator.d.ts.map +1 -1
- package/dist/core/validators/value-validator.d.ts +2 -1
- package/dist/core/validators/value-validator.d.ts.map +1 -1
- package/dist/index.d.ts +11 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -0
- package/dist/types/base.d.ts +10 -37
- package/dist/types/base.d.ts.map +1 -1
- package/dist/types/clickhouse-types.d.ts +9 -4
- package/dist/types/clickhouse-types.d.ts.map +1 -1
- package/dist/types/filters.d.ts +1 -1
- package/dist/types/filters.d.ts.map +1 -1
- package/dist/types/index.d.ts +2 -0
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/index.js +2 -0
- package/dist/types/schema.d.ts +19 -0
- package/dist/types/schema.d.ts.map +1 -0
- package/dist/types/schema.js +1 -0
- package/package.json +21 -23
- package/dist/core/features/pagination.d.ts +0 -23
- package/dist/core/features/pagination.d.ts.map +0 -1
- package/dist/core/features/pagination.js +0 -192
- package/dist/core/tests/integration/pagination-test-tbc.d.ts +0 -2
- package/dist/core/tests/integration/pagination-test-tbc.d.ts.map +0 -1
- package/dist/core/tests/integration/pagination-test-tbc.js +0 -189
- package/dist/core/tests/integration/test-initializer.d.ts +0 -7
- package/dist/core/tests/integration/test-initializer.d.ts.map +0 -1
- package/dist/core/tests/integration/test-initializer.js +0 -32
package/package.json
CHANGED
|
@@ -1,14 +1,23 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hypequery/clickhouse",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.5.0",
|
|
4
4
|
"description": "ClickHouse typescript query builder",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
7
7
|
"type": "module",
|
|
8
|
+
"browser": {
|
|
9
|
+
"./dist/core/env/auto-client.js": "./dist/core/env/auto-client.browser.js"
|
|
10
|
+
},
|
|
8
11
|
"exports": {
|
|
9
12
|
".": {
|
|
10
13
|
"types": "./dist/index.d.ts",
|
|
11
|
-
"import": "./dist/index.js"
|
|
14
|
+
"import": "./dist/index.js",
|
|
15
|
+
"require": "./dist/index.js"
|
|
16
|
+
},
|
|
17
|
+
"./cli": {
|
|
18
|
+
"types": "./dist/cli/index.d.ts",
|
|
19
|
+
"import": "./dist/cli/index.js",
|
|
20
|
+
"require": "./dist/cli/index.js"
|
|
12
21
|
}
|
|
13
22
|
},
|
|
14
23
|
"license": "Apache-2.0",
|
|
@@ -20,15 +29,14 @@
|
|
|
20
29
|
"verify-build": "node scripts/verify-build.js",
|
|
21
30
|
"diagnose-ci": "node scripts/diagnose-ci.js",
|
|
22
31
|
"dev": "tsc --watch",
|
|
23
|
-
"test": "npm run test:unit",
|
|
24
|
-
"test:unit": "
|
|
32
|
+
"test": "npm run test:types && npm run test:unit",
|
|
33
|
+
"test:unit": "vitest run --config vitest.config.ts",
|
|
34
|
+
"test:types": "tsc --project tsconfig.type-tests.json",
|
|
25
35
|
"test:integration": "node scripts/run-integration-tests.js",
|
|
26
|
-
"test:watch": "
|
|
27
|
-
"test:coverage": "
|
|
36
|
+
"test:watch": "vitest watch --config vitest.config.ts",
|
|
37
|
+
"test:coverage": "vitest run --coverage --config vitest.config.ts",
|
|
28
38
|
"test:cli": "node scripts/test-cli-integration.js",
|
|
29
|
-
"lint": "eslint src/**/*.ts",
|
|
30
|
-
"semantic-release": "npx semantic-release",
|
|
31
|
-
"release": "npx semantic-release --extends ./.releaserc.cjs --no-ci",
|
|
39
|
+
"lint": "eslint --ext .ts \"src/**/*.ts\"",
|
|
32
40
|
"docs:api": "typedoc --options typedoc.json",
|
|
33
41
|
"docs:mdx": "npm run docs:api && node scripts/process-typedoc-markdown.js"
|
|
34
42
|
},
|
|
@@ -52,27 +60,17 @@
|
|
|
52
60
|
}
|
|
53
61
|
},
|
|
54
62
|
"devDependencies": {
|
|
55
|
-
"@babel/plugin-transform-modules-commonjs": "^7.26.3",
|
|
56
63
|
"@clickhouse/client": "^1.11.2",
|
|
57
64
|
"@clickhouse/client-common": "^1.11.2",
|
|
58
65
|
"@clickhouse/client-web": "^1.11.2",
|
|
59
|
-
"@semantic-release/changelog": "^6.0.3",
|
|
60
|
-
"@semantic-release/commit-analyzer": "^11.1.0",
|
|
61
|
-
"@semantic-release/git": "^10.0.1",
|
|
62
|
-
"@semantic-release/github": "^9.2.6",
|
|
63
|
-
"@semantic-release/npm": "^11.0.2",
|
|
64
|
-
"@semantic-release/release-notes-generator": "^12.1.0",
|
|
65
|
-
"@types/jest": "^29.5.11",
|
|
66
66
|
"@types/node": "^18.19.80",
|
|
67
67
|
"glob": "^11.0.3",
|
|
68
|
-
"jest": "^29.7.0",
|
|
69
|
-
"jest-esbuild": "^0.3.0",
|
|
70
|
-
"semantic-release": "^23.0.2",
|
|
71
|
-
"ts-jest": "^29.1.1",
|
|
72
68
|
"ts-node": "^10.9.0",
|
|
73
69
|
"typedoc": "^0.28.1",
|
|
74
70
|
"typedoc-plugin-markdown": "^4.6.0",
|
|
75
|
-
"typescript": "^5.7.3"
|
|
71
|
+
"typescript": "^5.7.3",
|
|
72
|
+
"@vitest/coverage-v8": "^2.1.6",
|
|
73
|
+
"vitest": "^2.1.6"
|
|
76
74
|
},
|
|
77
75
|
"ts-node": {
|
|
78
76
|
"esm": true,
|
|
@@ -89,4 +87,4 @@
|
|
|
89
87
|
"publishConfig": {
|
|
90
88
|
"access": "public"
|
|
91
89
|
}
|
|
92
|
-
}
|
|
90
|
+
}
|
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
import { QueryBuilder } from '../query-builder.js';
|
|
2
|
-
import { ColumnType, PaginationOptions, PaginatedResult } from '../../types/index.js';
|
|
3
|
-
export declare class PaginationFeature<Schema extends {
|
|
4
|
-
[tableName: string]: {
|
|
5
|
-
[columnName: string]: ColumnType;
|
|
6
|
-
};
|
|
7
|
-
}, T, HasSelect extends boolean = false, Aggregations = {}, OriginalT = T> {
|
|
8
|
-
private builder;
|
|
9
|
-
private static cursorStacks;
|
|
10
|
-
private stackKey;
|
|
11
|
-
constructor(builder: QueryBuilder<Schema, T, HasSelect, Aggregations, OriginalT>);
|
|
12
|
-
private get cursorStack();
|
|
13
|
-
private set cursorStack(value);
|
|
14
|
-
private get currentPosition();
|
|
15
|
-
private set currentPosition(value);
|
|
16
|
-
private encodeCursor;
|
|
17
|
-
private decodeCursor;
|
|
18
|
-
paginate(options: PaginationOptions<T>): Promise<PaginatedResult<T>>;
|
|
19
|
-
private generateCursor;
|
|
20
|
-
firstPage(pageSize: number): Promise<PaginatedResult<T>>;
|
|
21
|
-
iteratePages(pageSize: number): AsyncGenerator<PaginatedResult<T>>;
|
|
22
|
-
}
|
|
23
|
-
//# sourceMappingURL=pagination.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"pagination.d.ts","sourceRoot":"","sources":["../../../src/core/features/pagination.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AACnD,OAAO,EAAE,UAAU,EAAE,iBAAiB,EAAE,eAAe,EAAyB,MAAM,sBAAsB,CAAC;AAE7G,qBAAa,iBAAiB,CAC5B,MAAM,SAAS;IAAE,CAAC,SAAS,EAAE,MAAM,GAAG;QAAE,CAAC,UAAU,EAAE,MAAM,GAAG,UAAU,CAAA;KAAE,CAAA;CAAE,EAC5E,CAAC,EACD,SAAS,SAAS,OAAO,GAAG,KAAK,EACjC,YAAY,GAAG,EAAE,EACjB,SAAS,GAAG,CAAC;IAKD,OAAO,CAAC,OAAO;IAH3B,OAAO,CAAC,MAAM,CAAC,YAAY,CAAiE;IAC5F,OAAO,CAAC,QAAQ,CAAS;gBAEL,OAAO,EAAE,YAAY,CAAC,MAAM,EAAE,CAAC,EAAE,SAAS,EAAE,YAAY,EAAE,SAAS,CAAC;IAQxF,OAAO,KAAK,WAAW,GAEtB;IAED,OAAO,KAAK,WAAW,QAGtB;IAED,OAAO,KAAK,eAAe,GAE1B;IAED,OAAO,KAAK,eAAe,QAG1B;IAED,OAAO,CAAC,YAAY;IAIpB,OAAO,CAAC,YAAY;IAId,QAAQ,CAAC,OAAO,EAAE,iBAAiB,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,eAAe,CAAC,CAAC,CAAC,CAAC;IA0I1E,OAAO,CAAC,cAAc;IAmBhB,SAAS,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,eAAe,CAAC,CAAC,CAAC,CAAC;IAIvD,YAAY,CAAC,QAAQ,EAAE,MAAM,GAAG,cAAc,CAAC,eAAe,CAAC,CAAC,CAAC,CAAC;CAkB1E"}
|
|
@@ -1,192 +0,0 @@
|
|
|
1
|
-
export class PaginationFeature {
|
|
2
|
-
builder;
|
|
3
|
-
static cursorStacks = new Map();
|
|
4
|
-
stackKey;
|
|
5
|
-
constructor(builder) {
|
|
6
|
-
this.builder = builder;
|
|
7
|
-
// Create a unique key for this pagination instance based on the table and sort
|
|
8
|
-
this.stackKey = builder.getTableName();
|
|
9
|
-
if (!PaginationFeature.cursorStacks.has(this.stackKey)) {
|
|
10
|
-
PaginationFeature.cursorStacks.set(this.stackKey, { stack: [], position: -1 });
|
|
11
|
-
}
|
|
12
|
-
}
|
|
13
|
-
get cursorStack() {
|
|
14
|
-
return PaginationFeature.cursorStacks.get(this.stackKey).stack;
|
|
15
|
-
}
|
|
16
|
-
set cursorStack(value) {
|
|
17
|
-
const current = PaginationFeature.cursorStacks.get(this.stackKey);
|
|
18
|
-
PaginationFeature.cursorStacks.set(this.stackKey, { ...current, stack: value });
|
|
19
|
-
}
|
|
20
|
-
get currentPosition() {
|
|
21
|
-
return PaginationFeature.cursorStacks.get(this.stackKey).position;
|
|
22
|
-
}
|
|
23
|
-
set currentPosition(value) {
|
|
24
|
-
const current = PaginationFeature.cursorStacks.get(this.stackKey);
|
|
25
|
-
PaginationFeature.cursorStacks.set(this.stackKey, { ...current, position: value });
|
|
26
|
-
}
|
|
27
|
-
encodeCursor(values) {
|
|
28
|
-
return Buffer.from(JSON.stringify(values)).toString('base64');
|
|
29
|
-
}
|
|
30
|
-
decodeCursor(cursor) {
|
|
31
|
-
return JSON.parse(Buffer.from(cursor, 'base64').toString());
|
|
32
|
-
}
|
|
33
|
-
async paginate(options) {
|
|
34
|
-
const { pageSize, after, before, orderBy = [] } = options;
|
|
35
|
-
const requestSize = pageSize + 1;
|
|
36
|
-
// Update cursor stack
|
|
37
|
-
if (after) {
|
|
38
|
-
// Moving forward: add new cursor and update position
|
|
39
|
-
if (this.currentPosition < this.cursorStack.length - 1) {
|
|
40
|
-
// If we're not at the end, truncate the stack
|
|
41
|
-
this.cursorStack = this.cursorStack.slice(0, this.currentPosition + 1);
|
|
42
|
-
}
|
|
43
|
-
this.cursorStack = [...this.cursorStack, after];
|
|
44
|
-
this.currentPosition = this.cursorStack.length - 1;
|
|
45
|
-
}
|
|
46
|
-
else if (before) {
|
|
47
|
-
// Moving backward: find the cursor in the stack
|
|
48
|
-
const cursorIndex = this.cursorStack.indexOf(before);
|
|
49
|
-
if (cursorIndex === -1) {
|
|
50
|
-
// If cursor not found in stack, add it
|
|
51
|
-
if (this.currentPosition === this.cursorStack.length - 1) {
|
|
52
|
-
this.cursorStack = [...this.cursorStack, before];
|
|
53
|
-
}
|
|
54
|
-
// Move back one position
|
|
55
|
-
this.currentPosition = Math.max(-1, this.currentPosition - 1);
|
|
56
|
-
}
|
|
57
|
-
else {
|
|
58
|
-
// Move to the previous cursor position
|
|
59
|
-
this.currentPosition = Math.max(-1, cursorIndex - 1);
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
else {
|
|
63
|
-
// Reset for first page only if we don't have a cursor
|
|
64
|
-
if (!this.cursorStack.length) {
|
|
65
|
-
this.cursorStack = [];
|
|
66
|
-
this.currentPosition = -1;
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
// Apply ordering first
|
|
70
|
-
orderBy.forEach(({ column, direction }) => {
|
|
71
|
-
this.builder.orderBy(column, direction);
|
|
72
|
-
});
|
|
73
|
-
// Handle cursor-based pagination
|
|
74
|
-
const cursor = after || before;
|
|
75
|
-
if (cursor && orderBy && orderBy.length > 0) {
|
|
76
|
-
const [{ column, direction }] = orderBy;
|
|
77
|
-
const columnName = String(column);
|
|
78
|
-
const cursorValues = this.decodeCursor(cursor);
|
|
79
|
-
const value = cursorValues[columnName];
|
|
80
|
-
if (before) {
|
|
81
|
-
// For backward pagination:
|
|
82
|
-
// If sorting DESC, we want records > cursor
|
|
83
|
-
// If sorting ASC, we want records < cursor
|
|
84
|
-
const operator = direction === 'DESC' ? 'gt' : 'lt';
|
|
85
|
-
this.builder.where(columnName, operator, value);
|
|
86
|
-
// Reverse the order for backward pagination
|
|
87
|
-
orderBy.forEach(({ column, direction }) => {
|
|
88
|
-
const reversedDirection = direction === 'DESC' ? 'ASC' : 'DESC';
|
|
89
|
-
this.builder.orderBy(column, reversedDirection);
|
|
90
|
-
});
|
|
91
|
-
}
|
|
92
|
-
else {
|
|
93
|
-
// For forward pagination:
|
|
94
|
-
// If sorting DESC, we want records < cursor
|
|
95
|
-
// If sorting ASC, we want records > cursor
|
|
96
|
-
const operator = direction === 'DESC' ? 'lt' : 'gt';
|
|
97
|
-
this.builder.where(columnName, operator, value);
|
|
98
|
-
}
|
|
99
|
-
}
|
|
100
|
-
this.builder.limit(requestSize);
|
|
101
|
-
// Execute query
|
|
102
|
-
let results = await this.builder.execute();
|
|
103
|
-
// For backward pagination, we need to reverse the results
|
|
104
|
-
if (before) {
|
|
105
|
-
results = results.reverse();
|
|
106
|
-
}
|
|
107
|
-
// Only take pageSize records for the actual data
|
|
108
|
-
const data = results.slice(0, pageSize);
|
|
109
|
-
// Generate cursors
|
|
110
|
-
const startCursor = data.length > 0 ? this.generateCursor(data[0], orderBy) : '';
|
|
111
|
-
const endCursor = data.length > 0 ? this.generateCursor(data[data.length - 1], orderBy) : '';
|
|
112
|
-
// Determine if there are more pages
|
|
113
|
-
const hasMore = results.length > pageSize;
|
|
114
|
-
// For the first page
|
|
115
|
-
if (!cursor) {
|
|
116
|
-
return {
|
|
117
|
-
data,
|
|
118
|
-
pageInfo: {
|
|
119
|
-
hasNextPage: hasMore,
|
|
120
|
-
hasPreviousPage: data.length > 0 && this.currentPosition > 0, // Only true if we have results AND previous cursors
|
|
121
|
-
startCursor,
|
|
122
|
-
endCursor,
|
|
123
|
-
totalCount: 0,
|
|
124
|
-
totalPages: 0,
|
|
125
|
-
pageSize
|
|
126
|
-
}
|
|
127
|
-
};
|
|
128
|
-
}
|
|
129
|
-
// For pages accessed via 'before' cursor
|
|
130
|
-
if (before) {
|
|
131
|
-
return {
|
|
132
|
-
data,
|
|
133
|
-
pageInfo: {
|
|
134
|
-
hasNextPage: true, // We can always go forward when we've gone back
|
|
135
|
-
hasPreviousPage: data.length > 0 && (this.currentPosition >= 0 || hasMore), // Need results to have previous page
|
|
136
|
-
startCursor,
|
|
137
|
-
endCursor,
|
|
138
|
-
totalCount: 0,
|
|
139
|
-
totalPages: 0,
|
|
140
|
-
pageSize
|
|
141
|
-
}
|
|
142
|
-
};
|
|
143
|
-
}
|
|
144
|
-
// For pages accessed via 'after' cursor
|
|
145
|
-
return {
|
|
146
|
-
data,
|
|
147
|
-
pageInfo: {
|
|
148
|
-
hasNextPage: hasMore, // Can go forward if we have more results
|
|
149
|
-
hasPreviousPage: data.length > 0, // Need results to have previous page
|
|
150
|
-
startCursor,
|
|
151
|
-
endCursor,
|
|
152
|
-
totalCount: 0,
|
|
153
|
-
totalPages: 0,
|
|
154
|
-
pageSize
|
|
155
|
-
}
|
|
156
|
-
};
|
|
157
|
-
}
|
|
158
|
-
generateCursor(record, orderBy) {
|
|
159
|
-
const cursorData = {};
|
|
160
|
-
if (orderBy) {
|
|
161
|
-
orderBy.forEach(({ column }) => {
|
|
162
|
-
const columnName = String(column);
|
|
163
|
-
cursorData[columnName] = record[columnName];
|
|
164
|
-
});
|
|
165
|
-
}
|
|
166
|
-
else {
|
|
167
|
-
// Use primary key or first column as default
|
|
168
|
-
const [firstColumn] = Object.keys(record);
|
|
169
|
-
if (firstColumn) {
|
|
170
|
-
cursorData[firstColumn] = record[firstColumn];
|
|
171
|
-
}
|
|
172
|
-
}
|
|
173
|
-
return this.encodeCursor(cursorData);
|
|
174
|
-
}
|
|
175
|
-
async firstPage(pageSize) {
|
|
176
|
-
return this.paginate({ pageSize });
|
|
177
|
-
}
|
|
178
|
-
async *iteratePages(pageSize) {
|
|
179
|
-
let currentCursor;
|
|
180
|
-
while (true) {
|
|
181
|
-
const result = await this.paginate({
|
|
182
|
-
pageSize,
|
|
183
|
-
after: currentCursor
|
|
184
|
-
});
|
|
185
|
-
yield result;
|
|
186
|
-
if (!result.pageInfo.hasNextPage) {
|
|
187
|
-
break;
|
|
188
|
-
}
|
|
189
|
-
currentCursor = result.pageInfo.endCursor;
|
|
190
|
-
}
|
|
191
|
-
}
|
|
192
|
-
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"pagination-test-tbc.d.ts","sourceRoot":"","sources":["../../../../src/core/tests/integration/pagination-test-tbc.ts"],"names":[],"mappings":""}
|
|
@@ -1,189 +0,0 @@
|
|
|
1
|
-
// Skipping tests becuase this feature is not ready
|
|
2
|
-
const SKIP_INTEGRATION_TESTS = true; // process.env.SKIP_INTEGRATION_TESTS === 'true' || process.env.CI === 'true';
|
|
3
|
-
describe('Integration Tests - Pagination', () => {
|
|
4
|
-
// Only run these tests if not skipped
|
|
5
|
-
// Removing until this feature is ready
|
|
6
|
-
// (SKIP_INTEGRATION_TESTS ? describe.skip : describe)('ClickHouse Integration', () => {
|
|
7
|
-
// let db: Awaited<ReturnType<typeof initializeTestConnection>>;
|
|
8
|
-
// beforeAll(async () => {
|
|
9
|
-
// if (!SKIP_INTEGRATION_TESTS) {
|
|
10
|
-
// try {
|
|
11
|
-
// // Start ClickHouse container
|
|
12
|
-
// startClickHouseContainer();
|
|
13
|
-
// // Initialize connection
|
|
14
|
-
// db = await initializeTestConnection();
|
|
15
|
-
// // Set up test database
|
|
16
|
-
// await setupTestDatabase();
|
|
17
|
-
// } catch (error) {
|
|
18
|
-
// console.error('Failed to set up integration tests:', error);
|
|
19
|
-
// throw error;
|
|
20
|
-
// }
|
|
21
|
-
// }
|
|
22
|
-
// }, 60000); // Allow up to 60 seconds for setup
|
|
23
|
-
// afterAll(async () => {
|
|
24
|
-
// if (!SKIP_INTEGRATION_TESTS) {
|
|
25
|
-
// try {
|
|
26
|
-
// // Wait for any pending operations to complete
|
|
27
|
-
// console.log('Starting test cleanup...');
|
|
28
|
-
// await new Promise(resolve => setTimeout(resolve, 1000));
|
|
29
|
-
// // Close any active client connections to prevent lingering queries
|
|
30
|
-
// if (db) {
|
|
31
|
-
// try {
|
|
32
|
-
// const client = ClickHouseConnection.getClient();
|
|
33
|
-
// console.log('Closing ClickHouse client connection...');
|
|
34
|
-
// // Wait for any in-flight queries to complete
|
|
35
|
-
// await new Promise(resolve => setTimeout(resolve, 500));
|
|
36
|
-
// // Ensure we're not running any more queries
|
|
37
|
-
// await client.close().catch(err => {
|
|
38
|
-
// console.error('Error closing ClickHouse client:', err);
|
|
39
|
-
// });
|
|
40
|
-
// console.log('ClickHouse client closed successfully');
|
|
41
|
-
// } catch (closeError) {
|
|
42
|
-
// console.error('Error during client close:', closeError);
|
|
43
|
-
// }
|
|
44
|
-
// }
|
|
45
|
-
// // Then stop the container
|
|
46
|
-
// console.log('Stopping ClickHouse container...');
|
|
47
|
-
// await stopClickHouseContainer();
|
|
48
|
-
// console.log('Cleanup completed');
|
|
49
|
-
// // Make sure all async operations have a chance to complete
|
|
50
|
-
// await new Promise(resolve => setTimeout(resolve, 500));
|
|
51
|
-
// } catch (error) {
|
|
52
|
-
// console.error('Error during test cleanup:', error);
|
|
53
|
-
// }
|
|
54
|
-
// }
|
|
55
|
-
// }, 15000); // Allow up to 15 seconds for teardown
|
|
56
|
-
// test('should paginate results with cursor-based pagination', async () => {
|
|
57
|
-
// // Get first page
|
|
58
|
-
// const firstPage = await db.table('test_table')
|
|
59
|
-
// .orderBy('id', 'ASC')
|
|
60
|
-
// .paginate({
|
|
61
|
-
// pageSize: 2,
|
|
62
|
-
// orderBy: [{ column: 'id', direction: 'ASC' }]
|
|
63
|
-
// });
|
|
64
|
-
// expect(firstPage.data).toHaveLength(2);
|
|
65
|
-
// expect(firstPage.pageInfo.hasNextPage).toBe(true);
|
|
66
|
-
// expect(firstPage.pageInfo.hasPreviousPage).toBe(false);
|
|
67
|
-
// expect(firstPage.pageInfo.startCursor).toBeTruthy();
|
|
68
|
-
// expect(firstPage.pageInfo.endCursor).toBeTruthy();
|
|
69
|
-
// expect(firstPage.data[0].id).toBe(1);
|
|
70
|
-
// expect(firstPage.data[1].id).toBe(2);
|
|
71
|
-
// // Get second page using the cursor
|
|
72
|
-
// const secondPage = await db.table('test_table')
|
|
73
|
-
// .orderBy('id', 'ASC')
|
|
74
|
-
// .paginate({
|
|
75
|
-
// pageSize: 2,
|
|
76
|
-
// after: firstPage.pageInfo.endCursor,
|
|
77
|
-
// orderBy: [{ column: 'id', direction: 'ASC' }]
|
|
78
|
-
// });
|
|
79
|
-
// expect(secondPage.data).toHaveLength(2);
|
|
80
|
-
// expect(secondPage.pageInfo.hasNextPage).toBe(true);
|
|
81
|
-
// expect(secondPage.pageInfo.hasPreviousPage).toBe(true);
|
|
82
|
-
// expect(secondPage.data[0].id).toBe(3);
|
|
83
|
-
// expect(secondPage.data[1].id).toBe(4);
|
|
84
|
-
// // Get third page using the cursor
|
|
85
|
-
// const thirdPage = await db.table('test_table')
|
|
86
|
-
// .orderBy('id', 'ASC')
|
|
87
|
-
// .paginate({
|
|
88
|
-
// pageSize: 2,
|
|
89
|
-
// after: secondPage.pageInfo.endCursor,
|
|
90
|
-
// orderBy: [{ column: 'id', direction: 'ASC' }]
|
|
91
|
-
// });
|
|
92
|
-
// expect(thirdPage.data).toHaveLength(1); // Only one record left
|
|
93
|
-
// expect(thirdPage.pageInfo.hasNextPage).toBe(false);
|
|
94
|
-
// expect(thirdPage.pageInfo.hasPreviousPage).toBe(true);
|
|
95
|
-
// expect(thirdPage.data[0].id).toBe(5);
|
|
96
|
-
// });
|
|
97
|
-
// test('should navigate backwards with cursor-based pagination', async () => {
|
|
98
|
-
// // Get first page
|
|
99
|
-
// const firstPage = await db.table('test_table')
|
|
100
|
-
// .orderBy('id', 'ASC')
|
|
101
|
-
// .paginate({
|
|
102
|
-
// pageSize: 2,
|
|
103
|
-
// orderBy: [{ column: 'id', direction: 'ASC' }]
|
|
104
|
-
// });
|
|
105
|
-
// // Get second page
|
|
106
|
-
// const secondPage = await db.table('test_table')
|
|
107
|
-
// .orderBy('id', 'ASC')
|
|
108
|
-
// .paginate({
|
|
109
|
-
// pageSize: 2,
|
|
110
|
-
// after: firstPage.pageInfo.endCursor,
|
|
111
|
-
// orderBy: [{ column: 'id', direction: 'ASC' }]
|
|
112
|
-
// });
|
|
113
|
-
// // Navigate back to first page
|
|
114
|
-
// const backToFirstPage = await db.table('test_table')
|
|
115
|
-
// .orderBy('id', 'ASC')
|
|
116
|
-
// .paginate({
|
|
117
|
-
// pageSize: 2,
|
|
118
|
-
// before: secondPage.pageInfo.startCursor,
|
|
119
|
-
// orderBy: [{ column: 'id', direction: 'ASC' }]
|
|
120
|
-
// });
|
|
121
|
-
// expect(backToFirstPage.data).toHaveLength(2);
|
|
122
|
-
// expect(backToFirstPage.pageInfo.hasNextPage).toBe(true);
|
|
123
|
-
// expect(backToFirstPage.pageInfo.hasPreviousPage).toBe(false);
|
|
124
|
-
// expect(backToFirstPage.data[0].id).toBe(1);
|
|
125
|
-
// expect(backToFirstPage.data[1].id).toBe(2);
|
|
126
|
-
// });
|
|
127
|
-
// test('should handle empty results', async () => {
|
|
128
|
-
// const result = await db.table('test_table')
|
|
129
|
-
// .where('id', 'gt', 100) // No records with id > 100
|
|
130
|
-
// .paginate({
|
|
131
|
-
// pageSize: 10,
|
|
132
|
-
// orderBy: [{ column: 'id', direction: 'ASC' }]
|
|
133
|
-
// });
|
|
134
|
-
// expect(result.data).toHaveLength(0);
|
|
135
|
-
// expect(result.pageInfo.hasNextPage).toBe(false);
|
|
136
|
-
// expect(result.pageInfo.hasPreviousPage).toBe(false);
|
|
137
|
-
// expect(result.pageInfo.startCursor).toBe('');
|
|
138
|
-
// expect(result.pageInfo.endCursor).toBe('');
|
|
139
|
-
// });
|
|
140
|
-
// test('should handle exactly pageSize results', async () => {
|
|
141
|
-
// const result = await db.table('test_table')
|
|
142
|
-
// .where('id', 'lte', 2) // Only 2 records
|
|
143
|
-
// .paginate({
|
|
144
|
-
// pageSize: 2,
|
|
145
|
-
// orderBy: [{ column: 'id', direction: 'ASC' }]
|
|
146
|
-
// });
|
|
147
|
-
// console.log('CHECK!!!!: ', result.pageInfo)
|
|
148
|
-
// expect(result.data).toHaveLength(2);
|
|
149
|
-
// expect(result.pageInfo.hasNextPage).toBe(false);
|
|
150
|
-
// expect(result.pageInfo.hasPreviousPage).toBe(false);
|
|
151
|
-
// });
|
|
152
|
-
// test('should iterate through all pages', async () => {
|
|
153
|
-
// const allResults: any[] = [];
|
|
154
|
-
// for await (const page of db.table('test_table')
|
|
155
|
-
// .orderBy('id', 'ASC')
|
|
156
|
-
// .iteratePages(2)) {
|
|
157
|
-
// allResults.push(...page.data);
|
|
158
|
-
// }
|
|
159
|
-
// expect(allResults).toHaveLength(TEST_DATA.test_table.length);
|
|
160
|
-
// // Check that all records were retrieved in the correct order
|
|
161
|
-
// for (let i = 0; i < allResults.length; i++) {
|
|
162
|
-
// expect(allResults[i].id).toBe(i + 1);
|
|
163
|
-
// }
|
|
164
|
-
// });
|
|
165
|
-
// test('should paginate with complex ordering', async () => {
|
|
166
|
-
// // First page ordered by price descending
|
|
167
|
-
// const firstPage = await db.table('test_table')
|
|
168
|
-
// .paginate({
|
|
169
|
-
// pageSize: 2,
|
|
170
|
-
// orderBy: [{ column: 'price', direction: 'DESC' }]
|
|
171
|
-
// });
|
|
172
|
-
// expect(firstPage.data).toHaveLength(2);
|
|
173
|
-
// // Verify ordering
|
|
174
|
-
// expect(Number(firstPage.data[0].price)).toBeGreaterThanOrEqual(Number(firstPage.data[1].price));
|
|
175
|
-
// // Get second page
|
|
176
|
-
// const secondPage = await db.table('test_table')
|
|
177
|
-
// .paginate({
|
|
178
|
-
// pageSize: 2,
|
|
179
|
-
// after: firstPage.pageInfo.endCursor,
|
|
180
|
-
// orderBy: [{ column: 'price', direction: 'DESC' }]
|
|
181
|
-
// });
|
|
182
|
-
// expect(secondPage.data).toHaveLength(2);
|
|
183
|
-
// // Verify ordering continues correctly
|
|
184
|
-
// expect(Number(firstPage.data[1].price)).toBeGreaterThanOrEqual(Number(secondPage.data[0].price));
|
|
185
|
-
// expect(Number(secondPage.data[0].price)).toBeGreaterThanOrEqual(Number(secondPage.data[1].price));
|
|
186
|
-
// });
|
|
187
|
-
// });
|
|
188
|
-
});
|
|
189
|
-
export {};
|
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Common initialization for all integration tests
|
|
3
|
-
* Import this at the beginning of each test file to ensure consistent setup
|
|
4
|
-
*/
|
|
5
|
-
export declare const skipIntegrationTests: () => boolean;
|
|
6
|
-
export declare const initializeForTest: () => Promise<void>;
|
|
7
|
-
//# sourceMappingURL=test-initializer.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"test-initializer.d.ts","sourceRoot":"","sources":["../../../../src/core/tests/integration/test-initializer.ts"],"names":[],"mappings":"AAAA;;;GAGG;AASH,eAAO,MAAM,oBAAoB,eAEhC,CAAC;AAEF,eAAO,MAAM,iBAAiB,QAAa,OAAO,CAAC,IAAI,CAqBtD,CAAC"}
|
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Common initialization for all integration tests
|
|
3
|
-
* Import this at the beginning of each test file to ensure consistent setup
|
|
4
|
-
*/
|
|
5
|
-
import { startClickHouseContainer, waitForClickHouse, ensureConnectionInitialized, setupTestDatabase } from './setup.js';
|
|
6
|
-
export const skipIntegrationTests = () => {
|
|
7
|
-
return process.env.SKIP_INTEGRATION_TESTS === 'true' || process.env.CI === 'true';
|
|
8
|
-
};
|
|
9
|
-
export const initializeForTest = async () => {
|
|
10
|
-
if (skipIntegrationTests()) {
|
|
11
|
-
return;
|
|
12
|
-
}
|
|
13
|
-
try {
|
|
14
|
-
// Initialize the connection
|
|
15
|
-
ensureConnectionInitialized();
|
|
16
|
-
// Make sure container is running
|
|
17
|
-
await startClickHouseContainer();
|
|
18
|
-
// Wait for ClickHouse to be ready
|
|
19
|
-
await waitForClickHouse();
|
|
20
|
-
// Set up the test database
|
|
21
|
-
await setupTestDatabase();
|
|
22
|
-
}
|
|
23
|
-
catch (error) {
|
|
24
|
-
console.error('Failed to initialize test environment:', error);
|
|
25
|
-
throw error;
|
|
26
|
-
}
|
|
27
|
-
};
|
|
28
|
-
// Automatically initialize when this module is imported
|
|
29
|
-
initializeForTest().catch(error => {
|
|
30
|
-
console.error('Test initialization failed:', error);
|
|
31
|
-
process.exit(1);
|
|
32
|
-
});
|