| Safe Haskell | None |
|---|---|
| Language | Haskell2010 |
Prolude.Persist
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.
Constructors
| 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 Keys.
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 ::SqlPersistTIO[EntityUser] usersWithAgeOver40 =selectList[UserAge>=.40] []
If you provide multiple values in the list, the conditions are ANDed
together.
usersWithAgeBetween30And50 ::SqlPersistTIO[EntityUser] 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 = HashedPassword ByteString
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 = SSN Text
deriving (Eq, Show, PersistFieldSql)
mkSSN :: Text -> Either Text SSN
mkSSN t = if (T.length t == 9) && (T.all C.isDigit t)
then Right $ SSN t
else Left $ "Invalid SSN: " <> t
instance PersistField SSN where
toPersistValue (SSN t) = PersistText t
fromPersistValue (PersistText t) = mkSSN t
-- Handle cases where the database does not give us PersistText
fromPersistValue x = Left $ "File.hs: When trying to deserialize an SSN: expected PersistText, received: " <> T.pack (show x)
Tips:
- This file contain dozens of
PersistFieldinstances you can look at for examples. - Typically custom
PersistFieldinstances will only accept a singlePersistValueconstructor infromPersistValue. - Internal
PersistFieldinstances accept a wide variety ofPersistValues 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
PersistFieldSqlto specify the type of the database column.
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) instancePersistFieldSwitch wheretoPersistValues = case s of On ->PersistBoolTrue Off ->PersistBoolFalsefromPersistValue(PersistBoolb) = if b thenRightOn elseRightOfffromPersistValuex = Left $ "File.hs: When trying to deserialize a Switch: expected PersistBool, received: " <> T.pack (show x) instancePersistFieldSqlSwitch 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 instancePersistFieldUUID wheretoPersistValue=PersistLiteralEncoded. toASCIIBytesfromPersistValue(PersistLiteralEncodeduuid) = case fromASCIIBytes uuid ofNothing->Left$ "Model/CustomTypes.hs: Failed to deserialize a UUID; received: " <> T.pack (show uuid)Justuuid' ->Rightuuid'fromPersistValuex = Left $ "File.hs: When trying to deserialize a UUID: expected PersistLiteralEncoded, received: "-- > <> T.pack (show x) instancePersistFieldSqlUUID 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}$');instancePersistFieldSQLSSN wheresqlType_ =SqlOther"ssn"
CREATE TYPE rainbow_color AS ENUM ('red', 'orange', 'yellow', 'green', 'blue', 'indigo', 'violet');instancePersistFieldSQLRainbowColor wheresqlType_ =SqlOther"rainbow_color"
Minimal complete definition