Persist.hs 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399
  1. {-# LANGUAGE ExistentialQuantification #-}
  2. module Database.Persist
  3. ( module Database.Persist.Class
  4. , module Database.Persist.Types
  5. -- * Reference Schema & Dataset
  6. -- |
  7. --
  8. -- All the combinators present here will be explained based on this schema:
  9. --
  10. -- > share [mkPersist sqlSettings, mkMigrate "migrateAll"] [persistLowerCase|
  11. -- > User
  12. -- > name String
  13. -- > age Int
  14. -- > deriving Show
  15. -- > |]
  16. --
  17. -- and this dataset. The examples below will refer to this as dataset-1.
  18. --
  19. -- #dataset#
  20. --
  21. -- > +-----+-----+-----+
  22. -- > |id |name |age |
  23. -- > +-----+-----+-----+
  24. -- > |1 |SPJ |40 |
  25. -- > +-----+-----+-----+
  26. -- > |2 |Simon|41 |
  27. -- > +-----+-----+-----+
  28. -- * Query update combinators
  29. , (=.), (+=.), (-=.), (*=.), (/=.)
  30. -- * Query filter combinators
  31. , (==.), (!=.), (<.), (>.), (<=.), (>=.), (<-.), (/<-.), (||.)
  32. -- * JSON Utilities
  33. , listToJSON
  34. , mapToJSON
  35. , toJsonText
  36. , getPersistMap
  37. -- * Other utilities
  38. , limitOffsetOrder
  39. ) where
  40. import Data.Aeson (toJSON, ToJSON)
  41. import Data.Aeson.Text (encodeToTextBuilder)
  42. import qualified Data.Text as T
  43. import Data.Text.Lazy (toStrict)
  44. import Data.Text.Lazy.Builder (toLazyText)
  45. import Database.Persist.Types
  46. import Database.Persist.Class
  47. import Database.Persist.Class.PersistField (getPersistMap)
  48. infixr 3 =., +=., -=., *=., /=.
  49. (=.), (+=.), (-=.), (*=.), (/=.) ::
  50. forall v typ. PersistField typ => EntityField v typ -> typ -> Update v
  51. -- | Assign a field a value.
  52. --
  53. -- === __Example usage__
  54. --
  55. -- @
  56. -- updateAge :: MonadIO m => ReaderT SqlBackend m ()
  57. -- updateAge = updateWhere [UserName ==. \"SPJ\" ] [UserAge =. 45]
  58. -- @
  59. --
  60. -- Similar to `updateWhere` which is shown in the above example you can use other functions present in the module "Database.Persist.Class". Note that the first parameter of `updateWhere` is [`Filter` val] and second parameter is [`Update` val]. By comparing this with the type of `==.` and `=.`, you can see that they match up in the above usage.
  61. --
  62. -- The above query when applied on <#dataset dataset-1>, will produce this:
  63. --
  64. -- > +-----+-----+--------+
  65. -- > |id |name |age |
  66. -- > +-----+-----+--------+
  67. -- > |1 |SPJ |40 -> 45|
  68. -- > +-----+-----+--------+
  69. -- > |2 |Simon|41 |
  70. -- > +-----+-----+--------+
  71. f =. a = Update f a Assign
  72. -- | Assign a field by addition (@+=@).
  73. --
  74. -- === __Example usage__
  75. --
  76. -- @
  77. -- addAge :: MonadIO m => ReaderT SqlBackend m ()
  78. -- addAge = updateWhere [UserName ==. \"SPJ\" ] [UserAge +=. 1]
  79. -- @
  80. --
  81. -- The above query when applied on <#dataset dataset-1>, will produce this:
  82. --
  83. -- > +-----+-----+---------+
  84. -- > |id |name |age |
  85. -- > +-----+-----+---------+
  86. -- > |1 |SPJ |40 -> 41 |
  87. -- > +-----+-----+---------+
  88. -- > |2 |Simon|41 |
  89. -- > +-----+-----+---------+
  90. f +=. a = Update f a Add
  91. -- | Assign a field by subtraction (@-=@).
  92. --
  93. -- === __Example usage__
  94. --
  95. -- @
  96. -- subtractAge :: MonadIO m => ReaderT SqlBackend m ()
  97. -- subtractAge = updateWhere [UserName ==. \"SPJ\" ] [UserAge -=. 1]
  98. -- @
  99. --
  100. -- The above query when applied on <#dataset dataset-1>, will produce this:
  101. --
  102. -- > +-----+-----+---------+
  103. -- > |id |name |age |
  104. -- > +-----+-----+---------+
  105. -- > |1 |SPJ |40 -> 39 |
  106. -- > +-----+-----+---------+
  107. -- > |2 |Simon|41 |
  108. -- > +-----+-----+---------+
  109. f -=. a = Update f a Subtract
  110. -- | Assign a field by multiplication (@*=@).
  111. --
  112. -- === __Example usage__
  113. --
  114. -- @
  115. -- multiplyAge :: MonadIO m => ReaderT SqlBackend m ()
  116. -- multiplyAge = updateWhere [UserName ==. \"SPJ\" ] [UserAge *=. 2]
  117. -- @
  118. --
  119. -- The above query when applied on <#dataset dataset-1>, will produce this:
  120. --
  121. -- > +-----+-----+--------+
  122. -- > |id |name |age |
  123. -- > +-----+-----+--------+
  124. -- > |1 |SPJ |40 -> 80|
  125. -- > +-----+-----+--------+
  126. -- > |2 |Simon|41 |
  127. -- > +-----+-----+--------+
  128. f *=. a = Update f a Multiply
  129. -- | Assign a field by division (@/=@).
  130. --
  131. -- === __Example usage__
  132. --
  133. -- @
  134. -- divideAge :: MonadIO m => ReaderT SqlBackend m ()
  135. -- divideAge = updateWhere [UserName ==. \"SPJ\" ] [UserAge /=. 2]
  136. -- @
  137. --
  138. -- The above query when applied on <#dataset dataset-1>, will produce this:
  139. --
  140. -- > +-----+-----+---------+
  141. -- > |id |name |age |
  142. -- > +-----+-----+---------+
  143. -- > |1 |SPJ |40 -> 20 |
  144. -- > +-----+-----+---------+
  145. -- > |2 |Simon|41 |
  146. -- > +-----+-----+---------+
  147. f /=. a = Update f a Divide
  148. infix 4 ==., <., <=., >., >=., !=.
  149. (==.), (!=.), (<.), (<=.), (>.), (>=.) ::
  150. forall v typ. PersistField typ => EntityField v typ -> typ -> Filter v
  151. -- | Check for equality.
  152. --
  153. -- === __Example usage__
  154. --
  155. -- @
  156. -- selectSPJ :: MonadIO m => ReaderT SqlBackend m [Entity User]
  157. -- selectSPJ = selectList [UserName ==. \"SPJ\" ] []
  158. -- @
  159. --
  160. -- The above query when applied on <#dataset dataset-1>, will produce this:
  161. --
  162. -- > +-----+-----+-----+
  163. -- > |id |name |age |
  164. -- > +-----+-----+-----+
  165. -- > |1 |SPJ |40 |
  166. -- > +-----+-----+-----+
  167. f ==. a = Filter f (FilterValue a) Eq
  168. -- | Non-equality check.
  169. --
  170. -- === __Example usage__
  171. --
  172. -- @
  173. -- selectSimon :: MonadIO m => ReaderT SqlBackend m [Entity User]
  174. -- selectSimon = selectList [UserName !=. \"SPJ\" ] []
  175. -- @
  176. --
  177. -- The above query when applied on <#dataset dataset-1>, will produce this:
  178. --
  179. -- > +-----+-----+-----+
  180. -- > |id |name |age |
  181. -- > +-----+-----+-----+
  182. -- > |2 |Simon|41 |
  183. -- > +-----+-----+-----+
  184. f !=. a = Filter f (FilterValue a) Ne
  185. -- | Less-than check.
  186. --
  187. -- === __Example usage__
  188. --
  189. -- @
  190. -- selectLessAge :: MonadIO m => ReaderT SqlBackend m [Entity User]
  191. -- selectLessAge = selectList [UserAge <. 41 ] []
  192. -- @
  193. --
  194. -- The above query when applied on <#dataset dataset-1>, will produce this:
  195. --
  196. -- > +-----+-----+-----+
  197. -- > |id |name |age |
  198. -- > +-----+-----+-----+
  199. -- > |1 |SPJ |40 |
  200. -- > +-----+-----+-----+
  201. f <. a = Filter f (FilterValue a) Lt
  202. -- | Less-than or equal check.
  203. --
  204. -- === __Example usage__
  205. --
  206. -- @
  207. -- selectLessEqualAge :: MonadIO m => ReaderT SqlBackend m [Entity User]
  208. -- selectLessEqualAge = selectList [UserAge <=. 40 ] []
  209. -- @
  210. --
  211. -- The above query when applied on <#dataset dataset-1>, will produce this:
  212. --
  213. -- > +-----+-----+-----+
  214. -- > |id |name |age |
  215. -- > +-----+-----+-----+
  216. -- > |1 |SPJ |40 |
  217. -- > +-----+-----+-----+
  218. f <=. a = Filter f (FilterValue a) Le
  219. -- | Greater-than check.
  220. --
  221. -- === __Example usage__
  222. --
  223. -- @
  224. -- selectGreaterAge :: MonadIO m => ReaderT SqlBackend m [Entity User]
  225. -- selectGreaterAge = selectList [UserAge >. 40 ] []
  226. -- @
  227. --
  228. -- The above query when applied on <#dataset dataset-1>, will produce this:
  229. --
  230. -- > +-----+-----+-----+
  231. -- > |id |name |age |
  232. -- > +-----+-----+-----+
  233. -- > |2 |Simon|41 |
  234. -- > +-----+-----+-----+
  235. f >. a = Filter f (FilterValue a) Gt
  236. -- | Greater-than or equal check.
  237. --
  238. -- === __Example usage__
  239. --
  240. -- @
  241. -- selectGreaterEqualAge :: MonadIO m => ReaderT SqlBackend m [Entity User]
  242. -- selectGreaterEqualAge = selectList [UserAge >=. 41 ] []
  243. -- @
  244. --
  245. -- The above query when applied on <#dataset dataset-1>, will produce this:
  246. --
  247. -- > +-----+-----+-----+
  248. -- > |id |name |age |
  249. -- > +-----+-----+-----+
  250. -- > |2 |Simon|41 |
  251. -- > +-----+-----+-----+
  252. f >=. a = Filter f (FilterValue a) Ge
  253. infix 4 <-., /<-.
  254. (<-.), (/<-.) :: forall v typ. PersistField typ => EntityField v typ -> [typ] -> Filter v
  255. -- | Check if value is in given list.
  256. --
  257. -- === __Example usage__
  258. --
  259. -- @
  260. -- selectUsers :: MonadIO m => ReaderT SqlBackend m [Entity User]
  261. -- selectUsers = selectList [UserAge <-. [40, 41]] []
  262. -- @
  263. --
  264. -- The above query when applied on <#dataset dataset-1>, will produce this:
  265. --
  266. -- > +-----+-----+-----+
  267. -- > |id |name |age |
  268. -- > +-----+-----+-----+
  269. -- > |1 |SPJ |40 |
  270. -- > +-----+-----+-----+
  271. -- > |2 |Simon|41 |
  272. -- > +-----+-----+-----+
  273. --
  274. --
  275. -- @
  276. -- selectSPJ :: MonadIO m => ReaderT SqlBackend m [Entity User]
  277. -- selectSPJ = selectList [UserAge <-. [40]] []
  278. -- @
  279. --
  280. -- The above query when applied on <#dataset dataset-1>, will produce this:
  281. --
  282. -- > +-----+-----+-----+
  283. -- > |id |name |age |
  284. -- > +-----+-----+-----+
  285. -- > |1 |SPJ |40 |
  286. -- > +-----+-----+-----+
  287. f <-. a = Filter f (FilterValues a) In
  288. -- | Check if value is not in given list.
  289. --
  290. -- === __Example usage__
  291. --
  292. -- @
  293. -- selectSimon :: MonadIO m => ReaderT SqlBackend m [Entity User]
  294. -- selectSimon = selectList [UserAge /<-. [40]] []
  295. -- @
  296. --
  297. -- The above query when applied on <#dataset dataset-1>, will produce this:
  298. --
  299. -- > +-----+-----+-----+
  300. -- > |id |name |age |
  301. -- > +-----+-----+-----+
  302. -- > |2 |Simon|41 |
  303. -- > +-----+-----+-----+
  304. f /<-. a = Filter f (FilterValues a) NotIn
  305. infixl 3 ||.
  306. (||.) :: forall v. [Filter v] -> [Filter v] -> [Filter v]
  307. -- | The OR of two lists of filters. For example:
  308. --
  309. -- > selectList
  310. -- > ([ PersonAge >. 25
  311. -- > , PersonAge <. 30 ] ||.
  312. -- > [ PersonIncome >. 15000
  313. -- > , PersonIncome <. 25000 ])
  314. -- > []
  315. --
  316. -- will filter records where a person's age is between 25 and 30 /or/ a
  317. -- person's income is between (15000 and 25000).
  318. --
  319. -- If you are looking for an @(&&.)@ operator to do @(A AND B AND (C OR D))@
  320. -- you can use the @(++)@ operator instead as there is no @(&&.)@. For
  321. -- example:
  322. --
  323. -- > selectList
  324. -- > ([ PersonAge >. 25
  325. -- > , PersonAge <. 30 ] ++
  326. -- > ([PersonCategory ==. 1] ||.
  327. -- > [PersonCategory ==. 5]))
  328. -- > []
  329. --
  330. -- will filter records where a person's age is between 25 and 30 /and/
  331. -- (person's category is either 1 or 5).
  332. a ||. b = [FilterOr [FilterAnd a, FilterAnd b]]
  333. -- | Convert list of 'PersistValue's into textual representation of JSON
  334. -- object. This is a type-constrained synonym for 'toJsonText'.
  335. listToJSON :: [PersistValue] -> T.Text
  336. listToJSON = toJsonText
  337. -- | Convert map (list of tuples) into textual representation of JSON
  338. -- object. This is a type-constrained synonym for 'toJsonText'.
  339. mapToJSON :: [(T.Text, PersistValue)] -> T.Text
  340. mapToJSON = toJsonText
  341. -- | A more general way to convert instances of `ToJSON` type class to
  342. -- strict text 'T.Text'.
  343. toJsonText :: ToJSON j => j -> T.Text
  344. toJsonText = toStrict . toLazyText . encodeToTextBuilder . toJSON
  345. -- | FIXME What's this exactly?
  346. limitOffsetOrder :: PersistEntity val
  347. => [SelectOpt val]
  348. -> (Int, Int, [SelectOpt val])
  349. limitOffsetOrder opts =
  350. foldr go (0, 0, []) opts
  351. where
  352. go (LimitTo l) (_, b, c) = (l, b ,c)
  353. go (OffsetBy o) (a, _, c) = (a, o, c)
  354. go x (a, b, c) = (a, b, x : c)