Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/noppoman/swiftknex
A Mysql Native Client and Query Builder written in Pure Swift
https://github.com/noppoman/swiftknex
expressive json-data mysql mysql-protocol pure-swift query-builder schema-migrations server-side-swift
Last synced: 16 days ago
JSON representation
A Mysql Native Client and Query Builder written in Pure Swift
- Host: GitHub
- URL: https://github.com/noppoman/swiftknex
- Owner: noppoMan
- License: mit
- Created: 2017-01-09T14:12:51.000Z (almost 8 years ago)
- Default Branch: master
- Last Pushed: 2019-09-10T08:32:31.000Z (about 5 years ago)
- Last Synced: 2024-04-14T06:51:43.912Z (7 months ago)
- Topics: expressive, json-data, mysql, mysql-protocol, pure-swift, query-builder, schema-migrations, server-side-swift
- Language: Swift
- Homepage:
- Size: 108 KB
- Stars: 38
- Watchers: 4
- Forks: 3
- Open Issues: 11
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README
# SwiftKnex
A Mysql Native Client and Query Builder that works on Mac and Linux.
This library is powerded by [Prorsum](https://github.com/noppoman/Prorsum)**SwiftKnex is aimed to use at production services at my company**
[](https://travis-ci.org/noppoMan/SwiftKnex)
## Features
* [x] Pure Swift Implementation(doesn't depend on libmysqlclient)
* [x] Expressive querying
* [x] Supporting Database Migration and Rollback
* [x] Supported Mysql 5.7 JSON Data Type
* [ ] Async I/O## TODO
* BlobType
* GeoType
* Investigate Performance
* Async I/O Mode
* Documentation## Contributing
All developers should feel welcome and encouraged to contribute to SwiftKnex.To contribute a feature or idea to SwiftKnex please submit an issue! If you find bugs, of course you can create the PR(s) directory.
# Query Builder Reference
## Initializing the Library
```swift
let config = KnexConfig(
host: "localhost",
user: "user",
database: "my_database",
isShowSQLLog: true
)let con = try KnexConnection(config: config)
let knex = con.knex()
```# Query Builder
## Select
### where equal
```swift
let results = try knex.table("users").where("id" == 1).fetch()
print(results)
```### where between and limit and offset
You can chain the conditional clauses like SQL and it's amazing expressive.
```swift
let results = try knex
.table("users")
.where(between("id", 1, 1000))
.limit(10)
.offset(10)
.fetch()print(result)
```### join
```swift
let results = try knex
.table("users")
.join("payments")
.on("users.id" == "payment.user_id")
.where("users.id" == 1)
.limit(10)
.offset(10)
.fetch()print(results)
```### count
```swift
let results = try knex
.select(count("id").as("count"))
.table("users")
.fetch()print(results)
```### where based on priority
```swift
let results = try knex
.table("users")
.where(("country" == "Japan" && "age" > 20) || "country" == "USA")print(results)
```### Sub Query
#### fetch all rows from the results that taken by subquery.
```swift
let results = try knex
.table(
Table(
QueryBuilder()
.table("users")
.where("country" == "USA")
)
).as("t1")
)
.where("t1.id" == 1)print(results)
```#### Use ids taken by subquery as argument of in clause
```swift
let results = try knex
.table("users")
.where(SwiftKnex.in("id",
QueryBuilder()
.select(col("id"))
.table("t1")
.where("country" == "USA")
)).fetch()print(results)
```## Operators, Conditions and Clauses
### Operators
SwiftKnex can evaluate comparison formula as function.
If you input `"id" == 1` in `where` clause, it is evaluated as `function`(Not BOOL) and tnrasform to SQL Comparison Literal.| SwiftKnex | Mysql |
| ------------------- |:------------------:|
| where("id" == 1) | where `id` = 1 |
| where("id" > 1) | where `id` > 1 |
| where("id" >= 1) | where `id` >= 1 |
| where("id" < 1) | where `id` < 1 |
| where("id" <= 1) | where `id` <= 1 |
| where("id" != 1) | where `id` != 1 |### Conditions
SwiftKnex provides conditions as function. Here are list of conditions available now.
* `where(_ filter: ConditionFilter)`
- like: `knex().where(like("name", "%a%"))`
- in: `knex().where(in("id", [1, 2, 3]))`
- notIn: `knex().where(notIn("id", [1, 2, 3]))`
- between: `knex().where(between("date", "2017-01-01", "2017-01-31"))`
- notBetween: `knex().where(notBetween("date", "2017-01-01", "2017-01-31"))`
- isNull: `knex().where(isNull("deleted_at"))`
- isNotNull: `knex().where(isNotNull("deleted_at"))`
- raw: `knex().where(raw("id = ?", [1]))`
* `or(_ filter: ConditionFilter)`
- `knex().where(in("id", [1, 2, 3])).or(in("id", [4, 5, 6]))`
### ClausesOf cource it supports other clauses. You can use them with `knex()`'s method chain.
* join(_ table: String): `knex().table("a").join("b")`
* leftJoin(_ table: String): `knex().table("a").leftJoin("b")`
* rightJoin(_ table: String): `knex().table("a").rightJoin("b")`
* innerJoin(_ with table: String): `knex().table("a").innerJoin("b")`
* on(_ filter: ConditionFilter): `knex().table("a").join("b").on("a.id" == "b.a_id")`
* limit(_ limit: Int): `knex().limit(10)`
* offset(_ offset: Int): `knex().limit(10).offset(100)`
* order(by: String, sort: OrderSort = .asc): `knex().order(by: "created_at", .desc)`
* group(by name: String): `knex().group(by: "company")`
* having(_ cond: ConditionFilter): `knex().group(by: "company").having(in("name", ["Google", "Apple"]))`## TypeSafe Querying with Entity protocol
Define Your Entity with confirming `Entity` protocol and fetch rows as your specified type.
```swift
struct User: Entity, Serializable {
let id: Int
let name: String
let email: String
let age: Int
let country: String?init(row: Row) throws {
self.id = row["id"] as! Int
self.name = row["name"] as! String
self.email = row["email"] as! String
self.age = row["age"] as! Int
self.country = row["country"] as? String
}func serialize() throws -> [String: Any] {
return [
"name": name,
"email": email,
"age": age,
"country": country
]
}
}// fetch rows as User(s)
let users: [User] = try! con.knex()
.table("users")
.where("country" == "Japan")
.fetch()print(users.first)
// Insert User(should confirm Serializable)
let result = try! con.knex().insert(into: "users", values: user)print(result?.insertId)
```## Insert
```swift
let result = try knex().insert(into: "users", values: ["id": 1, "name": "bar"])print(result.affectedRows)
```## Update
```swift
let result = try knex().table("users").where("id" == 1).update(sets: ["name": "foobar"])print(result.affectedRows)
```## Delete
```swift
let result = try knex().table("users").where("id" == 1).delete()print(result.affectedRows)
```## Transaction
```swift
do {
try con.knex().transaction { trx in // BEGIN TRANSCTION
try con.knex().table("users").where("id" == 1).update(sets: ["name": "foo"], trx: trx)
try con.knex().table("users").where("id" == 2).update(sets: ["name": "bar"], trx: trx)
try con.knex().table("users").where("id" == 3).update(sets: ["name": "foobar"], trx: trx)
}
// COMMIT
} catch {
// ROLLBACK
}
```# DDL
# Create
```swift
let create = Create(table: "users", fields: [
Schema.integer("id").asPrimaryKey().asAutoIncrement(),
Schema.string("name"),
Schema.string("email").asUnique(),
Schema.datetime("last_logined_at").asIndex()
])
.hasTimeStamps() // add created_at and updated_attry knex().execRaw(sql: create.toDDL())
```## Schema.Field Reference
### Schema Types Comparison
| SwiftKnex | Mysql Type |
| ------------------- |:--------------:|
| Schema.string | VARCHAR |
| Schema.integer | INT |
| Schema.bigInteger | BIGIMT |
| Schema.dateTime | DATETIME |
| Schema.text | TEXT |
| Schema.mediumText | MEDIUMTEXT |
| Schema.float | FLOAT |
| Schema.double | DOUBLE |
| Schema.boolean | TINYINT(1) |
| Schema.json | JSON |### Functions for adding field attributes
* `default(as value: Any)`
* `after(for name: String)`
* `asPrimaryKey()`
* `asAutoIncrement()`
* `asNotNullable()`
* `asUnsigned()`
* `charset(_ char: Charset)`
* `asUnique()`
* `asIndex()`# Drop
```swift
let drop = Drop(table: "users")
try knex().execRaw(sql: drop.toDDL())
```# Raw
You can perform raw sql with SwiftKnex```swift
try knex().execRaw(sql: "SELECT * from users where id = ?", params: [1])
```# Migration
SwiftKnex supports database migration and rollback features.## Flows
### 1. Install SwiftKnex into your project.
**Package.swift**
```swift
import PackageDescriptionlet package = Package(
name: "MyApp",
dependencies: [
.Package(url: "https://github.com/noppoMan/SwiftKnex.git", majorVersion: 0, minor: 1)
]
)
```
**run swift build**
```
$ swift build
```
and then, `SwiftKnexMigration` executable binary was created in the .build/debug directory.### 2. Create Migration file
the next step is creating migration class file into your `Sources/Migration` directory with `./build/debug/Migration create {ResourceName}`
here is an example for creating `CreateUser` migration file
```
.build/debug/SwiftKnexMigration create CreateUser#
# Created /YourProject/Sources/Migration/20170116015823_CreateUser.swift
#
```### 3. Edit Your Migration File
After create migration class file, edit it like following.
The created migration class has following methods.
#### up
Performed on migrate:latest#### down
Performed on migrate:rollback```swift
import SwiftKnex
import Foundationclass Migration_20170116015823_CreateUser: Migratable {
var name: String {
return "\(Mirror(reflecting: self).subjectType)"
}func up(_ migrator: Migrator) throws {
let create = Create(
table: "users",
fields: [
Schema.integer("id").asPrimaryKey().asAutoIncrement(),
Schema.string("name").asIndex().asNotNullable(),
Schema.string("email").asUnique().asNotNullable()
])
.hasTimestamps()
.index(columns: ["name", "email"], unique: true)try migrator.run(create)
}func down(_ migrator: Migrator) throws {
try migrator.run(Drop(table: "users"))
}
}
```### 4. Create `main.swift` in the `{$PROJ}/Sources/Migration`
Create `main.swift` in the `{$PROJ}/Sources/Migration` directory that is created by `Migrate create` at previous section.
And copy/paste the following code into your `{$PROJ}/Sources/Migration/main.swift` and then, replace the class names in the `knexMigrations` array to correct names, and change the database configuration depending on your environment.You need to add the new class name(s) to the `knexMigrations` at every migration resource created.
**main.swif example**
```swift
import SwiftKnexlet knexMigrations: [Migratable] = [
Migration_20170116015823_CreateUser()
]let config = KnexConfig(
host: "localhost",
user: "root",
database: "swift_knex_test"
)try Migrator.run(config: config, arguments: CommandLine.arguments, knexMigrations: knexMigrations)
```after editing main.swift, run `swift build`
```
swift build
```### 5. Perform Migration and Rollback
After that, you only need to run the migration**Current supporting commands are**
* `migrate:latest`: Perform to migrate recent unmigrated files.
* `migrate:rollback`: Rollback the migrations recent performed.(The rollback unit is grouped by `batch` number)#### Try to perform Migration
```
.build/debug/Migration migrate:latest
```#### Try to perform Rollback
```
.build/debug/Migration migrate:rollback
```#### Seed
TODO# Working with Prorsum
Go Style async query performing and syncronization with Prorsum```swift
import Prorsumlet chan = Channel.make()
go {
let rows = try! knex().table("users").where("id" == 1).fetch()
try chan.send(rows!)
}go {
let rows = try! knex().table("users").where("id" == 2).fetch()
try chan.send(rows!)
}print(try! chan.receive())
print(try! chan.receive())
```# Mysql Library
The base Connection and Querying library that used in SwiftKnex.TODO
## License
Prorsum is released under the MIT license. See LICENSE for details.