@hypequery/clickhouse 1.6.2 → 2.0.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 +43 -88
- package/README.md +84 -253
- package/dist/cli/bin.js +16 -8
- package/dist/core/adapters/clickhouse-adapter.d.ts.map +1 -1
- package/dist/core/adapters/clickhouse-adapter.js +3 -2
- package/dist/core/cache/cache-manager.d.ts.map +1 -1
- package/dist/core/cache/cache-manager.js +5 -3
- package/dist/core/connection.d.ts +6 -6
- package/dist/core/connection.js +9 -9
- package/dist/core/cross-filter.js +1 -1
- package/dist/core/dialects/clickhouse-dialect.d.ts +2 -2
- package/dist/core/dialects/clickhouse-dialect.d.ts.map +1 -1
- package/dist/core/dialects/clickhouse-dialect.js +39 -22
- package/dist/core/dialects/sql-dialect.d.ts +2 -2
- package/dist/core/dialects/sql-dialect.d.ts.map +1 -1
- package/dist/core/env/auto-client.d.ts.map +1 -1
- package/dist/core/env/auto-client.js +1 -1
- package/dist/core/features/aggregations.d.ts +7 -90
- package/dist/core/features/aggregations.d.ts.map +1 -1
- package/dist/core/features/aggregations.js +19 -7
- package/dist/core/features/analytics.d.ts +5 -870
- package/dist/core/features/analytics.d.ts.map +1 -1
- package/dist/core/features/analytics.js +15 -13
- package/dist/core/features/cross-filtering.d.ts +1 -1
- package/dist/core/features/cross-filtering.d.ts.map +1 -1
- package/dist/core/features/cross-filtering.js +28 -73
- package/dist/core/features/executor.d.ts +1 -1
- package/dist/core/features/executor.d.ts.map +1 -1
- package/dist/core/features/executor.js +9 -11
- package/dist/core/features/filtering.d.ts +5 -91
- package/dist/core/features/filtering.d.ts.map +1 -1
- package/dist/core/features/filtering.js +63 -77
- package/dist/core/features/joins.d.ts +2 -19
- package/dist/core/features/joins.d.ts.map +1 -1
- package/dist/core/features/joins.js +16 -5
- package/dist/core/features/query-modifiers.d.ts +10 -109
- package/dist/core/features/query-modifiers.d.ts.map +1 -1
- package/dist/core/features/query-modifiers.js +64 -18
- package/dist/core/formatters/sql-formatter.d.ts +16 -5
- package/dist/core/formatters/sql-formatter.d.ts.map +1 -1
- package/dist/core/formatters/sql-formatter.js +197 -93
- package/dist/core/join-relationships.d.ts +22 -5
- package/dist/core/join-relationships.d.ts.map +1 -1
- package/dist/core/join-relationships.js +1 -1
- package/dist/core/query-builder.d.ts +63 -12
- package/dist/core/query-builder.d.ts.map +1 -1
- package/dist/core/query-builder.js +210 -153
- package/dist/core/query-node.d.ts +7 -0
- package/dist/core/query-node.d.ts.map +1 -0
- package/dist/core/query-node.js +80 -0
- package/dist/core/tests/integration/setup.d.ts +1 -0
- package/dist/core/tests/integration/setup.d.ts.map +1 -1
- package/dist/core/tests/integration/setup.js +4 -2
- package/dist/core/types/select-types.d.ts +3 -0
- package/dist/core/types/select-types.d.ts.map +1 -1
- package/dist/core/utils/connection-endpoint.d.ts +3 -0
- package/dist/core/utils/connection-endpoint.d.ts.map +1 -0
- package/dist/core/utils/connection-endpoint.js +9 -0
- package/dist/core/utils/filter-application.d.ts +15 -0
- package/dist/core/utils/filter-application.d.ts.map +1 -0
- package/dist/core/utils/filter-application.js +32 -0
- package/dist/core/utils/query-config-compat.d.ts +48 -0
- package/dist/core/utils/query-config-compat.d.ts.map +1 -0
- package/dist/core/utils/query-config-compat.js +137 -0
- package/dist/core/utils/relation-application.d.ts +9 -0
- package/dist/core/utils/relation-application.d.ts.map +1 -0
- package/dist/core/utils/relation-application.js +19 -0
- package/dist/core/utils/relation-validation.d.ts +6 -0
- package/dist/core/utils/relation-validation.d.ts.map +1 -0
- package/dist/core/utils/relation-validation.js +29 -0
- package/dist/core/utils/sql-expressions.d.ts +14 -0
- package/dist/core/utils/sql-expressions.d.ts.map +1 -1
- package/dist/core/utils/sql-expressions.js +40 -0
- package/dist/core/utils/tuple-filter-validation.d.ts +3 -0
- package/dist/core/utils/tuple-filter-validation.d.ts.map +1 -0
- package/dist/core/utils/tuple-filter-validation.js +16 -0
- package/dist/index.d.ts +2 -13
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -8
- package/dist/types/base.d.ts +88 -22
- package/dist/types/base.d.ts.map +1 -1
- package/dist/types/filters.d.ts +9 -5
- package/dist/types/filters.d.ts.map +1 -1
- package/package.json +5 -5
- package/dist/migrations/config/index.d.ts +0 -3
- package/dist/migrations/config/index.d.ts.map +0 -1
- package/dist/migrations/config/index.js +0 -1
- package/dist/migrations/config/types.d.ts +0 -45
- package/dist/migrations/config/types.d.ts.map +0 -1
- package/dist/migrations/config/types.js +0 -28
- package/dist/migrations/diff/diff.d.ts +0 -11
- package/dist/migrations/diff/diff.d.ts.map +0 -1
- package/dist/migrations/diff/diff.js +0 -240
- package/dist/migrations/diff/index.d.ts +0 -3
- package/dist/migrations/diff/index.d.ts.map +0 -1
- package/dist/migrations/diff/index.js +0 -1
- package/dist/migrations/diff/types.d.ts +0 -74
- package/dist/migrations/diff/types.d.ts.map +0 -1
- package/dist/migrations/diff/types.js +0 -1
- package/dist/migrations/plan/index.d.ts +0 -3
- package/dist/migrations/plan/index.d.ts.map +0 -1
- package/dist/migrations/plan/index.js +0 -1
- package/dist/migrations/plan/plan.d.ts +0 -12
- package/dist/migrations/plan/plan.d.ts.map +0 -1
- package/dist/migrations/plan/plan.js +0 -416
- package/dist/migrations/plan/types.d.ts +0 -93
- package/dist/migrations/plan/types.d.ts.map +0 -1
- package/dist/migrations/plan/types.js +0 -1
- package/dist/migrations/schema/column.d.ts +0 -71
- package/dist/migrations/schema/column.d.ts.map +0 -1
- package/dist/migrations/schema/column.js +0 -123
- package/dist/migrations/schema/define.d.ts +0 -24
- package/dist/migrations/schema/define.d.ts.map +0 -1
- package/dist/migrations/schema/define.js +0 -47
- package/dist/migrations/schema/index.d.ts +0 -4
- package/dist/migrations/schema/index.d.ts.map +0 -1
- package/dist/migrations/schema/index.js +0 -2
- package/dist/migrations/schema/types.d.ts +0 -74
- package/dist/migrations/schema/types.d.ts.map +0 -1
- package/dist/migrations/schema/types.js +0 -1
- package/dist/migrations/snapshot/index.d.ts +0 -3
- package/dist/migrations/snapshot/index.d.ts.map +0 -1
- package/dist/migrations/snapshot/index.js +0 -1
- package/dist/migrations/snapshot/serialize.d.ts +0 -21
- package/dist/migrations/snapshot/serialize.d.ts.map +0 -1
- package/dist/migrations/snapshot/serialize.js +0 -127
- package/dist/migrations/snapshot/types.d.ts +0 -47
- package/dist/migrations/snapshot/types.d.ts.map +0 -1
- package/dist/migrations/snapshot/types.js +0 -1
- package/dist/migrations/sql/index.d.ts +0 -4
- package/dist/migrations/sql/index.d.ts.map +0 -1
- package/dist/migrations/sql/index.js +0 -2
- package/dist/migrations/sql/render.d.ts +0 -10
- package/dist/migrations/sql/render.d.ts.map +0 -1
- package/dist/migrations/sql/render.js +0 -347
- package/dist/migrations/sql/types.d.ts +0 -53
- package/dist/migrations/sql/types.d.ts.map +0 -1
- package/dist/migrations/sql/types.js +0 -1
- package/dist/migrations/sql/write.d.ts +0 -10
- package/dist/migrations/sql/write.d.ts.map +0 -1
- package/dist/migrations/sql/write.js +0 -35
|
@@ -9,8 +9,22 @@ import { QueryModifiersFeature } from './features/query-modifiers.js';
|
|
|
9
9
|
import { FilterValidator } from './validators/filter-validator.js';
|
|
10
10
|
import { createPredicateBuilder, } from './utils/predicate-builder.js';
|
|
11
11
|
import { CrossFilteringFeature } from './features/cross-filtering.js';
|
|
12
|
+
import { cloneSelectQueryNode, createSelectQueryNode, transformSelectQueryNode, } from './query-node.js';
|
|
12
13
|
import { executeWithCache } from './cache/cache-manager.js';
|
|
13
14
|
import { mergeCacheOptionsPartial, initializeCacheRuntime } from './cache/utils.js';
|
|
15
|
+
import { normalizeFilterApplication } from './utils/filter-application.js';
|
|
16
|
+
import { toLegacyQueryConfig } from './utils/query-config-compat.js';
|
|
17
|
+
import { applyRelationPath, resolveRelationPath } from './utils/relation-application.js';
|
|
18
|
+
const ADVANCED_IN_OPERATORS = new Set([
|
|
19
|
+
'globalIn',
|
|
20
|
+
'globalNotIn',
|
|
21
|
+
'inSubquery',
|
|
22
|
+
'globalInSubquery',
|
|
23
|
+
'inTable',
|
|
24
|
+
'globalInTable',
|
|
25
|
+
'inTuple',
|
|
26
|
+
'globalInTuple',
|
|
27
|
+
]);
|
|
14
28
|
/**
|
|
15
29
|
* Type guard to check if a config is a client-based configuration.
|
|
16
30
|
*/
|
|
@@ -23,7 +37,7 @@ export function isClientConfig(config) {
|
|
|
23
37
|
*/
|
|
24
38
|
export class QueryBuilder {
|
|
25
39
|
static relationships;
|
|
26
|
-
|
|
40
|
+
query;
|
|
27
41
|
tableName;
|
|
28
42
|
state;
|
|
29
43
|
aggregations;
|
|
@@ -37,8 +51,15 @@ export class QueryBuilder {
|
|
|
37
51
|
adapter;
|
|
38
52
|
dialect;
|
|
39
53
|
cacheOptions;
|
|
54
|
+
queryTransforms = [];
|
|
40
55
|
constructor(tableName, state, runtime, adapter, dialect) {
|
|
41
56
|
this.tableName = tableName;
|
|
57
|
+
this.query = createSelectQueryNode({
|
|
58
|
+
from: {
|
|
59
|
+
kind: 'table',
|
|
60
|
+
name: tableName,
|
|
61
|
+
},
|
|
62
|
+
});
|
|
42
63
|
this.state = state;
|
|
43
64
|
this.runtime = runtime;
|
|
44
65
|
this.adapter = adapter;
|
|
@@ -51,31 +72,98 @@ export class QueryBuilder {
|
|
|
51
72
|
this.modifiers = new QueryModifiersFeature(this);
|
|
52
73
|
this.crossFiltering = new CrossFilteringFeature(this);
|
|
53
74
|
}
|
|
54
|
-
fork(state,
|
|
75
|
+
fork(state, query) {
|
|
76
|
+
return this.transition(state, query);
|
|
77
|
+
}
|
|
78
|
+
transition(state, query) {
|
|
55
79
|
const builder = new QueryBuilder(this.tableName, state, this.runtime, this.adapter, this.dialect);
|
|
56
|
-
builder.
|
|
57
|
-
builder.cacheOptions = this.cacheOptions;
|
|
80
|
+
builder.query = cloneSelectQueryNode(query);
|
|
81
|
+
builder.cacheOptions = this.cacheOptions ? { ...this.cacheOptions } : undefined;
|
|
82
|
+
builder.queryTransforms = [...this.queryTransforms];
|
|
83
|
+
return builder;
|
|
84
|
+
}
|
|
85
|
+
cloneMutable() {
|
|
86
|
+
return this.fork(this.state, this.query);
|
|
87
|
+
}
|
|
88
|
+
assignQuery(builder, query) {
|
|
89
|
+
builder.query = cloneSelectQueryNode(query);
|
|
58
90
|
return builder;
|
|
59
91
|
}
|
|
92
|
+
updateQuery(updater) {
|
|
93
|
+
return this.assignQuery(this.cloneMutable(), updater(this.query));
|
|
94
|
+
}
|
|
95
|
+
withAliasesState(aliases) {
|
|
96
|
+
return {
|
|
97
|
+
...this.state,
|
|
98
|
+
aliases,
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
buildSelectState(output) {
|
|
102
|
+
return {
|
|
103
|
+
...this.state,
|
|
104
|
+
output,
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
buildSelectQuery(selections) {
|
|
108
|
+
return {
|
|
109
|
+
...this.query,
|
|
110
|
+
select: selections.map(selection => ({ kind: 'selection', selection })),
|
|
111
|
+
orderBy: this.query.orderBy?.map(({ column, direction }) => ({
|
|
112
|
+
kind: 'order-by-item',
|
|
113
|
+
column: String(column),
|
|
114
|
+
direction,
|
|
115
|
+
})),
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
createDetachedBuilder() {
|
|
119
|
+
const builder = new QueryBuilder(this.tableName, this.state, this.runtime, this.adapter, this.dialect);
|
|
120
|
+
builder.query = createSelectQueryNode();
|
|
121
|
+
builder.cacheOptions = this.cacheOptions ? { ...this.cacheOptions } : undefined;
|
|
122
|
+
builder.queryTransforms = [...this.queryTransforms];
|
|
123
|
+
return builder;
|
|
124
|
+
}
|
|
125
|
+
runDraftCallback(seed, callback) {
|
|
126
|
+
let current = seed;
|
|
127
|
+
// Group callbacks expect fluent chaining, so the draft keeps rebinding
|
|
128
|
+
// method calls to the latest immutable builder instance produced so far.
|
|
129
|
+
const draft = new Proxy(seed, {
|
|
130
|
+
get: (_target, prop, receiver) => {
|
|
131
|
+
const value = Reflect.get(current, prop, receiver);
|
|
132
|
+
if (typeof value !== 'function') {
|
|
133
|
+
return value;
|
|
134
|
+
}
|
|
135
|
+
return (...args) => {
|
|
136
|
+
const result = value.apply(current, args);
|
|
137
|
+
if (result instanceof QueryBuilder) {
|
|
138
|
+
current = result;
|
|
139
|
+
return draft;
|
|
140
|
+
}
|
|
141
|
+
return result;
|
|
142
|
+
};
|
|
143
|
+
},
|
|
144
|
+
});
|
|
145
|
+
callback(draft);
|
|
146
|
+
return current;
|
|
147
|
+
}
|
|
60
148
|
debug() {
|
|
61
149
|
console.log('Current Type:', {
|
|
62
150
|
state: this.state,
|
|
63
|
-
|
|
151
|
+
query: this.query
|
|
64
152
|
});
|
|
65
153
|
return this;
|
|
66
154
|
}
|
|
67
155
|
cache(options) {
|
|
156
|
+
const next = this.cloneMutable();
|
|
68
157
|
if (options === false) {
|
|
69
|
-
|
|
70
|
-
return
|
|
158
|
+
next.cacheOptions = { mode: 'no-store', ttlMs: 0, staleTtlMs: 0, cacheTimeMs: 0 };
|
|
159
|
+
return next;
|
|
71
160
|
}
|
|
72
|
-
|
|
73
|
-
return
|
|
161
|
+
next.cacheOptions = mergeCacheOptionsPartial(next.cacheOptions, options);
|
|
162
|
+
return next;
|
|
74
163
|
}
|
|
75
164
|
// --- Analytics Helper: Add a CTE.
|
|
76
165
|
withCTE(alias, subquery) {
|
|
77
|
-
this.
|
|
78
|
-
return this;
|
|
166
|
+
return this.updateQuery(() => this.analytics.addCTE(alias, subquery));
|
|
79
167
|
}
|
|
80
168
|
// --- Analytics Helper: Add a scalar WITH alias.
|
|
81
169
|
withScalar(alias, expressionBuilder) {
|
|
@@ -84,17 +172,15 @@ export class QueryBuilder {
|
|
|
84
172
|
}
|
|
85
173
|
const expression = expressionBuilder(createPredicateBuilder());
|
|
86
174
|
const nextConfig = this.analytics.addScalar(alias, expression);
|
|
175
|
+
const nextScalars = {
|
|
176
|
+
...this.state.scalars,
|
|
177
|
+
[alias]: undefined,
|
|
178
|
+
};
|
|
87
179
|
const nextState = {
|
|
88
180
|
...this.state,
|
|
89
|
-
scalars:
|
|
90
|
-
...this.state.scalars,
|
|
91
|
-
[alias]: undefined,
|
|
92
|
-
},
|
|
181
|
+
scalars: nextScalars,
|
|
93
182
|
};
|
|
94
|
-
|
|
95
|
-
builder.config = { ...nextConfig };
|
|
96
|
-
builder.cacheOptions = this.cacheOptions;
|
|
97
|
-
return builder;
|
|
183
|
+
return this.transition(nextState, nextConfig);
|
|
98
184
|
}
|
|
99
185
|
/**
|
|
100
186
|
* Groups results by a time interval using a specified ClickHouse function.
|
|
@@ -109,42 +195,35 @@ export class QueryBuilder {
|
|
|
109
195
|
* @returns The current QueryBuilder instance.
|
|
110
196
|
*/
|
|
111
197
|
groupByTimeInterval(column, interval, method = 'toStartOfInterval') {
|
|
112
|
-
this.
|
|
113
|
-
return this;
|
|
198
|
+
return this.updateQuery(() => this.analytics.addTimeInterval(String(column), interval, method, this.dialect));
|
|
114
199
|
}
|
|
115
200
|
// --- Analytics Helper: Add a raw SQL fragment.
|
|
116
201
|
raw(sql) {
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
return this;
|
|
202
|
+
return this.updateQuery(query => ({
|
|
203
|
+
...query,
|
|
204
|
+
having: [...(query.having || []), { kind: 'having', expression: sql }],
|
|
205
|
+
}));
|
|
122
206
|
}
|
|
123
207
|
// --- Analytics Helper: Add query settings.
|
|
124
208
|
settings(opts) {
|
|
125
|
-
this.
|
|
126
|
-
|
|
209
|
+
return this.updateQuery(() => this.analytics.addSettings(opts));
|
|
210
|
+
}
|
|
211
|
+
final() {
|
|
212
|
+
return this.updateQuery(query => ({
|
|
213
|
+
...query,
|
|
214
|
+
from: {
|
|
215
|
+
kind: 'table',
|
|
216
|
+
name: this.tableName,
|
|
217
|
+
final: true,
|
|
218
|
+
},
|
|
219
|
+
}));
|
|
127
220
|
}
|
|
128
221
|
applyCrossFilters(crossFilter) {
|
|
129
|
-
|
|
130
|
-
this.config = this.crossFiltering.applyCrossFilters(normalized);
|
|
131
|
-
return this;
|
|
222
|
+
return this.crossFiltering.applyCrossFilters(crossFilter);
|
|
132
223
|
}
|
|
133
224
|
select(columnsOrAsterisk) {
|
|
134
225
|
if (columnsOrAsterisk === '*') {
|
|
135
|
-
|
|
136
|
-
...this.state,
|
|
137
|
-
output: {}
|
|
138
|
-
};
|
|
139
|
-
const nextConfig = {
|
|
140
|
-
...this.config,
|
|
141
|
-
select: ['*'],
|
|
142
|
-
orderBy: this.config.orderBy?.map(({ column, direction }) => ({
|
|
143
|
-
column: String(column),
|
|
144
|
-
direction
|
|
145
|
-
}))
|
|
146
|
-
};
|
|
147
|
-
return this.fork(nextState, nextConfig);
|
|
226
|
+
return this.fork(this.buildSelectState({}), this.buildSelectQuery(['*']));
|
|
148
227
|
}
|
|
149
228
|
const columns = columnsOrAsterisk;
|
|
150
229
|
const processedColumns = columns.map(col => {
|
|
@@ -153,19 +232,7 @@ export class QueryBuilder {
|
|
|
153
232
|
}
|
|
154
233
|
return String(col);
|
|
155
234
|
});
|
|
156
|
-
|
|
157
|
-
...this.state,
|
|
158
|
-
output: {}
|
|
159
|
-
};
|
|
160
|
-
const nextConfig = {
|
|
161
|
-
...this.config,
|
|
162
|
-
select: processedColumns,
|
|
163
|
-
orderBy: this.config.orderBy?.map(({ column, direction }) => ({
|
|
164
|
-
column: String(column),
|
|
165
|
-
direction
|
|
166
|
-
}))
|
|
167
|
-
};
|
|
168
|
-
return this.fork(nextState, nextConfig);
|
|
235
|
+
return this.fork(this.buildSelectState({}), this.buildSelectQuery(processedColumns));
|
|
169
236
|
}
|
|
170
237
|
selectConst(...columns) {
|
|
171
238
|
return this.select(columns);
|
|
@@ -188,10 +255,7 @@ export class QueryBuilder {
|
|
|
188
255
|
applyAggregation(column, alias, suffix, updater) {
|
|
189
256
|
const columnName = String(column);
|
|
190
257
|
const finalAlias = (alias || `${columnName}_${suffix}`);
|
|
191
|
-
const nextState = {
|
|
192
|
-
...this.state,
|
|
193
|
-
output: {}
|
|
194
|
-
};
|
|
258
|
+
const nextState = this.buildSelectState({});
|
|
195
259
|
const nextConfig = updater(columnName, finalAlias);
|
|
196
260
|
return this.fork(nextState, nextConfig);
|
|
197
261
|
}
|
|
@@ -256,11 +320,7 @@ export class QueryBuilder {
|
|
|
256
320
|
return;
|
|
257
321
|
}
|
|
258
322
|
// Skip validation for advanced IN operators - they handle their own validation
|
|
259
|
-
|
|
260
|
-
'globalIn', 'globalNotIn', 'inSubquery', 'globalInSubquery',
|
|
261
|
-
'inTable', 'globalInTable', 'inTuple', 'globalInTuple'
|
|
262
|
-
];
|
|
263
|
-
if (advancedInOperators.includes(operator)) {
|
|
323
|
+
if (ADVANCED_IN_OPERATORS.has(operator) || operator === 'isNull' || operator === 'isNotNull') {
|
|
264
324
|
return;
|
|
265
325
|
}
|
|
266
326
|
const columnName = String(column);
|
|
@@ -270,46 +330,25 @@ export class QueryBuilder {
|
|
|
270
330
|
const columnType = baseColumns[columnName];
|
|
271
331
|
FilterValidator.validateFilterCondition({ column: columnName, operator, value }, columnType);
|
|
272
332
|
}
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
this.
|
|
277
|
-
return this;
|
|
278
|
-
}
|
|
279
|
-
if (operator === undefined) {
|
|
280
|
-
throw new Error('Operator is required when specifying a column for where()');
|
|
333
|
+
applyFilter(clause, conjunction, columnOrColumns, operator, value) {
|
|
334
|
+
const normalized = normalizeFilterApplication(clause, conjunction, columnOrColumns, operator, value, builder => builder(createPredicateBuilder()));
|
|
335
|
+
if (normalized.kind === 'expression') {
|
|
336
|
+
return this.updateQuery(() => this.filtering.addExpressionCondition(clause, conjunction, normalized.expression));
|
|
281
337
|
}
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
return this;
|
|
288
|
-
}
|
|
289
|
-
const column = columnOrColumns;
|
|
290
|
-
this.validateFilterValue(column, operator, value);
|
|
291
|
-
this.config = this.filtering.addCondition('AND', String(column), operator, value);
|
|
292
|
-
return this;
|
|
338
|
+
this.validateFilterValue(normalized.validationTarget, normalized.operator, normalized.value);
|
|
339
|
+
return this.updateQuery(() => this.filtering.addCondition(clause, conjunction, normalized.column, normalized.operator, normalized.value));
|
|
340
|
+
}
|
|
341
|
+
where(columnOrColumns, operator, value) {
|
|
342
|
+
return this.applyFilter('where', 'AND', columnOrColumns, operator, value);
|
|
293
343
|
}
|
|
294
344
|
orWhere(columnOrColumns, operator, value) {
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
}
|
|
303
|
-
if (Array.isArray(columnOrColumns) && (operator === 'inTuple' || operator === 'globalInTuple')) {
|
|
304
|
-
const columns = columnOrColumns;
|
|
305
|
-
this.validateFilterValue(columns, operator, value);
|
|
306
|
-
this.config = this.filtering.addCondition('OR', columns.map(String), operator, value);
|
|
307
|
-
return this;
|
|
308
|
-
}
|
|
309
|
-
const column = columnOrColumns;
|
|
310
|
-
this.validateFilterValue(column, operator, value);
|
|
311
|
-
this.config = this.filtering.addCondition('OR', String(column), operator, value);
|
|
312
|
-
return this;
|
|
345
|
+
return this.applyFilter('where', 'OR', columnOrColumns, operator, value);
|
|
346
|
+
}
|
|
347
|
+
prewhere(columnOrColumns, operator, value) {
|
|
348
|
+
return this.applyFilter('prewhere', 'AND', columnOrColumns, operator, value);
|
|
349
|
+
}
|
|
350
|
+
orPrewhere(columnOrColumns, operator, value) {
|
|
351
|
+
return this.applyFilter('prewhere', 'OR', columnOrColumns, operator, value);
|
|
313
352
|
}
|
|
314
353
|
/**
|
|
315
354
|
* Creates a parenthesized group of WHERE conditions joined with AND/OR operators.
|
|
@@ -323,10 +362,8 @@ export class QueryBuilder {
|
|
|
323
362
|
* ```
|
|
324
363
|
*/
|
|
325
364
|
whereGroup(callback) {
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
this.config = this.filtering.endWhereGroup();
|
|
329
|
-
return this;
|
|
365
|
+
const groupBuilder = this.runDraftCallback(this.createDetachedBuilder(), callback);
|
|
366
|
+
return this.updateQuery(() => this.filtering.addGroup('where', 'AND', groupBuilder.getQueryNode().where));
|
|
330
367
|
}
|
|
331
368
|
/**
|
|
332
369
|
* Creates a parenthesized group of WHERE conditions joined with OR operator.
|
|
@@ -340,10 +377,8 @@ export class QueryBuilder {
|
|
|
340
377
|
* ```
|
|
341
378
|
*/
|
|
342
379
|
orWhereGroup(callback) {
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
this.config = this.filtering.endWhereGroup();
|
|
346
|
-
return this;
|
|
380
|
+
const groupBuilder = this.runDraftCallback(this.createDetachedBuilder(), callback);
|
|
381
|
+
return this.updateQuery(() => this.filtering.addGroup('where', 'OR', groupBuilder.getQueryNode().where));
|
|
347
382
|
}
|
|
348
383
|
/**
|
|
349
384
|
* Adds a GROUP BY clause.
|
|
@@ -356,16 +391,23 @@ export class QueryBuilder {
|
|
|
356
391
|
*/
|
|
357
392
|
groupBy(columns) {
|
|
358
393
|
const normalized = Array.isArray(columns) ? columns.map(String) : String(columns);
|
|
359
|
-
this.
|
|
360
|
-
|
|
394
|
+
return this.updateQuery(() => this.modifiers.addGroupBy(normalized));
|
|
395
|
+
}
|
|
396
|
+
arrayJoin(column) {
|
|
397
|
+
return this.updateQuery(() => this.modifiers.addArrayJoin('ARRAY', String(column)));
|
|
398
|
+
}
|
|
399
|
+
leftArrayJoin(column) {
|
|
400
|
+
return this.updateQuery(() => this.modifiers.addArrayJoin('LEFT ARRAY', String(column)));
|
|
361
401
|
}
|
|
362
402
|
limit(count) {
|
|
363
|
-
this.
|
|
364
|
-
|
|
403
|
+
return this.updateQuery(() => this.modifiers.addLimit(count));
|
|
404
|
+
}
|
|
405
|
+
limitBy(count, by) {
|
|
406
|
+
const normalized = Array.isArray(by) ? by.map(String) : String(by);
|
|
407
|
+
return this.updateQuery(() => this.modifiers.addLimitBy(count, normalized));
|
|
365
408
|
}
|
|
366
409
|
offset(count) {
|
|
367
|
-
this.
|
|
368
|
-
return this;
|
|
410
|
+
return this.updateQuery(() => this.modifiers.addOffset(count));
|
|
369
411
|
}
|
|
370
412
|
/**
|
|
371
413
|
* Adds an ORDER BY clause.
|
|
@@ -378,8 +420,7 @@ export class QueryBuilder {
|
|
|
378
420
|
* ```
|
|
379
421
|
*/
|
|
380
422
|
orderBy(column, direction = 'ASC') {
|
|
381
|
-
this.
|
|
382
|
-
return this;
|
|
423
|
+
return this.updateQuery(() => this.modifiers.addOrderBy(String(column), direction));
|
|
383
424
|
}
|
|
384
425
|
/**
|
|
385
426
|
* Adds a HAVING clause for filtering grouped results.
|
|
@@ -391,12 +432,31 @@ export class QueryBuilder {
|
|
|
391
432
|
* ```
|
|
392
433
|
*/
|
|
393
434
|
having(condition, parameters) {
|
|
394
|
-
this.
|
|
395
|
-
return this;
|
|
435
|
+
return this.updateQuery(() => this.modifiers.addHaving(condition, parameters));
|
|
396
436
|
}
|
|
397
437
|
distinct() {
|
|
398
|
-
this.
|
|
399
|
-
|
|
438
|
+
return this.updateQuery(() => this.modifiers.setDistinct());
|
|
439
|
+
}
|
|
440
|
+
withTotals() {
|
|
441
|
+
return this.updateQuery(() => this.modifiers.setWithTotals());
|
|
442
|
+
}
|
|
443
|
+
whereNull(column) {
|
|
444
|
+
return this.updateQuery(() => this.filtering.addCondition('where', 'AND', String(column), 'isNull', null));
|
|
445
|
+
}
|
|
446
|
+
whereNotNull(column) {
|
|
447
|
+
return this.updateQuery(() => this.filtering.addCondition('where', 'AND', String(column), 'isNotNull', null));
|
|
448
|
+
}
|
|
449
|
+
orWhereNull(column) {
|
|
450
|
+
return this.updateQuery(() => this.filtering.addCondition('where', 'OR', String(column), 'isNull', null));
|
|
451
|
+
}
|
|
452
|
+
orWhereNotNull(column) {
|
|
453
|
+
return this.updateQuery(() => this.filtering.addCondition('where', 'OR', String(column), 'isNotNull', null));
|
|
454
|
+
}
|
|
455
|
+
prewhereNull(column) {
|
|
456
|
+
return this.updateQuery(() => this.filtering.addCondition('prewhere', 'AND', String(column), 'isNull', null));
|
|
457
|
+
}
|
|
458
|
+
prewhereNotNull(column) {
|
|
459
|
+
return this.updateQuery(() => this.filtering.addCondition('prewhere', 'AND', String(column), 'isNotNull', null));
|
|
400
460
|
}
|
|
401
461
|
whereBetween(column, [min, max]) {
|
|
402
462
|
if (min === null || max === null) {
|
|
@@ -417,46 +477,43 @@ export class QueryBuilder {
|
|
|
417
477
|
return this.applyJoin('FULL', table, leftColumn, rightColumn, alias);
|
|
418
478
|
}
|
|
419
479
|
applyJoin(type, table, leftColumn, rightColumn, alias) {
|
|
420
|
-
const
|
|
421
|
-
...this.state,
|
|
422
|
-
|
|
423
|
-
|
|
480
|
+
const nextAliases = (alias
|
|
481
|
+
? { ...this.state.aliases, [alias]: table }
|
|
482
|
+
: this.state.aliases);
|
|
483
|
+
const nextState = this.withAliasesState(nextAliases);
|
|
424
484
|
const nextConfig = this.joins.addJoin(type, table, String(leftColumn), rightColumn, alias);
|
|
425
|
-
return this.
|
|
485
|
+
return this.transition(nextState, nextConfig);
|
|
426
486
|
}
|
|
427
|
-
|
|
487
|
+
/**
|
|
488
|
+
* @deprecated Prefer `getQueryNode()` for inspection or `toQueryNode()` for the
|
|
489
|
+
* transformed query tree used during compilation.
|
|
490
|
+
*/
|
|
428
491
|
getConfig() {
|
|
429
|
-
return this.
|
|
492
|
+
return toLegacyQueryConfig(this.getQueryNode());
|
|
493
|
+
}
|
|
494
|
+
toQueryNode() {
|
|
495
|
+
return transformSelectQueryNode(this.query, this.queryTransforms);
|
|
496
|
+
}
|
|
497
|
+
getQueryNode() {
|
|
498
|
+
return cloneSelectQueryNode(this.query);
|
|
430
499
|
}
|
|
431
500
|
static setJoinRelationships(relationships) {
|
|
432
501
|
this.relationships = relationships;
|
|
433
502
|
}
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
if (!relationships) {
|
|
440
|
-
throw new Error('Join relationships have not been initialized. Call QueryBuilder.setJoinRelationships first.');
|
|
441
|
-
}
|
|
442
|
-
const path = relationships.get(name);
|
|
443
|
-
if (!path) {
|
|
444
|
-
throw new Error(`Join relationship '${name}' not found`);
|
|
445
|
-
}
|
|
446
|
-
const applyJoin = (joinPath) => {
|
|
503
|
+
withRelation(nameOrPath, options) {
|
|
504
|
+
const next = this.cloneMutable();
|
|
505
|
+
const { path, label } = resolveRelationPath(nameOrPath, QueryBuilder.relationships);
|
|
506
|
+
next.query = applyRelationPath(next.query, path, options, (currentQuery, joinPath, relationOptions) => {
|
|
507
|
+
next.query = currentQuery;
|
|
447
508
|
const type = options?.type || joinPath.type || 'INNER';
|
|
448
|
-
const alias =
|
|
509
|
+
const alias = relationOptions?.alias || joinPath.alias;
|
|
510
|
+
const leftColumn = String(joinPath.leftColumn);
|
|
511
|
+
const leftSource = String(joinPath.from);
|
|
449
512
|
const table = String(joinPath.to);
|
|
450
513
|
const rightColumn = `${table}.${joinPath.rightColumn}`;
|
|
451
|
-
|
|
452
|
-
};
|
|
453
|
-
|
|
454
|
-
path.forEach(applyJoin);
|
|
455
|
-
}
|
|
456
|
-
else {
|
|
457
|
-
applyJoin(path);
|
|
458
|
-
}
|
|
459
|
-
return this;
|
|
514
|
+
return next.joins.addJoin(type, table, leftColumn, rightColumn, alias, leftSource);
|
|
515
|
+
}, label);
|
|
516
|
+
return next;
|
|
460
517
|
}
|
|
461
518
|
}
|
|
462
519
|
export function createQueryBuilder(config) {
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { ExprNode, QueryConfig, SelectQueryNode } from '../types/index.js';
|
|
2
|
+
export declare function cloneExprNode(expr?: ExprNode): ExprNode | undefined;
|
|
3
|
+
export declare function createSelectQueryNode<TOutput, TSchema>(config?: QueryConfig<TOutput, TSchema>): SelectQueryNode<TOutput, TSchema>;
|
|
4
|
+
export declare function cloneSelectQueryNode<TOutput, TSchema>(query: SelectQueryNode<TOutput, TSchema>): SelectQueryNode<TOutput, TSchema>;
|
|
5
|
+
export type QueryNodeTransform<TOutput, TSchema> = (query: SelectQueryNode<TOutput, TSchema>) => SelectQueryNode<TOutput, TSchema>;
|
|
6
|
+
export declare function transformSelectQueryNode<TOutput, TSchema>(query: SelectQueryNode<TOutput, TSchema>, transforms: ReadonlyArray<QueryNodeTransform<TOutput, TSchema>>): SelectQueryNode<TOutput, TSchema>;
|
|
7
|
+
//# sourceMappingURL=query-node.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"query-node.d.ts","sourceRoot":"","sources":["../../src/core/query-node.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAEV,QAAQ,EACR,WAAW,EACX,eAAe,EAChB,MAAM,mBAAmB,CAAC;AAY3B,wBAAgB,aAAa,CAAC,IAAI,CAAC,EAAE,QAAQ,GAAG,QAAQ,GAAG,SAAS,CAgCnE;AAED,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,OAAO,EACpD,MAAM,GAAE,WAAW,CAAC,OAAO,EAAE,OAAO,CAAM,GACzC,eAAe,CAAC,OAAO,EAAE,OAAO,CAAC,CA+BnC;AAED,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,OAAO,EACnD,KAAK,EAAE,eAAe,CAAC,OAAO,EAAE,OAAO,CAAC,GACvC,eAAe,CAAC,OAAO,EAAE,OAAO,CAAC,CAEnC;AAED,MAAM,MAAM,kBAAkB,CAAC,OAAO,EAAE,OAAO,IAAI,CACjD,KAAK,EAAE,eAAe,CAAC,OAAO,EAAE,OAAO,CAAC,KACrC,eAAe,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;AAEvC,wBAAgB,wBAAwB,CAAC,OAAO,EAAE,OAAO,EACvD,KAAK,EAAE,eAAe,CAAC,OAAO,EAAE,OAAO,CAAC,EACxC,UAAU,EAAE,aAAa,CAAC,kBAAkB,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC,GAC9D,eAAe,CAAC,OAAO,EAAE,OAAO,CAAC,CAKnC"}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
function cloneConditionValue(value) {
|
|
2
|
+
if (typeof value === 'string') {
|
|
3
|
+
return value;
|
|
4
|
+
}
|
|
5
|
+
if (Array.isArray(value)) {
|
|
6
|
+
return value.map(item => Array.isArray(item) ? item.map(inner => ({ ...inner })) : { ...item });
|
|
7
|
+
}
|
|
8
|
+
return { ...value };
|
|
9
|
+
}
|
|
10
|
+
export function cloneExprNode(expr) {
|
|
11
|
+
if (!expr)
|
|
12
|
+
return undefined;
|
|
13
|
+
switch (expr.kind) {
|
|
14
|
+
case 'condition':
|
|
15
|
+
return { ...expr, value: cloneConditionValue(expr.value) };
|
|
16
|
+
case 'raw':
|
|
17
|
+
return {
|
|
18
|
+
...expr,
|
|
19
|
+
parameters: expr.parameters.map(parameter => ({ ...parameter })),
|
|
20
|
+
};
|
|
21
|
+
case 'group':
|
|
22
|
+
return {
|
|
23
|
+
...expr,
|
|
24
|
+
expression: cloneExprNode(expr.expression),
|
|
25
|
+
};
|
|
26
|
+
case 'logical':
|
|
27
|
+
return {
|
|
28
|
+
...expr,
|
|
29
|
+
conditions: expr.conditions.map(condition => cloneExprNode(condition)),
|
|
30
|
+
};
|
|
31
|
+
case 'sequence':
|
|
32
|
+
return {
|
|
33
|
+
...expr,
|
|
34
|
+
items: expr.items.map(item => ({
|
|
35
|
+
...item,
|
|
36
|
+
expression: cloneExprNode(item.expression),
|
|
37
|
+
})),
|
|
38
|
+
};
|
|
39
|
+
default:
|
|
40
|
+
throw new Error(`Unsupported expression kind: ${String(expr.kind)}`);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
export function createSelectQueryNode(config = {}) {
|
|
44
|
+
return {
|
|
45
|
+
kind: 'select-query',
|
|
46
|
+
from: config.from ? { ...config.from } : undefined,
|
|
47
|
+
select: config.select ? config.select.map(item => ({ ...item })) : undefined,
|
|
48
|
+
arrayJoins: config.arrayJoins ? config.arrayJoins.map(item => ({ ...item })) : undefined,
|
|
49
|
+
prewhere: cloneExprNode(config.prewhere),
|
|
50
|
+
where: cloneExprNode(config.where),
|
|
51
|
+
groupBy: config.groupBy ? config.groupBy.map(item => ({ ...item })) : undefined,
|
|
52
|
+
withTotals: config.withTotals,
|
|
53
|
+
having: config.having
|
|
54
|
+
? config.having.map(item => ({
|
|
55
|
+
...item,
|
|
56
|
+
parameters: item.parameters?.map(parameter => ({ ...parameter })),
|
|
57
|
+
}))
|
|
58
|
+
: undefined,
|
|
59
|
+
limitBy: config.limitBy
|
|
60
|
+
? {
|
|
61
|
+
...config.limitBy,
|
|
62
|
+
by: [...config.limitBy.by],
|
|
63
|
+
}
|
|
64
|
+
: undefined,
|
|
65
|
+
limit: config.limit,
|
|
66
|
+
offset: config.offset,
|
|
67
|
+
distinct: config.distinct,
|
|
68
|
+
orderBy: config.orderBy ? config.orderBy.map(item => ({ ...item })) : undefined,
|
|
69
|
+
joins: config.joins ? config.joins.map(item => ({ ...item })) : undefined,
|
|
70
|
+
ctes: config.ctes ? config.ctes.map(item => ({ ...item })) : undefined,
|
|
71
|
+
unionQueries: config.unionQueries ? [...config.unionQueries] : undefined,
|
|
72
|
+
settings: config.settings ? { ...config.settings } : undefined,
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
export function cloneSelectQueryNode(query) {
|
|
76
|
+
return createSelectQueryNode(query);
|
|
77
|
+
}
|
|
78
|
+
export function transformSelectQueryNode(query, transforms) {
|
|
79
|
+
return transforms.reduce((current, transform) => transform(current), cloneSelectQueryNode(query));
|
|
80
|
+
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"setup.d.ts","sourceRoot":"","sources":["../../../../src/core/tests/integration/setup.ts"],"names":[],"mappings":"AAgDA,eAAO,MAAM,sBAAsB;;;;;CAAS,CAAC;AAG7C,eAAO,MAAM,wBAAwB;;;;;;EAyBpC,CAAC;AAGF,eAAO,MAAM,2BAA2B,yGAevC,CAAC;AAGF,eAAO,MAAM,iBAAiB,QAAa,OAAO,CAAC,OAAO,CAOzD,CAAC;AAGF,eAAO,MAAM,wBAAwB,QAAa,OAAO,CAAC,OAAO,CAahE,CAAC;AAGF,eAAO,MAAM,kBAAkB,GAAU,eAAe,MAAM,KAAG,OAAO,CAAC,OAAO,CAO/E,CAAC;AAGF,eAAO,MAAM,iBAAiB,QAAa,OAAO,CAAC,OAAO,CAQzD,CAAC;AAGF,eAAO,MAAM,wBAAwB,QAAa,OAAO,CAAC,IAAI,CAwC7D,CAAC;AAGF,eAAO,MAAM,iBAAiB,GAC5B,oBAAgB,EAChB,sBAAoB,KACnB,OAAO,CAAC,IAAI,CAad,CAAC;AAGF,eAAO,MAAM,uBAAuB,QAAa,OAAO,CAAC,IAAI,CA0B5D,CAAC;AAGF,MAAM,WAAW,cAAc;IAC7B,UAAU,EAAE,KAAK,CAAC;QAChB,EAAE,EAAE,MAAM,CAAC;QACX,IAAI,EAAE,MAAM,CAAC;QACb,QAAQ,EAAE,MAAM,CAAC;QACjB,KAAK,EAAE,MAAM,CAAC;QACd,UAAU,EAAE,MAAM,CAAC;QACnB,SAAS,EAAE,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"setup.d.ts","sourceRoot":"","sources":["../../../../src/core/tests/integration/setup.ts"],"names":[],"mappings":"AAgDA,eAAO,MAAM,sBAAsB;;;;;CAAS,CAAC;AAG7C,eAAO,MAAM,wBAAwB;;;;;;EAyBpC,CAAC;AAGF,eAAO,MAAM,2BAA2B,yGAevC,CAAC;AAGF,eAAO,MAAM,iBAAiB,QAAa,OAAO,CAAC,OAAO,CAOzD,CAAC;AAGF,eAAO,MAAM,wBAAwB,QAAa,OAAO,CAAC,OAAO,CAahE,CAAC;AAGF,eAAO,MAAM,kBAAkB,GAAU,eAAe,MAAM,KAAG,OAAO,CAAC,OAAO,CAO/E,CAAC;AAGF,eAAO,MAAM,iBAAiB,QAAa,OAAO,CAAC,OAAO,CAQzD,CAAC;AAGF,eAAO,MAAM,wBAAwB,QAAa,OAAO,CAAC,IAAI,CAwC7D,CAAC;AAGF,eAAO,MAAM,iBAAiB,GAC5B,oBAAgB,EAChB,sBAAoB,KACnB,OAAO,CAAC,IAAI,CAad,CAAC;AAGF,eAAO,MAAM,uBAAuB,QAAa,OAAO,CAAC,IAAI,CA0B5D,CAAC;AAGF,MAAM,WAAW,cAAc;IAC7B,UAAU,EAAE,KAAK,CAAC;QAChB,EAAE,EAAE,MAAM,CAAC;QACX,IAAI,EAAE,MAAM,CAAC;QACb,QAAQ,EAAE,MAAM,CAAC;QACjB,KAAK,EAAE,MAAM,CAAC;QACd,UAAU,EAAE,MAAM,CAAC;QACnB,SAAS,EAAE,OAAO,CAAC;QACnB,IAAI,EAAE,MAAM,EAAE,CAAC;KAChB,CAAC,CAAC;IACH,KAAK,EAAE,KAAK,CAAC;QACX,EAAE,EAAE,MAAM,CAAC;QACX,SAAS,EAAE,MAAM,CAAC;QAClB,KAAK,EAAE,MAAM,CAAC;QACd,MAAM,EAAE,MAAM,CAAC;QACf,UAAU,EAAE,MAAM,CAAC;KACpB,CAAC,CAAC;IACH,MAAM,EAAE,KAAK,CAAC;QACZ,EAAE,EAAE,MAAM,CAAC;QACX,OAAO,EAAE,MAAM,CAAC;QAChB,UAAU,EAAE,MAAM,CAAC;QACnB,QAAQ,EAAE,MAAM,CAAC;QACjB,KAAK,EAAE,MAAM,CAAC;QACd,MAAM,EAAE,MAAM,CAAC;QACf,UAAU,EAAE,MAAM,CAAC;KACpB,CAAC,CAAC;CACJ;AAmDD,eAAO,MAAM,SAAS,EAAE,cAAoC,CAAC;AAK7D,eAAO,MAAM,iBAAiB,QAAa,OAAO,CAAC,IAAI,CAmGtD,CAAC"}
|
|
@@ -234,7 +234,8 @@ function normalizeTestData() {
|
|
|
234
234
|
category: row.category,
|
|
235
235
|
price: row.price,
|
|
236
236
|
created_at: normalizeDateValue(row.created_at),
|
|
237
|
-
is_active: row.is_active
|
|
237
|
+
is_active: row.is_active,
|
|
238
|
+
tags: row.tags ?? [],
|
|
238
239
|
}));
|
|
239
240
|
const users = (rawTestData.users ?? []).map(row => ({
|
|
240
241
|
id: row.id,
|
|
@@ -285,7 +286,8 @@ export const setupTestDatabase = async () => {
|
|
|
285
286
|
category String,
|
|
286
287
|
price Float64,
|
|
287
288
|
created_at Date,
|
|
288
|
-
is_active Boolean
|
|
289
|
+
is_active Boolean,
|
|
290
|
+
tags Array(String)
|
|
289
291
|
) ENGINE = MergeTree()
|
|
290
292
|
ORDER BY id
|
|
291
293
|
`
|
|
@@ -18,6 +18,9 @@ export type SelectableItem<State extends AnyBuilderState> = SelectableColumn<Sta
|
|
|
18
18
|
export type ColumnSelectionKey<P> = P extends `${string}.${infer C}` ? C : P;
|
|
19
19
|
type QualifiedColumnValue<State extends AnyBuilderState, P> = P extends `${infer Table}.${infer Column}` ? ResolveTableSchema<State, Table> extends Record<string, ColumnType> ? Column extends keyof ResolveTableSchema<State, Table> ? ResolveTableSchema<State, Table>[Column] extends ColumnType ? InferColumnType<ResolveTableSchema<State, Table>[Column]> : never : never : never : never;
|
|
20
20
|
export type ColumnSelectionValue<State extends AnyBuilderState, P> = P extends OutputColumnKeys<State> ? State['output'][P] : P extends ScalarColumnKeys<State> ? State['scalars'][P] : P extends BaseColumnKeys<State> ? BaseRow<State>[P] : QualifiedColumnValue<State, P>;
|
|
21
|
+
export type ArraySelectableColumn<State extends AnyBuilderState> = {
|
|
22
|
+
[P in SelectableColumn<State>]: ColumnSelectionValue<State, P> extends readonly unknown[] | null ? P : never;
|
|
23
|
+
}[SelectableColumn<State>];
|
|
21
24
|
export type ColumnSelectionRecord<State extends AnyBuilderState, K> = {
|
|
22
25
|
[P in Extract<K, SelectableColumn<State>> as ColumnSelectionKey<P>]: ColumnSelectionValue<State, P>;
|
|
23
26
|
};
|