packages feed

hasbolt-extras-0.0.0.16: example/Main.hs

{-# LANGUAGE DeriveGeneric       #-}
{-# LANGUAGE OverloadedStrings   #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TemplateHaskell     #-}
{-# LANGUAGE TypeApplications    #-}

module Main where

import           Control.Exception             (bracket)
import           Control.Monad.State           (execState, modify)
import           Data.Aeson                    (encode)
import qualified Data.ByteString.Lazy.Char8    as B (putStrLn)
import           Data.Default                  (def)
import           Data.Text                     (Text)
import           Database.Bolt                 (BoltActionT, BoltCfg (..),
                                                Value (..), close, connect, run)
import           Database.Bolt.Extras          (NodeLike (..),
                                                URelationLike (..))
import           Database.Bolt.Extras.Graph
import           Database.Bolt.Extras.Template (makeNodeLike, makeURelationLike)
import           GHC.Generics                  (Generic)

-- | Configuration for connection to local database.
boltCfg :: BoltCfg
boltCfg = def { host = "localhost"
              , user = "neo4j"
              , password = "12345"
              }

-- | Helper to run queries in Neo4j DB.
--
runQueryDB :: BoltActionT IO a -> IO a
runQueryDB act = bracket (connect boltCfg) close (`run` act)

data ExampleNode = ExampleNode { exampleFieldT :: Text
                               , exampleFieldI :: Int
                               }
  deriving (Show, Generic)

data EXAMPLE_RELATION = EXAMPLE_RELATION
  deriving (Show, Generic)

makeNodeLike ''ExampleNode
makeURelationLike ''EXAMPLE_RELATION

exNodeAVar :: Text
exNodeAVar = "nodeA"

exNodeBVar :: Text
exNodeBVar = "nodeB"

-- | Builds query:
-- CREATE (nodeA:ExampleNode { exampleFieldT: "A" , exampleFieldI: 1}) WITH nodeA
-- MERGE  (nodeB:ExampleNode { exampleFieldT: "B" , exampleFieldI: 2}) WITH nodeB
-- MERGE  (nodeA)-[nodeA0nodeB:EXAMPLE_RELATION]-(nodeB) WITH nodeA, nodeB, nodeA0nodeB
-- RETURN ID(nodeA), ID(nodeB), ID(nodeA0nodeB)
--
examplePutGraph :: GraphPutRequest
examplePutGraph = flip execState emptyGraph $ do
    modify $ addNode exNodeAVar (CreateN . toNode $ exNodeA)
    modify $ addNode exNodeBVar (MergeN  . toNode $ exNodeB)

    modify $ addRelation exNodeAVar exNodeBVar (MergeR . toURelation $ exRel)
  where
    exNodeA = ExampleNode "A" 1
    exNodeB = ExampleNode "B" 2
    exRel   = EXAMPLE_RELATION

-- | Builds query:
-- MATCH (nodeA)-[:EXAMPLE_RELATION]-(nodeB),
-- (nodeA:ExampleNode { exampleField: "A" }),
-- (nodeB:ExampleNode { exampleField: "B" })
-- RETURN { id: ID(nodeA), labels: labels(nodeA), props: properties(nodeA) }
--
exampleGetGraphB :: GraphGetRequest
exampleGetGraphB = flip execState emptyGraph $ do
    modify $ addNode exNodeAVar exNodeA
    modify $ addNode exNodeBVar exNodeB

    modify $ addRelation exNodeAVar exNodeBVar exRel
  where
    exNodeA = defaultNodeReturn    # withLabelQ ''ExampleNode
                                   # withProp   ("exampleFieldT", T "A")
                                   # withReturn allProps

    exNodeB = defaultNodeNotReturn # withLabelQ ''ExampleNode
                                   # withProp   ("exampleFieldT", T "B")

    exRel   = defaultRelNotReturn  # withLabelQ ''EXAMPLE_RELATION

-- | Builds query:
-- MATCH (nodeA:ExampleNode { exampleField: "A" })
-- RETURN { id: ID(nodeA), labels: labels(nodeA), props: nodeA {.exampleFieldI} }
--
exampleGetGraphA :: GraphGetRequest
exampleGetGraphA = flip execState emptyGraph $
    modify $ addNode exNodeAVar exNodeA
  where
    exNodeA = defaultNodeReturn    # withLabelQ ''ExampleNode
                                   # withProp   ("exampleFieldT", T "A")
                                   # withReturn ["exampleFieldI"]

-- | Put 'examplePutGraph' to the database.
--
putGraph :: IO ()
putGraph = do
    putGraphR <- runQueryDB $ makeRequest @PutRequest [] examplePutGraph
    putStrLn "Uploaded graph: "
    print putGraphR

-- Get 'exampleGetGraphB' and parse it to Haskell object.
--
getGraphB :: IO ()
getGraphB = do
    getGraphsR                  <- runQueryDB $ makeRequest @GetRequest [] exampleGetGraphB
    let nodesA :: [ExampleNode] = extractNode exNodeAVar <$> getGraphsR
    putStrLn "Downloaded graph and converted to Haskell object: "
    print nodesA

-- Get 'exampleGetGraphA' and parse it to JSON.
--
getGraphA :: IO ()
getGraphA = do
    getGraphsR <- runQueryDB $ makeRequest @GetRequest [] exampleGetGraphA
    let nodesA = extractNodeAeson exNodeAVar <$> getGraphsR
    putStrLn "Downloaded graph and converted to JSON: "
    B.putStrLn . encode $ nodesA

main :: IO ()
main = putGraph >> getGraphB >> getGraphA