Store.hs 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118
  1. {-# LANGUAGE FlexibleInstances #-}
  2. {-# LANGUAGE GeneralizedNewtypeDeriving #-}
  3. {-# LANGUAGE TypeFamilies #-}
  4. {-# OPTIONS_GHC -fno-warn-orphans #-}
  5. module Database.Persist.Redis.Store
  6. ( execRedisT
  7. , RedisBackend
  8. )where
  9. import Control.Monad.IO.Class (MonadIO (..))
  10. import Data.Aeson(FromJSON(..), ToJSON(..))
  11. import Data.Text (Text, pack)
  12. import qualified Database.Redis as R
  13. import Web.HttpApiData (ToHttpApiData (..), FromHttpApiData (..), parseUrlPieceMaybe)
  14. import Web.PathPieces (PathPiece(..))
  15. import Database.Persist
  16. import Database.Persist.Redis.Config (RedisT, thisConnection)
  17. import Database.Persist.Redis.Internal
  18. import Database.Persist.Redis.Update
  19. import qualified Database.Persist.Sql as Sql
  20. type RedisBackend = R.Connection
  21. -- | Fetches a next key from <object>_id record
  22. createKey :: (R.RedisCtx m f, PersistEntity val) => val -> m (f Integer)
  23. createKey val = do
  24. let keyId = toKeyId val
  25. R.incr keyId
  26. desugar :: R.TxResult a -> Either String a
  27. desugar (R.TxSuccess x) = Right x
  28. desugar R.TxAborted = Left "Transaction aborted!"
  29. desugar (R.TxError string) = Left string
  30. -- | Execute Redis transaction inside RedisT monad transformer
  31. execRedisT :: (MonadIO m) => R.RedisTx (R.Queued a) -> RedisT m a
  32. execRedisT action = do
  33. conn <- thisConnection
  34. result <- liftIO $ R.runRedis conn $ R.multiExec action -- this is the question if we should support transaction here
  35. let r = desugar result
  36. case r of
  37. (Right x) -> return x
  38. (Left x) -> fail x
  39. instance HasPersistBackend R.Connection where
  40. type BaseBackend R.Connection = R.Connection
  41. persistBackend = id
  42. instance PersistCore R.Connection where
  43. newtype BackendKey R.Connection = RedisKey Text
  44. deriving (Show, Read, Eq, Ord, PersistField, FromJSON, ToJSON)
  45. instance PersistStoreRead R.Connection where
  46. get k = do
  47. r <- execRedisT $ R.hgetall (unKey k)
  48. if null r
  49. then return Nothing
  50. else do
  51. Entity _ val <- mkEntity k r
  52. return $ Just val
  53. instance PersistStoreWrite R.Connection where
  54. insert val = do
  55. keyId <- execRedisT $ createKey val
  56. let textKey = toKeyText val keyId
  57. key <- toKey textKey
  58. _ <- insertKey key val
  59. return key
  60. insertKey k val = do
  61. let fields = toInsertFields val
  62. -- Inserts a hash map into <object>_<id> record
  63. _ <- execRedisT $ R.hmset (unKey k) fields
  64. return ()
  65. repsert k val = do
  66. _ <- execRedisT $ R.del [unKey k]
  67. insertKey k val
  68. return ()
  69. replace k val = do
  70. delete k
  71. insertKey k val
  72. return ()
  73. delete k = do
  74. r <- execRedisT $ R.del [unKey k]
  75. case r of
  76. 0 -> fail "there is no such key!"
  77. 1 -> return ()
  78. _ -> fail "there are a lot of such keys!"
  79. update _ [] = return ()
  80. update k upds = do
  81. r <- execRedisT $ R.hgetall (unKey k)
  82. if null r
  83. then fail "No such key exists!"
  84. else do
  85. v <- mkEntity k r
  86. let (Entity _ val) = cmdUpdate v upds
  87. insertKey k val
  88. return()
  89. instance ToHttpApiData (BackendKey RedisBackend) where
  90. toUrlPiece (RedisKey txt) = txt
  91. instance FromHttpApiData (BackendKey RedisBackend) where
  92. parseUrlPiece = return . RedisKey
  93. -- some checking that entity exists and it is in format of entityname_id is omitted
  94. instance PathPiece (BackendKey RedisBackend) where
  95. toPathPiece = toUrlPiece
  96. fromPathPiece = parseUrlPieceMaybe
  97. instance Sql.PersistFieldSql (BackendKey RedisBackend) where
  98. sqlType _ = Sql.SqlOther (pack "doesn't make much sense for Redis backend")