module Query ( emptyQueryState , updateQueryStateRef , QueryState ) where import Control.Monad (forM_) import Data.ByteString (ByteString) import Data.CQRS (enumerateEventStore, GUID, EventStore) import Data.Enumerator (run_, (>>==)) import qualified Data.Enumerator.List as EL import Data.IORef (IORef, readIORef, atomicModifyIORef) import Data.Map (Map) import qualified Data.Map as M import Data.Serialize (encode) import Data.Text (Text) import Events (Event(..)) -- Update query state with latest events. updateQueryStateRef :: IORef QueryState -> EventStore Event -> IO () updateQueryStateRef queryStateRef eventStore = do lastVersion <- fmap ((+) 1 . qsLastVersion) $ readIORef queryStateRef evs <- run_ (EL.consume >>== enumerateEventStore eventStore lastVersion) forM_ evs $ \(gv,(g,v,e)) -> do putStrLn $ show $ "ev(" ++ show gv ++ "): " ++ show e atomicModifyIORef queryStateRef $ \s -> (updateState s gv g v e, ()) return () -- Update state per event. updateState :: QueryState -> Int -> GUID a -> Int -> Event -> QueryState updateState queryState gv guid _ (ProjectCreated pn) = queryState { qsLastVersion = gv , qsProjectNamesById = M.insert (encode guid) pn $ qsProjectNamesById queryState } updateState queryState gv guid v (ProjectRenamed pn) = updateState queryState gv guid v (ProjectCreated pn) updateState queryState gv _ _ (TaskAdded _ _) = queryState { qsLastVersion = gv } -- In-memory data structure for querying. We should really use -- a persistent store for this, but this is just an example. data QueryState = QueryState { qsLastVersion :: Int , qsProjectNamesById :: Map ByteString Text } deriving (Show) -- Create "empty" data structure for querying. emptyQueryState :: QueryState emptyQueryState = QueryState 0 M.empty