packages feed

opaleye-trans-0.3.0: examples/v1/RoseTree.hs

{-# LANGUAGE Arrows                #-}
{-# LANGUAGE FlexibleContexts      #-}
{-# LANGUAGE FlexibleInstances     #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE OverloadedStrings     #-}
{-# LANGUAGE TemplateHaskell       #-}

module Main where

import           Control.Arrow

import           Data.Profunctor.Product.TH (makeAdaptorAndInstance)

import qualified Database.PostgreSQL.Simple as PSQL

import           Opaleye
import           Opaleye.Trans


data Rose a = Node a [Rose a]
    deriving (Show, Eq)


instance Functor Rose where
    fmap f (Node x rs) = Node (f x) (map (fmap f) rs)


data NodeP i b n a = NodeP
    { nodeId       :: i
    , nodeBranchId :: b
    , nextBranchId :: n
    , value        :: a
    } deriving (Show, Eq)


type WriteNode a = NodeP (Maybe (Column PGInt4)) (Column PGInt4) (Column PGInt4) (Column a)
type ReadNode a = NodeP (Column PGInt4) (Column PGInt4) (Column PGInt4) (Column a)


data BranchP i = BranchP
    { branchId :: i
    } deriving (Show, Eq)


type WriteBranch = BranchP (Maybe (Column PGInt4))
type ReadBranch = BranchP (Column PGInt4)


data TreeP i r = TreeP
    { treeId :: i
    , rootId :: r
    } deriving (Show, Eq)


type WriteTree = TreeP (Maybe (Column PGInt4)) (Column PGInt4)
type ReadTree = TreeP (Column PGInt4) (Column PGInt4)


makeAdaptorAndInstance "pNode" ''NodeP
makeAdaptorAndInstance "pBranch" ''BranchP
makeAdaptorAndInstance "pTree" ''TreeP


nodeTable :: Table (WriteNode a) (ReadNode a)
nodeTable = Table "node" $ pNode NodeP
    { nodeId = optional "id"
    , nodeBranchId = required "branch_id"
    , nextBranchId = required "next_branch_id"
    , value = required "value"
    }


branchTable :: Table WriteBranch ReadBranch
branchTable = Table "branch" $ pBranch (BranchP (optional "id"))


treeTable :: Table WriteTree ReadTree
treeTable = Table "rosetree" $ pTree TreeP
    { treeId = optional "id"
    , rootId = required "root_id"
    }


newTree :: Int -> Transaction (Maybe Int)
newTree rootId = insertReturningFirst treeTable treeId (TreeP Nothing (pgInt4 rootId))


newBranch :: Transaction (Maybe Int)
newBranch = insertReturningFirst branchTable branchId (BranchP Nothing)


insertNode :: Int -> Int -> Int -> Transaction (Maybe Int)
insertNode bid nbid x =
    insertReturningFirst nodeTable nodeId
        (NodeP Nothing (pgInt4 bid) (pgInt4 nbid) (pgInt4 x))


insertTree :: MonadIO m => Rose Int -> OpaleyeT m Int
insertTree (Node x xs) = transaction $ do
    Just bid <- newBranch
    Just rootId <- insertNode 0 bid x
    Just treeId <- newTree rootId

    mapM_ (insertTree' bid) xs

    return treeId


insertTree' :: Int -> Rose Int -> Transaction ()
insertTree' bid (Node x xs) = do
    Just nbid <- newBranch
    insertNode bid nbid x
    mapM_ (insertTree' nbid) xs


selectTree :: Int -> Transaction (Rose Int)
selectTree treeId = do
    Just rootId <- selectRootNode treeId
    Just (NodeP _ _ nbid x) <- selectNode rootId
    xs <- selectBranch nbid
    return (Node x xs)


selectRootNode :: Int -> Transaction (Maybe Int)
selectRootNode tid = queryFirst rootNode
  where
    rootNode :: Query (Column PGInt4)
    rootNode = proc () -> do
        tree <- queryTable treeTable -< ()
        restrict -< treeId tree .== pgInt4 tid
        returnA -< rootId tree


selectNode :: Int -> Transaction (Maybe (NodeP Int Int Int Int))
selectNode nid = queryFirst nodeById
  where
    nodeById :: Query (ReadNode PGInt4)
    nodeById = proc () -> do
        node <- queryTable nodeTable -< ()
        restrict -< nodeId node .== pgInt4 nid
        returnA -< node


selectBranch :: Int -> Transaction [Rose Int]
selectBranch bid = do
    nodes <- query nodeByBranchId
    sequence (mkNode <$> nodes)
  where
    nodeByBranchId :: Query (ReadNode PGInt4)
    nodeByBranchId = proc () -> do
        row@(NodeP _ bid' _ _) <- queryTable nodeTable -< ()
        restrict -< bid' .== pgInt4 bid
        returnA -< row

    mkNode :: NodeP Int Int Int Int -> Transaction (Rose Int)
    mkNode (NodeP _ _ nbid x) = do
        xs <- selectBranch nbid
        return (Node x xs)


main :: IO ()
main = do
    conn <- PSQL.connectPostgreSQL "dbname='rosetree' user='postgres'"

    let tree :: Rose Int
        tree =
            Node 6
                [ Node 7
                    [ Node 1425
                        [ Node 42 []
                        , Node 2354 []
                        , Node 4245 []]]
                , Node 10 []
                , Node 6
                    [ Node 12 []
                    , Node 14 []]]

    tree' <- runOpaleyeT conn $ run . selectTree =<< insertTree tree

    print (tree, tree')
    print (tree == tree')