Safe Haskell | None |
---|---|
Language | Haskell2010 |
Synopsis
- data SqlType
- data Entity record = Entity {}
- insert :: forall record (m :: Type -> Type). (PersistStoreWrite backend, MonadIO m, PersistRecordBackend record backend) => record -> ReaderT backend m (Key record)
- insertMany_ :: forall record (m :: Type -> Type). (PersistStoreWrite backend, MonadIO m, PersistRecordBackend record backend) => [record] -> ReaderT backend m ()
- exists :: forall (m :: Type -> Type) record. (PersistQueryRead backend, MonadIO m, PersistRecordBackend record backend) => [Filter record] -> ReaderT backend m Bool
- selectList :: forall record backend (m :: Type -> Type). (MonadIO m, PersistQueryRead backend, PersistRecordBackend record backend) => [Filter record] -> [SelectOpt record] -> ReaderT backend m [Entity record]
- class PersistField a where
- toPersistValue :: a -> PersistValue
- fromPersistValue :: PersistValue -> Either Text a
- keyToOid :: ToBackendKey MongoContext record => Key record -> ObjectId
- oidToKey :: ToBackendKey MongoContext record => ObjectId -> Key record
- type SqlPersistT = ReaderT SqlBackend
- class PersistField a => PersistFieldSql a
Persist re-exports
These are our re-exports from Persist. They include: Entity(..), SqlType(..), exists, insert, insertMany_, and selectList
A SQL data type. Naming attempts to reflect the underlying Haskell datatypes, eg SqlString instead of SqlVarchar. Different SQL databases may have different translations for these types.
SqlString | |
SqlInt32 | |
SqlInt64 | |
SqlReal | |
SqlNumeric Word32 Word32 | |
SqlBool | |
SqlDay | |
SqlTime | |
SqlDayTime | Always uses UTC timezone |
SqlBlob | |
SqlOther Text | a backend-specific name |
Datatype that represents an entity, with both its Key
and
its Haskell record representation.
When using a SQL-based backend (such as SQLite or
PostgreSQL), an Entity
may take any number of columns
depending on how many fields it has. In order to reconstruct
your entity on the Haskell side, persistent
needs all of
your entity columns and in the right order. Note that you
don't need to worry about this when using persistent
's API
since everything is handled correctly behind the scenes.
However, if you want to issue a raw SQL command that returns
an Entity
, then you have to be careful with the column
order. While you could use SELECT Entity.* WHERE ...
and
that would work most of the time, there are times when the
order of the columns on your database is different from the
order that persistent
expects (for example, if you add a new
field in the middle of you entity definition and then use the
migration code -- persistent
will expect the column to be in
the middle, but your DBMS will put it as the last column).
So, instead of using a query like the one above, you may use
rawSql
(from the
Database.Persist.GenericSql module) with its /entity
selection placeholder/ (a double question mark ??
). Using
rawSql
the query above must be written as SELECT ?? WHERE
..
. Then rawSql
will replace ??
with the list of all
columns that we need from your entity in the right order. If
your query returns two entities (i.e. (Entity backend a,
Entity backend b)
), then you must you use SELECT ??, ??
WHERE ...
, and so on.
Instances
insert :: forall record (m :: Type -> Type). (PersistStoreWrite backend, MonadIO m, PersistRecordBackend record backend) => record -> ReaderT backend m (Key record) #
Create a new record in the database, returning an automatically created key (in SQL an auto-increment id).
Example usage
Using schema-1 and dataset-1, let's insert a new user John
.
insertJohn :: MonadIO m => ReaderT SqlBackend m (Key User) insertJohn = insert $ User "John" 30
johnId <- insertJohn
The above query when applied on dataset-1, will produce this:
+-----+------+-----+ |id |name |age | +-----+------+-----+ |1 |SPJ |40 | +-----+------+-----+ |2 |Simon |41 | +-----+------+-----+ |3 |John |30 | +-----+------+-----+
insertMany_ :: forall record (m :: Type -> Type). (PersistStoreWrite backend, MonadIO m, PersistRecordBackend record backend) => [record] -> ReaderT backend m () #
Same as insertMany
, but doesn't return any Key
s.
The MongoDB, PostgreSQL, SQLite and MySQL backends insert all records in one database query.
Example usage
insertUsers_ :: MonadIO m => ReaderT SqlBackend m () insertUsers_ = insertMany_ [User "John" 30, User "Nick" 32, User "Jane" 20]
The above query when applied on dataset-1, will produce this:
+-----+------+-----+ |id |name |age | +-----+------+-----+ |1 |SPJ |40 | +-----+------+-----+ |2 |Simon |41 | +-----+------+-----+ |3 |John |30 | +-----+------+-----+ |4 |Nick |32 | +-----+------+-----+ |5 |Jane |20 | +-----+------+-----+
exists :: forall (m :: Type -> Type) record. (PersistQueryRead backend, MonadIO m, PersistRecordBackend record backend) => [Filter record] -> ReaderT backend m Bool #
Check if there is at least one record fulfilling the given criterion.
Since: persistent-2.11
selectList :: forall record backend (m :: Type -> Type). (MonadIO m, PersistQueryRead backend, PersistRecordBackend record backend) => [Filter record] -> [SelectOpt record] -> ReaderT backend m [Entity record] #
Returns a [
corresponding to the filters and options
provided.Entity
record]
Filters are constructed using the operators defined in Database.Persist (and re-exported from Database.Persist.Sql). Let's look at some examples:
usersWithAgeOver40 ::SqlPersistT
IO
[Entity
User] usersWithAgeOver40 =selectList
[UserAge>=.
40] []
If you provide multiple values in the list, the conditions are AND
ed
together.
usersWithAgeBetween30And50 ::SqlPersistT
IO
[Entity
User] usersWithAgeBetween30And50 =selectList
[ UserAge>=.
30 , UserAge<=.
50 ] []
The second list contains the SelectOpt
for a record. We can select the
first ten records with LimitTo
firstTenUsers =selectList
[] [LimitTo
10]
And we can select the second ten users with OffsetBy
.
secondTenUsers =selectList
[] [LimitTo
10,OffsetBy
10]
Warning that LIMIT/OFFSET is bad for pagination!
With Asc
and Desc
, we can provide the field we want to sort on. We can
provide multiple sort orders - later ones are used to sort records that are
equal on the first field.
newestUsers = selectList [] [Desc
UserCreatedAt,LimitTo
10] oldestUsers = selectList [] [Asc
UserCreatedAt,LimitTo
10]
Persist Field re-exports
This is our re-export of the PersistField class.
class PersistField a where #
This class teaches Persistent how to take a custom type and marshal it to and from a PersistValue
, allowing it to be stored in a database.
Examples
Simple Newtype
You can use newtype
to add more type safety/readability to a basis type like ByteString
. In these cases, just derive PersistField
and PersistFieldSql
:
{-# LANGUAGE GeneralizedNewtypeDeriving #-} newtype HashedPassword = HashedPasswordByteString
deriving (Eq, Show,PersistField
, PersistFieldSql)
Smart Constructor Newtype
In this example, we create a PersistField
instance for a newtype following the "Smart Constructor" pattern.
{-# LANGUAGE GeneralizedNewtypeDeriving #-} import qualified Data.Text as T import qualified Data.Char as C -- | An American Social Security Number newtype SSN = SSNText
deriving (Eq, Show, PersistFieldSql) mkSSN ::Text
->Either
Text
SSN mkSSN t = if (T.length t == 9) && (T.all C.isDigit t) thenRight
$ SSN t elseLeft
$ "Invalid SSN: " <> t instancePersistField
SSN wheretoPersistValue
(SSN t) =PersistText
tfromPersistValue
(PersistText
t) = mkSSN t -- Handle cases where the database does not give us PersistTextfromPersistValue
x =Left
$ "File.hs: When trying to deserialize an SSN: expected PersistText, received: " <> T.pack (show x)
Tips:
- This file contain dozens of
PersistField
instances you can look at for examples. - Typically custom
PersistField
instances will only accept a singlePersistValue
constructor infromPersistValue
. - Internal
PersistField
instances accept a wide variety ofPersistValue
s to accomodate e.g. storing booleans as integers, booleans or strings. - If you're making a custom instance and using a SQL database, you'll also need
PersistFieldSql
to specify the type of the database column.
toPersistValue :: a -> PersistValue #
fromPersistValue :: PersistValue -> Either Text a #
Instances
Persist MongoDB re-exports
These are our re-exports from Persist.MongoDB. They include: keyToOid and oidToKey
keyToOid :: ToBackendKey MongoContext record => Key record -> ObjectId #
oidToKey :: ToBackendKey MongoContext record => ObjectId -> Key record #
Persist Sql re-exports
These are our re-exports from Persist.Sql. They include: SqlPersistT and PersistFieldSql
type SqlPersistT = ReaderT SqlBackend #
class PersistField a => PersistFieldSql a #
Tells Persistent what database column type should be used to store a Haskell type.
Examples
Simple Boolean Alternative
data Switch = On | Off deriving (Show, Eq) instancePersistField
Switch wheretoPersistValue
s = case s of On ->PersistBool
True Off ->PersistBool
FalsefromPersistValue
(PersistBool
b) = if b thenRight
On elseRight
OfffromPersistValue
x = Left $ "File.hs: When trying to deserialize a Switch: expected PersistBool, received: " <> T.pack (show x) instancePersistFieldSql
Switch wheresqlType
_ =SqlBool
Non-Standard Database Types
If your database supports non-standard types, such as Postgres' uuid
, you can use SqlOther
to use them:
import qualified Data.UUID as UUID instancePersistField
UUID wheretoPersistValue
=PersistLiteralEncoded
. toASCIIBytesfromPersistValue
(PersistLiteralEncoded
uuid) = case fromASCIIBytes uuid ofNothing
->Left
$ "Model/CustomTypes.hs: Failed to deserialize a UUID; received: " <> T.pack (show uuid)Just
uuid' ->Right
uuid'fromPersistValue
x = Left $ "File.hs: When trying to deserialize a UUID: expected PersistLiteralEncoded, received: "-- > <> T.pack (show x) instancePersistFieldSql
UUID wheresqlType
_ =SqlOther
"uuid"
User Created Database Types
Similarly, some databases support creating custom types, e.g. Postgres' DOMAIN and ENUM features. You can use SqlOther
to specify a custom type:
CREATE DOMAIN ssn AS text CHECK ( value ~ '^[0-9]{9}$');
instancePersistFieldSQL
SSN wheresqlType
_ =SqlOther
"ssn"
CREATE TYPE rainbow_color AS ENUM ('red', 'orange', 'yellow', 'green', 'blue', 'indigo', 'violet');
instancePersistFieldSQL
RainbowColor wheresqlType
_ =SqlOther
"rainbow_color"