Readme update.

This commit is contained in:
go-jet 2019-06-25 12:56:10 +02:00
parent 9196d7a81e
commit 3dddd2f6bf

166
README.md
View file

@ -1,8 +1,9 @@
# Jet - Sql Builder for Postgresql
# Jet
[![CircleCI](https://circleci.com/gh/go-jet/jet/tree/develop.svg?style=svg&circle-token=97f255c6a4a3ab6590ea2e9195eb3ebf9f97b4a7)](https://circleci.com/gh/go-jet/jet/tree/develop)
Jet is sql builder for Postgresql(support for MySql and OracleSql will be added later). Jet enables writing typesafe SQL queries in Go, and ability to easily convert query result to desired arbitrary structure.
Jet is Go SQL Builder for PostgreSQL(support for MySql and OracleSql will be added later).
Jet enables writing type safe SQL queries in Go, and ability to easily convert database query result to desired arbitrary structure.
# Features
* TODO
@ -31,12 +32,12 @@ go install github.com/go-jet/jet/cmd/jetgen
Make sure GOPATH bin folder is added to the PATH environment variable.
## Quick Start
For this quick start example we will use sample dvd rental database. Full database dump can be found in [./tests/init/data/dvds.sql](./tests/init/data/dvds.sql)
For this quick start example we will use sample _dvd rental_ database. Full database dump can be found in [./tests/init/data/dvds.sql](./tests/init/data/dvds.sql).
Schema diagram of interest for example can be found [here](./examples/quick-start/diagram.png).
### Generate sql builder and model files
To generate sql builder and data model go files we need to call jetgen, and provide it with postgres connection parameters and destination folder for generated go files.
Sample command used in tests:
To generate Go sql builder and Go data model from postgres database we need to call jetgen, and provide it with postgres connection parameters and destination folder for generated go files.\
Assuming we are running local postgres database, with user `jet`, database `jetdb` and schema `dvds` we will use this command:
```sh
jetgen -host=localhost -port=5432 -user=jet -password=jet -dbname=jetdb -schema dvds -path ./gen
```
@ -52,11 +53,30 @@ Generating enum model files...
Done
```
As jetgen command output suggest, jetgen will:
-connect to `dvds` database to retrieve information about the structure of database
-delete everything in destination folder `./gen`,
-and finally generate sql builder and model *.go files for all database tables and enums in destination folder `./tests/gentestdata`.
- connect to postgres database and retrieve information about the tables and enums of `dvds` schema\
- delete everything in destination folder `./gen`,
- and finally generate sql builder and model Go files for all schema tables and enums in destination folder `./gen`.\
Generated files folder looks like this:
```sh
|-- gen # destination folder
| `-- jetdb # database name
| `-- dvds # schema name
| |-- enum # sql builder folder for enums
| | `-- mpaa_rating.go
| |-- table # sql builder folder for tables
| |-- actor.go
| |-- address.go
| |-- category.go
...
| |-- model # Plain Old Data for every enum and table
| | |-- actor.go
| | |-- address.go
| | |-- category.go
...
### Now lets write some SQL queries in go
```
You will be using types from `table` and `enum` to write type safe SQL in Go, and `model` types would be used to store results of the queries.
### Now lets write some SQL queries in Go
First lets import jet and generated files from previous step
```go
@ -71,38 +91,120 @@ Lets say we want to retrieve the list of all actors that acted in films longer t
and film category is not 'Action'.
```go
stmt := SELECT(
Actor.ActorID, Actor.FirstName, Actor.LastName, Actor.LastUpdate, // list of all actor columns (equivalent to Actor.AllColumns)
Film.AllColumns, // list of all film columns (equivalent to Film.FilmID, Film.Title, ...)
Language.AllColumns,
Category.AllColumns,
).FROM(
Actor.
INNER_JOIN(FilmActor, Actor.ActorID.EQ(FilmActor.ActorID)). // INNER JOIN Actor with FilmActor on condition Actor.ActorID = FilmActor.ActorID
INNER_JOIN(Film, Film.FilmID.EQ(FilmActor.FilmID)). // then with Film, Language, FilmCategory and Category.
INNER_JOIN(Language, Language.LanguageID.EQ(Film.LanguageID)).
INNER_JOIN(FilmCategory, FilmCategory.FilmID.EQ(Film.FilmID)).
INNER_JOIN(Category, Category.CategoryID.EQ(FilmCategory.CategoryID)),
).WHERE(
Language.Name.EQ(String("English")). // note every column has type.
AND(Category.Name.NOT_EQ(String("Action"))). // String column Language.Name and Category.Name can be compared only with string columns and expressions
AND(Film.Length.GT(Int(180))), // Film.Length is integer column and can be compared only with integer columns and expressions
).ORDER_BY(
Actor.ActorID.ASC(),
Film.FilmID.ASC(),
)
Actor.ActorID, Actor.FirstName, Actor.LastName, Actor.LastUpdate, // list of all actor columns (equivalent to Actor.AllColumns)
Film.AllColumns, // list of all film columns (equivalent to Film.FilmID, Film.Title, ...)
Language.AllColumns,
Category.AllColumns,
).FROM(
Actor.
INNER_JOIN(FilmActor, Actor.ActorID.EQ(FilmActor.ActorID)). // INNER JOIN Actor with FilmActor on condition Actor.ActorID = FilmActor.ActorID
INNER_JOIN(Film, Film.FilmID.EQ(FilmActor.FilmID)). // then with Film, Language, FilmCategory and Category.
INNER_JOIN(Language, Language.LanguageID.EQ(Film.LanguageID)).
INNER_JOIN(FilmCategory, FilmCategory.FilmID.EQ(Film.FilmID)).
INNER_JOIN(Category, Category.CategoryID.EQ(FilmCategory.CategoryID)),
).WHERE(
Language.Name.EQ(String("English")). // every column has type.
AND(Category.Name.NOT_EQ(String("Action"))). // String column Language.Name and Category.Name can be compared only with string columns and expressions
AND(Film.Length.GT(Int(180))), // Film.Length is integer column and can be compared only with integer columns and expressions
).ORDER_BY(
Actor.ActorID.ASC(),
Film.FilmID.ASC(),
)
```
To see sql formed with this statement:
```go
query, args, err := stmt.Sql()
```
query - is parametrized query
query - is parametrized query\
args - are parameters for the query
<details>
<summary>Click to see</summary>
```sh
Parameterized query:
SELECT actor.actor_id AS "actor.actor_id",
actor.first_name AS "actor.first_name",
actor.last_name AS "actor.last_name",
actor.last_update AS "actor.last_update",
film.film_id AS "film.film_id",
film.title AS "film.title",
film.description AS "film.description",
film.release_year AS "film.release_year",
film.language_id AS "film.language_id",
film.rental_duration AS "film.rental_duration",
film.rental_rate AS "film.rental_rate",
film.length AS "film.length",
film.replacement_cost AS "film.replacement_cost",
film.rating AS "film.rating",
film.last_update AS "film.last_update",
film.special_features AS "film.special_features",
film.fulltext AS "film.fulltext",
language.language_id AS "language.language_id",
language.name AS "language.name",
language.last_update AS "language.last_update",
category.category_id AS "category.category_id",
category.name AS "category.name",
category.last_update AS "category.last_update"
FROM dvds.actor
INNER JOIN dvds.film_actor ON (actor.actor_id = film_actor.actor_id)
INNER JOIN dvds.film ON (film.film_id = film_actor.film_id)
INNER JOIN dvds.language ON (language.language_id = film.language_id)
INNER JOIN dvds.film_category ON (film_category.film_id = film.film_id)
INNER JOIN dvds.category ON (category.category_id = film_category.category_id)
WHERE ((language.name = $1) AND (category.name != $2)) AND (film.length > $3)
ORDER BY actor.actor_id ASC, film.film_id ASC;
Arguments:
[English Action 180]
```
</details>
To see debug sql that can be copy pasted to sql editor and executed.
```go
query, err := stmt.DebugSql()
```
query - is parametrized query where every parameter is replaced with appropriate string argument representation
<details>
<summary>Click to see</summary>
```sh
Debug sql:
SELECT actor.actor_id AS "actor.actor_id",
actor.first_name AS "actor.first_name",
actor.last_name AS "actor.last_name",
actor.last_update AS "actor.last_update",
film.film_id AS "film.film_id",
film.title AS "film.title",
film.description AS "film.description",
film.release_year AS "film.release_year",
film.language_id AS "film.language_id",
film.rental_duration AS "film.rental_duration",
film.rental_rate AS "film.rental_rate",
film.length AS "film.length",
film.replacement_cost AS "film.replacement_cost",
film.rating AS "film.rating",
film.last_update AS "film.last_update",
film.special_features AS "film.special_features",
film.fulltext AS "film.fulltext",
language.language_id AS "language.language_id",
language.name AS "language.name",
language.last_update AS "language.last_update",
category.category_id AS "category.category_id",
category.name AS "category.name",
category.last_update AS "category.last_update"
FROM dvds.actor
INNER JOIN dvds.film_actor ON (actor.actor_id = film_actor.actor_id)
INNER JOIN dvds.film ON (film.film_id = film_actor.film_id)
INNER JOIN dvds.language ON (language.language_id = film.language_id)
INNER JOIN dvds.film_category ON (film_category.film_id = film.film_id)
INNER JOIN dvds.category ON (category.category_id = film_category.category_id)
WHERE ((language.name = 'English') AND (category.name != 'Action')) AND (film.length > 180)
ORDER BY actor.actor_id ASC, film.film_id ASC;
```
</details>
Well formed sql is just a first half the job. Now lets execute sql statement and store result in desired structure.
Let's say this is our desired structure:
@ -116,16 +218,16 @@ var dest []struct {
}
}
```
There is no limitation for how big or nested destination structure can be.
_There is no limitation for how big or nested destination structure can be._
Now to execute a statement on open database connection db.
Now to lets execute a above statement on open database connection db.
```go
err := stmt.Query(db, &dest)
handleError(err)
```
And thats it. `dest` now contains the list of all actors that acted in films longer than 180 minutes, film language is 'English'
And thats it. `dest` now contains the list of all actors(with list of films acted, where each film has information about language and list of belonging categories) that acted in films longer than 180 minutes, film language is 'English'
and film category is not 'Action'.
Lets print `dest` as a json to see: