A simple column-oriented ORM for the ClickHouse database in Swift.
Features:
- Fixed datatype ORM
- Connection pool
- Fully asynchronous based on the adapter ClickHouseNio
- Integrated with Vapor 4
- Add
ClickHouseVaporas a dependency to yourPackage.swift
dependencies:[.package(url:"https://github.com/patrick-zippenfenig/ClickHouseVapor.git", from:"1.0.0")],targets:[.target(name:"MyApp", dependencies:["ClickHouseVapor"])]- Build your project:
swift buildConfigure the connection credentials with a Vapor 4 application. Usually this is done in
config.swift.Note:
maxConnectionsPerEventLoopcontrols the number of connections per thread. If you have 4 CPU cores and Vapor is using 4 eventLoops, 8 connections will be used.requestTimeoutis the timeout to establish a connection. It does not limit query runtime.import ClickHouseVapor letapp=Application(.testing)defer{ app.shutdown()} app.clickHouse.configuration =tryClickHousePoolConfiguration( hostname:"localhost", port:9000, user:"default", password:"admin", database:"default", maxConnectionsPerEventLoop:2, requestTimeout:.seconds(10))
Define a table with fields and an engine.
publicclassTestModel:ClickHouseModel{@Field(key:"timestamp", isPrimary:true, isOrderBy:true)vartimestamp:[Int64]@Field(key:"stationID", isPrimary:true, isOrderBy:true)varid:[String]@Field(key:"fixed", fixedStringLen:10)varfixed:[String]@Field(key:"temperature")vartemperature:[Float]requiredpublicinit(){}publicstaticvarengine:ClickHouseEngine{returnClickHouseEngineReplacingMergeTree( table:"test", database:nil, cluster:nil, partitionBy:"toYYYYMM(toDateTime(timestamp))")}}
Create a table. For simplicity this example is calling
wait(). It is discouraged to usewait()in production.tryTestModel.createTable(on: app.clickHouse).wait()
Insert data
letmodel=TestModel() model.id =["x010","ax51","cd22"] model.fixed =["","123456","12345678901234"] model.timestamp =[100,200,300] model.temperature =[11.1,10.4,8.9]try model.insert(on: app.clickHouse).wait()
Query all data again
letresult=tryTestModel.select(on: app.clickHouse).wait()print(result.temperature) // [ 11.1, 10.4, 8.9 ] // Filter data in more detail letresult2=try!TestModel.select( on: app.clickHouse, fields:["timestamp","stationID"], where:"temperature > 10", order:"timestamp DESC", limit:10, offset:0).wait()print(result2.id) // ["ax51", "x010"] print(result2.timestamp) // [200, 100] // Perform raw queries, but assign the result to TestModel letsql="SELECT timestamp, stationID FROM default.test"letresult2=try!TestModel.select(on: app.clickHouse, sql: sql).wait()
If you have several models that follow a common base scheme, you can also use inheritance to keep your code tidy:
openclassTestParentClass{@Field(key:"timestamp", isPrimary:true, isOrderBy:true)vartimestamp:[Int64]@Field(key:"stationID", isPrimary:true, isOrderBy:true, isLowCardinality:true)varid:[String]}publicfinalclassInheritedTestModel:TestParentClass,ClickHouseModel{@Field(key:"temperature")vartemperature:[Float]overridepublicinit(){}publicstaticvarengine:ClickHouseEngine{returnClickHouseEngineReplacingMergeTree( table:"testInherited", database:nil, cluster:nil, partitionBy:"toYYYYMM(toDateTime(timestamp))")}}
- Query timeouts
- Implement more engines
Pull requests are welcome. For major changes, please open an issue first to discuss what you would like to change.
Please make sure to update tests as appropriate.