README fixes

This commit is contained in:
Chris Allen 2016-12-14 18:58:13 -06:00
parent 4fd0b47320
commit 9cc9479a92
2 changed files with 29 additions and 69 deletions

View File

@ -1,26 +1,20 @@
# Esqueleto Esqueleto [![TravisCI](https://travis-ci.org/bitemyapp/esqueleto.svg)](https://travis-ci.org/bitemyapp/esqueleto)
==========
Esqueleto is a bare bones, type-safe EDSL for SQL queries that works with ![Skeleton](./esqueleto.png)
unmodified persistent SQL backends. The name of this library means "skeleton" <sup>Image courtesy [Chrissy Long](https://www.flickr.com/photos/chrissylong/313800029/)</sup>
in Portuguese and contains all three SQL letters in the correct order =). It
was inspired by Scala's Squeryl but created from scratch. Its language closely
resembles SQL. Currently, SELECTs, UPDATEs, INSERTs and DELETEs are supported.
In particular, esqueleto is the recommended library for type-safe JOINs on # Esqueleto, a SQL DSL for Haskell
persistent SQL backends. (The alternative is using raw SQL, but that's error
prone and does not offer any composability.). For more information read Esqueleto is a bare bones, type-safe EDSL for SQL queries that works with unmodified persistent SQL backends. The name of this library means "skeleton" in Portuguese and contains all three SQL letters in the correct order =). It was inspired by Scala's Squeryl but created from scratch. Its language closely resembles SQL. Currently, SELECTs, UPDATEs, INSERTs and DELETEs are supported.
[esqueleto](http://hackage.haskell.org/package/esqueleto).
In particular, esqueleto is the recommended library for type-safe JOINs on persistent SQL backends. (The alternative is using raw SQL, but that's error prone and does not offer any composability.). For more information read [esqueleto](http://hackage.haskell.org/package/esqueleto).
## Setup ## Setup
If you're already using `persistent`, then you're ready to use If you're already using `persistent`, then you're ready to use `esqueleto`, no further setup is needed. If you're just starting a new project and would like to use `esqueleto`, take a look at `persistent`'s [book](http://www.yesodweb.com/book/persistent) first to learn how to define your schema.
`esqueleto`, no further setup is needed. If you're just
starting a new project and would like to use `esqueleto`, take
a look at `persistent`'s [book](http://www.yesodweb.com/book/persistent) first
to learn how to define your schema.
If you need to use `persistent`'s default support for queries If you need to use `persistent`'s default support for queries as well, either import it qualified:
as well, either import it qualified:
```haskell ```haskell
-- For a module that mostly uses esqueleto. -- For a module that mostly uses esqueleto.
@ -36,8 +30,7 @@ import Database.Persistent
import qualified Database.Esqueleto as E import qualified Database.Esqueleto as E
``` ```
Other than identifier name clashes, `esqueleto` does not Other than identifier name clashes, `esqueleto` does not conflict with `persistent` in any way.
conflict with `persistent` in any way.
## Goals ## Goals
@ -48,8 +41,7 @@ The main goals of `esqueleto` are:
- Support the most widely used SQL features. - Support the most widely used SQL features.
- Be as type-safe as possible. - Be as type-safe as possible.
It is _not_ a goal to be able to write portable SQL. It is _not_ a goal to be able to write portable SQL. We do not try to hide the differences between DBMSs from you
We do not try to hide the differences between DBMSs from you
## Introduction ## Introduction
@ -75,10 +67,7 @@ share [mkPersist sqlSettings, mkMigrate "migrateAll"] [persist|
## Select ## Select
Most of `esqueleto` was created with `SELECT` statements in Most of `esqueleto` was created with `SELECT` statements in mind, not only because they're the most common but also because they're the most complex kind of statement. The most simple kind of `SELECT` would be:
mind, not only because they're the most common but also
because they're the most complex kind of statement. The most
simple kind of `SELECT` would be:
```haskell ```haskell
putPersons :: SqlPersist m () putPersons :: SqlPersist m ()
@ -96,8 +85,7 @@ SELECT *
FROM Person FROM Person
``` ```
`esqueleto` knows that we want an `Entity Person` just because of the `personName` that is `esqueleto` knows that we want an `Entity Person` just because of the `personName` that is printed.
printed.
## Where ## Where
@ -118,10 +106,7 @@ FROM Person
WHERE Person.name = "John" WHERE Person.name = "John"
``` ```
The `(^.)` operator is used to project a field from an entity. The `(^.)` operator is used to project a field from an entity. The field name is the same one generated by `persistent`s Template Haskell functions. We use `val` to lift a constant Haskell value into the SQL query.
The field name is the same one generated by `persistent`s
Template Haskell functions. We use `val` to lift a constant
Haskell value into the SQL query.
Another example: Another example:
@ -142,16 +127,13 @@ FROM Person
WHERE Person.age >= 18 WHERE Person.age >= 18
``` ```
Since `age` is an optional `Person` field, we use `just` to lift Since `age` is an optional `Person` field, we use `just` to lift`val 18 :: SqlExpr (Value Int)` into `just (val 18) ::SqlExpr (Value (Maybe Int))`.
`val 18 :: SqlExpr (Value Int)` into `just (val 18) ::
SqlExpr (Value (Maybe Int))`.
## Joins ## Joins
Implicit joins are represented by tuples. Implicit joins are represented by tuples.
For example, to get the list of all blog posts and their authors, we could For example, to get the list of all blog posts and their authors, we could write:
write:
```haskell ```haskell
select $ select $
@ -171,8 +153,7 @@ ORDER BY BlogPost.title ASC
``` ```
However, you may want your results to include people who don't However, you may want your results to include people who don't have any blog posts as well using a `LEFT OUTER JOIN`:
have any blog posts as well using a `LEFT OUTER JOIN`:
```haskell ```haskell
select $ select $
@ -193,17 +174,9 @@ ORDER BY Person.name ASC, BlogPost.title ASC
## Left Outer Join ## Left Outer Join
On a `LEFT OUTER JOIN` the entity on the right hand side may On a `LEFT OUTER JOIN` the entity on the right hand side may not exist (i.e. there may be a `Person` without any `BlogPost`s), so while `p :: SqlExpr (Entity Person)`, we have `mb :: SqlExpr (Maybe (Entity BlogPost))`. The whole expression above has type `SqlPersist m [(Entity Person, Maybe (Entity BlogPost))]`. Instead of using `(^.)`, we used `(?.)` to project a field from a `Maybe (Entity a)`.
not exist (i.e. there may be a `Person` without any
`BlogPost`s), so while `p :: SqlExpr (Entity Person)`, we have
`mb :: SqlExpr (Maybe (Entity BlogPost))`. The whole
expression above has type `SqlPersist m [(Entity Person, Maybe
(Entity BlogPost))]`. Instead of using `(^.)`, we used
`(?.)` to project a field from a `Maybe (Entity a)`.
We are by no means limited to joins of two tables, nor by We are by no means limited to joins of two tables, nor by joins of different tables. For example, we may want a list of the `Follow` entity:
joins of different tables. For example, we may want a list
of the `Follow` entity:
```haskell ```haskell
select $ select $
@ -222,10 +195,7 @@ INNER JOIN Follow ON P1.id = Follow.follower
INNER JOIN P2 ON P2.id = Follow.followed INNER JOIN P2 ON P2.id = Follow.followed
``` ```
Note carefully that the order of the ON clauses is Note carefully that the order of the ON clauses is reversed! You're required to write your `on`s in reverse order because that helps composability (see the documentation of `on` for more details).
reversed! You're required to write your `on`s in reverse
order because that helps composability (see the documentation
of `on` for more details).
## Update and Delete ## Update and Delete
@ -238,9 +208,7 @@ do update $ \p -> do
where_ (p ^. PersonAge <. just (val 14)) where_ (p ^. PersonAge <. just (val 14))
``` ```
The results of queries can also be used for insertions. The results of queries can also be used for insertions. In `SQL`, we might write the following, inserting a new blog post for every user:
In `SQL`, we might write the following, inserting a new blog
post for every user:
```haskell ```haskell
insertSelect $ from $ \p-> insertSelect $ from $ \p->
@ -255,28 +223,20 @@ SELECT ('Group Blog Post', id)
FROM Person FROM Person
``` ```
Individual insertions can be performed through Persistent's Individual insertions can be performed through Persistent's `insert` function, reexported for convenience.
`insert` function, reexported for convenience.
### Re-exports ### Re-exports
We re-export many symbols from `persistent` for convenience: We re-export many symbols from `persistent` for convenience:
- "Store functions" from "Database.Persist". - "Store functions" from "Database.Persist".
- Everything from "Database.Persist.Class" except for - Everything from "Database.Persist.Class" except for `PersistQuery` and `delete` (use `deleteKey` instead).
`PersistQuery` and `delete` (use `deleteKey` instead). - Everything from "Database.Persist.Types" except for `Update`, `SelectOpt`, `BackendSpecificFilter` and `Filter`.
- Everything from "Database.Persist.Types" except for - Everything from "Database.Persist.Sql" except for `deleteWhereCount` and `updateWhereCount`.
`Update`, `SelectOpt`, `BackendSpecificFilter` and `Filter`.
- Everything from "Database.Persist.Sql" except for
`deleteWhereCount` and `updateWhereCount`.
### RDBMS Specific ### RDBMS Specific
There are many differences between SQL syntax and functions There are many differences between SQL syntax and functions supported by different RDBMSs. Since version 2.2.8, `esqueleto` includes modules containing functions that are specific to a given RDBMS.
supported by different RDBMSs. Since version 2.2.8,
`esqueleto` includes modules containing functions that are
specific to a given RDBMS.
- PostgreSQL: `Database.Esqueleto.PostgreSQL` - PostgreSQL: `Database.Esqueleto.PostgreSQL`
In order to use these functions, you need to explicitly import In order to use these functions, you need to explicitly import their corresponding modules.
their corresponding modules

BIN
esqueleto.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 283 KiB