@housekit/orm 0.1.25 → 0.1.26
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 +249 -134
- package/dist/builders/insert.d.ts +25 -22
- package/dist/builders/select.d.ts +3 -1
- package/dist/client.d.ts +15 -0
- package/dist/column.d.ts +2 -2
- package/dist/index.d.ts +45 -6
- package/dist/index.js +445 -150
- package/dist/relational.d.ts +92 -20
- package/dist/schema-builder.d.ts +20 -4
- package/dist/table.d.ts +11 -1
- package/dist/utils/batch-transform.d.ts +0 -2
- package/dist/utils/binary-serializer.d.ts +57 -0
- package/dist/utils/binary-worker-code.d.ts +1 -1
- package/dist/utils/binary-worker-pool.d.ts +2 -2
- package/dist/utils/insert-processing.d.ts +7 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -14,10 +14,10 @@ HouseKit ORM is a modern database toolkit designed specifically for ClickHouse.
|
|
|
14
14
|
## 🚀 Key Features
|
|
15
15
|
|
|
16
16
|
- **🛡️ First-Class TypeScript**: Full type inference for every query. Schema definition acts as the single source of truth.
|
|
17
|
-
- **🏎️
|
|
17
|
+
- **🏎️ Binary Inserts by Default**: Native `RowBinary` serialization is used automatically. **5-10x faster** than JSON.
|
|
18
18
|
- **🏗️ ClickHouse Native Engines**: Fluent DSL for `MergeTree`, `ReplacingMergeTree`, `SummingMergeTree`, `Distributed`, `Buffer`, and more.
|
|
19
19
|
- **🔍 Advanced Analytics**: Specialized support for `ASOF JOIN`, `ARRAY JOIN`, `PREWHERE`, and complex Window Functions.
|
|
20
|
-
- **🤝 Smart Relational API**: Query relations using `groupArray` internally, preventing row duplication
|
|
20
|
+
- **🤝 Smart Relational API**: Query relations using `groupArray` internally, preventing row duplication.
|
|
21
21
|
- **📦 Background Batching**: Built-in buffering to collect small inserts into high-performance batches automatically.
|
|
22
22
|
|
|
23
23
|
---
|
|
@@ -25,9 +25,6 @@ HouseKit ORM is a modern database toolkit designed specifically for ClickHouse.
|
|
|
25
25
|
## 📦 Installation
|
|
26
26
|
|
|
27
27
|
```bash
|
|
28
|
-
# HouseKit requires the official ClickHouse client as a peer dependency
|
|
29
|
-
npm install @housekit/orm @clickhouse/client
|
|
30
|
-
# or
|
|
31
28
|
bun add @housekit/orm @clickhouse/client
|
|
32
29
|
```
|
|
33
30
|
|
|
@@ -35,68 +32,192 @@ bun add @housekit/orm @clickhouse/client
|
|
|
35
32
|
|
|
36
33
|
## ⚡️ Quick Start
|
|
37
34
|
|
|
38
|
-
### 1. Define your Table
|
|
39
|
-
Use the fluent `defineTable` API. You can export the inferred types directly from the schema definition thanks to **Phantom Types**.
|
|
35
|
+
### 1. Define your Table
|
|
40
36
|
|
|
41
37
|
```typescript
|
|
42
38
|
// schema.ts
|
|
43
|
-
import { defineTable, t, Engine } from '@housekit/orm';
|
|
44
|
-
|
|
45
|
-
export const
|
|
46
|
-
id: t.uuid('id').primaryKey(),
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
tags: t.array(t.string('tag')),
|
|
51
|
-
metadata: t.json('metadata'),
|
|
52
|
-
at: t.datetime('at').default('now()'),
|
|
39
|
+
import { defineTable, t, Engine, relations } from '@housekit/orm';
|
|
40
|
+
|
|
41
|
+
export const users = defineTable('users', {
|
|
42
|
+
id: t.uuid('id').autoGenerate({ version: 7 }).primaryKey().default('generateUUIDv7()'),
|
|
43
|
+
email: t.string('email'),
|
|
44
|
+
role: t.enum('role', ['admin', 'user']),
|
|
45
|
+
...t.timestamps(),
|
|
53
46
|
}, {
|
|
54
47
|
engine: Engine.MergeTree(),
|
|
55
|
-
orderBy: '
|
|
56
|
-
partitionBy: 'toYYYYMM(at)',
|
|
57
|
-
ttl: 'at + INTERVAL 1 MONTH'
|
|
48
|
+
orderBy: 'id'
|
|
58
49
|
});
|
|
59
50
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
51
|
+
export const posts = defineTable('posts', {
|
|
52
|
+
id: t.uuid('id').autoGenerate({ version: 7 }).primaryKey().default('generateUUIDv7()'),
|
|
53
|
+
userId: t.uuid('user_id'),
|
|
54
|
+
title: t.string('title'),
|
|
55
|
+
createdAt: t.timestamp('created_at').default('now()'),
|
|
56
|
+
}, {
|
|
57
|
+
engine: Engine.MergeTree(),
|
|
58
|
+
orderBy: 'createdAt'
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
relations(users, ({ many }) => ({
|
|
62
|
+
posts: many(posts, { fields: [users.id], references: [posts.userId] })
|
|
63
|
+
}));
|
|
64
|
+
|
|
65
|
+
export type User = typeof users.$type;
|
|
66
|
+
export type NewUser = typeof users.$insert;
|
|
63
67
|
```
|
|
64
68
|
|
|
65
69
|
### 2. Connect and Query
|
|
66
|
-
HouseKit automatically picks up configuration from your environment or `housekit.config.ts`.
|
|
67
70
|
|
|
68
71
|
```typescript
|
|
69
|
-
import {
|
|
70
|
-
import
|
|
71
|
-
|
|
72
|
-
const db =
|
|
73
|
-
|
|
74
|
-
//
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
})
|
|
81
|
-
.
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
72
|
+
import { housekit } from '@housekit/orm';
|
|
73
|
+
import * as schema from './schema';
|
|
74
|
+
|
|
75
|
+
const db = housekit({ url: 'http://localhost:8123' }, { schema });
|
|
76
|
+
|
|
77
|
+
// Binary insert (default, fastest)
|
|
78
|
+
await db.insert(schema.users).values({ email: 'a@b.com', role: 'admin' });
|
|
79
|
+
|
|
80
|
+
// JSON insert with returning data
|
|
81
|
+
const [user] = await db
|
|
82
|
+
.insert(schema.users)
|
|
83
|
+
.values({ email: 'a@b.com', role: 'admin' })
|
|
84
|
+
.returning();
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
---
|
|
88
|
+
|
|
89
|
+
## 🔍 Relational Query API
|
|
90
|
+
|
|
91
|
+
### findMany / findFirst
|
|
92
|
+
|
|
93
|
+
```typescript
|
|
94
|
+
const users = await db.query.users.findMany({
|
|
95
|
+
where: { role: 'admin', active: true },
|
|
96
|
+
columns: { id: true, email: true },
|
|
97
|
+
orderBy: (cols, { desc }) => desc(cols.createdAt),
|
|
98
|
+
limit: 10,
|
|
99
|
+
with: {
|
|
100
|
+
posts: { limit: 5 }
|
|
101
|
+
}
|
|
102
|
+
});
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
### findById
|
|
106
|
+
|
|
107
|
+
```typescript
|
|
108
|
+
// Simple lookup
|
|
109
|
+
const user = await db.query.users.findById('uuid-here');
|
|
110
|
+
|
|
111
|
+
// With relations
|
|
112
|
+
const user = await db.query.users.findById('uuid-here', {
|
|
113
|
+
with: { posts: true }
|
|
114
|
+
});
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
### where syntax
|
|
118
|
+
|
|
119
|
+
```typescript
|
|
120
|
+
// Object syntax (simplest)
|
|
121
|
+
where: { email: 'a@b.com' }
|
|
122
|
+
where: { role: 'admin', active: true } // AND implícito
|
|
123
|
+
|
|
124
|
+
// Direct expression
|
|
125
|
+
where: eq(users.role, 'admin')
|
|
126
|
+
|
|
127
|
+
// Callback for complex filters
|
|
128
|
+
where: (cols, { and, gt, inArray }) => and(
|
|
129
|
+
gt(cols.age, 18),
|
|
130
|
+
inArray(cols.role, ['admin', 'moderator'])
|
|
131
|
+
)
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
Available operators: `eq`, `ne`, `gt`, `gte`, `lt`, `lte`, `inArray`, `notInArray`, `between`, `notBetween`, `has`, `hasAll`, `hasAny`, `and`, `or`, `not`, `isNull`, `isNotNull`
|
|
135
|
+
|
|
136
|
+
### columns selection
|
|
137
|
+
|
|
138
|
+
Select specific columns:
|
|
139
|
+
|
|
140
|
+
```typescript
|
|
141
|
+
const users = await db.query.users.findMany({
|
|
142
|
+
columns: { id: true, email: true }
|
|
143
|
+
});
|
|
144
|
+
// Returns: [{ id: '...', email: '...' }]
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
### orderBy
|
|
148
|
+
|
|
149
|
+
```typescript
|
|
150
|
+
// Callback (recommended)
|
|
151
|
+
orderBy: (cols, { desc }) => desc(cols.createdAt)
|
|
152
|
+
|
|
153
|
+
// Multiple columns
|
|
154
|
+
orderBy: (cols, { desc, asc }) => [desc(cols.createdAt), asc(cols.name)]
|
|
155
|
+
|
|
156
|
+
// Direct value
|
|
157
|
+
orderBy: desc(users.createdAt)
|
|
158
|
+
|
|
159
|
+
// Array
|
|
160
|
+
orderBy: [desc(users.createdAt), asc(users.name)]
|
|
89
161
|
```
|
|
90
162
|
|
|
91
163
|
---
|
|
92
164
|
|
|
93
|
-
##
|
|
165
|
+
## 🚀 High-Performance Inserts
|
|
166
|
+
|
|
167
|
+
### Binary Insert (Default)
|
|
168
|
+
|
|
169
|
+
Binary format is used by default for maximum performance:
|
|
170
|
+
|
|
171
|
+
```typescript
|
|
172
|
+
// Binary insert (no data returned)
|
|
173
|
+
await db.insert(events).values([
|
|
174
|
+
{ type: 'click', userId: '...' },
|
|
175
|
+
{ type: 'view', userId: '...' },
|
|
176
|
+
]);
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
### JSON Insert with Returning
|
|
180
|
+
|
|
181
|
+
Use `.returning()` when you need the inserted data back:
|
|
182
|
+
|
|
183
|
+
```typescript
|
|
184
|
+
const [user] = await db
|
|
185
|
+
.insert(users)
|
|
186
|
+
.values({ email: 'a@b.com', role: 'admin' })
|
|
187
|
+
.returning();
|
|
188
|
+
|
|
189
|
+
console.log(user.id); // Generated UUID
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
### Force JSON Format
|
|
193
|
+
|
|
194
|
+
```typescript
|
|
195
|
+
await db.insert(events).values(data).useJsonFormat();
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
### Background Batching
|
|
199
|
+
|
|
200
|
+
Collect small writes into efficient batches:
|
|
201
|
+
|
|
202
|
+
```typescript
|
|
203
|
+
const builder = db.insert(events).batch({
|
|
204
|
+
maxRows: 10000,
|
|
205
|
+
flushIntervalMs: 5000
|
|
206
|
+
});
|
|
207
|
+
|
|
208
|
+
// Fire-and-forget
|
|
209
|
+
await builder.append(event1);
|
|
210
|
+
await builder.append(event2);
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
---
|
|
214
|
+
|
|
215
|
+
## 🧠 Advanced Schema
|
|
94
216
|
|
|
95
217
|
### Complex Engines
|
|
96
|
-
HouseKit supports specialized ClickHouse engines with strict type checking for their parameters.
|
|
97
218
|
|
|
98
219
|
```typescript
|
|
99
|
-
// SummingMergeTree
|
|
220
|
+
// SummingMergeTree
|
|
100
221
|
export const dailyRevenue = defineTable('daily_revenue', {
|
|
101
222
|
day: t.date('day'),
|
|
102
223
|
revenue: t.float64('revenue'),
|
|
@@ -105,23 +226,19 @@ export const dailyRevenue = defineTable('daily_revenue', {
|
|
|
105
226
|
orderBy: 'day'
|
|
106
227
|
});
|
|
107
228
|
|
|
108
|
-
// ReplacingMergeTree
|
|
229
|
+
// ReplacingMergeTree
|
|
109
230
|
export const users = defineTable('users', {
|
|
110
231
|
id: t.uint64('id'),
|
|
111
232
|
email: t.string('email'),
|
|
112
233
|
version: t.uint64('version'),
|
|
113
234
|
}, {
|
|
114
235
|
engine: Engine.ReplacingMergeTree('version'),
|
|
115
|
-
|
|
116
|
-
// Portability: '{cluster}' references the server-side macro.
|
|
117
|
-
onCluster: '{cluster}',
|
|
118
|
-
|
|
236
|
+
onCluster: '{cluster}',
|
|
119
237
|
orderBy: 'id'
|
|
120
238
|
});
|
|
121
239
|
```
|
|
122
240
|
|
|
123
241
|
### Dictionaries
|
|
124
|
-
Map external data or internal tables to fast in-memory dictionaries for ultra-low latency lookups.
|
|
125
242
|
|
|
126
243
|
```typescript
|
|
127
244
|
import { defineDictionary } from '@housekit/orm';
|
|
@@ -138,130 +255,128 @@ export const userCache = defineDictionary('user_dict', {
|
|
|
138
255
|
|
|
139
256
|
---
|
|
140
257
|
|
|
141
|
-
##
|
|
258
|
+
## 🔍 Specialized Joins
|
|
142
259
|
|
|
143
|
-
###
|
|
144
|
-
When you call `db.insert()`, HouseKit analyzes your schema. If types are compatible, it automatically switches to **Turbo Mode**, using native binary serialization instead of JSON.
|
|
260
|
+
### ASOF JOIN
|
|
145
261
|
|
|
146
262
|
```typescript
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
]);
|
|
152
|
-
// Logic: Object -> Buffer (Binary) -> ClickHouse Stream (Zero-copy)
|
|
263
|
+
const matched = await db.select()
|
|
264
|
+
.from(trades)
|
|
265
|
+
.asofJoin(quotes, sql`${trades.symbol} = ${quotes.symbol} AND ${trades.at} >= ${quotes.at}`)
|
|
266
|
+
.limit(100);
|
|
153
267
|
```
|
|
154
268
|
|
|
155
|
-
###
|
|
156
|
-
Collect small, frequent writes into large batches to prevent the "too many parts" error in ClickHouse.
|
|
269
|
+
### GLOBAL JOIN
|
|
157
270
|
|
|
158
271
|
```typescript
|
|
159
|
-
|
|
160
|
-
.
|
|
161
|
-
|
|
162
|
-
flushIntervalMs: 5000
|
|
163
|
-
});
|
|
164
|
-
|
|
165
|
-
// Add rows to the background queue.
|
|
166
|
-
// Processing and flushing happen automatically.
|
|
167
|
-
// Returns immediately (Fire-and-forget).
|
|
168
|
-
await builder.append(row1);
|
|
169
|
-
await builder.append(row2);
|
|
272
|
+
await db.select()
|
|
273
|
+
.from(distributedTable)
|
|
274
|
+
.globalJoin(rightTable, condition);
|
|
170
275
|
```
|
|
171
276
|
|
|
172
277
|
---
|
|
173
278
|
|
|
174
|
-
##
|
|
175
|
-
|
|
176
|
-
Because we use **Phantom Types**, you don't need to import generic helpers like `Infer<T>` in your application code. You can use `typeof table.$infer...` or the types you exported from your schema file.
|
|
279
|
+
## 🛠 SQL Utilities
|
|
177
280
|
|
|
178
|
-
###
|
|
281
|
+
### Dynamic Queries
|
|
179
282
|
|
|
180
283
|
```typescript
|
|
181
|
-
|
|
284
|
+
const conditions = [
|
|
285
|
+
eq(users.active, true),
|
|
286
|
+
gte(users.age, 18)
|
|
287
|
+
];
|
|
182
288
|
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
}
|
|
289
|
+
const query = await db.select()
|
|
290
|
+
.from(users)
|
|
291
|
+
.where(sql.join(conditions, sql` AND `));
|
|
187
292
|
```
|
|
188
293
|
|
|
189
294
|
---
|
|
190
295
|
|
|
191
|
-
##
|
|
296
|
+
## ⚡ Performance Optimizations
|
|
297
|
+
|
|
298
|
+
HouseKit includes several optimizations for maximum throughput in production environments.
|
|
192
299
|
|
|
193
|
-
|
|
300
|
+
### Connection Pooling
|
|
301
|
+
|
|
302
|
+
Reuse HTTP connections across requests:
|
|
194
303
|
|
|
195
304
|
```typescript
|
|
196
|
-
|
|
197
|
-
|
|
305
|
+
const db = housekit({
|
|
306
|
+
url: 'http://localhost:8123',
|
|
307
|
+
pool: {
|
|
308
|
+
maxSockets: 200, // Max concurrent connections
|
|
309
|
+
keepAlive: true, // Reuse connections
|
|
310
|
+
timeout: 30000 // Socket timeout (ms)
|
|
311
|
+
}
|
|
312
|
+
}, { schema });
|
|
313
|
+
```
|
|
198
314
|
|
|
199
|
-
|
|
200
|
-
posts: many(posts, { fields: [users.id], references: [posts.userId] })
|
|
201
|
-
}));
|
|
315
|
+
### Skip Validation
|
|
202
316
|
|
|
203
|
-
|
|
204
|
-
const usersWithData = await db.query.users.findMany({
|
|
205
|
-
where: { country: 'US' }, // Object syntax
|
|
206
|
-
with: {
|
|
207
|
-
posts: {
|
|
208
|
-
limit: 5,
|
|
209
|
-
orderBy: { col: posts.createdAt, dir: 'DESC' }
|
|
210
|
-
}
|
|
211
|
-
},
|
|
212
|
-
limit: 10
|
|
213
|
-
});
|
|
317
|
+
Bypass enum validation in production when you trust your data source:
|
|
214
318
|
|
|
215
|
-
|
|
216
|
-
//
|
|
319
|
+
```typescript
|
|
320
|
+
// Global (all inserts)
|
|
321
|
+
const db = housekit({
|
|
322
|
+
url: 'http://localhost:8123',
|
|
323
|
+
skipValidation: true
|
|
324
|
+
}, { schema });
|
|
325
|
+
|
|
326
|
+
// Per-insert
|
|
327
|
+
await db.insert(events).values(data).skipValidation();
|
|
217
328
|
```
|
|
218
329
|
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
## 🛠 SQL Utilities
|
|
330
|
+
### Object Pooling
|
|
222
331
|
|
|
223
|
-
|
|
224
|
-
Easily build complex queries by joining SQL fragments with separators.
|
|
332
|
+
HouseKit automatically pools `BinaryWriter` instances to reduce GC pressure. For advanced use cases:
|
|
225
333
|
|
|
226
334
|
```typescript
|
|
227
|
-
|
|
228
|
-
eq(users.active, true),
|
|
229
|
-
gte(users.age, 18)
|
|
230
|
-
];
|
|
335
|
+
import { acquireWriter, releaseWriter } from '@housekit/orm';
|
|
231
336
|
|
|
232
|
-
const
|
|
233
|
-
|
|
234
|
-
|
|
337
|
+
const writer = acquireWriter();
|
|
338
|
+
try {
|
|
339
|
+
// Use writer for custom serialization
|
|
340
|
+
} finally {
|
|
341
|
+
releaseWriter(writer);
|
|
342
|
+
}
|
|
235
343
|
```
|
|
236
344
|
|
|
237
|
-
|
|
345
|
+
### Optimized Serialization
|
|
238
346
|
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
### ASOF JOIN
|
|
242
|
-
The industry standard for time-series matches (e.g., matching a trade with the closest price quote).
|
|
347
|
+
For maximum performance with large batches:
|
|
243
348
|
|
|
244
349
|
```typescript
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
350
|
+
import {
|
|
351
|
+
buildOptimizedBinaryConfig,
|
|
352
|
+
serializeRowsOptimized
|
|
353
|
+
} from '@housekit/orm';
|
|
354
|
+
|
|
355
|
+
// Pre-compile accessors once
|
|
356
|
+
const config = buildOptimizedBinaryConfig(columns, {
|
|
357
|
+
skipValidation: true
|
|
358
|
+
});
|
|
359
|
+
|
|
360
|
+
// Serialize batches with pooled writer + pre-compiled accessors
|
|
361
|
+
const buffer = serializeRowsOptimized(rows, config);
|
|
249
362
|
```
|
|
250
363
|
|
|
251
|
-
###
|
|
252
|
-
|
|
364
|
+
### TypedArray for Numeric Data
|
|
365
|
+
|
|
366
|
+
For schemas with mostly numeric columns:
|
|
253
367
|
|
|
254
368
|
```typescript
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
369
|
+
import { isNumericHeavySchema, serializeNumericBatch } from '@housekit/orm';
|
|
370
|
+
|
|
371
|
+
if (isNumericHeavySchema(columns)) {
|
|
372
|
+
// Uses Float64Array for better memory layout
|
|
373
|
+
const { numericBuffer, otherData } = serializeNumericBatch(rows, config, numericIndices);
|
|
374
|
+
}
|
|
258
375
|
```
|
|
259
376
|
|
|
260
377
|
---
|
|
261
378
|
|
|
262
|
-
## 🛠 Observability
|
|
263
|
-
|
|
264
|
-
Inject a custom logger to monitor query performance, throughput, and error rates.
|
|
379
|
+
## 🛠 Observability
|
|
265
380
|
|
|
266
381
|
```typescript
|
|
267
382
|
const db = await createClient({
|
|
@@ -278,4 +393,4 @@ const db = await createClient({
|
|
|
278
393
|
|
|
279
394
|
## License
|
|
280
395
|
|
|
281
|
-
MIT © [Pablo Fernandez Ruiz](https://github.com/pablofdezr)
|
|
396
|
+
MIT © [Pablo Fernandez Ruiz](https://github.com/pablofdezr)
|
|
@@ -3,40 +3,43 @@ import { type TableRuntime, type CleanInsert, type CleanSelect } from '../core';
|
|
|
3
3
|
import { type BatchTransformOptions } from '../utils/batch-transform';
|
|
4
4
|
import { Readable } from 'stream';
|
|
5
5
|
import { type BatchConfig } from '../utils/background-batcher';
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
type InsertFormat = 'auto' | 'binary' | 'json' | 'compact';
|
|
14
|
-
export interface InsertOptions {
|
|
15
|
-
/**
|
|
16
|
-
* Format strategy for serialization.
|
|
17
|
-
* Default: 'auto' (uses RowBinary when possible, falls back to JSON)
|
|
18
|
-
*/
|
|
19
|
-
format?: InsertFormat;
|
|
20
|
-
/** Rows per batch when streaming */
|
|
21
|
-
batchSize?: number;
|
|
6
|
+
export interface BinaryInsertConfig {
|
|
7
|
+
url: string;
|
|
8
|
+
username: string;
|
|
9
|
+
password: string;
|
|
10
|
+
database: string;
|
|
11
|
+
/** Skip validation for maximum performance */
|
|
12
|
+
skipValidation?: boolean;
|
|
22
13
|
}
|
|
23
|
-
export declare class ClickHouseInsertBuilder<TTable extends TableRuntime<any, any>, TReturn =
|
|
14
|
+
export declare class ClickHouseInsertBuilder<TTable extends TableRuntime<any, any>, TReturn = any> {
|
|
24
15
|
private client;
|
|
25
16
|
private table;
|
|
17
|
+
private connectionConfig?;
|
|
26
18
|
private _values;
|
|
27
19
|
private _async;
|
|
28
20
|
private _waitForAsync;
|
|
29
21
|
private _batchOptions;
|
|
30
22
|
private _format;
|
|
31
|
-
private _batchSize;
|
|
32
23
|
private _batchConfig;
|
|
33
24
|
private _forceJson;
|
|
34
25
|
private _returning;
|
|
35
|
-
|
|
36
|
-
|
|
26
|
+
private _isSingle;
|
|
27
|
+
private _skipValidation;
|
|
28
|
+
constructor(client: ClickHouseClient, table: TTable, connectionConfig?: BinaryInsertConfig | undefined);
|
|
29
|
+
/**
|
|
30
|
+
* Skip enum validation for maximum performance.
|
|
31
|
+
* Use in production when you trust your data source.
|
|
32
|
+
*/
|
|
33
|
+
skipValidation(): this;
|
|
34
|
+
values(value: CleanInsert<TTable> | Array<CleanInsert<TTable>> | Iterable<CleanInsert<TTable>> | AsyncIterable<CleanInsert<TTable>> | Readable): ClickHouseInsertBuilder<TTable, TReturn>;
|
|
37
35
|
/** @template [T = CleanInsert<TTable>] */
|
|
38
|
-
insert(data: CleanInsert<TTable> | CleanInsert<TTable>[]): Promise<
|
|
36
|
+
insert(data: CleanInsert<TTable> | CleanInsert<TTable>[]): Promise<TReturn>;
|
|
39
37
|
returning(): ClickHouseInsertBuilder<TTable, CleanSelect<TTable>[]>;
|
|
38
|
+
/**
|
|
39
|
+
* Disable the default returning() behavior.
|
|
40
|
+
* Useful when you don't need the inserted data back and want to avoid the overhead.
|
|
41
|
+
*/
|
|
42
|
+
noReturning(): ClickHouseInsertBuilder<TTable, void>;
|
|
40
43
|
/**
|
|
41
44
|
* Force synchronous insert (disables async_insert).
|
|
42
45
|
* Use when you need immediate durability guarantee.
|
|
@@ -131,6 +134,7 @@ export declare class ClickHouseInsertBuilder<TTable extends TableRuntime<any, an
|
|
|
131
134
|
private executeJsonInsert;
|
|
132
135
|
/**
|
|
133
136
|
* Execute insert using RowBinary format (fastest)
|
|
137
|
+
* Uses direct HTTP request since the official client doesn't support RowBinary yet
|
|
134
138
|
*/
|
|
135
139
|
private executeBinaryInsert;
|
|
136
140
|
/**
|
|
@@ -142,4 +146,3 @@ export declare class ClickHouseInsertBuilder<TTable extends TableRuntime<any, an
|
|
|
142
146
|
private resolveClientDefaultExpr;
|
|
143
147
|
then<TResult1 = TReturn, TResult2 = never>(onfulfilled?: ((value: TReturn) => TResult1 | PromiseLike<TResult1>) | null, onrejected?: ((reason: any) => TResult2 | PromiseLike<TResult2>) | null): Promise<TResult1 | TResult2>;
|
|
144
148
|
}
|
|
145
|
-
export {};
|
|
@@ -221,7 +221,9 @@ export declare class ClickHouseQueryBuilder<TTable extends TableDefinition<any>
|
|
|
221
221
|
}>;
|
|
222
222
|
readonly $inferSelect: { [K_2 in keyof (TSelection extends SelectionShape ? { [K in keyof (TSelection extends infer T_2 ? T_2 extends TSelection ? T_2 extends SelectionShape ? SelectResult<T_2> : Record<string, any> : never : never)]: ClickHouseColumn<(TSelection extends infer T_3 ? T_3 extends TSelection ? T_3 extends SelectionShape ? SelectResult<T_3> : Record<string, any> : never : never)[K], true, false>; } : Record<string, ClickHouseColumn<any, true, false>>)]: (TSelection extends SelectionShape ? { [K in keyof (TSelection extends infer T_3 ? T_3 extends TSelection ? T_3 extends SelectionShape ? SelectResult<T_3> : Record<string, any> : never : never)]: ClickHouseColumn<(TSelection extends infer T_4 ? T_4 extends TSelection ? T_4 extends SelectionShape ? SelectResult<T_4> : Record<string, any> : never : never)[K], true, false>; } : Record<string, ClickHouseColumn<any, true, false>>)[K_2] extends infer T_4 ? T_4 extends (TSelection extends SelectionShape ? { [K in keyof (TSelection extends infer T_5 ? T_5 extends TSelection ? T_5 extends SelectionShape ? SelectResult<T_5> : Record<string, any> : never : never)]: ClickHouseColumn<(TSelection extends infer T_6 ? T_6 extends TSelection ? T_6 extends SelectionShape ? SelectResult<T_6> : Record<string, any> : never : never)[K], true, false>; } : Record<string, ClickHouseColumn<any, true, false>>)[K_2] ? T_4 extends ClickHouseColumn<infer Type, infer IsNotNull extends boolean, any> ? IsNotNull extends true ? Type : Type | null : never : never : never; } extends infer T_1 ? { [K_1 in keyof T_1]: T_1[K_1]; } : never;
|
|
223
223
|
readonly $inferInsert: import("..").TableInsert<TSelection extends SelectionShape ? { [K in keyof (TSelection extends infer T_4 ? T_4 extends TSelection ? T_4 extends SelectionShape ? SelectResult<T_4> : Record<string, any> : never : never)]: ClickHouseColumn<(TSelection extends infer T_5 ? T_5 extends TSelection ? T_5 extends SelectionShape ? SelectResult<T_5> : Record<string, any> : never : never)[K], true, false>; } : Record<string, ClickHouseColumn<any, true, false>>> extends infer T_3 ? { [K_3 in keyof T_3]: T_3[K_3]; } : never;
|
|
224
|
-
|
|
224
|
+
readonly $type: { [K_5 in keyof (TSelection extends SelectionShape ? { [K in keyof (TSelection extends infer T_6 ? T_6 extends TSelection ? T_6 extends SelectionShape ? SelectResult<T_6> : Record<string, any> : never : never)]: ClickHouseColumn<(TSelection extends infer T_7 ? T_7 extends TSelection ? T_7 extends SelectionShape ? SelectResult<T_7> : Record<string, any> : never : never)[K], true, false>; } : Record<string, ClickHouseColumn<any, true, false>>)]: (TSelection extends SelectionShape ? { [K in keyof (TSelection extends infer T_7 ? T_7 extends TSelection ? T_7 extends SelectionShape ? SelectResult<T_7> : Record<string, any> : never : never)]: ClickHouseColumn<(TSelection extends infer T_8 ? T_8 extends TSelection ? T_8 extends SelectionShape ? SelectResult<T_8> : Record<string, any> : never : never)[K], true, false>; } : Record<string, ClickHouseColumn<any, true, false>>)[K_5] extends infer T_8 ? T_8 extends (TSelection extends SelectionShape ? { [K in keyof (TSelection extends infer T_9 ? T_9 extends TSelection ? T_9 extends SelectionShape ? SelectResult<T_9> : Record<string, any> : never : never)]: ClickHouseColumn<(TSelection extends infer T_10 ? T_10 extends TSelection ? T_10 extends SelectionShape ? SelectResult<T_10> : Record<string, any> : never : never)[K], true, false>; } : Record<string, ClickHouseColumn<any, true, false>>)[K_5] ? T_8 extends ClickHouseColumn<infer Type, infer IsNotNull extends boolean, any> ? IsNotNull extends true ? Type : Type | null : never : never : never; } extends infer T_5 ? { [K_4 in keyof T_5]: T_5[K_4]; } : never;
|
|
225
|
+
readonly $insert: import("..").TableInsert<TSelection extends SelectionShape ? { [K in keyof (TSelection extends infer T_8 ? T_8 extends TSelection ? T_8 extends SelectionShape ? SelectResult<T_8> : Record<string, any> : never : never)]: ClickHouseColumn<(TSelection extends infer T_9 ? T_9 extends TSelection ? T_9 extends SelectionShape ? SelectResult<T_9> : Record<string, any> : never : never)[K], true, false>; } : Record<string, ClickHouseColumn<any, true, false>>> extends infer T_7 ? { [K_3 in keyof T_7]: T_7[K_3]; } : never;
|
|
226
|
+
} & (TSelection extends SelectionShape ? { [K in keyof (TSelection extends infer T_9 ? T_9 extends TSelection ? T_9 extends SelectionShape ? SelectResult<T_9> : Record<string, any> : never : never)]: ClickHouseColumn<(TSelection extends infer T_10 ? T_10 extends TSelection ? T_10 extends SelectionShape ? SelectResult<T_10> : Record<string, any> : never : never)[K], true, false>; } : Record<string, ClickHouseColumn<any, true, false>>);
|
|
225
227
|
register: (mainQuery: ClickHouseQueryBuilder<any, any, any>) => ClickHouseQueryBuilder<any, any, any>;
|
|
226
228
|
};
|
|
227
229
|
final(): this;
|
package/dist/client.d.ts
CHANGED
|
@@ -6,9 +6,23 @@ import { ClickHouseUpdateBuilder } from './builders/update';
|
|
|
6
6
|
import { type TableDefinition, type TableRuntime, type CleanInsert } from './core';
|
|
7
7
|
import { type HousekitLogger } from './logger';
|
|
8
8
|
import { type RelationalAPI } from './relational';
|
|
9
|
+
interface ConnectionPoolConfig {
|
|
10
|
+
/** Maximum concurrent sockets (default: 100) */
|
|
11
|
+
maxSockets?: number;
|
|
12
|
+
/** Keep connections alive (default: true) */
|
|
13
|
+
keepAlive?: boolean;
|
|
14
|
+
/** Keep-alive initial delay in ms (default: 1000) */
|
|
15
|
+
keepAliveInitialDelay?: number;
|
|
16
|
+
/** Socket timeout in ms (default: 30000) */
|
|
17
|
+
timeout?: number;
|
|
18
|
+
}
|
|
9
19
|
export type HousekitClientConfig = ClickHouseClientConfigOptions & {
|
|
10
20
|
schema?: Record<string, TableDefinition<any>>;
|
|
11
21
|
logger?: HousekitLogger;
|
|
22
|
+
/** Connection pool configuration */
|
|
23
|
+
pool?: ConnectionPoolConfig;
|
|
24
|
+
/** Skip validation for maximum insert performance */
|
|
25
|
+
skipValidation?: boolean;
|
|
12
26
|
};
|
|
13
27
|
export type ClientConfigWithSchema = HousekitClientConfig;
|
|
14
28
|
export interface HousekitClient<TSchema extends Record<string, TableDefinition<any>> | undefined = any> {
|
|
@@ -45,3 +59,4 @@ export declare function createHousekitClient<TSchema extends Record<string, Tabl
|
|
|
45
59
|
schema?: TSchema;
|
|
46
60
|
}): HousekitClient<TSchema>;
|
|
47
61
|
export declare const createClientFromConfigObject: typeof createHousekitClient;
|
|
62
|
+
export {};
|
package/dist/column.d.ts
CHANGED
|
@@ -44,7 +44,7 @@ export declare class ClickHouseColumn<TType = any, TNotNull extends boolean = tr
|
|
|
44
44
|
* Define a default value for the column.
|
|
45
45
|
* Can be a static value or a SQL expression like 'now()'.
|
|
46
46
|
*/
|
|
47
|
-
default(value: any): ClickHouseColumn<TType, TNotNull,
|
|
47
|
+
default(value: any): ClickHouseColumn<TType, TNotNull, true>;
|
|
48
48
|
references(getColumn: () => ClickHouseColumn, options?: {
|
|
49
49
|
onDelete?: 'cascade' | 'set null' | 'restrict' | 'no action';
|
|
50
50
|
onUpdate?: 'cascade' | 'set null' | 'restrict' | 'no action';
|
|
@@ -71,6 +71,6 @@ export declare class ClickHouseColumn<TType = any, TNotNull extends boolean = tr
|
|
|
71
71
|
* Calculates the column value on the client side before insertion.
|
|
72
72
|
* Useful for UUIDs, hashes, or computations based on other fields.
|
|
73
73
|
*/
|
|
74
|
-
$defaultFn(fn: (row: Record<string, any>) => any): ClickHouseColumn<TType, TNotNull,
|
|
74
|
+
$defaultFn(fn: (row: Record<string, any>) => any): ClickHouseColumn<TType, TNotNull, true>;
|
|
75
75
|
toSQL(): string;
|
|
76
76
|
}
|