@hypequery/clickhouse 0.2.1 → 0.2.2-beta.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-CLI.md +1 -1
- package/dist/cli/bin.js +129 -37
- package/dist/cli/generate-types.js +104 -15
- package/dist/core/connection.d.ts +112 -0
- package/dist/core/connection.d.ts.map +1 -1
- package/dist/core/connection.js +148 -25
- package/dist/core/cross-filter.d.ts +69 -0
- package/dist/core/cross-filter.d.ts.map +1 -1
- package/dist/core/cross-filter.js +1 -83
- package/dist/core/features/aggregations.d.ts +102 -0
- package/dist/core/features/analytics.d.ts +66 -0
- package/dist/core/features/analytics.d.ts.map +1 -1
- package/dist/core/features/cross-filtering.d.ts +31 -0
- package/dist/core/features/cross-filtering.d.ts.map +1 -0
- package/dist/core/features/cross-filtering.js +123 -0
- package/dist/core/features/executor.d.ts +19 -0
- package/dist/core/features/executor.js +3 -3
- package/dist/core/features/filtering.d.ts +95 -0
- package/dist/core/features/filtering.d.ts.map +1 -1
- package/dist/core/features/filtering.js +89 -3
- package/dist/core/features/joins.d.ts +29 -0
- package/dist/core/features/pagination.d.ts +23 -0
- package/dist/core/features/query-modifiers.d.ts +119 -0
- package/dist/core/formatters/sql-formatter.d.ts +9 -0
- package/dist/core/formatters/sql-formatter.d.ts.map +1 -1
- package/dist/core/formatters/sql-formatter.js +61 -5
- package/dist/core/join-relationships.d.ts +50 -0
- package/dist/core/join-relationships.d.ts.map +1 -1
- package/dist/core/query-builder.d.ts +258 -0
- package/dist/core/query-builder.d.ts.map +1 -1
- package/dist/core/query-builder.js +88 -27
- package/dist/core/tests/index.d.ts +2 -0
- package/dist/core/tests/integration/pagination-test-tbc.d.ts +2 -0
- package/dist/core/tests/integration/pagination-test-tbc.d.ts.map +1 -0
- package/dist/core/tests/integration/pagination-test-tbc.js +189 -0
- package/dist/core/tests/integration/setup.d.ts +40 -0
- package/dist/core/tests/integration/setup.d.ts.map +1 -1
- package/dist/core/tests/integration/setup.js +279 -238
- package/dist/core/tests/integration/test-config.d.ts +15 -0
- package/dist/core/tests/integration/test-config.d.ts.map +1 -0
- package/dist/core/tests/integration/test-config.js +15 -0
- package/dist/core/tests/integration/test-initializer.d.ts +7 -0
- package/dist/core/tests/integration/test-initializer.d.ts.map +1 -0
- package/dist/core/tests/integration/test-initializer.js +32 -0
- package/dist/core/tests/test-utils.d.ts +29 -0
- package/dist/core/tests/test-utils.d.ts.map +1 -1
- package/dist/core/tests/test-utils.js +6 -2
- package/dist/core/utils/logger.d.ts +37 -0
- package/dist/core/utils/logger.js +6 -6
- package/dist/core/utils/sql-expressions.d.ts +63 -0
- package/dist/core/utils/sql-expressions.d.ts.map +1 -1
- package/dist/core/utils/sql-expressions.js +9 -5
- package/dist/core/utils.d.ts +3 -0
- package/dist/core/validators/filter-validator.d.ts +8 -0
- package/dist/core/validators/filter-validator.js +1 -1
- package/dist/core/validators/value-validator.d.ts +6 -0
- package/dist/formatters/index.d.ts +1 -0
- package/dist/index.d.ts +12 -27
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +14 -2
- package/dist/types/base.d.ts +77 -0
- package/dist/types/base.d.ts.map +1 -1
- package/dist/types/clickhouse-types.d.ts +13 -0
- package/dist/types/clickhouse-types.d.ts.map +1 -1
- package/dist/types/filters.d.ts +53 -0
- package/dist/types/filters.d.ts.map +1 -1
- package/dist/types/index.d.ts +3 -0
- package/package.json +36 -13
- package/README.md +0 -276
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
//@ts-
|
|
2
|
+
/**
|
|
3
|
+
* Type guard to check if an object is a FilterConditionInput
|
|
4
|
+
*/
|
|
5
|
+
function isFilterCondition(obj) {
|
|
6
|
+
return obj && 'column' in obj && 'operator' in obj && 'value' in obj;
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Type guard to check if an object is a FilterGroup
|
|
10
|
+
*/
|
|
11
|
+
function isFilterGroup(obj) {
|
|
12
|
+
return obj && 'conditions' in obj && 'operator' in obj;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Feature for handling cross-filter operations on queries
|
|
16
|
+
*/
|
|
17
|
+
export class CrossFilteringFeature {
|
|
18
|
+
constructor(builder) {
|
|
19
|
+
this.builder = builder;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Applies a set of cross filters to the query
|
|
23
|
+
* @param crossFilter - An instance of CrossFilter containing shared filter conditions
|
|
24
|
+
* @returns Updated query config
|
|
25
|
+
*/
|
|
26
|
+
applyCrossFilters(crossFilter) {
|
|
27
|
+
const filterGroup = crossFilter.getConditions();
|
|
28
|
+
if (filterGroup.conditions.length === 0) {
|
|
29
|
+
return this.builder.getConfig();
|
|
30
|
+
}
|
|
31
|
+
// Apply conditions based on filter group operator
|
|
32
|
+
if (filterGroup.operator === 'AND') {
|
|
33
|
+
// For AND groups, apply each condition directly
|
|
34
|
+
this.applyAndConditions(filterGroup.conditions);
|
|
35
|
+
}
|
|
36
|
+
else {
|
|
37
|
+
// For OR groups, special handling to ensure proper parentheses
|
|
38
|
+
// We use whereGroup instead of orWhereGroup here since this is a top-level group
|
|
39
|
+
this.builder.whereGroup(builder => {
|
|
40
|
+
this.applyOrConditions(filterGroup.conditions, builder);
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
return this.builder.getConfig();
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Apply AND conditions - each condition is applied with WHERE
|
|
47
|
+
*/
|
|
48
|
+
applyAndConditions(conditions) {
|
|
49
|
+
conditions.forEach(condition => {
|
|
50
|
+
if (isFilterCondition(condition)) {
|
|
51
|
+
// Simple condition - apply with WHERE
|
|
52
|
+
this.builder.where(condition.column, condition.operator, condition.value);
|
|
53
|
+
}
|
|
54
|
+
else if (isFilterGroup(condition)) {
|
|
55
|
+
// Nested group
|
|
56
|
+
if (condition.operator === 'AND') {
|
|
57
|
+
// AND subgroup - apply all conditions
|
|
58
|
+
this.builder.whereGroup(builder => {
|
|
59
|
+
const feature = new CrossFilteringFeature(builder);
|
|
60
|
+
feature.applyAndConditions(condition.conditions);
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
else {
|
|
64
|
+
// OR subgroup within AND - needs special parentheses handling
|
|
65
|
+
this.builder.whereGroup(builder => {
|
|
66
|
+
const feature = new CrossFilteringFeature(builder);
|
|
67
|
+
feature.applyOrConditions(condition.conditions, builder);
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Apply direct OR conditions without adding extra conjunctions
|
|
75
|
+
* @param conditions The conditions to apply
|
|
76
|
+
* @param builder The builder to apply conditions to, defaults to this.builder
|
|
77
|
+
*/
|
|
78
|
+
applyOrConditions(conditions, builder = this.builder) {
|
|
79
|
+
if (conditions.length === 0)
|
|
80
|
+
return;
|
|
81
|
+
// Handle first condition
|
|
82
|
+
const firstCondition = conditions[0];
|
|
83
|
+
if (isFilterCondition(firstCondition)) {
|
|
84
|
+
builder.where(firstCondition.column, firstCondition.operator, firstCondition.value);
|
|
85
|
+
}
|
|
86
|
+
else if (isFilterGroup(firstCondition)) {
|
|
87
|
+
// Handle nested group
|
|
88
|
+
if (firstCondition.operator === 'AND') {
|
|
89
|
+
builder.whereGroup(innerBuilder => {
|
|
90
|
+
const feature = new CrossFilteringFeature(innerBuilder);
|
|
91
|
+
feature.applyAndConditions(firstCondition.conditions);
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
else {
|
|
95
|
+
builder.whereGroup(innerBuilder => {
|
|
96
|
+
const feature = new CrossFilteringFeature(innerBuilder);
|
|
97
|
+
feature.applyOrConditions(firstCondition.conditions, innerBuilder);
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
// Handle remaining conditions
|
|
102
|
+
for (let i = 1; i < conditions.length; i++) {
|
|
103
|
+
const condition = conditions[i];
|
|
104
|
+
if (isFilterCondition(condition)) {
|
|
105
|
+
builder.orWhere(condition.column, condition.operator, condition.value);
|
|
106
|
+
}
|
|
107
|
+
else if (isFilterGroup(condition)) {
|
|
108
|
+
if (condition.operator === 'AND') {
|
|
109
|
+
builder.orWhereGroup(innerBuilder => {
|
|
110
|
+
const feature = new CrossFilteringFeature(innerBuilder);
|
|
111
|
+
feature.applyAndConditions(condition.conditions);
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
else {
|
|
115
|
+
builder.orWhereGroup(innerBuilder => {
|
|
116
|
+
const feature = new CrossFilteringFeature(innerBuilder);
|
|
117
|
+
feature.applyOrConditions(condition.conditions, innerBuilder);
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { QueryBuilder } from '../query-builder';
|
|
2
|
+
import { ColumnType } from '../../types';
|
|
3
|
+
export declare class ExecutorFeature<Schema extends {
|
|
4
|
+
[tableName: string]: {
|
|
5
|
+
[columnName: string]: ColumnType;
|
|
6
|
+
};
|
|
7
|
+
}, T, HasSelect extends boolean = false, Aggregations = {}, OriginalT = T> {
|
|
8
|
+
private builder;
|
|
9
|
+
constructor(builder: QueryBuilder<Schema, T, HasSelect, Aggregations, OriginalT>);
|
|
10
|
+
toSQLWithParams(): {
|
|
11
|
+
sql: string;
|
|
12
|
+
parameters: any[];
|
|
13
|
+
};
|
|
14
|
+
toSQL(): string;
|
|
15
|
+
execute(): Promise<T[]>;
|
|
16
|
+
stream(): Promise<ReadableStream<T[]>>;
|
|
17
|
+
private toSQLWithoutParameters;
|
|
18
|
+
}
|
|
19
|
+
//# sourceMappingURL=executor.d.ts.map
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { ClickHouseConnection } from '../connection';
|
|
2
|
-
import { substituteParameters } from '../utils';
|
|
3
|
-
import { logger } from '../utils/logger';
|
|
1
|
+
import { ClickHouseConnection } from '../connection.js';
|
|
2
|
+
import { substituteParameters } from '../utils.js';
|
|
3
|
+
import { logger } from '../utils/logger.js';
|
|
4
4
|
export class ExecutorFeature {
|
|
5
5
|
constructor(builder) {
|
|
6
6
|
this.builder = builder;
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import { QueryBuilder } from '../query-builder';
|
|
2
|
+
import { ColumnType, FilterOperator, TableColumn } from '../../types';
|
|
3
|
+
export declare class FilteringFeature<Schema extends {
|
|
4
|
+
[tableName: string]: {
|
|
5
|
+
[columnName: string]: ColumnType;
|
|
6
|
+
};
|
|
7
|
+
}, T, HasSelect extends boolean = false, Aggregations = {}, OriginalT = T> {
|
|
8
|
+
private builder;
|
|
9
|
+
constructor(builder: QueryBuilder<Schema, T, HasSelect, Aggregations, OriginalT>);
|
|
10
|
+
addCondition<K extends keyof OriginalT | TableColumn<Schema>>(conjunction: 'AND' | 'OR', column: K | K[], operator: FilterOperator, value: any): {
|
|
11
|
+
where: import("../../types").WhereCondition[];
|
|
12
|
+
parameters: any[];
|
|
13
|
+
select?: (string | keyof T)[] | undefined;
|
|
14
|
+
groupBy?: string[];
|
|
15
|
+
having?: string[];
|
|
16
|
+
limit?: number;
|
|
17
|
+
offset?: number;
|
|
18
|
+
distinct?: boolean;
|
|
19
|
+
orderBy?: {
|
|
20
|
+
column: TableColumn<Schema> | keyof T;
|
|
21
|
+
direction: import("../../types").OrderDirection;
|
|
22
|
+
}[] | undefined;
|
|
23
|
+
joins?: import("../../types").JoinClause[];
|
|
24
|
+
ctes?: string[];
|
|
25
|
+
unionQueries?: string[];
|
|
26
|
+
settings?: string;
|
|
27
|
+
};
|
|
28
|
+
/**
|
|
29
|
+
* Adds a group-start marker to start a parenthesized group of conditions with AND conjunction
|
|
30
|
+
* @returns The updated query config
|
|
31
|
+
*/
|
|
32
|
+
startWhereGroup(): {
|
|
33
|
+
where: import("../../types").WhereCondition[];
|
|
34
|
+
select?: (string | keyof T)[] | undefined;
|
|
35
|
+
groupBy?: string[];
|
|
36
|
+
having?: string[];
|
|
37
|
+
limit?: number;
|
|
38
|
+
offset?: number;
|
|
39
|
+
distinct?: boolean;
|
|
40
|
+
orderBy?: {
|
|
41
|
+
column: TableColumn<Schema> | keyof T;
|
|
42
|
+
direction: import("../../types").OrderDirection;
|
|
43
|
+
}[] | undefined;
|
|
44
|
+
joins?: import("../../types").JoinClause[];
|
|
45
|
+
parameters?: any[];
|
|
46
|
+
ctes?: string[];
|
|
47
|
+
unionQueries?: string[];
|
|
48
|
+
settings?: string;
|
|
49
|
+
};
|
|
50
|
+
/**
|
|
51
|
+
* Adds a group-start marker to start a parenthesized group of conditions with OR conjunction
|
|
52
|
+
* @returns The updated query config
|
|
53
|
+
*/
|
|
54
|
+
startOrWhereGroup(): {
|
|
55
|
+
where: import("../../types").WhereCondition[];
|
|
56
|
+
select?: (string | keyof T)[] | undefined;
|
|
57
|
+
groupBy?: string[];
|
|
58
|
+
having?: string[];
|
|
59
|
+
limit?: number;
|
|
60
|
+
offset?: number;
|
|
61
|
+
distinct?: boolean;
|
|
62
|
+
orderBy?: {
|
|
63
|
+
column: TableColumn<Schema> | keyof T;
|
|
64
|
+
direction: import("../../types").OrderDirection;
|
|
65
|
+
}[] | undefined;
|
|
66
|
+
joins?: import("../../types").JoinClause[];
|
|
67
|
+
parameters?: any[];
|
|
68
|
+
ctes?: string[];
|
|
69
|
+
unionQueries?: string[];
|
|
70
|
+
settings?: string;
|
|
71
|
+
};
|
|
72
|
+
/**
|
|
73
|
+
* Adds a group-end marker to end a parenthesized group of conditions
|
|
74
|
+
* @returns The updated query config
|
|
75
|
+
*/
|
|
76
|
+
endWhereGroup(): {
|
|
77
|
+
where: import("../../types").WhereCondition[];
|
|
78
|
+
select?: (string | keyof T)[] | undefined;
|
|
79
|
+
groupBy?: string[];
|
|
80
|
+
having?: string[];
|
|
81
|
+
limit?: number;
|
|
82
|
+
offset?: number;
|
|
83
|
+
distinct?: boolean;
|
|
84
|
+
orderBy?: {
|
|
85
|
+
column: TableColumn<Schema> | keyof T;
|
|
86
|
+
direction: import("../../types").OrderDirection;
|
|
87
|
+
}[] | undefined;
|
|
88
|
+
joins?: import("../../types").JoinClause[];
|
|
89
|
+
parameters?: any[];
|
|
90
|
+
ctes?: string[];
|
|
91
|
+
unionQueries?: string[];
|
|
92
|
+
settings?: string;
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
//# sourceMappingURL=filtering.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"filtering.d.ts","sourceRoot":"","sources":["../../../src/core/features/filtering.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAChD,OAAO,EAAE,UAAU,EAAE,cAAc,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAEtE,qBAAa,gBAAgB,CAC3B,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;IAED,OAAO,CAAC,OAAO;gBAAP,OAAO,EAAE,YAAY,CAAC,MAAM,EAAE,CAAC,EAAE,SAAS,EAAE,YAAY,EAAE,SAAS,CAAC;IAExF,YAAY,CAAC,CAAC,SAAS,MAAM,SAAS,GAAG,WAAW,CAAC,MAAM,CAAC,EAC1D,WAAW,EAAE,KAAK,GAAG,IAAI,EACzB,MAAM,EAAE,CAAC,
|
|
1
|
+
{"version":3,"file":"filtering.d.ts","sourceRoot":"","sources":["../../../src/core/features/filtering.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAChD,OAAO,EAAE,UAAU,EAAE,cAAc,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAEtE,qBAAa,gBAAgB,CAC3B,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;IAED,OAAO,CAAC,OAAO;gBAAP,OAAO,EAAE,YAAY,CAAC,MAAM,EAAE,CAAC,EAAE,SAAS,EAAE,YAAY,EAAE,SAAS,CAAC;IAExF,YAAY,CAAC,CAAC,SAAS,MAAM,SAAS,GAAG,WAAW,CAAC,MAAM,CAAC,EAC1D,WAAW,EAAE,KAAK,GAAG,IAAI,EACzB,MAAM,EAAE,CAAC,GAAG,CAAC,EAAE,EACf,QAAQ,EAAE,cAAc,EACxB,KAAK,EAAE,GAAG;;;;;;;;;;;;;;;;;;IA4DZ;;;OAGG;IACH,eAAe;;;;;;;;;;;;;;;;;;IAkBf;;;OAGG;IACH,iBAAiB;;;;;;;;;;;;;;;;;;IAkBjB;;;OAGG;IACH,aAAa;;;;;;;;;;;;;;;;;;CAiBd"}
|
|
@@ -6,15 +6,44 @@ export class FilteringFeature {
|
|
|
6
6
|
const config = this.builder.getConfig();
|
|
7
7
|
const where = config.where || [];
|
|
8
8
|
const parameters = config.parameters || [];
|
|
9
|
+
// Handle tuple columns
|
|
10
|
+
const columnString = Array.isArray(column)
|
|
11
|
+
? `(${column.map(String).join(', ')})`
|
|
12
|
+
: String(column);
|
|
9
13
|
where.push({
|
|
10
|
-
column:
|
|
14
|
+
column: columnString,
|
|
11
15
|
operator,
|
|
12
16
|
value,
|
|
13
|
-
conjunction
|
|
17
|
+
conjunction,
|
|
18
|
+
type: 'condition'
|
|
14
19
|
});
|
|
15
|
-
|
|
20
|
+
// Handle different parameter types based on operator
|
|
21
|
+
if (operator === 'in' || operator === 'notIn' || operator === 'globalIn' || operator === 'globalNotIn') {
|
|
22
|
+
if (!Array.isArray(value)) {
|
|
23
|
+
throw new Error(`Expected an array for ${operator} operator, but got ${typeof value}`);
|
|
24
|
+
}
|
|
16
25
|
parameters.push(...value);
|
|
17
26
|
}
|
|
27
|
+
else if (operator === 'inTuple' || operator === 'globalInTuple') {
|
|
28
|
+
if (!Array.isArray(value)) {
|
|
29
|
+
throw new Error(`Expected an array of tuples for ${operator} operator, but got ${typeof value}`);
|
|
30
|
+
}
|
|
31
|
+
value.forEach((tuple) => {
|
|
32
|
+
parameters.push(...tuple);
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
else if (operator === 'inSubquery' || operator === 'globalInSubquery') {
|
|
36
|
+
if (typeof value !== 'string') {
|
|
37
|
+
throw new Error(`Expected a string (subquery) for ${operator} operator, but got ${typeof value}`);
|
|
38
|
+
}
|
|
39
|
+
// No parameters
|
|
40
|
+
}
|
|
41
|
+
else if (operator === 'inTable' || operator === 'globalInTable') {
|
|
42
|
+
if (typeof value !== 'string') {
|
|
43
|
+
throw new Error(`Expected a string (table name) for ${operator} operator, but got ${typeof value}`);
|
|
44
|
+
}
|
|
45
|
+
// No parameters
|
|
46
|
+
}
|
|
18
47
|
else if (operator === 'between') {
|
|
19
48
|
parameters.push(value[0], value[1]);
|
|
20
49
|
}
|
|
@@ -27,4 +56,61 @@ export class FilteringFeature {
|
|
|
27
56
|
parameters
|
|
28
57
|
};
|
|
29
58
|
}
|
|
59
|
+
/**
|
|
60
|
+
* Adds a group-start marker to start a parenthesized group of conditions with AND conjunction
|
|
61
|
+
* @returns The updated query config
|
|
62
|
+
*/
|
|
63
|
+
startWhereGroup() {
|
|
64
|
+
const config = this.builder.getConfig();
|
|
65
|
+
const where = config.where || [];
|
|
66
|
+
where.push({
|
|
67
|
+
column: '', // Not used for group markers
|
|
68
|
+
operator: 'eq', // Not used for group markers
|
|
69
|
+
value: null, // Not used for group markers
|
|
70
|
+
conjunction: 'AND',
|
|
71
|
+
type: 'group-start'
|
|
72
|
+
});
|
|
73
|
+
return {
|
|
74
|
+
...config,
|
|
75
|
+
where
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Adds a group-start marker to start a parenthesized group of conditions with OR conjunction
|
|
80
|
+
* @returns The updated query config
|
|
81
|
+
*/
|
|
82
|
+
startOrWhereGroup() {
|
|
83
|
+
const config = this.builder.getConfig();
|
|
84
|
+
const where = config.where || [];
|
|
85
|
+
where.push({
|
|
86
|
+
column: '', // Not used for group markers
|
|
87
|
+
operator: 'eq', // Not used for group markers
|
|
88
|
+
value: null, // Not used for group markers
|
|
89
|
+
conjunction: 'OR',
|
|
90
|
+
type: 'group-start'
|
|
91
|
+
});
|
|
92
|
+
return {
|
|
93
|
+
...config,
|
|
94
|
+
where
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Adds a group-end marker to end a parenthesized group of conditions
|
|
99
|
+
* @returns The updated query config
|
|
100
|
+
*/
|
|
101
|
+
endWhereGroup() {
|
|
102
|
+
const config = this.builder.getConfig();
|
|
103
|
+
const where = config.where || [];
|
|
104
|
+
where.push({
|
|
105
|
+
column: '', // Not used for group markers
|
|
106
|
+
operator: 'eq', // Not used for group markers
|
|
107
|
+
value: null, // Not used for group markers
|
|
108
|
+
conjunction: 'AND', // Not relevant for end markers
|
|
109
|
+
type: 'group-end'
|
|
110
|
+
});
|
|
111
|
+
return {
|
|
112
|
+
...config,
|
|
113
|
+
where
|
|
114
|
+
};
|
|
115
|
+
}
|
|
30
116
|
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { QueryBuilder } from '../query-builder';
|
|
2
|
+
import { ColumnType, JoinType } from '../../types';
|
|
3
|
+
export declare class JoinFeature<Schema extends {
|
|
4
|
+
[tableName: string]: {
|
|
5
|
+
[columnName: string]: ColumnType;
|
|
6
|
+
};
|
|
7
|
+
}, T, HasSelect extends boolean = false, Aggregations = {}, OriginalT = T> {
|
|
8
|
+
private builder;
|
|
9
|
+
constructor(builder: QueryBuilder<Schema, T, HasSelect, Aggregations, OriginalT>);
|
|
10
|
+
addJoin<TableName extends keyof Schema>(type: JoinType, table: TableName, leftColumn: keyof OriginalT, rightColumn: `${TableName & string}.${keyof Schema[TableName] & string}`, alias?: string): {
|
|
11
|
+
joins: import("../../types").JoinClause[];
|
|
12
|
+
select?: (string | keyof T)[] | undefined;
|
|
13
|
+
where?: import("../../types").WhereCondition[];
|
|
14
|
+
groupBy?: string[];
|
|
15
|
+
having?: string[];
|
|
16
|
+
limit?: number;
|
|
17
|
+
offset?: number;
|
|
18
|
+
distinct?: boolean;
|
|
19
|
+
orderBy?: {
|
|
20
|
+
column: keyof T | import("../../types").TableColumn<Schema>;
|
|
21
|
+
direction: import("../../types").OrderDirection;
|
|
22
|
+
}[] | undefined;
|
|
23
|
+
parameters?: any[];
|
|
24
|
+
ctes?: string[];
|
|
25
|
+
unionQueries?: string[];
|
|
26
|
+
settings?: string;
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
//# sourceMappingURL=joins.d.ts.map
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { QueryBuilder } from '../query-builder';
|
|
2
|
+
import { ColumnType, PaginationOptions, PaginatedResult } from '../../types';
|
|
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
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
import { QueryBuilder } from '../query-builder';
|
|
2
|
+
import { ColumnType, OrderDirection, TableColumn } from '../../types';
|
|
3
|
+
export declare class QueryModifiersFeature<Schema extends {
|
|
4
|
+
[tableName: string]: {
|
|
5
|
+
[columnName: string]: ColumnType;
|
|
6
|
+
};
|
|
7
|
+
}, T, HasSelect extends boolean = false, Aggregations = {}, OriginalT = T> {
|
|
8
|
+
private builder;
|
|
9
|
+
constructor(builder: QueryBuilder<Schema, T, HasSelect, Aggregations, OriginalT>);
|
|
10
|
+
addGroupBy(columns: (keyof T | TableColumn<Schema>) | Array<keyof T | TableColumn<Schema>>): {
|
|
11
|
+
groupBy: string[];
|
|
12
|
+
select?: (string | keyof T)[] | undefined;
|
|
13
|
+
where?: import("../../types").WhereCondition[];
|
|
14
|
+
having?: string[];
|
|
15
|
+
limit?: number;
|
|
16
|
+
offset?: number;
|
|
17
|
+
distinct?: boolean;
|
|
18
|
+
orderBy?: {
|
|
19
|
+
column: TableColumn<Schema> | keyof T;
|
|
20
|
+
direction: OrderDirection;
|
|
21
|
+
}[] | undefined;
|
|
22
|
+
joins?: import("../../types").JoinClause[];
|
|
23
|
+
parameters?: any[];
|
|
24
|
+
ctes?: string[];
|
|
25
|
+
unionQueries?: string[];
|
|
26
|
+
settings?: string;
|
|
27
|
+
};
|
|
28
|
+
addLimit(count: number): {
|
|
29
|
+
limit: number;
|
|
30
|
+
select?: (string | keyof T)[] | undefined;
|
|
31
|
+
where?: import("../../types").WhereCondition[];
|
|
32
|
+
groupBy?: string[];
|
|
33
|
+
having?: string[];
|
|
34
|
+
offset?: number;
|
|
35
|
+
distinct?: boolean;
|
|
36
|
+
orderBy?: {
|
|
37
|
+
column: TableColumn<Schema> | keyof T;
|
|
38
|
+
direction: OrderDirection;
|
|
39
|
+
}[] | undefined;
|
|
40
|
+
joins?: import("../../types").JoinClause[];
|
|
41
|
+
parameters?: any[];
|
|
42
|
+
ctes?: string[];
|
|
43
|
+
unionQueries?: string[];
|
|
44
|
+
settings?: string;
|
|
45
|
+
};
|
|
46
|
+
addOffset(count: number): {
|
|
47
|
+
offset: number;
|
|
48
|
+
select?: (string | keyof T)[] | undefined;
|
|
49
|
+
where?: import("../../types").WhereCondition[];
|
|
50
|
+
groupBy?: string[];
|
|
51
|
+
having?: string[];
|
|
52
|
+
limit?: number;
|
|
53
|
+
distinct?: boolean;
|
|
54
|
+
orderBy?: {
|
|
55
|
+
column: TableColumn<Schema> | keyof T;
|
|
56
|
+
direction: OrderDirection;
|
|
57
|
+
}[] | undefined;
|
|
58
|
+
joins?: import("../../types").JoinClause[];
|
|
59
|
+
parameters?: any[];
|
|
60
|
+
ctes?: string[];
|
|
61
|
+
unionQueries?: string[];
|
|
62
|
+
settings?: string;
|
|
63
|
+
};
|
|
64
|
+
addOrderBy<K extends keyof T | TableColumn<Schema>>(column: K, direction?: OrderDirection): {
|
|
65
|
+
orderBy: {
|
|
66
|
+
column: TableColumn<Schema> | keyof T;
|
|
67
|
+
direction: OrderDirection;
|
|
68
|
+
}[];
|
|
69
|
+
select?: (string | keyof T)[] | undefined;
|
|
70
|
+
where?: import("../../types").WhereCondition[];
|
|
71
|
+
groupBy?: string[];
|
|
72
|
+
having?: string[];
|
|
73
|
+
limit?: number;
|
|
74
|
+
offset?: number;
|
|
75
|
+
distinct?: boolean;
|
|
76
|
+
joins?: import("../../types").JoinClause[];
|
|
77
|
+
parameters?: any[];
|
|
78
|
+
ctes?: string[];
|
|
79
|
+
unionQueries?: string[];
|
|
80
|
+
settings?: string;
|
|
81
|
+
};
|
|
82
|
+
addHaving(condition: string, parameters?: any[]): {
|
|
83
|
+
having: string[];
|
|
84
|
+
parameters: any[] | undefined;
|
|
85
|
+
select?: (string | keyof T)[] | undefined;
|
|
86
|
+
where?: import("../../types").WhereCondition[];
|
|
87
|
+
groupBy?: string[];
|
|
88
|
+
limit?: number;
|
|
89
|
+
offset?: number;
|
|
90
|
+
distinct?: boolean;
|
|
91
|
+
orderBy?: {
|
|
92
|
+
column: TableColumn<Schema> | keyof T;
|
|
93
|
+
direction: OrderDirection;
|
|
94
|
+
}[] | undefined;
|
|
95
|
+
joins?: import("../../types").JoinClause[];
|
|
96
|
+
ctes?: string[];
|
|
97
|
+
unionQueries?: string[];
|
|
98
|
+
settings?: string;
|
|
99
|
+
};
|
|
100
|
+
setDistinct(): {
|
|
101
|
+
distinct: boolean;
|
|
102
|
+
select?: (string | keyof T)[] | undefined;
|
|
103
|
+
where?: import("../../types").WhereCondition[];
|
|
104
|
+
groupBy?: string[];
|
|
105
|
+
having?: string[];
|
|
106
|
+
limit?: number;
|
|
107
|
+
offset?: number;
|
|
108
|
+
orderBy?: {
|
|
109
|
+
column: TableColumn<Schema> | keyof T;
|
|
110
|
+
direction: OrderDirection;
|
|
111
|
+
}[] | undefined;
|
|
112
|
+
joins?: import("../../types").JoinClause[];
|
|
113
|
+
parameters?: any[];
|
|
114
|
+
ctes?: string[];
|
|
115
|
+
unionQueries?: string[];
|
|
116
|
+
settings?: string;
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
//# sourceMappingURL=query-modifiers.d.ts.map
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { QueryConfig } from '../../types';
|
|
2
|
+
export declare class SQLFormatter {
|
|
3
|
+
formatSelect(config: QueryConfig<any, any>): string;
|
|
4
|
+
formatGroupBy(config: QueryConfig<any, any>): string;
|
|
5
|
+
formatWhere(config: QueryConfig<any, any>): string;
|
|
6
|
+
private getSqlOperator;
|
|
7
|
+
formatJoins(config: QueryConfig<any, any>): string;
|
|
8
|
+
}
|
|
9
|
+
//# sourceMappingURL=sql-formatter.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sql-formatter.d.ts","sourceRoot":"","sources":["../../../src/core/formatters/sql-formatter.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAkB,MAAM,aAAa,CAAC;AAE1D,qBAAa,YAAY;IACvB,YAAY,CAAC,MAAM,EAAE,WAAW,CAAC,GAAG,EAAE,GAAG,CAAC,GAAG,MAAM;IAMnD,aAAa,CAAC,MAAM,EAAE,WAAW,CAAC,GAAG,EAAE,GAAG,CAAC,GAAG,MAAM;IASpD,WAAW,CAAC,MAAM,EAAE,WAAW,CAAC,GAAG,EAAE,GAAG,CAAC,GAAG,MAAM;
|
|
1
|
+
{"version":3,"file":"sql-formatter.d.ts","sourceRoot":"","sources":["../../../src/core/formatters/sql-formatter.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAkB,MAAM,aAAa,CAAC;AAE1D,qBAAa,YAAY;IACvB,YAAY,CAAC,MAAM,EAAE,WAAW,CAAC,GAAG,EAAE,GAAG,CAAC,GAAG,MAAM;IAMnD,aAAa,CAAC,MAAM,EAAE,WAAW,CAAC,GAAG,EAAE,GAAG,CAAC,GAAG,MAAM;IASpD,WAAW,CAAC,MAAM,EAAE,WAAW,CAAC,GAAG,EAAE,GAAG,CAAC,GAAG,MAAM;IA6FlD,OAAO,CAAC,cAAc;IActB,WAAW,CAAC,MAAM,EAAE,WAAW,CAAC,GAAG,EAAE,GAAG,CAAC,GAAG,MAAM;CAUnD"}
|
|
@@ -17,10 +17,26 @@ export class SQLFormatter {
|
|
|
17
17
|
formatWhere(config) {
|
|
18
18
|
if (!config.where?.length)
|
|
19
19
|
return '';
|
|
20
|
-
|
|
21
|
-
|
|
20
|
+
let afterGroupStart = false; // Track whether we're immediately after a group-start
|
|
21
|
+
// First pass - generate the SQL fragments for each condition
|
|
22
|
+
const fragments = config.where.map((condition, index) => {
|
|
23
|
+
// Handle special group markers
|
|
24
|
+
if (condition.type === 'group-start') {
|
|
25
|
+
const prefix = index === 0 ? '' : ` ${condition.conjunction} `;
|
|
26
|
+
afterGroupStart = true; // Mark that the next condition follows a group-start
|
|
27
|
+
return `${prefix}(`.trim();
|
|
28
|
+
}
|
|
29
|
+
if (condition.type === 'group-end') {
|
|
30
|
+
afterGroupStart = false; // Reset the flag after group-end
|
|
31
|
+
return ')';
|
|
32
|
+
}
|
|
33
|
+
// Normal conditions
|
|
22
34
|
const { column, operator, value, conjunction } = condition;
|
|
23
|
-
|
|
35
|
+
// Don't add conjunction if it's the first condition or right after a group-start
|
|
36
|
+
const prefix = index === 0 || afterGroupStart ? '' : ` ${conjunction} `;
|
|
37
|
+
// Reset the afterGroupStart flag
|
|
38
|
+
afterGroupStart = false;
|
|
39
|
+
// Handle IN operators
|
|
24
40
|
if (operator === 'in' || operator === 'notIn') {
|
|
25
41
|
if (!Array.isArray(value)) {
|
|
26
42
|
throw new Error(`Expected an array for ${operator} operator, but got ${typeof value}`);
|
|
@@ -31,6 +47,42 @@ export class SQLFormatter {
|
|
|
31
47
|
const placeholders = value.map(() => '?').join(', ');
|
|
32
48
|
return `${prefix}${column} ${operator === 'in' ? 'IN' : 'NOT IN'} (${placeholders})`.trim();
|
|
33
49
|
}
|
|
50
|
+
// Handle GLOBAL IN operators
|
|
51
|
+
else if (operator === 'globalIn' || operator === 'globalNotIn') {
|
|
52
|
+
if (!Array.isArray(value)) {
|
|
53
|
+
throw new Error(`Expected an array for ${operator} operator, but got ${typeof value}`);
|
|
54
|
+
}
|
|
55
|
+
if (value.length === 0) {
|
|
56
|
+
return `${prefix}1 = 0`;
|
|
57
|
+
}
|
|
58
|
+
const placeholders = value.map(() => '?').join(', ');
|
|
59
|
+
return `${prefix}${column} ${operator === 'globalIn' ? 'GLOBAL IN' : 'GLOBAL NOT IN'} (${placeholders})`.trim();
|
|
60
|
+
}
|
|
61
|
+
// Handle subquery IN operators
|
|
62
|
+
else if (operator === 'inSubquery' || operator === 'globalInSubquery') {
|
|
63
|
+
if (typeof value !== 'string') {
|
|
64
|
+
throw new Error(`Expected a string (subquery) for ${operator} operator, but got ${typeof value}`);
|
|
65
|
+
}
|
|
66
|
+
return `${prefix}${column} ${operator === 'inSubquery' ? 'IN' : 'GLOBAL IN'} (${value})`.trim();
|
|
67
|
+
}
|
|
68
|
+
// Handle table reference IN operators
|
|
69
|
+
else if (operator === 'inTable' || operator === 'globalInTable') {
|
|
70
|
+
if (typeof value !== 'string') {
|
|
71
|
+
throw new Error(`Expected a string (table name) for ${operator} operator, but got ${typeof value}`);
|
|
72
|
+
}
|
|
73
|
+
return `${prefix}${column} ${operator === 'inTable' ? 'IN' : 'GLOBAL IN'} ${value}`.trim();
|
|
74
|
+
}
|
|
75
|
+
// Handle tuple IN operators
|
|
76
|
+
else if (operator === 'inTuple' || operator === 'globalInTuple') {
|
|
77
|
+
if (!Array.isArray(value)) {
|
|
78
|
+
throw new Error(`Expected an array of tuples for ${operator} operator, but got ${typeof value}`);
|
|
79
|
+
}
|
|
80
|
+
if (value.length === 0) {
|
|
81
|
+
return `${prefix}1 = 0`;
|
|
82
|
+
}
|
|
83
|
+
const placeholders = value.map(() => '(?, ?)').join(', ');
|
|
84
|
+
return `${prefix}${column} ${operator === 'inTuple' ? 'IN' : 'GLOBAL IN'} (${placeholders})`.trim();
|
|
85
|
+
}
|
|
34
86
|
else if (operator === 'between') {
|
|
35
87
|
return `${prefix}${column} BETWEEN ? AND ?`.trim();
|
|
36
88
|
}
|
|
@@ -40,8 +92,12 @@ export class SQLFormatter {
|
|
|
40
92
|
else {
|
|
41
93
|
return `${prefix}${column} ${this.getSqlOperator(operator)} ?`.trim();
|
|
42
94
|
}
|
|
43
|
-
})
|
|
44
|
-
|
|
95
|
+
});
|
|
96
|
+
// Join fragments and then remove extra spaces around parentheses
|
|
97
|
+
let result = fragments.join(' ');
|
|
98
|
+
// Replace "( " with "(" and " )" with ")"
|
|
99
|
+
result = result.replace(/\(\s+/g, '(').replace(/\s+\)/g, ')');
|
|
100
|
+
return result;
|
|
45
101
|
}
|
|
46
102
|
getSqlOperator(operator) {
|
|
47
103
|
switch (operator) {
|